diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-04-02 09:54:08 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-04-02 09:54:08 +0000 |
| commit | dfdfae3018f8499240f48d28ce634f4a5c56e006 (patch) | |
| tree | 4493b172c061fa5bf4e94c083788110eb1507f6d /lib/vendor-investigation/table/update-investigation-sheet.tsx | |
| parent | 21a72eeddc74cf775e2a76e2c569de970bd62a7f (diff) | |
벤더 코멘트 처리
Diffstat (limited to 'lib/vendor-investigation/table/update-investigation-sheet.tsx')
| -rw-r--r-- | lib/vendor-investigation/table/update-investigation-sheet.tsx | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/lib/vendor-investigation/table/update-investigation-sheet.tsx b/lib/vendor-investigation/table/update-investigation-sheet.tsx new file mode 100644 index 00000000..fe30c892 --- /dev/null +++ b/lib/vendor-investigation/table/update-investigation-sheet.tsx @@ -0,0 +1,324 @@ +"use client" + +import * as React from "react" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { Loader } from "lucide-react" +import { toast } from "sonner" + +import { Button } from "@/components/ui/button" +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { Input } from "@/components/ui/input" +import { + Sheet, + SheetClose, + SheetContent, + SheetDescription, + SheetFooter, + SheetHeader, + SheetTitle, +} from "@/components/ui/sheet" +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" + +import { + updateVendorInvestigationSchema, + type UpdateVendorInvestigationSchema, +} from "../validations" +import { updateVendorInvestigationAction } from "../service" +import { VendorInvestigationsViewWithContacts } from "@/config/vendorInvestigationsColumnsConfig" + +/** + * The shape of `vendorInvestigation` + * might come from your `vendorInvestigationsView` row + * or your existing type for a single investigation. + */ + +interface UpdateVendorInvestigationSheetProps + extends React.ComponentPropsWithoutRef<typeof Sheet> { + investigation: VendorInvestigationsViewWithContacts | null +} + +/** + * A sheet for updating a vendor investigation (plus optional attachments). + */ +export function UpdateVendorInvestigationSheet({ + investigation, + ...props +}: UpdateVendorInvestigationSheetProps) { + const [isPending, startTransition] = React.useTransition() + + // RHF + Zod + const form = useForm<UpdateVendorInvestigationSchema>({ + resolver: zodResolver(updateVendorInvestigationSchema), + defaultValues: { + investigationId: investigation?.investigationId ?? 0, + investigationStatus: investigation?.investigationStatus ?? "PLANNED", + scheduledStartAt: investigation?.scheduledStartAt ?? undefined, + scheduledEndAt: investigation?.scheduledEndAt ?? undefined, + completedAt: investigation?.completedAt ?? undefined, + investigationNotes: investigation?.investigationNotes ?? "", + }, + }) + + React.useEffect(() => { + if (investigation) { + form.reset({ + investigationId: investigation.investigationId, + investigationStatus: investigation.investigationStatus || "PLANNED", + scheduledStartAt: investigation.scheduledStartAt ?? undefined, + scheduledEndAt: investigation.scheduledEndAt ?? undefined, + completedAt: investigation.completedAt ?? undefined, + investigationNotes: investigation.investigationNotes ?? "", + }) + } + }, [investigation, form]) + + // Format date for form data + const formatDateForFormData = (date: Date | undefined): string | null => { + if (!date) return null; + return date.toISOString(); + } + + // Submit handler + async function onSubmit(values: UpdateVendorInvestigationSchema) { + if (!values.investigationId) return + + startTransition(async () => { + // 1) Build a FormData object for the server action + const formData = new FormData() + + // Add text fields + formData.append("investigationId", String(values.investigationId)) + formData.append("investigationStatus", values.investigationStatus) + + // Format dates properly before appending to FormData + if (values.scheduledStartAt) { + const formattedDate = formatDateForFormData(values.scheduledStartAt) + if (formattedDate) formData.append("scheduledStartAt", formattedDate) + } + + if (values.scheduledEndAt) { + const formattedDate = formatDateForFormData(values.scheduledEndAt) + if (formattedDate) formData.append("scheduledEndAt", formattedDate) + } + + if (values.completedAt) { + const formattedDate = formatDateForFormData(values.completedAt) + if (formattedDate) formData.append("completedAt", formattedDate) + } + + if (values.investigationNotes) { + formData.append("investigationNotes", values.investigationNotes) + } + + // Add attachments (if any) + // Note: If you have multiple files in "attachments", we store them in the form under the same key. + const attachmentValue = form.getValues("attachments"); + if (attachmentValue instanceof FileList) { + for (let i = 0; i < attachmentValue.length; i++) { + formData.append("attachments", attachmentValue[i]); + } + } + + const { error } = await updateVendorInvestigationAction(formData) + if (error) { + toast.error(error) + return + } + + toast.success("Investigation updated!") + form.reset() + props.onOpenChange?.(false) + }) + } + + // Format date value for input field + const formatDateForInput = (date: Date | undefined): string => { + if (!date) return ""; + return date instanceof Date ? date.toISOString().slice(0, 10) : ""; + } + + // Handle date input change + const handleDateChange = (e: React.ChangeEvent<HTMLInputElement>, onChange: (...event: any[]) => void) => { + const val = e.target.value; + if (val) { + // Ensure proper date handling by setting to noon to avoid timezone issues + const newDate = new Date(`${val}T12:00:00`); + onChange(newDate); + } else { + onChange(undefined); + } + } + + return ( + <Sheet {...props}> + <SheetContent className="flex flex-col gap-6 sm:max-w-md"> + <SheetHeader className="text-left"> + <SheetTitle>Update Investigation</SheetTitle> + <SheetDescription> + Change the investigation details & attachments + </SheetDescription> + </SheetHeader> + + <Form {...form}> + <form + onSubmit={form.handleSubmit(onSubmit)} + className="flex flex-col gap-4" + // Must use multipart to support file uploads + encType="multipart/form-data" + > + {/* investigationStatus */} + <FormField + control={form.control} + name="investigationStatus" + render={({ field }) => ( + <FormItem> + <FormLabel>Status</FormLabel> + <FormControl> + <Select value={field.value} onValueChange={field.onChange}> + <SelectTrigger className="capitalize"> + <SelectValue placeholder="Select a status" /> + </SelectTrigger> + <SelectContent> + <SelectGroup> + <SelectItem value="PLANNED">PLANNED</SelectItem> + <SelectItem value="IN_PROGRESS">IN_PROGRESS</SelectItem> + <SelectItem value="COMPLETED">COMPLETED</SelectItem> + <SelectItem value="CANCELED">CANCELED</SelectItem> + </SelectGroup> + </SelectContent> + </Select> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + {/* scheduledStartAt */} + <FormField + control={form.control} + name="scheduledStartAt" + render={({ field }) => ( + <FormItem> + <FormLabel>Scheduled Start</FormLabel> + <FormControl> + <Input + type="date" + value={formatDateForInput(field.value)} + onChange={(e) => handleDateChange(e, field.onChange)} + /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + {/* scheduledEndAt */} + <FormField + control={form.control} + name="scheduledEndAt" + render={({ field }) => ( + <FormItem> + <FormLabel>Scheduled End</FormLabel> + <FormControl> + <Input + type="date" + value={formatDateForInput(field.value)} + onChange={(e) => handleDateChange(e, field.onChange)} + /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + {/* completedAt */} + <FormField + control={form.control} + name="completedAt" + render={({ field }) => ( + <FormItem> + <FormLabel>Completed At</FormLabel> + <FormControl> + <Input + type="date" + value={formatDateForInput(field.value)} + onChange={(e) => handleDateChange(e, field.onChange)} + /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + {/* investigationNotes */} + <FormField + control={form.control} + name="investigationNotes" + render={({ field }) => ( + <FormItem> + <FormLabel>Notes</FormLabel> + <FormControl> + <Input placeholder="Notes about the investigation..." {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + {/* attachments: multiple file upload */} + <FormField + control={form.control} + name="attachments" + render={({ field: { value, onChange, ...fieldProps } }) => ( + <FormItem> + <FormLabel>Attachments</FormLabel> + <FormControl> + <Input + type="file" + multiple + onChange={(e) => { + onChange(e.target.files); // Store the FileList directly + }} + {...fieldProps} + /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + {/* Footer Buttons */} + <SheetFooter className="gap-2 pt-2 sm:space-x-0"> + <SheetClose asChild> + <Button type="button" variant="outline"> + Cancel + </Button> + </SheetClose> + <Button disabled={isPending}> + {isPending && ( + <Loader className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" /> + )} + Save + </Button> + </SheetFooter> + </form> + </Form> + </SheetContent> + </Sheet> + ) +}
\ No newline at end of file |
