From 95866a13ba4e1c235373834460aa284b763fe0d9 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Mon, 23 Jun 2025 09:03:29 +0000
Subject: (최겸) 기술영업 RFQ 개발(0620 요구사항, 첨부파일, REV 등)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../table/detail-table/rfq-detail-column.tsx       | 101 +++++++++++++++++++--
 1 file changed, 95 insertions(+), 6 deletions(-)

(limited to 'lib/techsales-rfq/table/detail-table/rfq-detail-column.tsx')

diff --git a/lib/techsales-rfq/table/detail-table/rfq-detail-column.tsx b/lib/techsales-rfq/table/detail-table/rfq-detail-column.tsx
index 3e50a516..e921fcaa 100644
--- a/lib/techsales-rfq/table/detail-table/rfq-detail-column.tsx
+++ b/lib/techsales-rfq/table/detail-table/rfq-detail-column.tsx
@@ -5,7 +5,7 @@ import type { ColumnDef, Row } from "@tanstack/react-table";
 import { formatDate } from "@/lib/utils"
 import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
 import { Checkbox } from "@/components/ui/checkbox";
-import { MessageCircle, MoreHorizontal, Trash2 } from "lucide-react";
+import { MessageCircle, MoreHorizontal, Trash2, Paperclip } from "lucide-react";
 import { Button } from "@/components/ui/button";
 import { Badge } from "@/components/ui/badge";
 import {
@@ -38,6 +38,24 @@ export interface RfqDetailView {
   createdAt: Date | null
   updatedAt: Date | null
   createdByName: string | null
+  quotationCode?: string | null
+  rfqCode?: string | null
+  quotationAttachments?: Array<{
+    id: number
+    revisionId: number
+    fileName: string
+    fileSize: number
+    filePath: string
+    description?: string | null
+  }>
+}
+
+// 견적서 정보 타입 (Sheet용)
+export interface QuotationInfo {
+  id: number
+  quotationCode: string | null
+  vendorName?: string
+  rfqCode?: string
 }
 
 interface GetColumnsProps<TData> {
@@ -45,11 +63,15 @@ interface GetColumnsProps<TData> {
     React.SetStateAction<DataTableRowAction<TData> | null>
   >;
   unreadMessages?: Record<number, number>; // 읽지 않은 메시지 개수
+  onQuotationClick?: (quotationId: number) => void; // 견적 클릭 핸들러
+  openQuotationAttachmentsSheet?: (quotationId: number, quotationInfo: QuotationInfo) => void; // 견적서 첨부파일 sheet 열기
 }
 
 export function getRfqDetailColumns({
   setRowAction,
-  unreadMessages = {}
+  unreadMessages = {},
+  onQuotationClick,
+  openQuotationAttachmentsSheet
 }: GetColumnsProps<RfqDetailView>): ColumnDef<RfqDetailView>[] {
   return [
     {
@@ -66,15 +88,15 @@ export function getRfqDetailColumns({
       ),
       cell: ({ row }) => {
         const status = row.original.status;
-        const isDraft = status === "Draft";
+        const isSelectable = status ? !["Accepted", "Rejected"].includes(status) : true;
         
         return (
           <Checkbox
             checked={row.getIsSelected()}
             onCheckedChange={(value) => row.toggleSelected(!!value)}
-            disabled={!isDraft}
+            disabled={!isSelectable}
             aria-label="행 선택"
-            className={!isDraft ? "opacity-50 cursor-not-allowed" : ""}
+            className={!isSelectable ? "opacity-50 cursor-not-allowed" : ""}
           />
         );
       },
@@ -163,15 +185,31 @@ export function getRfqDetailColumns({
       cell: ({ row }) => {
         const value = row.getValue("totalPrice") as string | number | null;
         const currency = row.getValue("currency") as string | null;
+        const quotationId = row.original.id;
         
         if (value === null || value === undefined) return "-";
         
         // 숫자로 변환 시도
         const numValue = typeof value === 'string' ? parseFloat(value) : value;
+        const displayValue = isNaN(numValue) ? value : numValue.toLocaleString();
+        
+        // 견적값이 있고 클릭 핸들러가 있는 경우 클릭 가능한 버튼으로 표시
+        if (onQuotationClick && quotationId) {
+          return (
+            <Button
+              variant="link"
+              className="p-0 h-auto font-medium text-left justify-start hover:underline"
+              onClick={() => onQuotationClick(quotationId)}
+              title="견적 히스토리 보기"
+            >
+              {displayValue} {currency}
+            </Button>
+          );
+        }
         
         return (
           <div className="font-medium">
-            {isNaN(numValue) ? value : numValue.toLocaleString()} {currency}
+            {displayValue} {currency}
           </div>
         );
       },
@@ -181,6 +219,57 @@ export function getRfqDetailColumns({
       enableResizing: true,
       size: 140,
     },
+    {
+      accessorKey: "quotationAttachments",
+      header: ({ column }) => (
+        <DataTableColumnHeaderSimple column={column} title="첨부파일" />
+      ),
+      cell: ({ row }) => {
+        const attachments = row.original.quotationAttachments || [];
+        const attachmentCount = attachments.length;
+
+        if (attachmentCount === 0) {
+          return <div className="text-muted-foreground">-</div>;
+        }
+
+        return (
+          <Button
+            variant="ghost"
+            size="sm"
+            className="relative h-8 w-8 p-0 group"
+            onClick={() => {
+              // 견적서 첨부파일 sheet 열기
+              if (openQuotationAttachmentsSheet) {
+                const quotation = row.original;
+                openQuotationAttachmentsSheet(quotation.id, {
+                  id: quotation.id,
+                  quotationCode: quotation.quotationCode || null,
+                  vendorName: quotation.vendorName || undefined,
+                  rfqCode: quotation.rfqCode || undefined,
+                });
+              }
+            }}
+            title={
+              attachmentCount === 1 
+                ? `${attachments[0].fileName} (${(attachments[0].fileSize / 1024 / 1024).toFixed(2)} MB)` 
+                : `${attachmentCount}개의 첨부파일:\n${attachments.map(att => att.fileName).join('\n')}`
+            }
+          >
+            <Paperclip className="h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors" />
+            {attachmentCount > 0 && (
+              <span className="pointer-events-none absolute -top-1 -right-1 inline-flex h-4 min-w-[1rem] items-center justify-center rounded-full bg-primary px-1 text-[0.625rem] font-medium leading-none text-primary-foreground">
+                {attachmentCount}
+              </span>
+            )}
+          </Button>
+        );
+      },
+      meta: {
+        excelHeader: "첨부파일"
+      },
+      enableResizing: false,
+      size: 80,
+    },
     {
       accessorKey: "currency",
       header: ({ column }) => (
-- 
cgit v1.2.3

