"use client" import { useState, useEffect } from "react" import { useAuth } from "@/lib/auth-context" import { apiFetch, getErrorMessage } from "@/lib/apiutils" import { toast } from "sonner" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { format } from "date-fns" import { es } from "date-fns/locale" import { CloudRain, Trash2, Calendar, Droplets, Plus, Pencil } from "lucide-react" import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip as RechartsTooltip, ResponsiveContainer, Cell } from "recharts" import { PaginationControl } from "@/components/ui/pagination-control" import { DashboardLayout } from "@/components/dashboard-layout" interface Establecimiento { id: number nombre: string } interface Precipitacion { id: number establecimiento_id: number fecha: string milimetros: number observaciones: string | null establecimiento?: Establecimiento } export default function PrecipitacionesPage() { const { user } = useAuth() const [mounted, setMounted] = useState(false) // Data const [establecimientos, setEstablecimientos] = useState([]) const [precipitaciones, setPrecipitaciones] = useState([]) const [stats, setStats] = useState(null) // Filters and Pagination const [selectedEstablecimiento, setSelectedEstablecimiento] = useState("all") const [selectedYear, setSelectedYear] = useState(new Date().getFullYear().toString()) const [currentPage, setCurrentPage] = useState(1) const [loading, setLoading] = useState(false) // Modal Form State const [isDialogOpen, setIsDialogOpen] = useState(false) const [fecha, setFecha] = useState(format(new Date(), "yyyy-MM-dd")) const [milimetros, setMilimetros] = useState("") const [establecimientoId, setEstablecimientoId] = useState("") const [observaciones, setObservaciones] = useState("") const [isSubmitting, setIsSubmitting] = useState(false) const [editingId, setEditingId] = useState(null) const itemsPerPage = 10 useEffect(() => { setMounted(true) fetchEstablecimientos() fetchPrecipitaciones() fetchStats() }, []) useEffect(() => { if (mounted) { fetchPrecipitaciones() fetchStats() } }, [selectedEstablecimiento, selectedYear]) const fetchEstablecimientos = async () => { try { const response = await apiFetch("/establecimientos?all=1") if (response.ok) { const responseData = await response.json() const data = responseData.data || responseData setEstablecimientos(data) if (data.length > 0) setEstablecimientoId(data[0].id.toString()) } } catch (error) { console.error("Error fetching establecimientos", error) } } const fetchPrecipitaciones = async () => { setLoading(true) try { let url = "/precipitaciones" const params = new URLSearchParams() if (selectedEstablecimiento !== "all") params.append("establecimiento_id", selectedEstablecimiento) if (selectedYear !== "all") params.append("year", selectedYear) if (params.toString()) { url += `?${params.toString()}` } const response = await apiFetch(url) if (response.ok) { const data = await response.json() setPrecipitaciones(data) setCurrentPage(1) } } catch (error) { console.error("Error fetching precipitaciones", error) } finally { setLoading(false) } } const fetchStats = async () => { try { let url = `/precipitaciones/stats?year=${selectedYear === "all" ? new Date().getFullYear() : selectedYear}` if (selectedEstablecimiento !== "all") { url += `&establecimiento_id=${selectedEstablecimiento}` } const response = await apiFetch(url) if (response.ok) { const data = await response.json() setStats(data) } } catch (error) { console.error("Error fetching stats", error) } } const handleOpenDialog = (p?: Precipitacion) => { if (p) { setEditingId(p.id) setFecha(p.fecha.substring(0, 10)) setMilimetros(p.milimetros.toString()) setEstablecimientoId(p.establecimiento_id.toString()) setObservaciones(p.observaciones || "") } else { setEditingId(null) setFecha(format(new Date(), "yyyy-MM-dd")) setMilimetros("") setObservaciones("") if (establecimientos.length > 0) { setEstablecimientoId(establecimientos[0].id.toString()) } } setIsDialogOpen(true) } const handleSubmit = async () => { if (!establecimientoId || !fecha || !milimetros) { toast.error("Complete los campos obligatorios (Establecimiento, Fecha, Milímetros)") return } setIsSubmitting(true) try { const method = editingId ? "PUT" : "POST" const url = editingId ? `/precipitaciones/${editingId}` : "/precipitaciones" const response = await apiFetch(url, { method, body: JSON.stringify({ establecimiento_id: establecimientoId, fecha, milimetros: parseFloat(milimetros), observaciones }), }) if (response.ok) { toast.success(`Precipitación ${editingId ? 'actualizada' : 'registrada'} correctamente`) setIsDialogOpen(false) fetchPrecipitaciones() fetchStats() } else { const error = await getErrorMessage(response) toast.error(error) } } catch (error) { toast.error("Error de red al registrar") } finally { setIsSubmitting(false) } } const handleDelete = async (id: number) => { if (!confirm("¿Está seguro de eliminar este registro?")) return try { const response = await apiFetch(`/precipitaciones/${id}`, { method: "DELETE", }) if (response.ok) { toast.success("Registro eliminado") fetchPrecipitaciones() fetchStats() } else { const error = await getErrorMessage(response) toast.error(error) } } catch (error) { toast.error("Error al eliminar") } } if (!mounted) return null // Pagination logic const totalPages = Math.ceil(precipitaciones.length / itemsPerPage) const paginatedPrecipitaciones = precipitaciones.slice( (currentPage - 1) * itemsPerPage, currentPage * itemsPerPage ) const availableYears = ["all", ...Array.from({ length: 10 }, (_, i) => (new Date().getFullYear() - i).toString())] return (

Registro de Precipitaciones

Administra y visualice los registros de lluvia de sus campos

{editingId ? "Editar Medición" : "Nueva Medición"} Cargue los mm llovidos
setFecha(e.target.value)} disabled={isSubmitting} required />
{ const val = e.target.value; const sanitized = val.replace(',', '.'); if (sanitized === '' || /^\d*\.?\d*$/.test(sanitized)) { setMilimetros(sanitized); } }} onBlur={(e) => { const num = parseFloat(e.target.value); if (!isNaN(num)) setMilimetros(num.toString()); }} disabled={isSubmitting} required />
setObservaciones(e.target.value)} disabled={isSubmitting} />
Acumulado {selectedYear !== "all" ? selectedYear : "General"} {loading ? (
) : (
{stats?.totalYear !== undefined ? Number(stats.totalYear).toFixed(1) : "0.0"} mm
)}

Lluvia total acumulada

Mes Más Lluvioso {loading ? (
) : (
{stats?.maxMonth !== undefined ? Number(stats.maxMonth).toFixed(1) : "0.0"} mm
)}

Mayor registro mensual

Eventos de Lluvia {loading ? (
) : (
{stats?.eventsCount || 0}
)}

Registros en el periodo

Historial de Precipitaciones Lista de precipitaciones cargadas
Fecha Establecimiento Milímetros Observaciones Acciones {loading ? ( Cargando registros... ) : precipitaciones.length === 0 ? ( No hay precipitaciones registradas. ) : ( paginatedPrecipitaciones.map((p) => ( {format(new Date(p.fecha.substring(0, 10) + "T12:00:00"), "dd MMM yyyy", { locale: es })} {p.establecimiento?.nombre} {p.milimetros} mm {p.observaciones || "-"}
)) )}
{/* Chart is shown ONLY if a specific year is selected, as month aggregation over 'all' years is confusing */} {selectedYear !== "all" && stats?.monthly && ( Análisis Mensual ({selectedYear}) Distribución de precipitaciones durante el año
{ if (active && payload && payload.length) { return (

{label}

Lluvia: {payload[0].value} mm

) } return null }} /> {stats.monthly.map((entry: any, index: number) => ( 0 ? '#3b82f6' : 'rgba(150, 150, 150, 0.2)'} /> ))}
)}
) }