Spaces:
Build error
Build error
Download static/index.html from Hztech/passport-static: direct link, hf CLI and curl.
- Browser
- Download file 8.33 kB
-
https://huggingface.co/spaces/Hztech/passport-static/resolve/main/static/index.html
- Command line
-
hf download hf://spaces/Hztech/passport-static/static/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Hztech/passport-static/resolve/main/static/index.html
8.33 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Passport OCR Extractor</title> | |
| <style> | |
| body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f4f7f6; color: #333; margin: 0; padding: 20px; } | |
| .container { max-width: 1000px; margin: auto; background: white; padding: 30px; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } | |
| h1 { text-align: center; color: #2c3e50; margin-bottom: 30px; } | |
| .controls { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 30px; } | |
| .control-group { display: flex; flex-direction: column; } | |
| label { font-weight: bold; margin-bottom: 8px; color: #555; } | |
| select, input[type="file"] { padding: 10px; border: 1px solid #ddd; border-radius: 6px; } | |
| button { background-color: #3498db; color: white; border: none; padding: 12px 25px; font-size: 16px; border-radius: 6px; cursor: pointer; transition: background 0.3s; margin-top: 20px; width: 100%; } | |
| button:hover { background-color: #2980b9; } | |
| button:disabled { background-color: #bdc3c7; cursor: not-allowed; } | |
| #results { margin-top: 40px; } | |
| .summary { display: flex; justify-content: space-around; margin-bottom: 20px; padding: 15px; background: #e8f4fd; border-radius: 8px; } | |
| .summary-item { text-align: center; } | |
| .summary-val { font-size: 24px; font-weight: bold; color: #2980b9; } | |
| .summary-label { font-size: 12px; color: #7f8c8d; text-transform: uppercase; } | |
| .table-container { overflow-x: auto; margin-top: 20px; border: 1px solid #eee; border-radius: 8px; } | |
| table { width: 100%; border-collapse: collapse; font-size: 14px; } | |
| th, td { padding: 12px; text-align: left; border-bottom: 1px solid #eee; } | |
| th { background-color: #f8f9fa; color: #2c3e50; } | |
| .downloads { display: flex; gap: 15px; margin-top: 20px; justify-content: center; } | |
| .download-btn { background-color: #27ae60; color: white; text-decoration: none; padding: 10px 20px; border-radius: 6px; font-weight: bold; } | |
| .download-btn:hover { background-color: #219150; } | |
| .loader { border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; width: 30px; height: 30px; animation: spin 2s linear infinite; margin: 20px auto; display: none; } | |
| @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } | |
| .checkbox-group { flex-direction: row; align-items: center; gap: 10px; cursor: pointer; } | |
| .checkbox-group input { width: auto; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>🛂 Passport OCR Extractor</h1> | |
| <div class="controls"> | |
| <div class="control-group"> | |
| <label for="files">Upload Passport Files (Images or PDFs)</label> | |
| <input type="file" id="files" multiple accept=".png,.jpg,.jpeg,.pdf,.webp"> | |
| </div> | |
| <div class="control-group"> | |
| <label for="airline">Choose Airline Format</label> | |
| <select id="airline"> | |
| <option value="Default">Default</option> | |
| <option value="Fly Dubai">Fly Dubai</option> | |
| <option value="Iraqi">Iraqi</option> | |
| <option value="Fly Baghdad">Fly Baghdad</option> | |
| </select> | |
| <label class="checkbox-group" style="margin-top: 15px;"> | |
| <input type="checkbox" id="include_all"> Include all files in export | |
| </label> | |
| </div> | |
| </div> | |
| <button id="processBtn">Extract Data</button> | |
| <div id="loader" class="loader"></div> | |
| <div id="results" style="display: none;"> | |
| <div class="summary"> | |
| <div class="summary-item"> | |
| <div id="summaryTotal" class="summary-val">0</div> | |
| <div class="summary-label">Total Files</div> | |
| </div> | |
| <div class="summary-item"> | |
| <div id="summarySuccess" class="summary-val">0</div> | |
| <div class="summary-label">Success</div> | |
| </div> | |
| <div class="summary-item"> | |
| <div id="summaryFailed" class="summary-val">0</div> | |
| <div class="summary-label">Failed</div> | |
| </div> | |
| </div> | |
| <div class="downloads"> | |
| <a id="downloadCsv" href="#" class="download-btn">Download CSV</a> | |
| <a id="downloadXlsx" href="#" class="download-btn">Download Excel</a> | |
| </div> | |
| <div class="table-container"> | |
| <table id="resultsTable"> | |
| <thead><tr id="tableHeader"></tr></thead> | |
| <tbody id="tableBody"></tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| const processBtn = document.getElementById('processBtn'); | |
| const loader = document.getElementById('loader'); | |
| const results = document.getElementById('results'); | |
| const filesInput = document.getElementById('files'); | |
| const airlineSelect = document.getElementById('airline'); | |
| const includeAllCheckbox = document.getElementById('include_all'); | |
| processBtn.addEventListener('click', async () => { | |
| const files = filesInput.files; | |
| if (files.length === 0) { | |
| alert('Please upload at least one file.'); | |
| return; | |
| } | |
| processBtn.disabled = true; | |
| loader.style.display = 'block'; | |
| results.style.display = 'none'; | |
| const formData = new FormData(); | |
| for (let i = 0; i < files.length; i++) { | |
| formData.append('files', files[i]); | |
| } | |
| formData.append('airline', airlineSelect.value); | |
| formData.append('include_all', includeAllCheckbox.checked); | |
| try { | |
| const response = await fetch('/api/process', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| if (!response.ok) throw new Error('Processing failed'); | |
| const result = await response.json(); | |
| displayResults(result); | |
| } catch (error) { | |
| alert('Error: ' + error.message); | |
| } finally { | |
| processBtn.disabled = false; | |
| loader.style.display = 'none'; | |
| } | |
| }); | |
| function displayResults(res) { | |
| results.style.display = 'block'; | |
| document.getElementById('summaryTotal').innerText = res.summary.total; | |
| document.getElementById('summarySuccess').innerText = res.summary.success; | |
| document.getElementById('summaryFailed').innerText = res.summary.failed; | |
| document.getElementById('downloadCsv').href = '/api/download/' + res.csv_id; | |
| document.getElementById('downloadXlsx').href = '/api/download/' + res.xlsx_id; | |
| const tableHeader = document.getElementById('tableHeader'); | |
| const tableBody = document.getElementById('tableBody'); | |
| tableHeader.innerHTML = ''; | |
| tableBody.innerHTML = ''; | |
| if (res.data.length > 0) { | |
| const keys = Object.keys(res.data[0]); | |
| keys.forEach(key => { | |
| const th = document.createElement('th'); | |
| th.innerText = key; | |
| tableHeader.appendChild(th); | |
| }); | |
| res.data.forEach(row => { | |
| const tr = document.createElement('tr'); | |
| keys.forEach(key => { | |
| const td = document.createElement('td'); | |
| td.innerText = row[key] || ''; | |
| tr.appendChild(td); | |
| }); | |
| tableBody.appendChild(tr); | |
| }); | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |