summaryrefslogtreecommitdiff
path: root/lib/rfq-last/vendor/rfq-vendor-table.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rfq-last/vendor/rfq-vendor-table.tsx')
-rw-r--r--lib/rfq-last/vendor/rfq-vendor-table.tsx208
1 files changed, 155 insertions, 53 deletions
diff --git a/lib/rfq-last/vendor/rfq-vendor-table.tsx b/lib/rfq-last/vendor/rfq-vendor-table.tsx
index b6d42804..7f7afe14 100644
--- a/lib/rfq-last/vendor/rfq-vendor-table.tsx
+++ b/lib/rfq-last/vendor/rfq-vendor-table.tsx
@@ -3,7 +3,7 @@
import * as React from "react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
-import {
+import {
Plus,
Send,
Eye,
@@ -32,7 +32,7 @@ import { type ColumnDef } from "@tanstack/react-table";
import { Checkbox } from "@/components/ui/checkbox";
import { ClientDataTableColumnHeaderSimple } from "@/components/client-data-table/data-table-column-simple-header";
import { ClientDataTable } from "@/components/client-data-table/data-table";
-import {
+import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
@@ -50,7 +50,9 @@ import { cn } from "@/lib/utils";
import { toast } from "sonner";
import { AddVendorDialog } from "./add-vendor-dialog";
import { BatchUpdateConditionsDialog } from "./batch-update-conditions-dialog";
+import { SendRfqDialog } from "./send-rfq-dialog";
// import { VendorDetailDialog } from "./vendor-detail-dialog";
+// import { sendRfqToVendors } from "@/app/actions/rfq/send-rfq.action";
// 타입 정의
interface RfqDetail {
@@ -59,9 +61,10 @@ interface RfqDetail {
vendorName: string | null;
vendorCode: string | null;
vendorCountry: string | null;
- vendorCategory?: string | null; // 업체분류
- vendorGrade?: string | null; // AVL 등급
- basicContract?: string | null; // 기본계약
+ vendorEmail?: string | null;
+ vendorCategory?: string | null;
+ vendorGrade?: string | null;
+ basicContract?: string | null;
shortList: boolean;
currency: string | null;
paymentTermsCode: string | null;
@@ -97,11 +100,42 @@ interface VendorResponse {
attachmentCount?: number;
}
+// Props 타입 정의 (중복 제거하고 하나로 통합)
interface RfqVendorTableProps {
rfqId: number;
rfqCode?: string;
rfqDetails: RfqDetail[];
vendorResponses: VendorResponse[];
+ // 추가 props
+ rfqInfo?: {
+ rfqTitle: string;
+ rfqType: string;
+ projectCode?: string;
+ projectName?: string;
+ picName?: string;
+ picCode?: string;
+ picTeam?: string;
+ packageNo?: string;
+ packageName?: string;
+ designPicName?: string;
+ designTeam?: string;
+ materialGroup?: string;
+ materialGroupDesc?: string;
+ dueDate: Date;
+ quotationType?: string;
+ evaluationApply?: boolean;
+ contractType?: string;
+ };
+ attachments?: Array<{
+ id: number;
+ attachmentType: string;
+ serialNo: string;
+ currentRevision: string;
+ description?: string;
+ fileName?: string;
+ fileSize?: number;
+ uploadedAt?: Date;
+ }>;
}
// 상태별 아이콘 반환
@@ -158,43 +192,94 @@ export function RfqVendorTable({
rfqCode,
rfqDetails,
vendorResponses,
+ rfqInfo,
+ attachments,
}: RfqVendorTableProps) {
const [isRefreshing, setIsRefreshing] = React.useState(false);
const [selectedRows, setSelectedRows] = React.useState<any[]>([]);
const [isAddDialogOpen, setIsAddDialogOpen] = React.useState(false);
const [isBatchUpdateOpen, setIsBatchUpdateOpen] = React.useState(false);
const [selectedVendor, setSelectedVendor] = React.useState<any | null>(null);
+ const [isSendDialogOpen, setIsSendDialogOpen] = React.useState(false);
// 데이터 병합
const mergedData = React.useMemo(
() => mergeVendorData(rfqDetails, vendorResponses, rfqCode),
[rfqDetails, vendorResponses, rfqCode]
);
-
+
+ // 일괄 발송 핸들러
+ const handleBulkSend = React.useCallback(async () => {
+ if (selectedRows.length === 0) {
+ toast.warning("발송할 벤더를 선택해주세요.");
+ return;
+ }
+
+ // 다이얼로그 열기
+ setIsSendDialogOpen(true);
+ }, [selectedRows]);
+
+ // RFQ 발송 핸들러
+ const handleSendRfq = React.useCallback(async (data: {
+ vendors: Array<{
+ vendorId: number;
+ vendorName: string;
+ vendorCode?: string | null;
+ vendorCountry?: string | null;
+ vendorEmail?: string | null;
+ currency?: string | null;
+ additionalRecipients: string[];
+ }>;
+ attachments: number[];
+ message?: string;
+ }) => {
+ try {
+ // 서버 액션 호출
+ // const result = await sendRfqToVendors({
+ // rfqId,
+ // rfqCode,
+ // vendors: data.vendors,
+ // attachmentIds: data.attachments,
+ // message: data.message,
+ // });
+
+ // 임시 성공 처리
+ console.log("RFQ 발송 데이터:", data);
+
+ // 성공 후 처리
+ setSelectedRows([]);
+ toast.success(`${data.vendors.length}개 업체에 RFQ를 발송했습니다.`);
+ } catch (error) {
+ console.error("RFQ 발송 실패:", error);
+ toast.error("RFQ 발송에 실패했습니다.");
+ throw error;
+ }
+ }, [rfqId, rfqCode]);
+
// 액션 처리
const handleAction = React.useCallback(async (action: string, vendor: any) => {
switch (action) {
case "view":
setSelectedVendor(vendor);
break;
-
+
case "send":
// RFQ 발송 로직
toast.info(`${vendor.vendorName}에게 RFQ를 발송합니다.`);
break;
-
+
case "edit":
// 수정 로직
toast.info("수정 기능은 준비중입니다.");
break;
-
+
case "delete":
// 삭제 로직
if (confirm(`${vendor.vendorName}을(를) 삭제하시겠습니까?`)) {
toast.success(`${vendor.vendorName}이(가) 삭제되었습니다.`);
}
break;
-
+
case "response-detail":
// 회신 상세 보기
toast.info(`${vendor.vendorName}의 회신 상세를 확인합니다.`);
@@ -202,21 +287,6 @@ export function RfqVendorTable({
}
}, []);
- // 선택된 벤더들에게 일괄 발송
- const handleBulkSend = React.useCallback(async () => {
- if (selectedRows.length === 0) {
- toast.warning("발송할 벤더를 선택해주세요.");
- return;
- }
-
- const vendorNames = selectedRows.map(r => r.vendorName).join(", ");
- if (confirm(`선택한 ${selectedRows.length}개 벤더에게 RFQ를 발송하시겠습니까?\n\n${vendorNames}`)) {
- toast.success(`${selectedRows.length}개 벤더에게 RFQ를 발송했습니다.`);
- setSelectedRows([]);
- }
- }, [selectedRows]);
-
-
// 컬럼 정의 (확장된 버전)
const columns: ColumnDef<any>[] = React.useMemo(() => [
{
@@ -251,19 +321,6 @@ export function RfqVendorTable({
},
size: 120,
},
- // {
- // accessorKey: "response.responseVersion",
- // header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="Rev" />,
- // cell: ({ row }) => {
- // const version = row.original.response?.responseVersion;
- // return version ? (
- // <Badge variant="outline" className="font-mono">v{version}</Badge>
- // ) : (
- // <span className="text-muted-foreground">-</span>
- // );
- // },
- // size: 60,
- // },
{
accessorKey: "vendorName",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="협력업체정보" />,
@@ -307,14 +364,14 @@ export function RfqVendorTable({
cell: ({ row }) => {
const grade = row.original.vendorGrade;
if (!grade) return <span className="text-muted-foreground">-</span>;
-
+
const gradeColor = {
"A": "text-green-600",
- "B": "text-blue-600",
+ "B": "text-blue-600",
"C": "text-yellow-600",
"D": "text-red-600",
}[grade] || "text-gray-600";
-
+
return <span className={cn("font-semibold", gradeColor)}>{grade}</span>;
},
size: 100,
@@ -373,15 +430,15 @@ export function RfqVendorTable({
cell: ({ row }) => {
const deliveryDate = row.original.deliveryDate;
const contractDuration = row.original.contractDuration;
-
+
return (
<div className="flex flex-col gap-0.5">
- {deliveryDate && (
+ {deliveryDate && !rfqCode?.startsWith("F") && (
<span className="text-xs">
{format(new Date(deliveryDate), "yyyy-MM-dd")}
</span>
)}
- {contractDuration && (
+ {contractDuration && rfqCode?.startsWith("F") && (
<span className="text-xs text-muted-foreground">{contractDuration}</span>
)}
{!deliveryDate && !contractDuration && (
@@ -398,7 +455,7 @@ export function RfqVendorTable({
cell: ({ row }) => {
const code = row.original.incotermsCode;
const detail = row.original.incotermsDetail;
-
+
return (
<TooltipProvider>
<Tooltip>
@@ -459,7 +516,7 @@ export function RfqVendorTable({
if (conditions === "-") {
return <span className="text-muted-foreground">-</span>;
}
-
+
const items = conditions.split(", ");
return (
<div className="flex flex-wrap gap-1">
@@ -479,11 +536,11 @@ export function RfqVendorTable({
cell: ({ row }) => {
const submittedAt = row.original.response?.submittedAt;
const status = row.original.response?.status;
-
+
if (!submittedAt) {
return <Badge variant="outline">미참여</Badge>;
}
-
+
return (
<div className="flex flex-col gap-0.5">
<Badge variant="default" className="text-xs">참여</Badge>
@@ -500,11 +557,11 @@ export function RfqVendorTable({
header: "회신상세",
cell: ({ row }) => {
const hasResponse = !!row.original.response?.submittedAt;
-
+
if (!hasResponse) {
return <span className="text-muted-foreground text-xs">-</span>;
}
-
+
return (
<Button
variant="ghost"
@@ -565,7 +622,7 @@ export function RfqVendorTable({
cell: ({ row }) => {
const vendor = row.original;
const hasResponse = !!vendor.response;
-
+
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -592,7 +649,7 @@ export function RfqVendorTable({
조건 수정
</DropdownMenuItem>
<DropdownMenuSeparator />
- <DropdownMenuItem
+ <DropdownMenuItem
onClick={() => handleAction("delete", vendor)}
className="text-red-600"
>
@@ -605,7 +662,7 @@ export function RfqVendorTable({
},
size: 60,
},
- ], [handleAction]);
+ ], [handleAction, rfqCode]);
const advancedFilterFields: DataTableAdvancedFilterField<any>[] = [
{ id: "vendorName", label: "벤더명", type: "text" },
@@ -644,6 +701,41 @@ export function RfqVendorTable({
}));
}, [selectedRows]);
+ // 선택된 벤더 정보 (Send용)
+ const selectedVendorsForSend = React.useMemo(() => {
+ return selectedRows.map(row => ({
+ vendorId: row.vendorId,
+ vendorName: row.vendorName,
+ vendorCode: row.vendorCode,
+ vendorCountry: row.vendorCountry,
+ vendorEmail: row.vendorEmail || `vendor${row.vendorId}@example.com`,
+ currency: row.currency,
+ }));
+ }, [selectedRows]);
+
+ // RFQ 정보 준비 (다이얼로그용)
+ const rfqInfoForDialog = React.useMemo(() => {
+ // props로 받은 rfqInfo 사용, 없으면 기본값
+ return rfqInfo || {
+ rfqCode: rfqCode || '',
+ rfqTitle: '테스트 RFQ',
+ rfqType: '정기견적',
+ projectCode: 'PN003',
+ projectName: 'PETRONAS ZLNG nearshore project',
+ picName: '김*종',
+ picCode: '86D',
+ picTeam: '해양구매팀(해양구매1)',
+ packageNo: 'MM03',
+ packageName: 'Deck Machinery',
+ designPicName: '이*진',
+ designTeam: '전장설계팀 (전장기기시스템)',
+ materialGroup: 'BE2101',
+ materialGroupDesc: 'Combined Windlass & Mooring Wi',
+ dueDate: new Date('2025-07-05'),
+ evaluationApply: true,
+ };
+ }, [rfqInfo, rfqCode]);
+
// 추가 액션 버튼들
const additionalActions = React.useMemo(() => (
<div className="flex items-center gap-2">
@@ -732,6 +824,16 @@ export function RfqVendorTable({
}}
/>
+ {/* RFQ 발송 다이얼로그 */}
+ <SendRfqDialog
+ open={isSendDialogOpen}
+ onOpenChange={setIsSendDialogOpen}
+ selectedVendors={selectedVendorsForSend}
+ rfqInfo={rfqInfoForDialog}
+ attachments={attachments || []}
+ onSend={handleSendRfq}
+ />
+
{/* 벤더 상세 다이얼로그 */}
{/* {selectedVendor && (
<VendorDetailDialog