feat: 품목기준관리 Zustand 리팩토링 (테스트 페이지)

## 주요 변경사항
- Zustand 정규화 스토어 구현 (useItemMasterStore)
- 테스트 페이지 구현 (/items-management-test)
- 계층구조/섹션/항목/속성 탭 완성
- CRUD 다이얼로그 (페이지/섹션/필드/BOM/속성)
- Import 기능 (섹션/필드 불러오기)
- 드래그앤드롭 순서 변경
- 인라인 편집 기능

## 구현 완료 (약 72%)
- 페이지/섹션/필드 CRUD 
- BOM 관리 
- 단위/재질/표면처리 CRUD 
- Import/복제 기능 

## 미구현 기능
- 절대경로(absolute_path) 수정
- 페이지 복제
- 필드 조건부 표시
- 칼럼 관리

🤖 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-22 09:04:28 +09:00
parent d7f491fa84
commit 52b8b1f0be
29 changed files with 8248 additions and 18 deletions

View File

@@ -38,6 +38,11 @@ import type {
LinkEntityRequest,
LinkBomRequest,
ReorderRelationshipsRequest,
// 2025-12-21 추가: 재질/표면처리 타입
MaterialOptionRequest,
MaterialOptionResponse,
TreatmentOptionRequest,
TreatmentOptionResponse,
} from '@/types/item-master-api';
import { getAuthHeaders } from './auth-headers';
import { handleApiError } from './error-handler';
@@ -1893,6 +1898,36 @@ export const itemMasterApi = {
}
},
update: async (id: number, data: Partial<UnitOptionRequest>): Promise<ApiResponse<UnitOptionResponse>> => {
const startTime = apiLogger.logRequest('PUT', `${BASE_URL}/item-master/unit-options/${id}`, data);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/unit-options/${id}`, {
method: 'PUT',
headers,
body: JSON.stringify(data),
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<UnitOptionResponse> = await response.json();
apiLogger.logResponse('PUT', `${BASE_URL}/item-master/unit-options/${id}`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('PUT', `${BASE_URL}/item-master/unit-options/${id}`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('PUT', `${BASE_URL}/item-master/unit-options/${id}`, error as Error, undefined, startTime);
throw error;
}
},
delete: async (id: number): Promise<ApiResponse<void>> => {
const startTime = apiLogger.logRequest('DELETE', `${BASE_URL}/item-master/unit-options/${id}`);
@@ -1922,4 +1957,276 @@ export const itemMasterApi = {
}
},
},
// ============================================
// 재질 관리
// ============================================
materials: {
list: async (): Promise<ApiResponse<any[]>> => {
const startTime = apiLogger.logRequest('GET', `${BASE_URL}/item-master/materials`);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/materials`, {
method: 'GET',
headers,
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<any[]> = await response.json();
apiLogger.logResponse('GET', `${BASE_URL}/item-master/materials`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('GET', `${BASE_URL}/item-master/materials`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('GET', `${BASE_URL}/item-master/materials`, error as Error, undefined, startTime);
throw error;
}
},
create: async (data: {
material_code: string;
material_name: string;
material_type: string;
thickness?: string;
description?: string;
is_active?: boolean;
}): Promise<ApiResponse<any>> => {
const startTime = apiLogger.logRequest('POST', `${BASE_URL}/item-master/materials`, data);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/materials`, {
method: 'POST',
headers,
body: JSON.stringify(data),
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<any> = await response.json();
apiLogger.logResponse('POST', `${BASE_URL}/item-master/materials`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('POST', `${BASE_URL}/item-master/materials`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('POST', `${BASE_URL}/item-master/materials`, error as Error, undefined, startTime);
throw error;
}
},
update: async (id: number | string, data: {
material_code?: string;
material_name?: string;
material_type?: string;
thickness?: string;
description?: string;
is_active?: boolean;
}): Promise<ApiResponse<any>> => {
const startTime = apiLogger.logRequest('PUT', `${BASE_URL}/item-master/materials/${id}`, data);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/materials/${id}`, {
method: 'PUT',
headers,
body: JSON.stringify(data),
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<any> = await response.json();
apiLogger.logResponse('PUT', `${BASE_URL}/item-master/materials/${id}`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('PUT', `${BASE_URL}/item-master/materials/${id}`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('PUT', `${BASE_URL}/item-master/materials/${id}`, error as Error, undefined, startTime);
throw error;
}
},
delete: async (id: number | string): Promise<ApiResponse<void>> => {
const startTime = apiLogger.logRequest('DELETE', `${BASE_URL}/item-master/materials/${id}`);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/materials/${id}`, {
method: 'DELETE',
headers,
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<void> = await response.json();
apiLogger.logResponse('DELETE', `${BASE_URL}/item-master/materials/${id}`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('DELETE', `${BASE_URL}/item-master/materials/${id}`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('DELETE', `${BASE_URL}/item-master/materials/${id}`, error as Error, undefined, startTime);
throw error;
}
},
},
// ============================================
// 표면처리 관리
// ============================================
treatments: {
list: async (): Promise<ApiResponse<any[]>> => {
const startTime = apiLogger.logRequest('GET', `${BASE_URL}/item-master/surface-treatments`);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/surface-treatments`, {
method: 'GET',
headers,
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<any[]> = await response.json();
apiLogger.logResponse('GET', `${BASE_URL}/item-master/surface-treatments`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('GET', `${BASE_URL}/item-master/surface-treatments`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('GET', `${BASE_URL}/item-master/surface-treatments`, error as Error, undefined, startTime);
throw error;
}
},
create: async (data: {
treatment_code: string;
treatment_name: string;
treatment_type: string;
description?: string;
is_active?: boolean;
}): Promise<ApiResponse<any>> => {
const startTime = apiLogger.logRequest('POST', `${BASE_URL}/item-master/surface-treatments`, data);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/surface-treatments`, {
method: 'POST',
headers,
body: JSON.stringify(data),
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<any> = await response.json();
apiLogger.logResponse('POST', `${BASE_URL}/item-master/surface-treatments`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('POST', `${BASE_URL}/item-master/surface-treatments`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('POST', `${BASE_URL}/item-master/surface-treatments`, error as Error, undefined, startTime);
throw error;
}
},
update: async (id: number | string, data: {
treatment_code?: string;
treatment_name?: string;
treatment_type?: string;
description?: string;
is_active?: boolean;
}): Promise<ApiResponse<any>> => {
const startTime = apiLogger.logRequest('PUT', `${BASE_URL}/item-master/surface-treatments/${id}`, data);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/surface-treatments/${id}`, {
method: 'PUT',
headers,
body: JSON.stringify(data),
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<any> = await response.json();
apiLogger.logResponse('PUT', `${BASE_URL}/item-master/surface-treatments/${id}`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('PUT', `${BASE_URL}/item-master/surface-treatments/${id}`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('PUT', `${BASE_URL}/item-master/surface-treatments/${id}`, error as Error, undefined, startTime);
throw error;
}
},
delete: async (id: number | string): Promise<ApiResponse<void>> => {
const startTime = apiLogger.logRequest('DELETE', `${BASE_URL}/item-master/surface-treatments/${id}`);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/surface-treatments/${id}`, {
method: 'DELETE',
headers,
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<void> = await response.json();
apiLogger.logResponse('DELETE', `${BASE_URL}/item-master/surface-treatments/${id}`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('DELETE', `${BASE_URL}/item-master/surface-treatments/${id}`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('DELETE', `${BASE_URL}/item-master/surface-treatments/${id}`, error as Error, undefined, startTime);
throw error;
}
},
},
};

View File

@@ -376,9 +376,9 @@ export const transformUnitOptionResponse = (
): { id: string; value: string; label: string; isActive: boolean } => {
return {
id: response.id.toString(), // number → string 변환
value: response.value,
label: response.label,
isActive: true, // API에 없으므로 기본값
value: response.unit_code,
label: response.unit_name,
isActive: response.is_active,
};
};