import * as React from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; 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 } 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"; interface SEDPCompareDialogProps { isOpen: boolean; onClose: () => void; tableData: any[]; columnsJSON: DataTableColumnJSON[]; projectCode: string; formCode: string; fetchTagDataFromSEDP: (projectCode: string, formCode: string) => Promise; } interface ComparisonResult { tagNo: string; tagDesc: string; isMatching: boolean; attributes: { key: string; label: string; localValue: any; sedpValue: any; isMatching: boolean; uom?: string; }[]; } // 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); } // SEDP 값은 UOM을 표시하지 않음 (이미 포함되어 있다고 가정) if (isSedp) { return {value}; } // 로컬 값은 UOM과 함께 표시 return ( {value} {uom && {uom}} ); }; // 범례 컴포넌트 추가 const ColorLegend = () => { return (
범례:
로컬 값
SEDP 값
); }; export function SEDPCompareDialog({ isOpen, onClose, tableData, columnsJSON, projectCode, formCode, fetchTagDataFromSEDP, }: SEDPCompareDialogProps) { const [isLoading, setIsLoading] = React.useState(false); const [comparisonResults, setComparisonResults] = React.useState([]); const [activeTab, setActiveTab] = React.useState("all"); const [isExporting, setIsExporting] = React.useState(false); // Stats for summary const totalTags = comparisonResults.length; const matchingTags = comparisonResults.filter(r => r.isMatching).length; const nonMatchingTags = totalTags - matchingTags; // Get column label map and UOM map for better display const { columnLabelMap, columnUomMap } = React.useMemo(() => { const labelMap: Record = {}; const uomMap: Record = {}; columnsJSON.forEach(col => { labelMap[col.key] = col.displayLabel || col.label; if (col.uom) { uomMap[col.key] = col.uom; } }); return { columnLabelMap: labelMap, columnUomMap: uomMap }; }, [columnsJSON]); const fetchAndCompareData = React.useCallback(async () => { if (!projectCode || !formCode) { toast.error("Project code or form code is missing"); return; } try { setIsLoading(true); // Fetch data from SEDP API const sedpData = await fetchTagDataFromSEDP(projectCode, formCode); // Get the table name from the response const tableName = Object.keys(sedpData)[0]; const sedpTagEntries = sedpData[tableName] || []; // Create a map of SEDP data by TAG_NO for quick lookup const sedpTagMap = new Map(); sedpTagEntries.forEach((entry: any) => { const tagNo = entry.TAG_NO; const attributesMap = new Map(); // Convert attributes array to map for easier access if (Array.isArray(entry.ATTRIBUTES)) { entry.ATTRIBUTES.forEach((attr: any) => { attributesMap.set(attr.ATT_ID, attr.VALUE); }); } sedpTagMap.set(tagNo, { tagDesc: entry.TAG_DESC, attributes: attributesMap }); }); // Compare with local table data const results: ComparisonResult[] = tableData.map(localItem => { const tagNo = localItem.TAG_NO; const sedpItem = sedpTagMap.get(tagNo); // If tag not found in SEDP data if (!sedpItem) { return { tagNo, tagDesc: localItem.TAG_DESC || "", isMatching: false, attributes: columnsJSON .filter(col => col.key !== "TAG_NO" && col.key !== "TAG_DESC") .map(col => ({ key: col.key, label: columnLabelMap[col.key] || col.key, localValue: localItem[col.key], sedpValue: null, isMatching: false, uom: columnUomMap[col.key] })) }; } // Compare attributes const attributeComparisons = columnsJSON .filter(col => col.key !== "TAG_NO" && col.key !== "TAG_DESC") .map(col => { const localValue = localItem[col.key]; const sedpValue = sedpItem.attributes.get(col.key); const uom = columnUomMap[col.key]; // Compare values (with type handling) let isMatching = false; // 문자열 비교 // Normalize empty values const normalizedLocal = localValue === undefined || localValue === null ? "" : String(localValue).trim(); const normalizedSedp = sedpValue === undefined || sedpValue === null ? "" : String(sedpValue).trim(); isMatching = normalizedLocal === normalizedSedp; return { key: col.key, label: columnLabelMap[col.key] || col.key, localValue, sedpValue, isMatching, uom }; }); // Item is matching if all attributes match const isItemMatching = attributeComparisons.every(attr => attr.isMatching); return { tagNo, tagDesc: localItem.TAG_DESC || "", isMatching: isItemMatching, attributes: attributeComparisons }; }); setComparisonResults(results); // Show summary in toast const matchCount = results.filter(r => r.isMatching).length; const nonMatchCount = results.length - matchCount; if (nonMatchCount > 0) { toast.warning(`Found ${nonMatchCount} tags with differences`); } else if (results.length > 0) { toast.success(`All ${results.length} tags match with SEDP data`); } else { toast.info("No tags to compare"); } } catch (error) { console.error("SEDP comparison error:", error); toast.error(`Failed to compare with SEDP: ${error instanceof Error ? error.message : 'Unknown error'}`); } finally { setIsLoading(false); } }, [projectCode, formCode, tableData, columnsJSON, fetchTagDataFromSEDP, columnLabelMap, columnUomMap]); // Fetch data when dialog opens React.useEffect(() => { if (isOpen) { fetchAndCompareData(); } }, [isOpen, fetchAndCompareData]); // Filter results based on active tab const filteredResults = React.useMemo(() => { switch (activeTab) { case "matching": return comparisonResults.filter(r => r.isMatching); case "differences": return comparisonResults.filter(r => !r.isMatching); case "all": default: return comparisonResults; } }, [comparisonResults, activeTab]); return ( SEDP 데이터 비교
{matchingTags} / {totalTags} 일치
{/* 범례 추가 */}
전체 태그 ({totalTags}) 차이 있음 ({nonMatchingTags}) 일치함 ({matchingTags}) {isLoading ? (
데이터 비교 중...
) : filteredResults.length > 0 ? ( Tag Number Tag Description 상태 차이점 {filteredResults.map((result) => { // Find differences to display const differences = result.attributes.filter(attr => !attr.isMatching); return ( {result.tagNo} {result.tagDesc} {result.isMatching ? ( 일치 ) : ( 차이 있음 )} {differences.length > 0 ? (
{differences.map((diff) => (
{diff.label}:
))}
) : ( 모든 값이 일치합니다 )}
); })}
) : (
현재 필터에 맞는 태그가 없습니다
)}
); }