코드 활성/비활성화 해결

This commit is contained in:
dohyeons 2025-09-30 14:28:40 +09:00
parent 0b787b4c4c
commit 142f6a1a90
6 changed files with 80 additions and 39 deletions

View File

@ -65,12 +65,26 @@ export class CommonCodeController {
// 프론트엔드가 기대하는 형식으로 데이터 변환
const transformedData = result.data.map((code: any) => ({
// 새로운 필드명 (카멜케이스)
codeValue: code.code_value,
codeName: code.code_name,
codeNameEng: code.code_name_eng,
description: code.description,
sortOrder: code.sort_order,
isActive: code.is_active === "Y",
isActive: code.is_active,
useYn: code.is_active,
// 기존 필드명도 유지 (하위 호환성)
code_category: code.code_category,
code_value: code.code_value,
code_name: code.code_name,
code_name_eng: code.code_name_eng,
sort_order: code.sort_order,
is_active: code.is_active,
created_date: code.created_date,
created_by: code.created_by,
updated_date: code.updated_date,
updated_by: code.updated_by,
}));
return res.json({

View File

@ -276,8 +276,11 @@ export class CommonCodeService {
updatedBy: string
) {
try {
// 디버깅: 받은 데이터 로그
logger.info(`코드 수정 데이터:`, { categoryCode, codeValue, data });
// codeValue가 undefined이거나 빈 문자열인지 확인
if (!codeValue || codeValue === 'undefined') {
throw new Error(`잘못된 코드 값입니다: ${codeValue}`);
}
const code = await prisma.code_info.update({
where: {
code_category_code_value: {

View File

@ -67,7 +67,7 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
})),
});
},
getItemId: (code: CodeInfo) => code.code_value,
getItemId: (code: CodeInfo) => code.codeValue || code.code_value,
});
// 새 코드 생성
@ -95,7 +95,7 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
try {
await deleteCodeMutation.mutateAsync({
categoryCode,
codeValue: deletingCode.code_value,
codeValue: deletingCode.codeValue || deletingCode.code_value,
});
setShowDeleteModal(false);
@ -182,13 +182,13 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
<div className="p-2">
<DndContext {...dragAndDrop.dndContextProps}>
<SortableContext
items={filteredCodes.map((code) => code.code_value)}
items={filteredCodes.map((code) => code.codeValue || code.code_value)}
strategy={verticalListSortingStrategy}
>
<div className="space-y-1">
{filteredCodes.map((code, index) => (
<SortableCodeItem
key={`${code.code_value}-${index}`}
key={`${code.codeValue || code.code_value}-${index}`}
code={code}
categoryCode={categoryCode}
onEdit={() => handleEditCode(code)}
@ -208,20 +208,28 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
<div className="flex items-start justify-between">
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<h3 className="font-medium text-gray-900">{activeCode.code_name}</h3>
<h3 className="font-medium text-gray-900">
{activeCode.codeName || activeCode.code_name}
</h3>
<Badge
variant={activeCode.is_active === "Y" ? "default" : "secondary"}
variant={
activeCode.isActive === "Y" || activeCode.is_active === "Y"
? "default"
: "secondary"
}
className={cn(
"transition-colors",
activeCode.is_active === "Y"
activeCode.isActive === "Y" || activeCode.is_active === "Y"
? "bg-green-100 text-green-800"
: "bg-gray-100 text-gray-600",
)}
>
{activeCode.is_active === "Y" ? "활성" : "비활성"}
{activeCode.isActive === "Y" || activeCode.is_active === "Y" ? "활성" : "비활성"}
</Badge>
</div>
<p className="mt-1 text-sm text-gray-600">{activeCode.code_value}</p>
<p className="mt-1 text-sm text-gray-600">
{activeCode.codeValue || activeCode.code_value}
</p>
{activeCode.description && (
<p className="mt-1 text-sm text-gray-500">{activeCode.description}</p>
)}

View File

@ -51,7 +51,7 @@ export function CodeFormModal({ isOpen, onClose, categoryCode, editingCode, code
categoryCode,
"codeValue",
validationStates.codeValue.value,
isEditing ? editingCode?.code_value : undefined,
isEditing ? editingCode?.codeValue || editingCode?.code_value : undefined,
validationStates.codeValue.enabled,
);
@ -59,7 +59,7 @@ export function CodeFormModal({ isOpen, onClose, categoryCode, editingCode, code
categoryCode,
"codeName",
validationStates.codeName.value,
isEditing ? editingCode?.code_value : undefined,
isEditing ? editingCode?.codeValue || editingCode?.code_value : undefined,
validationStates.codeName.enabled,
);
@ -67,7 +67,7 @@ export function CodeFormModal({ isOpen, onClose, categoryCode, editingCode, code
categoryCode,
"codeNameEng",
validationStates.codeNameEng.value,
isEditing ? editingCode?.code_value : undefined,
isEditing ? editingCode?.codeValue || editingCode?.code_value : undefined,
validationStates.codeNameEng.enabled,
);
@ -102,18 +102,18 @@ export function CodeFormModal({ isOpen, onClose, categoryCode, editingCode, code
if (isEditing && editingCode) {
// 수정 모드: 기존 데이터 로드 (codeValue는 표시용으로만 설정)
form.reset({
codeName: editingCode.code_name,
codeNameEng: editingCode.code_name_eng || "",
codeName: editingCode.codeName || editingCode.code_name,
codeNameEng: editingCode.codeNameEng || editingCode.code_name_eng || "",
description: editingCode.description || "",
sortOrder: editingCode.sort_order,
isActive: editingCode.is_active as "Y" | "N", // 타입 캐스팅
sortOrder: editingCode.sortOrder || editingCode.sort_order,
isActive: (editingCode.isActive || editingCode.is_active) as "Y" | "N", // 타입 캐스팅
});
// codeValue는 별도로 설정 (표시용)
form.setValue("codeValue" as any, editingCode.code_value);
form.setValue("codeValue" as any, editingCode.codeValue || editingCode.code_value);
} else {
// 새 코드 모드: 자동 순서 계산
const maxSortOrder = codes.length > 0 ? Math.max(...codes.map((c) => c.sort_order)) : 0;
const maxSortOrder = codes.length > 0 ? Math.max(...codes.map((c) => c.sortOrder || c.sort_order)) : 0;
form.reset({
codeValue: "",
@ -132,7 +132,7 @@ export function CodeFormModal({ isOpen, onClose, categoryCode, editingCode, code
// 수정
await updateCodeMutation.mutateAsync({
categoryCode,
codeValue: editingCode.code_value,
codeValue: editingCode.codeValue || editingCode.code_value,
data: data as UpdateCodeData,
});
} else {

View File

@ -26,7 +26,7 @@ export function SortableCodeItem({
isDragOverlay = false,
}: SortableCodeItemProps) {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
id: code.code_value,
id: code.codeValue || code.code_value,
disabled: isDragOverlay,
});
const updateCodeMutation = useUpdateCode();
@ -39,14 +39,20 @@ export function SortableCodeItem({
// 활성/비활성 토글 핸들러
const handleToggleActive = async (checked: boolean) => {
try {
// codeValue 또는 code_value가 없으면 에러 처리
const codeValue = code.codeValue || code.code_value;
if (!codeValue) {
return;
}
await updateCodeMutation.mutateAsync({
categoryCode,
codeValue: code.code_value,
codeValue: codeValue,
data: {
codeName: code.code_name,
codeNameEng: code.code_name_eng || "",
codeName: code.codeName || code.code_name,
codeNameEng: code.codeNameEng || code.code_name_eng || "",
description: code.description || "",
sortOrder: code.sort_order,
sortOrder: code.sortOrder || code.sort_order,
isActive: checked ? "Y" : "N",
},
});
@ -70,12 +76,12 @@ export function SortableCodeItem({
<div className="flex items-start justify-between">
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<h3 className="font-medium text-gray-900">{code.code_name}</h3>
<h3 className="font-medium text-gray-900">{code.codeName || code.code_name}</h3>
<Badge
variant={code.is_active === "Y" ? "default" : "secondary"}
variant={code.isActive === "Y" || code.is_active === "Y" ? "default" : "secondary"}
className={cn(
"cursor-pointer transition-colors",
code.is_active === "Y"
code.isActive === "Y" || code.is_active === "Y"
? "bg-green-100 text-green-800 hover:bg-green-200 hover:text-green-900"
: "bg-gray-100 text-gray-600 hover:bg-gray-200 hover:text-gray-700",
updateCodeMutation.isPending && "cursor-not-allowed opacity-50",
@ -84,16 +90,17 @@ export function SortableCodeItem({
e.preventDefault();
e.stopPropagation();
if (!updateCodeMutation.isPending) {
handleToggleActive(code.is_active !== "Y");
const isActive = code.isActive === "Y" || code.is_active === "Y";
handleToggleActive(!isActive);
}
}}
onPointerDown={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
>
{code.is_active === "Y" ? "활성" : "비활성"}
{code.isActive === "Y" || code.is_active === "Y" ? "활성" : "비활성"}
</Badge>
</div>
<p className="mt-1 text-sm text-gray-600">{code.code_value}</p>
<p className="mt-1 text-sm text-gray-600">{code.codeValue || code.code_value}</p>
{code.description && <p className="mt-1 text-sm text-gray-500">{code.description}</p>}
</div>

View File

@ -17,13 +17,22 @@ export interface CodeCategory {
export type CategoryInfo = CodeCategory;
export interface CodeInfo {
code_category: string;
code_value: string;
code_name: string;
code_name_eng?: string | null;
// 백엔드 응답 필드 (변환된 형태)
codeValue?: string;
codeName?: string;
codeNameEng?: string | null;
description?: string | null;
sort_order: number;
is_active: string;
sortOrder?: number;
isActive?: string | boolean;
useYn?: string;
// 기존 필드 (하위 호환성을 위해 유지)
code_category?: string;
code_value?: string;
code_name?: string;
code_name_eng?: string | null;
sort_order?: number;
is_active?: string;
created_date?: string | null;
created_by?: string | null;
updated_date?: string | null;