summaryrefslogtreecommitdiff
path: root/lib/rfq-last
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-09-19 07:51:27 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-09-19 07:51:27 +0000
commit9ecdfb23fe3df6a5df86782385002c562dfc1198 (patch)
tree4188cb7e6bf2c862d9c86a59d79946bd41217227 /lib/rfq-last
parentb67861fbb424c7ad47ad1538f75e2945bd8890c5 (diff)
(대표님) rfq 히스토리, swp 등
Diffstat (limited to 'lib/rfq-last')
-rw-r--r--lib/rfq-last/attachment/rfq-attachments-table.tsx128
-rw-r--r--lib/rfq-last/quotation-compare-view.tsx4
-rw-r--r--lib/rfq-last/service.ts1
-rw-r--r--lib/rfq-last/table/create-general-rfq-dialog.tsx4
-rw-r--r--lib/rfq-last/table/rfq-table-toolbar-actions.tsx18
-rw-r--r--lib/rfq-last/vendor/rfq-vendor-table.tsx341
6 files changed, 363 insertions, 133 deletions
diff --git a/lib/rfq-last/attachment/rfq-attachments-table.tsx b/lib/rfq-last/attachment/rfq-attachments-table.tsx
index 155fd412..09c9fe35 100644
--- a/lib/rfq-last/attachment/rfq-attachments-table.tsx
+++ b/lib/rfq-last/attachment/rfq-attachments-table.tsx
@@ -50,6 +50,7 @@ import { AddAttachmentDialog } from "./add-attachment-dialog";
import { UpdateRevisionDialog } from "./update-revision-dialog";
import { toast } from "sonner";
import { RevisionHistoryDialog } from "./revision-historty-dialog";
+import { createFilterFn } from "@/components/client-data-table/table-filters";
// 타입 정의
interface RfqAttachment {
@@ -238,6 +239,7 @@ export function RfqAttachmentsTable({
{
accessorKey: "serialNo",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="일련번호" />,
+ filterFn: createFilterFn("text"), // 추가
cell: ({ row }) => (
<span className="font-mono text-sm">{row.original.serialNo || "-"}</span>
),
@@ -248,6 +250,7 @@ export function RfqAttachmentsTable({
{
accessorKey: "originalFileName",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="파일명" />,
+ filterFn: createFilterFn("text"), // 추가
cell: ({ row }) => {
const file = row.original;
return (
@@ -266,6 +269,7 @@ export function RfqAttachmentsTable({
{
accessorKey: "description",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="설명" />,
+ filterFn: createFilterFn("text"), // 추가
cell: ({ row }) => (
<div className="max-w-[200px] truncate" title={row.original.description || ""}>
{row.original.description || "-"}
@@ -276,6 +280,7 @@ export function RfqAttachmentsTable({
{
accessorKey: "currentRevision",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="리비전" />,
+ filterFn: createFilterFn("text"), // 추가
cell: ({ row }) => {
const revision = row.original.currentRevision;
return revision ? (
@@ -291,6 +296,7 @@ export function RfqAttachmentsTable({
{
accessorKey: "fileSize",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="크기" />,
+ filterFn: createFilterFn("number"), // number 타입으로 변경
cell: ({ row }) => (
<span className="text-sm text-muted-foreground">
{formatFileSize(row.original.fileSize)}
@@ -299,14 +305,50 @@ export function RfqAttachmentsTable({
size: 80,
},
{
+ accessorKey: "fileType",
+ header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="파일 타입" />,
+ filterFn: createFilterFn("select"), // 추가
+ cell: ({ row }) => {
+ const fileType = row.original.fileType;
+ if (!fileType) return <span className="text-muted-foreground">-</span>;
+
+ const type = fileType.toLowerCase();
+ let displayType = "기타";
+ let color = "text-gray-500";
+
+ if (type.includes('pdf')) {
+ displayType = "PDF";
+ color = "text-red-500";
+ } else if (type.includes('excel') || ['xls', 'xlsx'].includes(type)) {
+ displayType = "Excel";
+ color = "text-green-500";
+ } else if (type.includes('word') || ['doc', 'docx'].includes(type)) {
+ displayType = "Word";
+ color = "text-blue-500";
+ } else if (type.includes('image') || ['jpg', 'jpeg', 'png', 'gif'].includes(type)) {
+ displayType = "이미지";
+ color = "text-purple-500";
+ }
+
+ return (
+ <Badge variant="outline" className={cn("text-xs", color)}>
+ {displayType}
+ </Badge>
+ );
+ },
+ size: 100,
+ },
+ {
accessorKey: "createdByName",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="업로드자" />,
+ filterFn: createFilterFn("text"), // 추가
cell: ({ row }) => row.original.createdByName || "-",
size: 100,
},
{
accessorKey: "createdAt",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="업로드일" />,
+ filterFn: createFilterFn("date"), // date 타입으로 변경
cell: ({ row }) => {
const date = row.original.createdAt;
return date ? (
@@ -334,6 +376,7 @@ export function RfqAttachmentsTable({
{
accessorKey: "updatedAt",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="수정일" />,
+ filterFn: createFilterFn("date"), // date 타입으로 변경
cell: ({ row }) => {
const date = row.original.updatedAt;
return date ? format(new Date(date), "MM-dd HH:mm") : "-";
@@ -341,46 +384,37 @@ export function RfqAttachmentsTable({
size: 100,
},
{
+ accessorKey: "revisionComment",
+ header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="리비전 코멘트" />,
+ filterFn: createFilterFn("text"), // 추가
+ cell: ({ row }) => {
+ const comment = row.original.revisionComment;
+ return comment ? (
+ <TooltipProvider>
+ <Tooltip>
+ <TooltipTrigger asChild>
+ <span className="text-sm truncate max-w-[150px] block cursor-help">
+ {comment}
+ </span>
+ </TooltipTrigger>
+ <TooltipContent className="max-w-[300px]">
+ <p className="text-sm">{comment}</p>
+ </TooltipContent>
+ </Tooltip>
+ </TooltipProvider>
+ ) : (
+ <span className="text-muted-foreground">-</span>
+ );
+ },
+ size: 150,
+ },
+ {
id: "actions",
header: "작업",
cell: ({ row }) => {
return (
<DropdownMenu>
- <DropdownMenuTrigger asChild>
- <Button variant="ghost" className="h-8 w-8 p-0">
- <span className="sr-only">메뉴 열기</span>
- <svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path d="M3.625 7.5C3.625 8.12132 3.12132 8.625 2.5 8.625C1.87868 8.625 1.375 8.12132 1.375 7.5C1.375 6.87868 1.87868 6.375 2.5 6.375C3.12132 6.375 3.625 6.87868 3.625 7.5ZM8.625 7.5C8.625 8.12132 8.12132 8.625 7.5 8.625C6.87868 8.625 6.375 8.12132 6.375 7.5C6.375 6.87868 6.87868 6.375 7.5 6.375C8.12132 6.375 8.625 6.87868 8.625 7.5ZM12.5 8.625C13.1213 8.625 13.625 8.12132 13.625 7.5C13.625 6.87868 13.1213 6.375 12.5 6.375C11.8787 6.375 11.375 6.87868 11.375 7.5C11.375 8.12132 11.8787 8.625 12.5 8.625Z" fill="currentColor" fillRule="evenodd" clipRule="evenodd"></path>
- </svg>
- </Button>
- </DropdownMenuTrigger>
- <DropdownMenuContent align="end">
- <DropdownMenuItem onClick={() => handleAction({ row, type: "download" })}>
- <Download className="mr-2 h-4 w-4" />
- 다운로드
- </DropdownMenuItem>
- <DropdownMenuItem onClick={() => handleAction({ row, type: "preview" })}>
- <Eye className="mr-2 h-4 w-4" />
- 미리보기
- </DropdownMenuItem>
- <DropdownMenuSeparator />
- <DropdownMenuItem onClick={() => handleAction({ row, type: "history" })}>
- <History className="mr-2 h-4 w-4" />
- 리비전 이력
- </DropdownMenuItem>
- <DropdownMenuItem onClick={() => handleAction({ row, type: "update" })}>
- <Upload className="mr-2 h-4 w-4" />
- 새 버전 업로드
- </DropdownMenuItem>
- <DropdownMenuSeparator />
- <DropdownMenuItem
- onClick={() => handleAction({ row, type: "delete" })}
- className="text-red-600"
- >
- <Trash2 className="mr-2 h-4 w-4" />
- 삭제
- </DropdownMenuItem>
- </DropdownMenuContent>
+ {/* ... 기존 드롭다운 메뉴 내용 ... */}
</DropdownMenu>
);
},
@@ -394,18 +428,18 @@ export function RfqAttachmentsTable({
{ id: "originalFileName", label: "파일명", type: "text" },
{ id: "description", label: "설명", type: "text" },
{ id: "currentRevision", label: "리비전", type: "text" },
- {
- id: "fileType",
- label: "파일 타입",
- type: "select",
- options: [
- { label: "PDF", value: "pdf" },
- { label: "Excel", value: "xlsx" },
- { label: "Word", value: "docx" },
- { label: "이미지", value: "image" },
- { label: "기타", value: "other" },
- ]
- },
+ // {
+ // id: "fileType",
+ // label: "파일 타입",
+ // type: "select",
+ // options: [
+ // { label: "PDF", value: "pdf" },
+ // { label: "Excel", value: "xlsx" },
+ // { label: "Word", value: "docx" },
+ // { label: "이미지", value: "image" },
+ // { label: "기타", value: "other" },
+ // ]
+ // },
{ id: "createdByName", label: "업로드자", type: "text" },
{ id: "createdAt", label: "업로드일", type: "date" },
{ id: "updatedAt", label: "수정일", type: "date" },
diff --git a/lib/rfq-last/quotation-compare-view.tsx b/lib/rfq-last/quotation-compare-view.tsx
index 28c8b3b1..91d46295 100644
--- a/lib/rfq-last/quotation-compare-view.tsx
+++ b/lib/rfq-last/quotation-compare-view.tsx
@@ -507,7 +507,7 @@ export function QuotationCompareView({ data }: QuotationCompareViewProps) {
<FileText className="h-3 w-3" />
일반계약
</Button>
- <Button
+ {/* <Button
size="sm"
variant="default"
onClick={() => {
@@ -518,7 +518,7 @@ export function QuotationCompareView({ data }: QuotationCompareViewProps) {
>
<Globe className="h-3 w-3" />
입찰
- </Button>
+ </Button> */}
</div>
)}
</div>
diff --git a/lib/rfq-last/service.ts b/lib/rfq-last/service.ts
index 85db1ea7..43943c71 100644
--- a/lib/rfq-last/service.ts
+++ b/lib/rfq-last/service.ts
@@ -3096,7 +3096,6 @@ async function processVendors({
// PDF 저장 디렉토리 준비
const contractsDir = path.join(
- process.cwd(),
`${process.env.NAS_PATH}`,
"contracts",
"generated"
diff --git a/lib/rfq-last/table/create-general-rfq-dialog.tsx b/lib/rfq-last/table/create-general-rfq-dialog.tsx
index 7abf06a3..2c69f4b7 100644
--- a/lib/rfq-last/table/create-general-rfq-dialog.tsx
+++ b/lib/rfq-last/table/create-general-rfq-dialog.tsx
@@ -347,7 +347,7 @@ export function CreateGeneralRfqDialog({ onSuccess }: CreateGeneralRfqDialogProp
{/* 스크롤 가능한 컨텐츠 영역 */}
<ScrollArea className="flex-1 px-1">
<Form {...form}>
- <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 py-2">
+ <form id="createGeneralRfqForm" onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 py-2">
{/* 기본 정보 섹션 */}
<div className="space-y-4">
@@ -766,8 +766,10 @@ export function CreateGeneralRfqDialog({ onSuccess }: CreateGeneralRfqDialogProp
</Button>
<Button
type="submit"
+ form="createGeneralRfqForm"
onClick={form.handleSubmit(onSubmit)}
disabled={isLoading}
+ // variant="default"
>
{isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
{isLoading ? "생성 중..." : "일반견적 생성"}
diff --git a/lib/rfq-last/table/rfq-table-toolbar-actions.tsx b/lib/rfq-last/table/rfq-table-toolbar-actions.tsx
index d933fa95..7d48f5a4 100644
--- a/lib/rfq-last/table/rfq-table-toolbar-actions.tsx
+++ b/lib/rfq-last/table/rfq-table-toolbar-actions.tsx
@@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button";
import { Users, RefreshCw, FileDown, Plus } from "lucide-react";
import { RfqsLastView } from "@/db/schema";
import { RfqAssignPicDialog } from "./rfq-assign-pic-dialog";
+import { CreateGeneralRfqDialog } from "./create-general-rfq-dialog"; // 추가
import { Badge } from "@/components/ui/badge";
import {
Tooltip,
@@ -26,6 +27,8 @@ export function RfqTableToolbarActions<TData>({
onRefresh
}: RfqTableToolbarActionsProps<TData>) {
const [showAssignDialog, setShowAssignDialog] = React.useState(false);
+
+ console.log(rfqCategory)
// 선택된 행 가져오기
const selectedRows = table.getFilteredSelectedRowModel().rows;
@@ -52,6 +55,10 @@ export function RfqTableToolbarActions<TData>({
onRefresh?.();
};
+ const handleCreateGeneralRfqSuccess = () => {
+ onRefresh?.(); // 테이블 데이터 새로고침
+ };
+
return (
<>
<div className="flex items-center gap-2">
@@ -114,16 +121,8 @@ export function RfqTableToolbarActions<TData>({
</Button>
{rfqCategory === "general" && (
- <Button
- variant="outline"
- size="sm"
- className="flex items-center gap-2"
- >
- <Plus className="h-4 w-4" />
- 일반견적 생성
- </Button>
+ <CreateGeneralRfqDialog onSuccess={handleCreateGeneralRfqSuccess} />
)}
-
<Button
variant="outline"
size="sm"
@@ -134,6 +133,7 @@ export function RfqTableToolbarActions<TData>({
엑셀 다운로드
</Button>
</div>
+
{/* 담당자 지정 다이얼로그 */}
<RfqAssignPicDialog
diff --git a/lib/rfq-last/vendor/rfq-vendor-table.tsx b/lib/rfq-last/vendor/rfq-vendor-table.tsx
index 72539113..0ebcecbd 100644
--- a/lib/rfq-last/vendor/rfq-vendor-table.tsx
+++ b/lib/rfq-last/vendor/rfq-vendor-table.tsx
@@ -29,7 +29,8 @@ import {
Router,
Shield,
CheckSquare,
- GitCompare
+ GitCompare,
+ Link
} from "lucide-react";
import { format } from "date-fns";
import { ko } from "date-fns/locale";
@@ -69,6 +70,7 @@ import { VendorResponseDetailDialog } from "./vendor-detail-dialog";
import { DeleteVendorDialog } from "./delete-vendor-dialog";
import { useRouter } from "next/navigation"
import { EditContractDialog } from "./edit-contract-dialog";
+import { createFilterFn } from "@/components/client-data-table/table-filters";
// 타입 정의
interface RfqDetail {
@@ -292,13 +294,14 @@ export function RfqVendorTable({
);
console.log(mergedData, "mergedData")
+ console.log(rfqId, "rfqId")
// Short List 확정 핸들러
const handleShortListConfirm = React.useCallback(async () => {
try {
setIsUpdatingShortList(true);
-
+
const vendorIds = selectedRows
.map(vendor => vendor.vendorId)
.filter(id => id != null);
@@ -320,7 +323,7 @@ export function RfqVendorTable({
// 견적 비교 핸들러
const handleQuotationCompare = React.useCallback(() => {
- const vendorsWithQuotation = selectedRows.filter(row =>
+ const vendorsWithQuotation = selectedRows.filter(row =>
row.response?.submission?.submittedAt
);
@@ -334,7 +337,7 @@ export function RfqVendorTable({
.map(v => v.vendorId)
.filter(id => id != null)
.join(',');
-
+
router.push(`/evcp/rfq-last/${rfqId}/compare?vendors=${vendorIds}`);
}, [selectedRows, rfqId, router]);
@@ -349,8 +352,8 @@ export function RfqVendorTable({
setIsLoadingSendData(true);
// 선택된 벤더 ID들 추출
- const selectedVendorIds = rfqCode?.startsWith("I")? selectedRows
- .filter(v=>v.shortList)
+ const selectedVendorIds = rfqCode?.startsWith("I") ? selectedRows
+ .filter(v => v.shortList)
.map(row => row.vendorId)
.filter(id => id != null) :
selectedRows
@@ -468,7 +471,7 @@ export function RfqVendorTable({
} else {
toast.success(`${data.vendors.length}개 업체에 RFQ를 발송했습니다.`);
}
-
+
// 페이지 새로고침
router.refresh();
} catch (error) {
@@ -593,6 +596,8 @@ export function RfqVendorTable({
{
accessorKey: "rfqCode",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="ITB/RFQ/견적 No." />,
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
return (
<span className="font-mono text-xs">{row.original.rfqCode || "-"}</span>
@@ -603,6 +608,8 @@ export function RfqVendorTable({
{
accessorKey: "vendorName",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="협력업체정보" />,
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const vendor = row.original;
return (
@@ -620,12 +627,16 @@ export function RfqVendorTable({
{
accessorKey: "vendorCategory",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="업체분류" />,
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => row.original.vendorCategory || "-",
size: 100,
},
{
accessorKey: "vendorCountry",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="내외자 (위치)" />,
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const country = row.original.vendorCountry;
const isLocal = country === "KR" || country === "한국";
@@ -640,6 +651,8 @@ export function RfqVendorTable({
{
accessorKey: "vendorGrade",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="AVL 정보 (등급)" />,
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const grade = row.original.vendorGrade;
if (!grade) return <span className="text-muted-foreground">-</span>;
@@ -661,9 +674,11 @@ export function RfqVendorTable({
header: ({ column }) => (
<ClientDataTableColumnHeaderSimple column={column} title="TBE 상태" />
),
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const status = row.original.tbeStatus;
-
+
if (!status || status === "준비중") {
return (
<Badge variant="outline" className="text-gray-500">
@@ -672,7 +687,7 @@ export function RfqVendorTable({
</Badge>
);
}
-
+
const statusConfig = {
"진행중": { variant: "default", icon: <Clock className="h-3 w-3 mr-1" />, color: "text-blue-600" },
"검토중": { variant: "secondary", icon: <Eye className="h-3 w-3 mr-1" />, color: "text-orange-600" },
@@ -680,7 +695,7 @@ export function RfqVendorTable({
"완료": { variant: "success", icon: <CheckCircle className="h-3 w-3 mr-1" />, color: "text-green-600" },
"취소": { variant: "destructive", icon: <XCircle className="h-3 w-3 mr-1" />, color: "text-red-600" },
}[status] || { variant: "outline", icon: null, color: "text-gray-600" };
-
+
return (
<Badge variant={statusConfig.variant as any} className={statusConfig.color}>
{statusConfig.icon}
@@ -690,42 +705,44 @@ export function RfqVendorTable({
},
size: 100,
},
-
+
{
accessorKey: "tbeEvaluationResult",
header: ({ column }) => (
<ClientDataTableColumnHeaderSimple column={column} title="TBE 평가" />
),
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const result = row.original.tbeEvaluationResult;
const status = row.original.tbeStatus;
-
+
// TBE가 완료되지 않았으면 표시하지 않음
if (status !== "완료" || !result) {
return <span className="text-xs text-muted-foreground">-</span>;
}
-
+
const resultConfig = {
- "Acceptable": {
- variant: "success",
- icon: <CheckCircle className="h-3 w-3" />,
+ "Acceptable": {
+ variant: "success",
+ icon: <CheckCircle className="h-3 w-3" />,
text: "적합",
color: "bg-green-50 text-green-700 border-green-200"
},
- "Acceptable with Comment": {
- variant: "warning",
- icon: <AlertCircle className="h-3 w-3" />,
+ "Acceptable with Comment": {
+ variant: "warning",
+ icon: <AlertCircle className="h-3 w-3" />,
text: "조건부 적합",
color: "bg-yellow-50 text-yellow-700 border-yellow-200"
},
- "Not Acceptable": {
- variant: "destructive",
- icon: <XCircle className="h-3 w-3" />,
+ "Not Acceptable": {
+ variant: "destructive",
+ icon: <XCircle className="h-3 w-3" />,
text: "부적합",
color: "bg-red-50 text-red-700 border-red-200"
},
}[result];
-
+
return (
<TooltipProvider>
<Tooltip>
@@ -755,6 +772,8 @@ export function RfqVendorTable({
{
accessorKey: "contractRequirements",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="기본계약 요청" />,
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const vendor = row.original;
const isKorean = vendor.vendorCountry === "KR" || vendor.vendorCountry === "한국";
@@ -833,6 +852,8 @@ export function RfqVendorTable({
{
accessorKey: "sendVersion",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="발송 회차" />,
+ filterFn: createFilterFn("number"),
+
cell: ({ row }) => {
const version = row.original.sendVersion;
@@ -844,6 +865,8 @@ export function RfqVendorTable({
{
accessorKey: "emailStatus",
header: "이메일 상태",
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const response = row.original;
const emailSentAt = response?.emailSentAt;
@@ -936,6 +959,8 @@ export function RfqVendorTable({
{
accessorKey: "currency",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="요청 통화" />,
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const currency = row.original.currency;
return currency ? (
@@ -949,6 +974,8 @@ export function RfqVendorTable({
{
accessorKey: "paymentTermsCode",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="지급조건" />,
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const code = row.original.paymentTermsCode;
const desc = row.original.paymentTermsDescription;
@@ -972,12 +999,16 @@ export function RfqVendorTable({
{
accessorKey: "taxCode",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="Tax" />,
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => row.original.taxCode || "-",
size: 60,
},
{
accessorKey: "deliveryDate",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="계약납기일/기간" />,
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const deliveryDate = row.original.deliveryDate;
const contractDuration = row.original.contractDuration;
@@ -1003,6 +1034,8 @@ export function RfqVendorTable({
{
accessorKey: "incotermsCode",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="Incoterms" />,
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const code = row.original.incotermsCode;
const detail = row.original.incotermsDetail;
@@ -1030,6 +1063,8 @@ export function RfqVendorTable({
{
accessorKey: "placeOfShipping",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="선적지" />,
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const place = row.original.placeOfShipping;
return place ? (
@@ -1046,6 +1081,7 @@ export function RfqVendorTable({
{
accessorKey: "placeOfDestination",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="도착지" />,
+ filterFn: createFilterFn("text"),
cell: ({ row }) => {
const place = row.original.placeOfDestination;
return place ? (
@@ -1062,6 +1098,8 @@ export function RfqVendorTable({
{
id: "additionalConditions",
header: "추가조건",
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const conditions = formatAdditionalConditions(row.original);
if (conditions === "-") {
@@ -1084,6 +1122,8 @@ export function RfqVendorTable({
{
accessorKey: "response.submission.submittedAt",
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="참여여부 (회신일)" />,
+ filterFn: createFilterFn("text"),
+
cell: ({ row }) => {
const participationRepliedAt = row.original.response?.attend?.participationRepliedAt;
@@ -1131,6 +1171,7 @@ export function RfqVendorTable({
},
...(rfqCode?.startsWith("I") ? [{
accessorKey: "shortList",
+ filterFn: createFilterFn("boolean"), // boolean으로 변경
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="Short List" />,
cell: ({ row }) => (
row.original.shortList ? (
@@ -1143,6 +1184,7 @@ export function RfqVendorTable({
}] : []),
{
accessorKey: "updatedByUserName",
+ filterFn: createFilterFn("text"), // 추가
header: ({ column }) => <ClientDataTableColumnHeaderSimple column={column} title="최신수정자" />,
cell: ({ row }) => {
const name = row.original.updatedByUserName;
@@ -1238,24 +1280,160 @@ export function RfqVendorTable({
}
], [handleAction, rfqCode, isLoadingSendData]);
+ // advancedFilterFields 정의 - columns와 매칭되도록 정리
const advancedFilterFields: DataTableAdvancedFilterField<any>[] = [
- { id: "vendorName", label: "벤더명", type: "text" },
- { id: "vendorCode", label: "벤더코드", type: "text" },
- { id: "vendorCountry", label: "국가", type: "text" },
{
- id: "response.status",
- label: "응답 상태",
+ id: "rfqCode",
+ label: "ITB/RFQ/견적 No.",
+ type: "text"
+ },
+ {
+ id: "vendorName",
+ label: "협력업체명",
+ type: "text"
+ },
+ {
+ id: "vendorCode",
+ label: "협력업체코드",
+ type: "text"
+ },
+ {
+ id: "vendorCategory",
+ label: "업체분류",
+ type: "select",
+ options: [
+ { label: "제조업체", value: "제조업체" },
+ { label: "무역업체", value: "무역업체" },
+ { label: "대리점", value: "대리점" },
+ // 실제 카테고리에 맞게 추가
+ ]
+ },
+ {
+ id: "vendorCountry",
+ label: "내외자(위치)",
+ type: "select",
+ options: [
+ { label: "한국(KR)", value: "KR" },
+ { label: "한국", value: "한국" },
+ { label: "중국(CN)", value: "CN" },
+ { label: "일본(JP)", value: "JP" },
+ { label: "미국(US)", value: "US" },
+ { label: "독일(DE)", value: "DE" },
+ // 필요한 국가 추가
+ ]
+ },
+ {
+ id: "vendorGrade",
+ label: "AVL 등급",
type: "select",
options: [
- { label: "초대됨", value: "초대됨" },
- { label: "작성중", value: "작성중" },
- { label: "제출완료", value: "제출완료" },
- { label: "수정요청", value: "수정요청" },
- { label: "최종확정", value: "최종확정" },
+ { label: "A", value: "A" },
+ { label: "B", value: "B" },
+ { label: "C", value: "C" },
+ { label: "D", value: "D" },
+ ]
+ },
+ {
+ id: "tbeStatus",
+ label: "TBE 상태",
+ type: "select",
+ options: [
+ { label: "대기", value: "준비중" },
+ { label: "진행중", value: "진행중" },
+ { label: "검토중", value: "검토중" },
+ { label: "보류", value: "보류" },
+ { label: "완료", value: "완료" },
{ label: "취소", value: "취소" },
]
},
{
+ id: "tbeEvaluationResult",
+ label: "TBE 평가결과",
+ type: "select",
+ options: [
+ { label: "적합", value: "Acceptable" },
+ { label: "조건부 적합", value: "Acceptable with Comment" },
+ { label: "부적합", value: "Not Acceptable" },
+ ]
+ },
+ {
+ id: "sendVersion",
+ label: "발송 회차",
+ type: "number"
+ },
+ {
+ id: "emailStatus",
+ label: "이메일 상태",
+ type: "select",
+ options: [
+ { label: "미발송", value: "미발송" },
+ { label: "발송됨", value: "sent" },
+ { label: "발송 실패", value: "failed" },
+ ]
+ },
+ {
+ id: "currency",
+ label: "요청 통화",
+ type: "select",
+ options: [
+ { label: "KRW", value: "KRW" },
+ { label: "USD", value: "USD" },
+ { label: "EUR", value: "EUR" },
+ { label: "JPY", value: "JPY" },
+ { label: "CNY", value: "CNY" },
+ ]
+ },
+ {
+ id: "paymentTermsCode",
+ label: "지급조건",
+ type: "text"
+ },
+ {
+ id: "taxCode",
+ label: "Tax",
+ type: "text",
+ },
+ {
+ id: "deliveryDate",
+ label: "계약납기일",
+ type: "date"
+ },
+ {
+ id: "contractDuration",
+ label: "계약기간",
+ type: "text"
+ },
+ {
+ id: "incotermsCode",
+ label: "Incoterms",
+ type: "text",
+ },
+ {
+ id: "placeOfShipping",
+ label: "선적지",
+ type: "text"
+ },
+ {
+ id: "placeOfDestination",
+ label: "도착지",
+ type: "text"
+ },
+ {
+ id: "firstYn",
+ label: "초도품",
+ type: "boolean"
+ },
+ {
+ id: "materialPriceRelatedYn",
+ label: "연동제",
+ type: "boolean"
+ },
+ {
+ id: "sparepartYn",
+ label: "스페어파트",
+ type: "boolean"
+ },
+ ...(rfqCode?.startsWith("I") ? [{
id: "shortList",
label: "Short List",
type: "select",
@@ -1263,7 +1441,12 @@ export function RfqVendorTable({
{ label: "선정", value: "true" },
{ label: "대기", value: "false" },
]
- },
+ }] : []),
+ {
+ id: "updatedByUserName",
+ label: "최신수정자",
+ type: "text"
+ }
];
// 선택된 벤더 정보 (BatchUpdate용)
@@ -1280,15 +1463,27 @@ export function RfqVendorTable({
// 참여 의사가 있는 선택된 벤더 수 계산
const participatingCount = selectedRows.length;
- const shortListCount = selectedRows.filter(v=>v.shortList).length;
+ const shortListCount = selectedRows.filter(v => v.shortList).length;
// 견적서가 있는 선택된 벤더 수 계산
- const quotationCount = selectedRows.filter(row =>
+ const quotationCount = selectedRows.filter(row =>
row.response?.submission?.submittedAt
).length;
return (
<div className="flex items-center gap-2">
+ {(rfqCode?.startsWith("I") || rfqCode?.startsWith("R")) &&
+
+ <Button
+ variant="outline"
+ size="sm"
+ >
+ <Link className="h-4 w-4 mr-2" />
+ AVL 연동
+ </Button>
+ }
+
+
<Button
variant="outline"
size="sm"
@@ -1298,32 +1493,32 @@ export function RfqVendorTable({
<Plus className="h-4 w-4 mr-2" />
벤더 추가
</Button>
-
+
{selectedRows.length > 0 && (
<>
{/* Short List 확정 버튼 */}
- {rfqCode?.startsWith("I")&&
- <Button
- variant="outline"
- size="sm"
- onClick={handleShortListConfirm}
- disabled={isUpdatingShortList }
+ {rfqCode?.startsWith("I") &&
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleShortListConfirm}
+ disabled={isUpdatingShortList}
// className={ "border-green-500 text-green-600 hover:bg-green-50" }
- >
- {isUpdatingShortList ? (
- <>
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
- 처리중...
- </>
- ) : (
- <>
- <CheckSquare className="h-4 w-4 mr-2" />
- Short List 확정
- {participatingCount > 0 && ` (${participatingCount})`}
- </>
- )}
- </Button>
- }
+ >
+ {isUpdatingShortList ? (
+ <>
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
+ 처리중...
+ </>
+ ) : (
+ <>
+ <CheckSquare className="h-4 w-4 mr-2" />
+ Short List 확정
+ {participatingCount > 0 && ` (${participatingCount})`}
+ </>
+ )}
+ </Button>
+ }
{/* 견적 비교 버튼 */}
<Button
@@ -1334,7 +1529,7 @@ export function RfqVendorTable({
className={quotationCount >= 2 ? "border-blue-500 text-blue-600 hover:bg-blue-50" : ""}
>
<GitCompare className="h-4 w-4 mr-2" />
- 견적 비교
+ 견적 비교
{quotationCount > 0 && ` (${quotationCount})`}
</Button>
@@ -1370,7 +1565,7 @@ export function RfqVendorTable({
</Button>
</>
)}
-
+
<Button
variant="outline"
size="sm"
@@ -1464,19 +1659,19 @@ export function RfqVendorTable({
/>
)}
- {/* 기본계약 수정 다이얼로그 - 새로 추가 */}
- {editContractVendor && (
- <EditContractDialog
- open={!!editContractVendor}
- onOpenChange={(open) => !open && setEditContractVendor(null)}
- rfqId={rfqId}
- vendor={editContractVendor}
- onSuccess={() => {
- setEditContractVendor(null);
- router.refresh();
- }}
- />
- )}
+ {/* 기본계약 수정 다이얼로그 - 새로 추가 */}
+ {editContractVendor && (
+ <EditContractDialog
+ open={!!editContractVendor}
+ onOpenChange={(open) => !open && setEditContractVendor(null)}
+ rfqId={rfqId}
+ vendor={editContractVendor}
+ onSuccess={() => {
+ setEditContractVendor(null);
+ router.refresh();
+ }}
+ />
+ )}
</>
);
} \ No newline at end of file