diff options
Diffstat (limited to 'components/form-data/sedp-compare-dialog.tsx')
| -rw-r--r-- | components/form-data/sedp-compare-dialog.tsx | 233 |
1 files changed, 121 insertions, 112 deletions
diff --git a/components/form-data/sedp-compare-dialog.tsx b/components/form-data/sedp-compare-dialog.tsx index 647f2810..1a9938bd 100644 --- a/components/form-data/sedp-compare-dialog.tsx +++ b/components/form-data/sedp-compare-dialog.tsx @@ -11,6 +11,8 @@ 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"; +import { useTranslation } from "@/i18n/client" +import { useParams } from "next/navigation" interface SEDPCompareDialogProps { isOpen: boolean; @@ -56,85 +58,6 @@ const DisplayValue = ({ value, uom, isSedp = false }: { value: any; uom?: string ); }; -// 범례 컴포넌트 -const ColorLegend = () => { - return ( - <div className="flex items-center gap-4 text-sm p-2 bg-muted/20 rounded"> - <div className="flex items-center gap-1.5"> - <Info className="h-4 w-4 text-muted-foreground" /> - <span className="font-medium">범례:</span> - </div> - <div className="flex items-center gap-3"> - <div className="flex items-center gap-1.5"> - <div className="h-3 w-3 rounded-full bg-red-500"></div> - <span className="line-through text-red-500">로컬 값</span> - </div> - <div className="flex items-center gap-1.5"> - <div className="h-3 w-3 rounded-full bg-green-500"></div> - <span className="text-green-500">SEDP 값</span> - </div> - </div> - </div> - ); -}; - -// 확장 가능한 차이점 표시 컴포넌트 -const DifferencesCard = ({ - attributes, - columnLabelMap, - showOnlyDifferences -}: { - attributes: ComparisonResult['attributes']; - columnLabelMap: Record<string, string>; - showOnlyDifferences: boolean; -}) => { - const attributesToShow = showOnlyDifferences - ? attributes.filter(attr => !attr.isMatching) - : attributes; - - if (attributesToShow.length === 0) { - return ( - <div className="text-center text-muted-foreground py-4"> - 모든 속성이 일치합니다 - </div> - ); - } - - return ( - <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3 p-4"> - {attributesToShow.map((attr) => ( - <Card key={attr.key} className={`${!attr.isMatching ? 'border-red-200' : 'border-green-200'}`}> - <CardContent className="p-3"> - <div className="font-medium text-sm mb-2 truncate" title={attr.label}> - {attr.label} - {attr.uom && <span className="text-xs text-muted-foreground ml-1">({attr.uom})</span>} - </div> - {attr.isMatching ? ( - <div className="text-sm"> - <DisplayValue value={attr.localValue} uom={attr.uom} /> - </div> - ) : ( - <div className="space-y-2"> - <div className="flex items-center gap-2 text-sm"> - <span className="text-xs text-muted-foreground min-w-[35px]">로컬:</span> - <span className="line-through text-red-500 flex-1"> - <DisplayValue value={attr.localValue} uom={attr.uom} isSedp={false} /> - </span> - </div> - <div className="flex items-center gap-2 text-sm"> - <span className="text-xs text-muted-foreground min-w-[35px]">SEDP:</span> - <span className="text-green-500 flex-1"> - <DisplayValue value={attr.sedpValue} uom={attr.uom} isSedp={true} /> - </span> - </div> - </div> - )} - </CardContent> - </Card> - ))} - </div> - ); -}; export function SEDPCompareDialog({ isOpen, @@ -145,6 +68,92 @@ export function SEDPCompareDialog({ formCode, fetchTagDataFromSEDP, }: SEDPCompareDialogProps) { + + const params = useParams() || {} + const lng = params.lng ? String(params.lng) : "ko" + const { t } = useTranslation(lng, "engineering") + + // 범례 컴포넌트 + const ColorLegend = () => { + return ( + <div className="flex items-center gap-4 text-sm p-2 bg-muted/20 rounded"> + <div className="flex items-center gap-1.5"> + <Info className="h-4 w-4 text-muted-foreground" /> + <span className="font-medium">{t("labels.legend")}:</span> + </div> + <div className="flex items-center gap-3"> + <div className="flex items-center gap-1.5"> + <div className="h-3 w-3 rounded-full bg-red-500"></div> + <span className="line-through text-red-500">{t("labels.localValue")}</span> + </div> + <div className="flex items-center gap-1.5"> + <div className="h-3 w-3 rounded-full bg-green-500"></div> + <span className="text-green-500">{t("labels.sedpValue")}</span> + </div> + </div> + </div> + ); + }; + + // 확장 가능한 차이점 표시 컴포넌트 + const DifferencesCard = ({ + attributes, + columnLabelMap, + showOnlyDifferences + }: { + attributes: ComparisonResult['attributes']; + columnLabelMap: Record<string, string>; + showOnlyDifferences: boolean; + }) => { + const attributesToShow = showOnlyDifferences + ? attributes.filter(attr => !attr.isMatching) + : attributes; + + if (attributesToShow.length === 0) { + return ( + <div className="text-center text-muted-foreground py-4"> + {t("messages.allAttributesMatch")} + </div> + ); + } + + return ( + <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3 p-4"> + {attributesToShow.map((attr) => ( + <Card key={attr.key} className={`${!attr.isMatching ? 'border-red-200' : 'border-green-200'}`}> + <CardContent className="p-3"> + <div className="font-medium text-sm mb-2 truncate" title={attr.label}> + {attr.label} + {attr.uom && <span className="text-xs text-muted-foreground ml-1">({attr.uom})</span>} + </div> + {attr.isMatching ? ( + <div className="text-sm"> + <DisplayValue value={attr.localValue} uom={attr.uom} /> + </div> + ) : ( + <div className="space-y-2"> + <div className="flex items-center gap-2 text-sm"> + <span className="text-xs text-muted-foreground min-w-[35px]">로컬:</span> + <span className="line-through text-red-500 flex-1"> + <DisplayValue value={attr.localValue} uom={attr.uom} isSedp={false} /> + </span> + </div> + <div className="flex items-center gap-2 text-sm"> + <span className="text-xs text-muted-foreground min-w-[35px]">SEDP:</span> + <span className="text-green-500 flex-1"> + <DisplayValue value={attr.sedpValue} uom={attr.uom} isSedp={true} /> + </span> + </div> + </div> + )} + </CardContent> + </Card> + ))} + </div> + ); + }; + + const [isLoading, setIsLoading] = React.useState(false); const [comparisonResults, setComparisonResults] = React.useState<ComparisonResult[]>([]); const [activeTab, setActiveTab] = React.useState("all"); @@ -181,7 +190,7 @@ export function SEDPCompareDialog({ // Filter and search results const filteredResults = React.useMemo(() => { let results = comparisonResults; - + // Filter by tab switch (activeTab) { case "matching": @@ -198,8 +207,8 @@ export function SEDPCompareDialog({ // Apply search filter if (searchTerm.trim()) { const search = searchTerm.toLowerCase(); - results = results.filter(r => - r.tagNo.toLowerCase().includes(search) || + results = results.filter(r => + r.tagNo.toLowerCase().includes(search) || r.tagDesc.toLowerCase().includes(search) ); } @@ -291,7 +300,7 @@ export function SEDPCompareDialog({ // Compare attributes const attributeComparisons = columnsJSON - .filter(col => col.key !== "TAG_NO" && col.key !== "TAG_DESC"&& col.key !== "status") + .filter(col => col.key !== "TAG_NO" && col.key !== "TAG_DESC" && col.key !== "status") .map(col => { const localValue = localItem[col.key]; const sedpValue = sedpItem.attributes.get(col.key); @@ -370,7 +379,7 @@ export function SEDPCompareDialog({ <Dialog open={isOpen} onOpenChange={onClose}> <DialogContent className="max-w-6xl max-h-[90vh] overflow-hidden flex flex-col"> <DialogHeader> - <DialogTitle className="mb-2">SEDP 데이터 비교</DialogTitle> + <DialogTitle className="mb-2">{t("dialogs.sedpDataComparison")}</DialogTitle> <div className="flex items-center justify-between gap-2 pr-8"> <div className="flex items-center gap-4"> <div className="flex items-center gap-2"> @@ -380,22 +389,22 @@ export function SEDPCompareDialog({ id="show-differences" /> <label htmlFor="show-differences" className="text-sm cursor-pointer"> - 차이가 있는 항목만 표시 + {t("switches.showOnlyDifferences")} </label> </div> - + {/* 검색 입력 */} <div className="relative"> <Search className="absolute left-2 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" /> <Input - placeholder="태그 번호 또는 설명 검색..." + placeholder={t("placeholders.searchTagOrDesc")} value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} className="pl-8 w-64" /> </div> </div> - + <div className="flex items-center gap-2"> <Badge variant={matchingTags === totalTags && totalMissingTags === 0 ? "default" : "destructive"}> {matchingTags} / {totalTags} 일치 {totalMissingTags > 0 ? `(${totalMissingTags} 누락)` : ''} @@ -411,7 +420,7 @@ export function SEDPCompareDialog({ ) : ( <RefreshCw className="h-4 w-4" /> )} - <span className="ml-2">새로고침</span> + <span className="ml-2">{t("buttons.refresh")}</span> </Button> </div> </div> @@ -424,11 +433,11 @@ export function SEDPCompareDialog({ <Tabs value={activeTab} onValueChange={setActiveTab} className="flex-1 flex flex-col overflow-hidden"> <TabsList> - <TabsTrigger value="all">전체 태그 ({totalTags})</TabsTrigger> - <TabsTrigger value="differences">차이 있음 ({nonMatchingTags})</TabsTrigger> - <TabsTrigger value="matching">일치함 ({matchingTags})</TabsTrigger> + <TabsTrigger value="all">{t("tabs.allTags")} ({totalTags})</TabsTrigger> + <TabsTrigger value="differences">{t("tabs.differences")} ({nonMatchingTags})</TabsTrigger> + <TabsTrigger value="matching">{t("tabs.matching")} ({matchingTags})</TabsTrigger> <TabsTrigger value="missing" className={totalMissingTags > 0 ? "text-red-500" : ""}> - 누락된 태그 ({totalMissingTags}) + {t("tabs.missingTags")} ({totalMissingTags}) </TabsTrigger> </TabsList> @@ -436,19 +445,19 @@ export function SEDPCompareDialog({ {isLoading ? ( <div className="flex items-center justify-center h-full"> <Loader className="h-8 w-8 animate-spin mr-2" /> - <span>데이터 비교 중...</span> + <span>{t("messages.dataComparing")}</span> </div> ) : activeTab === "missing" ? ( // Missing tags tab content <div className="space-y-6"> {missingTags.localOnly.length > 0 && ( <div> - <h3 className="text-sm font-medium mb-2">로컬에만 있는 태그 ({missingTags.localOnly.length})</h3> + <h3 className="text-sm font-medium mb-2">{t("sections.localOnlyTags")} ({missingTags.localOnly.length})</h3> <Table> <TableHeader className="sticky top-0 bg-background z-10"> <TableRow> - <TableHead className="w-[180px]">Tag Number</TableHead> - <TableHead>Tag Description</TableHead> + <TableHead className="w-[180px]">{t("labels.tagNo")}</TableHead> + <TableHead>{t("labels.description")}</TableHead> </TableRow> </TableHeader> <TableBody> @@ -465,12 +474,12 @@ export function SEDPCompareDialog({ {missingTags.sedpOnly.length > 0 && ( <div> - <h3 className="text-sm font-medium mb-2">SEDP에만 있는 태그 ({missingTags.sedpOnly.length})</h3> + <h3 className="text-sm font-medium mb-2">{t("sections.sedpOnlyTags")} ({missingTags.sedpOnly.length})</h3> <Table> <TableHeader className="sticky top-0 bg-background z-10"> <TableRow> - <TableHead className="w-[180px]">Tag Number</TableHead> - <TableHead>Tag Description</TableHead> + <TableHead className="w-[180px]">{t("labels.tagNo")}</TableHead> + <TableHead>{t("labels.description")}</TableHead> </TableRow> </TableHeader> <TableBody> @@ -487,7 +496,7 @@ export function SEDPCompareDialog({ {totalMissingTags === 0 && ( <div className="flex items-center justify-center h-full text-muted-foreground"> - 모든 태그가 양쪽 시스템에 존재합니다 + {t("messages.allTagsExistInBothSystems")} </div> )} </div> @@ -498,9 +507,9 @@ export function SEDPCompareDialog({ <TableHeader className="sticky top-0 bg-muted/50 z-10"> <TableRow> <TableHead className="w-12"></TableHead> - <TableHead className="w-[180px]">Tag Number</TableHead> - <TableHead className="w-[250px]">Tag Description</TableHead> - <TableHead className="w-[120px]">상태</TableHead> + <TableHead className="w-[180px]">{t("labels.tagNo")}</TableHead> + <TableHead className="w-[250px]">{t("labels.description")}</TableHead> + <TableHead className="w-[120px]">{t("labels.status")}</TableHead> <TableHead>차이점 개수</TableHead> </TableRow> </TableHeader> @@ -508,7 +517,7 @@ export function SEDPCompareDialog({ {filteredResults.map((result) => ( <React.Fragment key={result.tagNo}> {/* 메인 행 */} - <TableRow + <TableRow className={`hover:bg-muted/20 cursor-pointer ${!result.isMatching ? "bg-muted/10" : ""}`} onClick={() => toggleRowExpansion(result.tagNo)} > @@ -533,29 +542,29 @@ export function SEDPCompareDialog({ {result.isMatching ? ( <Badge variant="default" className="flex items-center gap-1"> <CheckCircle className="h-3 w-3" /> - <span>일치</span> + <span>{t("labels.matching")}</span> </Badge> ) : ( <Badge variant="destructive" className="flex items-center gap-1"> <AlertCircle className="h-3 w-3" /> - <span>차이</span> + <span>{t("labels.different")}</span> </Badge> )} </TableCell> <TableCell> {!result.isMatching && ( <span className="text-sm text-muted-foreground"> - {result.attributes.filter(attr => !attr.isMatching).length}개 속성이 다름 + {result.attributes.filter(attr => !attr.isMatching).length}{t("sections.differenceCount")} </span> )} </TableCell> </TableRow> - + {/* 확장된 차이점 표시 행 */} {expandedRows.has(result.tagNo) && ( <TableRow> <TableCell colSpan={5} className="p-0 bg-muted/5"> - <DifferencesCard + <DifferencesCard attributes={result.attributes} columnLabelMap={columnLabelMap} showOnlyDifferences={showOnlyDifferences} @@ -570,7 +579,7 @@ export function SEDPCompareDialog({ </div> ) : ( <div className="flex items-center justify-center h-full text-muted-foreground"> - {searchTerm ? "검색 결과가 없습니다" : "현재 필터에 맞는 태그가 없습니다"} + {searchTerm ? t("messages.noSearchResults") : t("messages.noMatchingTags")} </div> )} </TabsContent> @@ -583,7 +592,7 @@ export function SEDPCompareDialog({ formCode={formCode} disabled={isLoading || (nonMatchingTags === 0 && totalMissingTags === 0)} /> - <Button onClick={onClose}>닫기</Button> + <Button onClick={onClose}>{t("buttons.close")}</Button> </DialogFooter> </DialogContent> </Dialog> |
