docsync / index.html
NRbones's picture
Add 2 files
e350624 verified
<!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/[email protected]/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">
<!-- Sidebar -->
<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>
<!-- Main content -->
<div class="flex flex-col flex-1 overflow-hidden">
<!-- Top navigation -->
<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>
<!-- Main content area -->
<div class="flex-1 overflow-auto p-6">
<div class="max-w-7xl mx-auto">
<!-- Process steps -->
<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>
<!-- Upload source document -->
<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>
<!-- Processing status (hidden initially) -->
<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>
<!-- Extracted data preview (hidden initially) -->
<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">
<!-- Document preview -->
<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>
<!-- Extracted fields -->
<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">
<!-- Sample rows will be inserted here by JavaScript -->
</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>
<!-- Upload template document (hidden initially) -->
<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>
<!-- Field mapping (hidden initially) -->
<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">
<!-- Source fields -->
<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">
<!-- Source fields will be inserted here by JavaScript -->
</div>
</div>
</div>
<!-- Mapping visualization -->
<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>
<!-- Template fields -->
<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">
<!-- Template fields will be inserted here by JavaScript -->
</div>
</div>
</div>
</div>
</div>
<!-- Final output (hidden initially) -->
<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">
<!-- Document preview -->
<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>
<!-- Export options -->
<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>
<!-- Navigation buttons -->
<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>
// Sample data for demonstration
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 },
];
// Current state
let currentStep = 1;
let currentPage = 1;
let totalPages = 3;
let currentCompletedPage = 1;
let totalCompletedPages = 3;
let sourceFileName = '';
let templateFileName = '';
// DOM elements
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');
// Initialize the app
function init() {
// Set up event listeners
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));
// Set up drag and drop
setupDragAndDrop(sourceDropzone, sourceFileInput);
setupDragAndDrop(templateDropzone, templateFileInput);
// Initialize showing fields
showingStartSpan.textContent = '1';
showingEndSpan.textContent = '10';
totalFieldsSpan.textContent = extractedFields.length;
// Initialize page numbers
currentPageSpan.textContent = currentPage;
totalPagesSpan.textContent = totalPages;
currentCompletedPageSpan.textContent = currentCompletedPage;
totalCompletedPagesSpan.textContent = totalCompletedPages;
// Disable previous page buttons initially
prevPageButton.disabled = true;
prevCompletedPageButton.disabled = true;
// Set up export buttons
downloadPdfButton.addEventListener('click', exportPdf);
downloadJsonButton.addEventListener('click', exportJson);
downloadCsvButton.addEventListener('click', exportCsv);
}
// Set up drag and drop functionality
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();
}
}
});
}
// Handle source file upload
function handleSourceFileUpload() {
if (sourceFileInput.files.length) {
const file = sourceFileInput.files[0];
sourceFileName = file.name;
sourceFileNameSpan.textContent = sourceFileName;
console.log('Source file uploaded:', file.name);
// Show processing status
processingStatus.classList.remove('hidden');
sourceUploadSection.classList.add('hidden');
// Simulate processing
setTimeout(() => {
processingStatus.classList.add('hidden');
extractedDataPreview.classList.remove('hidden');
populateExtractedFieldsTable();
updateProcessSteps(2);
// Enable template upload section
templateUploadSection.classList.remove('hidden');
}, 3000);
}
}
// Handle template file upload
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);
// Show field mapping section
templateUploadSection.classList.add('hidden');
fieldMappingSection.classList.remove('hidden');
populateFieldMappingLists();
renderMappingConfidenceChart();
updateProcessSteps(3);
}
}
// Populate extracted fields table
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);
});
// Add event listeners to edit and delete buttons
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);
});
});
}
// Populate field mapping lists
function populateFieldMappingLists() {
sourceFieldsList.innerHTML = '';
templateFieldsList.innerHTML = '';
// Source fields
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);
// Add drag start event
fieldElement.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', `source:${field.id}`);
});
});
// Template fields
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);
// Add drag start event
fieldElement.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', `template:${field.id}`);
});
// Add drop event
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') {
// Map source field to template field
const sourceFieldId = parseInt(id);
const templateFieldId = field.id;
mapFields(sourceFieldId, templateFieldId);
}
});
});
}
// Render mapping confidence chart
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
}
}
}
}
});
}
// Map source field to template field
function mapFields(sourceFieldId, templateFieldId) {
const templateField = templateFields.find(f => f.id === templateFieldId);
templateField.mappedTo = sourceFieldId;
// Calculate confidence based on field name similarity (simplified for demo)
const sourceField = extractedFields.find(f => f.id === sourceFieldId);
const sourceName = sourceField.name.toLowerCase();
const templateName = templateField.name.toLowerCase();
// Simple similarity calculation for demo
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); // Ensure we don't go over 100%
// Update the UI
populateFieldMappingLists();
// Re-render the chart
const chartCanvas = document.getElementById('mappingConfidenceChart');
chartCanvas.innerHTML = ''; // Clear old chart
chartCanvas.width = chartCanvas.offsetWidth;
chartCanvas.height = chartCanvas.offsetHeight;
renderMappingConfidenceChart();
}
// Edit field
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();
}
}
// Delete field
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();
}
}
}
// Change page in document preview
function changePage(delta) {
const newPage = currentPage + delta;
if (newPage >= 1 && newPage <= totalPages) {
currentPage = newPage;
currentPageSpan.textContent = currentPage;
// Update document preview image (simulated)
documentPreviewImage.src = `https://via.placeholder.com/600x800?text=Document+Page+${currentPage}`;
// Update extracted fields table highlighting
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');
}
});
// Update pagination buttons
prevPageButton.disabled = currentPage === 1;
nextPageButton.disabled = currentPage === totalPages;
// Add page turn animation
documentPreviewImage.classList.add('page-turner');
setTimeout(() => {
documentPreviewImage.classList.remove('page-turner');
}, 500);
}
}
// Change page in completed document preview
function changeCompletedPage(delta) {
const newPage = currentCompletedPage + delta;
if (newPage >= 1 && newPage <= totalCompletedPages) {
currentCompletedPage = newPage;
currentCompletedPageSpan.textContent = currentCompletedPage;
// Update completed document preview image (simulated)
document.getElementById('completedDocumentPreview').src = `https://via.placeholder.com/600x800?text=Completed+Page+${currentCompletedPage}`;
// Update pagination buttons
prevCompletedPageButton.disabled = currentCompletedPage === 1;
nextCompletedPageButton.disabled = currentCompletedPage === totalCompletedPages;
// Add page turn animation
document.getElementById('completedDocumentPreview').classList.add('page-turner');
setTimeout(() => {
document.getElementById('completedDocumentPreview').classList.remove('page-turner');
}, 500);
}
}
// Handle next step
function handleNextStep() {
if (currentStep === 1) {
// From upload source to upload template
// This is now handled automatically after source file processing
} else if (currentStep === 2) {
// From upload template to field mapping (handled in upload handler)
} else if (currentStep === 3) {
// From field mapping to final output
fieldMappingSection.classList.add('hidden');
finalOutputSection.classList.remove('hidden');
nextButton.textContent = 'Finish';
updateProcessSteps(4);
} else if (currentStep === 4) {
// Finish the process
alert('Document processing completed!');
// In a real app, you would reset the form or redirect
}
}
// Handle previous step
function handlePreviousStep() {
if (currentStep === 2) {
// From upload template back to upload source
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) {
// From field mapping back to upload template
fieldMappingSection.classList.add('hidden');
templateUploadSection.classList.remove('hidden');
updateProcessSteps(2);
} else if (currentStep === 4) {
// From final output back to field mapping
finalOutputSection.classList.add('hidden');
fieldMappingSection.classList.remove('hidden');
nextButton.textContent = 'Continue';
updateProcessSteps(3);
}
}
// Update process steps UI
function updateProcessSteps(step) {
currentStep = step;
// Update step indicators
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) {
// Completed 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) {
// Current 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 {
// Future step
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');
}
});
// Update next button text
if (step === 3) {
nextButton.textContent = 'Complete Processing';
} else if (step === 4) {
nextButton.textContent = 'Finish';
} else {
nextButton.textContent = 'Continue';
}
// Update back button visibility
if (step > 1) {
backButton.classList.remove('hidden');
} else {
backButton.classList.add('hidden');
}
}
// Export PDF
function exportPdf() {
// In a real app, this would use PDF-lib to create a filled PDF
// For demo purposes, we'll create a simple PDF with the data
// Create a JSON representation of the mapped data
const mappedData = {};
templateFields.forEach(templateField => {
if (templateField.mappedTo) {
const sourceField = extractedFields.find(f => f.id === templateField.mappedTo);
mappedData[templateField.name] = sourceField.value;
}
});
// Create a simple PDF (simulated)
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
// Add title
doc.setFontSize(20);
doc.text('Completed Document', 105, 20, { align: 'center' });
// Add fields
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;
}
});
// Save the PDF
doc.save(`filled_${templateFileName || 'document'}.pdf`);
alert('PDF document generated successfully!');
}
// Export JSON
function exportJson() {
// Create a JSON representation of the mapped data
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
};
}
});
// Create JSON string
const jsonStr = JSON.stringify(mappedData, null, 2);
// Create blob and download
const blob = new Blob([jsonStr], { type: 'application/json' });
saveAs(blob, `document_data_${new Date().toISOString().slice(0, 10)}.json`);
alert('JSON data exported successfully!');
}
// Export CSV
function exportCsv() {
// Create CSV header
let csv = 'Field Name,Value,Confidence,Source Field\n';
// Add rows for each mapped field
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`;
}
});
// Create blob and download
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!');
}
// Initialize the app when DOM is loaded
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>