diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-03-26 00:37:41 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-03-26 00:37:41 +0000 |
| commit | e0dfb55c5457aec489fc084c4567e791b4c65eb1 (patch) | |
| tree | 68543a65d88f5afb3a0202925804103daa91bc6f /components/form-data/update-form-sheet.tsx | |
3/25 까지의 대표님 작업사항
Diffstat (limited to 'components/form-data/update-form-sheet.tsx')
| -rw-r--r-- | components/form-data/update-form-sheet.tsx | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/components/form-data/update-form-sheet.tsx b/components/form-data/update-form-sheet.tsx new file mode 100644 index 00000000..d5f7d21b --- /dev/null +++ b/components/form-data/update-form-sheet.tsx @@ -0,0 +1,239 @@ +"use client" + +import * as React from "react" +import { z } from "zod" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { Loader } from "lucide-react" +import { toast } from "sonner" + +import { + Sheet, + SheetClose, + SheetContent, + SheetDescription, + SheetFooter, + SheetHeader, + SheetTitle, +} from "@/components/ui/sheet" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { + Form, + FormField, + FormItem, + FormLabel, + FormControl, + FormMessage, +} from "@/components/ui/form" +import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from "@/components/ui/select" + +import { DataTableColumnJSON } from "./form-data-table-columns" +import { updateFormDataInDB } from "@/lib/forms/services" + +interface UpdateTagSheetProps extends React.ComponentPropsWithoutRef<typeof Sheet> { + open: boolean + onOpenChange: (open: boolean) => void + columns: DataTableColumnJSON[] + rowData: Record<string, any> | null + formCode: string + contractItemId: number + /** 업데이트 성공 시 호출될 콜백 */ + onUpdateSuccess?: (updatedValues: Record<string, any>) => void +} + +export function UpdateTagSheet({ + open, + onOpenChange, + columns, + rowData, + formCode, + contractItemId, + onUpdateSuccess, + ...props +}: UpdateTagSheetProps) { + const [isPending, startTransition] = React.useTransition() + + // 1) zod 스키마 + const dynamicSchema = React.useMemo(() => { + const shape: Record<string, z.ZodType<any>> = {} + for (const col of columns) { + if (col.type === "NUMBER") { + shape[col.key] = z + .union([z.coerce.number(), z.nan()]) + .transform((val) => (isNaN(val) ? undefined : val)) + .optional() + } else { + shape[col.key] = z.string().optional() + } + } + return z.object(shape) + }, [columns]) + + // 2) form init + const form = useForm({ + resolver: zodResolver(dynamicSchema), + defaultValues: React.useMemo(() => { + if (!rowData) return {} + const defaults: Record<string, any> = {} + for (const col of columns) { + defaults[col.key] = rowData[col.key] ?? "" + } + return defaults + }, [rowData, columns]), + }) + + React.useEffect(() => { + if (!rowData) { + form.reset({}) + return + } + const defaults: Record<string, any> = {} + for (const col of columns) { + defaults[col.key] = rowData[col.key] ?? "" + } + form.reset(defaults) + }, [rowData, columns, form]) + + async function onSubmit(values: Record<string, any>) { + startTransition(async () => { + const { success, message } = await updateFormDataInDB(formCode, contractItemId, values) + if (!success) { + toast.error(message) + return + } + toast.success("Updated successfully!") + + // (A) 수정된 값(폼 데이터)을 부모 콜백에 전달 + onUpdateSuccess?.({ + // rowData(원본)와 values를 합쳐서 최종 "수정된 row"를 만든다. + // tagNumber는 기존 그대로 + ...rowData, + ...values, + tagNumber: rowData?.tagNumber, + }) + + onOpenChange(false) + }) + } + + return ( + <Sheet open={open} onOpenChange={onOpenChange} {...props}> + <SheetContent className="sm:max-w-xl md:max-w-3xl lg:max-w-4xl xl:max-w-5xl flex flex-col"> + <SheetHeader className="text-left"> + <SheetTitle>Update Row</SheetTitle> + <SheetDescription> + Modify the fields below and save changes + </SheetDescription> + </SheetHeader> + + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-4"> + <div className="overflow-y-auto max-h-[80vh] flex-1 pr-4 -mr-4"> + <div className="flex flex-col gap-4 pt-2"> + {columns.map((col) => { + const isTagNumberField = col.key === "tagNumber" || col.key === "tagDescription" + return ( + <FormField + key={col.key} + control={form.control} + name={col.key} + render={({ field }) => { + switch (col.type) { + case "NUMBER": + return ( + <FormItem> + <FormLabel>{col.displayLabel}</FormLabel> + <FormControl> + <Input + type="number" + readOnly={isTagNumberField} + onChange={(e) => { + const num = parseFloat(e.target.value) + field.onChange(isNaN(num) ? "" : num) + }} + value={field.value ?? ""} + /> + </FormControl> + <FormMessage /> + </FormItem> + ) + + case "LIST": + return ( + <FormItem> + <FormLabel>{col.label}</FormLabel> + <Select + disabled={isTagNumberField} + value={field.value ?? ""} + onValueChange={(val) => field.onChange(val)} + > + <SelectTrigger> + <SelectValue placeholder="Select an option" /> + </SelectTrigger> + <SelectContent> + {col.options?.map((opt) => ( + <SelectItem key={opt} value={opt}> + {opt} + </SelectItem> + ))} + </SelectContent> + </Select> + <FormMessage /> + </FormItem> + ) + + // case "date": + // return ( + // <FormItem> + // <FormLabel>{col.label}</FormLabel> + // <FormControl> + // <Input + // type="date" + // readOnly={isTagNumberField} + // onChange={field.onChange} + // value={field.value ?? ""} + // /> + // </FormControl> + // <FormMessage /> + // </FormItem> + // ) + + case "STRING": + default: + return ( + <FormItem> + <FormLabel>{col.label}</FormLabel> + <FormControl> + <Input readOnly={isTagNumberField} {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + ) + } + }} + /> + ) + })} + + </div> + </div> + + <SheetFooter className="gap-2 pt-2"> + <SheetClose asChild> + <Button type="button" variant="outline"> + Cancel + </Button> + </SheetClose> + + <Button type="submit" disabled={isPending}> + {isPending && <Loader className="mr-2 h-4 w-4 animate-spin" />} + Save + </Button> + </SheetFooter> + </form> + </Form> + </SheetContent> + </Sheet> + ) +}
\ No newline at end of file |
