"use client"; /** * /admin/water-log/settings * * Site-admin-facing settings page for the Water Log module. Controls: * - Whether `/water/admin` is enabled * - 4-digit admin PIN (regenerate on demand) * - Session duration (hours) * - Per-coarse-permission flags (edit / delete / export) * - High/low alert phone + enable flag * * Visual language: Field Almanac. Same cream + forest palette as * the main WaterLogAdminPanel, but trimmed to a single column for * a focused "form" feel. */ import { useState, useEffect, useCallback } from "react"; import { getWaterAdminSettings, saveWaterAdminSettings, regenerateAdminPin, type AdminSettings, } from "@/actions/water-log/settings"; const TUXEDO_BRAND_ID = "64294306-5f42-463d-a5e8-2ad6c81a96de"; export default function WaterLogSettingsPage() { const [settings, setSettings] = useState(null); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [regenerating, setRegenerating] = useState(false); const [revealedPin, setRevealedPin] = useState(null); const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null); // Form state const [enabled, setEnabled] = useState(false); const [sessionDuration, setSessionDuration] = useState(12); const [canEdit, setCanEdit] = useState(true); const [canDelete, setCanDelete] = useState(true); const [canExport, setCanExport] = useState(true); const [alertPhone, setAlertPhone] = useState(""); const [alertsEnabled, setAlertsEnabled] = useState(false); const loadSettings = useCallback(async () => { setLoading(true); const data = await getWaterAdminSettings(TUXEDO_BRAND_ID); if (data) { setSettings(data); setEnabled(data.enabled); setSessionDuration(data.sessionDurationHours); setCanEdit(data.canEditEntries); setCanDelete(data.canDeleteEntries); setCanExport(data.canExportCsv); setAlertPhone(data.alertPhone ?? ""); setAlertsEnabled(data.alertsEnabled); } setLoading(false); }, []); useEffect(() => { // Load on mount — setState-in-effect is intentional here // because we're hydrating from server data. // eslint-disable-next-line react-hooks/set-state-in-effect void loadSettings(); }, [loadSettings]); async function handleSave(e: React.FormEvent) { e.preventDefault(); setSaving(true); setMessage(null); const result = await saveWaterAdminSettings(TUXEDO_BRAND_ID, { enabled, sessionDurationHours: sessionDuration, canEditEntries: canEdit, canDeleteEntries: canDelete, canExportCsv: canExport, alertPhone: alertPhone || null, alertsEnabled, }); if (result.success) { setMessage({ type: "success", text: "Settings saved" }); if (result.settings) setSettings(result.settings); } else { setMessage({ type: "error", text: result.error ?? "Save failed" }); } setSaving(false); } async function handleRegenerate() { if (!confirm("Regenerate the admin PIN? All current admin sessions will be signed out.")) { return; } setRegenerating(true); setMessage(null); const result = await regenerateAdminPin(TUXEDO_BRAND_ID); if (result.success && result.pin) { setRevealedPin(result.pin); setMessage({ type: "success", text: "New PIN generated. Save it now — it will not be shown again.", }); } else { setMessage({ type: "error", text: result.error ?? "Failed to regenerate PIN" }); } setRegenerating(false); } if (loading) { return (
Loading…
); } return (
{/* Header */}

§ 04 — Settings

Water Log · Admin Portal

Configure PIN access for the field admin portal at /water/admin. This is separate from the platform admin login.

{message && (
{message.text}
)} {/* Enable toggle */} {enabled && ( <> {/* PIN */} {revealedPin ? (

New PIN — write it down

{revealedPin}
) : (

{settings?.pin === null ? "Generate a new PIN to give to your water admin." : "A PIN is already configured. Regenerate to issue a new one (this signs out existing admin sessions)."}

)}
{/* Session Duration */}
setSessionDuration(parseInt(e.target.value, 10))} className="flex-1 accent-[#1a4d2e]" aria-label="Session duration in hours" /> {sessionDuration}h
{/* Permissions */}
{/* High/Low Alerts */} {alertsEnabled && (
setAlertPhone(e.target.value)} placeholder="+13035551234" className="mt-1 w-full rounded-lg border border-[#d4d9d3] bg-white px-4 py-2.5 text-base outline-none focus:border-[#1a4d2e] focus:ring-1 focus:ring-[#1a4d2e]" autoComplete="tel" />

U.S. format recommended. Include country code (e.g. +1 for USA).

)}
)}
); } function Card({ title, subtitle, children, }: { title: string; subtitle?: string; children: React.ReactNode; }) { return (

{title}

{subtitle &&

{subtitle}

}
{children}
); } function ToggleRow({ label, description, value, onChange, }: { label: string; description?: string; value: boolean; onChange: (v: boolean) => void; }) { return (

{label}

{description &&

{description}

}
); }