From dfdfae3018f8499240f48d28ce634f4a5c56e006 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Wed, 2 Apr 2025 09:54:08 +0000
Subject: 벤더 코멘트 처리
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../table/investigation-table-columns.tsx          | 251 +++++++++++++++++++++
 1 file changed, 251 insertions(+)
 create mode 100644 lib/vendor-investigation/table/investigation-table-columns.tsx

(limited to 'lib/vendor-investigation/table/investigation-table-columns.tsx')

diff --git a/lib/vendor-investigation/table/investigation-table-columns.tsx b/lib/vendor-investigation/table/investigation-table-columns.tsx
new file mode 100644
index 00000000..fd76a9a5
--- /dev/null
+++ b/lib/vendor-investigation/table/investigation-table-columns.tsx
@@ -0,0 +1,251 @@
+"use client"
+
+import * as React from "react"
+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 { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
+import { formatDate } from "@/lib/utils" // or your date util
+
+// Example: If you have a type for row actions
+import { type DataTableRowAction } from "@/types/table"
+import { ContactItem, PossibleItem, vendorInvestigationsColumnsConfig, VendorInvestigationsViewWithContacts } from "@/config/vendorInvestigationsColumnsConfig"
+
+// Props that define how we handle special columns (contacts, items, actions, etc.)
+interface GetVendorInvestigationsColumnsProps {
+  setRowAction?: React.Dispatch<
+    React.SetStateAction<
+      DataTableRowAction<VendorInvestigationsViewWithContacts> | null
+    >
+  >
+  openContactsModal?: (investigationId: number, contacts: ContactItem[]) => void
+  openItemsDrawer?: (investigationId: number, items: PossibleItem[]) => void
+}
+
+// This function returns the array of columns for TanStack Table
+export function getColumns({
+  setRowAction,
+  openContactsModal,
+  openItemsDrawer,
+}: GetVendorInvestigationsColumnsProps): ColumnDef<
+  VendorInvestigationsViewWithContacts
+>[] {
+  // --------------------------------------------
+  // 1) Select (checkbox) column
+  // --------------------------------------------
+  const selectColumn: ColumnDef<VendorInvestigationsViewWithContacts> = {
+    id: "select",
+    header: ({ table }) => (
+      <Checkbox
+        checked={
+          table.getIsAllPageRowsSelected() ||
+          (table.getIsSomePageRowsSelected() && "indeterminate")
+        }
+        onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
+        aria-label="Select all"
+        className="translate-y-0.5"
+      />
+    ),
+    cell: ({ row }) => (
+      <Checkbox
+        checked={row.getIsSelected()}
+        onCheckedChange={(value) => row.toggleSelected(!!value)}
+        aria-label="Select row"
+        className="translate-y-0.5"
+      />
+    ),
+    size: 40,
+    enableSorting: false,
+    enableHiding: false,
+  }
+
+  // --------------------------------------------
+  // 2) Actions column (optional)
+  // --------------------------------------------
+  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"
+          onClick={() => {
+            // e.g. open a dropdown or set your row action
+            setRowAction?.({ type: "update", row })
+          }}
+        >
+          <Ellipsis className="size-4" aria-hidden="true" />
+        </Button>
+      )
+    },
+    size: 40,
+  }
+
+  // --------------------------------------------
+  // 3) Contacts column (badge count -> open modal)
+  // --------------------------------------------
+  const contactsColumn: ColumnDef<VendorInvestigationsViewWithContacts> = {
+    id: "contacts",
+    header: "Contacts",
+    cell: ({ row }) => {
+      const { contacts, investigationId } = row.original
+      const count = contacts?.length ?? 0
+
+      const handleClick = () => {
+        openContactsModal?.(investigationId, contacts)
+      }
+
+      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"
+          }
+        >
+          <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>
+        </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>
+      )
+    },
+    enableSorting: false,
+    size: 60,
+  }
+
+  // --------------------------------------------
+  // 5) Build "grouped" columns from config
+  // --------------------------------------------
+  const groupMap: Record<string, ColumnDef<VendorInvestigationsViewWithContacts>[]> = {}
+
+  vendorInvestigationsColumnsConfig.forEach((cfg) => {
+    const groupName = cfg.group || "_noGroup"
+
+    if (!groupMap[groupName]) {
+      groupMap[groupName] = []
+    }
+
+    const childCol: ColumnDef<VendorInvestigationsViewWithContacts> = {
+      accessorKey: cfg.id,
+      header: ({ column }) => (
+        <DataTableColumnHeaderSimple column={column} title={cfg.label} />
+      ),
+      meta: {
+        excelHeader: cfg.excelHeader,
+        group: cfg.group,
+        type: cfg.type,
+      },
+      cell: ({ row, cell }) => {
+        const val = cell.getValue()
+
+        // Example: Format date fields
+        if (
+          cfg.id === "investigationCreatedAt" ||
+          cfg.id === "investigationUpdatedAt" ||
+          cfg.id === "scheduledStartAt" ||
+          cfg.id === "scheduledEndAt" ||
+          cfg.id === "completedAt"
+        ) {
+          const dateVal = val ? new Date(val as string) : null
+          return dateVal ? formatDate(dateVal) : ""
+        }
+
+        // Example: You could show an icon for "investigationStatus"
+        if (cfg.id === "investigationStatus") {
+          return <span className="capitalize">{val as string}</span>
+        }
+
+        return val ?? ""
+      },
+    }
+
+    groupMap[groupName].push(childCol)
+  })
+
+  // Turn the groupMap into nested columns
+  const nestedColumns: ColumnDef<VendorInvestigationsViewWithContacts>[] = []
+  for (const [groupName, colDefs] of Object.entries(groupMap)) {
+    if (groupName === "_noGroup") {
+      nestedColumns.push(...colDefs)
+    } else {
+      nestedColumns.push({
+        id: groupName,
+        header: groupName,
+        columns: colDefs,
+      })
+    }
+  }
+
+  // --------------------------------------------
+  // 6) Return final columns array
+  //    (You can reorder these as you wish.)
+  // --------------------------------------------
+  return [
+    selectColumn,
+    ...nestedColumns,
+    contactsColumn,
+    possibleItemsColumn,
+    actionsColumn,
+  ]
+}
\ No newline at end of file
-- 
cgit v1.2.3

