[feat]: Item Master 데이터 관리 기능 구현 및 타입 에러 수정

- ItemMasterDataManagement 컴포넌트 구조화 (tabs, dialogs, components 분리)
- HierarchyTab 타입 에러 수정 (BOMItem section_id, updated_at 추가)
- API 클라이언트 구현 (item-master.ts, 13개 엔드포인트)
- ItemMasterContext 구현 (상태 관리 및 데이터 흐름)
- 백엔드 요구사항 문서 작성 (CORS 설정, API 스펙 등)
- SSR 호환성 수정 (navigator API typeof window 체크)
- 미사용 변수 ESLint 에러 해결
- Context 리팩토링 (AuthContext, RootProvider 추가)
- API 유틸리티 추가 (error-handler, logger, transformers)

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
byeongcheolryu
2025-11-23 16:10:27 +09:00
parent 63f5df7d7d
commit df3db155dd
69 changed files with 31467 additions and 4796 deletions

View File

@@ -0,0 +1,412 @@
// 품목기준관리 API 타입 정의
// API 응답 기준 snake_case 사용
// ============================================
// 공통 타입
// ============================================
/**
* 표준 API 응답 래퍼
*/
export interface ApiResponse<T> {
success: boolean;
message: string;
data: T;
}
/**
* 페이지네이션 메타데이터
*/
export interface PaginationMeta {
current_page: number;
per_page: number;
total: number;
last_page: number;
}
// ============================================
// 초기화 API
// ============================================
/**
* 초기화 API 응답 - 화면 진입 시 전체 데이터 로드
* GET /v1/item-master/init
*/
export interface InitResponse {
pages: ItemPageResponse[];
sectionTemplates: SectionTemplateResponse[];
masterFields: MasterFieldResponse[];
customTabs: CustomTabResponse[];
tabColumns: Record<number, TabColumnResponse[]>; // tab_id를 key로 사용
unitOptions: UnitOptionResponse[];
}
// ============================================
// 페이지 관리
// ============================================
/**
* 페이지 생성/수정 요청
* POST /v1/item-master/pages
* PUT /v1/item-master/pages/{id}
*/
export interface ItemPageRequest {
page_name: string;
item_type: 'FG' | 'PT' | 'SM' | 'RM' | 'CS';
absolute_path?: string;
is_active?: boolean;
}
/**
* 페이지 응답
*/
export interface ItemPageResponse {
id: number;
tenant_id: number;
page_name: string;
item_type: string;
absolute_path: string | null;
is_active: boolean;
created_by: number | null;
updated_by: number | null;
created_at: string;
updated_at: string;
sections?: ItemSectionResponse[]; // Nested 조회 시 포함
}
/**
* 페이지 순서 변경 요청
* PUT /v1/item-master/pages/reorder (향후 구현 가능성)
*/
export interface PageReorderRequest {
page_orders: Array<{
id: number;
order_no: number;
}>;
}
// ============================================
// 섹션 관리
// ============================================
/**
* 섹션 생성/수정 요청
* POST /v1/item-master/pages/{pageId}/sections
* PUT /v1/item-master/sections/{id}
*/
export interface ItemSectionRequest {
title: string;
type: 'fields' | 'bom';
}
/**
* 섹션 응답
*/
export interface ItemSectionResponse {
id: number;
tenant_id: number;
page_id: number;
title: string;
type: 'fields' | 'bom';
order_no: number;
created_by: number | null;
updated_by: number | null;
created_at: string;
updated_at: string;
fields?: ItemFieldResponse[]; // Nested 조회 시 포함
bomItems?: BomItemResponse[]; // Nested 조회 시 포함
}
/**
* 섹션 순서 변경 요청
* PUT /v1/item-master/pages/{pageId}/sections/reorder
*/
export interface SectionReorderRequest {
section_orders: Array<{
id: number;
order_no: number;
}>;
}
// ============================================
// 필드 관리
// ============================================
/**
* 필드 생성/수정 요청
* POST /v1/item-master/sections/{sectionId}/fields
* PUT /v1/item-master/fields/{id}
*/
export interface ItemFieldRequest {
field_name: string;
field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea';
is_required?: boolean;
placeholder?: string;
default_value?: string;
display_condition?: Record<string, any>; // {"field_id": "1", "operator": "equals", "value": "true"}
validation_rules?: Record<string, any>; // {"min": 0, "max": 100, "pattern": "regex"}
options?: Array<{ label: string; value: string }>; // dropdown 옵션
properties?: Record<string, any>; // {"unit": "mm", "precision": 2, "format": "YYYY-MM-DD"}
}
/**
* 필드 응답
*/
export interface ItemFieldResponse {
id: number;
tenant_id: number;
section_id: number;
field_name: string;
field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea';
order_no: number;
is_required: boolean;
placeholder: string | null;
default_value: string | null;
display_condition: Record<string, any> | null;
validation_rules: Record<string, any> | null;
options: Array<{ label: string; value: string }> | null;
properties: Record<string, any> | null;
created_by: number | null;
updated_by: number | null;
created_at: string;
updated_at: string;
}
/**
* 필드 순서 변경 요청
* PUT /v1/item-master/sections/{sectionId}/fields/reorder
*/
export interface FieldReorderRequest {
field_orders: Array<{
id: number;
order_no: number;
}>;
}
// ============================================
// BOM 관리
// ============================================
/**
* BOM 항목 생성/수정 요청
* POST /v1/item-master/sections/{sectionId}/bom-items
* PUT /v1/item-master/bom-items/{id}
*/
export interface BomItemRequest {
item_code?: string;
item_name: string;
quantity: number;
unit?: string;
unit_price?: number;
total_price?: number;
spec?: string;
note?: string;
}
/**
* BOM 항목 응답
*/
export interface BomItemResponse {
id: number;
tenant_id: number;
section_id: number;
item_code: string | null;
item_name: string;
quantity: number;
unit: string | null;
unit_price: number | null;
total_price: number | null;
spec: string | null;
note: string | null;
created_by: number | null;
updated_by: number | null;
created_at: string;
updated_at: string;
}
// ============================================
// 섹션 템플릿
// ============================================
/**
* 섹션 템플릿 생성/수정 요청
* POST /v1/item-master/section-templates
* PUT /v1/item-master/section-templates/{id}
*/
export interface SectionTemplateRequest {
title: string;
type: 'fields' | 'bom';
description?: string;
is_default?: boolean;
}
/**
* 섹션 템플릿 응답
*/
export interface SectionTemplateResponse {
id: number;
tenant_id: number;
title: string;
type: 'fields' | 'bom';
description: string | null;
is_default: boolean;
created_by: number | null;
updated_by: number | null;
created_at: string;
updated_at: string;
}
// ============================================
// 마스터 필드
// ============================================
/**
* 마스터 필드 생성/수정 요청
* POST /v1/item-master/master-fields
* PUT /v1/item-master/master-fields/{id}
*/
export interface MasterFieldRequest {
field_name: string;
field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea';
category?: string;
description?: string;
is_common?: boolean;
default_value?: string;
options?: Array<{ label: string; value: string }>;
validation_rules?: Record<string, any>;
properties?: Record<string, any>;
}
/**
* 마스터 필드 응답
*/
export interface MasterFieldResponse {
id: number;
tenant_id: number;
field_name: string;
field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea';
category: string | null;
description: string | null;
is_common: boolean;
default_value: string | null;
options: Array<{ label: string; value: string }> | null;
validation_rules: Record<string, any> | null;
properties: Record<string, any> | null;
created_by: number | null;
updated_by: number | null;
created_at: string;
updated_at: string;
}
// ============================================
// 커스텀 탭
// ============================================
/**
* 커스텀 탭 생성/수정 요청
* POST /v1/item-master/custom-tabs
* PUT /v1/item-master/custom-tabs/{id}
*/
export interface CustomTabRequest {
label: string;
icon?: string;
is_default?: boolean;
}
/**
* 커스텀 탭 응답
*/
export interface CustomTabResponse {
id: number;
tenant_id: number;
label: string;
icon: string | null;
is_default: boolean;
order_no: number;
created_by: number | null;
updated_by: number | null;
created_at: string;
updated_at: string;
}
/**
* 탭 순서 변경 요청
* PUT /v1/item-master/custom-tabs/reorder
*/
export interface TabReorderRequest {
tab_orders: Array<{
id: number;
order_no: number;
}>;
}
/**
* 탭 컬럼 설정 업데이트 요청
* PUT /v1/item-master/custom-tabs/{id}/columns
*/
export interface TabColumnUpdateRequest {
columns: Array<{
key: string;
label: string;
visible: boolean;
order: number;
}>;
}
/**
* 탭 컬럼 응답
*/
export interface TabColumnResponse {
key: string;
label: string;
visible: boolean;
order: number;
}
// ============================================
// 단위 옵션
// ============================================
/**
* 단위 옵션 생성 요청
* POST /v1/item-master/units
*/
export interface UnitOptionRequest {
label: string;
value: string;
}
/**
* 단위 옵션 응답
*/
export interface UnitOptionResponse {
id: number;
tenant_id: number;
label: string;
value: string;
created_by: number | null;
created_at: string;
updated_at: string;
}
// ============================================
// 에러 타입
// ============================================
/**
* API 에러 응답
*/
export interface ApiErrorResponse {
success: false;
message: string;
errors?: Record<string, string[]>; // Validation 에러
}
/**
* API 에러 클래스용 타입
*/
export interface ApiErrorData {
status: number;
message: string;
errors?: Record<string, string[]>;
}