diff options
Diffstat (limited to 'lib/b-rfq/initial/initial-rfq-detail-columns.tsx')
| -rw-r--r-- | lib/b-rfq/initial/initial-rfq-detail-columns.tsx | 358 |
1 files changed, 209 insertions, 149 deletions
diff --git a/lib/b-rfq/initial/initial-rfq-detail-columns.tsx b/lib/b-rfq/initial/initial-rfq-detail-columns.tsx index f7ac0960..02dfd765 100644 --- a/lib/b-rfq/initial/initial-rfq-detail-columns.tsx +++ b/lib/b-rfq/initial/initial-rfq-detail-columns.tsx @@ -3,8 +3,9 @@ import * as React from "react" import { type ColumnDef } from "@tanstack/react-table" +import { type Row } from "@tanstack/react-table" import { - Ellipsis, Building, Calendar, Eye, + Ellipsis, Building, Eye, Edit, Trash, MessageSquare, Settings, CheckCircle2, XCircle } from "lucide-react" @@ -14,17 +15,27 @@ import { Button } from "@/components/ui/button" import { Checkbox } from "@/components/ui/checkbox" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, - DropdownMenuSeparator, DropdownMenuTrigger + DropdownMenuSeparator, DropdownMenuTrigger, DropdownMenuShortcut } from "@/components/ui/dropdown-menu" import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" +import { InitialRfqDetailView } from "@/db/schema" + + +// RowAction 타입 정의 +export interface DataTableRowAction<TData> { + row: Row<TData> + type: "update" | "delete" +} interface GetInitialRfqDetailColumnsProps { onSelectDetail?: (detail: any) => void + setRowAction?: React.Dispatch<React.SetStateAction<DataTableRowAction<InitialRfqDetailView> | null>> } export function getInitialRfqDetailColumns({ - onSelectDetail -}: GetInitialRfqDetailColumnsProps = {}): ColumnDef<any>[] { + onSelectDetail, + setRowAction +}: GetInitialRfqDetailColumnsProps = {}): ColumnDef<InitialRfqDetailView>[] { return [ /** ───────────── 체크박스 ───────────── */ @@ -56,53 +67,6 @@ export function getInitialRfqDetailColumns({ /** ───────────── RFQ 정보 ───────────── */ { - accessorKey: "rfqCode", - header: ({ column }) => ( - <DataTableColumnHeaderSimple column={column} title="RFQ 코드" /> - ), - cell: ({ row }) => ( - <Button - variant="link" - className="p-0 h-auto font-medium text-blue-600 hover:text-blue-800" - onClick={() => onSelectDetail?.(row.original)} - > - {row.getValue("rfqCode") as string} - </Button> - ), - size: 120, - }, - { - accessorKey: "rfqStatus", - header: ({ column }) => ( - <DataTableColumnHeaderSimple column={column} title="RFQ 상태" /> - ), - cell: ({ row }) => { - const status = row.getValue("rfqStatus") as string - const getStatusColor = (status: string) => { - switch (status) { - case "DRAFT": return "secondary" - case "Doc. Received": return "outline" - case "PIC Assigned": return "default" - case "Doc. Confirmed": return "default" - case "Init. RFQ Sent": return "default" - case "Init. RFQ Answered": return "success" - case "TBE started": return "warning" - case "TBE finished": return "warning" - case "Final RFQ Sent": return "default" - case "Quotation Received": return "success" - case "Vendor Selected": return "success" - default: return "secondary" - } - } - return ( - <Badge variant={getStatusColor(status) as any}> - {status} - </Badge> - ) - }, - size: 140 - }, - { accessorKey: "initialRfqStatus", header: ({ column }) => ( <DataTableColumnHeaderSimple column={column} title="초기 RFQ 상태" /> @@ -111,11 +75,10 @@ export function getInitialRfqDetailColumns({ const status = row.getValue("initialRfqStatus") as string const getInitialStatusColor = (status: string) => { switch (status) { - case "PENDING": return "outline" - case "SENT": return "default" - case "RESPONDED": return "success" - case "EXPIRED": return "destructive" - case "CANCELLED": return "secondary" + case "DRAFT": return "outline" + case "Init. RFQ Sent": return "default" + case "Init. RFQ Answered": return "success" + case "S/L Decline": return "destructive" default: return "secondary" } } @@ -127,6 +90,30 @@ export function getInitialRfqDetailColumns({ }, size: 120 }, + { + accessorKey: "rfqCode", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="RFQ 코드" /> + ), + cell: ({ row }) => ( + <div className="text-sm"> + {row.getValue("rfqCode") as string} + </div> + ), + size: 120, + }, + { + accessorKey: "rfqRevision", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="RFQ 리비전" /> + ), + cell: ({ row }) => ( + <div className="text-sm"> + Rev. {row.getValue("rfqRevision") as number} + </div> + ), + size: 120, + }, /** ───────────── 벤더 정보 ───────────── */ { @@ -137,7 +124,8 @@ export function getInitialRfqDetailColumns({ cell: ({ row }) => { const vendorName = row.original.vendorName as string const vendorCode = row.original.vendorCode as string - const vendorCountry = row.original.vendorCountry as string + const vendorType = row.original.vendorCategory as string + const vendorCountry = row.original.vendorCountry === "KR" ? "D":"F" const businessSize = row.original.vendorBusinessSize as string return ( @@ -147,7 +135,7 @@ export function getInitialRfqDetailColumns({ <div className="font-medium">{vendorName}</div> </div> <div className="text-sm text-muted-foreground"> - {vendorCode} • {vendorCountry} + {vendorCode} • {vendorType} • {vendorCountry} </div> {businessSize && ( <Badge variant="outline" className="text-xs"> @@ -160,42 +148,67 @@ export function getInitialRfqDetailColumns({ size: 200, }, - /** ───────────── 날짜 정보 ───────────── */ { - accessorKey: "dueDate", + accessorKey: "cpRequestYn", header: ({ column }) => ( - <DataTableColumnHeaderSimple column={column} title="마감일" /> + <DataTableColumnHeaderSimple column={column} title="CP" /> ), cell: ({ row }) => { - const dueDate = row.getValue("dueDate") as Date - const isOverdue = dueDate && new Date(dueDate) < new Date() - - return dueDate ? ( - <div className={`flex items-center gap-2 ${isOverdue ? 'text-red-600' : ''}`}> - <Calendar className="h-4 w-4" /> - <div> - <div className="font-medium">{formatDate(dueDate)}</div> - {isOverdue && ( - <div className="text-xs text-red-600">지연</div> - )} - </div> - </div> + const cpRequest = row.getValue("cpRequestYn") as boolean + return cpRequest ? ( + <Badge variant="outline" className="text-xs"> + Yes + </Badge> ) : ( - <span className="text-muted-foreground">-</span> + <span className="text-muted-foreground text-xs">-</span> ) }, - size: 120, + size: 60, }, { - accessorKey: "validDate", + accessorKey: "prjectGtcYn", header: ({ column }) => ( - <DataTableColumnHeaderSimple column={column} title="유효일" /> + <DataTableColumnHeaderSimple column={column} title="Project GTC" /> ), cell: ({ row }) => { - const validDate = row.getValue("validDate") as Date - return validDate ? ( + const projectGtc = row.getValue("prjectGtcYn") as boolean + return projectGtc ? ( + <Badge variant="outline" className="text-xs"> + Yes + </Badge> + ) : ( + <span className="text-muted-foreground text-xs">-</span> + ) + }, + size: 100, + }, + { + accessorKey: "gtcYn", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="GTC" /> + ), + cell: ({ row }) => { + const gtc = row.getValue("gtcYn") as boolean + return gtc ? ( + <Badge variant="outline" className="text-xs"> + Yes + </Badge> + ) : ( + <span className="text-muted-foreground text-xs">-</span> + ) + }, + size: 60, + }, + { + accessorKey: "gtcValidDate", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="GTC 유효일" /> + ), + cell: ({ row }) => { + const gtcValidDate = row.getValue("gtcValidDate") as string + return gtcValidDate ? ( <div className="text-sm"> - {formatDate(validDate)} + {gtcValidDate} </div> ) : ( <span className="text-muted-foreground">-</span> @@ -204,7 +217,42 @@ export function getInitialRfqDetailColumns({ size: 100, }, - /** ───────────── Incoterms ───────────── */ + { + accessorKey: "classification", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="선급" /> + ), + cell: ({ row }) => { + const classification = row.getValue("classification") as string + return classification ? ( + <div className="text-sm font-medium max-w-[120px] truncate" title={classification}> + {classification} + </div> + ) : ( + <span className="text-muted-foreground">-</span> + ) + }, + size: 120, + }, + + { + accessorKey: "sparepart", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Spare Part" /> + ), + cell: ({ row }) => { + const sparepart = row.getValue("sparepart") as string + return sparepart ? ( + <Badge variant="outline" className="text-xs"> + {sparepart} + </Badge> + ) : ( + <span className="text-muted-foreground">-</span> + ) + }, + size: 100, + }, + { id: "incoterms", header: ({ column }) => ( @@ -230,84 +278,71 @@ export function getInitialRfqDetailColumns({ size: 120, }, - /** ───────────── 플래그 정보 ───────────── */ + /** ───────────── 날짜 정보 ───────────── */ { - id: "flags", + accessorKey: "validDate", header: ({ column }) => ( - <DataTableColumnHeaderSimple column={column} title="플래그" /> + <DataTableColumnHeaderSimple column={column} title="유효일" /> ), cell: ({ row }) => { - const shortList = row.original.shortList as boolean - const returnYn = row.original.returnYn as boolean - const cpRequestYn = row.original.cpRequestYn as boolean - const prjectGtcYn = row.original.prjectGtcYn as boolean - - return ( - <div className="flex flex-wrap gap-1"> - {shortList && ( - <Badge variant="secondary" className="text-xs"> - <CheckCircle2 className="h-3 w-3 mr-1" /> - Short List - </Badge> - )} - {returnYn && ( - <Badge variant="outline" className="text-xs"> - Return - </Badge> - )} - {cpRequestYn && ( - <Badge variant="outline" className="text-xs"> - CP Request - </Badge> - )} - {prjectGtcYn && ( - <Badge variant="outline" className="text-xs"> - GTC - </Badge> - )} + const validDate = row.getValue("validDate") as Date + return validDate ? ( + <div className="text-sm"> + {formatDate(validDate)} </div> + ) : ( + <span className="text-muted-foreground">-</span> ) }, - size: 150, + size: 100, }, - - /** ───────────── 분류 정보 ───────────── */ { - id: "classification", + accessorKey: "dueDate", header: ({ column }) => ( - <DataTableColumnHeaderSimple column={column} title="분류" /> + <DataTableColumnHeaderSimple column={column} title="마감일" /> ), cell: ({ row }) => { - const classification = row.original.classification as string - const sparepart = row.original.sparepart as string + const dueDate = row.getValue("dueDate") as Date + const isOverdue = dueDate && new Date(dueDate) < new Date() - return ( - <div className="space-y-1"> - {classification && ( - <div className="text-sm font-medium max-w-[120px] truncate" title={classification}> - {classification} - </div> - )} - {sparepart && ( - <Badge variant="outline" className="text-xs"> - {sparepart} - </Badge> + return dueDate ? ( + <div className={`${isOverdue ? 'text-red-600' : ''}`}> + <div className="font-medium">{formatDate(dueDate)}</div> + {isOverdue && ( + <div className="text-xs text-red-600">지연</div> )} </div> + ) : ( + <span className="text-muted-foreground">-</span> ) }, size: 120, }, - - /** ───────────── 리비전 정보 ───────────── */ + { + accessorKey: "returnYn", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="RFQ 회신여부" /> + ), + cell: ({ row }) => { + const returnFlag = row.getValue("returnYn") as boolean + return returnFlag ? ( + <Badge variant="outline" className="text-xs"> + Yes + </Badge> + ) : ( + <span className="text-muted-foreground text-xs">-</span> + ) + }, + size: 70, + }, { accessorKey: "returnRevision", header: ({ column }) => ( - <DataTableColumnHeaderSimple column={column} title="리비전" /> + <DataTableColumnHeaderSimple column={column} title="회신 리비전" /> ), cell: ({ row }) => { const revision = row.getValue("returnRevision") as number - return revision ? ( + return revision > 0 ? ( <Badge variant="outline"> Rev. {revision} </Badge> @@ -318,6 +353,25 @@ export function getInitialRfqDetailColumns({ size: 80, }, + { + accessorKey: "shortList", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Short List" /> + ), + cell: ({ row }) => { + const shortList = row.getValue("shortList") as boolean + return shortList ? ( + <Badge variant="secondary" className="text-xs"> + <CheckCircle2 className="h-3 w-3 mr-1" /> + Yes + </Badge> + ) : ( + <span className="text-muted-foreground text-xs">-</span> + ) + }, + size: 90, + }, + /** ───────────── 등록/수정 정보 ───────────── */ { accessorKey: "createdAt", @@ -333,7 +387,7 @@ export function getInitialRfqDetailColumns({ <div className="text-sm">{formatDate(created)}</div> {updated && new Date(updated) > new Date(created) && ( <div className="text-xs text-blue-600"> - 수정: {formatDate(updated)} + 수정: {formatDate(updated, "KR")} </div> )} </div> @@ -346,7 +400,7 @@ export function getInitialRfqDetailColumns({ { id: "actions", enableHiding: false, - cell: ({ row }) => { + cell: function Cell({ row }) { return ( <DropdownMenu> <DropdownMenuTrigger asChild> @@ -359,23 +413,29 @@ export function getInitialRfqDetailColumns({ </Button> </DropdownMenuTrigger> <DropdownMenuContent align="end" className="w-48"> - <DropdownMenuItem onClick={() => onSelectDetail?.(row.original)}> - <Eye className="mr-2 h-4 w-4" /> - 상세 보기 - </DropdownMenuItem> <DropdownMenuItem> <MessageSquare className="mr-2 h-4 w-4" /> 벤더 응답 보기 </DropdownMenuItem> <DropdownMenuSeparator /> - <DropdownMenuItem> - <Settings className="mr-2 h-4 w-4" /> - 설정 수정 - </DropdownMenuItem> - <DropdownMenuItem className="text-red-600"> - <XCircle className="mr-2 h-4 w-4" /> - 삭제 - </DropdownMenuItem> + {setRowAction && ( + <> + <DropdownMenuItem + onSelect={() => setRowAction({ row, type: "update" })} + > + <Edit className="mr-2 h-4 w-4" /> + 수정 + </DropdownMenuItem> + <DropdownMenuItem + onSelect={() => setRowAction({ row, type: "delete" })} + > + <Trash className="mr-2 h-4 w-4" /> + 삭제 + <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut> + </DropdownMenuItem> + </> + )} + </DropdownMenuContent> </DropdownMenu> ) |
