Files
sam-react-prod/src/components/items/ItemMasterDataManagement/services/masterFieldService.ts
유병철 020d74f36c feat(WEB): DynamicItemForm 필드 타입 확장 및 컴포넌트 레지스트리 추가
- DynamicFieldRenderer에 신규 필드 타입 추가 (Currency, File, MultiSelect, Radio, Reference, Toggle, UnitValue, Computed)
- DynamicTableSection 및 TableCellRenderer 추가
- 필드 프리셋 및 설정 구조 분리
- 컴포넌트 레지스트리 개발 도구 페이지 추가
- UniversalListPage 개선
- 근태관리 코드 정리
- 즐겨찾기 기능 및 동적 필드 타입 백엔드 스펙 문서 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 11:17:57 +09:00

202 lines
5.8 KiB
TypeScript

/**
* Master Field Service
* 마스터 필드(항목탭) 관련 도메인 로직 중앙화
* - validation (fieldService 재사용)
* - parsing
* - transform (폼 ↔ API)
* - defaults
*/
import type { ItemMasterField } from '@/contexts/ItemMasterContext';
import type { ItemFieldType } from '@/types/item-master-api';
import { fieldService, type SingleFieldValidation } from './fieldService';
// ===== Types =====
export type MasterFieldType = ItemFieldType;
export type AttributeType = 'custom' | 'unit' | 'material' | 'surface';
export interface MasterFieldFormData {
name: string;
key: string;
inputType: MasterFieldType;
required: boolean;
category: string;
description: string;
options: string; // 콤마 구분 문자열
attributeType: AttributeType;
multiColumn: boolean;
columnCount: number;
columnNames: string[];
}
export interface MasterFieldValidationResult {
valid: boolean;
errors: {
field_name?: string;
field_key?: string;
field_type?: string;
};
}
// ===== Service =====
export const masterFieldService = {
// ===== Validation (fieldService 재사용) =====
/**
* 전체 마스터 필드 폼 유효성 검사
*/
validate: (data: Partial<MasterFieldFormData>): MasterFieldValidationResult => {
const errors: MasterFieldValidationResult['errors'] = {};
// 필드명 검증 (fieldService 재사용)
const nameValidation = fieldService.validateFieldName(data.name || '');
if (!nameValidation.valid) {
errors.field_name = (nameValidation as { valid: false; error: string }).error;
}
// 필드 키 검증 (fieldService 재사용)
const keyValidation = fieldService.validateFieldKey(data.key || '');
if (!keyValidation.valid) {
errors.field_key = (keyValidation as { valid: false; error: string }).error;
}
return {
valid: Object.keys(errors).length === 0,
errors,
};
},
/**
* 필드명 유효성 검사 (fieldService 위임)
*/
validateFieldName: fieldService.validateFieldName,
/**
* 필드 키 유효성 검사 (fieldService 위임)
*/
validateFieldKey: fieldService.validateFieldKey,
/**
* 필드 키 패턴 정규식 (fieldService 재사용)
*/
fieldKeyPattern: fieldService.fieldKeyPattern,
/**
* 필드 키가 유효한지 간단 체크 (fieldService 위임)
*/
isFieldKeyValid: fieldService.isFieldKeyValid,
// ===== Parsing =====
/**
* field_key에서 사용자 입력 부분 추출 (fieldService 위임)
* 형식: {ID}_{사용자입력} → 사용자입력 반환
*/
extractUserInputFromFieldKey: fieldService.extractUserInputFromFieldKey,
/**
* 옵션 문자열을 배열로 파싱 (fieldService 위임)
*/
parseOptionsFromString: fieldService.parseOptionsFromString,
/**
* 옵션 배열을 문자열로 변환 (fieldService 위임)
*/
optionsToString: fieldService.optionsToString,
// ===== Transform =====
/**
* 폼 데이터 → API 요청 객체 변환
*/
toApiRequest: (
formData: MasterFieldFormData
): Omit<ItemMasterField, 'id' | 'tenant_id' | 'created_by' | 'updated_by' | 'created_at' | 'updated_at'> => {
const supportsMultiColumn = formData.inputType === 'textbox' || formData.inputType === 'textarea';
return {
field_name: formData.name,
field_key: formData.key,
field_type: formData.inputType,
category: formData.category || null,
description: formData.description || null,
is_common: false,
default_value: null,
options: formData.inputType === 'dropdown'
? fieldService.parseOptionsFromString(formData.options)
: null,
validation_rules: null,
properties: {
required: formData.required,
attributeType: formData.inputType === 'dropdown' ? formData.attributeType : undefined,
multiColumn: supportsMultiColumn ? formData.multiColumn : undefined,
columnCount: supportsMultiColumn && formData.multiColumn ? formData.columnCount : undefined,
columnNames: supportsMultiColumn && formData.multiColumn ? formData.columnNames : undefined,
},
};
},
/**
* ItemMasterField → 폼 데이터 변환 (수정 시 폼에 채우기 위함)
*/
toFormData: (field: ItemMasterField): MasterFieldFormData => {
const properties = field.properties as Record<string, any> | null;
return {
name: field.field_name,
key: masterFieldService.extractUserInputFromFieldKey(field.field_key),
inputType: field.field_type || 'textbox',
required: properties?.required || false,
category: field.category || '공통',
description: field.description || '',
options: masterFieldService.optionsToString(field.options),
attributeType: properties?.attributeType || 'custom',
multiColumn: properties?.multiColumn || false,
columnCount: properties?.columnCount || 2,
columnNames: properties?.columnNames || ['컬럼1', '컬럼2'],
};
},
// ===== Defaults =====
/**
* 새 마스터 필드 생성 시 기본값
*/
getDefaultFormData: (): MasterFieldFormData => ({
name: '',
key: '',
inputType: 'textbox',
required: false,
category: '공통',
description: '',
options: '',
attributeType: 'custom',
multiColumn: false,
columnCount: 2,
columnNames: ['컬럼1', '컬럼2'],
}),
/**
* 지원하는 필드 타입 목록 (fieldService 재사용)
*/
fieldTypes: fieldService.fieldTypes,
/**
* 지원하는 속성 타입 목록
*/
attributeTypes: [
{ value: 'custom', label: '직접 입력' },
{ value: 'unit', label: '단위' },
{ value: 'material', label: '재질' },
{ value: 'surface', label: '표면처리' },
] as const,
/**
* 다중 컬럼 지원 여부 확인
*/
supportsMultiColumn: (inputType: MasterFieldType): boolean => {
return inputType === 'textbox' || inputType === 'textarea';
},
};