"use client" import * as React from "react" import { type Table } from "@tanstack/react-table" import { Layers, Check, ChevronsUpDown, GripVertical, XCircle } from "lucide-react" import { toSentenceCase, cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover" import { Command, CommandList, CommandGroup, CommandItem, CommandInput, CommandEmpty, } from "@/components/ui/command" import { Sortable, SortableItem, SortableDragHandle, } from "@/components/ui/sortable" interface DataTableGroupListLocalProps { /** TanStack Table 인스턴스 (grouping을 사용할 수 있어야 함) */ table: Table } export function ClientDataTableGroupList({ table, }: DataTableGroupListLocalProps) { // ----------------------------- // 1) Local grouping state // ----------------------------- const [grouping, setGrouping] = React.useState( (table.initialState.grouping as string[]) ?? [] ) // Keep the table grouping in sync React.useEffect(() => { table.setGrouping(grouping) }, [grouping, table]) // Avoid duplicates (just in case) const uniqueGrouping = React.useMemo( () => [...new Set(grouping)], [grouping] ) // ----------------------------- // 2) Groupable columns // ----------------------------- const groupableColumns = React.useMemo( () => table .getAllColumns() .flatMap((column) => { if (column.columns && column.columns.length > 0) { return column.columns.filter(c => c.getCanGroup()); } return column.getCanGroup() ? [column] : []; }) .filter((col) => col.getCanGroup?.() !== false) .map((col) => { // If meta?.excelHeader is missing or undefined, fall back to `col.id` const friendlyName = typeof col.columnDef?.meta?.excelHeader === "string" ? col.columnDef.meta.excelHeader : col.id return { id: col.id, // Ensure it's always a string, so no type error: label: toSentenceCase(friendlyName), } }), [table] ) const ungroupedColumns = React.useMemo( () => groupableColumns.filter((c) => !uniqueGrouping.includes(c.id)), [groupableColumns, uniqueGrouping] ) // ----------------------------- // 3) Handlers // ----------------------------- // Add the first ungrouped column function addGroup() { const firstAvailable = ungroupedColumns[0] if (!firstAvailable) return setGrouping((prev) => [...prev, firstAvailable.id]) } // Remove a group function removeGroup(colId: string) { setGrouping((prev) => prev.filter((g) => g !== colId)) } // Reset grouping entirely function resetGrouping() { setGrouping([]) } // Reorder groups via Sortable function onGroupOrderChange(newGroups: string[]) { setGrouping(newGroups) } // ----------------------------- // 4) Render // ----------------------------- return ( ({ id }))} onValueChange={(items) => onGroupOrderChange(items.map((i) => i.id))} overlay={
} > 0 ? "gap-3.5" : "gap-2" )} > {uniqueGrouping.length > 0 ? ( <>

Group by

Grouping is applied to the currently loaded data only.

) : (

No grouping applied

Add grouping to organize your results.

)} {/* Current groups */}
{uniqueGrouping.map((colId) => { // Find the column's friendly label const colDef = groupableColumns.find((c) => c.id === colId) const label = colDef?.label ?? toSentenceCase(colId) return (
No columns found. {ungroupedColumns.map((column) => ( { // Replace colId with new value setGrouping((prev) => prev.map((g) => (g === colId ? value : g)) ) }} > {column.label} ))} {/* remove group */} {/* drag handle */}
) })}
{/* Footer: "Add group" & "Reset grouping" */}
{uniqueGrouping.length > 0 && ( )}
) }