Spaces:
Paused
Paused
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>ποΈ Legal Dashboard - OCR System</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| padding: 20px; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| background: white; | |
| border-radius: 20px; | |
| box-shadow: 0 20px 40px rgba(0,0,0,0.1); | |
| overflow: hidden; | |
| } | |
| .header { | |
| background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); | |
| color: white; | |
| padding: 40px; | |
| text-align: center; | |
| } | |
| .header h1 { | |
| font-size: 2.5em; | |
| margin-bottom: 10px; | |
| } | |
| .header p { | |
| font-size: 1.2em; | |
| opacity: 0.9; | |
| } | |
| .main-content { | |
| padding: 40px; | |
| } | |
| .status-section { | |
| background: #f8f9fa; | |
| border-radius: 15px; | |
| padding: 30px; | |
| margin-bottom: 30px; | |
| border-left: 5px solid #4CAF50; | |
| } | |
| .status-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 20px; | |
| margin-top: 20px; | |
| } | |
| .status-item { | |
| background: white; | |
| padding: 20px; | |
| border-radius: 10px; | |
| box-shadow: 0 2px 10px rgba(0,0,0,0.05); | |
| text-align: center; | |
| } | |
| .status-indicator { | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| display: inline-block; | |
| margin-right: 10px; | |
| } | |
| .status-healthy { background: #4CAF50; } | |
| .status-loading { background: #FF9800; } | |
| .status-error { background: #f44336; } | |
| .upload-section { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 30px; | |
| margin-bottom: 30px; | |
| } | |
| .upload-card { | |
| background: #f8f9fa; | |
| border-radius: 15px; | |
| padding: 30px; | |
| text-align: center; | |
| border: 2px dashed #ddd; | |
| transition: all 0.3s ease; | |
| } | |
| .upload-card:hover { | |
| border-color: #4CAF50; | |
| background: #f0f8f0; | |
| } | |
| .upload-card h3 { | |
| color: #333; | |
| margin-bottom: 15px; | |
| font-size: 1.5em; | |
| } | |
| .file-input { | |
| margin: 20px 0; | |
| } | |
| .file-input input[type="file"] { | |
| width: 100%; | |
| padding: 15px; | |
| border: 1px solid #ddd; | |
| border-radius: 10px; | |
| font-size: 16px; | |
| } | |
| .upload-btn { | |
| background: #4CAF50; | |
| color: white; | |
| border: none; | |
| padding: 15px 30px; | |
| border-radius: 10px; | |
| font-size: 16px; | |
| cursor: pointer; | |
| transition: background 0.3s ease; | |
| width: 100%; | |
| } | |
| .upload-btn:hover { | |
| background: #45a049; | |
| } | |
| .upload-btn:disabled { | |
| background: #ccc; | |
| cursor: not-allowed; | |
| } | |
| .result-section { | |
| background: #f8f9fa; | |
| border-radius: 15px; | |
| padding: 30px; | |
| margin-top: 30px; | |
| display: none; | |
| } | |
| .result-text { | |
| background: white; | |
| border: 1px solid #ddd; | |
| border-radius: 10px; | |
| padding: 20px; | |
| max-height: 400px; | |
| overflow-y: auto; | |
| font-family: 'Courier New', monospace; | |
| white-space: pre-wrap; | |
| margin-top: 15px; | |
| } | |
| .nav-links { | |
| display: flex; | |
| gap: 15px; | |
| justify-content: center; | |
| flex-wrap: wrap; | |
| margin-top: 30px; | |
| } | |
| .nav-link { | |
| background: #2196F3; | |
| color: white; | |
| text-decoration: none; | |
| padding: 12px 25px; | |
| border-radius: 25px; | |
| transition: all 0.3s ease; | |
| font-weight: 500; | |
| } | |
| .nav-link:hover { | |
| background: #1976D2; | |
| transform: translateY(-2px); | |
| } | |
| .loading { | |
| display: inline-block; | |
| width: 20px; | |
| height: 20px; | |
| border: 3px solid #f3f3f3; | |
| border-top: 3px solid #4CAF50; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| @media (max-width: 768px) { | |
| .upload-section { | |
| grid-template-columns: 1fr; | |
| } | |
| .nav-links { | |
| flex-direction: column; | |
| align-items: center; | |
| } | |
| .header h1 { | |
| font-size: 2em; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>ποΈ Legal Dashboard</h1> | |
| <p>Advanced OCR & Document Processing System</p> | |
| </div> | |
| <div class="main-content"> | |
| <div class="status-section"> | |
| <h2>π System Status</h2> | |
| <div class="status-grid"> | |
| <div class="status-item"> | |
| <div id="api-status"> | |
| <span class="status-indicator status-loading"></span> | |
| <strong>API Server</strong><br> | |
| <span id="api-text">Checking...</span> | |
| </div> | |
| </div> | |
| <div class="status-item"> | |
| <div id="pdf-status"> | |
| <span class="status-indicator status-loading"></span> | |
| <strong>PDF Processing</strong><br> | |
| <span id="pdf-text">Checking...</span> | |
| </div> | |
| </div> | |
| <div class="status-item"> | |
| <div id="ocr-status"> | |
| <span class="status-indicator status-loading"></span> | |
| <strong>OCR Models</strong><br> | |
| <span id="ocr-text">Loading...</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="upload-section"> | |
| <div class="upload-card"> | |
| <h3>π PDF Document</h3> | |
| <p>Upload a PDF file to extract text using advanced OCR</p> | |
| <div class="file-input"> | |
| <input type="file" id="pdfFile" accept=".pdf"> | |
| </div> | |
| <button class="upload-btn" onclick="uploadPDF()" id="pdfBtn"> | |
| Extract Text from PDF | |
| </button> | |
| </div> | |
| <div class="upload-card"> | |
| <h3>πΌοΈ Image Document</h3> | |
| <p>Upload an image to extract text using TrOCR AI</p> | |
| <div class="file-input"> | |
| <input type="file" id="imageFile" accept=".jpg,.jpeg,.png,.bmp,.tiff"> | |
| </div> | |
| <button class="upload-btn" onclick="uploadImage()" id="imageBtn"> | |
| Extract Text from Image | |
| </button> | |
| </div> | |
| </div> | |
| <div class="result-section" id="resultSection"> | |
| <h3>π Extraction Results</h3> | |
| <div id="resultInfo"></div> | |
| <div class="result-text" id="resultText"></div> | |
| </div> | |
| <div class="nav-links"> | |
| <a href="/api/docs" class="nav-link">π API Documentation</a> | |
| <a href="/health" class="nav-link">β€οΈ Health Check</a> | |
| <a href="/system/status" class="nav-link">π System Status</a> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Check system status on load | |
| async function checkSystemStatus() { | |
| try { | |
| const response = await fetch('/health'); | |
| const data = await response.json(); | |
| // Update API status | |
| updateStatus('api', 'healthy', 'Running'); | |
| // Update PDF status | |
| updateStatus('pdf', data.services.pdf_processing ? 'healthy' : 'error', | |
| data.services.pdf_processing ? 'Available' : 'Not Available'); | |
| // Update OCR status | |
| updateStatus('ocr', data.services.ocr_model_loaded ? 'healthy' : 'loading', | |
| data.services.ocr_model_loaded ? 'Ready' : 'Loading...'); | |
| } catch (error) { | |
| updateStatus('api', 'error', 'Offline'); | |
| updateStatus('pdf', 'error', 'Unknown'); | |
| updateStatus('ocr', 'error', 'Unknown'); | |
| } | |
| } | |
| function updateStatus(service, status, text) { | |
| const indicator = document.querySelector(`#${service}-status .status-indicator`); | |
| const textElement = document.getElementById(`${service}-text`); | |
| indicator.className = `status-indicator status-${status}`; | |
| textElement.textContent = text; | |
| } | |
| async function uploadPDF() { | |
| const fileInput = document.getElementById('pdfFile'); | |
| const btn = document.getElementById('pdfBtn'); | |
| if (!fileInput.files[0]) { | |
| alert('Please select a PDF file'); | |
| return; | |
| } | |
| const formData = new FormData(); | |
| formData.append('file', fileInput.files[0]); | |
| btn.disabled = true; | |
| btn.innerHTML = '<span class="loading"></span> Processing PDF...'; | |
| try { | |
| const response = await fetch('/api/ocr/extract-pdf', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| const result = await response.json(); | |
| showResult(result); | |
| } catch (error) { | |
| alert('Error processing PDF: ' + error.message); | |
| } finally { | |
| btn.disabled = false; | |
| btn.innerHTML = 'Extract Text from PDF'; | |
| } | |
| } | |
| async function uploadImage() { | |
| const fileInput = document.getElementById('imageFile'); | |
| const btn = document.getElementById('imageBtn'); | |
| if (!fileInput.files[0]) { | |
| alert('Please select an image file'); | |
| return; | |
| } | |
| const formData = new FormData(); | |
| formData.append('file', fileInput.files[0]); | |
| btn.disabled = true; | |
| btn.innerHTML = '<span class="loading"></span> Processing Image...'; | |
| try { | |
| const response = await fetch('/api/ocr/extract-image', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| const result = await response.json(); | |
| showResult(result); | |
| } catch (error) { | |
| alert('Error processing image: ' + error.message); | |
| } finally { | |
| btn.disabled = false; | |
| btn.innerHTML = 'Extract Text from Image'; | |
| } | |
| } | |
| function showResult(result) { | |
| const resultSection = document.getElementById('resultSection'); | |
| const resultInfo = document.getElementById('resultInfo'); | |
| const resultText = document.getElementById('resultText'); | |
| resultSection.style.display = 'block'; | |
| if (result.success) { | |
| resultInfo.innerHTML = ` | |
| <p><strong>β Success!</strong> Method: ${result.method}</p> | |
| <p><strong>Metadata:</strong> ${JSON.stringify(result.metadata, null, 2)}</p> | |
| `; | |
| resultText.textContent = result.text || 'No text extracted'; | |
| } else { | |
| resultInfo.innerHTML = `<p><strong>β Error:</strong> ${result.metadata.error}</p>`; | |
| resultText.textContent = 'Processing failed'; | |
| } | |
| resultSection.scrollIntoView({ behavior: 'smooth' }); | |
| } | |
| // Initialize | |
| checkSystemStatus(); | |
| // Refresh status every 30 seconds | |
| setInterval(checkSystemStatus, 30000); | |
| </script> | |
| </body> | |
| </html> |