Substituído HTML estático por versão dinâmica que carrega dados do report.json via JavaScript. Benefícios: - Não precisa mais reescrever todo o HTML para atualizar - Atualização automática ao atualizar report.json - Menos risco de erro humano Fluxo atualizado: 1. Coleta dados do Supabase 2. Gera report.json 3. Dashboard carrega report.json dinamicamente
193 lines
9.7 KiB
HTML
193 lines
9.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="pt-BR">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Dashboard Financeiro</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
|
body { font-family: 'Plus Jakarta Sans', sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; padding: 0; }
|
|
.table-container { overflow-x: auto; -webkit-overflow-scrolling: touch; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container mx-auto px-4 py-6 sm:px-6 lg:px-8">
|
|
<div class="text-center mb-8">
|
|
<h1 class="text-4xl sm:text-5xl font-bold text-white mb-2">Dashboard Financeiro</h1>
|
|
<p class="text-xl text-white/90">Grupo Inova</p>
|
|
<p class="text-lg text-white/70" id="date">Carregando...</p>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
|
<div class="bg-white rounded-xl shadow-lg p-6">
|
|
<div class="text-gray-500 text-sm mb-1">Total de Hoteis</div>
|
|
<div class="text-3xl sm:text-4xl font-bold text-gray-800" id="total-hotels">-</div>
|
|
</div>
|
|
<div class="bg-white rounded-xl shadow-lg p-6">
|
|
<div class="text-gray-500 text-sm mb-1">Custo Total</div>
|
|
<div class="text-3xl sm:text-4xl font-bold text-gray-800" id="total-amount">-</div>
|
|
</div>
|
|
<div class="bg-white rounded-xl shadow-lg p-6">
|
|
<div class="text-gray-500 text-sm mb-1">Total Transacoes</div>
|
|
<div class="text-3xl sm:text-4xl font-bold text-gray-800" id="total-transactions">-</div>
|
|
</div>
|
|
<div class="bg-white rounded-xl shadow-lg p-6">
|
|
<div class="text-gray-500 text-sm mb-1">Hotel Maior Gasto</div>
|
|
<div class="text-3xl sm:text-4xl font-bold text-gray-800" id="highest-hotel">-</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white rounded-xl shadow-lg p-6 mb-8">
|
|
<input type="text" id="search" class="w-full p-3 border border-gray-300 rounded-lg outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent" placeholder="Buscar transacoes...">
|
|
</div>
|
|
|
|
<div id="hotels-container">
|
|
<div class="text-center text-gray-500">Carregando dados...</div>
|
|
</div>
|
|
|
|
<div class="text-center mt-12 text-gray-300 text-sm">
|
|
<p>© 2026 Grupo Inova</p>
|
|
<p>Sistema Financeiro Automatizado</p>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let allHotels = [];
|
|
|
|
async function loadReport() {
|
|
try {
|
|
const response = await fetch('report.json');
|
|
const data = await response.json();
|
|
allHotels = data.hotels;
|
|
|
|
// Update date
|
|
document.getElementById('date').textContent = 'Atualizado: ' + data.date;
|
|
|
|
// Update stats
|
|
document.getElementById('total-hotels').textContent = data.hotels.length;
|
|
document.getElementById('total-amount').textContent = 'R$ ' + data.global_total.toLocaleString('pt-BR', {minimumFractionDigits: 2, maximumFractionDigits: 2});
|
|
document.getElementById('total-transactions').textContent = data.global_count;
|
|
|
|
// Find highest hotel
|
|
const highest = data.hotels.reduce((prev, current) => (prev.total > current.total) ? prev : current);
|
|
document.getElementById('highest-hotel').textContent = highest.name;
|
|
|
|
// Render hotels
|
|
renderHotels(data.hotels);
|
|
} catch (error) {
|
|
console.error('Erro ao carregar report.json:', error);
|
|
document.getElementById('hotels-container').innerHTML = '<div class="text-center text-red-500">Erro ao carregar dados</div>';
|
|
}
|
|
}
|
|
|
|
function renderHotels(hotels) {
|
|
const container = document.getElementById('hotels-container');
|
|
container.innerHTML = '';
|
|
|
|
hotels.forEach((hotel, index) => {
|
|
const hotelId = 'hotel-' + index;
|
|
const btnId = 'btn-' + index;
|
|
const iconId = 'icon-' + index;
|
|
const textId = 'text-' + index;
|
|
|
|
const hotelHtml = `
|
|
<div class="bg-gray-50 border border-gray-200 rounded-xl p-6 mb-8" data-hotel="${hotel.name}">
|
|
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-4 sm:mb-6">
|
|
<div class="flex items-center gap-3 mb-4 sm:mb-0">
|
|
<div class="bg-indigo-500 text-white rounded-lg p-2">
|
|
<i class="fas fa-building"></i>
|
|
</div>
|
|
<div>
|
|
<div class="text-lg sm:text-xl font-bold text-gray-800">${hotel.name}</div>
|
|
</div>
|
|
</div>
|
|
<div class="text-right sm:text-center sm:mt-0 sm:pt-4">
|
|
<div class="text-sm text-gray-600">${hotel.count} transacoes</div>
|
|
<div class="text-2xl sm:text-3xl font-bold text-gray-800">R$ ${hotel.total.toLocaleString('pt-BR', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button id="${btnId}" onclick="toggleHotel('${hotelId}')" class="w-full sm:w-auto bg-white border-2 border-indigo-500 text-indigo-600 hover:bg-indigo-500 hover:text-white font-semibold py-2 px-4 sm:py-3 rounded-lg transition-colors">
|
|
<i id="${iconId}" class="fas fa-chevron-down mr-1"></i>
|
|
<span id="${textId}">Mostrar Transacoes</span>
|
|
</button>
|
|
|
|
<div id="${hotelId}" class="hidden mt-4 sm:mt-6">
|
|
<div class="table-container">
|
|
<table class="table-auto w-full text-[10px] sm:text-xs md:text-sm border-collapse">
|
|
<thead>
|
|
<tr class="border-b">
|
|
<th class="px-2 py-2 text-left">Descricao</th>
|
|
<th class="px-2 py-2 text-left">Categoria</th>
|
|
<th class="px-2 py-2 text-left">Valor</th>
|
|
<th class="px-2 py-2 text-left sm:hidden">Fornecedor</th>
|
|
<th class="px-2 py-2 text-left sm:hidden">Data</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${hotel.transactions.map(t => `
|
|
<tr class="border-b hover:bg-gray-100">
|
|
<td class="px-2 py-2">${(t.descricao || '-').substring(0, 50)}</td>
|
|
<td class="px-2 py-2"><span class="bg-indigo-100 text-indigo-700 px-2 py-1 rounded text-xs font-semibold">${t.categoria}</span></td>
|
|
<td class="px-2 py-2 font-semibold">R$ ${t.valor.toLocaleString('pt-BR', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
|
|
<td class="px-2 py-2 sm:hidden">${(t.fornecedor || '-').substring(0, 20)}</td>
|
|
<td class="px-2 py-2 sm:hidden">${t.data_vencimento}</td>
|
|
</tr>
|
|
`).join('')}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>`;
|
|
container.innerHTML += hotelHtml;
|
|
});
|
|
}
|
|
|
|
function toggleHotel(id) {
|
|
const container = document.getElementById(id);
|
|
const index = id.split('-')[1];
|
|
const btn = document.getElementById('btn-' + index);
|
|
const icon = document.getElementById('icon-' + index);
|
|
const text = document.getElementById('text-' + index);
|
|
|
|
if (container.classList.contains('hidden')) {
|
|
container.classList.remove('hidden');
|
|
icon.className = 'fas fa-chevron-up mr-1';
|
|
text.textContent = 'Esconder Transacoes';
|
|
} else {
|
|
container.classList.add('hidden');
|
|
icon.className = 'fas fa-chevron-down mr-1';
|
|
text.textContent = 'Mostrar Transacoes';
|
|
}
|
|
}
|
|
|
|
// Search functionality
|
|
document.getElementById('search').addEventListener('input', function(e) {
|
|
const query = e.target.value.toLowerCase();
|
|
|
|
if (query === '') {
|
|
renderHotels(allHotels);
|
|
return;
|
|
}
|
|
|
|
const filtered = allHotels.filter(hotel => {
|
|
const hotelName = hotel.name.toLowerCase();
|
|
const hasMatchingTransaction = hotel.transactions.some(t =>
|
|
(t.descricao || '').toLowerCase().includes(query) ||
|
|
(t.categoria || '').toLowerCase().includes(query) ||
|
|
(t.fornecedor || '').toLowerCase().includes(query)
|
|
);
|
|
return hotelName.includes(query) || hasMatchingTransaction;
|
|
});
|
|
|
|
renderHotels(filtered);
|
|
});
|
|
|
|
// Load report on page load
|
|
loadReport();
|
|
</script>
|
|
</body>
|
|
</html>
|