"use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import type { LeaveStatus, LeaveType } from "@prisma/client"; import { Badge } from "@/components/ui/badge"; import { AdminDialog } from "@/components/ui/admin-dialog"; import { applyLeave, decideLeave } from "./actions"; const INPUT = "w-full rounded-lg border border-neutral-300 px-3 py-2 text-sm focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20"; const LEAVE_TYPES: LeaveType[] = ["ANNUAL", "MEDICAL", "EMERGENCY", "UNPAID", "OTHER"]; const fmt = (iso: string) => new Date(iso).toLocaleDateString(); const label = (s: string) => s.replace(/_/g, " ").toLowerCase().replace(/\b\w/g, (m) => m.toUpperCase()); type Assignment = { id: string; crewName: string; rank: string; location: string }; type Request = { id: string; crewName: string; rank: string; location: string; type: LeaveType; status: LeaveStatus; fromDate: string; toDate: string; reason: string | null }; const STATUS_VARIANT: Record = { APPLIED: "warning", APPROVED: "success", REJECTED: "danger", CANCELLED: "secondary", }; export function LeaveManager({ assignments, requests, canApply, canDecide }: { assignments: Assignment[]; requests: Request[]; canApply: boolean; canDecide: boolean }) { const router = useRouter(); const [open, setOpen] = useState(false); const [pending, setPending] = useState(false); const [error, setError] = useState(""); const [f, setF] = useState({ assignmentId: "", type: "ANNUAL", fromDate: "", toDate: "", reason: "" }); const duration = f.fromDate && f.toDate ? Math.max(0, Math.round((new Date(f.toDate).getTime() - new Date(f.fromDate).getTime()) / 86400000) + 1) : 0; async function submitApply(e: React.FormEvent) { e.preventDefault(); setPending(true); setError(""); const fd = new FormData(); Object.entries(f).forEach(([k, v]) => v && fd.set(k, v)); const res = await applyLeave(fd); setPending(false); if ("error" in res) setError(res.error); else { setOpen(false); setF({ assignmentId: "", type: "ANNUAL", fromDate: "", toDate: "", reason: "" }); router.refresh(); } } return (

Leave

Site staff apply on behalf of crew · the Manager approves.

{canApply && }
{requests.length === 0 ? ( ) : requests.map((r) => ( router.refresh()} /> ))}
Crew Rank / location Type Dates Status
No leave requests.
setOpen(false)}>
setF({ ...f, fromDate: e.target.value })} required />
setF({ ...f, toDate: e.target.value })} required />
{duration > 0 &&

{duration} day{duration === 1 ? "" : "s"} of leave.

}
setF({ ...f, reason: e.target.value })} placeholder="Optional" />
{error &&

{error}

}
); } function DecisionRow({ r, canDecide, onDone }: { r: Request; canDecide: boolean; onDone: () => void }) { const [pending, setPending] = useState(false); const [error, setError] = useState(""); const [declineOpen, setDeclineOpen] = useState(false); const [reason, setReason] = useState(""); async function approve() { setPending(true); setError(""); const res = await decideLeave(r.id, true); setPending(false); if ("error" in res) setError(res.error); else onDone(); } async function decline(e: React.FormEvent) { e.preventDefault(); setPending(true); setError(""); const res = await decideLeave(r.id, false, reason); setPending(false); if ("error" in res) setError(res.error); else { setDeclineOpen(false); onDone(); } } return ( {r.crewName} {r.rank} · {r.location} {label(r.type)} {fmt(r.fromDate)} – {fmt(r.toDate)} {label(r.status)} {r.status === "APPLIED" && (canDecide ? (
) : Awaiting manager)} {error &&

{error}

} setDeclineOpen(false)}>