diff options
Diffstat (limited to 'lib/vendor-investigation/table/investigation-table-columns.tsx')
| -rw-r--r-- | lib/vendor-investigation/table/investigation-table-columns.tsx | 313 |
1 files changed, 202 insertions, 111 deletions
diff --git a/lib/vendor-investigation/table/investigation-table-columns.tsx b/lib/vendor-investigation/table/investigation-table-columns.tsx index fd76a9a5..6146d940 100644 --- a/lib/vendor-investigation/table/investigation-table-columns.tsx +++ b/lib/vendor-investigation/table/investigation-table-columns.tsx @@ -5,31 +5,30 @@ import { ColumnDef } from "@tanstack/react-table" import { Checkbox } from "@/components/ui/checkbox" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" -import { Ellipsis, Users, Boxes } from "lucide-react" -// import { toast } from "sonner" // If needed +import { Edit, Ellipsis } from "lucide-react" import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" -import { formatDate } from "@/lib/utils" // or your date util +import { formatDate } from "@/lib/utils" -// Example: If you have a type for row actions +// Import types import { type DataTableRowAction } from "@/types/table" -import { ContactItem, PossibleItem, vendorInvestigationsColumnsConfig, VendorInvestigationsViewWithContacts } from "@/config/vendorInvestigationsColumnsConfig" +import { + vendorInvestigationsColumnsConfig, + VendorInvestigationsViewWithContacts +} from "@/config/vendorInvestigationsColumnsConfig" -// Props that define how we handle special columns (contacts, items, actions, etc.) +// Props for the column generator function interface GetVendorInvestigationsColumnsProps { setRowAction?: React.Dispatch< React.SetStateAction< DataTableRowAction<VendorInvestigationsViewWithContacts> | null > > - openContactsModal?: (investigationId: number, contacts: ContactItem[]) => void - openItemsDrawer?: (investigationId: number, items: PossibleItem[]) => void + openVendorDetailsModal?: (vendorId: number) => void } -// This function returns the array of columns for TanStack Table export function getColumns({ setRowAction, - openContactsModal, - openItemsDrawer, + openVendorDetailsModal, }: GetVendorInvestigationsColumnsProps): ColumnDef< VendorInvestigationsViewWithContacts >[] { @@ -63,25 +62,22 @@ export function getColumns({ } // -------------------------------------------- - // 2) Actions column (optional) + // 2) Actions column // -------------------------------------------- const actionsColumn: ColumnDef<VendorInvestigationsViewWithContacts> = { id: "actions", enableHiding: false, cell: ({ row }) => { - const inv = row.original - return ( <Button variant="ghost" className="flex size-8 p-0 data-[state=open]:bg-muted" - aria-label="Open menu" + aria-label="실사 정보 수정" onClick={() => { - // e.g. open a dropdown or set your row action setRowAction?.({ type: "update", row }) }} > - <Ellipsis className="size-4" aria-hidden="true" /> + <Edit className="size-4" aria-hidden="true" /> </Button> ) }, @@ -89,97 +85,44 @@ export function getColumns({ } // -------------------------------------------- - // 3) Contacts column (badge count -> open modal) + // 3) Vendor Name with click handler // -------------------------------------------- - const contactsColumn: ColumnDef<VendorInvestigationsViewWithContacts> = { - id: "contacts", - header: "Contacts", + const vendorNameColumn: ColumnDef<VendorInvestigationsViewWithContacts> = { + accessorKey: "vendorName", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="협력사명" /> + ), cell: ({ row }) => { - const { contacts, investigationId } = row.original - const count = contacts?.length ?? 0 - - const handleClick = () => { - openContactsModal?.(investigationId, contacts) - } + const vendorId = row.original.vendorId + const vendorName = row.getValue("vendorName") as string return ( <Button - variant="ghost" - size="sm" - className="relative h-8 w-8 p-0 group" - onClick={handleClick} - aria-label={ - count > 0 ? `View ${count} contacts` : "Add contacts" - } + variant="link" + className="p-0 h-auto font-normal" + onClick={() => openVendorDetailsModal?.(vendorId)} > - <Users className="h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors" /> - {count > 0 && ( - <Badge - variant="secondary" - className="pointer-events-none absolute -top-1 -right-1 h-4 min-w-[1rem] p-0 text-[0.625rem] leading-none flex items-center justify-center" - > - {count} - </Badge> - )} - <span className="sr-only"> - {count > 0 ? `${count} Contacts` : "Add Contacts"} - </span> + {vendorName} </Button> ) }, - enableSorting: false, - size: 60, - } - - // -------------------------------------------- - // 4) Possible Items column (badge count -> open drawer) - // -------------------------------------------- - const possibleItemsColumn: ColumnDef<VendorInvestigationsViewWithContacts> = { - id: "possibleItems", - header: "Items", - cell: ({ row }) => { - const { possibleItems, investigationId } = row.original - const count = possibleItems?.length ?? 0 - - const handleClick = () => { - openItemsDrawer?.(investigationId, possibleItems) - } - - return ( - <Button - variant="ghost" - size="sm" - className="relative h-8 w-8 p-0 group" - onClick={handleClick} - aria-label={ - count > 0 ? `View ${count} items` : "Add items" - } - > - <Boxes className="h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors" /> - {count > 0 && ( - <Badge - variant="secondary" - className="pointer-events-none absolute -top-1 -right-1 h-4 min-w-[1rem] p-0 text-[0.625rem] leading-none flex items-center justify-center" - > - {count} - </Badge> - )} - <span className="sr-only"> - {count > 0 ? `${count} Items` : "Add Items"} - </span> - </Button> - ) + meta: { + excelHeader: "협력사명", + group: "협력업체", }, - enableSorting: false, - size: 60, } // -------------------------------------------- - // 5) Build "grouped" columns from config + // 4) Build grouped columns from config // -------------------------------------------- const groupMap: Record<string, ColumnDef<VendorInvestigationsViewWithContacts>[]> = {} vendorInvestigationsColumnsConfig.forEach((cfg) => { + // Skip vendorName as we have a custom column for it + if (cfg.id === "vendorName") { + return + } + const groupName = cfg.group || "_noGroup" if (!groupMap[groupName]) { @@ -196,34 +139,120 @@ export function getColumns({ group: cfg.group, type: cfg.type, }, - cell: ({ row, cell }) => { - const val = cell.getValue() - - // Example: Format date fields + cell: ({ row, column }) => { + const value = row.getValue(column.id) + + // Handle date fields if ( - cfg.id === "investigationCreatedAt" || - cfg.id === "investigationUpdatedAt" || - cfg.id === "scheduledStartAt" || - cfg.id === "scheduledEndAt" || - cfg.id === "completedAt" + column.id === "scheduledStartAt" || + column.id === "scheduledEndAt" || + column.id === "forecastedAt" || + column.id === "requestedAt" || + column.id === "confirmedAt" || + column.id === "completedAt" || + column.id === "createdAt" || + column.id === "updatedAt" ) { - const dateVal = val ? new Date(val as string) : null - return dateVal ? formatDate(dateVal) : "" + if (!value) return "" + return formatDate(new Date(value as string), "KR") + } + + // Handle status fields with badges + if (column.id === "investigationStatus") { + if (!value) return "" + + return ( + <Badge variant={getStatusVariant(value as string)}> + {formatStatus(value as string)} + </Badge> + ) + } + + // Handle evaluation type + if (column.id === "evaluationType") { + if (!value) return "" + + return ( + <span> + {formatEnumValue(value as string)} + </span> + ) + } + + // Handle evaluation result + if (column.id === "evaluationResult") { + if (!value) return "" + + return ( + <Badge variant={getResultVariant(value as string)}> + {formatEnumValue(value as string)} + </Badge> + ) + } + + // Handle IDs for pqSubmissionId (keeping for reference) + if (column.id === "pqSubmissionId") { + return value ? `#${value}` : "" + } + + // Handle file attachment status + if (column.id === "hasAttachments") { + return ( + <div className="flex items-center justify-center"> + {value ? ( + <Badge variant="default" className="text-xs"> + 📎 첨부 + </Badge> + ) : ( + <span className="text-muted-foreground text-xs">-</span> + )} + </div> + ) } - // Example: You could show an icon for "investigationStatus" - if (cfg.id === "investigationStatus") { - return <span className="capitalize">{val as string}</span> + if (column.id === "requesterName") { + if (!value && !row.original.requesterEmail) { + return <span className="text-muted-foreground">미배정</span> + } + + return ( + <div className="flex flex-col"> + <span>{value || "미배정"}</span> + {row.original.requesterEmail && ( + <span className="text-xs text-muted-foreground">{row.original.requesterEmail}</span> + )} + </div> + ) + } + + if (column.id === "qmManagerName") { + if (!value && !row.original.qmManagerEmail) { + return <span className="text-muted-foreground">미배정</span> + } + + return ( + <div className="flex flex-col"> + <span>{value || "미배정"}</span> + {row.original.qmManagerEmail && ( + <span className="text-xs text-muted-foreground">{row.original.qmManagerEmail}</span> + )} + </div> + ) } - return val ?? "" + return value ?? "" }, } groupMap[groupName].push(childCol) }) - // Turn the groupMap into nested columns + // Insert custom vendorNameColumn in the 협력업체 group + if (groupMap["협력업체"]) { + groupMap["협력업체"].unshift(vendorNameColumn) + } + + // Convert the groupMap into nested columns const nestedColumns: ColumnDef<VendorInvestigationsViewWithContacts>[] = [] for (const [groupName, colDefs] of Object.entries(groupMap)) { if (groupName === "_noGroup") { @@ -238,14 +267,76 @@ export function getColumns({ } // -------------------------------------------- - // 6) Return final columns array - // (You can reorder these as you wish.) + // 5) Return final columns array (simplified) // -------------------------------------------- return [ selectColumn, ...nestedColumns, - contactsColumn, - possibleItemsColumn, actionsColumn, ] +} + +// Helper functions for formatting +function formatStatus(status: string): string { + switch (status) { + case "PLANNED": + return "계획됨" + case "IN_PROGRESS": + return "진행 중" + case "COMPLETED": + return "완료됨" + case "CANCELED": + return "취소됨" + default: + return status + } +} + +function formatEnumValue(value: string): string { + switch (value) { + // Evaluation types + case "SITE_AUDIT": + return "실사의뢰평가" + case "QM_SELF_AUDIT": + return "QM자체평가" + + // Evaluation results + case "APPROVED": + return "승인" + case "SUPPLEMENT": + return "보완" + case "REJECTED": + return "불가" + + default: + return value.replace(/_/g, " ").toLowerCase() + } +} + +function getStatusVariant(status: string): "default" | "secondary" | "outline" | "destructive" { + switch (status) { + case "PLANNED": + return "secondary" + case "IN_PROGRESS": + return "default" + case "COMPLETED": + return "outline" + case "CANCELED": + return "destructive" + default: + return "default" + } +} + +function getResultVariant(result: string): "default" | "secondary" | "outline" | "destructive" { + switch (result) { + case "APPROVED": + return "default" + case "SUPPLEMENT": + return "secondary" + case "REJECTED": + return "destructive" + default: + return "outline" + } }
\ No newline at end of file |
