summaryrefslogtreecommitdiff
path: root/components/form-data/update-form-sheet.tsx
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-03-25 15:55:45 +0900
committerjoonhoekim <26rote@gmail.com>2025-03-25 15:55:45 +0900
commit1a2241c40e10193c5ff7008a7b7b36cc1d855d96 (patch)
tree8a5587f10ca55b162d7e3254cb088b323a34c41b /components/form-data/update-form-sheet.tsx
initial commit
Diffstat (limited to 'components/form-data/update-form-sheet.tsx')
-rw-r--r--components/form-data/update-form-sheet.tsx239
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