From e0dfb55c5457aec489fc084c4567e791b4c65eb1 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Wed, 26 Mar 2025 00:37:41 +0000
Subject: 3/25 까지의 대표님 작업사항
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/form-data/update-form-sheet.tsx | 239 +++++++++++++++++++++++++++++
 1 file changed, 239 insertions(+)
 create mode 100644 components/form-data/update-form-sheet.tsx

(limited to 'components/form-data/update-form-sheet.tsx')

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
-- 
cgit v1.2.3

