From b54f6f03150dd78d86db62201b6386bf14b72394 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Wed, 15 Oct 2025 12:52:11 +0000
Subject: (대표님) 커버, 데이터룸, 파일매니저, 담당자할당 등
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/form-data/form-data-table.tsx | 190 ++++++++++++++++++++++++-------
 1 file changed, 148 insertions(+), 42 deletions(-)

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

diff --git a/components/form-data/form-data-table.tsx b/components/form-data/form-data-table.tsx
index d5d79735..9dbcb627 100644
--- a/components/form-data/form-data-table.tsx
+++ b/components/form-data/form-data-table.tsx
@@ -19,7 +19,7 @@ import {
   Upload,
   Plus,
   Tag,
-  TagsIcon, 
+  TagsIcon,
   FileOutput,
   Clipboard,
   Send,
@@ -115,7 +115,97 @@ export default function DynamicTable({
   const [formStats, setFormStats] = React.useState<FormStatusByVendor | null>(null);
   const [isLoadingStats, setIsLoadingStats] = React.useState(true);
 
+  const [activeFilter, setActiveFilter] = React.useState<string | null>(null);
+  const [filteredTableData, setFilteredTableData] = React.useState<GenericData[]>(tableData);
 
+  // 필터링 로직
+  React.useEffect(() => {
+    if (!activeFilter) {
+      setFilteredTableData(tableData);
+      return;
+    }
+
+    const today = new Date();
+    today.setHours(0, 0, 0, 0);
+    const sevenDaysLater = new Date(today);
+    sevenDaysLater.setDate(sevenDaysLater.getDate() + 7);
+
+    let filtered = [...tableData];
+
+    switch (activeFilter) {
+      case 'completed':
+        // 모든 필수 필드가 완료된 태그만 표시
+        filtered = tableData.filter(item => {
+          const tagEditableFields = editableFieldsMap.get(item.TAG_NO) || [];
+          return columnsJSON
+            .filter(col => (col.shi === 'IN' || col.shi === 'BOTH') && tagEditableFields.includes(col.key))
+            .every(col => {
+              const value = item[col.key];
+              return value !== undefined && value !== null && value !== '';
+            });
+        });
+        break;
+
+      case 'remaining':
+        // 미완료 필드가 있는 태그만 표시
+        filtered = tableData.filter(item => {
+          const tagEditableFields = editableFieldsMap.get(item.TAG_NO) || [];
+          return columnsJSON
+            .filter(col => (col.shi === 'IN' || col.shi === 'BOTH') && tagEditableFields.includes(col.key))
+            .some(col => {
+              const value = item[col.key];
+              return value === undefined || value === null || value === '';
+            });
+        });
+        break;
+
+      case 'upcoming':
+        // 7일 이내 임박한 태그만 표시
+        filtered = tableData.filter(item => {
+          const dueDate = item.DUE_DATE;
+          if (!dueDate) return false;
+
+          const target = new Date(dueDate);
+          target.setHours(0, 0, 0, 0);
+
+          // 미완료이면서 7일 이내인 경우
+          const hasIncompleteFields = columnsJSON
+            .filter(col => col.shi === 'IN' || col.shi === 'BOTH')
+            .some(col => !item[col.key]);
+
+          return hasIncompleteFields && target >= today && target <= sevenDaysLater;
+        });
+        break;
+
+      case 'overdue':
+        // 지연된 태그만 표시
+        filtered = tableData.filter(item => {
+          const dueDate = item.DUE_DATE;
+          if (!dueDate) return false;
+
+          const target = new Date(dueDate);
+          target.setHours(0, 0, 0, 0);
+
+          // 미완료이면서 지연된 경우
+          const hasIncompleteFields = columnsJSON
+            .filter(col => col.shi === 'IN' || col.shi === 'BOTH')
+            .some(col => !item[col.key]);
+
+          return hasIncompleteFields && target < today;
+        });
+        break;
+
+      default:
+        filtered = tableData;
+    }
+
+    setFilteredTableData(filtered);
+  }, [activeFilter, tableData, columnsJSON, editableFieldsMap]);
+
+  // 카드 클릭 핸들러
+  const handleCardClick = (filterType: string | null) => {
+    setActiveFilter(prev => prev === filterType ? null : filterType);
+  };
 
   React.useEffect(() => {
     const fetchFormStats = async () => {
@@ -310,7 +400,7 @@ export default function DynamicTable({
         isArray: Array.isArray(templateResult),
         data: templateResult
       });
-      
+
       if (Array.isArray(templateResult)) {
         templateResult.forEach((tmpl, idx) => {
           console.log(`  [${idx}] TMPL_ID: ${tmpl?.TMPL_ID || 'MISSING'}, NAME: ${tmpl?.NAME || 'N/A'}, TYPE: ${tmpl?.TMPL_TYPE || 'N/A'}`);
@@ -687,11 +777,15 @@ export default function DynamicTable({
 
   return (
     <>
-  
+
       <div className="mb-6">
         <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-6">
-          {/* Tag Count */}
-          <Card>
+          {/* Total Tags Card - 클릭 시 전체 보기 */}
+          <Card
+            className={`cursor-pointer transition-all ${activeFilter === null ? 'ring-2 ring-primary' : 'hover:shadow-lg'
+              }`}
+            onClick={() => handleCardClick(null)}
+          >
             <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
               <CardTitle className="text-sm font-medium">
                 Total Tags
@@ -707,35 +801,17 @@ export default function DynamicTable({
                 )}
               </div>
               <p className="text-xs text-muted-foreground">
-                Total Tag Count
+                {activeFilter === null ? 'Showing all' : 'Click to show all'}
               </p>
             </CardContent>
           </Card>
 
-          {/* Completion Rate */}
-          <Card>
-            <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
-              <CardTitle className="text-sm font-medium">
-                Completion
-              </CardTitle>
-              <Target className="h-4 w-4 text-muted-foreground" />
-            </CardHeader>
-            <CardContent>
-              <div className="text-2xl font-bold">
-                {isLoadingStats ? (
-                  <span className="animate-pulse">-</span>
-                ) : (
-                  `${formStats?.completionRate || 0}%`
-                )}
-              </div>
-              <p className="text-xs text-muted-foreground">
-                {formStats ? `${formStats.completedFields} / ${formStats.totalFields}` : '-'}
-              </p>
-            </CardContent>
-          </Card>
-
-          {/* Completed Fields */}
-          <Card>
+          {/* Completed Fields Card */}
+          <Card
+            className={`cursor-pointer transition-all ${activeFilter === 'completed' ? 'ring-2 ring-green-600' : 'hover:shadow-lg'
+              }`}
+            onClick={() => handleCardClick('completed')}
+          >
             <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
               <CardTitle className="text-sm font-medium">
                 Completed
@@ -751,13 +827,17 @@ export default function DynamicTable({
                 )}
               </div>
               <p className="text-xs text-muted-foreground">
-                Completed Fields
+                {activeFilter === 'completed' ? 'Filtering active' : 'Click to filter'}
               </p>
             </CardContent>
           </Card>
 
-          {/* Remaining Fields */}
-          <Card>
+          {/* Remaining Fields Card */}
+          <Card
+            className={`cursor-pointer transition-all ${activeFilter === 'remaining' ? 'ring-2 ring-blue-600' : 'hover:shadow-lg'
+              }`}
+            onClick={() => handleCardClick('remaining')}
+          >
             <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
               <CardTitle className="text-sm font-medium">
                 Remaining
@@ -773,13 +853,17 @@ export default function DynamicTable({
                 )}
               </div>
               <p className="text-xs text-muted-foreground">
-                Remaining Fields
+                {activeFilter === 'remaining' ? 'Filtering active' : 'Click to filter'}
               </p>
             </CardContent>
           </Card>
 
-          {/* Upcoming (7 days) */}
-          <Card>
+          {/* Upcoming Card */}
+          <Card
+            className={`cursor-pointer transition-all ${activeFilter === 'upcoming' ? 'ring-2 ring-yellow-600' : 'hover:shadow-lg'
+              }`}
+            onClick={() => handleCardClick('upcoming')}
+          >
             <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
               <CardTitle className="text-sm font-medium">
                 Upcoming
@@ -795,13 +879,17 @@ export default function DynamicTable({
                 )}
               </div>
               <p className="text-xs text-muted-foreground">
-                Due in 7 Days
+                {activeFilter === 'upcoming' ? 'Filtering active' : 'Click to filter'}
               </p>
             </CardContent>
           </Card>
 
-          {/* Overdue */}
-          <Card>
+          {/* Overdue Card */}
+          <Card
+            className={`cursor-pointer transition-all ${activeFilter === 'overdue' ? 'ring-2 ring-red-600' : 'hover:shadow-lg'
+              }`}
+            onClick={() => handleCardClick('overdue')}
+          >
             <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
               <CardTitle className="text-sm font-medium">
                 Overdue
@@ -817,22 +905,40 @@ export default function DynamicTable({
                 )}
               </div>
               <p className="text-xs text-muted-foreground">
-                Overdue
+                {activeFilter === 'overdue' ? 'Filtering active' : 'Click to filter'}
               </p>
             </CardContent>
           </Card>
         </div>
       </div>
-   
+
 
       <ClientDataTable
-        data={tableData}
+        data={filteredTableData}  // tableData 대신 filteredTableData 사용
         columns={columns}
         advancedFilterFields={advancedFilterFields}
         autoSizeColumns
         onSelectedRowsChange={setSelectedRowsData}
         clearSelection={clearSelection}
       >
+        {/* 필터 상태 표시 */}
+        {activeFilter && (
+          <div className="flex items-center gap-2 mr-auto">
+            <span className="text-sm text-muted-foreground">
+              Filter: {activeFilter === 'completed' ? 'Completed' :
+                activeFilter === 'remaining' ? 'Remaining' :
+                  activeFilter === 'upcoming' ? 'Upcoming (7 days)' :
+                    activeFilter === 'overdue' ? 'Overdue' : 'All'}
+            </span>
+            <Button
+              variant="ghost"
+              size="sm"
+              onClick={() => setActiveFilter(null)}
+            >
+              Clear filter
+            </Button>
+          </div>
+        )}
         {/* 선택된 항목 수 표시 (선택된 항목이 있을 때만) */}
         {selectedRowCount > 0 && (
           <Button
-- 
cgit v1.2.3

