"use client"; import { useState, useEffect, useCallback } from "react"; import { useRouter } from "next/navigation"; import { getWaterEntries, getWaterDisplaySummary, getWaterIrrigators, getWaterHeadgatesAdmin, createWaterIrrigator, createWaterHeadgate, getWaterAlertLog, type WaterDisplaySummary, type WaterDisplayHeadgate, type AlertLogEntry } from "@/actions/water-log/admin"; import { logoutWaterAdmin } from "@/actions/water-log/field"; type WaterEntry = { id: string; headgate_id: string; user_id: string; headgate_name: string; user_name: string; measurement: number; unit: string; notes: string | null; submitted_via: string; logged_at: string; }; type WaterUser = { id: string; name: string; role: "irrigator" | "water_admin"; active: boolean; language_preference: string; last_used_at: string | null; created_at: string; }; type Headgate = { id: string; name: string; active: boolean; unit: string; created_at: string; }; const TUXEDO_BRAND_ID = "64294306-5f42-463d-a5e8-2ad6c81a96de"; const LABELS = { en: { title: "Water Log Admin", loggedInAs: "Logged in as", logout: "Salir", recentEntries: "Entradas Recientes", noEntries: "No entries yet", when: "Fecha", user: "Usuario", headgate: "Compuerta", measurement: "Medición", via: "Vía", field: "Campo", admin: "Admin", exportCSV: "Export CSV", clearFilters: "Clear", displaySummary: "Resumen de Pantalla", todayCount: "Entradas de hoy", lastUpdate: "Última actualización", refreshIn: "Refresh in", noHeadgates: "Sin compuertas activas", noLatest: "Sin lecturas", noEntriesToday: "Sin entradas hoy", recentEntriesLabel: "Entradas recientes", headgates: "Headgates", users: "Users", addHeadgate: "Add Headgate", addUser: "Add User", edit: "Edit", cancel: "Cancel", name: "Name", unit: "Unit", active: "Active", inactive: "Inactive", role: "Role", language: "Language", pin: "PIN", newPinFor: "New PIN for", writeThisDown: "Write this down — it will not be shown again.", dismiss: "Dismiss", saving: "Saving...", }, es: { title: "Admin Registro de Agua", loggedInAs: "Conectado como", logout: "Salir", recentEntries: "Entradas Recientes", noEntries: "Sin entradas aún", when: "Fecha", user: "Usuario", headgate: "Compuerta", measurement: "Medición", via: "Vía", field: "Campo", admin: "Admin", exportCSV: "Exportar CSV", clearFilters: "Limpiar", displaySummary: "Resumen de Pantalla", todayCount: "Entradas de hoy", lastUpdate: "Última actualización", refreshIn: "Refresh in", noHeadgates: "Sin compuertas activas", noLatest: "Sin lecturas", noEntriesToday: "Sin entradas hoy", recentEntriesLabel: "Entradas recientes", headgates: "Compuertas", users: "Usuarios", addHeadgate: "Agregar Compuerta", addUser: "Agregar Usuario", edit: "Editar", cancel: "Cancelar", name: "Nombre", unit: "Unidad", active: "Activo", inactive: "Inactivo", role: "Rol", language: "Idioma", pin: "PIN", newPinFor: "Nuevo PIN para", writeThisDown: "Write this down — it will not be shown again.", dismiss: "Dismiss", saving: "Saving...", }, }; function SummarySkeleton() { return (
{[1, 2, 3].map(i => (
))}
); } function TableSkeleton({ rows = 5 }: { rows?: number }) { return (
{Array.from({ length: rows }).map((_, i) => (
))}
); } function CardsSkeleton({ count = 4 }: { count?: number }) { return (
{Array.from({ length: count }).map((_, i) => (
))}
); } function formatDateTime(iso: string): string { return new Date(iso).toLocaleDateString("en-US", { month: "short", day: "numeric", hour: "numeric", minute: "2-digit", }); } const AGE_COLORS = { green: "bg-green-100 text-green-700", yellow: "bg-yellow-100 text-yellow-700", red: "bg-red-100 text-red-700", }; const UNIT_OPTIONS = ["CFS", "GPM", "gal", "ac-in", "ac-ft"]; function HeadgateCard({ hg }: { hg: WaterDisplayHeadgate }) { const ageColor = (["green", "yellow", "red"] as const)[ hg.minutes_ago === null ? 2 : hg.minutes_ago > 60 ? 1 : 0 ]; const ageLabel = hg.minutes_ago === null ? "Never" : hg.minutes_ago < 2 ? "Just now" : hg.minutes_ago < 60 ? `${hg.minutes_ago}m ago` : `${Math.floor(hg.minutes_ago / 60)}h ago`; return (

{hg.name}

{hg.latest_entry ? ( <>

{hg.latest_entry.measurement}{" "} {hg.unit}

{hg.latest_entry.user_name}

{ageLabel} ) : (

{LABELS.en.noLatest}

)}
); } function SectionHeader({ title, count, onToggle, action, }: { title: string; count?: number; onToggle: () => void; action?: React.ReactNode; }) { return (
{action}
); } export default function WaterAdminClient() { const router = useRouter(); const [lang, setLang] = useState<"en" | "es">("en"); const [displaySummary, setDisplaySummary] = useState(null); const [displayLoading, setDisplayLoading] = useState(true); const [refreshCountdown, setRefreshCountdown] = useState(30); const [allEntries, setAllEntries] = useState([]); const [users, setUsers] = useState([]); const [headgates, setHeadgates] = useState([]); const [loading, setLoading] = useState(true); const [alertLog, setAlertLog] = useState([]); // Filter state const [filterDateFrom, setFilterDateFrom] = useState(""); const [filterDateTo, setFilterDateTo] = useState(""); const [filterHeadgate, setFilterHeadgate] = useState(""); const [filterVia, setFilterVia] = useState(""); // Section toggles const [showHeadgates, setShowHeadgates] = useState(true); const [showUsers, setShowUsers] = useState(true); const [showEntries, setShowEntries] = useState(true); const [showAlerts, setShowAlerts] = useState(true); // Add forms const [showAddHg, setShowAddHg] = useState(false); const [showAddUser, setShowAddUser] = useState(false); const [newPin, setNewPin] = useState<{ name: string; pin: string } | null>(null); // New user form const [newUserName, setNewUserName] = useState(""); const [newUserLang, setNewUserLang] = useState<"en" | "es">("en"); const [savingUser, setSavingUser] = useState(false); const [newUserError, setNewUserError] = useState(null); // New headgate form const [newHgName, setNewHgName] = useState(""); const [newHgUnit, setNewHgUnit] = useState("CFS"); const [savingHg, setSavingHg] = useState(false); const [newHgError, setNewHgError] = useState(null); const t = LABELS[lang]; // Initial loading state const [initialLoading, setInitialLoading] = useState(true); useEffect(() => { const savedLang = document.cookie.match(/wl_lang=(en|es)/)?.[1] as "en" | "es" | undefined; if (savedLang) setLang(savedLang); loadAll(); }, []); const loadAll = async () => { setLoading(true); const [entries, usersData, headgatesData, alertsData] = await Promise.all([ getWaterEntries(TUXEDO_BRAND_ID, 500), getWaterIrrigators(TUXEDO_BRAND_ID), getWaterHeadgatesAdmin(TUXEDO_BRAND_ID), getWaterAlertLog(TUXEDO_BRAND_ID, 50), ]); setAllEntries(entries); setUsers(usersData); setHeadgates(headgatesData); setAlertLog(alertsData as AlertLogEntry[]); setLoading(false); setInitialLoading(false); }; const loadDisplaySummary = useCallback(async () => { const data = await getWaterDisplaySummary(TUXEDO_BRAND_ID); setDisplaySummary(data); setDisplayLoading(false); setRefreshCountdown(30); }, []); useEffect(() => { loadDisplaySummary(); const interval = setInterval(() => { setRefreshCountdown((c) => { if (c <= 1) { loadDisplaySummary(); return 30; } return c - 1; }); }, 1000); return () => clearInterval(interval); }, [loadDisplaySummary]); async function handleLogout() { await logoutWaterAdmin(); window.location.href = "/water"; } // Add user async function handleAddUser(e: React.FormEvent) { e.preventDefault(); if (!newUserName.trim()) return; setSavingUser(true); setNewUserError(null); const result = await createWaterIrrigator(TUXEDO_BRAND_ID, newUserName.trim(), newUserLang); if (result.success && result.pin) { setNewPin({ name: result.irrigator!.name, pin: result.pin }); setNewUserName(""); setShowAddUser(false); const updated = await getWaterIrrigators(TUXEDO_BRAND_ID); setUsers(updated); } else { setNewUserError(result.error ?? "Failed to add user"); } setSavingUser(false); } // Add headgate async function handleAddHg(e: React.FormEvent) { e.preventDefault(); if (!newHgName.trim()) return; setSavingHg(true); setNewHgError(null); const result = await createWaterHeadgate(TUXEDO_BRAND_ID, newHgName.trim(), newHgUnit); if (result.success) { setNewHgName(""); setNewHgUnit("CFS"); setShowAddHg(false); const updated = await getWaterHeadgatesAdmin(TUXEDO_BRAND_ID); setHeadgates(updated); } else { setNewHgError(result.error ?? "Failed to add headgate"); } setSavingHg(false); } // Filtered entries const filteredEntries = allEntries.filter((e) => { if (filterDateFrom && e.logged_at < filterDateFrom) return false; if (filterDateTo && e.logged_at > filterDateTo + "T23:59:59.999Z") return false; if (filterHeadgate && e.headgate_id !== filterHeadgate) return false; if (filterVia && e.submitted_via !== filterVia) return false; return true; }); const headgateOptions = Array.from( new Map(allEntries.map((e) => [e.headgate_id, e.headgate_name])).entries() ).map(([id, name]) => ({ id, name })); return (
{/* Header */}

{t.title}

{t.loggedInAs}: Admin

{/* ── Display Summary ── */}

{t.displaySummary}

{t.refreshIn}: {refreshCountdown}s
{initialLoading ? ( ) : displayLoading ? ( ) : displaySummary ? (
{displaySummary.headgates.length === 0 ? (

{t.noHeadgates}

) : (
{displaySummary.headgates.map((hg) => ( ))}
)}
{t.todayCount}

{displaySummary.today_count} {displaySummary.today_count > 0 && ( ({displaySummary.today_total.toFixed(1)} total) )}

{displaySummary.recent_entries.length > 0 && ( {t.lastUpdate}: {formatDateTime(displaySummary.recent_entries[0]?.logged_at)} )}
) : (
{t.noEntriesToday}
)}
{/* ── Headgates ── */}
setShowHeadgates((v) => !v)} action={ } /> {showAddHg && (
setNewHgName(e.target.value)} placeholder={t.name} className="flex-1 rounded-lg border border-stone-300 px-3 py-2 text-sm min-h-[44px]" required />
)} {newHgError && (
{newHgError}
)} {showHeadgates && (
{initialLoading ? ( ) : headgates.length === 0 ? (

{t.noHeadgates}

) : ( headgates.map((hg) => (
{hg.name} {hg.unit}
)) )}
)}
{/* ── Users ── */}
{newPin && (

{t.newPinFor} {newPin.name}:

{newPin.pin}

{t.writeThisDown}

)} setShowUsers((v) => !v)} action={ } /> {showAddUser && (
setNewUserName(e.target.value)} placeholder={t.name} className="flex-1 rounded-lg border border-stone-300 px-3 py-2 text-sm min-h-[44px]" required />
)} {newUserError && (
{newUserError}
)} {showUsers && (
{initialLoading ? ( ) : users.length === 0 ? (

No users

) : ( users.map((u) => (
{u.name} {u.role === "water_admin" ? t.admin : t.field}
)) )}
)}
{/* ── Entries ── */}
setShowEntries((v) => !v)} /> {showEntries && ( <> {/* Filters */}
setFilterDateFrom(e.target.value)} className="rounded-lg border border-stone-300 px-2 py-1.5 text-sm min-h-[36px]" /> setFilterDateTo(e.target.value)} className="rounded-lg border border-stone-300 px-2 py-1.5 text-sm min-h-[36px]" /> {(filterDateFrom || filterDateTo || filterHeadgate || filterVia) && ( )}
{initialLoading ? ( Array.from({ length: 8 }).map((_, i) => ( )) ) : filteredEntries.length === 0 ? ( ) : ( filteredEntries.map((e) => ( router.push(`/admin/water-log/entries/${e.id}?from=/water/admin`)} className="border-b border-stone-50 last:border-0 hover:bg-stone-50 cursor-pointer" > )) )}
{t.when} {t.user} {t.headgate} {t.measurement} {t.via}
{allEntries.length === 0 ? t.noEntries : "No matches"}
{formatDateTime(e.logged_at)} {e.user_name} {e.headgate_name} {e.measurement} {e.unit} {e.submitted_via === "field" ? t.field[0] : t.admin[0]}
)}
{/* ── Alert Log ── */}
setShowAlerts((v) => !v)} /> {showAlerts && ( <> {alertLog.length === 0 ? (

No alerts triggered yet.

) : (
{alertLog.map((a) => ( ))}
Time Headgate Type Reading Threshold
{a.formatted_time} {a.headgate_name} {a.alert_type === "high" ? "High ↑" : "Low ↓"} {a.reading_value} {a.threshold_value}
)} )}
); }