Files
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

87 lines
3.5 KiB
TypeScript

import { z } from "zod";
/**
* 공통코드 (마스터-디테일) zod 스키마
*
* 마스터: code_info (그룹)
* 디테일: code_detail (트리 노드, depth 2 시작)
*
* 폼 필드명은 백엔드 컬럼명 (snake_case) 그대로 사용.
*/
/* ───────────────────────── code_info (마스터) ───────────────────────── */
export const codeInfoSchema = z.object({
code_info: z
.string()
.min(1, "그룹 코드는 필수입니다")
.max(50, "그룹 코드는 50자 이하여야 합니다")
.regex(/^[A-Z0-9_]+$/, "대문자, 숫자, 언더스코어(_)만 사용 가능합니다"),
code_name: z.string().min(1, "그룹명은 필수입니다").max(100, "그룹명은 100자 이하여야 합니다"),
code_name_eng: z
.string()
.max(100, "영문 그룹명은 100자 이하여야 합니다")
.optional()
.or(z.literal("")),
description: z.string().max(500, "설명은 500자 이하여야 합니다").optional().or(z.literal("")),
sort_order: z
.number()
.min(0, "정렬 순서는 0 이상이어야 합니다")
.max(9999, "정렬 순서는 9999 이하여야 합니다"),
});
export const createCodeInfoSchema = codeInfoSchema;
export const updateCodeInfoSchema = codeInfoSchema.omit({ code_info: true }).extend({
is_active: z.enum(["Y", "N"]),
});
export type CodeInfoFormData = z.infer<typeof codeInfoSchema>;
export type CreateCodeInfoData = z.infer<typeof createCodeInfoSchema>;
export type UpdateCodeInfoData = z.infer<typeof updateCodeInfoSchema>;
/* ───────────────────────── code_detail (디테일 트리 노드) ───────────────────────── */
export const codeDetailSchema = z.object({
code_info: z.string().min(1, "그룹은 필수입니다"),
parent_detail_id: z.number().int().positive().nullable().optional(),
code_value: z.string().min(1, "코드값은 필수입니다").max(50, "코드값은 50자 이하여야 합니다"),
code_name: z.string().min(1, "코드명은 필수입니다").max(100, "코드명은 100자 이하여야 합니다"),
code_name_eng: z
.string()
.max(100, "영문 코드명은 100자 이하여야 합니다")
.optional()
.or(z.literal("")),
description: z.string().max(500, "설명은 500자 이하여야 합니다").optional().or(z.literal("")),
sort_order: z
.number()
.min(0, "정렬 순서는 0 이상이어야 합니다")
.max(9999, "정렬 순서는 9999 이하여야 합니다")
.optional(),
});
export const createCodeDetailSchema = codeDetailSchema;
export const updateCodeDetailSchema = codeDetailSchema.extend({
is_active: z.enum(["Y", "N"]),
});
export type CodeDetailFormData = z.infer<typeof codeDetailSchema>;
export type CreateCodeDetailData = z.infer<typeof createCodeDetailSchema>;
export type UpdateCodeDetailData = z.infer<typeof updateCodeDetailSchema>;
/* ───────────────────────── 검색 필터 ───────────────────────── */
export const codeInfoFilterSchema = z.object({
search: z.string().optional(),
active: z.boolean().optional(),
});
export const codeDetailFilterSchema = z.object({
search: z.string().optional(),
active: z.boolean().optional(),
});
export type CodeInfoFilter = z.infer<typeof codeInfoFilterSchema>;
export type CodeDetailFilter = z.infer<typeof codeDetailFilterSchema>;