testes / index.html
dalton758's picture
Add 3 files
7ebd53b verified
<!DOCTYPE html>
<html lang="pt">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Água Sonho Real - Sistema de Cobrança</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#3498db',
secondary: '#2980b9',
danger: '#e74c3c',
warning: '#f39c12',
success: '#2ecc71',
dark: '#2c3e50',
}
}
}
}
</script>
<style>
.sidebar {
transition: all 0.3s;
}
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.active {
transform: translateX(0);
}
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.alert-pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(231, 76, 60, 0.4);
}
70% {
box-shadow: 0 0 0 10px rgba(231, 76, 60, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(231, 76, 60, 0);
}
}
</style>
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div class="sidebar bg-dark text-white w-64 fixed h-full z-10">
<div class="p-4 flex items-center space-x-2 border-b border-gray-700">
<i class="fas fa-tint text-2xl text-primary"></i>
<h1 class="text-xl font-bold">Água Sonho Real</h1>
</div>
<nav class="p-4">
<ul class="space-y-2">
<li>
<button onclick="showTab('dashboard')" class="tab-btn w-full text-left p-2 rounded hover:bg-secondary flex items-center space-x-2">
<i class="fas fa-home"></i>
<span>Dashboard</span>
</button>
</li>
<li>
<button onclick="showTab('billing')" class="tab-btn w-full text-left p-2 rounded hover:bg-secondary flex items-center space-x-2">
<i class="fas fa-file-invoice-dollar"></i>
<span>Cobrança</span>
</button>
</li>
<li>
<button onclick="showTab('customers')" class="tab-btn w-full text-left p-2 rounded hover:bg-secondary flex items-center space-x-2">
<i class="fas fa-users"></i>
<span>Clientes</span>
</button>
</li>
<li>
<button onclick="showTab('reports')" class="tab-btn w-full text-left p-2 rounded hover:bg-secondary flex items-center space-x-2">
<i class="fas fa-chart-bar"></i>
<span>Relatórios</span>
</button>
</li>
<li>
<button onclick="showTab('settings')" class="tab-btn w-full text-left p-2 rounded hover:bg-secondary flex items-center space-x-2">
<i class="fas fa-cog"></i>
<span>Configurações</span>
</button>
</li>
</ul>
</nav>
<div class="absolute bottom-0 w-full p-4 border-t border-gray-700">
<button class="w-full bg-primary hover:bg-secondary text-white p-2 rounded flex items-center justify-center space-x-2">
<i class="fas fa-sign-out-alt"></i>
<span>Sair</span>
</button>
</div>
</div>
<!-- Main Content -->
<div class="flex-1 ml-0 md:ml-64 transition-all duration-300">
<!-- Mobile Header -->
<header class="bg-white shadow md:hidden p-4 flex items-center justify-between">
<button id="menu-toggle" class="text-dark">
<i class="fas fa-bars text-xl"></i>
</button>
<h1 class="text-xl font-bold text-dark">Água Sonho Real</h1>
<div class="w-8"></div> <!-- Spacer -->
</header>
<!-- Content -->
<main class="p-4 md:p-6">
<!-- Dashboard Tab -->
<div id="dashboard" class="tab-content active">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-dark">Dashboard</h2>
<div class="text-sm text-gray-500">
<span id="current-date"></span>
</div>
</div>
<!-- Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<div class="bg-white p-4 rounded-lg shadow">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500">Clientes Ativos</p>
<h3 class="text-2xl font-bold" id="active-customers">0</h3>
</div>
<div class="bg-primary bg-opacity-10 p-3 rounded-full">
<i class="fas fa-users text-primary text-xl"></i>
</div>
</div>
</div>
<div class="bg-white p-4 rounded-lg shadow">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500">Receita Mensal</p>
<h3 class="text-2xl font-bold" id="monthly-revenue">0 MZN</h3>
</div>
<div class="bg-success bg-opacity-10 p-3 rounded-full">
<i class="fas fa-money-bill-wave text-success text-xl"></i>
</div>
</div>
</div>
<div class="bg-white p-4 rounded-lg shadow">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500">Pagamentos Atrasados</p>
<h3 class="text-2xl font-bold" id="late-payments">0</h3>
</div>
<div class="bg-danger bg-opacity-10 p-3 rounded-full">
<i class="fas fa-exclamation-triangle text-danger text-xl"></i>
</div>
</div>
</div>
<div class="bg-white p-4 rounded-lg shadow">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500">Consumo Médio</p>
<h3 class="text-2xl font-bold" id="avg-consumption">0 m³</h3>
</div>
<div class="bg-secondary bg-opacity-10 p-3 rounded-full">
<i class="fas fa-tint text-secondary text-xl"></i>
</div>
</div>
</div>
</div>
<!-- Alerts -->
<div class="mb-6">
<h3 class="text-lg font-semibold mb-2 text-dark">Alertas de Pagamento</h3>
<div id="alerts-container" class="space-y-2">
<!-- Alerts will be added here dynamically -->
</div>
</div>
<!-- Recent Payments -->
<div class="bg-white rounded-lg shadow overflow-hidden">
<div class="p-4 border-b">
<h3 class="text-lg font-semibold text-dark">Últimos Pagamentos</h3>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Cliente</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Nº Cliente</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Área</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Valor</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Data</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody id="recent-payments" class="bg-white divide-y divide-gray-200">
<!-- Recent payments will be added here dynamically -->
</tbody>
</table>
</div>
</div>
</div>
<!-- Billing Tab -->
<div id="billing" class="tab-content">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-dark">Cobrança</h2>
<button onclick="showNewBillingModal()" class="bg-primary hover:bg-secondary text-white px-4 py-2 rounded flex items-center space-x-2">
<i class="fas fa-plus"></i>
<span>Nova Cobrança</span>
</button>
</div>
<div class="bg-white rounded-lg shadow overflow-hidden mb-6">
<div class="p-4 border-b">
<h3 class="text-lg font-semibold text-dark">Registrar Pagamento</h3>
</div>
<div class="p-4">
<form id="payment-form" class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label for="customer-search" class="block text-sm font-medium text-gray-700 mb-1">Buscar Cliente</label>
<div class="relative">
<input type="text" id="customer-search" class="w-full p-2 border border-gray-300 rounded" placeholder="Nome ou Nº Cliente">
<div id="search-results" class="absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded shadow-lg hidden"></div>
</div>
</div>
<div>
<label for="consumption" class="block text-sm font-medium text-gray-700 mb-1">Consumo (m³)</label>
<input type="number" id="consumption" min="5" step="0.1" class="w-full p-2 border border-gray-300 rounded" value="5">
</div>
<div>
<label for="amount" class="block text-sm font-medium text-gray-700 mb-1">Valor (MZN)</label>
<input type="number" id="amount" class="w-full p-2 border border-gray-300 rounded" readonly>
</div>
<div class="md:col-span-3">
<button type="button" onclick="calculateAmount()" class="bg-primary hover:bg-secondary text-white px-4 py-2 rounded mr-2">
Calcular
</button>
<button type="button" onclick="registerPayment()" class="bg-success hover:bg-green-600 text-white px-4 py-2 rounded">
Registrar Pagamento
</button>
</div>
</form>
</div>
</div>
<div class="bg-white rounded-lg shadow overflow-hidden">
<div class="p-4 border-b flex justify-between items-center">
<h3 class="text-lg font-semibold text-dark">Histórico de Cobranças</h3>
<div class="flex space-x-2">
<select id="billing-filter" class="p-2 border border-gray-300 rounded">
<option value="all">Todos</option>
<option value="paid">Pagas</option>
<option value="pending">Pendentes</option>
<option value="overdue">Atrasadas</option>
</select>
<input type="month" id="billing-month" class="p-2 border border-gray-300 rounded">
</div>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Nº Cliente</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Cliente</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Área</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Consumo</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Valor</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Data</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ações</th>
</tr>
</thead>
<tbody id="billing-history" class="bg-white divide-y divide-gray-200">
<!-- Billing history will be added here dynamically -->
</tbody>
</table>
</div>
</div>
</div>
<!-- Customers Tab -->
<div id="customers" class="tab-content">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-dark">Clientes</h2>
<button onclick="showNewCustomerModal()" class="bg-primary hover:bg-secondary text-white px-4 py-2 rounded flex items-center space-x-2">
<i class="fas fa-plus"></i>
<span>Novo Cliente</span>
</button>
</div>
<div class="bg-white rounded-lg shadow overflow-hidden mb-6">
<div class="p-4 border-b flex justify-between items-center">
<h3 class="text-lg font-semibold text-dark">Lista de Clientes</h3>
<div class="flex space-x-2">
<select id="customer-area-filter" class="p-2 border border-gray-300 rounded">
<option value="all">Todas Áreas</option>
<option value="1">Possulane</option>
<option value="2">Condomínios</option>
</select>
<input type="text" id="customer-search-main" placeholder="Buscar cliente..." class="p-2 border border-gray-300 rounded">
</div>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Nº Cliente</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Nome</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Área</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Contacto</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Último Pagamento</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ações</th>
</tr>
</thead>
<tbody id="customers-list" class="bg-white divide-y divide-gray-200">
<!-- Customers list will be added here dynamically -->
</tbody>
</table>
</div>
</div>
</div>
<!-- Reports Tab -->
<div id="reports" class="tab-content">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-dark">Relatórios</h2>
<div class="flex space-x-2">
<button onclick="generatePDFReport()" class="bg-danger hover:bg-red-600 text-white px-4 py-2 rounded flex items-center space-x-2">
<i class="fas fa-file-pdf"></i>
<span>Exportar PDF</span>
</button>
<button onclick="generateExcelReport()" class="bg-success hover:bg-green-600 text-white px-4 py-2 rounded flex items-center space-x-2">
<i class="fas fa-file-excel"></i>
<span>Exportar Excel</span>
</button>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<div class="bg-white p-4 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-4 text-dark">Filtros do Relatório</h3>
<form id="report-filters" class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Tipo de Relatório</label>
<select id="report-type" class="w-full p-2 border border-gray-300 rounded">
<option value="monthly">Mensal</option>
<option value="quarterly">Trimestral</option>
<option value="annual">Anual</option>
<option value="custom">Personalizado</option>
</select>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="start-date" class="block text-sm font-medium text-gray-700 mb-1">Data Inicial</label>
<input type="date" id="start-date" class="w-full p-2 border border-gray-300 rounded">
</div>
<div>
<label for="end-date" class="block text-sm font-medium text-gray-700 mb-1">Data Final</label>
<input type="date" id="end-date" class="w-full p-2 border border-gray-300 rounded">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Área</label>
<select id="report-area" class="w-full p-2 border border-gray-300 rounded">
<option value="all">Todas Áreas</option>
<option value="1">Possulane</option>
<option value="2">Condomínios</option>
</select>
</div>
<button type="button" onclick="generateReport()" class="w-full bg-primary hover:bg-secondary text-white px-4 py-2 rounded">
Gerar Relatório
</button>
</form>
</div>
<div class="bg-white p-4 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-4 text-dark">Resumo Financeiro</h3>
<div class="space-y-4">
<div>
<h4 class="font-medium text-gray-700">Receita Total</h4>
<p class="text-2xl font-bold" id="total-revenue">0 MZN</p>
</div>
<div>
<h4 class="font-medium text-gray-700">Pagamentos Pendentes</h4>
<p class="text-2xl font-bold text-danger" id="pending-revenue">0 MZN</p>
</div>
<div>
<h4 class="font-medium text-gray-700">Número de Clientes</h4>
<div class="flex justify-between">
<div>
<p class="text-sm text-gray-500">Possulane</p>
<p class="text-xl font-bold" id="possulane-customers">0</p>
</div>
<div>
<p class="text-sm text-gray-500">Condomínios</p>
<p class="text-xl font-bold" id="condominios-customers">0</p>
</div>
<div>
<p class="text-sm text-gray-500">Total</p>
<p class="text-xl font-bold" id="total-customers">0</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow overflow-hidden">
<div class="p-4 border-b">
<h3 class="text-lg font-semibold text-dark">Resultados do Relatório</h3>
</div>
<div class="p-4">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Mês</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Receita (MZN)</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Clientes Ativos</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Pagamentos Atrasados</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Consumo Médio (m³)</th>
</tr>
</thead>
<tbody id="report-results" class="bg-white divide-y divide-gray-200">
<!-- Report results will be added here dynamically -->
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Settings Tab -->
<div id="settings" class="tab-content">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-dark">Configurações</h2>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="bg-white p-4 rounded-lg shadow lg:col-span-2">
<h3 class="text-lg font-semibold mb-4 text-dark">Configurações de Cobrança</h3>
<form id="billing-settings" class="space-y-4">
<div>
<label for="min-consumption" class="block text-sm font-medium text-gray-700 mb-1">Consumo Mínimo (m³)</label>
<input type="number" id="min-consumption" min="1" step="1" class="w-full p-2 border border-gray-300 rounded" value="5">
</div>
<div>
<label for="min-consumption-price" class="block text-sm font-medium text-gray-700 mb-1">Preço Consumo Mínimo (MZN)</label>
<input type="number" id="min-consumption-price" min="1" step="1" class="w-full p-2 border border-gray-300 rounded" value="300">
</div>
<div>
<label for="extra-consumption-price" class="block text-sm font-medium text-gray-700 mb-1">Preço por m³ adicional (MZN)</label>
<input type="number" id="extra-consumption-price" min="1" step="1" class="w-full p-2 border border-gray-300 rounded" value="60">
</div>
<div>
<label for="due-date" class="block text-sm font-medium text-gray-700 mb-1">Dia de Vencimento</label>
<input type="number" id="due-date" min="1" max="31" class="w-full p-2 border border-gray-300 rounded" value="15">
</div>
<div>
<label for="late-fee" class="block text-sm font-medium text-gray-700 mb-1">Multa por Atraso (%)</label>
<input type="number" id="late-fee" min="0" max="100" step="0.1" class="w-full p-2 border border-gray-300 rounded" value="5">
</div>
<button type="button" onclick="saveBillingSettings()" class="bg-primary hover:bg-secondary text-white px-4 py-2 rounded">
Salvar Configurações
</button>
</form>
</div>
<div class="bg-white p-4 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-4 text-dark">Configurações de Usuário</h3>
<form id="user-settings" class="space-y-4">
<div>
<label for="username" class="block text-sm font-medium text-gray-700 mb-1">Nome de Usuário</label>
<input type="text" id="username" class="w-full p-2 border border-gray-300 rounded" value="admin">
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email</label>
<input type="email" id="email" class="w-full p-2 border border-gray-300 rounded" value="admin@aguasonhoreal.com">
</div>
<div>
<label for="current-password" class="block text-sm font-medium text-gray-700 mb-1">Senha Atual</label>
<input type="password" id="current-password" class="w-full p-2 border border-gray-300 rounded">
</div>
<div>
<label for="new-password" class="block text-sm font-medium text-gray-700 mb-1">Nova Senha</label>
<input type="password" id="new-password" class="w-full p-2 border border-gray-300 rounded">
</div>
<div>
<label for="confirm-password" class="block text-sm font-medium text-gray-700 mb-1">Confirmar Nova Senha</label>
<input type="password" id="confirm-password" class="w-full p-2 border border-gray-300 rounded">
</div>
<button type="button" onclick="saveUserSettings()" class="bg-primary hover:bg-secondary text-white px-4 py-2 rounded">
Salvar Alterações
</button>
</form>
</div>
</div>
</div>
</main>
</div>
</div>
<!-- Modals -->
<!-- New Customer Modal -->
<div id="new-customer-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-lg w-full max-w-md">
<div class="p-4 border-b">
<h3 class="text-lg font-semibold text-dark">Novo Cliente</h3>
</div>
<div class="p-4">
<form id="new-customer-form" class="space-y-4">
<div>
<label for="customer-name" class="block text-sm font-medium text-gray-700 mb-1">Nome Completo</label>
<input type="text" id="customer-name" class="w-full p-2 border border-gray-300 rounded" required>
</div>
<div>
<label for="customer-area" class="block text-sm font-medium text-gray-700 mb-1">Área</label>
<select id="customer-area" class="w-full p-2 border border-gray-300 rounded" required>
<option value="1">Possulane</option>
<option value="2">Condomínios</option>
</select>
</div>
<div>
<label for="customer-address" class="block text-sm font-medium text-gray-700 mb-1">Endereço</label>
<input type="text" id="customer-address" class="w-full p-2 border border-gray-300 rounded" required>
</div>
<div>
<label for="customer-phone" class="block text-sm font-medium text-gray-700 mb-1">Telefone</label>
<input type="tel" id="customer-phone" class="w-full p-2 border border-gray-300 rounded" required>
</div>
<div>
<label for="customer-email" class="block text-sm font-medium text-gray-700 mb-1">Email (Opcional)</label>
<input type="email" id="customer-email" class="w-full p-2 border border-gray-300 rounded">
</div>
</form>
</div>
<div class="p-4 border-t flex justify-end space-x-2">
<button onclick="hideModal('new-customer-modal')" class="px-4 py-2 border border-gray-300 rounded text-gray-700 hover:bg-gray-100">
Cancelar
</button>
<button onclick="saveNewCustomer()" class="bg-primary hover:bg-secondary text-white px-4 py-2 rounded">
Salvar Cliente
</button>
</div>
</div>
</div>
<!-- Customer Details Modal -->
<div id="customer-details-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-lg w-full max-w-md">
<div class="p-4 border-b flex justify-between items-center">
<h3 class="text-lg font-semibold text-dark">Detalhes do Cliente</h3>
<button onclick="hideModal('customer-details-modal')" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="p-4">
<div class="mb-4">
<h4 class="font-bold text-lg" id="detail-customer-name"></h4>
<p class="text-gray-600" id="detail-customer-number"></p>
</div>
<div class="space-y-3">
<div>
<p class="text-sm text-gray-500">Área</p>
<p id="detail-customer-area"></p>
</div>
<div>
<p class="text-sm text-gray-500">Endereço</p>
<p id="detail-customer-address"></p>
</div>
<div>
<p class="text-sm text-gray-500">Telefone</p>
<p id="detail-customer-phone"></p>
</div>
<div>
<p class="text-sm text-gray-500">Email</p>
<p id="detail-customer-email"></p>
</div>
<div>
<p class="text-sm text-gray-500">Data de Registro</p>
<p id="detail-customer-register-date"></p>
</div>
</div>
</div>
<div class="p-4 border-t">
<h4 class="font-semibold mb-2">Histórico de Pagamentos</h4>
<div class="overflow-y-auto max-h-40">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-2 py-1 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Data</th>
<th class="px-2 py-1 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Valor</th>
<th class="px-2 py-1 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody id="customer-payment-history" class="bg-white divide-y divide-gray-200">
<!-- Payment history will be added here dynamically -->
</tbody>
</table>
</div>
</div>
<div class="p-4 border-t flex justify-end space-x-2">
<button onclick="hideModal('customer-details-modal')" class="px-4 py-2 border border-gray-300 rounded text-gray-700 hover:bg-gray-100">
Fechar
</button>
</div>
</div>
</div>
<!-- New Billing Modal -->
<div id="new-billing-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-lg w-full max-w-md">
<div class="p-4 border-b">
<h3 class="text-lg font-semibold text-dark">Nova Cobrança</h3>
</div>
<div class="p-4">
<form id="new-billing-form" class="space-y-4">
<div>
<label for="billing-customer" class="block text-sm font-medium text-gray-700 mb-1">Cliente</label>
<select id="billing-customer" class="w-full p-2 border border-gray-300 rounded" required>
<!-- Customer options will be added here dynamically -->
</select>
</div>
<div>
<label for="billing-month" class="block text-sm font-medium text-gray-700 mb-1">Mês de Referência</label>
<input type="month" id="billing-month" class="w-full p-2 border border-gray-300 rounded" required>
</div>
<div>
<label for="billing-consumption" class="block text-sm font-medium text-gray-700 mb-1">Consumo (m³)</label>
<input type="number" id="billing-consumption" min="5" step="0.1" class="w-full p-2 border border-gray-300 rounded" value="5" required>
</div>
<div>
<label for="billing-amount" class="block text-sm font-medium text-gray-700 mb-1">Valor (MZN)</label>
<input type="number" id="billing-amount" class="w-full p-2 border border-gray-300 rounded" readonly>
</div>
<div>
<label for="billing-due-date" class="block text-sm font-medium text-gray-700 mb-1">Data de Vencimento</label>
<input type="date" id="billing-due-date" class="w-full p-2 border border-gray-300 rounded" required>
</div>
</form>
</div>
<div class="p-4 border-t flex justify-end space-x-2">
<button onclick="hideModal('new-billing-modal')" class="px-4 py-2 border border-gray-300 rounded text-gray-700 hover:bg-gray-100">
Cancelar
</button>
<button onclick="saveNewBilling()" class="bg-primary hover:bg-secondary text-white px-4 py-2 rounded">
Salvar Cobrança
</button>
</div>
</div>
</div>
<!-- Payment Receipt Modal -->
<div id="payment-receipt-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-lg w-full max-w-md">
<div class="p-4 border-b flex justify-between items-center">
<h3 class="text-lg font-semibold text-dark">Recibo de Pagamento</h3>
<button onclick="hideModal('payment-receipt-modal')" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="p-4">
<div class="text-center mb-4">
<h2 class="text-xl font-bold">Água Sonho Real</h2>
<p class="text-sm text-gray-600">Sistema de Cobrança</p>
</div>
<div class="border-b pb-4 mb-4">
<div class="flex justify-between mb-2">
<span class="font-semibold">Nº Recibo:</span>
<span id="receipt-number"></span>
</div>
<div class="flex justify-between mb-2">
<span class="font-semibold">Data:</span>
<span id="receipt-date"></span>
</div>
<div class="flex justify-between">
<span class="font-semibold">Cliente:</span>
<span id="receipt-customer"></span>
</div>
</div>
<div class="mb-4">
<table class="w-full">
<thead>
<tr class="border-b">
<th class="text-left py-2">Descrição</th>
<th class="text-right py-2">Valor</th>
</tr>
</thead>
<tbody>
<tr class="border-b">
<td class="py-2">Consumo de Água</td>
<td class="text-right py-2" id="receipt-water-amount"></td>
</tr>
<tr class="border-b">
<td class="py-2">Multa por Atraso</td>
<td class="text-right py-2" id="receipt-late-fee"></td>
</tr>
<tr class="font-bold">
<td class="py-2">Total</td>
<td class="text-right py-2" id="receipt-total"></td>
</tr>
</tbody>
</table>
</div>
<div class="text-center text-sm text-gray-500">
<p>Obrigado pelo seu pagamento!</p>
<p>Para dúvidas, contacte: +258 84 123 4567</p>
</div>
</div>
<div class="p-4 border-t flex justify-end space-x-2">
<button onclick="printReceipt()" class="bg-primary hover:bg-secondary text-white px-4 py-2 rounded flex items-center space-x-2">
<i class="fas fa-print"></i>
<span>Imprimir</span>
</button>
<button onclick="hideModal('payment-receipt-modal')" class="px-4 py-2 border border-gray-300 rounded text-gray-700 hover:bg-gray-100">
Fechar
</button>
</div>
</div>
</div>
<!-- Confirmation Modal -->
<div id="confirmation-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-lg w-full max-w-md">
<div class="p-4 border-b">
<h3 class="text-lg font-semibold text-dark" id="confirmation-title"></h3>
</div>
<div class="p-4">
<p id="confirmation-message"></p>
</div>
<div class="p-4 border-t flex justify-end space-x-2">
<button onclick="hideModal('confirmation-modal')" class="px-4 py-2 border border-gray-300 rounded text-gray-700 hover:bg-gray-100">
Cancelar
</button>
<button onclick="confirmAction()" class="bg-primary hover:bg-secondary text-white px-4 py-2 rounded" id="confirm-button">
Confirmar
</button>
</div>
</div>
</div>
<script>
// Sample data for demonstration
let customers = [
{ id: 1, number: '1-001', name: 'João Matola', area: '1', areaName: 'Possulane', address: 'Av. Marginal, 123', phone: '841234567', email: 'joao@email.com', registrationDate: '2023-01-15', status: 'active' },
{ id: 2, number: '1-002', name: 'Maria Nhantumbo', area: '1', areaName: 'Possulane', address: 'Rua da Escola, 45', phone: '842345678', email: 'maria@email.com', registrationDate: '2023-02-10', status: 'active' },
{ id: 3, number: '2-001', name: 'Condomínio Flor do Mar', area: '2', areaName: 'Condomínios', address: 'Av. da Praia, 300', phone: '843456789', email: 'flordomar@email.com', registrationDate: '2023-01-05', status: 'active' },
{ id: 4, number: '2-002', name: 'Condomínio Vista Linda', area: '2', areaName: 'Condomínios', address: 'Rua dos Coqueiros, 12', phone: '844567890', email: 'vistalinda@email.com', registrationDate: '2023-03-20', status: 'active' },
];
let bills = [
{ id: 1, customerId: 1, customerNumber: '1-001', customerName: 'João Matola', area: '1', areaName: 'Possulane', consumption: 5, amount: 300, date: '2023-06-15', dueDate: '2023-07-15', paymentDate: '2023-07-10', status: 'paid', lateFee: 0 },
{ id: 2, customerId: 2, customerNumber: '1-002', customerName: 'Maria Nhantumbo', area: '1', areaName: 'Possulane', consumption: 7, amount: 420, date: '2023-06-15', dueDate: '2023-07-15', paymentDate: '2023-07-18', status: 'paid', lateFee: 21 },
{ id: 3, customerId: 3, customerNumber: '2-001', customerName: 'Condomínio Flor do Mar', area: '2', areaName: 'Condomínios', consumption: 15, amount: 900, date: '2023-06-15', dueDate: '2023-07-15', paymentDate: null, status: 'overdue', lateFee: 45 },
{ id: 4, customerId: 4, customerNumber: '2-002', customerName: 'Condomínio Vista Linda', area: '2', areaName: 'Condomínios', consumption: 8, amount: 480, date: '2023-06-15', dueDate: '2023-07-15', paymentDate: null, status: 'pending', lateFee: 0 },
{ id: 5, customerId: 1, customerNumber: '1-001', customerName: 'João Matola', area: '1', areaName: 'Possulane', consumption: 6, amount: 360, date: '2023-05-15', dueDate: '2023-06-15', paymentDate: '2023-06-12', status: 'paid', lateFee: 0 },
{ id: 6, customerId: 2, customerNumber: '1-002', customerName: 'Maria Nhantumbo', area: '1', areaName: 'Possulane', consumption: 5, amount: 300, date: '2023-05-15', dueDate: '2023-06-15', paymentDate: '2023-06-20', status: 'paid', lateFee: 15 },
{ id: 7, customerId: 3, customerNumber: '2-001', customerName: 'Condomínio Flor do Mar', area: '2', areaName: 'Condomínios', consumption: 12, amount: 720, date: '2023-05-15', dueDate: '2023-06-15', paymentDate: '2023-06-10', status: 'paid', lateFee: 0 },
{ id: 8, customerId: 4, customerNumber: '2-002', customerName: 'Condomínio Vista Linda', area: '2', areaName: 'Condomínios', consumption: 7, amount: 420, date: '2023-05-15', dueDate: '2023-06-15', paymentDate: '2023-06-14', status: 'paid', lateFee: 0 },
{ id: 9, customerId: 1, customerNumber: '1-001', customerName: 'João Matola', area: '1', areaName: 'Possulane', consumption: 5, amount: 300, date: '2023-04-15', dueDate: '2023-05-15', paymentDate: '2023-05-10', status: 'paid', lateFee: 0 },
{ id: 10, customerId: 2, customerNumber: '1-002', customerName: 'Maria Nhantumbo', area: '1', areaName: 'Possulane', consumption: 9, amount: 540, date: '2023-04-15', dueDate: '2023-05-15', paymentDate: '2023-05-18', status: 'paid', lateFee: 27 },
];
let settings = {
minConsumption: 5,
minConsumptionPrice: 300,
extraConsumptionPrice: 60,
dueDate: 15,
lateFee: 5
};
// Current action and data for confirmation modal
let currentAction = null;
let currentActionData = null;
// Initialize the application
document.addEventListener('DOMContentLoaded', function() {
// Set current date
const now = new Date();
document.getElementById('current-date').textContent = now.toLocaleDateString('pt-PT', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
});
// Initialize dashboard
updateDashboard();
loadRecentPayments();
loadAlerts();
// Initialize billing tab
calculateAmount();
loadBillingHistory();
// Initialize customers tab
loadCustomersList();
// Initialize reports tab
updateReportSummary();
// Initialize settings tab
loadSettings();
// Mobile menu toggle
document.getElementById('menu-toggle').addEventListener('click', function() {
document.querySelector('.sidebar').classList.toggle('active');
});
// Customer search functionality
document.getElementById('customer-search').addEventListener('input', function() {
searchCustomers(this.value);
});
// Customer search in customers tab
document.getElementById('customer-search-main').addEventListener('input', function() {
filterCustomers();
});
// Billing filter
document.getElementById('billing-filter').addEventListener('change', function() {
loadBillingHistory();
});
// Billing month filter
document.getElementById('billing-month').addEventListener('change', function() {
loadBillingHistory();
});
// Customer area filter
document.getElementById('customer-area-filter').addEventListener('change', function() {
filterCustomers();
});
});
// Tab navigation
function showTab(tabId) {
document.querySelectorAll('.tab-content').forEach(tab => {
tab.classList.remove('active');
});
document.getElementById(tabId).classList.add('active');
// Close mobile menu if open
document.querySelector('.sidebar').classList.remove('active');
}
// Modal functions
function showModal(modalId) {
document.getElementById(modalId).classList.remove('hidden');
}
function hideModal(modalId) {
document.getElementById(modalId).classList.add('hidden');
}
function showNewCustomerModal() {
document.getElementById('customer-name').value = '';
document.getElementById('customer-area').value = '1';
document.getElementById('customer-address').value = '';
document.getElementById('customer-phone').value = '';
document.getElementById('customer-email').value = '';
showModal('new-customer-modal');
}
function showNewBillingModal() {
const billingCustomerSelect = document.getElementById('billing-customer');
billingCustomerSelect.innerHTML = '';
customers.forEach(customer => {
const option = document.createElement('option');
option.value = customer.id;
option.textContent = `${customer.number} - ${customer.name}`;
billingCustomerSelect.appendChild(option);
});
const now = new Date();
const currentMonth = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0');
document.getElementById('billing-month').value = currentMonth;
document.getElementById('billing-consumption').value = '5';
document.getElementById('billing-amount').value = '300';
// Set due date to the 15th of next month
const nextMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1);
const dueDate = new Date(nextMonth.getFullYear(), nextMonth.getMonth(), settings.dueDate);
document.getElementById('billing-due-date').value = dueDate.toISOString().split('T')[0];
showModal('new-billing-modal');
}
function showCustomerDetails(customerId) {
const customer = customers.find(c => c.id == customerId);
if (!customer) return;
document.getElementById('detail-customer-name').textContent = customer.name;
document.getElementById('detail-customer-number').textContent = `Nº Cliente: ${customer.number}`;
document.getElementById('detail-customer-area').textContent = customer.areaName;
document.getElementById('detail-customer-address').textContent = customer.address;
document.getElementById('detail-customer-phone').textContent = customer.phone;
document.getElementById('detail-customer-email').textContent = customer.email || 'N/A';
document.getElementById('detail-customer-register-date').textContent = new Date(customer.registrationDate).toLocaleDateString('pt-PT');
// Load payment history
const paymentHistory = bills.filter(bill => bill.customerId == customerId)
.sort((a, b) => new Date(b.date) - new Date(a.date));
const paymentHistoryTable = document.getElementById('customer-payment-history');
paymentHistoryTable.innerHTML = '';
paymentHistory.forEach(bill => {
const row = document.createElement('tr');
const dateCell = document.createElement('td');
dateCell.className = 'px-2 py-1 whitespace-nowrap';
dateCell.textContent = new Date(bill.date).toLocaleDateString('pt-PT');
const amountCell = document.createElement('td');
amountCell.className = 'px-2 py-1 whitespace-nowrap';
amountCell.textContent = `${bill.amount} MZN`;
const statusCell = document.createElement('td');
statusCell.className = 'px-2 py-1 whitespace-nowrap';
const statusBadge = document.createElement('span');
statusBadge.className = 'px-2 py-1 text-xs rounded-full';
if (bill.status === 'paid') {
statusBadge.classList.add('bg-green-100', 'text-green-800');
statusBadge.textContent = 'Pago';
} else if (bill.status === 'overdue') {
statusBadge.classList.add('bg-red-100', 'text-red-800');
statusBadge.textContent = 'Atrasado';
} else {
statusBadge.classList.add('bg-yellow-100', 'text-yellow-800');
statusBadge.textContent = 'Pendente';
}
statusCell.appendChild(statusBadge);
row.appendChild(dateCell);
row.appendChild(amountCell);
row.appendChild(statusCell);
paymentHistoryTable.appendChild(row);
});
showModal('customer-details-modal');
}
function showConfirmationModal(title, message, action, data = null) {
document.getElementById('confirmation-title').textContent = title;
document.getElementById('confirmation-message').textContent = message;
currentAction = action;
currentActionData = data;
showModal('confirmation-modal');
}
function confirmAction() {
if (currentAction === 'deleteCustomer') {
deleteCustomer(currentActionData);
} else if (currentAction === 'deleteBill') {
deleteBill(currentActionData);
}
hideModal('confirmation-modal');
}
// Dashboard functions
function updateDashboard() {
// Active customers
document.getElementById('active-customers').textContent = customers.length;
// Monthly revenue (last month)
const now = new Date();
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
const lastMonthStr = lastMonth.getFullYear() + '-' + String(lastMonth.getMonth() + 1).padStart(2, '0');
const lastMonthBills = bills.filter(bill => {
const billDate = new Date(bill.date);
const billMonth = billDate.getFullYear() + '-' + String(billDate.getMonth() + 1).padStart(2, '0');
return billMonth === lastMonthStr && bill.status === 'paid';
});
const monthlyRevenue = lastMonthBills.reduce((sum, bill) => sum + bill.amount + bill.lateFee, 0);
document.getElementById('monthly-revenue').textContent = `${monthlyRevenue} MZN`;
// Late payments
const overdueBills = bills.filter(bill => bill.status === 'overdue');
document.getElementById('late-payments').textContent = overdueBills.length;
// Average consumption
const consumptions = bills.map(bill => bill.consumption);
const avgConsumption = consumptions.length > 0 ?
(consumptions.reduce((sum, val) => sum + val, 0) / consumptions.length).toFixed(1) : 0;
document.getElementById('avg-consumption').textContent = `${avgConsumption} m³`;
}
function loadRecentPayments() {
const recentPaymentsTable = document.getElementById('recent-payments');
recentPaymentsTable.innerHTML = '';
// Get last 5 paid bills, sorted by payment date (newest first)
const paidBills = bills.filter(bill => bill.status === 'paid')
.sort((a, b) => new Date(b.paymentDate) - new Date(a.paymentDate))
.slice(0, 5);
paidBills.forEach(bill => {
const row = document.createElement('tr');
const customerCell = document.createElement('td');
customerCell.className = 'px-6 py-4 whitespace-nowrap';
customerCell.textContent = bill.customerName;
const numberCell = document.createElement('td');
numberCell.className = 'px-6 py-4 whitespace-nowrap';
numberCell.textContent = bill.customerNumber;
const areaCell = document.createElement('td');
areaCell.className = 'px-6 py-4 whitespace-nowrap';
areaCell.textContent = bill.areaName;
const amountCell = document.createElement('td');
amountCell.className = 'px-6 py-4 whitespace-nowrap';
amountCell.textContent = `${bill.amount + bill.lateFee} MZN`;
const dateCell = document.createElement('td');
dateCell.className = 'px-6 py-4 whitespace-nowrap';
dateCell.textContent = new Date(bill.paymentDate).toLocaleDateString('pt-PT');
const statusCell = document.createElement('td');
statusCell.className = 'px-6 py-4 whitespace-nowrap';
const statusBadge = document.createElement('span');
statusBadge.className = 'px-2 py-1 text-xs rounded-full bg-green-100 text-green-800';
statusBadge.textContent = 'Pago';
statusCell.appendChild(statusBadge);
row.appendChild(customerCell);
row.appendChild(numberCell);
row.appendChild(areaCell);
row.appendChild(amountCell);
row.appendChild(dateCell);
row.appendChild(statusCell);
recentPaymentsTable.appendChild(row);
});
}
function loadAlerts() {
const alertsContainer = document.getElementById('alerts-container');
alertsContainer.innerHTML = '';
// Get customers with overdue bills (2+ months)
const now = new Date();
const twoMonthsAgo = new Date(now.getFullYear(), now.getMonth() - 2, 1);
const overdueBills = bills.filter(bill => {
if (bill.status !== 'overdue') return false;
const dueDate = new Date(bill.dueDate);
return dueDate < twoMonthsAgo;
});
// Group by customer
const overdueCustomers = {};
overdueBills.forEach(bill => {
if (!overdueCustomers[bill.customerId]) {
overdueCustomers[bill.customerId] = {
customer: customers.find(c => c.id === bill.customerId),
bills: []
};
}
overdueCustomers[bill.customerId].bills.push(bill);
});
// Create alerts
Object.values(overdueCustomers).forEach(customerData => {
const totalDebt = customerData.bills.reduce((sum, bill) => sum + bill.amount + bill.lateFee, 0);
const monthsOverdue = customerData.bills.length;
const alert = document.createElement('div');
alert.className = 'p-3 rounded bg-red-50 border border-red-200 flex justify-between items-center alert-pulse';
const alertContent = document.createElement('div');
const alertTitle = document.createElement('h4');
alertTitle.className = 'font-semibold text-red-800';
alertTitle.textContent = `${customerData.customer.name} (${customerData.customer.number})`;
const alertMessage = document.createElement('p');
alertMessage.className = 'text-sm text-red-600';
alertMessage.textContent = `Dívida de ${totalDebt} MZN (${monthsOverdue} ${monthsOverdue === 1 ? 'mês' : 'meses'} atrasado)`;
alertContent.appendChild(alertTitle);
alertContent.appendChild(alertMessage);
const alertButton = document.createElement('button');
alertButton.className = 'px-3 py-1 bg-red-100 hover:bg-red-200 text-red-800 rounded text-sm';
alertButton.textContent = 'Ver Detalhes';
alertButton.onclick = () => showCustomerDetails(customerData.customer.id);
alert.appendChild(alertContent);
alert.appendChild(alertButton);
alertsContainer.appendChild(alert);
});
// If no alerts, show message
if (alertsContainer.children.length === 0) {
const noAlerts = document.createElement('div');
noAlerts.className = 'p-3 rounded bg-blue-50 border border-blue-200 text-blue-800';
noAlerts.textContent = 'Nenhum alerta de pagamento atrasado.';
alertsContainer.appendChild(noAlerts);
}
}
// Billing functions
function calculateAmount() {
const consumption = parseFloat(document.getElementById('consumption').value) || 5;
const minConsumption = settings.minConsumption;
const minPrice = settings.minConsumptionPrice;
const extraPrice = settings.extraConsumptionPrice;
let amount = minPrice;
if (consumption > minConsumption) {
amount += (consumption - minConsumption) * extraPrice;
}
document.getElementById('amount').value = amount.toFixed(2);
}
function registerPayment() {
const customerSearch = document.getElementById('customer-search').value.trim();
const consumption = parseFloat(document.getElementById('consumption').value) || 5;
const amount = parseFloat(document.getElementById('amount').value) || 300;
if (!customerSearch) {
alert('Por favor, selecione um cliente.');
return;
}
// In a real app, this would save to a database
// For demo, we'll just show a receipt
// Find customer (simplified for demo)
const customer = customers.find(c =>
c.name.toLowerCase().includes(customerSearch.toLowerCase()) ||
c.number.toLowerCase().includes(customerSearch.toLowerCase())
);
if (!customer) {
alert('Cliente não encontrado.');
return;
}
// Generate receipt
document.getElementById('receipt-number').textContent = `REC-${Date.now().toString().slice(-6)}`;
document.getElementById('receipt-date').textContent = new Date().toLocaleDateString('pt-PT');
document.getElementById('receipt-customer').textContent = `${customer.number} - ${customer.name}`;
document.getElementById('receipt-water-amount').textContent = `${amount} MZN`;
document.getElementById('receipt-late-fee').textContent = '0 MZN'; // No late fee for immediate payment
document.getElementById('receipt-total').textContent = `${amount} MZN`;
showModal('payment-receipt-modal');
// Clear form
document.getElementById('customer-search').value = '';
document.getElementById('consumption').value = '5';
document.getElementById('amount').value = '300';
// Add to bills (for demo)
const now = new Date();
const currentMonth = new Date(now.getFullYear(), now.getMonth(), 1);
const dueDate = new Date(now.getFullYear(), now.getMonth(), settings.dueDate);
const newBill = {
id: bills.length + 1,
customerId: customer.id,
customerNumber: customer.number,
customerName: customer.name,
area: customer.area,
areaName: customer.areaName,
consumption: consumption,
amount: amount,
date: currentMonth.toISOString().split('T')[0],
dueDate: dueDate.toISOString().split('T')[0],
paymentDate: now.toISOString().split('T')[0],
status: 'paid',
lateFee: 0
};
bills.push(newBill);
// Update dashboard
updateDashboard();
loadRecentPayments();
loadAlerts();
loadBillingHistory();
}
function searchCustomers(query) {
const resultsContainer = document.getElementById('search-results');
resultsContainer.innerHTML = '';
if (query.length < 2) {
resultsContainer.classList.add('hidden');
return;
}
const filteredCustomers = customers.filter(customer =>
customer.name.toLowerCase().includes(query.toLowerCase()) ||
customer.number.toLowerCase().includes(query.toLowerCase())
);
if (filteredCustomers.length === 0) {
const noResults = document.createElement('div');
noResults.className = 'p-2 text-sm text-gray-500';
noResults.textContent = 'Nenhum cliente encontrado';
resultsContainer.appendChild(noResults);
} else {
filteredCustomers.forEach(customer => {
const resultItem = document.createElement('div');
resultItem.className = 'p-2 hover:bg-gray-100 cursor-pointer';
resultItem.textContent = `${customer.number} - ${customer.name}`;
resultItem.onclick = () => {
document.getElementById('customer-search').value = `${customer.number} - ${customer.name}`;
resultsContainer.classList.add('hidden');
};
resultsContainer.appendChild(resultItem);
});
}
resultsContainer.classList.remove('hidden');
}
function loadBillingHistory() {
const billingHistoryTable = document.getElementById('billing-history');
billingHistoryTable.innerHTML = '';
const filter = document.getElementById('billing-filter').value;
const monthFilter = document.getElementById('billing-month').value;
let filteredBills = [...bills];
// Apply status filter
if (filter === 'paid') {
filteredBills = filteredBills.filter(bill => bill.status === 'paid');
} else if (filter === 'pending') {
filteredBills = filteredBills.filter(bill => bill.status === 'pending');
} else if (filter === 'overdue') {
filteredBills = filteredBills.filter(bill => bill.status === 'overdue');
}
// Apply month filter
if (monthFilter) {
filteredBills = filteredBills.filter(bill => {
const billDate = new Date(bill.date);
const billMonth = billDate.getFullYear() + '-' + String(billDate.getMonth() + 1).padStart(2, '0');
return billMonth === monthFilter;
});
}
// Sort by date (newest first)
filteredBills.sort((a, b) => new Date(b.date) - new Date(a.date));
filteredBills.forEach(bill => {
const row = document.createElement('tr');
const numberCell = document.createElement('td');
numberCell.className = 'px-6 py-4 whitespace-nowrap';
numberCell.textContent = bill.customerNumber;
const customerCell = document.createElement('td');
customerCell.className = 'px-6 py-4 whitespace-nowrap';
customerCell.textContent = bill.customerName;
const areaCell = document.createElement('td');
areaCell.className = 'px-6 py-4 whitespace-nowrap';
areaCell.textContent = bill.areaName;
const consumptionCell = document.createElement('td');
consumptionCell.className = 'px-6 py-4 whitespace-nowrap';
consumptionCell.textContent = `${bill.consumption} m³`;
const amountCell = document.createElement('td');
amountCell.className = 'px-6 py-4 whitespace-nowrap';
amountCell.textContent = `${bill.amount + bill.lateFee} MZN`;
const dateCell = document.createElement('td');
dateCell.className = 'px-6 py-4 whitespace-nowrap';
dateCell.textContent = new Date(bill.date).toLocaleDateString('pt-PT');
const statusCell = document.createElement('td');
statusCell.className = 'px-6 py-4 whitespace-nowrap';
const statusBadge = document.createElement('span');
statusBadge.className = 'px-2 py-1 text-xs rounded-full';
if (bill.status === 'paid') {
statusBadge.classList.add('bg-green-100', 'text-green-800');
statusBadge.textContent = 'Pago';
} else if (bill.status === 'overdue') {
statusBadge.classList.add('bg-red-100', 'text-red-800');
statusBadge.textContent = 'Atrasado';
} else {
statusBadge.classList.add('bg-yellow-100', 'text-yellow-800');
statusBadge.textContent = 'Pendente';
}
statusCell.appendChild(statusBadge);
const actionsCell = document.createElement('td');
actionsCell.className = 'px-6 py-4 whitespace-nowrap text-right text-sm font-medium';
const viewButton = document.createElement('button');
viewButton.className = 'text-primary hover:text-secondary mr-2';
viewButton.innerHTML = '<i class="fas fa-eye"></i>';
viewButton.title = 'Ver Detalhes';
viewButton.onclick = () => viewBillDetails(bill.id);
const editButton = document.createElement('button');
editButton.className = 'text-yellow-600 hover:text-yellow-800 mr-2';
editButton.innerHTML = '<i class="fas fa-edit"></i>';
editButton.title = 'Editar';
editButton.onclick = () => editBill(bill.id);
const deleteButton = document.createElement('button');
deleteButton.className = 'text-red-600 hover:text-red-800';
deleteButton.innerHTML = '<i class="fas fa-trash"></i>';
deleteButton.title = 'Eliminar';
deleteButton.onclick = () => confirmDeleteBill(bill.id);
actionsCell.appendChild(viewButton);
actionsCell.appendChild(editButton);
actionsCell.appendChild(deleteButton);
row.appendChild(numberCell);
row.appendChild(customerCell);
row.appendChild(areaCell);
row.appendChild(consumptionCell);
row.appendChild(amountCell);
row.appendChild(dateCell);
row.appendChild(statusCell);
row.appendChild(actionsCell);
billingHistoryTable.appendChild(row);
});
// If no bills, show message
if (filteredBills.length === 0) {
const emptyRow = document.createElement('tr');
const emptyCell = document.createElement('td');
emptyCell.colSpan = 8;
emptyCell.className = 'px-6 py-4 text-center text-gray-500';
emptyCell.textContent = 'Nenhuma cobrança encontrada';
emptyRow.appendChild(emptyCell);
billingHistoryTable.appendChild(emptyRow);
}
}
function viewBillDetails(billId) {
const bill = bills.find(b => b.id == billId);
if (!bill) return;
// Generate receipt
document.getElementById('receipt-number').textContent = `REC-${bill.id.toString().padStart(6, '0')}`;
document.getElementById('receipt-date').textContent = bill.paymentDate ?
new Date(bill.paymentDate).toLocaleDateString('pt-PT') :
new Date().toLocaleDateString('pt-PT');
const customer = customers.find(c => c.id == bill.customerId);
document.getElementById('receipt-customer').textContent = customer ?
`${customer.number} - ${customer
</html>