| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>DocSync - Intelligent Document Automation</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://cdn.jsdelivr.net/npm/pdf-lib@1.17.1/dist/pdf-lib.min.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| .dropzone { |
| border: 2px dashed #3b82f6; |
| transition: all 0.3s ease; |
| } |
| .dropzone.active { |
| border-color: #10b981; |
| background-color: #f0fdf4; |
| } |
| .document-preview { |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); |
| } |
| .confidence-meter { |
| height: 6px; |
| border-radius: 3px; |
| } |
| .sidebar-item { |
| transition: all 0.2s ease; |
| } |
| .sidebar-item:hover { |
| background-color: #f3f4f6; |
| } |
| .page-turner { |
| animation: pageTurn 0.5s ease-in-out; |
| } |
| @keyframes pageTurn { |
| 0% { transform: rotateY(0deg); } |
| 50% { transform: rotateY(90deg); } |
| 100% { transform: rotateY(0deg); } |
| } |
| .highlight-field { |
| animation: pulse 2s infinite; |
| } |
| @keyframes pulse { |
| 0% { background-color: rgba(255, 255, 0, 0.3); } |
| 50% { background-color: rgba(255, 255, 0, 0.7); } |
| 100% { background-color: rgba(255, 255, 0, 0.3); } |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50 font-sans"> |
| <div class="flex h-screen overflow-hidden"> |
| |
| <div class="hidden md:flex md:flex-shrink-0"> |
| <div class="flex flex-col w-64 bg-white border-r border-gray-200"> |
| <div class="flex items-center justify-center h-16 px-4 bg-blue-600"> |
| <h1 class="text-white font-bold text-xl">DocSync</h1> |
| </div> |
| <div class="flex flex-col flex-grow p-4 overflow-y-auto"> |
| <nav class="flex-1 space-y-2"> |
| <a href="#" class="flex items-center px-4 py-2 text-sm font-medium text-blue-700 bg-blue-100 rounded-md sidebar-item"> |
| <i class="fas fa-home mr-3"></i> |
| Dashboard |
| </a> |
| <a href="#" class="flex items-center px-4 py-2 text-sm font-medium text-gray-600 rounded-md sidebar-item"> |
| <i class="fas fa-file-import mr-3"></i> |
| Document Processing |
| </a> |
| <a href="#" class="flex items-center px-4 py-2 text-sm font-medium text-gray-600 rounded-md sidebar-item"> |
| <i class="fas fa-history mr-3"></i> |
| Processing History |
| </a> |
| <a href="#" class="flex items-center px-4 py-2 text-sm font-medium text-gray-600 rounded-md sidebar-item"> |
| <i class="fas fa-cog mr-3"></i> |
| Settings |
| </a> |
| </nav> |
| <div class="mt-auto p-4"> |
| <div class="flex items-center"> |
| <img class="w-10 h-10 rounded-full" src="https://randomuser.me/api/portraits/women/44.jpg" alt="User profile"> |
| <div class="ml-3"> |
| <p class="text-sm font-medium text-gray-700">Sarah Johnson</p> |
| <p class="text-xs font-medium text-gray-500">Admin</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="flex flex-col flex-1 overflow-hidden"> |
| |
| <div class="flex items-center justify-between h-16 px-6 bg-white border-b border-gray-200"> |
| <div class="flex items-center"> |
| <button class="md:hidden text-gray-500 focus:outline-none"> |
| <i class="fas fa-bars"></i> |
| </button> |
| <h2 class="ml-4 text-lg font-medium text-gray-900">Document Automation</h2> |
| </div> |
| <div class="flex items-center space-x-4"> |
| <button class="p-1 text-gray-400 rounded-full hover:text-gray-500 focus:outline-none"> |
| <i class="fas fa-bell"></i> |
| </button> |
| <button class="p-1 text-gray-400 rounded-full hover:text-gray-500 focus:outline-none"> |
| <i class="fas fa-question-circle"></i> |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div class="flex-1 overflow-auto p-6"> |
| <div class="max-w-7xl mx-auto"> |
| |
| <div class="mb-8"> |
| <div class="flex items-center"> |
| <div class="flex items-center relative"> |
| <div class="flex items-center justify-center w-10 h-10 rounded-full bg-blue-600 text-white font-semibold"> |
| 1 |
| </div> |
| <div class="ml-3 text-sm font-medium text-blue-600">Upload Source</div> |
| </div> |
| <div class="flex-auto border-t-2 border-gray-200 mx-4"></div> |
| <div class="flex items-center"> |
| <div class="flex items-center justify-center w-10 h-10 rounded-full bg-gray-200 text-gray-600 font-semibold"> |
| 2 |
| </div> |
| <div class="ml-3 text-sm font-medium text-gray-500">Upload Template</div> |
| </div> |
| <div class="flex-auto border-t-2 border-gray-200 mx-4"></div> |
| <div class="flex items-center"> |
| <div class="flex items-center justify-center w-10 h-10 rounded-full bg-gray-200 text-gray-600 font-semibold"> |
| 3 |
| </div> |
| <div class="ml-3 text-sm font-medium text-gray-500">Review & Export</div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="sourceUploadSection" class="bg-white rounded-lg shadow p-6 mb-8"> |
| <h3 class="text-lg font-medium text-gray-900 mb-4">Upload Source Document</h3> |
| <p class="text-sm text-gray-500 mb-6">Upload a completed invoice, form, or PDF document that contains the data you want to extract.</p> |
| |
| <div id="sourceDropzone" class="dropzone rounded-lg p-8 text-center cursor-pointer mb-4"> |
| <div class="flex flex-col items-center justify-center"> |
| <i class="fas fa-cloud-upload-alt text-4xl text-blue-500 mb-3"></i> |
| <p class="text-sm text-gray-600 mb-1">Drag and drop your file here</p> |
| <p class="text-xs text-gray-400">or click to browse</p> |
| <input type="file" id="sourceFileInput" class="hidden" accept=".pdf,.jpg,.jpeg,.png,.tiff"> |
| </div> |
| </div> |
| <div class="text-xs text-gray-500 text-center">Supported formats: PDF, JPG, PNG, TIFF (Max 20MB)</div> |
| </div> |
|
|
| |
| <div id="processingStatus" class="hidden bg-white rounded-lg shadow p-6 mb-8"> |
| <div class="flex items-center justify-between mb-4"> |
| <h3 class="text-lg font-medium text-gray-900">Processing Document</h3> |
| <div class="text-sm text-blue-600">Step 1 of 3</div> |
| </div> |
| |
| <div class="space-y-4"> |
| <div> |
| <div class="flex items-center justify-between mb-1"> |
| <span class="text-sm font-medium text-gray-700">OCR Processing</span> |
| <span class="text-xs font-medium text-green-600">Completed</span> |
| </div> |
| <div class="w-full bg-gray-200 rounded-full h-1.5"> |
| <div class="bg-green-600 h-1.5 rounded-full" style="width: 100%"></div> |
| </div> |
| </div> |
| |
| <div> |
| <div class="flex items-center justify-between mb-1"> |
| <span class="text-sm font-medium text-gray-700">Layout Analysis</span> |
| <span class="text-xs font-medium text-green-600">Completed</span> |
| </div> |
| <div class="w-full bg-gray-200 rounded-full h-1.5"> |
| <div class="bg-green-600 h-1.5 rounded-full" style="width: 100%"></div> |
| </div> |
| </div> |
| |
| <div> |
| <div class="flex items-center justify-between mb-1"> |
| <span class="text-sm font-medium text-gray-700">Field Extraction</span> |
| <span class="text-xs font-medium text-blue-600">In Progress</span> |
| </div> |
| <div class="w-full bg-gray-200 rounded-full h-1.5"> |
| <div class="bg-blue-600 h-1.5 rounded-full" style="width: 65%"></div> |
| </div> |
| </div> |
| |
| <div> |
| <div class="flex items-center justify-between mb-1"> |
| <span class="text-sm font-medium text-gray-700">Semantic Matching</span> |
| <span class="text-xs font-medium text-gray-600">Pending</span> |
| </div> |
| <div class="w-full bg-gray-200 rounded-full h-1.5"> |
| <div class="bg-gray-300 h-1.5 rounded-full" style="width: 0%"></div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="extractedDataPreview" class="hidden bg-white rounded-lg shadow overflow-hidden mb-8"> |
| <div class="border-b border-gray-200 px-6 py-4"> |
| <h3 class="text-lg font-medium text-gray-900">Extracted Data Preview</h3> |
| </div> |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-0"> |
| |
| <div class="md:col-span-1 border-r border-gray-200"> |
| <div class="p-4"> |
| <div class="document-preview bg-gray-100 rounded-lg overflow-hidden relative"> |
| <div class="absolute top-0 left-0 right-0 bg-gray-800 text-white py-1 px-3 text-sm flex justify-between items-center"> |
| <span id="sourceFileName">source_document.pdf</span> |
| <div class="flex space-x-2"> |
| <button class="text-gray-300 hover:text-white"> |
| <i class="fas fa-search-plus"></i> |
| </button> |
| <button class="text-gray-300 hover:text-white"> |
| <i class="fas fa-search-minus"></i> |
| </button> |
| </div> |
| </div> |
| <div class="p-4 h-96 overflow-auto"> |
| <img id="documentPreviewImage" src="https://via.placeholder.com/600x800?text=Document+Preview" alt="Document preview" class="mx-auto shadow-lg"> |
| </div> |
| <div class="absolute bottom-0 left-0 right-0 bg-gray-100 py-2 px-4 flex justify-between items-center border-t border-gray-200"> |
| <button id="prevPage" class="text-gray-600 hover:text-blue-600 disabled:text-gray-300"> |
| <i class="fas fa-chevron-left mr-1"></i> Previous |
| </button> |
| <span class="text-sm text-gray-600">Page <span id="currentPage">1</span> of <span id="totalPages">3</span></span> |
| <button id="nextPage" class="text-gray-600 hover:text-blue-600 disabled:text-gray-300"> |
| Next <i class="fas fa-chevron-right ml-1"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="md:col-span-2"> |
| <div class="p-4"> |
| <div class="flex justify-between items-center mb-4"> |
| <div> |
| <h4 class="font-medium text-gray-900">Extracted Fields</h4> |
| <p class="text-sm text-gray-500">Review and edit the extracted data before proceeding</p> |
| </div> |
| <div class="flex space-x-2"> |
| <button class="px-3 py-1 bg-gray-100 text-gray-700 rounded text-sm hover:bg-gray-200"> |
| <i class="fas fa-filter mr-1"></i> Filter |
| </button> |
| <button class="px-3 py-1 bg-gray-100 text-gray-700 rounded text-sm hover:bg-gray-200"> |
| <i class="fas fa-search mr-1"></i> Search |
| </button> |
| </div> |
| </div> |
| |
| <div class="overflow-auto" style="max-height: 500px;"> |
| <table class="min-w-full divide-y divide-gray-200"> |
| <thead class="bg-gray-50"> |
| <tr> |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Field Name</th> |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Extracted Value</th> |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Confidence</th> |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> |
| </tr> |
| </thead> |
| <tbody id="extractedFieldsTable" class="bg-white divide-y divide-gray-200"> |
| |
| </tbody> |
| </table> |
| </div> |
| |
| <div class="mt-4 flex justify-between items-center"> |
| <div class="text-sm text-gray-500"> |
| Showing <span id="showingStart">1</span> to <span id="showingEnd">10</span> of <span id="totalFields">24</span> fields |
| </div> |
| <div class="flex space-x-2"> |
| <button class="px-3 py-1 bg-gray-100 text-gray-700 rounded text-sm hover:bg-gray-200 disabled:opacity-50"> |
| <i class="fas fa-chevron-left"></i> |
| </button> |
| <button class="px-3 py-1 bg-gray-100 text-gray-700 rounded text-sm hover:bg-gray-200 disabled:opacity-50"> |
| <i class="fas fa-chevron-right"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="templateUploadSection" class="hidden bg-white rounded-lg shadow p-6 mb-8"> |
| <h3 class="text-lg font-medium text-gray-900 mb-4">Upload Template Document</h3> |
| <p class="text-sm text-gray-500 mb-6">Upload a blank or partially completed template form that you want to fill with the extracted data.</p> |
| |
| <div id="templateDropzone" class="dropzone rounded-lg p-8 text-center cursor-pointer mb-4"> |
| <div class="flex flex-col items-center justify-center"> |
| <i class="fas fa-file-alt text-4xl text-blue-500 mb-3"></i> |
| <p class="text-sm text-gray-600 mb-1">Drag and drop your template file here</p> |
| <p class="text-xs text-gray-400">or click to browse</p> |
| <input type="file" id="templateFileInput" class="hidden" accept=".pdf"> |
| </div> |
| </div> |
| <div class="text-xs text-gray-500 text-center">Supported formats: PDF (Max 20MB)</div> |
| </div> |
|
|
| |
| <div id="fieldMappingSection" class="hidden bg-white rounded-lg shadow overflow-hidden mb-8"> |
| <div class="border-b border-gray-200 px-6 py-4"> |
| <h3 class="text-lg font-medium text-gray-900">Field Mapping</h3> |
| <p class="text-sm text-gray-500">Review and adjust how fields from your source document map to the template fields</p> |
| </div> |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-0"> |
| |
| <div class="md:col-span-1 border-r border-gray-200 p-4"> |
| <h4 class="font-medium text-gray-900 mb-3">Source Fields</h4> |
| <div class="relative mb-3"> |
| <input type="text" placeholder="Search source fields..." class="w-full pl-8 pr-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> |
| <i class="fas fa-search absolute left-3 top-3 text-gray-400"></i> |
| </div> |
| <div class="overflow-auto" style="max-height: 500px;"> |
| <div id="sourceFieldsList" class="space-y-2"> |
| |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="md:col-span-1 border-r border-gray-200 p-4"> |
| <h4 class="font-medium text-gray-900 mb-3 text-center">Field Mapping</h4> |
| <div class="flex flex-col items-center justify-center h-full"> |
| <div class="w-full max-w-xs"> |
| <div class="bg-blue-50 rounded-lg p-6 text-center"> |
| <i class="fas fa-random text-3xl text-blue-500 mb-3"></i> |
| <p class="text-sm text-gray-700 mb-3">Drag fields between columns to create custom mappings</p> |
| <p class="text-xs text-gray-500">Auto-mapped fields are shown in green</p> |
| </div> |
| <div class="mt-6"> |
| <h5 class="text-sm font-medium text-gray-700 mb-2">Mapping Confidence</h5> |
| <div class="bg-white rounded-lg shadow p-4"> |
| <canvas id="mappingConfidenceChart" height="150"></canvas> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="md:col-span-1 p-4"> |
| <h4 class="font-medium text-gray-900 mb-3">Template Fields</h4> |
| <div class="relative mb-3"> |
| <input type="text" placeholder="Search template fields..." class="w-full pl-8 pr-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> |
| <i class="fas fa-search absolute left-3 top-3 text-gray-400"></i> |
| </div> |
| <div class="overflow-auto" style="max-height: 500px;"> |
| <div id="templateFieldsList" class="space-y-2"> |
| |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="finalOutputSection" class="hidden bg-white rounded-lg shadow overflow-hidden mb-8"> |
| <div class="border-b border-gray-200 px-6 py-4"> |
| <h3 class="text-lg font-medium text-gray-900">Final Output</h3> |
| <p class="text-sm text-gray-500">Review and download your completed document</p> |
| </div> |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-0"> |
| |
| <div class="md:col-span-1 border-r border-gray-200 p-4"> |
| <h4 class="font-medium text-gray-900 mb-3">Completed Document Preview</h4> |
| <div class="document-preview bg-gray-100 rounded-lg overflow-hidden relative"> |
| <div class="absolute top-0 left-0 right-0 bg-gray-800 text-white py-1 px-3 text-sm flex justify-between items-center"> |
| <span id="completedDocumentName">completed_document.pdf</span> |
| <div class="flex space-x-2"> |
| <button class="text-gray-300 hover:text-white"> |
| <i class="fas fa-search-plus"></i> |
| </button> |
| <button class="text-gray-300 hover:text-white"> |
| <i class="fas fa-search-minus"></i> |
| </button> |
| </div> |
| </div> |
| <div class="p-4 h-96 overflow-auto"> |
| <img id="completedDocumentPreview" src="https://via.placeholder.com/600x800?text=Completed+Document+Preview" alt="Completed document preview" class="mx-auto shadow-lg"> |
| </div> |
| <div class="absolute bottom-0 left-0 right-0 bg-gray-100 py-2 px-4 flex justify-between items-center border-t border-gray-200"> |
| <button id="prevCompletedPage" class="text-gray-600 hover:text-blue-600 disabled:text-gray-300"> |
| <i class="fas fa-chevron-left mr-1"></i> Previous |
| </button> |
| <span class="text-sm text-gray-600">Page <span id="currentCompletedPage">1</span> of <span id="totalCompletedPages">3</span></span> |
| <button id="nextCompletedPage" class="text-gray-600 hover:text-blue-600 disabled:text-gray-300"> |
| Next <i class="fas fa-chevron-right ml-1"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="md:col-span-1 p-4"> |
| <h4 class="font-medium text-gray-900 mb-3">Export Options</h4> |
| |
| <div class="space-y-4"> |
| <div class="bg-gray-50 rounded-lg p-4 border border-gray-200"> |
| <div class="flex items-start"> |
| <div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600"> |
| <i class="fas fa-file-pdf"></i> |
| </div> |
| <div class="ml-3"> |
| <h5 class="text-sm font-medium text-gray-900">PDF Document</h5> |
| <p class="text-sm text-gray-500">Download the filled-in PDF document</p> |
| <div class="mt-2"> |
| <button id="downloadPdf" class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"> |
| <i class="fas fa-download mr-1"></i> Download PDF |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="bg-gray-50 rounded-lg p-4 border border-gray-200"> |
| <div class="flex items-start"> |
| <div class="flex-shrink-0 h-10 w-10 rounded-full bg-green-100 flex items-center justify-center text-green-600"> |
| <i class="fas fa-file-code"></i> |
| </div> |
| <div class="ml-3"> |
| <h5 class="text-sm font-medium text-gray-900">JSON Data</h5> |
| <p class="text-sm text-gray-500">Export the structured data in JSON format</p> |
| <div class="mt-2"> |
| <button id="downloadJson" class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded shadow-sm text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"> |
| <i class="fas fa-download mr-1"></i> Download JSON |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="bg-gray-50 rounded-lg p-4 border border-gray-200"> |
| <div class="flex items-start"> |
| <div class="flex-shrink-0 h-10 w-10 rounded-full bg-purple-100 flex items-center justify-center text-purple-600"> |
| <i class="fas fa-file-csv"></i> |
| </div> |
| <div class="ml-3"> |
| <h5 class="text-sm font-medium text-gray-900">CSV Data</h5> |
| <p class="text-sm text-gray-500">Export the data in tabular CSV format</p> |
| <div class="mt-2"> |
| <button id="downloadCsv" class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded shadow-sm text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500"> |
| <i class="fas fa-download mr-1"></i> Download CSV |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="mt-6 pt-4 border-t border-gray-200"> |
| <h5 class="text-sm font-medium text-gray-900 mb-2">Processing Summary</h5> |
| <div class="bg-white rounded-lg shadow p-4"> |
| <div class="grid grid-cols-2 gap-4"> |
| <div> |
| <p class="text-xs text-gray-500">Source Fields</p> |
| <p class="font-medium">24</p> |
| </div> |
| <div> |
| <p class="text-xs text-gray-500">Template Fields</p> |
| <p class="font-medium">18</p> |
| </div> |
| <div> |
| <p class="text-xs text-gray-500">Auto-mapped</p> |
| <p class="font-medium text-green-600">16 (89%)</p> |
| </div> |
| <div> |
| <p class="text-xs text-gray-500">Manual Mappings</p> |
| <p class="font-medium">2</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="flex justify-between"> |
| <button id="backButton" class="hidden px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"> |
| <i class="fas fa-arrow-left mr-2"></i> Back |
| </button> |
| <button id="nextButton" class="px-4 py-2 border border-transparent rounded-md text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"> |
| Continue <i class="fas fa-arrow-right ml-2"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| const extractedFields = [ |
| { id: 1, name: "Invoice Number", value: "INV-2023-0042", confidence: 98, page: 1, boundingBox: "100,200,300,220" }, |
| { id: 2, name: "Invoice Date", value: "2023-05-15", confidence: 95, page: 1, boundingBox: "100,250,300,270" }, |
| { id: 3, name: "Due Date", value: "2023-06-14", confidence: 92, page: 1, boundingBox: "100,300,300,320" }, |
| { id: 4, name: "Customer Name", value: "Acme Corporation", confidence: 97, page: 1, boundingBox: "100,350,300,370" }, |
| { id: 5, name: "Customer Address", value: "123 Business Ave, Suite 450", confidence: 90, page: 1, boundingBox: "100,400,300,450" }, |
| { id: 6, name: "Customer ID", value: "ACME-0042", confidence: 88, page: 1, boundingBox: "100,500,300,520" }, |
| { id: 7, name: "Subtotal", value: "$1,250.00", confidence: 96, page: 2, boundingBox: "400,200,500,220" }, |
| { id: 8, name: "Tax (8.25%)", value: "$103.13", confidence: 94, page: 2, boundingBox: "400,250,500,270" }, |
| { id: 9, name: "Total Amount", value: "$1,353.13", confidence: 99, page: 2, boundingBox: "400,300,500,320" }, |
| { id: 10, name: "Payment Terms", value: "Net 30", confidence: 85, page: 2, boundingBox: "400,350,500,370" }, |
| ]; |
| |
| const templateFields = [ |
| { id: 't1', name: "Invoice No.", mappedTo: 1, confidence: 95 }, |
| { id: 't2', name: "Date Issued", mappedTo: 2, confidence: 90 }, |
| { id: 't3', name: "Payment Due Date", mappedTo: 3, confidence: 88 }, |
| { id: 't4', name: "Client Name", mappedTo: 4, confidence: 93 }, |
| { id: 't5', name: "Client Address", mappedTo: 5, confidence: 85 }, |
| { id: 't6', name: "Client Reference", mappedTo: 6, confidence: 80 }, |
| { id: 't7', name: "Subtotal Amount", mappedTo: 7, confidence: 96 }, |
| { id: 't8', name: "Tax Amount", mappedTo: 8, confidence: 94 }, |
| { id: 't9', name: "Total Due", mappedTo: 9, confidence: 97 }, |
| { id: 't10', name: "Payment Terms", mappedTo: 10, confidence: 82 }, |
| { id: 't11', name: "Bank Account", mappedTo: null, confidence: null }, |
| { id: 't12', name: "Payment Instructions", mappedTo: null, confidence: null }, |
| ]; |
| |
| |
| let currentStep = 1; |
| let currentPage = 1; |
| let totalPages = 3; |
| let currentCompletedPage = 1; |
| let totalCompletedPages = 3; |
| let sourceFileName = ''; |
| let templateFileName = ''; |
| |
| |
| const sourceDropzone = document.getElementById('sourceDropzone'); |
| const sourceFileInput = document.getElementById('sourceFileInput'); |
| const templateDropzone = document.getElementById('templateDropzone'); |
| const templateFileInput = document.getElementById('templateFileInput'); |
| const processingStatus = document.getElementById('processingStatus'); |
| const extractedDataPreview = document.getElementById('extractedDataPreview'); |
| const templateUploadSection = document.getElementById('templateUploadSection'); |
| const fieldMappingSection = document.getElementById('fieldMappingSection'); |
| const finalOutputSection = document.getElementById('finalOutputSection'); |
| const nextButton = document.getElementById('nextButton'); |
| const backButton = document.getElementById('backButton'); |
| const extractedFieldsTable = document.getElementById('extractedFieldsTable'); |
| const sourceFieldsList = document.getElementById('sourceFieldsList'); |
| const templateFieldsList = document.getElementById('templateFieldsList'); |
| const documentPreviewImage = document.getElementById('documentPreviewImage'); |
| const currentPageSpan = document.getElementById('currentPage'); |
| const totalPagesSpan = document.getElementById('totalPages'); |
| const prevPageButton = document.getElementById('prevPage'); |
| const nextPageButton = document.getElementById('nextPage'); |
| const currentCompletedPageSpan = document.getElementById('currentCompletedPage'); |
| const totalCompletedPagesSpan = document.getElementById('totalCompletedPages'); |
| const prevCompletedPageButton = document.getElementById('prevCompletedPage'); |
| const nextCompletedPageButton = document.getElementById('nextCompletedPage'); |
| const showingStartSpan = document.getElementById('showingStart'); |
| const showingEndSpan = document.getElementById('showingEnd'); |
| const totalFieldsSpan = document.getElementById('totalFields'); |
| const sourceFileNameSpan = document.getElementById('sourceFileName'); |
| const completedDocumentNameSpan = document.getElementById('completedDocumentName'); |
| const sourceUploadSection = document.getElementById('sourceUploadSection'); |
| const downloadPdfButton = document.getElementById('downloadPdf'); |
| const downloadJsonButton = document.getElementById('downloadJson'); |
| const downloadCsvButton = document.getElementById('downloadCsv'); |
| |
| |
| function init() { |
| |
| sourceDropzone.addEventListener('click', () => sourceFileInput.click()); |
| sourceFileInput.addEventListener('change', handleSourceFileUpload); |
| |
| templateDropzone.addEventListener('click', () => templateFileInput.click()); |
| templateFileInput.addEventListener('change', handleTemplateFileUpload); |
| |
| nextButton.addEventListener('click', handleNextStep); |
| backButton.addEventListener('click', handlePreviousStep); |
| |
| prevPageButton.addEventListener('click', () => changePage(-1)); |
| nextPageButton.addEventListener('click', () => changePage(1)); |
| |
| prevCompletedPageButton.addEventListener('click', () => changeCompletedPage(-1)); |
| nextCompletedPageButton.addEventListener('click', () => changeCompletedPage(1)); |
| |
| |
| setupDragAndDrop(sourceDropzone, sourceFileInput); |
| setupDragAndDrop(templateDropzone, templateFileInput); |
| |
| |
| showingStartSpan.textContent = '1'; |
| showingEndSpan.textContent = '10'; |
| totalFieldsSpan.textContent = extractedFields.length; |
| |
| |
| currentPageSpan.textContent = currentPage; |
| totalPagesSpan.textContent = totalPages; |
| currentCompletedPageSpan.textContent = currentCompletedPage; |
| totalCompletedPagesSpan.textContent = totalCompletedPages; |
| |
| |
| prevPageButton.disabled = true; |
| prevCompletedPageButton.disabled = true; |
| |
| |
| downloadPdfButton.addEventListener('click', exportPdf); |
| downloadJsonButton.addEventListener('click', exportJson); |
| downloadCsvButton.addEventListener('click', exportCsv); |
| } |
| |
| |
| function setupDragAndDrop(dropzone, fileInput) { |
| dropzone.addEventListener('dragover', (e) => { |
| e.preventDefault(); |
| dropzone.classList.add('active'); |
| }); |
| |
| dropzone.addEventListener('dragleave', () => { |
| dropzone.classList.remove('active'); |
| }); |
| |
| dropzone.addEventListener('drop', (e) => { |
| e.preventDefault(); |
| dropzone.classList.remove('active'); |
| |
| if (e.dataTransfer.files.length) { |
| fileInput.files = e.dataTransfer.files; |
| if (fileInput === sourceFileInput) { |
| handleSourceFileUpload(); |
| } else { |
| handleTemplateFileUpload(); |
| } |
| } |
| }); |
| } |
| |
| |
| function handleSourceFileUpload() { |
| if (sourceFileInput.files.length) { |
| const file = sourceFileInput.files[0]; |
| sourceFileName = file.name; |
| sourceFileNameSpan.textContent = sourceFileName; |
| console.log('Source file uploaded:', file.name); |
| |
| |
| processingStatus.classList.remove('hidden'); |
| sourceUploadSection.classList.add('hidden'); |
| |
| |
| setTimeout(() => { |
| processingStatus.classList.add('hidden'); |
| extractedDataPreview.classList.remove('hidden'); |
| populateExtractedFieldsTable(); |
| updateProcessSteps(2); |
| |
| |
| templateUploadSection.classList.remove('hidden'); |
| }, 3000); |
| } |
| } |
| |
| |
| function handleTemplateFileUpload() { |
| if (templateFileInput.files.length) { |
| const file = templateFileInput.files[0]; |
| templateFileName = file.name; |
| completedDocumentNameSpan.textContent = `filled_${templateFileName}`; |
| console.log('Template file uploaded:', file.name); |
| |
| |
| templateUploadSection.classList.add('hidden'); |
| fieldMappingSection.classList.remove('hidden'); |
| populateFieldMappingLists(); |
| renderMappingConfidenceChart(); |
| updateProcessSteps(3); |
| } |
| } |
| |
| |
| function populateExtractedFieldsTable() { |
| extractedFieldsTable.innerHTML = ''; |
| |
| extractedFields.forEach(field => { |
| const row = document.createElement('tr'); |
| row.className = field.page === currentPage ? 'bg-blue-50' : ''; |
| row.dataset.id = field.id; |
| row.dataset.page = field.page; |
| |
| row.innerHTML = ` |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${field.name}</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${field.value}</td> |
| <td class="px-6 py-4 whitespace-nowrap"> |
| <div class="flex items-center"> |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> |
| <div class="bg-blue-600 h-2.5 rounded-full" style="width: ${field.confidence}%"></div> |
| </div> |
| <span class="ml-2 text-xs font-medium text-gray-700">${field.confidence}%</span> |
| </div> |
| </td> |
| <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> |
| <button class="text-blue-600 hover:text-blue-900 mr-3 edit-field"> |
| <i class="fas fa-edit"></i> |
| </button> |
| <button class="text-red-600 hover:text-red-900 delete-field"> |
| <i class="fas fa-trash"></i> |
| </button> |
| </td> |
| `; |
| |
| extractedFieldsTable.appendChild(row); |
| }); |
| |
| |
| document.querySelectorAll('.edit-field').forEach(button => { |
| button.addEventListener('click', (e) => { |
| const row = e.target.closest('tr'); |
| const fieldId = parseInt(row.dataset.id); |
| editField(fieldId); |
| }); |
| }); |
| |
| document.querySelectorAll('.delete-field').forEach(button => { |
| button.addEventListener('click', (e) => { |
| const row = e.target.closest('tr'); |
| const fieldId = parseInt(row.dataset.id); |
| deleteField(fieldId); |
| }); |
| }); |
| } |
| |
| |
| function populateFieldMappingLists() { |
| sourceFieldsList.innerHTML = ''; |
| templateFieldsList.innerHTML = ''; |
| |
| |
| extractedFields.forEach(field => { |
| const fieldElement = document.createElement('div'); |
| fieldElement.className = 'flex items-center justify-between p-2 border border-gray-200 rounded-md cursor-move'; |
| fieldElement.dataset.id = field.id; |
| fieldElement.draggable = true; |
| |
| fieldElement.innerHTML = ` |
| <div> |
| <div class="text-sm font-medium text-gray-700">${field.name}</div> |
| <div class="text-xs text-gray-500">${field.value}</div> |
| </div> |
| <div class="text-xs text-gray-400"> |
| <i class="fas fa-grip-vertical"></i> |
| </div> |
| `; |
| |
| sourceFieldsList.appendChild(fieldElement); |
| |
| |
| fieldElement.addEventListener('dragstart', (e) => { |
| e.dataTransfer.setData('text/plain', `source:${field.id}`); |
| }); |
| }); |
| |
| |
| templateFields.forEach(field => { |
| const fieldElement = document.createElement('div'); |
| fieldElement.className = `flex items-center justify-between p-2 border rounded-md cursor-move ${field.mappedTo ? 'border-green-200 bg-green-50' : 'border-gray-200'}`; |
| fieldElement.dataset.id = field.id; |
| fieldElement.draggable = true; |
| |
| fieldElement.innerHTML = ` |
| <div> |
| <div class="text-sm font-medium text-gray-700">${field.name}</div> |
| ${field.mappedTo ? |
| `<div class="text-xs text-green-600">Mapped to: ${extractedFields.find(f => f.id === field.mappedTo).name}</div>` : |
| `<div class="text-xs text-gray-500">Not mapped</div>`} |
| </div> |
| <div class="text-xs ${field.mappedTo ? 'text-green-500' : 'text-gray-400'}"> |
| <i class="fas fa-grip-vertical"></i> |
| </div> |
| `; |
| |
| templateFieldsList.appendChild(fieldElement); |
| |
| |
| fieldElement.addEventListener('dragstart', (e) => { |
| e.dataTransfer.setData('text/plain', `template:${field.id}`); |
| }); |
| |
| |
| fieldElement.addEventListener('dragover', (e) => { |
| e.preventDefault(); |
| }); |
| |
| fieldElement.addEventListener('drop', (e) => { |
| e.preventDefault(); |
| const data = e.dataTransfer.getData('text/plain'); |
| const [type, id] = data.split(':'); |
| |
| if (type === 'source') { |
| |
| const sourceFieldId = parseInt(id); |
| const templateFieldId = field.id; |
| mapFields(sourceFieldId, templateFieldId); |
| } |
| }); |
| }); |
| } |
| |
| |
| function renderMappingConfidenceChart() { |
| const ctx = document.getElementById('mappingConfidenceChart').getContext('2d'); |
| const mappedFields = templateFields.filter(f => f.mappedTo !== null); |
| const unmappedFields = templateFields.filter(f => f.mappedTo === null); |
| |
| new Chart(ctx, { |
| type: 'doughnut', |
| data: { |
| labels: ['High Confidence', 'Medium Confidence', 'Low Confidence', 'Unmapped'], |
| datasets: [{ |
| data: [ |
| mappedFields.filter(f => f.confidence >= 90).length, |
| mappedFields.filter(f => f.confidence >= 70 && f.confidence < 90).length, |
| mappedFields.filter(f => f.confidence < 70).length, |
| unmappedFields.length |
| ], |
| backgroundColor: [ |
| '#10B981', |
| '#3B82F6', |
| '#F59E0B', |
| '#EF4444' |
| ], |
| borderWidth: 0 |
| }] |
| }, |
| options: { |
| cutout: '70%', |
| plugins: { |
| legend: { |
| position: 'bottom', |
| labels: { |
| boxWidth: 12, |
| padding: 20 |
| } |
| } |
| } |
| } |
| }); |
| } |
| |
| |
| function mapFields(sourceFieldId, templateFieldId) { |
| const templateField = templateFields.find(f => f.id === templateFieldId); |
| templateField.mappedTo = sourceFieldId; |
| |
| |
| const sourceField = extractedFields.find(f => f.id === sourceFieldId); |
| const sourceName = sourceField.name.toLowerCase(); |
| const templateName = templateField.name.toLowerCase(); |
| |
| |
| let similarity = 0; |
| if (sourceName.includes('invoice') && templateName.includes('invoice')) similarity += 30; |
| if (sourceName.includes('date') && templateName.includes('date')) similarity += 30; |
| if (sourceName.includes('customer') && templateName.includes('client')) similarity += 20; |
| if (sourceName.includes('total') && templateName.includes('total')) similarity += 20; |
| |
| templateField.confidence = Math.min(100, similarity + 50); |
| |
| |
| populateFieldMappingLists(); |
| |
| |
| const chartCanvas = document.getElementById('mappingConfidenceChart'); |
| chartCanvas.innerHTML = ''; |
| chartCanvas.width = chartCanvas.offsetWidth; |
| chartCanvas.height = chartCanvas.offsetHeight; |
| renderMappingConfidenceChart(); |
| } |
| |
| |
| function editField(fieldId) { |
| const field = extractedFields.find(f => f.id === fieldId); |
| const newValue = prompt(`Edit field: ${field.name}\nCurrent value: ${field.value}`, field.value); |
| if (newValue !== null) { |
| field.value = newValue; |
| populateExtractedFieldsTable(); |
| } |
| } |
| |
| |
| function deleteField(fieldId) { |
| if (confirm('Are you sure you want to delete this field?')) { |
| const index = extractedFields.findIndex(f => f.id === fieldId); |
| if (index !== -1) { |
| extractedFields.splice(index, 1); |
| populateExtractedFieldsTable(); |
| } |
| } |
| } |
| |
| |
| function changePage(delta) { |
| const newPage = currentPage + delta; |
| if (newPage >= 1 && newPage <= totalPages) { |
| currentPage = newPage; |
| currentPageSpan.textContent = currentPage; |
| |
| |
| documentPreviewImage.src = `https://via.placeholder.com/600x800?text=Document+Page+${currentPage}`; |
| |
| |
| document.querySelectorAll('#extractedFieldsTable tr').forEach(row => { |
| if (parseInt(row.dataset.page) === currentPage) { |
| row.classList.add('bg-blue-50'); |
| } else { |
| row.classList.remove('bg-blue-50'); |
| } |
| }); |
| |
| |
| prevPageButton.disabled = currentPage === 1; |
| nextPageButton.disabled = currentPage === totalPages; |
| |
| |
| documentPreviewImage.classList.add('page-turner'); |
| setTimeout(() => { |
| documentPreviewImage.classList.remove('page-turner'); |
| }, 500); |
| } |
| } |
| |
| |
| function changeCompletedPage(delta) { |
| const newPage = currentCompletedPage + delta; |
| if (newPage >= 1 && newPage <= totalCompletedPages) { |
| currentCompletedPage = newPage; |
| currentCompletedPageSpan.textContent = currentCompletedPage; |
| |
| |
| document.getElementById('completedDocumentPreview').src = `https://via.placeholder.com/600x800?text=Completed+Page+${currentCompletedPage}`; |
| |
| |
| prevCompletedPageButton.disabled = currentCompletedPage === 1; |
| nextCompletedPageButton.disabled = currentCompletedPage === totalCompletedPages; |
| |
| |
| document.getElementById('completedDocumentPreview').classList.add('page-turner'); |
| setTimeout(() => { |
| document.getElementById('completedDocumentPreview').classList.remove('page-turner'); |
| }, 500); |
| } |
| } |
| |
| |
| function handleNextStep() { |
| if (currentStep === 1) { |
| |
| |
| } else if (currentStep === 2) { |
| |
| } else if (currentStep === 3) { |
| |
| fieldMappingSection.classList.add('hidden'); |
| finalOutputSection.classList.remove('hidden'); |
| nextButton.textContent = 'Finish'; |
| updateProcessSteps(4); |
| } else if (currentStep === 4) { |
| |
| alert('Document processing completed!'); |
| |
| } |
| } |
| |
| |
| function handlePreviousStep() { |
| if (currentStep === 2) { |
| |
| templateUploadSection.classList.add('hidden'); |
| extractedDataPreview.classList.add('hidden'); |
| sourceUploadSection.classList.remove('hidden'); |
| backButton.classList.add('hidden'); |
| nextButton.textContent = 'Continue'; |
| updateProcessSteps(1); |
| } else if (currentStep === 3) { |
| |
| fieldMappingSection.classList.add('hidden'); |
| templateUploadSection.classList.remove('hidden'); |
| updateProcessSteps(2); |
| } else if (currentStep === 4) { |
| |
| finalOutputSection.classList.add('hidden'); |
| fieldMappingSection.classList.remove('hidden'); |
| nextButton.textContent = 'Continue'; |
| updateProcessSteps(3); |
| } |
| } |
| |
| |
| function updateProcessSteps(step) { |
| currentStep = step; |
| |
| |
| const steps = document.querySelectorAll('.flex.items-center.relative'); |
| steps.forEach((stepElement, index) => { |
| const stepNumber = index + 1; |
| const circle = stepElement.querySelector('div'); |
| const text = stepElement.querySelector('div + div'); |
| |
| if (stepNumber < step) { |
| |
| circle.classList.remove('bg-gray-200', 'text-gray-600'); |
| circle.classList.add('bg-green-500', 'text-white'); |
| text.classList.remove('text-gray-500'); |
| text.classList.add('text-green-600'); |
| } else if (stepNumber === step) { |
| |
| circle.classList.remove('bg-gray-200', 'text-gray-600'); |
| circle.classList.add('bg-blue-600', 'text-white'); |
| text.classList.remove('text-gray-500'); |
| text.classList.add('text-blue-600'); |
| } else { |
| |
| circle.classList.remove('bg-blue-600', 'bg-green-500', 'text-white'); |
| circle.classList.add('bg-gray-200', 'text-gray-600'); |
| text.classList.remove('text-blue-600', 'text-green-600'); |
| text.classList.add('text-gray-500'); |
| } |
| }); |
| |
| |
| if (step === 3) { |
| nextButton.textContent = 'Complete Processing'; |
| } else if (step === 4) { |
| nextButton.textContent = 'Finish'; |
| } else { |
| nextButton.textContent = 'Continue'; |
| } |
| |
| |
| if (step > 1) { |
| backButton.classList.remove('hidden'); |
| } else { |
| backButton.classList.add('hidden'); |
| } |
| } |
| |
| |
| function exportPdf() { |
| |
| |
| |
| |
| const mappedData = {}; |
| templateFields.forEach(templateField => { |
| if (templateField.mappedTo) { |
| const sourceField = extractedFields.find(f => f.id === templateField.mappedTo); |
| mappedData[templateField.name] = sourceField.value; |
| } |
| }); |
| |
| |
| const { jsPDF } = window.jspdf; |
| const doc = new jsPDF(); |
| |
| |
| doc.setFontSize(20); |
| doc.text('Completed Document', 105, 20, { align: 'center' }); |
| |
| |
| doc.setFontSize(12); |
| let y = 40; |
| templateFields.forEach(templateField => { |
| if (templateField.mappedTo) { |
| const sourceField = extractedFields.find(f => f.id === templateField.mappedTo); |
| doc.text(`${templateField.name}: ${sourceField.value}`, 20, y); |
| y += 10; |
| } |
| }); |
| |
| |
| doc.save(`filled_${templateFileName || 'document'}.pdf`); |
| |
| alert('PDF document generated successfully!'); |
| } |
| |
| |
| function exportJson() { |
| |
| const mappedData = {}; |
| templateFields.forEach(templateField => { |
| if (templateField.mappedTo) { |
| const sourceField = extractedFields.find(f => f.id === templateField.mappedTo); |
| mappedData[templateField.name] = { |
| value: sourceField.value, |
| confidence: templateField.confidence, |
| sourceField: sourceField.name |
| }; |
| } |
| }); |
| |
| |
| const jsonStr = JSON.stringify(mappedData, null, 2); |
| |
| |
| const blob = new Blob([jsonStr], { type: 'application/json' }); |
| saveAs(blob, `document_data_${new Date().toISOString().slice(0, 10)}.json`); |
| |
| alert('JSON data exported successfully!'); |
| } |
| |
| |
| function exportCsv() { |
| |
| let csv = 'Field Name,Value,Confidence,Source Field\n'; |
| |
| |
| templateFields.forEach(templateField => { |
| if (templateField.mappedTo) { |
| const sourceField = extractedFields.find(f => f.id === templateField.mappedTo); |
| csv += `"${templateField.name}","${sourceField.value}",${templateField.confidence},"${sourceField.name}"\n`; |
| } |
| }); |
| |
| |
| const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); |
| saveAs(blob, `document_data_${new Date().toISOString().slice(0, 10)}.csv`); |
| |
| alert('CSV data exported successfully!'); |
| } |
| |
| |
| document.addEventListener('DOMContentLoaded', init); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=NRbones/docsync" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |