"use client" import * as React from "react" import { type Table } from "@tanstack/react-table" import { useQueryState, parseAsArrayOf, parseAsString } from "nuqs" 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" import { useTranslation } from '@/i18n/client' import { useParams, usePathname } from "next/navigation"; interface DataTableGroupListProps { /** TanStack Table 인스턴스 (grouping을 이미 사용할 수 있어야 함) */ table: Table /** 정렬과 동일하게 URL 쿼리에 grouping을 저장할 때 쓰는 debounce 시간 (ms) */ debounceMs: number /** shallow 라우팅 여부 */ shallow?: boolean } export function DataTableGroupList({ table, debounceMs, shallow, }: DataTableGroupListProps) { const id = React.useId() const params = useParams(); const lng = params?.lng as string; const { t } = useTranslation(lng); // ------------------------------------------------------ // 1) 초기 그룹핑 상태 + URL Query State 동기화 // ------------------------------------------------------ const initialGrouping = (table.initialState.grouping ?? []) as string[] // group 쿼리 파라미터를 string[]로 파싱 // parseAsArrayOf(parseAsString, ',')를 이용 const [grouping, setGrouping] = useQueryState( "group", parseAsArrayOf(parseAsString, ",") .withDefault(initialGrouping) .withOptions({ clearOnDefault: true, shallow, }) ) // TanStack Table의 `table.setGrouping()`과 동기화 // (정렬 모달 예시에서 setSorting()을 쓰듯이 여기서는 setGrouping() 호출) React.useEffect(() => { table.setGrouping(grouping) }, [grouping, table]) // 이미 중복 추가된 그룹은 제거 // (정렬 예시에서도 uniqueSorting 했듯이) const uniqueGrouping = React.useMemo( () => grouping.filter((id, i, self) => self.indexOf(id) === i), [grouping] ) // ------------------------------------------------------ // 2) 그룹핑 가능한 컬럼만 골라내기 // ------------------------------------------------------ const groupableColumns = React.useMemo( () => table .getAllColumns() .flatMap((column) => { if (column.columns && column.columns.length > 0) { return column.columns.filter(c => c.getCanSort()); } return column.getCanSort() ? [column] : []; }) .filter((col) => col.getCanGroup?.() !== false) .map((col) => ({ id: col.id, label: toSentenceCase(col.id), })), [table] ) // 이미 그룹핑 중인 컬럼 제외하고 "추가 가능"한 컬럼들 const ungroupedColumns = React.useMemo(() => { return groupableColumns.filter( (column) => !grouping.includes(column.id) ) }, [groupableColumns, grouping]) // ------------------------------------------------------ // 3) 그룹 배열을 업데이트하는 함수들 // ------------------------------------------------------ // 드래그/드롭으로 순서 변경 function onGroupOrderChange(newGroups: string[]) { setGrouping(newGroups) } // "Add group" : 아직 그룹핑되지 않은 첫 번째 컬럼 추가 function addGroup() { const firstAvailable = ungroupedColumns[0] if (!firstAvailable) return setGrouping([...grouping, firstAvailable.id]) } // 특정 아이템(그룹 컬럼 id) 제거 function removeGroup(id: string) { setGrouping((prev) => prev.filter((g) => g !== id)) } // 전체 그룹핑 초기화 function resetGrouping() { setGrouping([]) } // ------------------------------------------------------ // 4) 렌더링 // ------------------------------------------------------ return ( ({ id }))} onValueChange={(items) => { // 드래그 완료 시 string[] 형태로 되돌림 onGroupOrderChange(items.map((i) => i.id)) }} // overlay : 드래그 중 placeholder UI overlay={
} > 0 ? "gap-3.5" : "gap-2" )} > {uniqueGrouping.length > 0 ? ( <>

Group by

그룹핑은 불러온 데이터에 한해서 그룹핑이 됩니다.

) : (

No grouping applied

Add grouping to organize your results.

)} {/* 그룹 목록 */}
{uniqueGrouping.map((colId) => { // SortableItem에 key로 colId return (
No columns found. {ungroupedColumns.map((column) => ( { // colId -> 새로 선택한 value로 교체 setGrouping((prev) => prev.map((g) => g === colId ? value : g ) ) }} > {column.label} ))} {/* remove group */} {/* drag handle */}
) })}
{/* 새 그룹 추가 */} {grouping.length > 0 && ( )}
) }