This commit is contained in:
Stefan Lazic committed 2026-08-03 21:15:24 +01:00
1 parent ec5583f972
commit 546613e677
189 files changed
+9873 -655

No files matched your search

@@ -8,6 +8,7 @@ import { BottomSheet } from "@/components/ui/bottom-sheet";
import { Input } from "@/components/ui/input";
import type { TimeOffActionResult, TimeOffConflictItem } from "../actions/time-off";
import { createTimeOffAction, deleteTimeOffAction, updateTimeOffAction } from "../actions/time-off";
import type { TimeOffInput } from "@/services/staff/staff.service";
export interface EditableTimeOff {
id: string;
@@ -16,6 +17,26 @@ export interface EditableTimeOff {
reason: string | null;
}
/** Same DI shape as AppointmentStatusActions' `actions` prop — the owner-on-behalf-of-staff editor injects its own Server Actions bound to a chosen staffId instead of forking this component. */
export interface TimeOffActionSet {
create: (
input: Pick<TimeOffInput, "startDate" | "endDate" | "reason">,
acknowledgeConflicts?: boolean,
) => Promise<TimeOffActionResult>;
update: (
timeOffId: string,
input: Pick<TimeOffInput, "startDate" | "endDate" | "reason">,
acknowledgeConflicts?: boolean,
) => Promise<TimeOffActionResult>;
remove: (timeOffId: string) => Promise<TimeOffActionResult>;
}
const DEFAULT_ACTIONS: TimeOffActionSet = {
create: createTimeOffAction,
update: updateTimeOffAction,
remove: deleteTimeOffAction,
};
function todayIso(): string {
return new Date().toISOString().slice(0, 10);
}
@@ -24,10 +45,14 @@ export function TimeOffFormSheet({
open,
onClose,
existing,
actions = DEFAULT_ACTIONS,
appointmentHrefBase = "/staff/appointments",
}: {
open: boolean;
onClose: () => void;
existing?: EditableTimeOff | null;
actions?: TimeOffActionSet;
appointmentHrefBase?: string;
}) {
const router = useRouter();
const [startDate, setStartDate] = useState(existing?.startDate ?? todayIso());
@@ -60,8 +85,8 @@ export function TimeOffFormSheet({
startTransition(async () => {
const input = { startDate, endDate, reason: reason.trim() || undefined };
const result: TimeOffActionResult = existing
? await updateTimeOffAction(existing.id, input, acknowledgeConflicts)
: await createTimeOffAction(input, acknowledgeConflicts);
? await actions.update(existing.id, input, acknowledgeConflicts)
: await actions.create(input, acknowledgeConflicts);
if (!result.success) {
setError(result.error ?? "Something went wrong.");
setConflicts(result.conflicts ?? null);
@@ -75,7 +100,7 @@ export function TimeOffFormSheet({
const handleDelete = () => {
if (!existing) return;
startDeleteTransition(async () => {
const result = await deleteTimeOffAction(existing.id);
const result = await actions.remove(existing.id);
if (!result.success) {
setError(result.error ?? "Couldn't remove this time off.");
return;
@@ -98,7 +123,7 @@ export function TimeOffFormSheet({
<ul className="space-y-2">
{conflicts.map((c) => (
<li key={c.id} className="border-line rounded-lg border bg-white p-3 text-xs">
<Link href={`/staff/appointments/${c.id}`} className="font-bold underline">
<Link href={`${appointmentHrefBase}/${c.id}`} className="font-bold underline">
{c.customerName}
</Link>
<p className="text-muted">{c.serviceNames}</p>