From 748bb1720fd81e97a84c3e92f89d606e976b52e3 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Wed, 28 May 2025 00:18:16 +0000 Subject: (대표님) 컴포넌트 파트 커밋 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/form-data/sedp-compare-dialog.tsx | 317 ++++++++++++++++----------- 1 file changed, 195 insertions(+), 122 deletions(-) (limited to 'components/form-data/sedp-compare-dialog.tsx') diff --git a/components/form-data/sedp-compare-dialog.tsx b/components/form-data/sedp-compare-dialog.tsx index 37fe18ed..3107193a 100644 --- a/components/form-data/sedp-compare-dialog.tsx +++ b/components/form-data/sedp-compare-dialog.tsx @@ -3,12 +3,14 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from " import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { Loader, RefreshCw, AlertCircle, CheckCircle, Info, EyeOff } from "lucide-react"; +import { Input } from "@/components/ui/input"; +import { Loader, RefreshCw, AlertCircle, CheckCircle, Info, EyeOff, ChevronDown, ChevronRight, Search } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { toast } from "sonner"; import { DataTableColumnJSON } from "./form-data-table-columns"; import { ExcelDownload } from "./sedp-excel-download"; import { Switch } from "../ui/switch"; +import { Card, CardContent } from "@/components/ui/card"; interface SEDPCompareDialogProps { isOpen: boolean; @@ -37,7 +39,7 @@ interface ComparisonResult { // Component for formatting display value with UOM const DisplayValue = ({ value, uom, isSedp = false }: { value: any; uom?: string; isSedp?: boolean }) => { if (value === "" || value === null || value === undefined) { - return (empty); + return (empty); } // SEDP 값은 UOM을 표시하지 않음 (이미 포함되어 있다고 가정) @@ -54,7 +56,7 @@ const DisplayValue = ({ value, uom, isSedp = false }: { value: any; uom?: string ); }; -// 범례 컴포넌트 추가 +// 범례 컴포넌트 const ColorLegend = () => { return (
@@ -76,6 +78,64 @@ const ColorLegend = () => { ); }; +// 확장 가능한 차이점 표시 컴포넌트 +const DifferencesCard = ({ + attributes, + columnLabelMap, + showOnlyDifferences +}: { + attributes: ComparisonResult['attributes']; + columnLabelMap: Record; + showOnlyDifferences: boolean; +}) => { + const attributesToShow = showOnlyDifferences + ? attributes.filter(attr => !attr.isMatching) + : attributes; + + if (attributesToShow.length === 0) { + return ( +
+ 모든 속성이 일치합니다 +
+ ); + } + + return ( +
+ {attributesToShow.map((attr) => ( + + +
+ {attr.label} + {attr.uom && ({attr.uom})} +
+ {attr.isMatching ? ( +
+ +
+ ) : ( +
+
+ 로컬: + + + +
+
+ SEDP: + + + +
+
+ )} +
+
+ ))} +
+ ); +}; + export function SEDPCompareDialog({ isOpen, onClose, @@ -92,11 +152,10 @@ export function SEDPCompareDialog({ const [missingTags, setMissingTags] = React.useState<{ localOnly: { tagNo: string; tagDesc: string }[]; sedpOnly: { tagNo: string; tagDesc: string }[]; - }>( - { localOnly: [], sedpOnly: [] } - ); - // 추가: 차이점만 표시하는 옵션 + }>({ localOnly: [], sedpOnly: [] }); const [showOnlyDifferences, setShowOnlyDifferences] = React.useState(true); + const [searchTerm, setSearchTerm] = React.useState(""); + const [expandedRows, setExpandedRows] = React.useState>(new Set()); // Stats for summary const totalTags = comparisonResults.length; @@ -119,41 +178,56 @@ export function SEDPCompareDialog({ return { columnLabelMap: labelMap, columnUomMap: uomMap }; }, [columnsJSON]); - // Filter results based on active tab + // Filter and search results const filteredResults = React.useMemo(() => { + let results = comparisonResults; + + // Filter by tab switch (activeTab) { case "matching": - return comparisonResults.filter(r => r.isMatching); + results = results.filter(r => r.isMatching); + break; case "differences": - return comparisonResults.filter(r => !r.isMatching); + results = results.filter(r => !r.isMatching); + break; case "all": default: - return comparisonResults; + break; + } + + // Apply search filter + if (searchTerm.trim()) { + const search = searchTerm.toLowerCase(); + results = results.filter(r => + r.tagNo.toLowerCase().includes(search) || + r.tagDesc.toLowerCase().includes(search) + ); } - }, [comparisonResults, activeTab]); - // 변경: 표시할 컬럼 결정 (차이가 있는 컬럼만 or 모든 컬럼) - const columnsToDisplay = React.useMemo(() => { - // 기본 컬럼 (TAG_NO, TAG_DESC 제외) - const columns = columnsJSON.filter(col => col.key !== "TAG_NO" && col.key !== "TAG_DESC"); + return results; + }, [comparisonResults, activeTab, searchTerm]); - if (!showOnlyDifferences) { - return columns; // 모든 컬럼 표시 + // Toggle row expansion + const toggleRowExpansion = (tagNo: string) => { + const newExpanded = new Set(expandedRows); + if (newExpanded.has(tagNo)) { + newExpanded.delete(tagNo); + } else { + newExpanded.add(tagNo); } + setExpandedRows(newExpanded); + }; - // 하나라도 차이가 있는 속성만 필터링 - const columnsWithDifferences = new Set(); - comparisonResults.forEach(result => { - result.attributes.forEach(attr => { - if (!attr.isMatching) { - columnsWithDifferences.add(attr.key); - } + // Auto-expand rows with differences when switching to differences tab + React.useEffect(() => { + if (activeTab === "differences") { + const newExpanded = new Set(); + filteredResults.filter(r => !r.isMatching).forEach(r => { + newExpanded.add(r.tagNo); }); - }); - - // 차이가 있는 컬럼만 반환 - return columns.filter(col => columnsWithDifferences.has(col.key)); - }, [columnsJSON, comparisonResults, showOnlyDifferences]); + setExpandedRows(newExpanded); + } + }, [activeTab, filteredResults]); const fetchAndCompareData = React.useCallback(async () => { if (!projectCode || !formCode) { @@ -294,20 +368,34 @@ export function SEDPCompareDialog({ return ( - + SEDP 데이터 비교
-
- - +
+
+ + +
+ + {/* 검색 입력 */} +
+ + setSearchTerm(e.target.value)} + className="pl-8 w-64" + /> +
+
{matchingTags} / {totalTags} 일치 {totalMissingTags > 0 ? `(${totalMissingTags} 누락)` : ''} @@ -329,7 +417,7 @@ export function SEDPCompareDialog({
- {/* 범례 추가 */} + {/* 범례 */}
@@ -357,7 +445,7 @@ export function SEDPCompareDialog({

로컬에만 있는 태그 ({missingTags.localOnly.length})

- + Tag Number Tag Description @@ -379,7 +467,7 @@ export function SEDPCompareDialog({

SEDP에만 있는 태그 ({missingTags.sedpOnly.length})

- + Tag Number Tag Description @@ -404,100 +492,85 @@ export function SEDPCompareDialog({ )} ) : filteredResults.length > 0 ? ( - // 개선된 테이블 구조 -
+ // 개선된 확장 가능한 테이블 구조 +
- + - Tag Number - Tag Description - 상태 - - {/* 동적으로 속성 열 헤더 생성 */} - {columnsToDisplay.length > 0 ? ( - columnsToDisplay.map(col => ( - - {columnLabelMap[col.key] || col.key} - {columnUomMap[col.key] && ( - - ({columnUomMap[col.key]}) - - )} - - )) - ) : ( - -
- - 차이가 있는 항목이 없습니다 -
-
- )} + + Tag Number + Tag Description + 상태 + 차이점 개수
{filteredResults.map((result) => ( - - - {result.tagNo} - - - {result.tagDesc} - - - {result.isMatching ? ( - - - 일치 - - ) : ( - - - 차이 있음 - - )} - - - {/* 각 속성에 대한 셀 동적 생성 */} - {columnsToDisplay.length > 0 ? ( - columnsToDisplay.map(col => { - const attr = result.attributes.find(a => a.key === col.key); - - if (!attr) return -; - - return ( - - {attr.isMatching ? ( - - ) : ( -
-
- -
-
- -
-
- )} -
- ); - }) - ) : ( + + {/* 메인 행 */} + toggleRowExpansion(result.tagNo)} + > - 모든 값이 일치합니다 + {result.attributes.some(attr => !attr.isMatching) ? ( + expandedRows.has(result.tagNo) ? ( + + ) : ( + + ) + ) : null} + + + {result.tagNo} + +
+ {result.tagDesc} +
+
+ + {result.isMatching ? ( + + + 일치 + + ) : ( + + + 차이 + + )} + + + {!result.isMatching && ( + + {result.attributes.filter(attr => !attr.isMatching).length}개 속성이 다름 + + )} + +
+ + {/* 확장된 차이점 표시 행 */} + {expandedRows.has(result.tagNo) && ( + + + + + )} -
+ ))}
) : (
- 현재 필터에 맞는 태그가 없습니다 + {searchTerm ? "검색 결과가 없습니다" : "현재 필터에 맞는 태그가 없습니다"}
)} -- cgit v1.2.3