Files
invyone/frontend/components/admin/CodeInfoItem.tsx
T
DDD1542 2348800e68
Build & Deploy to K8s / build-and-deploy (push) Successful in 9m22s
refactor(common-code): 마스터-디테일 재설계 — code_info(그룹) + code_detail(재귀 트리)
카테고리/캐스케이딩 시스템 (B/C/D) 전부 폐기:
- BE: mapper/Service/Controller 9세트 삭제 (cascading*, categoryTree, tableCategoryValue, categoryValueCascading, codeMerge)
- FE: 페이지 3 + API 8 + hooks 2 + 폐기 컴포넌트 6 삭제, 14곳 의존성 정리
- DB: 12 테이블 DROP, TABLE_TYPE_COLUMNS.CODE_CATEGORY → CODE_INFO rename

신설 commonCode 마스터-디테일:
- code_info: 1레벨 그룹 마스터
- code_detail: 2~∞ depth 재귀 트리 (parent_detail_id self-FK, depth 자동 계산)
- API: /api/common-codes/{info,detail}
- CodeCategoryFormModal/Panel → CodeInfoFormModal/Panel rename
- code_category 컬럼명 전부 code_info 로 치환 (mapper/Java/FE)
- 옛 commonCode API URL (/categories/...) → getCodeOptions 어댑터 + /detail?code_info=... 전환

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 16:50:50 +09:00

90 lines
2.9 KiB
TypeScript

"use client";
import React from "react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Edit, Trash2 } from "lucide-react";
import { cn } from "@/lib/utils";
import { useUpdateCodeInfo } from "@/hooks/queries/useCodeInfo";
import type { CodeInfo } from "@/types/commonCode";
interface CodeInfoItemProps {
row: CodeInfo;
isSelected: boolean;
onSelect: () => void;
onEdit: () => void;
onDelete: () => void;
}
export function CodeInfoItem({ row, isSelected, onSelect, onEdit, onDelete }: CodeInfoItemProps) {
const updateMutation = useUpdateCodeInfo();
const handleToggleActive = async (next: "Y" | "N") => {
try {
await updateMutation.mutateAsync({
codeInfo: row.code_info,
data: {
code_name: row.code_name,
code_name_eng: row.code_name_eng || "",
description: row.description || "",
sort_order: row.sort_order ?? 0,
is_active: next,
},
});
} catch (error) {
console.error("그룹 활성 상태 변경 실패:", error);
}
};
return (
<div
className={cn(
"cursor-pointer rounded-lg border bg-card p-4 shadow-sm transition-all",
isSelected ? "border-primary shadow-md" : "hover:shadow-md",
)}
onClick={onSelect}
>
<div className="flex items-start justify-between gap-2">
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<h4 className="text-sm font-semibold">{row.code_name}</h4>
<Badge
variant={row.is_active === "Y" ? "default" : "secondary"}
className={cn(
"cursor-pointer text-xs transition-colors",
updateMutation.isPending && "cursor-not-allowed opacity-50",
)}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (!updateMutation.isPending) {
handleToggleActive(row.is_active === "Y" ? "N" : "Y");
}
}}
onPointerDown={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
>
{row.is_active === "Y" ? "활성" : "비활성"}
</Badge>
</div>
<p className="mt-1 text-xs text-muted-foreground">{row.code_info}</p>
{row.description && (
<p className="mt-1 text-xs text-muted-foreground">{row.description}</p>
)}
</div>
{isSelected && (
<div className="flex items-center gap-1" onClick={(e) => e.stopPropagation()}>
<Button variant="ghost" size="sm" onClick={onEdit}>
<Edit className="h-3 w-3" />
</Button>
<Button variant="ghost" size="sm" onClick={onDelete}>
<Trash2 className="h-3 w-3" />
</Button>
</div>
)}
</div>
</div>
);
}