fix: field_key 저장 및 표시 기능 완전 수정
- field_key 저장 시 백엔드 형식({ID}_{사용자입력})으로 전송
- API 요청 전 field_key 유효성 검증 추가
- 계층구조 탭 필드 추가/수정 시 field_key 반영
- 섹션 탭에서 field_key 표시 시 사용자입력 부분만 추출
- sectionsAsTemplates useMemo에서 linkedSections/unlinkedSections 모두 수정
- 마스터 필드, 템플릿 필드 다이얼로그에서 field_key 입력 지원
- ItemMasterContext에 field_key 상태 업데이트 로직 추가
- transformers에서 field_key 변환 처리
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -261,6 +261,7 @@ export interface ItemMasterField {
|
||||
id: number;
|
||||
tenant_id: number;
|
||||
field_name: string;
|
||||
field_key?: string | null; // 2025-11-28: field_key 추가 (형식: {ID}_{사용자입력})
|
||||
field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea'; // API와 동일
|
||||
category: string | null;
|
||||
description: string | null;
|
||||
@@ -296,6 +297,7 @@ export interface ItemField {
|
||||
section_id: number | null; // 외래키 - 섹션 ID (독립 필드는 null)
|
||||
master_field_id?: number | null; // 마스터 항목 ID (마스터에서 가져온 경우)
|
||||
field_name: string; // 항목명 (name → field_name)
|
||||
field_key?: string | null; // 2025-11-28: 필드 키 (형식: {ID}_{사용자입력}, 백엔드에서 생성)
|
||||
field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea'; // 필드 타입
|
||||
order_no: number; // 항목 순서 (order → order_no, required)
|
||||
is_required: boolean; // 필수 여부
|
||||
@@ -305,6 +307,10 @@ export interface ItemField {
|
||||
validation_rules?: Record<string, any> | null; // 검증 규칙
|
||||
options?: Array<{ label: string; value: string }> | null; // dropdown 옵션
|
||||
properties?: Record<string, any> | null; // 추가 속성
|
||||
// 2025-11-28 추가: 잠금 기능
|
||||
is_locked?: boolean; // 잠금 여부
|
||||
locked_by?: number | null; // 잠금 설정자
|
||||
locked_at?: string | null; // 잠금 시간
|
||||
created_by?: number | null; // 생성자 ID 추가
|
||||
updated_by?: number | null; // 수정자 ID 추가
|
||||
created_at: string; // 생성일 (camelCase → snake_case)
|
||||
@@ -879,8 +885,10 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
try {
|
||||
// API 호출 - 2025-11-27: fields.createIndependent() 사용 (masterFields.create() deprecated)
|
||||
// Note: API가 ItemFieldResponse를 직접 반환 (wrapper 없음)
|
||||
// 2025-11-28: field_key 추가
|
||||
const response = await itemMasterApi.fields.createIndependent({
|
||||
field_name: field.field_name,
|
||||
field_key: field.field_key ?? undefined, // 2025-11-28: field_key 추가
|
||||
field_type: field.field_type,
|
||||
category: field.category ?? undefined,
|
||||
description: field.description ?? undefined,
|
||||
@@ -893,10 +901,12 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
// 응답 데이터 변환 및 state 업데이트
|
||||
// 2025-11-27: API가 ItemFieldResponse를 직접 반환하므로 response를 직접 사용
|
||||
// 2025-11-28: field_key 추가
|
||||
const newField: ItemMasterField = {
|
||||
id: response.id,
|
||||
tenant_id: response.tenant_id,
|
||||
field_name: response.field_name,
|
||||
field_key: response.field_key, // 2025-11-28: field_key 추가
|
||||
field_type: response.field_type,
|
||||
category: response.category,
|
||||
description: response.description,
|
||||
@@ -929,6 +939,7 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
// API 호출 - 2025-11-27: fields.update() 사용 (masterFields.update() deprecated)
|
||||
const requestData: any = {};
|
||||
if (updates.field_name !== undefined) requestData.field_name = updates.field_name;
|
||||
if (updates.field_key !== undefined) requestData.field_key = updates.field_key; // 2025-11-28: field_key 추가
|
||||
if (updates.field_type !== undefined) requestData.field_type = updates.field_type;
|
||||
if (updates.category !== undefined) requestData.category = updates.category;
|
||||
if (updates.description !== undefined) requestData.description = updates.description;
|
||||
@@ -944,11 +955,12 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
throw new Error(response.message || '마스터 필드 수정 실패');
|
||||
}
|
||||
|
||||
// state 업데이트
|
||||
// state 업데이트 - 2025-11-28: field_key 추가
|
||||
setItemMasterFields(prev => prev.map(field =>
|
||||
field.id === id ? {
|
||||
...field,
|
||||
field_name: response.data!.field_name,
|
||||
field_key: response.data!.field_key, // 2025-11-28: field_key 추가
|
||||
field_type: response.data!.field_type,
|
||||
category: response.data!.category,
|
||||
description: response.data!.description,
|
||||
@@ -963,12 +975,14 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
// 2025-11-27: 섹션탭/계층구조 실시간 반영
|
||||
// sectionsAsTemplates useMemo가 [itemPages, independentSections] 둘 다 의존
|
||||
// 2025-11-28: field_key 추가
|
||||
setIndependentSections(prev => prev.map(section => ({
|
||||
...section,
|
||||
fields: (section.fields || []).map(field =>
|
||||
field.id === id ? {
|
||||
...field,
|
||||
field_name: response.data!.field_name,
|
||||
field_key: response.data!.field_key, // 2025-11-28: field_key 추가
|
||||
field_type: response.data!.field_type,
|
||||
is_required: response.data!.is_required,
|
||||
default_value: response.data!.default_value,
|
||||
@@ -980,6 +994,7 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
)
|
||||
})));
|
||||
|
||||
// 2025-11-28: field_key 추가
|
||||
setItemPages(prev => prev.map(page => ({
|
||||
...page,
|
||||
sections: page.sections.map(section => ({
|
||||
@@ -988,6 +1003,7 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
field.id === id ? {
|
||||
...field,
|
||||
field_name: response.data!.field_name,
|
||||
field_key: response.data!.field_key, // 2025-11-28: field_key 추가
|
||||
field_type: response.data!.field_type,
|
||||
is_required: response.data!.is_required,
|
||||
default_value: response.data!.default_value,
|
||||
@@ -1549,8 +1565,10 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
const addFieldToSection = async (sectionId: number, fieldData: Omit<ItemField, 'id' | 'created_at' | 'updated_at'>) => {
|
||||
try {
|
||||
// API 호출 (null → undefined 변환)
|
||||
// 2025-11-28: field_key 추가
|
||||
const response = await itemMasterApi.fields.create(sectionId, {
|
||||
field_name: fieldData.field_name,
|
||||
field_key: fieldData.field_key ?? undefined, // 2025-11-28: field_key 추가
|
||||
field_type: fieldData.field_type,
|
||||
is_required: fieldData.is_required,
|
||||
default_value: fieldData.default_value ?? undefined,
|
||||
@@ -1566,12 +1584,14 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
}
|
||||
|
||||
// 응답 데이터 변환 및 state 업데이트
|
||||
// 2025-11-28: field_key 추가
|
||||
const newField: ItemField = {
|
||||
id: response.data.id,
|
||||
tenant_id: response.data.tenant_id,
|
||||
section_id: response.data.section_id,
|
||||
master_field_id: response.data.master_field_id,
|
||||
field_name: response.data.field_name,
|
||||
field_key: response.data.field_key, // 2025-11-28: field_key 추가
|
||||
field_type: response.data.field_type,
|
||||
order_no: response.data.order_no,
|
||||
is_required: response.data.is_required,
|
||||
@@ -1627,10 +1647,12 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
// 2025-11-27: 항목탭/속성탭 실시간 동기화
|
||||
// ItemField → ItemMasterField 변환하여 추가
|
||||
// 2025-11-28: field_key 추가
|
||||
const newMasterField: ItemMasterField = {
|
||||
id: newField.id,
|
||||
tenant_id: newField.tenant_id ?? 0,
|
||||
field_name: newField.field_name,
|
||||
field_key: newField.field_key, // 2025-11-28: field_key 추가
|
||||
field_type: newField.field_type,
|
||||
description: newField.placeholder ?? null,
|
||||
category: null,
|
||||
@@ -1660,6 +1682,8 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
// API 호출 (null → undefined 변환)
|
||||
const requestData: any = {};
|
||||
if (updates.field_name !== undefined) requestData.field_name = updates.field_name;
|
||||
// 2025-11-28: field_key 추가
|
||||
if (updates.field_key !== undefined) requestData.field_key = updates.field_key ?? undefined;
|
||||
if (updates.field_type !== undefined) requestData.field_type = updates.field_type;
|
||||
if (updates.is_required !== undefined) requestData.is_required = updates.is_required;
|
||||
if (updates.default_value !== undefined) requestData.default_value = updates.default_value ?? undefined;
|
||||
@@ -1677,6 +1701,7 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
}
|
||||
|
||||
// state 업데이트
|
||||
// 2025-11-28: field_key 추가
|
||||
setItemPages(prev => prev.map(page => ({
|
||||
...page,
|
||||
sections: page.sections.map(section => ({
|
||||
@@ -1685,6 +1710,7 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
field.id === fieldId ? {
|
||||
...field,
|
||||
field_name: response.data!.field_name,
|
||||
field_key: response.data!.field_key, // 2025-11-28: field_key 추가
|
||||
field_type: response.data!.field_type,
|
||||
is_required: response.data!.is_required,
|
||||
default_value: response.data!.default_value,
|
||||
@@ -1701,10 +1727,12 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
// 2025-11-27: itemMasterFields 동기화 (항목탭 실시간 반영)
|
||||
// ItemField → ItemMasterField 타입 매핑
|
||||
// 2025-11-28: field_key 추가
|
||||
setItemMasterFields(prev => prev.map(mf =>
|
||||
mf.id === fieldId ? {
|
||||
...mf,
|
||||
field_name: response.data!.field_name,
|
||||
field_key: response.data!.field_key, // 2025-11-28: field_key 추가
|
||||
field_type: response.data!.field_type,
|
||||
is_required: response.data!.is_required,
|
||||
default_value: response.data!.default_value ?? null,
|
||||
@@ -1717,10 +1745,12 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
));
|
||||
|
||||
// 2025-11-27: independentFields 동기화 (섹션에 연결되지 않은 필드)
|
||||
// 2025-11-28: field_key 추가
|
||||
setIndependentFields(prev => prev.map(field =>
|
||||
field.id === fieldId ? {
|
||||
...field,
|
||||
field_name: response.data!.field_name,
|
||||
field_key: response.data!.field_key, // 2025-11-28: field_key 추가
|
||||
field_type: response.data!.field_type,
|
||||
is_required: response.data!.is_required,
|
||||
default_value: response.data!.default_value,
|
||||
@@ -1735,12 +1765,14 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
// 2025-11-27: independentSections 동기화 (섹션탭 실시간 반영)
|
||||
// sectionsAsTemplates useMemo가 [itemPages, independentSections] 둘 다 의존
|
||||
// 2025-11-28: field_key 추가
|
||||
setIndependentSections(prev => prev.map(section => ({
|
||||
...section,
|
||||
fields: (section.fields || []).map(field =>
|
||||
field.id === fieldId ? {
|
||||
...field,
|
||||
field_name: response.data!.field_name,
|
||||
field_key: response.data!.field_key, // 2025-11-28: field_key 추가
|
||||
field_type: response.data!.field_type,
|
||||
is_required: response.data!.is_required,
|
||||
default_value: response.data!.default_value,
|
||||
|
||||
Reference in New Issue
Block a user