151 lines
5.4 KiB
TypeScript
151 lines
5.4 KiB
TypeScript
"use client"
|
|
|
|
import { z } from "zod"
|
|
import { useForm } from "react-hook-form"
|
|
import { zodResolver } from "@hookform/resolvers/zod"
|
|
import { Plus, Save } from "lucide-react"
|
|
import { Button } from "@/shared/components/ui/button"
|
|
import { FieldGroup } from "@/shared/components/ui/field"
|
|
import { Rhform, RhfTextField, RhfAsyncSelectField, RhfCheckboxField } from "@/shared/components/form"
|
|
import { toast } from "sonner"
|
|
import { useAuthApi } from "@/shared/useApi"
|
|
import { useEffect } from "react"
|
|
import { DEPARTMENT_ROUTES } from "@garage/api"
|
|
|
|
const invoiceSequenceSchema = z.object({
|
|
title: z.string().min(1, "Title is required"),
|
|
sequence_title: z.string().optional(),
|
|
prefix: z.string().optional(),
|
|
start_number: z.coerce.number().int().min(1).optional(),
|
|
auto_generate: z.boolean().optional(),
|
|
department: z.object({ value: z.string(), label: z.string() }).nullable().optional(),
|
|
})
|
|
|
|
type InvoiceSequenceFormValues = z.infer<typeof invoiceSequenceSchema>
|
|
|
|
const STORE_OBJECT = { getOptionValue: (o: any) => o, getOptionLabel: (o: any) => o.label }
|
|
|
|
type InvoiceSequenceFormProps = {
|
|
resourceId?: string | null
|
|
initialData?: any
|
|
onSuccess?: () => void
|
|
}
|
|
|
|
export function InvoiceSequenceForm({ resourceId, initialData, onSuccess }: InvoiceSequenceFormProps) {
|
|
const api = useAuthApi()
|
|
const isEditing = !!resourceId
|
|
|
|
const form = useForm<InvoiceSequenceFormValues>({
|
|
resolver: zodResolver(invoiceSequenceSchema) as any,
|
|
defaultValues: {
|
|
title: "",
|
|
sequence_title: "",
|
|
prefix: "",
|
|
start_number: 1,
|
|
auto_generate: false,
|
|
department: null,
|
|
},
|
|
})
|
|
|
|
useEffect(() => {
|
|
if (initialData) {
|
|
const d = initialData?.data ?? initialData
|
|
form.reset({
|
|
title: d.title ?? "",
|
|
sequence_title: d.sequence_title ?? "",
|
|
prefix: d.prefix ?? "",
|
|
start_number: d.start_number ?? 1,
|
|
auto_generate: d.auto_generate ?? false,
|
|
department: d.department_id
|
|
? { value: String(d.department_id), label: d.department_name ?? `#${d.department_id}` }
|
|
: null,
|
|
})
|
|
}
|
|
}, [initialData, form])
|
|
|
|
const handleSubmit = async (values: InvoiceSequenceFormValues) => {
|
|
try {
|
|
const payload = {
|
|
title: values.title,
|
|
sequence_title: values.sequence_title || undefined,
|
|
prefix: values.prefix || undefined,
|
|
start_number: values.start_number,
|
|
auto_generate: values.auto_generate,
|
|
department_id: values.department ? Number(values.department.value) : undefined,
|
|
}
|
|
|
|
const promise = isEditing
|
|
? api.invoiceSequences.update(resourceId!, payload)
|
|
: api.invoiceSequences.create(payload)
|
|
|
|
toast.promise(promise, {
|
|
loading: isEditing ? "Updating..." : "Creating...",
|
|
success: isEditing ? "Updated successfully" : "Created successfully",
|
|
error: isEditing ? "Failed to update" : "Failed to create",
|
|
})
|
|
|
|
await promise
|
|
form.reset()
|
|
onSuccess?.()
|
|
} catch {
|
|
// toast already shown
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Rhform form={form} onSubmit={handleSubmit}>
|
|
<FieldGroup>
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
<RhfTextField
|
|
name="title"
|
|
label="Title"
|
|
placeholder="e.g. Default Invoice Sequence"
|
|
required
|
|
/>
|
|
<RhfTextField
|
|
name="sequence_title"
|
|
label="Sequence Title"
|
|
placeholder="e.g. INV"
|
|
/>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
<RhfTextField
|
|
name="prefix"
|
|
label="Prefix"
|
|
placeholder="e.g. INV-"
|
|
/>
|
|
<RhfTextField
|
|
name="start_number"
|
|
label="Start Number"
|
|
type="number"
|
|
placeholder="e.g. 1"
|
|
/>
|
|
</div>
|
|
|
|
<RhfAsyncSelectField
|
|
name="department"
|
|
label="Department"
|
|
placeholder="Select department"
|
|
queryKey={[DEPARTMENT_ROUTES.INDEX]}
|
|
listFn={() => api.departments.list()}
|
|
mapOption={(item: any) => ({
|
|
value: String(item.id),
|
|
label: item.name ?? item.title ?? `#${item.id}`,
|
|
})}
|
|
{...STORE_OBJECT}
|
|
/>
|
|
|
|
<RhfCheckboxField name="auto_generate" label="Auto Generate" />
|
|
|
|
<Button type="submit" disabled={form.formState.isSubmitting}>
|
|
{isEditing ? <Save className="h-4 w-4" /> : <Plus className="h-4 w-4" />}
|
|
{form.formState.isSubmitting
|
|
? (isEditing ? "Updating..." : "Creating...")
|
|
: (isEditing ? "Update" : "Create")}
|
|
</Button>
|
|
</FieldGroup>
|
|
</Rhform>
|
|
)
|
|
}
|