/** * TodayTab — the "Today" surface for the Water Log admin. * * Combines the hero gauge + summary stats with the recent entries * table + filter bar. Holds the entry CRUD, CSV export, and * "Generate Report" trigger. */ "use client"; import * as React from "react"; import { useRouter } from "next/navigation"; import AdminButton from "@/components/admin/design-system/AdminButton"; import { WaterGauge, SeasonMark } from "@/components/water/icons"; import { getWaterEntries, type AdminEntry, type AdminHeadgate, type AdminIrrigator, } from "@/actions/water-log/admin"; import { downloadWaterLogCSV, filterWaterLogEntries, shapeWaterLogEntry, type IrrigationSeasonSettings, type WaterLogReportRow, } from "@/lib/water-log-reporting"; import { formatDateTime } from "@/lib/format-date"; import { DayInput, EmptyState, FilterChip, MonthSelect, SectionHeader, StatTile, } from "../shared/Primitives"; import { DownloadIcon, ReportIcon, TableIcon } from "../shared/icons"; import type { Action, NotifyFn, State } from "../types"; export type TodayTabProps = { brandId: string; state: State; todayLabel: string; todayEntries: AdminEntry[]; todayTotal: number; inSeason: boolean; notify: NotifyFn; dispatch: React.Dispatch; onPreviewReport: () => void; onSeasonChange: (s: IrrigationSeasonSettings) => void; onRecipientNameChange: (v: string) => void; }; export function TodayTab({ brandId, state, todayLabel, todayEntries, todayTotal, inSeason, notify, dispatch, onPreviewReport, onSeasonChange, onRecipientNameChange, }: TodayTabProps) { const router = useRouter(); async function handleExportCSV() { dispatch({ type: "SET_CSV_LOADING", value: true }); try { const all = await getWaterEntries(brandId, 5000); const shaped: WaterLogReportRow[] = all.map(shapeWaterLogEntry); const exportFilters = { dateFrom: state.filters.dateFrom || undefined, dateTo: state.filters.dateTo || undefined, headgateId: state.filters.headgate || undefined, userId: state.filters.user || undefined, submittedVia: state.filters.via || undefined, }; const filtered = filterWaterLogEntries(shaped, exportFilters); downloadWaterLogCSV(`water-log-${state.today}.csv`, filtered); notify(`Exported ${filtered.length} entries`); } finally { dispatch({ type: "SET_CSV_LOADING", value: false }); } } return ( <> dispatch({ type: "TOGGLE_REPORT_SETTINGS" })} onSeasonChange={onSeasonChange} onRecipientNameChange={onRecipientNameChange} onPreviewReport={onPreviewReport} showReportSettings={state.showReportSettings} /> router.push(`/admin/water-log/entries/${e.id}`)} onExportCSV={handleExportCSV} /> ); } // ── TodaySummary (hero gauge + stats + report settings toggle) ────────────── function TodaySummary({ todayLabel, todayEntries, todayTotal, headgates, users, inSeason, showReportSettings, seasonStart, recipientName, onToggleReportSettings, onSeasonChange, onRecipientNameChange, onPreviewReport, }: { todayLabel: string; todayEntries: AdminEntry[]; todayTotal: number; headgates: AdminHeadgate[]; users: AdminIrrigator[]; inSeason: boolean; showReportSettings: boolean; seasonStart: IrrigationSeasonSettings; recipientName: string; onToggleReportSettings: () => void; onSeasonChange: (s: IrrigationSeasonSettings) => void; onRecipientNameChange: (v: string) => void; onPreviewReport: () => void; }) { return (
{/* Hero gauge */}
Tuxedo Ditch Co.
Vol. {todayTotal.toFixed(2)} {headgates[0]?.unit ?? "CFS"}

Today's Summary

{inSeason ? ( In irrigation season ) : ( Outside irrigation season )} · {todayEntries.length}{" "} {todayEntries.length === 1 ? "entry" : "entries"} · {todayLabel}

h.active).length.toString()} unit={`of ${headgates.length}`} /> u.active).length.toString()} unit={`of ${users.length}`} />
} onClick={onPreviewReport} > Preview Report } onClick={onToggleReportSettings} > Report Settings
{showReportSettings && ( )}
); } // ── Inline Report Settings (kept under the toggle button) ─────────────────── function ReportSettingsInline({ season, onSeasonChange, recipientName, onRecipientNameChange, }: { season: IrrigationSeasonSettings; onSeasonChange: (s: IrrigationSeasonSettings) => void; recipientName: string; onRecipientNameChange: (v: string) => void; }) { return (

Preview only. Daily-report delivery is wired through the scheduled api/cron/water-report endpoint and respects these settings.

onSeasonChange({ ...season, seasonStartMonth: m }) } onDayChange={(d) => onSeasonChange({ ...season, seasonStartDay: d })} /> onSeasonChange({ ...season, seasonEndMonth: m }) } onDayChange={(d) => onSeasonChange({ ...season, seasonEndDay: d })} />
); } function ReportSeasonRow({ label, month, day, onMonthChange, onDayChange, }: { label: string; month: number; day: number; onMonthChange: (m: number) => void; onDayChange: (d: number) => void; }) { return (
{label}
); } // ── RecentEntriesSection (table + filters) ────────────────────────────────── function RecentEntriesSection({ state, dispatch, onEditEntry, onExportCSV, }: { state: State; dispatch: React.Dispatch; onEditEntry: (e: AdminEntry) => void; onExportCSV: () => void; }) { const filteredEntries = state.entries.filter((e) => { if (state.filters.dateFrom && e.logged_at < state.filters.dateFrom) return false; if ( state.filters.dateTo && e.logged_at > state.filters.dateTo + "T23:59:59.999Z" ) return false; if (state.filters.headgate && e.headgate_id !== state.filters.headgate) return false; if (state.filters.user && e.user_id !== state.filters.user) return false; if (state.filters.via && e.submitted_via !== state.filters.via) return false; return true; }); return ( <> } > Export CSV } />
dispatch({ type: "SET_FILTER_DATE_FROM", value: e.target.value }) } className="border-0 bg-transparent text-sm text-[#1d1d1f] outline-none" aria-label="Filter: from date" /> dispatch({ type: "SET_FILTER_DATE_TO", value: e.target.value }) } className="border-0 bg-transparent text-sm text-[#1d1d1f] outline-none" aria-label="Filter: to date" /> {(state.filters.dateFrom || state.filters.dateTo || state.filters.headgate || state.filters.user || state.filters.via) && ( )} {filteredEntries.length} of {state.entries.length} entries
{filteredEntries.length === 0 ? ( } title="No entries match" body={ state.entries.length === 0 ? "No readings have been logged yet." : "Try clearing the filters above." } /> ) : (
{filteredEntries.map((e, i) => ( ))}
When User Headgate Measurement Gallons Method Via
{formatDateTime(e.logged_at)} {e.user_name} {e.headgate_name} {e.measurement}{" "} {e.unit} {e.total_gallons != null ? e.total_gallons.toFixed(1) : "—"} {e.method} {e.submitted_via}
)} ); }