Najjar\NajjarV02 16fd7dd423 feat(shop-timings): day-of-week select and closed toggle in form
Add day_of_week dropdown and is_closed checkbox to the shop timing form.
In/out times become optional and disabled when the day is marked closed;
required otherwise via schema superRefine.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 14:39:55 +04:00

187 lines
6.6 KiB
TypeScript

"use client"
import { AlertTriangle, Plus, Save } from "lucide-react"
import { Button } from "@/shared/components/ui/button"
import { Alert, AlertTitle } from "@/shared/components/ui/alert"
import { FieldGroup } from "@/shared/components/ui/field"
import {
Rhform,
RhfTextField,
RhfSelectField,
RhfCheckboxField,
} from "@/shared/components/form"
import { toast } from "sonner"
import { useAuthApi } from "@/shared/useApi"
import { useResourceForm } from "@/shared/hooks/use-resource-form"
import { useFormMutation } from "@/shared/hooks/use-form-mutation"
import {
shopTimingFormSchema,
DAY_OF_WEEK_VALUES,
type ShopTimingFormValues,
} from "./shop-timing.schema"
import { SHOP_TIMING_ROUTES } from "@garage/api"
// ── Props ──
export type ShopTimingFormProps = {
resourceId?: string | null
initialData?: unknown
onSuccess?: () => void
}
// ── Options ──
const DAY_OPTIONS = DAY_OF_WEEK_VALUES.map((day) => ({
value: day,
label: day.charAt(0).toUpperCase() + day.slice(1),
}))
// ── Default values ──
const DEFAULT_VALUES: ShopTimingFormValues = {
title: "",
day_of_week: "",
is_closed: false,
in_time: "",
out_time: "",
full_day_hours: "",
half_day_hours: "",
punch_in: "",
punch_out: "",
before_time: "",
after_time: "",
is_default: false,
}
// ── Mapping helpers ──
function mapToFormValues(data: unknown): ShopTimingFormValues {
const d = (data as any)?.data ?? data ?? {}
return {
title: d.title ?? "",
day_of_week: d.day_of_week ?? "",
is_closed: d.is_closed ?? false,
in_time: d.in_time ?? "",
out_time: d.out_time ?? "",
full_day_hours: d.full_day_hours ?? "",
half_day_hours: d.half_day_hours ?? "",
punch_in: d.punch_in ?? "",
punch_out: d.punch_out ?? "",
before_time: d.before_time ?? "",
after_time: d.after_time ?? "",
is_default: d.is_default ?? false,
}
}
function mapFormToPayload(values: ShopTimingFormValues) {
const closed = values.is_closed
return {
title: values.title,
day_of_week: values.day_of_week || undefined,
is_closed: closed,
// Closed days carry no opening hours.
in_time: closed ? undefined : values.in_time,
out_time: closed ? undefined : values.out_time,
full_day_hours: values.full_day_hours || undefined,
half_day_hours: values.half_day_hours || undefined,
punch_in: values.punch_in || undefined,
punch_out: values.punch_out || undefined,
before_time: values.before_time || undefined,
after_time: values.after_time || undefined,
is_default: values.is_default,
}
}
// ── Component ──
export function ShopTimingForm({ resourceId, initialData, onSuccess }: ShopTimingFormProps) {
const api = useAuthApi()
const { form, isEditing } = useResourceForm<ShopTimingFormValues, any>({
schema: shopTimingFormSchema,
defaultValues: DEFAULT_VALUES,
resourceId,
initialData,
initialize: (id) => api.shopTimings.show(id),
queryKey: [SHOP_TIMING_ROUTES.BY_ID, resourceId],
mapToFormValues: mapToFormValues,
})
const isClosed = form.watch("is_closed")
const { mutate, error, isPending } = useFormMutation(form, {
mutationFn: (values: ShopTimingFormValues) => {
const payload = mapFormToPayload(values)
const promise = isEditing && resourceId
? api.shopTimings.update(resourceId, payload)
: api.shopTimings.create(payload)
toast.promise(promise, {
loading: isEditing ? "Updating shop timing..." : "Creating shop timing...",
success: isEditing ? "Shop timing updated successfully" : "Shop timing created successfully",
error: isEditing ? "Failed to update shop timing" : "Failed to create shop timing",
})
return promise
},
onSuccess: () => {
form.reset()
onSuccess?.()
},
})
return (
<Rhform form={form} onSubmit={(values) => mutate(values)}>
{error && (
<Alert variant="destructive" className="mb-4">
<AlertTriangle className="me-2 h-4 w-4" />
<AlertTitle>
{isEditing ? "Failed to update shop timing" : "Failed to create shop timing"}
</AlertTitle>
{error.message}
</Alert>
)}
<FieldGroup>
<RhfTextField name="title" label="Title" placeholder="Enter title" required />
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<RhfSelectField name="day_of_week" label="Day" placeholder="Select day" options={DAY_OPTIONS} />
<RhfCheckboxField name="is_closed" label="Closed (shop shut this day)" />
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<RhfTextField name="in_time" label="In Time" placeholder="HH:MM:SS" required={!isClosed} disabled={isClosed} />
<RhfTextField name="out_time" label="Out Time" placeholder="HH:MM:SS" required={!isClosed} disabled={isClosed} />
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<RhfTextField name="full_day_hours" label="Full Day Hours" placeholder="HH:MM:SS" />
<RhfTextField name="half_day_hours" label="Half Day Hours" placeholder="HH:MM:SS" />
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<RhfTextField name="punch_in" label="Punch In" placeholder="HH:MM:SS" />
<RhfTextField name="punch_out" label="Punch Out" placeholder="HH:MM:SS" />
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<RhfTextField name="before_time" label="Before Time" placeholder="HH:MM:SS" />
<RhfTextField name="after_time" label="After Time" placeholder="HH:MM:SS" />
</div>
<RhfCheckboxField name="is_default" label="Set as default" />
<Button type="submit" variant="default" disabled={isPending}>
{isEditing ? <Save /> : <Plus />}
{isPending
? (isEditing ? "Updating..." : "Creating...")
: (isEditing ? "Update Shop Timing" : "Create Shop Timing")}
</Button>
</FieldGroup>
</Rhform>
)
}