From 6d654b1ba2c19e0bf1745b636908e3b00a0f02c7 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Wed, 9 Jul 2025 12:19:05 +0000
Subject: (대표님) 20250709 변경사항 (약 18시 30분까지)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/form-data/form-data-table-columns.tsx | 218 +++++++++++++++--------
 1 file changed, 148 insertions(+), 70 deletions(-)

(limited to 'components/form-data/form-data-table-columns.tsx')

diff --git a/components/form-data/form-data-table-columns.tsx b/components/form-data/form-data-table-columns.tsx
index 3749fe02..930e113b 100644
--- a/components/form-data/form-data-table-columns.tsx
+++ b/components/form-data/form-data-table-columns.tsx
@@ -42,6 +42,11 @@ export interface DataTableColumnJSON {
   uom?: string;
   uomId?: string;
   shi?: boolean;
+  
+  /** 템플릿에서 가져온 추가 정보 */
+  hidden?: boolean;  // true이면 컬럼 숨김
+  seq?: number;      // 정렬 순서
+  head?: string;     // 헤더 텍스트 (우선순위 가장 높음)
 }
 
 /**
@@ -87,79 +92,70 @@ function getStatusBadgeVariant(status: string): "default" | "secondary" | "destr
 }
 
 /**
- * getColumns 함수
- * 1) columnsJSON 배열을 순회하면서 accessorKey / header / cell 등을 설정
- * 2) 체크박스 컬럼 추가 (showBatchSelection이 true일 때)
- * 3) 마지막에 "Action" 칼럼(예: update 버튼) 추가
+ * 헤더 텍스트를 결정하는 헬퍼 함수
+ * displayLabel이 있으면 사용, 없으면 label 사용
  */
-export function getColumns<TData extends object>({
-  columnsJSON,
-  setRowAction,
-  setReportData,
-  tempCount,
-  selectedRows = {},
-  onRowSelectionChange,
-  // editableFieldsMap 매개변수 제거됨
-}: GetColumnsProps<TData>): ColumnDef<TData>[] {
-  const columns: ColumnDef<TData>[] = [];
+function getHeaderText(col: DataTableColumnJSON): string {
+  if (col.displayLabel && col.displayLabel.trim()) {
+    return col.displayLabel;
+  }
+  return col.label;
+}
 
-  // (1) 체크박스 컬럼 (항상 표시)
-  const selectColumn: ColumnDef<TData> = {
-    id: "select",
-    header: ({ table }) => (
-      <Checkbox
-        checked={
-          table.getIsAllPageRowsSelected() ||
-          (table.getIsSomePageRowsSelected() && "indeterminate")
-        }
-        onCheckedChange={(value) => {
-          table.toggleAllPageRowsSelected(!!value);
-          
-          // 모든 행 선택/해제
-          if (onRowSelectionChange) {
-            const allRowsSelection: Record<string, boolean> = {};
-            table.getRowModel().rows.forEach((row) => {
-              allRowsSelection[row.id] = !!value;
-            });
-            onRowSelectionChange(allRowsSelection);
-          }
-        }}
-        aria-label="Select all"
-        className="translate-y-[2px]"
-      />
-    ),
-    cell: ({ row }) => (
-      <Checkbox
-        checked={row.getIsSelected()}
-        onCheckedChange={(value) => {
-          row.toggleSelected(!!value);
-          
-          // 개별 행 선택 상태 업데이트
-          if (onRowSelectionChange) {
-            onRowSelectionChange(prev => ({
-              ...prev,
-              [row.id]: !!value
-            }));
-          }
-        }}
-        aria-label="Select row"
-        className="translate-y-[2px]"
-      />
-    ),
-    enableSorting: false,
-    enableHiding: false,
-    enablePinning: true,
-    size: 40,
-  };
-  columns.push(selectColumn);
+/**
+ * 컬럼들을 head 값에 따라 그룹핑하는 헬퍼 함수
+ */
+function groupColumnsByHead(columns: DataTableColumnJSON[]): ColumnDef<any>[] {
+  const groupedColumns: ColumnDef<any>[] = [];
+  const groupMap = new Map<string, DataTableColumnJSON[]>();
+  const ungroupedColumns: DataTableColumnJSON[] = [];
+
+  // head 값에 따라 컬럼들을 그룹핑
+  columns.forEach(col => {
+    if (col.head && col.head.trim()) {
+      const groupKey = col.head.trim();
+      if (!groupMap.has(groupKey)) {
+        groupMap.set(groupKey, []);
+      }
+      groupMap.get(groupKey)!.push(col);
+    } else {
+      ungroupedColumns.push(col);
+    }
+  });
+
+  // 그룹핑된 컬럼들 처리
+  groupMap.forEach((groupColumns, groupHeader) => {
+    if (groupColumns.length === 1) {
+      // 그룹에 컬럼이 하나만 있으면 일반 컬럼으로 처리
+      ungroupedColumns.push(groupColumns[0]);
+    } else {
+      // 그룹 컬럼 생성
+      const groupColumn: ColumnDef<any> = {
+        header: groupHeader,
+        columns: groupColumns.map(col => createColumnDef(col))
+      };
+      groupedColumns.push(groupColumn);
+    }
+  });
+
+  // 그룹핑되지 않은 컬럼들 처리
+  ungroupedColumns.forEach(col => {
+    groupedColumns.push(createColumnDef(col));
+  });
+
+  return groupedColumns;
+}
 
-  // (2) 기본 컬럼들
-  const baseColumns: ColumnDef<TData>[] = columnsJSON.map((col) => ({
+/**
+ * 개별 컬럼 정의를 생성하는 헬퍼 함수
+ */
+function createColumnDef(col: DataTableColumnJSON): ColumnDef<any> {
+  return {
     accessorKey: col.key,
     header: ({ column }) => (
       <ClientDataTableColumnHeaderSimple
         column={column}
-        title={col.displayLabel || col.label}
+        title={getHeaderText(col)}
       />
     ),
 
@@ -240,11 +236,93 @@ export function getColumns<TData extends object>({
           );
       }
     },
-  }));
+  };
+}
+
+/**
+ * getColumns 함수
+ * 1) columnsJSON 배열을 필터링 (hidden이 true가 아닌 것들만)
+ * 2) seq에 따라 정렬
+ * 3) head 값에 따라 컬럼 그룹핑
+ * 4) 체크박스 컬럼 추가
+ * 5) 마지막에 "Action" 칼럼 추가
+ */
+export function getColumns<TData extends object>({
+  columnsJSON,
+  setRowAction,
+  setReportData,
+  tempCount,
+  selectedRows = {},
+  onRowSelectionChange,
+  // editableFieldsMap 매개변수 제거됨
+}: GetColumnsProps<TData>): ColumnDef<TData>[] {
+  const columns: ColumnDef<TData>[] = [];
+
+  // (0) 컬럼 필터링 및 정렬
+  const visibleColumns = columnsJSON
+    .filter(col => col.hidden !== true) // hidden이 true가 아닌 것들만
+    .sort((a, b) => {
+      // seq가 없는 경우 999999로 처리하여 맨 뒤로 보냄
+      const seqA = a.seq !== undefined ? a.seq : 999999;
+      const seqB = b.seq !== undefined ? b.seq : 999999;
+      return seqA - seqB;
+    });
+
+  // (1) 체크박스 컬럼 (항상 표시)
+  const selectColumn: ColumnDef<TData> = {
+    id: "select",
+    header: ({ table }) => (
+      <Checkbox
+        checked={
+          table.getIsAllPageRowsSelected() ||
+          (table.getIsSomePageRowsSelected() && "indeterminate")
+        }
+        onCheckedChange={(value) => {
+          table.toggleAllPageRowsSelected(!!value);
+          
+          // 모든 행 선택/해제
+          if (onRowSelectionChange) {
+            const allRowsSelection: Record<string, boolean> = {};
+            table.getRowModel().rows.forEach((row) => {
+              allRowsSelection[row.id] = !!value;
+            });
+            onRowSelectionChange(allRowsSelection);
+          }
+        }}
+        aria-label="Select all"
+        className="translate-y-[2px]"
+      />
+    ),
+    cell: ({ row }) => (
+      <Checkbox
+        checked={row.getIsSelected()}
+        onCheckedChange={(value) => {
+          row.toggleSelected(!!value);
+          
+          // 개별 행 선택 상태 업데이트
+          if (onRowSelectionChange) {
+            onRowSelectionChange(prev => ({
+              ...prev,
+              [row.id]: !!value
+            }));
+          }
+        }}
+        aria-label="Select row"
+        className="translate-y-[2px]"
+      />
+    ),
+    enableSorting: false,
+    enableHiding: false,
+    enablePinning: true,
+    size: 40,
+  };
+  columns.push(selectColumn);
 
-  columns.push(...baseColumns);
+  // (2) 기본 컬럼들 (head에 따라 그룹핑 처리)
+  const groupedColumns = groupColumnsByHead(visibleColumns);
+  columns.push(...groupedColumns);
   
-  // (4) 액션 칼럼 - update 버튼 예시
+  // (3) 액션 칼럼 - update 버튼 예시
   const actionColumn: ColumnDef<TData> = {
     id: "update",
     header: "",
@@ -297,6 +375,6 @@ export function getColumns<TData extends object>({
 
   columns.push(actionColumn);
 
-  // (5) 최종 반환
+  // (4) 최종 반환
   return columns;
 }
\ No newline at end of file
-- 
cgit v1.2.3

