fix: TypeScript 타입 오류 수정 및 설정 페이지 추가

- BOMItem Omit 타입 시그니처 통일 (useTemplateManagement, SectionsTab, ItemMasterContext)
- HeadersInit → Record<string, string> 타입 변경
- Zustand useShallow 마이그레이션 (zustand/react/shallow)
- DataTable, ListPageTemplate 제네릭 타입 제약 추가
- 설정 관리 페이지 추가 (직급, 직책, 휴가정책, 근무일정, 권한)
- HR 관리 페이지 추가 (급여, 휴가)
- 단가관리 페이지 리팩토링

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
byeongcheolryu
2025-12-09 18:07:47 +09:00
parent 48dbba0e5f
commit ded0bc2439
98 changed files with 10608 additions and 1204 deletions

View File

@@ -14,8 +14,9 @@ export type MaterialType = typeof MATERIAL_TYPES[number];
/**
* Material 타입인지 확인
*/
export function isMaterialType(itemType: string | null | undefined): boolean {
return itemType ? MATERIAL_TYPES.includes(itemType as MaterialType) : false;
export function isMaterialType(itemType: string | null | undefined): itemType is MaterialType {
if (!itemType) return false;
return (MATERIAL_TYPES as readonly string[]).includes(itemType);
}
/**
@@ -34,11 +35,13 @@ export function convertStandardFieldsToOptions(data: DynamicFormData): {
const remainingData: DynamicFormData = {};
Object.entries(data).forEach(([key, value]) => {
// standard_로 시작하는 필드 또는 옵션 관련 필드 탐지
const isStandardField = key.startsWith('standard_') ||
key.startsWith('option_') ||
/^[0-9]+_standard_/.test(key) || // "{id}_standard_1" 형식
/^[0-9]+_option_/.test(key); // "{id}_option_1" 형식
// 규격 옵션 필드 탐지 (specification_*, standard_*, option_* 패턴)
const isStandardField = key.startsWith('specification_') || // specification_1, specification_2
key.startsWith('standard_') || // standard_1, standard_2
key.startsWith('option_') || // option_1, option_2
/^[0-9]+_specification_/.test(key) || // "{id}_specification_1" 형식
/^[0-9]+_standard_/.test(key) || // "{id}_standard_1" 형식
/^[0-9]+_option_/.test(key); // "{id}_option_1" 형식
if (isStandardField && value && typeof value === 'string' && value.trim()) {
// standard_* 필드는 options 배열로 변환
@@ -82,18 +85,29 @@ export function convertOptionsToStandardFields(
* Material 저장 데이터 변환 (등록/수정 공통)
*
* 프론트엔드 폼 데이터를 백엔드 Material API 형식으로 변환
*
* - SM(철판), RM(원자재): standard_* 필드 조합으로 specification 자동 생성
* - CS(소모품): spec 필드에 직접 입력한 값 사용
*/
export function transformMaterialDataForSave(
data: DynamicFormData,
itemType: string
): DynamicFormData {
// standard_* 필드들을 options 배열로 변환
const { options, specification, remainingData } = convertStandardFieldsToOptions(data);
const { options, specification: optionSpecification, remainingData } = convertStandardFieldsToOptions(data);
// Material 품목코드 생성: 품목명-규격(옵션조합)
// CS(소모품)은 spec 필드 직접 입력, SM/RM은 옵션 조합
const isConsumable = itemType === 'CS';
const directSpec = (remainingData.spec || remainingData.specification || '') as string;
const finalSpecification = isConsumable
? (directSpec || null) // CS: 직접 입력한 spec 값 사용
: (optionSpecification || null); // SM, RM: 옵션 조합값 사용
// Material 품목코드 생성: 품목명-규격
const materialName = (remainingData.name || remainingData.item_name || '') as string;
const specForCode = finalSpecification || '';
const materialCode = remainingData.code ||
(specification ? `${materialName}-${specification}` : materialName);
(specForCode ? `${materialName}-${specForCode}` : materialName);
return {
...remainingData,
@@ -101,11 +115,12 @@ export function transformMaterialDataForSave(
material_type: (remainingData.product_type as string) || itemType,
remarks: remainingData.note as string, // note → remarks 변환
material_code: materialCode,
specification: specification || null, // 옵션 조합값을 specification으로 저장
specification: finalSpecification, // CS: 직접 입력값, SM/RM: 옵션 조합값
options: options.length > 0 ? options : null, // options 배열로 저장
// 불필요한 필드 제거
code: undefined,
product_type: undefined,
note: undefined,
spec: undefined, // spec → specification으로 통합됨
};
}

View File

@@ -35,7 +35,7 @@ const itemNameSchema = z.preprocess(
* 품목 유형 검증
*/
const itemTypeSchema = z.enum(['FG', 'PT', 'SM', 'RM', 'CS'], {
errorMap: () => ({ message: '품목 유형을 선택해주세요' }),
message: '품목 유형을 선택해주세요',
});
/**