Madras1's picture
Upload index.html
59d8957 verified
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Notas Fiscais AI (Static)</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<script>
// CONFIGURATION: Change this URL to your deployed Backend URL (e.g., https://huggingface.co/spaces/username/space/api)
// For local testing, keep it as localhost
const API_URL = "http://localhost:8000";
</script>
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div class="bg-gray-800 text-white w-64 flex-shrink-0">
<div class="p-4 text-2xl font-bold border-b border-gray-700">
<i class="fas fa-file-invoice-dollar mr-2"></i> ERP AI
</div>
<nav class="mt-4">
<a href="#" class="block py-2.5 px-4 bg-gray-700 border-l-4 border-blue-500">
<i class="fas fa-tachometer-alt mr-2"></i> Dashboard
</a>
</nav>
</div>
<!-- Main Content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Header -->
<header class="bg-white shadow p-4 flex justify-between items-center">
<h2 class="text-xl font-semibold text-gray-800">Visão Geral</h2>
<button class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition" onclick="document.getElementById('uploadModal').classList.remove('hidden')">
<i class="fas fa-plus mr-2"></i> Nova Nota Fiscal
</button>
</header>
<!-- Content -->
<main class="flex-1 overflow-x-hidden overflow-y-auto bg-gray-100 p-6">
<!-- Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<div class="bg-white p-6 rounded-lg shadow border-l-4 border-green-500">
<div class="flex items-center">
<div class="p-3 rounded-full bg-green-100 text-green-500 mr-4">
<i class="fas fa-dollar-sign fa-2x"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Total Processado</p>
<p id="totalRevenue" class="text-2xl font-bold text-gray-800">R$ 0,00</p>
</div>
</div>
</div>
<div class="bg-white p-6 rounded-lg shadow border-l-4 border-blue-500">
<div class="flex items-center">
<div class="p-3 rounded-full bg-blue-100 text-blue-500 mr-4">
<i class="fas fa-file-alt fa-2x"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Notas Cadastradas</p>
<p id="totalInvoices" class="text-2xl font-bold text-gray-800">0</p>
</div>
</div>
</div>
</div>
<!-- Recent Invoices Table -->
<div class="bg-white shadow rounded-lg overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-semibold text-gray-800">Últimas Notas Fiscais</h3>
</div>
<table class="min-w-full leading-normal">
<thead>
<tr>
<th class="px-5 py-3 border-b-2 border-gray-200 bg-gray-100 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">ID</th>
<th class="px-5 py-3 border-b-2 border-gray-200 bg-gray-100 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Emissor</th>
<th class="px-5 py-3 border-b-2 border-gray-200 bg-gray-100 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Data</th>
<th class="px-5 py-3 border-b-2 border-gray-200 bg-gray-100 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Valor</th>
<th class="px-5 py-3 border-b-2 border-gray-200 bg-gray-100 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody id="invoicesTableBody">
<!-- JS will populate this -->
</tbody>
</table>
</div>
</main>
</div>
</div>
<!-- Upload Modal -->
<div id="uploadModal" class="fixed z-10 inset-0 overflow-y-auto hidden" aria-labelledby="modal-title" role="dialog" aria-modal="true">
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true" onclick="document.getElementById('uploadModal').classList.add('hidden')"></div>
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<h3 class="text-lg leading-6 font-medium text-gray-900">Upload de Nota Fiscal</h3>
<div class="mt-2">
<input type="file" id="fileInput" class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"/>
<div id="uploadStatus" class="mt-2 text-sm font-medium hidden"></div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" onclick="uploadFile()" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 sm:ml-3 sm:w-auto sm:text-sm">Processar</button>
<button type="button" onclick="document.getElementById('uploadModal').classList.add('hidden')" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">Cancelar</button>
</div>
</div>
</div>
</div>
<script>
// Fetch and Render Invoices
async function fetchInvoices() {
try {
const response = await fetch(`${API_URL}/invoices`);
const invoices = await response.json();
const tbody = document.getElementById('invoicesTableBody');
tbody.innerHTML = '';
let totalVal = 0;
// Sort by ID desc
invoices.sort((a, b) => b.id - a.id);
invoices.forEach(inv => {
if (inv.total_value) totalVal += inv.total_value;
const date = new Date(inv.created_at).toLocaleString('pt-BR');
const value = inv.total_value ? `R$ ${inv.total_value.toFixed(2)}` : 'R$ 0.00';
const statusColor = inv.status === 'processed' ? 'text-green-900 bg-green-200' :
(inv.status === 'failed' ? 'text-red-900 bg-red-200' : 'text-yellow-900 bg-yellow-200');
const row = `
<tr>
<td class="px-5 py-5 border-b border-gray-200 bg-white text-sm"><p class="text-gray-900 whitespace-no-wrap">#${inv.id}</p></td>
<td class="px-5 py-5 border-b border-gray-200 bg-white text-sm">
<p class="text-gray-900 whitespace-no-wrap font-medium">${inv.issuer || 'Desconhecido'}</p>
<p class="text-gray-500 text-xs">${inv.filename}</p>
</td>
<td class="px-5 py-5 border-b border-gray-200 bg-white text-sm"><p class="text-gray-900 whitespace-no-wrap">${date}</p></td>
<td class="px-5 py-5 border-b border-gray-200 bg-white text-sm"><p class="text-gray-900 whitespace-no-wrap font-bold">${value}</p></td>
<td class="px-5 py-5 border-b border-gray-200 bg-white text-sm">
<span class="relative inline-block px-3 py-1 font-semibold leading-tight ${statusColor.split(' ')[0]}">
<span aria-hidden class="absolute inset-0 opacity-50 rounded-full ${statusColor.split(' ')[1]}"></span>
<span class="relative">${inv.status}</span>
</span>
</td>
</tr>
`;
tbody.innerHTML += row;
});
document.getElementById('totalRevenue').textContent = `R$ ${totalVal.toFixed(2)}`;
document.getElementById('totalInvoices').textContent = invoices.length;
} catch (error) {
console.error("Erro ao buscar notas:", error);
}
}
// Upload Function
async function uploadFile() {
const fileInput = document.getElementById('fileInput');
const statusDiv = document.getElementById('uploadStatus');
if (!fileInput.files[0]) {
alert('Selecione um arquivo!');
return;
}
const formData = new FormData();
formData.append('file', fileInput.files[0]);
statusDiv.textContent = "Processando...";
statusDiv.classList.remove('hidden', 'text-green-600', 'text-red-600');
statusDiv.classList.add('text-blue-600');
try {
const response = await fetch(`${API_URL}/upload`, {
method: 'POST',
body: formData
});
if (response.ok) {
statusDiv.textContent = "Sucesso!";
statusDiv.classList.replace('text-blue-600', 'text-green-600');
fetchInvoices(); // Refresh list
setTimeout(() => {
document.getElementById('uploadModal').classList.add('hidden');
statusDiv.classList.add('hidden');
fileInput.value = '';
}, 1500);
} else {
throw new Error('Upload failed');
}
} catch (error) {
statusDiv.textContent = "Erro!";
statusDiv.classList.replace('text-blue-600', 'text-red-600');
}
}
// Init
fetchInvoices();
</script>
</body>
</html>