"use client"; /* eslint-disable react-hooks/set-state-in-effect */ import { useState, useEffect, useCallback } from "react"; import GlassModal from "@/components/admin/GlassModal"; import { getTimeTrackingWorkers, getTimeTrackingTasks, getWorkerTimeLogs, getTimeTrackingSummary, createTimeWorker, updateTimeWorker, deleteTimeWorker, resetTimeWorkerPin, createTimeTask, updateTimeTask, deleteTimeTask, type TimeWorker, type TimeTask, type TimeLog, type TimeSummary, } from "@/actions/time-tracking"; import { formatDate } from "@/lib/format-date"; import TimeTrackingSettingsClient from "./TimeTrackingSettingsClient"; import { AdminButton, AdminFilterTabs } from "./design-system"; // One-color outline icons const Icons = { clock: (className: string) => ( ), chart: (className: string) => ( ), user: (className: string) => ( ), clipboard: (className: string) => ( ), plus: (className: string) => ( ), refresh: (className: string) => ( ), download: (className: string) => ( ), edit: (className: string) => ( ), trash: (className: string) => ( ), fileText: (className: string) => ( ), timer: (className: string) => ( ), settings: (className: string) => ( ), }; const PAGE_SIZE = 50; // ── Status Badge ───────────────────────────────────────────────────────────── function StatusBadge({ active }: { active: boolean }) { return ( {active ? "Active" : "Inactive"} ); } // ── Formatters ──────────────────────────────────────────────────────────────── function formatHours(h: number): string { return h < 1 ? `${(h * 60).toFixed(0)}m` : `${h.toFixed(1)}h`; } function formatMinutes(minutes: number): string { const h = minutes / 60; return h < 1 ? `${minutes}m` : `${h.toFixed(1)}h`; } // ── Main Component ─────────────────────────────────────────────────────────── type Tab = "summary" | "workers" | "tasks" | "logs" | "settings"; const TABS = [ { value: "summary", label: "Summary", icon: Icons.chart("h-4 w-4") }, { value: "workers", label: "Workers", icon: Icons.user("h-4 w-4") }, { value: "tasks", label: "Tasks", icon: Icons.clipboard("h-4 w-4") }, { value: "logs", label: "Logs", icon: Icons.clock("h-4 w-4") }, { value: "settings", label: "Settings", icon: Icons.settings("h-4 w-4") }, ]; export default function TimeTrackingAdminPanel({ brandId }: { brandId?: string }) { const [tab, setTab] = useState(() => { if (typeof window !== "undefined") { const params = new URLSearchParams(window.location.search); const t = params.get("tab"); if (t === "tasks" || t === "workers") return t as Tab; } return "summary" as Tab; }); const [workers, setWorkers] = useState([]); const [tasks, setTasks] = useState([]); const [logs, setLogs] = useState([]); const [summary, setSummary] = useState(null); const [loading, setLoading] = useState(true); const [dateRange, setDateRange] = useState<{ start: string; end: string }>(() => { const now = new Date(); const start = new Date(now.getFullYear(), now.getMonth(), 1).toISOString().slice(0, 10); return { start, end: now.toISOString().slice(0, 10) }; }); const [selectedWorker, setSelectedWorker] = useState(""); const [selectedTask, setSelectedTask] = useState(""); const [logPage, setLogPage] = useState(0); const [workerModal, setWorkerModal] = useState(null); const [taskModal, setTaskModal] = useState(null); const [exportModal, setExportModal] = useState(false); const [logModal, setLogModal] = useState(null); const load = useCallback(async () => { if (!brandId) return; setLoading(true); const [w, t, s, l] = await Promise.all([ getTimeTrackingWorkers(brandId), getTimeTrackingTasks(brandId, false), getTimeTrackingSummary(brandId, dateRange.start, dateRange.end), getWorkerTimeLogs(brandId, { workerId: selectedWorker || undefined, taskId: selectedTask || undefined, start: dateRange.start, end: dateRange.end, limit: PAGE_SIZE, offset: logPage * PAGE_SIZE, }), ]); setWorkers(w); setTasks(t); setSummary(s); setLogs(l); setLoading(false); }, [brandId, dateRange, selectedWorker, selectedTask, logPage]); useEffect(() => { load(); }, [load]); useEffect(() => { setLogPage(0); }, [dateRange, selectedWorker, selectedTask]); if (!brandId) { return (

Select a brand to view time tracking.

); } return (
{/* Header with date range */}
{Icons.clock("h-5 w-5 sm:h-6 sm:w-6 text-white")}

Time Tracking

{dateRange.start} — {dateRange.end}

setExportModal(true)} icon={Icons.download("h-4 w-4")} variant="secondary" > Export
{/* Tab navigation - using AdminFilterTabs */} setTab(t as Tab)} tabs={TABS} size="md" showCounts={false} />
{/* Content */}
{/* ── Summary Tab ── */} {tab === "summary" && ( )} {/* ── Workers Tab ── */} {tab === "workers" && ( setWorkerModal("new")} onEdit={(w) => setWorkerModal(w)} brandId={brandId} onSave={() => { load(); setWorkerModal(null); }} /> )} {/* ── Tasks Tab ── */} {tab === "tasks" && ( setTaskModal("new")} onEdit={(t) => setTaskModal(t)} brandId={brandId} onSave={() => { load(); setTaskModal(null); }} /> )} {/* ── Logs Tab ── */} {tab === "logs" && ( )} {/* ── Settings Tab ── */} {tab === "settings" && (
{Icons.settings("w-4 h-4 sm:w-5 sm:h-5 text-[var(--admin-bg)]")}

Time Tracking Settings

Configure your time tracking workflow

)}
{/* Worker Modal */} {workerModal !== null && ( setWorkerModal(null)} onSave={(saved) => { if (saved) load(); setWorkerModal(null); }} /> )} {/* Task Modal */} {taskModal !== null && ( setTaskModal(null)} onSave={(saved) => { if (saved) load(); setTaskModal(null); }} /> )} {/* Export Modal */} {exportModal && ( setExportModal(false)}>

Download time logs for the selected date range.

{ window.location.href = `/api/time-tracking/export?brandId=${brandId}&start=${dateRange.start}&end=${dateRange.end}`; setExportModal(false); }} icon={Icons.download("h-4 w-4")} fullWidth > Download CSV
)} {/* Log Detail Modal */} {logModal && ( setLogModal(null)}>

Worker

{logModal.worker_name}

Task

{logModal.task_name || "—"}

Clock In

{logModal.clock_in ? formatDate(logModal.clock_in) : "—"}

Clock Out

{logModal.clock_out ? formatDate(logModal.clock_out) : "Open"}

Duration

{formatMinutes(logModal.total_minutes)}

Notes

{logModal.notes || "—"}

)}
); } // ── Summary Tab ────────────────────────────────────────────────────────────── function SummaryTab({ summary, workers, loading, dateRange, setDateRange, onRefresh }: { summary: TimeSummary | null; workers: TimeWorker[]; loading: boolean; dateRange: { start: string; end: string }; setDateRange: React.Dispatch>; onRefresh: () => void; }) { return (
{/* Filters */}
setDateRange((prev) => ({ ...prev, start: e.target.value }))} className="px-3 py-2 rounded-lg border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] text-sm focus:outline-none focus:border-[var(--admin-accent)]" />
setDateRange((prev) => ({ ...prev, end: e.target.value }))} className="px-3 py-2 rounded-lg border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] text-sm focus:outline-none focus:border-[var(--admin-accent)]" />
Refresh
{/* Loading */} {loading ? (
Loading...
) : summary ? ( <> {/* Stats */}
{Icons.timer("h-4 w-4")} Total Hours

{formatMinutes(summary.totals.total_hours * 60)}

{summary.totals.entry_count} entries

{Icons.fileText("h-4 w-4")} Entries

{summary.totals.entry_count}

This period

{Icons.clock("h-4 w-4")} Open Clock-ins

{summary.totals.open_count}

In progress

{Icons.user("h-4 w-4")} Workers

{workers.length}

Active

{/* By Worker Table */}

By Worker

{(summary.by_worker ?? []).length === 0 ? ( ) : ( (summary.by_worker ?? []).map((w) => ( )) )}
Worker Entries Total Hours
No data
{w.name} {w.entry_count} {formatMinutes(w.total_hours * 60)}
) : (

No summary data available

)}
); } // ── Workers Tab ─────────────────────────────────────────────────────────────── function WorkersTab({ workers, onAdd, onEdit, brandId, onSave }: { workers: TimeWorker[]; onAdd: () => void; onEdit: (w: TimeWorker) => void; brandId: string; onSave: () => void; }) { return (

Workers ({workers.length})

Add Worker
{workers.length === 0 ? ( ) : ( workers.map((w) => ( )) )}
Name PIN Status Language
No workers yet
{w.name} {w.pin || "—"} {w.lang?.toUpperCase() ?? "EN"}
); } // ── Tasks Tab ──────────────────────────────────────────────────────────────── function TasksTab({ tasks, onAdd, onEdit, brandId, onSave }: { tasks: TimeTask[]; onAdd: () => void; onEdit: (t: TimeTask) => void; brandId: string; onSave: () => void; }) { return (

Tasks ({tasks.length})

Add Task
{tasks.length === 0 ? ( ) : ( tasks.map((t) => ( )) )}
Name Unit Status
No tasks yet
{t.name}
{t.name_es &&
{t.name_es}
}
{t.unit || "hours"}
); } // ── Logs Tab ───────────────────────────────────────────────────────────────── function LogsTab({ logs, workers, tasks, dateRange, setDateRange, selectedWorker, setSelectedWorker, selectedTask, setSelectedTask, loading, onRefresh, onViewLog }: { logs: TimeLog[]; workers: TimeWorker[]; tasks: TimeTask[]; dateRange: { start: string; end: string }; setDateRange: React.Dispatch>; selectedWorker: string; setSelectedWorker: (v: string) => void; selectedTask: string; setSelectedTask: (v: string) => void; loading: boolean; onRefresh: () => void; onViewLog: (log: TimeLog) => void; }) { return (
{/* Filters */}
setDateRange((prev) => ({ ...prev, start: e.target.value }))} className="px-3 py-2 rounded-lg border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] text-sm focus:outline-none focus:border-[var(--admin-accent)]" />
setDateRange((prev) => ({ ...prev, end: e.target.value }))} className="px-3 py-2 rounded-lg border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] text-sm focus:outline-none focus:border-[var(--admin-accent)]" />
Refresh
{/* Logs Table */} {loading ? (
Loading...
) : (

Time Logs

{logs.length === 0 ? ( ) : ( logs.map((log) => ( onViewLog(log)}> )) )}
Worker Task Clock In Clock Out Duration
No logs yet
{log.worker_name} {log.task_name || "—"} {log.clock_in ? formatDate(log.clock_in) : "—"} {log.clock_out ? formatDate(log.clock_out) : "Open"} {formatMinutes(log.total_minutes)}
)}
); } // ── Worker Modal ───────────────────────────────────────────────────────────── function WorkerModal({ worker, brandId, onClose, onSave }: { worker: TimeWorker | null; brandId: string; onClose: () => void; onSave: (saved: boolean) => void; }) { const [name, setName] = useState(worker?.name ?? ""); const [role, setRole] = useState(worker?.role ?? "worker"); const [lang, setLang] = useState(worker?.lang ?? "en"); const [active, setActive] = useState(worker?.active ?? true); const [loading, setLoading] = useState(false); const isEdit = !!worker; async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setLoading(true); try { if (isEdit && worker) { await updateTimeWorker(worker.id, name, role, lang, active); } else { await createTimeWorker(brandId, name, role, lang); } onSave(true); } catch { onSave(false); } setLoading(false); } async function handleResetPin() { if (!worker) return; setLoading(true); await resetTimeWorkerPin(worker.id); setLoading(false); } async function handleDelete() { if (!worker || !confirm("Delete this worker?")) return; setLoading(true); await deleteTimeWorker(worker.id); onSave(true); setLoading(false); } return (
setName(e.target.value)} required className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]" />
{isEdit && worker && ( <> Reset PIN Delete )} {loading ? "Saving..." : isEdit ? "Update" : "Create"}
); } // ── Task Modal ─────────────────────────────────────────────────────────────── function TaskModal({ task, brandId, onClose, onSave }: { task: TimeTask | null; brandId: string; onClose: () => void; onSave: (saved: boolean) => void; }) { const [name, setName] = useState(task?.name ?? ""); const [nameEs, setNameEs] = useState(task?.name_es ?? ""); const [unit, setUnit] = useState(task?.unit ?? "hours"); const [active, setActive] = useState(task?.active ?? true); const [loading, setLoading] = useState(false); const isEdit = !!task; async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setLoading(true); try { if (isEdit && task) { await updateTimeTask(task.id, name, nameEs, unit, active, task.sort_order); } else { await createTimeTask(brandId, name, nameEs || null, unit); } onSave(true); } catch { onSave(false); } setLoading(false); } async function handleDelete() { if (!task || !confirm("Delete this task?")) return; setLoading(true); await deleteTimeTask(task.id); onSave(true); setLoading(false); } return (
setName(e.target.value)} required placeholder="e.g., Harvesting" className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]" />
setNameEs(e.target.value)} placeholder="e.g., Cosecha" className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]" />
{isEdit && task && ( Delete )} {loading ? "Saving..." : isEdit ? "Update" : "Create"}
); }