54 lines
3.2 KiB
TypeScript
54 lines
3.2 KiB
TypeScript
"use client";
|
|
import * as React from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { useAuth } from "@/hooks/use-auth";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
|
|
import { workDate } from "@/lib/clock-day";
|
|
|
|
export function WorkerStatusControl({ id, name, active, onSaved }: { id: string; name: string; active: boolean; onSaved?: () => void }) {
|
|
const router = useRouter();
|
|
const { mode, expectedPin } = useAuth();
|
|
const [open, setOpen] = React.useState(false);
|
|
const [date, setDate] = React.useState(() => workDate(new Date().toISOString()));
|
|
const [busy, setBusy] = React.useState(false);
|
|
const [error, setError] = React.useState("");
|
|
const today = workDate(new Date().toISOString());
|
|
async function save(event: React.FormEvent) {
|
|
event.preventDefault();
|
|
if (busy) return;
|
|
setBusy(true); setError("");
|
|
try {
|
|
const response = await fetch(`/api/employees/${encodeURIComponent(id)}/status`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json", ...(mode === "pin" ? { "X-App-Pin": expectedPin } : {}) },
|
|
body: JSON.stringify({ status: active ? "inactive" : "active", inactiveFrom: active ? date : undefined }),
|
|
});
|
|
const body = await response.json();
|
|
if (!response.ok) throw new Error(body.error ?? "Could not update worker status");
|
|
setOpen(false);
|
|
window.dispatchEvent(new Event("mega:attention-changed"));
|
|
onSaved?.(); router.refresh();
|
|
} catch (cause) { setError(cause instanceof Error ? cause.message : "Could not update worker status"); }
|
|
finally { setBusy(false); }
|
|
}
|
|
return <>
|
|
<Button variant="outline" onClick={() => setOpen(true)} aria-label={`${active ? "Mark inactive" : "Reactivate"}: ${name}`}>{active ? "Mark inactive" : "Reactivate worker"}</Button>
|
|
<Dialog open={open} onOpenChange={(value) => { if (!busy) setOpen(value); }}>
|
|
<DialogContent className="sm:max-w-md">
|
|
<DialogHeader>
|
|
<DialogTitle>{active ? "Mark worker inactive" : "Reactivate worker"}</DialogTitle>
|
|
<DialogDescription>{name}</DialogDescription>
|
|
</DialogHeader>
|
|
<form onSubmit={save} className="space-y-4">
|
|
{active && <label className="block text-sm font-medium">Inactive from
|
|
<input type="date" required value={date} max={today} disabled={busy} onChange={(event) => setDate(event.target.value)} className="mt-1 w-full rounded-md border border-input bg-background px-3 py-2" />
|
|
</label>}
|
|
<p className="text-sm text-muted-foreground">{active ? "This worker will leave the active roster, current alerts and new clock-ins. Their rates, recorded hours and previous costs are kept." : "This worker will return to the active roster and can clock in again. Their existing history is kept."}</p>
|
|
{error && <p role="alert" className="text-sm text-destructive">{error}</p>}
|
|
<Button type="submit" disabled={busy || (active && (!date || date > today))} className="w-full">{busy ? "Saving…" : active ? "Mark inactive" : "Reactivate worker"}</Button>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>;
|
|
}
|