summaryrefslogtreecommitdiff
path: root/lib/rfqs/table/update-rfq-sheet.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rfqs/table/update-rfq-sheet.tsx')
-rw-r--r--lib/rfqs/table/update-rfq-sheet.tsx406
1 files changed, 0 insertions, 406 deletions
diff --git a/lib/rfqs/table/update-rfq-sheet.tsx b/lib/rfqs/table/update-rfq-sheet.tsx
deleted file mode 100644
index 22ca2c37..00000000
--- a/lib/rfqs/table/update-rfq-sheet.tsx
+++ /dev/null
@@ -1,406 +0,0 @@
-"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, getBudgetaryRfqs } from "../service"
-import { ProjectSelector } from "@/components/ProjectSelector"
-import { type Project } from "../service"
-import { ParentRfqSelector } from "./ParentRfqSelector"
-
-interface UpdateRfqSheetProps
- extends React.ComponentPropsWithRef<typeof Sheet> {
- rfq: RfqWithItemCount | null
-}
-
-// 부모 RFQ 정보 타입 정의
-interface ParentRfq {
- id: number;
- rfqCode: string;
- description: string | null;
- rfqType: RfqType;
- projectId: number | null;
- projectCode: string | null;
- projectName: string | null;
-}
-
-export function UpdateRfqSheet({ rfq, ...props }: UpdateRfqSheetProps) {
- const [isUpdatePending, startUpdateTransition] = React.useTransition()
- const { data: session } = useSession()
- const userId = Number(session?.user?.id || 1)
- const [selectedParentRfq, setSelectedParentRfq] = React.useState<ParentRfq | null>(null)
-
- // RFQ의 타입 가져오기
- const rfqType = rfq?.rfqType || RfqType.PURCHASE;
-
- // 초기 부모 RFQ ID 가져오기
- const initialParentRfqId = rfq?.parentRfqId;
-
- // 현재 RFQ 타입에 따라 선택 가능한 부모 RFQ 타입들 결정
- const getParentRfqTypes = (): RfqType[] => {
- switch(rfqType) {
- case RfqType.PURCHASE:
- // PURCHASE는 BUDGETARY와 PURCHASE_BUDGETARY를 부모로 가질 수 있음
- return [RfqType.BUDGETARY, RfqType.PURCHASE_BUDGETARY];
- case RfqType.PURCHASE_BUDGETARY:
- // PURCHASE_BUDGETARY는 BUDGETARY만 부모로 가질 수 있음
- return [RfqType.BUDGETARY];
- default:
- return [];
- }
- };
-
- // 부모 RFQ 타입들
- const parentRfqTypes = getParentRfqTypes();
-
- // 부모 RFQ를 보여줄지 결정
- const shouldShowParentRfqSelector = rfqType === RfqType.PURCHASE || rfqType === RfqType.PURCHASE_BUDGETARY;
-
- // 타입에 따른 타이틀 생성
- const getTypeTitle = () => {
- switch(rfqType) {
- case RfqType.PURCHASE:
- return "Purchase RFQ";
- case RfqType.BUDGETARY:
- return "Budgetary RFQ";
- case RfqType.PURCHASE_BUDGETARY:
- return "Purchase Budgetary RFQ";
- default:
- return "RFQ";
- }
- };
-
- // 타입 설명 가져오기
- const getTypeDescription = () => {
- switch(rfqType) {
- case RfqType.PURCHASE:
- return "실제 구매 발주 전에 가격을 요청";
- case RfqType.BUDGETARY:
- return "기술영업 단계에서 입찰가 산정을 위한 견적 요청";
- case RfqType.PURCHASE_BUDGETARY:
- return "프로젝트 수주 후, 공식 입찰 전 예산 책정을 위한 가격 요청";
- default:
- return "";
- }
- };
-
- // 부모 RFQ 선택기 레이블 및 설명 가져오기
- const getParentRfqSelectorLabel = () => {
- if (rfqType === RfqType.PURCHASE) {
- return "부모 RFQ (BUDGETARY/PURCHASE_BUDGETARY)";
- } else if (rfqType === RfqType.PURCHASE_BUDGETARY) {
- return "부모 RFQ (BUDGETARY)";
- }
- return "부모 RFQ";
- };
-
- const getParentRfqDescription = () => {
- if (rfqType === RfqType.PURCHASE) {
- return "BUDGETARY 또는 PURCHASE_BUDGETARY 타입의 RFQ를 부모로 선택할 수 있습니다.";
- } else if (rfqType === RfqType.PURCHASE_BUDGETARY) {
- return "BUDGETARY 타입의 RFQ만 부모로 선택할 수 있습니다.";
- }
- return "";
- };
-
- // 초기 부모 RFQ 로드
- React.useEffect(() => {
- if (initialParentRfqId && shouldShowParentRfqSelector) {
- const loadInitialParentRfq = async () => {
- try {
- const result = await getBudgetaryRfqs({
- rfqId: initialParentRfqId
- });
-
- if ('rfqs' in result && result.rfqs && result.rfqs.length > 0) {
- setSelectedParentRfq(result.rfqs[0] as unknown as ParentRfq);
- }
- } catch (error) {
- console.error("부모 RFQ 로드 오류:", error);
- }
- };
-
- loadInitialParentRfq();
- }
- }, [initialParentRfqId, shouldShowParentRfqSelector]);
-
- // 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
- parentRfqId: rfq?.parentRfqId, // 부모 RFQ ID
- dueDate: rfq?.dueDate ?? undefined, // null을 undefined로 변환
- status: rfq?.status ?? "DRAFT",
- createdBy: rfq?.createdBy ?? userId,
- },
- });
-
- // 프로젝트 선택 처리
- const handleProjectSelect = (project: Project | null) => {
- if (project === null) {
- return;
- }
- form.setValue("projectId", project.id);
- };
-
- // 부모 RFQ 선택 처리
- const handleParentRfqSelect = (rfq: ParentRfq | null) => {
- setSelectedParentRfq(rfq);
- form.setValue("parentRfqId", rfq?.id);
- };
-
- async function onSubmit(input: UpdateRfqSchema) {
- startUpdateTransition(async () => {
- if (!rfq) return
-
- const { error } = await modifyRfq({
- ...input,
- rfqType: rfqType as RfqType,
-
- })
-
- 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 {getTypeTitle()}</SheetTitle>
- <SheetDescription>
- Update the {getTypeTitle()} details and save the changes
- <div className="mt-1 text-xs text-muted-foreground">
- {getTypeDescription()}
- </div>
- </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} />
- )}
- />
-
- {/* Hidden rfqType field */}
- {/* <FormField
- control={form.control}
- name="rfqType"
- 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>
- )}
- />
-
- {/* Parent RFQ Selector - PURCHASE 또는 PURCHASE_BUDGETARY 타입일 때만 표시 */}
- {shouldShowParentRfqSelector && (
- <FormField
- control={form.control}
- name="parentRfqId"
- render={({ field }) => (
- <FormItem>
- <FormLabel>{getParentRfqSelectorLabel()}</FormLabel>
- <FormControl>
- <ParentRfqSelector
- selectedRfqId={field.value as number | undefined}
- onRfqSelect={handleParentRfqSelect}
- rfqType={rfqType}
- parentRfqTypes={parentRfqTypes}
- placeholder={
- rfqType === RfqType.PURCHASE
- ? "BUDGETARY 또는 PURCHASE_BUDGETARY RFQ 선택..."
- : "BUDGETARY RFQ 선택..."
- }
- />
- </FormControl>
- <div className="text-xs text-muted-foreground mt-1">
- {getParentRfqDescription()}
- </div>
- <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