+ {/* ── Page Header ─────────────────────────────────────────────────── */}
+
+
+
+
+
+
Servicios Municipales
+
+ Municipalidad de Belén — recibos y consumo de agua
+
+
+
+
+ {/* ── Summary Cards ───────────────────────────────────────────────── */}
+
+
+
+
+
+
+ Último recibo
+
+
+
+ {latestReceipt ? formatCRC(latestReceipt.total) : '—'}
+
+ {latestReceipt && (
+
+ {periodLabel(latestReceipt.period)}
+
+ )}
+
+
+
+
+
+
+
+
+ Promedio mensual
+
+
+
+ {receipts.length > 0 ? formatCRC(avgMonthly) : '—'}
+
+
+ {receipts.length} {receipts.length === 1 ? 'recibo' : 'recibos'}
+
+
+
+
+
+
+
+
+
+ Consumo actual
+
+
+
+ {currentConsumption.total > 0 ? `${currentConsumption.total} m³` : '—'}
+
+ {chartData.length > 0 && (
+
+ {periodLabel(chartData[chartData.length - 1].period)}
+
+ )}
+
+
+
+
+
+
+ {consumptionDelta <= 0 ? (
+
+ ) : (
+
+ )}
+
+ Variación
+
+
+
+ {currentConsumption.prev > 0
+ ? `${consumptionDelta > 0 ? '+' : ''}${consumptionDelta} m³`
+ : '—'}
+
+
+ vs mes anterior
+
+
+
+
+
+ {/* ── Water Consumption Chart ─────────────────────────────────────── */}
+ {chartData.length > 0 && (
+
+
+
+ Consumo de Agua (m³)
+
+
+
+
+
+
+
+
+ } />
+
+
+
+
+
+ )}
+
+ {/* ── Receipt History ──────────────────────────────────────────────── */}
+
+
+
+ Historial de Recibos
+
+ {loading && receipts.length === 0 ? (
+
+
+
+ Cargando...
+
+
+ ) : receipts.length === 0 ? (
+
+
+
+ No hay recibos aún. Sube un PDF para comenzar.
+
+
+ ) : (
+
+
+
+ {receipts.map((receipt) => (
+
+
+
+
+
+ {periodLabel(receipt.period)}
+
+
+ Vence{' '}
+ {new Date(receipt.due_date).toLocaleDateString('es-CR', {
+ day: 'numeric',
+ month: 'short',
+ })}
+
+
+
+ {formatCRC(receipt.total)}
+
+
+
+
+
+ {/* Charges breakdown */}
+
+
+
+
+ |
+ Detalle
+ |
+
+ Monto
+ |
+
+
+
+ {receipt.raw_charges.map((charge, i) => (
+
+ | {charge.detail} |
+
+ {formatCRC(charge.amount)}
+ |
+
+ ))}
+
+
+ {receipt.interests > 0 && (
+
+ | Intereses |
+
+ {formatCRC(receipt.interests)}
+ |
+
+ )}
+ {receipt.iva > 0 && (
+
+ | IVA |
+
+ {formatCRC(receipt.iva)}
+ |
+
+ )}
+
+ | Total |
+
+ {formatCRC(receipt.total)}
+ |
+
+
+
+
+
+ {/* Meta info */}
+
+ Cuenta: {receipt.account}
+ Finca: {receipt.finca}
+
+ Fecha:{' '}
+ {new Date(receipt.receipt_date).toLocaleDateString('es-CR')}
+
+
+
+
+
+ ))}
+
+
+
+ )}
+
+
+ {/* ── PDF Upload ──────────────────────────────────────────────────── */}
+
+
+
+ Subir Recibo
+
+
+
+ {/* Drop zone */}
+ { e.preventDefault(); setIsDragging(true); }}
+ onDragLeave={(e) => { e.preventDefault(); setIsDragging(false); }}
+ onDrop={(e) => { e.preventDefault(); setIsDragging(false); handleFile(e.dataTransfer.files); }}
+ onClick={() => fileInputRef.current?.click()}
+ role="button"
+ tabIndex={0}
+ onKeyDown={(e) => e.key === 'Enter' && fileInputRef.current?.click()}
+ aria-label="Seleccionar archivo PDF"
+ className={[
+ 'border-2 border-dashed rounded-lg p-8',
+ 'flex flex-col items-center justify-center gap-3',
+ 'cursor-pointer transition-colors select-none',
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
+ isDragging
+ ? 'border-primary bg-primary/5'
+ : 'border-border hover:border-primary/50 hover:bg-muted/30',
+ ].join(' ')}
+ >
+
+
+
+ {isDragging
+ ? 'Suelta el archivo aquí'
+ : 'Arrastra el PDF aquí o toca para seleccionar'}
+
+
+ Solo archivos PDF · Recibo Municipal de Belén
+
+
+
+
+ handleFile(e.target.files)}
+ />
+
+ {/* Selected file */}
+ {uploadedFile && (
+
+
+
+
+
{uploadedFile.name}
+
{formatFileSize(uploadedFile.size)}
+
+
+
+
+ )}
+
+ {/* Submit */}
+
+
+ {/* Upload result */}
+ {uploadResult && (
+ 0 && !uploadResult.receipt
+ ? 'border-destructive/50 bg-destructive/5'
+ : 'border-emerald-500/50 bg-emerald-500/5',
+ ].join(' ')}
+ >
+
+ {uploadResult.receipt ? (
+
+ ) : (
+
+ )}
+
+ {uploadResult.imported > 0 && 'Recibo importado'}
+ {uploadResult.updated > 0 && 'Recibo actualizado'}
+ {!uploadResult.receipt && 'Error al procesar'}
+
+
+ {uploadResult.receipt && (
+
+
+ {periodLabel(uploadResult.receipt.period)}
+
+
+ {formatCRC(uploadResult.receipt.total)}
+
+
+ )}
+ {uploadResult.errors.map((err, i) => (
+
{err}
+ ))}
+
+ )}
+
+
+
+
+ );
+}