Files
sam-react-prod/src/components/items/ItemMasterDataManagement/services/pageService.ts
byeongcheolryu 0552b02ba9 refactor: 품목기준관리 서비스 레이어 도입 및 버그 수정
서비스 레이어 리팩토링:
- services/ 폴더 생성 (fieldService, masterFieldService, sectionService, pageService, templateService, attributeService)
- 도메인 로직 중앙화 (validation, parsing, transform)
- hooks와 dialogs에서 서비스 호출로 변경

버그 수정:
- 섹션탭 실시간 동기화 문제 수정 (sectionsAsTemplates 중복 제거 순서 변경)
- 422 Validation Error 수정 (createIndependentField → addFieldToSection)
- 페이지 삭제 시 섹션-필드 연결 유지 (refreshIndependentSections 대신 직접 이동)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 14:23:57 +09:00

194 lines
4.9 KiB
TypeScript

/**
* Page Service
* 페이지 관련 도메인 로직 중앙화
* - validation
* - path generation
* - transform (폼 ↔ API)
* - defaults
*/
import type { ItemPage } from '@/contexts/ItemMasterContext';
// ===== Types =====
export type ItemType = 'FG' | 'PT' | 'SM' | 'RM' | 'CS';
export interface PageFormData {
pageName: string;
itemType: ItemType;
absolutePath?: string;
}
export interface PageValidationResult {
valid: boolean;
errors: {
page_name?: string;
item_type?: string;
absolute_path?: string;
};
}
// ===== Constants =====
const ITEM_TYPE_MAP: Record<ItemType, string> = {
'FG': '제품관리',
'PT': '부품관리',
'SM': '부자재관리',
'RM': '원자재관리',
'CS': '소모품관리',
};
// ===== Service =====
export const pageService = {
// ===== Validation =====
/**
* 전체 페이지 폼 유효성 검사
*/
validate: (data: Partial<PageFormData>): PageValidationResult => {
const errors: PageValidationResult['errors'] = {};
const nameValidation = pageService.validatePageName(data.pageName || '');
if (!nameValidation.valid) {
errors.page_name = nameValidation.error;
}
const typeValidation = pageService.validateItemType(data.itemType || '');
if (!typeValidation.valid) {
errors.item_type = typeValidation.error;
}
return {
valid: Object.keys(errors).length === 0,
errors,
};
},
/**
* 페이지명 유효성 검사
*/
validatePageName: (name: string): { valid: boolean; error?: string } => {
if (!name || !name.trim()) {
return { valid: false, error: '페이지명을 입력해주세요' };
}
return { valid: true };
},
/**
* 품목 타입 유효성 검사
*/
validateItemType: (type: string): { valid: boolean; error?: string } => {
const validTypes: ItemType[] = ['FG', 'PT', 'SM', 'RM', 'CS'];
if (!validTypes.includes(type as ItemType)) {
return { valid: false, error: '유효하지 않은 품목 타입입니다' };
}
return { valid: true };
},
/**
* 절대경로 유효성 검사
*/
validateAbsolutePath: (path: string): { valid: boolean; error?: string } => {
if (!path || !path.trim()) {
return { valid: false, error: '절대경로를 입력해주세요' };
}
if (!path.startsWith('/')) {
return { valid: false, error: '절대경로는 /로 시작해야 합니다' };
}
return { valid: true };
},
// ===== Path Generation =====
/**
* 품목 타입과 페이지명으로 절대 경로 생성
*/
generateAbsolutePath: (itemType: ItemType, pageName: string): string => {
const category = ITEM_TYPE_MAP[itemType] || '기타';
return `/${category}/${pageName}`;
},
/**
* 품목 타입 코드를 한글 카테고리명으로 변환
*/
getItemTypeLabel: (itemType: ItemType): string => {
return ITEM_TYPE_MAP[itemType] || '기타';
},
// ===== Transform =====
/**
* 폼 데이터 → API 요청 객체 변환
*/
toApiRequest: (
formData: PageFormData
): Omit<ItemPage, 'id' | 'tenant_id' | 'created_at' | 'updated_at' | 'created_by' | 'updated_by'> => {
const absolutePath = formData.absolutePath ||
pageService.generateAbsolutePath(formData.itemType, formData.pageName);
return {
page_name: formData.pageName,
item_type: formData.itemType,
absolute_path: absolutePath,
is_active: true,
sections: [],
order_no: 0,
};
},
/**
* ItemPage → 폼 데이터 변환
*/
toFormData: (page: ItemPage): PageFormData => {
return {
pageName: page.page_name,
itemType: page.item_type as ItemType,
absolutePath: page.absolute_path,
};
},
/**
* 페이지 복제용 데이터 생성
*/
toDuplicateRequest: (
originalPage: ItemPage
): Omit<ItemPage, 'id' | 'tenant_id' | 'created_at' | 'updated_at' | 'created_by' | 'updated_by'> => {
const duplicatedName = `${originalPage.page_name} (복제)`;
const absolutePath = pageService.generateAbsolutePath(
originalPage.item_type as ItemType,
duplicatedName
);
return {
page_name: duplicatedName,
item_type: originalPage.item_type,
absolute_path: absolutePath,
is_active: true,
sections: [], // 섹션은 별도 API로 복제
order_no: 0,
};
},
// ===== Defaults =====
/**
* 새 페이지 생성 시 기본값
*/
getDefaultFormData: (): PageFormData => ({
pageName: '',
itemType: 'FG',
}),
/**
* 지원하는 품목 타입 목록
*/
itemTypes: [
{ value: 'FG', label: '제품관리', description: '완제품' },
{ value: 'PT', label: '부품관리', description: '조립 부품' },
{ value: 'SM', label: '부자재관리', description: '부자재' },
{ value: 'RM', label: '원자재관리', description: '원자재' },
{ value: 'CS', label: '소모품관리', description: '소모품' },
] as const,
};