From 1a2241c40e10193c5ff7008a7b7b36cc1d855d96 Mon Sep 17 00:00:00 2001
From: joonhoekim <26rote@gmail.com>
Date: Tue, 25 Mar 2025 15:55:45 +0900
Subject: initial commit

---
 lib/rfqs/table/update-rfq-sheet.tsx | 283 ++++++++++++++++++++++++++++++++++++
 1 file changed, 283 insertions(+)
 create mode 100644 lib/rfqs/table/update-rfq-sheet.tsx

(limited to 'lib/rfqs/table/update-rfq-sheet.tsx')

diff --git a/lib/rfqs/table/update-rfq-sheet.tsx b/lib/rfqs/table/update-rfq-sheet.tsx
new file mode 100644
index 00000000..769f25e7
--- /dev/null
+++ b/lib/rfqs/table/update-rfq-sheet.tsx
@@ -0,0 +1,283 @@
+"use client"
+
+import * as React from "react"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { Loader } from "lucide-react"
+import { useForm } from "react-hook-form"
+import { toast } from "sonner"
+import { useSession } from "next-auth/react"
+
+import { Button } from "@/components/ui/button"
+import {
+  Form,
+  FormControl,
+  FormField,
+  FormItem,
+  FormLabel,
+  FormMessage,
+} from "@/components/ui/form"
+import {
+  Select,
+  SelectContent,
+  SelectGroup,
+  SelectItem,
+  SelectTrigger,
+  SelectValue,
+} from "@/components/ui/select"
+import {
+  Sheet,
+  SheetClose,
+  SheetContent,
+  SheetDescription,
+  SheetFooter,
+  SheetHeader,
+  SheetTitle,
+} from "@/components/ui/sheet"
+import { Input } from "@/components/ui/input"
+
+import { Rfq, RfqWithItemCount } from "@/db/schema/rfq"
+import { RfqType, updateRfqSchema, type UpdateRfqSchema } from "../validations"
+import { modifyRfq } from "../service"
+import { ProjectSelector } from "@/components/ProjectSelector"
+import { type Project } from "../service"
+import { BudgetaryRfqSelector } from "./BudgetaryRfqSelector"
+
+interface UpdateRfqSheetProps
+  extends React.ComponentPropsWithRef<typeof Sheet> {
+  rfq: RfqWithItemCount | null
+  rfqType?: RfqType;
+}
+
+
+interface BudgetaryRfq {
+  id: number;
+  rfqCode: string;
+  description: string | null;
+}
+
+
+export function UpdateRfqSheet({ rfq,rfqType = RfqType.PURCHASE , ...props }: UpdateRfqSheetProps) {
+  const [isUpdatePending, startUpdateTransition] = React.useTransition()
+  const { data: session } = useSession()
+  const userId = Number(session?.user?.id || 1)
+  const [selectedBudgetaryRfq, setSelectedBudgetaryRfq] = React.useState<BudgetaryRfq | null>(null)
+
+  // RHF setup
+  const form = useForm<UpdateRfqSchema>({
+    resolver: zodResolver(updateRfqSchema),
+    defaultValues: {
+      id: rfq?.rfqId ?? 0,         // PK
+      rfqCode: rfq?.rfqCode ?? "",
+      description: rfq?.description ?? "",
+      projectId: rfq?.projectId, // 프로젝트 ID
+      dueDate: rfq?.dueDate ?? undefined,    // null을 undefined로 변환
+      status: rfq?.status ?? "DRAFT",
+      createdBy: rfq?.createdBy ?? userId,
+    },
+  });
+
+  // 프로젝트 선택 처리
+  const handleProjectSelect = (project: Project) => {
+    form.setValue("projectId", project.id);
+  };
+
+  async function onSubmit(input: UpdateRfqSchema) {
+    startUpdateTransition(async () => {
+      if (!rfq) return
+
+      const { error } = await modifyRfq({
+        ...input,
+      })
+
+      if (error) {
+        toast.error(error)
+        return
+      }
+
+      form.reset()
+      props.onOpenChange?.(false)  // close the sheet
+      toast.success("RFQ updated!")
+    })
+  }
+
+  return (
+    <Sheet {...props}>
+      <SheetContent className="flex flex-col gap-6 sm:max-w-md">
+        <SheetHeader className="text-left">
+          <SheetTitle>Update RFQ</SheetTitle>
+          <SheetDescription>
+            Update the RFQ details and save the changes
+          </SheetDescription>
+        </SheetHeader>
+
+        {/* RHF Form */}
+        <Form {...form}>
+          <form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-4">
+
+            {/* Hidden or code-based id field */}
+            <FormField
+              control={form.control}
+              name="id"
+              render={({ field }) => (
+                <input type="hidden" {...field} />
+              )}
+            />
+
+            {/* Project Selector - 재사용 컴포넌트 사용 */}
+            <FormField
+              control={form.control}
+              name="projectId"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>Project</FormLabel>
+                  <FormControl>
+                    <ProjectSelector
+                      selectedProjectId={field.value}
+                      onProjectSelect={handleProjectSelect}
+                      placeholder="프로젝트 선택..."
+                    />
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+
+            {/* Budgetary RFQ Selector - 구매용 RFQ 생성 시에만 표시 */}
+            {rfqType === RfqType.PURCHASE && (
+              <FormField
+                control={form.control}
+                name="parentRfqId"
+                render={({ field }) => (
+                  <FormItem>
+                    <FormLabel>Budgetary RFQ (Optional)</FormLabel>
+                    <FormControl>
+                      <BudgetaryRfqSelector
+                        selectedRfqId={field.value as number | undefined}
+                        onRfqSelect={(rfq) => {
+                          setSelectedBudgetaryRfq(rfq as any);
+                          form.setValue("parentRfqId", rfq?.id);
+                        }}
+                        placeholder="Budgetary RFQ 선택..."
+                      />
+                    </FormControl>
+                    <FormMessage />
+                  </FormItem>
+                )}
+              />
+            )}
+
+
+            {/* rfqCode */}
+            <FormField
+              control={form.control}
+              name="rfqCode"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>RFQ Code</FormLabel>
+                  <FormControl>
+                    <Input placeholder="e.g. RFQ-2025-001" {...field} />
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+
+            {/* description */}
+            <FormField
+              control={form.control}
+              name="description"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>Description</FormLabel>
+                  <FormControl>
+                    <Input placeholder="Description" {...field} value={field.value || ""} />
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+
+
+
+            {/* dueDate (type="date") */}
+            <FormField
+              control={form.control}
+              name="dueDate"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>Due Date</FormLabel>
+                  <FormControl>
+                    <Input
+                      type="date"
+                      // convert Date -> yyyy-mm-dd
+                      value={field.value ? field.value.toISOString().slice(0, 10) : ""}
+                      onChange={(e) => {
+                        const val = e.target.value
+                        field.onChange(val ? new Date(val + "T00:00:00") : undefined)
+                      }}
+                    />
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+
+            {/* status (Select) */}
+            <FormField
+              control={form.control}
+              name="status"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>Status</FormLabel>
+                  <FormControl>
+                    <Select
+                      onValueChange={field.onChange}
+                      value={field.value ?? "DRAFT"}
+                    >
+                      <SelectTrigger className="capitalize">
+                        <SelectValue placeholder="Select status" />
+                      </SelectTrigger>
+                      <SelectContent>
+                        <SelectGroup>
+                          {["DRAFT", "PUBLISHED", "EVALUATION", "AWARDED"].map((item) => (
+                            <SelectItem key={item} value={item} className="capitalize">
+                              {item}
+                            </SelectItem>
+                          ))}
+                        </SelectGroup>
+                      </SelectContent>
+                    </Select>
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+
+            {/* createdBy (hidden or read-only) */}
+            <FormField
+              control={form.control}
+              name="createdBy"
+              render={({ field }) => (
+                <input type="hidden" {...field} />
+              )}
+            />
+
+            <SheetFooter className="gap-2 pt-2 sm:space-x-0">
+              <SheetClose asChild>
+                <Button type="button" variant="outline">
+                  Cancel
+                </Button>
+              </SheetClose>
+              <Button disabled={isUpdatePending}>
+                {isUpdatePending && (
+                  <Loader className="mr-2 size-4 animate-spin" aria-hidden="true" />
+                )}
+                Save
+              </Button>
+            </SheetFooter>
+          </form>
+        </Form>
+      </SheetContent>
+    </Sheet>
+  )
+}
\ No newline at end of file
-- 
cgit v1.2.3

