From cd0ce0cbe8af8719a6f542098ec78f2a5c1222ce Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Mon, 1 Dec 2025 10:28:05 +0000
Subject: (최겸) 구매 입찰 사전견적 개발(rfq-last)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../bidding/manage/create-pre-quote-rfq-dialog.tsx | 421 +++++++++------------
 1 file changed, 184 insertions(+), 237 deletions(-)

(limited to 'components/bidding/manage/create-pre-quote-rfq-dialog.tsx')

diff --git a/components/bidding/manage/create-pre-quote-rfq-dialog.tsx b/components/bidding/manage/create-pre-quote-rfq-dialog.tsx
index cdcf1ef1..de3c19ff 100644
--- a/components/bidding/manage/create-pre-quote-rfq-dialog.tsx
+++ b/components/bidding/manage/create-pre-quote-rfq-dialog.tsx
@@ -46,12 +46,14 @@ import { cn } from "@/lib/utils"
 import { toast } from "sonner"
 import { ScrollArea } from "@/components/ui/scroll-area"
 import { Separator } from "@/components/ui/separator"
-import { createPreQuoteRfqAction, previewGeneralRfqCode } from "@/lib/bidding/service"
+import { createPreQuoteRfqAction } from "@/lib/bidding/pre-quote/service"
+import { previewGeneralRfqCode } from "@/lib/rfq-last/service"
 import { MaterialGroupSelectorDialogSingle } from "@/components/common/material/material-group-selector-dialog-single"
 import { MaterialSearchItem } from "@/lib/material/material-group-service"
 import { MaterialSelectorDialogSingle } from "@/components/common/selectors/material/material-selector-dialog-single"
 import { MaterialSearchItem as SAPMaterialSearchItem } from "@/components/common/selectors/material/material-service"
 import { PurchaseGroupCodeSelector } from "@/components/common/selectors/purchase-group-code/purchase-group-code-selector"
+import type { PurchaseGroupCodeWithUser } from "@/components/common/selectors/purchase-group-code"
 import { getBiddingById } from "@/lib/bidding/service"
 
 // 아이템 스키마
@@ -67,14 +69,17 @@ const itemSchema = z.object({
 
 // 사전견적용 일반견적 생성 폼 스키마
 const createPreQuoteRfqSchema = z.object({
-  rfqType: z.string().min(1, "견적 종류를 선택해주세요"),
+  rfqType: z.string().optional(),
   rfqTitle: z.string().min(1, "견적명을 입력해주세요"),
   dueDate: z.date({
     required_error: "제출마감일을 선택해주세요",
-  }),
+  }).optional(), // 필수값 해제
   picUserId: z.number().optional(),
   projectId: z.number().optional(),
   remark: z.string().optional(),
+  biddingNumber: z.string().optional(), // 입찰 No. 추가
+  contractStartDate: z.date().optional(), // 계약기간 시작
+  contractEndDate: z.date().optional(), // 계약기간 종료
   items: z.array(itemSchema).min(1, "최소 하나의 자재를 추가해주세요"),
 })
 
@@ -122,7 +127,7 @@ export function CreatePreQuoteRfqDialog({
   const [isLoading, setIsLoading] = React.useState(false)
   const [previewCode, setPreviewCode] = React.useState("")
   const [isLoadingPreview, setIsLoadingPreview] = React.useState(false)
-  const [selectedBidPic, setSelectedBidPic] = React.useState<any | undefined>(undefined)
+  const [selectedBidPic, setSelectedBidPic] = React.useState<PurchaseGroupCodeWithUser | undefined>(undefined)
   const { data: session } = useSession()
 
   const userId = React.useMemo(() => {
@@ -165,24 +170,31 @@ export function CreatePreQuoteRfqDialog({
     },
   })
 
+  /*
   const { fields, append, remove } = useFieldArray({
     control: form.control,
     name: "items",
   })
+  */
 
-  // 입찰담당자 정보 로드
+  // 견적담당자 정보 로드
   React.useEffect(() => {
     const loadBiddingInfo = async () => {
       if (!biddingId || !open) return
 
       try {
         const bidding = await getBiddingById(biddingId)
-        if (bidding && bidding.bidPicId) {
-          // 입찰담당자 정보를 로드하는 로직 추가 필요
-          // 현재는 임시로 bidPicId를 사용
+        if (bidding) {
           setSelectedBidPic({
-            USER_ID: bidding.bidPicId,
-            DISPLAY_NAME: bidding.bidPicName || '입찰담당자'
+            DISPLAY_NAME: bidding.bidPicName || '',
+            PURCHASE_GROUP_CODE: bidding.bidPicCode || '',
+            EMPLOYEE_NUMBER: '',
+            user: bidding.bidPicId ? {
+              id: bidding.bidPicId,
+              name: bidding.bidPicName || '',
+              email: '',
+              employeeNumber: null
+            } : undefined
           })
         }
       } catch (error) {
@@ -192,17 +204,58 @@ export function CreatePreQuoteRfqDialog({
 
     loadBiddingInfo()
   }, [biddingId, open])
+  
+  // 프로젝트 정보 상태 추가
+  const [projectInfo, setProjectInfo] = React.useState<string>("")
 
   // 다이얼로그가 열릴 때 폼 초기화
   React.useEffect(() => {
     if (open) {
+      // 입찰 정보를 기반으로 기본값 설정
+      let rfqTitle = "";
+      let projectId: number | undefined = undefined;
+      let contractStartDate: Date | undefined = undefined;
+      let contractEndDate: Date | undefined = undefined;
+      let biddingNumber = "";
+
+      const loadDetailedBiddingInfo = async () => {
+        if (biddingId) {
+          try {
+            const bidding = await getBiddingById(biddingId)
+            if (bidding) {
+              rfqTitle = bidding.title;
+              biddingNumber = bidding.biddingNumber;
+              
+              // 프로젝트 정보 설정
+              const pCode = bidding.projectCode || "";
+              const pName = bidding.projectName || "";
+              setProjectInfo(pCode && pName ? `${pCode} - ${pName}` : pCode || pName || "");
+
+              // 폼 값 설정
+              form.setValue("rfqTitle", rfqTitle);
+              form.setValue("rfqType", "pre_bidding"); // 기본값 설정
+              if (biddingNumber) form.setValue("biddingNumber", biddingNumber);
+              
+              if (bidding.contractStartDate) form.setValue("contractStartDate", new Date(bidding.contractStartDate));
+              if (bidding.contractEndDate) form.setValue("contractEndDate", new Date(bidding.contractEndDate));
+            }
+          } catch (e) {
+            console.error(e);
+          }
+        }
+      };
+      loadDetailedBiddingInfo();
+
       form.reset({
-        rfqType: "",
+        rfqType: "pre_bidding", // 기본값
         rfqTitle: "",
-        dueDate: undefined,
-        picUserId: selectedBidPic?.USER_ID,
+        dueDate: undefined, // 필수값 해제되었으므로 undefined 가능
+        picUserId: selectedBidPic?.user?.id,
         projectId: undefined,
         remark: "",
+        biddingNumber: "",
+        contractStartDate: undefined,
+        contractEndDate: undefined,
         items: initialItems.length > 0 ? initialItems : [
           {
             itemCode: "",
@@ -217,11 +270,11 @@ export function CreatePreQuoteRfqDialog({
       })
       setPreviewCode("")
     }
-  }, [open, initialItems, form, selectedBidPic])
+  }, [open, initialItems, form, selectedBidPic, biddingId])
 
   // 견적담당자 선택 시 RFQ 코드 미리보기 생성
   React.useEffect(() => {
-    if (!selectedBidPic?.USER_ID) {
+    if (!selectedBidPic?.user?.id) {
       setPreviewCode("")
       return
     }
@@ -230,7 +283,7 @@ export function CreatePreQuoteRfqDialog({
     (async () => {
       setIsLoadingPreview(true)
       try {
-        const code = await previewGeneralRfqCode(selectedBidPic.USER_ID)
+        const code = await previewGeneralRfqCode(selectedBidPic.user!.id)
         setPreviewCode(code)
       } catch (error) {
         console.error("코드 미리보기 오류:", error)
@@ -277,25 +330,26 @@ export function CreatePreQuoteRfqDialog({
       return
     }
 
-    if (!selectedBidPic?.USER_ID) {
-      toast.error("입찰담당자를 선택해주세요")
-      return
-    }
 
-    const picUserId = selectedBidPic.USER_ID
+
+    const picUserId = selectedBidPic?.user?.id || session?.user?.id
 
     setIsLoading(true)
     
     try {
       // 서버 액션 호출 (입찰 조건 포함)
       const result = await createPreQuoteRfqAction({
+        // biddingId, // createPreQuoteRfqAction 인터페이스 변경됨
         biddingId,
-        rfqType: data.rfqType,
+        rfqType: data.rfqType || "pre_bidding",
         rfqTitle: data.rfqTitle,
-        dueDate: data.dueDate,
+        dueDate: data.dueDate ? new Date(data.dueDate) : undefined, // optional이지만 submit시에는 값이 있을 수 있음 (없으면 서비스에서 처리)
         picUserId,
         projectId: data.projectId,
         remark: data.remark || "",
+        biddingNumber: data.biddingNumber, // 추가
+        contractStartDate: data.contractStartDate, // 추가
+        contractEndDate: data.contractEndDate, // 추가
         items: data.items as Array<{
           itemCode: string;
           itemName: string;
@@ -336,7 +390,8 @@ export function CreatePreQuoteRfqDialog({
     }
   }
 
-  // 아이템 추가
+  // 아이템 추가 (사용안함)
+  /*
   const handleAddItem = () => {
     append({
       itemCode: "",
@@ -348,6 +403,7 @@ export function CreatePreQuoteRfqDialog({
       remark: "",
     })
   }
+  */
 
   return (
     <Dialog open={open} onOpenChange={onOpenChange}>
@@ -371,7 +427,7 @@ export function CreatePreQuoteRfqDialog({
                 
                 <div className="grid grid-cols-2 gap-4">
                   {/* 견적 종류 */}
-                  <div className="space-y-2">
+                  {/* <div className="space-y-2">
                     <FormField
                       control={form.control}
                       name="rfqType"
@@ -380,23 +436,14 @@ export function CreatePreQuoteRfqDialog({
                           <FormLabel>
                             견적 종류 <span className="text-red-500">*</span>
                           </FormLabel>
-                          <Select onValueChange={handleRfqTypeChange} value={field.value}>
-                            <FormControl>
-                              <SelectTrigger>
-                                <SelectValue placeholder="견적 종류 선택" />
-                              </SelectTrigger>
-                            </FormControl>
-                            <SelectContent>
-                              <SelectItem value="단가계약">단가계약</SelectItem>
-                              <SelectItem value="매각계약">매각계약</SelectItem>
-                              <SelectItem value="일반계약">일반계약</SelectItem>
-                            </SelectContent>
-                          </Select>
+                          <FormControl>
+                            <Input {...field} value="사전견적(입찰)" readOnly className="bg-muted" />
+                          </FormControl>
                           <FormMessage />
                         </FormItem>
                       )}
                     />
-                  </div>
+                  </div> */}
 
                   {/* 제출마감일 */}
                   <FormField
@@ -405,7 +452,7 @@ export function CreatePreQuoteRfqDialog({
                     render={({ field }) => (
                       <FormItem className="flex flex-col">
                         <FormLabel>
-                          제출마감일 <span className="text-red-500">*</span>
+                          제출마감일
                         </FormLabel>
                         <Popover>
                           <PopoverTrigger asChild>
@@ -420,7 +467,7 @@ export function CreatePreQuoteRfqDialog({
                                 {field.value ? (
                                   format(field.value, "yyyy-MM-dd")
                                 ) : (
-                                  <span>제출마감일을 선택하세요</span>
+                                  <span>제출마감일을 선택하세요 (선택)</span>
                                 )}
                                 <CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
                               </Button>
@@ -468,23 +515,24 @@ export function CreatePreQuoteRfqDialog({
                 />
 
                 {/* 프로젝트 선택 */}
-                <FormField
-                  control={form.control}
-                  name="projectId"
-                  render={({ field }) => (
+                <div className="space-y-2">
                     <FormItem className="flex flex-col">
                       <FormLabel>프로젝트</FormLabel>
                       <FormControl>
-                        {/* ProjectSelector는 별도 컴포넌트 필요 */}
                         <Input
-                          placeholder="프로젝트 ID (선택사항)"
-                          type="number"
-                          {...field}
-                          onChange={(e) => field.onChange(e.target.value ? Number(e.target.value) : undefined)}
+                          value={projectInfo}
+                          readOnly
+                          className="bg-muted"
+                          placeholder="프로젝트 정보 없음"
                         />
                       </FormControl>
-                      <FormMessage />
                     </FormItem>
+                </div>
+                <FormField
+                  control={form.control}
+                  name="projectId"
+                  render={({ field }) => (
+                    <input type="hidden" {...field} value={field.value || ''} />
                   )}
                 />
 
@@ -502,7 +550,7 @@ export function CreatePreQuoteRfqDialog({
                           selectedCode={selectedBidPic}
                           onCodeSelect={(code) => {
                             setSelectedBidPic(code)
-                            field.onChange(code.USER_ID)
+                            field.onChange(code.user?.id)
                           }}
                           placeholder="입찰담당자 선택"
                         />
@@ -526,6 +574,87 @@ export function CreatePreQuoteRfqDialog({
                   </div>
                 )}
 
+                {/* 계약기간 */}
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={form.control}
+                    name="contractStartDate"
+                    render={({ field }) => (
+                      <FormItem className="flex flex-col">
+                        <FormLabel>계약기간 시작</FormLabel>
+                        <Popover>
+                          <PopoverTrigger asChild>
+                            <FormControl>
+                              <Button
+                                variant="outline"
+                                className={cn(
+                                  "w-full pl-3 text-left font-normal",
+                                  !field.value && "text-muted-foreground"
+                                )}
+                              >
+                                {field.value ? (
+                                  format(field.value, "yyyy-MM-dd")
+                                ) : (
+                                  <span>시작일 선택</span>
+                                )}
+                                <CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
+                              </Button>
+                            </FormControl>
+                          </PopoverTrigger>
+                          <PopoverContent className="w-auto p-0" align="start">
+                            <Calendar
+                              mode="single"
+                              selected={field.value}
+                              onSelect={field.onChange}
+                              initialFocus
+                            />
+                          </PopoverContent>
+                        </Popover>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={form.control}
+                    name="contractEndDate"
+                    render={({ field }) => (
+                      <FormItem className="flex flex-col">
+                        <FormLabel>계약기간 종료</FormLabel>
+                        <Popover>
+                          <PopoverTrigger asChild>
+                            <FormControl>
+                              <Button
+                                variant="outline"
+                                className={cn(
+                                  "w-full pl-3 text-left font-normal",
+                                  !field.value && "text-muted-foreground"
+                                )}
+                              >
+                                {field.value ? (
+                                  format(field.value, "yyyy-MM-dd")
+                                ) : (
+                                  <span>종료일 선택</span>
+                                )}
+                                <CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
+                              </Button>
+                            </FormControl>
+                          </PopoverTrigger>
+                          <PopoverContent className="w-auto p-0" align="start">
+                            <Calendar
+                              mode="single"
+                              selected={field.value}
+                              onSelect={field.onChange}
+                              initialFocus
+                            />
+                          </PopoverContent>
+                        </Popover>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
                 {/* 비고 */}
                 <FormField
                   control={form.control}
@@ -549,192 +678,10 @@ export function CreatePreQuoteRfqDialog({
 
               <Separator />
 
-              {/* 아이템 정보 섹션 */}
-              <div className="space-y-4">
-                <div className="flex items-center justify-between">
-                  <h3 className="text-lg font-semibold">자재 정보</h3>
-                  <Button
-                    type="button"
-                    variant="outline"
-                    size="sm"
-                    onClick={handleAddItem}
-                  >
-                    <PlusCircle className="mr-2 h-4 w-4" />
-                    자재 추가
-                  </Button>
-                </div>
-
-                <div className="space-y-3">
-                  {fields.map((field, index) => (
-                    <div key={field.id} className="border rounded-lg p-3 bg-gray-50/50">
-                      <div className="flex items-center justify-between mb-3">
-                        <span className="text-sm font-medium text-gray-700">
-                          자재 #{index + 1}
-                        </span>
-                        {fields.length > 1 && (
-                          <Button
-                            type="button"
-                            variant="ghost"
-                            size="sm"
-                            onClick={() => remove(index)}
-                            className="h-6 w-6 p-0 text-destructive hover:text-destructive"
-                          >
-                            <Trash2 className="h-3 w-3" />
-                          </Button>
-                        )}
-                      </div>
-                      
-                      {/* 자재그룹 선택 */}
-                      <div className="mb-3">
-                        <FormLabel className="text-xs">
-                          자재그룹(자재그룹명) <span className="text-red-500">*</span>
-                        </FormLabel>
-                        <div className="mt-1">
-                          <MaterialGroupSelectorDialogSingle
-                            triggerLabel="자재그룹 선택"
-                            selectedMaterial={(() => {
-                              const itemCode = form.watch(`items.${index}.itemCode`);
-                              const itemName = form.watch(`items.${index}.itemName`);
-                              if (itemCode && itemName) {
-                                return {
-                                  materialGroupCode: itemCode,
-                                  materialGroupDescription: itemName,
-                                  displayText: `${itemCode} - ${itemName}`
-                                } as MaterialSearchItem;
-                              }
-                              return null;
-                            })()}
-                            onMaterialSelect={(material) => {
-                              form.setValue(`items.${index}.itemCode`, material?.materialGroupCode || '');
-                              form.setValue(`items.${index}.itemName`, material?.materialGroupDescription || '');
-                            }}
-                            placeholder="자재그룹을 검색하세요..."
-                            title="자재그룹 선택"
-                            description="원하는 자재그룹을 검색하고 선택해주세요."
-                            triggerVariant="outline"
-                          />
-                        </div>
-                      </div>
-
-                      {/* 자재코드 선택 */}
-                      <div className="mb-3">
-                        <FormLabel className="text-xs">
-                          자재코드(자재명)
-                        </FormLabel>
-                        <div className="mt-1">
-                          <MaterialSelectorDialogSingle
-                            triggerLabel="자재코드 선택"
-                            selectedMaterial={(() => {
-                              const materialCode = form.watch(`items.${index}.materialCode`);
-                              const materialName = form.watch(`items.${index}.materialName`);
-                              if (materialCode && materialName) {
-                                return {
-                                  materialCode: materialCode,
-                                  materialName: materialName,
-                                  displayText: `${materialCode} - ${materialName}`
-                                } as SAPMaterialSearchItem;
-                              }
-                              return null;
-                            })()}
-                            onMaterialSelect={(material) => {
-                              form.setValue(`items.${index}.materialCode`, material?.materialCode || '');
-                              form.setValue(`items.${index}.materialName`, material?.materialName || '');
-                            }}
-                            placeholder="자재코드를 검색하세요..."
-                            title="자재코드 선택"
-                            description="원하는 자재코드를 검색하고 선택해주세요."
-                            triggerVariant="outline"
-                          />
-                        </div>
-                      </div>
-
-                      <div className="grid grid-cols-2 gap-3">
-                        {/* 수량 */}
-                        <FormField
-                          control={form.control}
-                          name={`items.${index}.quantity`}
-                          render={({ field }) => (
-                            <FormItem>
-                              <FormLabel className="text-xs">
-                                수량 <span className="text-red-500">*</span>
-                              </FormLabel>
-                              <FormControl>
-                                <Input
-                                  type="number"
-                                  min="1"
-                                  placeholder="1"
-                                  className="h-8 text-sm"
-                                  {...field}
-                                  onChange={(e) => field.onChange(Number(e.target.value))}
-                                />
-                              </FormControl>
-                              <FormMessage />
-                            </FormItem>
-                          )}
-                        />
-
-                        {/* 단위 */}
-                        <FormField
-                          control={form.control}
-                          name={`items.${index}.uom`}
-                          render={({ field }) => (
-                            <FormItem>
-                              <FormLabel className="text-xs">
-                                단위 <span className="text-red-500">*</span>
-                              </FormLabel>
-                              <Select onValueChange={field.onChange} value={field.value}>
-                                <FormControl>
-                                  <SelectTrigger className="h-8 text-sm">
-                                    <SelectValue placeholder="단위 선택" />
-                                  </SelectTrigger>
-                                </FormControl>
-                                <SelectContent>
-                                  <SelectItem value="EA">EA (Each)</SelectItem>
-                                  <SelectItem value="KG">KG (Kilogram)</SelectItem>
-                                  <SelectItem value="M">M (Meter)</SelectItem>
-                                  <SelectItem value="L">L (Liter)</SelectItem>
-                                  <SelectItem value="PC">PC (Piece)</SelectItem>
-                                  <SelectItem value="BOX">BOX (Box)</SelectItem>
-                                  <SelectItem value="SET">SET (Set)</SelectItem>
-                                  <SelectItem value="LOT">LOT (Lot)</SelectItem>
-                                  <SelectItem value="PCS">PCS (Pieces)</SelectItem>
-                                  <SelectItem value="TON">TON (Ton)</SelectItem>
-                                  <SelectItem value="G">G (Gram)</SelectItem>
-                                  <SelectItem value="ML">ML (Milliliter)</SelectItem>
-                                  <SelectItem value="CM">CM (Centimeter)</SelectItem>
-                                  <SelectItem value="MM">MM (Millimeter)</SelectItem>
-                                </SelectContent>
-                              </Select>
-                              <FormMessage />
-                            </FormItem>
-                          )}
-                        />
-                      </div>
-
-                      {/* 비고 */}
-                      <div className="mt-3">
-                        <FormField
-                          control={form.control}
-                          name={`items.${index}.remark`}
-                          render={({ field }) => (
-                            <FormItem>
-                              <FormLabel className="text-xs">비고</FormLabel>
-                              <FormControl>
-                                <Input
-                                  placeholder="자재별 비고사항"
-                                  className="h-8 text-sm"
-                                  {...field}
-                                />
-                              </FormControl>
-                              <FormMessage />
-                            </FormItem>
-                          )}
-                        />
-                      </div>
-                    </div>
-                  ))}
-                </div>
-              </div>
+              {/* 아이템 정보 섹션 (자동 매핑되므로 UI 제거) */}
+              {/* <div className="space-y-4">
+                ...
+              </div> */}
             </form>
           </Form>
         </ScrollArea>
-- 
cgit v1.2.3

