feat: API 프록시 추가 및 품목기준관리 기능 개선
- HttpOnly 쿠키 기반 API 프록시 라우트 추가 (/api/proxy/[...path]) - 품목기준관리 컴포넌트 개선 (섹션, 필드, 다이얼로그) - ItemMasterContext API 연동 강화 - mock-data 제거 및 실제 API 연동 - 문서 명명규칙 정리 ([TYPE-DATE] 형식) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -242,20 +242,23 @@ export interface ItemFieldProperty {
|
||||
columnNames?: string[]; // 각 컬럼의 이름
|
||||
}
|
||||
|
||||
// 항목 마스터 (재사용 가능한 항목 템플릿) - API 응답 구조에 맞춰 수정
|
||||
// 항목 마스터 (재사용 가능한 항목 템플릿) - MasterFieldResponse와 정확히 일치
|
||||
export interface ItemMasterField {
|
||||
id: number; // 서버 생성 ID (string → number)
|
||||
tenant_id?: number; // 백엔드에서 자동 추가
|
||||
field_name: string; // 항목명 (name → field_name)
|
||||
field_type: 'TEXT' | 'NUMBER' | 'DATE' | 'SELECT' | 'TEXTAREA' | 'CHECKBOX'; // 필드 타입 추가
|
||||
category?: string | null; // 카테고리 (사용 용도 구분용)
|
||||
description?: string | null; // 설명
|
||||
default_validation?: Record<string, any> | null; // 기본 검증 규칙 (JSON)
|
||||
default_properties?: Record<string, any> | null; // 기본 속성 (JSON, property/properties → default_properties)
|
||||
created_by?: number | null; // 생성자 ID 추가
|
||||
updated_by?: number | null; // 수정자 ID 추가
|
||||
created_at: string; // 생성일 (createdAt → created_at)
|
||||
updated_at: string; // 수정일 추가
|
||||
id: number;
|
||||
tenant_id: number;
|
||||
field_name: string;
|
||||
field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea'; // API와 동일
|
||||
category: string | null;
|
||||
description: string | null;
|
||||
is_common: boolean; // 공통 필드 여부
|
||||
default_value: string | null; // 기본값
|
||||
options: Array<{ label: string; value: string }> | null; // dropdown 옵션
|
||||
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;
|
||||
}
|
||||
|
||||
// 조건부 표시 설정
|
||||
@@ -336,7 +339,7 @@ export interface ItemPage {
|
||||
id: number; // 서버 생성 ID (string → number)
|
||||
tenant_id?: number; // 백엔드에서 자동 추가
|
||||
page_name: string; // 페이지명 (camelCase → snake_case)
|
||||
item_type: string; // 품목유형
|
||||
item_type: 'FG' | 'PT' | 'SM' | 'RM' | 'CS'; // 품목유형
|
||||
description?: string | null; // 설명 추가
|
||||
absolute_path: string; // 절대경로 (camelCase → snake_case)
|
||||
is_active: boolean; // 사용 여부 (camelCase → snake_case)
|
||||
@@ -348,19 +351,37 @@ export interface ItemPage {
|
||||
sections: ItemSection[]; // 페이지에 포함된 섹션들 (Nested)
|
||||
}
|
||||
|
||||
// 섹션 템플릿 (재사용 가능한 섹션) - API 응답 구조에 맞춰 수정
|
||||
// 템플릿 필드 (로컬 관리용 - API에서 제공하지 않음)
|
||||
export interface TemplateField {
|
||||
id: string;
|
||||
name: string;
|
||||
fieldKey: string;
|
||||
property: {
|
||||
inputType: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea';
|
||||
required: boolean;
|
||||
options?: string[];
|
||||
multiColumn?: boolean;
|
||||
columnCount?: number;
|
||||
columnNames?: string[];
|
||||
};
|
||||
description?: string;
|
||||
}
|
||||
|
||||
// 섹션 템플릿 (재사용 가능한 섹션) - Transformer 출력과 UI 요구사항에 맞춤
|
||||
export interface SectionTemplate {
|
||||
id: number; // 서버 생성 ID (string → number)
|
||||
tenant_id?: number; // 백엔드에서 자동 추가
|
||||
template_name: string; // 템플릿명 (title → template_name)
|
||||
section_type: 'BASIC' | 'BOM' | 'CUSTOM'; // 섹션 타입 (type → section_type, 값 변경)
|
||||
description?: string | null; // 설명
|
||||
default_fields?: Record<string, any> | null; // 기본 필드 설정 (JSON)
|
||||
bomItems?: BOMItem[]; // BOM 섹션의 BOM 아이템 목록
|
||||
created_by?: number | null; // 생성자 ID 추가
|
||||
updated_by?: number | null; // 수정자 ID 추가
|
||||
created_at: string; // 생성일 (createdAt → created_at)
|
||||
updated_at: string; // 수정일 (updatedAt → updated_at, required)
|
||||
id: number;
|
||||
tenant_id: number;
|
||||
template_name: string; // transformer가 title → template_name으로 변환
|
||||
section_type: 'BASIC' | 'BOM' | 'CUSTOM'; // transformer가 type → section_type으로 변환
|
||||
description: string | null;
|
||||
default_fields: TemplateField[] | null; // 기본 필드 (로컬 관리)
|
||||
category?: string[]; // 적용 카테고리 (로컬 관리)
|
||||
fields?: TemplateField[]; // 템플릿에 포함된 필드 (로컬 관리)
|
||||
bomItems?: BOMItem[]; // BOM 타입일 경우 BOM 품목 (로컬 관리)
|
||||
created_by: number | null;
|
||||
updated_by: number | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
// ===== Context Type =====
|
||||
@@ -385,19 +406,22 @@ interface ItemMasterContextType {
|
||||
|
||||
// 품목기준관리 - 마스터 항목
|
||||
itemMasterFields: ItemMasterField[];
|
||||
loadItemMasterFields: (fields: ItemMasterField[]) => void; // 초기 데이터 로딩용 (API 호출 없음)
|
||||
addItemMasterField: (field: Omit<ItemMasterField, 'id' | 'created_at' | 'updated_at'>) => Promise<void>;
|
||||
updateItemMasterField: (id: number, updates: Partial<ItemMasterField>) => Promise<void>;
|
||||
deleteItemMasterField: (id: number) => Promise<void>;
|
||||
|
||||
// 품목기준관리 - 섹션 템플릿
|
||||
sectionTemplates: SectionTemplate[];
|
||||
loadSectionTemplates: (templates: SectionTemplate[]) => void; // 초기 데이터 로딩용 (API 호출 없음)
|
||||
addSectionTemplate: (template: Omit<SectionTemplate, 'id' | 'created_at' | 'updated_at'>) => Promise<void>;
|
||||
updateSectionTemplate: (id: number, updates: Partial<SectionTemplate>) => Promise<void>;
|
||||
deleteSectionTemplate: (id: number) => Promise<void>;
|
||||
|
||||
// 품목기준관리 계층구조
|
||||
itemPages: ItemPage[];
|
||||
addItemPage: (page: Omit<ItemPage, 'id' | 'created_at' | 'updated_at'>) => Promise<void>;
|
||||
loadItemPages: (pages: ItemPage[]) => void; // 초기 데이터 로딩용 (API 호출 없음)
|
||||
addItemPage: (page: Omit<ItemPage, 'id' | 'created_at' | 'updated_at'>) => Promise<ItemPage>;
|
||||
updateItemPage: (id: number, updates: Partial<ItemPage>) => Promise<void>;
|
||||
deleteItemPage: (id: number) => Promise<void>;
|
||||
reorderPages: (newOrder: Array<{ id: number; order_no: number }>) => Promise<void>;
|
||||
@@ -455,6 +479,9 @@ interface ItemMasterContextType {
|
||||
// 캐시 및 데이터 초기화
|
||||
clearCache: () => void; // TenantAwareCache 정리
|
||||
resetAllData: () => void; // 모든 state 초기화
|
||||
|
||||
// 테넌트 정보
|
||||
tenantId: number | undefined; // 현재 로그인한 사용자의 테넌트 ID
|
||||
}
|
||||
|
||||
// Create context
|
||||
@@ -1056,19 +1083,27 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
};
|
||||
|
||||
// ItemMasterField CRUD (임시: 로컬 state)
|
||||
/**
|
||||
* 초기 데이터 로딩용: API 호출 없이 마스터 필드를 state에 로드 (덮어쓰기)
|
||||
*/
|
||||
const loadItemMasterFields = (fields: ItemMasterField[]) => {
|
||||
setItemMasterFields(fields);
|
||||
console.log('[ItemMasterContext] 마스터 필드 로드 완료:', fields.length);
|
||||
};
|
||||
|
||||
const addItemMasterField = async (field: Omit<ItemMasterField, 'id' | 'created_at' | 'updated_at'>) => {
|
||||
try {
|
||||
// API 호출
|
||||
const response = await itemMasterApi.masterFields.create({
|
||||
field_name: field.field_name,
|
||||
field_type: field.field_type,
|
||||
category: field.category,
|
||||
description: field.description,
|
||||
category: field.category ?? undefined,
|
||||
description: field.description ?? undefined,
|
||||
is_common: field.is_common,
|
||||
default_value: field.default_value,
|
||||
options: field.options,
|
||||
validation_rules: field.validation_rules,
|
||||
properties: field.properties,
|
||||
default_value: field.default_value ?? undefined,
|
||||
options: field.options ?? undefined,
|
||||
validation_rules: field.validation_rules ?? undefined,
|
||||
properties: field.properties ?? undefined,
|
||||
});
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
@@ -1088,6 +1123,8 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
options: response.data.options,
|
||||
validation_rules: response.data.validation_rules,
|
||||
properties: response.data.properties,
|
||||
created_by: response.data.created_by,
|
||||
updated_by: response.data.updated_by,
|
||||
created_at: response.data.created_at,
|
||||
updated_at: response.data.updated_at,
|
||||
};
|
||||
@@ -1166,28 +1203,50 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
};
|
||||
|
||||
// SectionTemplate CRUD with API
|
||||
/**
|
||||
* 초기 데이터 로딩용: API 호출 없이 섹션 템플릿을 state에 로드 (덮어쓰기)
|
||||
*/
|
||||
const loadSectionTemplates = (templates: SectionTemplate[]) => {
|
||||
setSectionTemplates(templates);
|
||||
console.log('[ItemMasterContext] 섹션 템플릿 로드 완료:', templates.length);
|
||||
};
|
||||
|
||||
const addSectionTemplate = async (template: Omit<SectionTemplate, 'id' | 'created_at' | 'updated_at'>) => {
|
||||
try {
|
||||
// API 호출
|
||||
// 프론트엔드 형식 → API 형식 변환
|
||||
// template_name → title, section_type → type
|
||||
const apiType = template.section_type === 'BOM' ? 'bom' : 'fields';
|
||||
|
||||
const response = await itemMasterApi.templates.create({
|
||||
title: template.title,
|
||||
type: template.type,
|
||||
description: template.description,
|
||||
is_default: template.is_default,
|
||||
title: template.template_name,
|
||||
type: apiType,
|
||||
description: template.description ?? undefined,
|
||||
is_default: false, // 기본값
|
||||
});
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
throw new Error(response.message || '섹션 템플릿 생성 실패');
|
||||
}
|
||||
|
||||
// 응답 데이터 변환 및 state 업데이트
|
||||
// API 응답 → 프론트엔드 형식 변환
|
||||
// title → template_name, type → section_type
|
||||
const SECTION_TYPE_MAP: Record<string, 'BASIC' | 'BOM' | 'CUSTOM'> = {
|
||||
fields: 'BASIC',
|
||||
bom: 'BOM',
|
||||
};
|
||||
|
||||
const newTemplate: SectionTemplate = {
|
||||
id: response.data.id,
|
||||
tenant_id: response.data.tenant_id,
|
||||
title: response.data.title,
|
||||
type: response.data.type,
|
||||
template_name: response.data.title,
|
||||
section_type: SECTION_TYPE_MAP[response.data.type] || 'BASIC',
|
||||
description: response.data.description,
|
||||
is_default: response.data.is_default,
|
||||
default_fields: null,
|
||||
category: template.category,
|
||||
fields: template.fields,
|
||||
bomItems: template.bomItems,
|
||||
created_by: response.data.created_by,
|
||||
updated_by: response.data.updated_by,
|
||||
created_at: response.data.created_at,
|
||||
updated_at: response.data.updated_at,
|
||||
};
|
||||
@@ -1203,32 +1262,79 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
const updateSectionTemplate = async (id: number, updates: Partial<SectionTemplate>) => {
|
||||
try {
|
||||
// API 호출
|
||||
const requestData: any = {};
|
||||
if (updates.title !== undefined) requestData.title = updates.title;
|
||||
if (updates.type !== undefined) requestData.type = updates.type;
|
||||
if (updates.description !== undefined) requestData.description = updates.description;
|
||||
if (updates.is_default !== undefined) requestData.is_default = updates.is_default;
|
||||
// default_fields, fields, category, bomItems는 로컬에서만 관리 (API 미지원)
|
||||
const localOnlyUpdates = ['default_fields', 'fields', 'category', 'bomItems'];
|
||||
const hasApiUpdates = Object.keys(updates).some(key => !localOnlyUpdates.includes(key));
|
||||
const hasLocalUpdates = Object.keys(updates).some(key => localOnlyUpdates.includes(key));
|
||||
|
||||
const response = await itemMasterApi.templates.update(id, requestData);
|
||||
// API 호출이 필요한 경우에만 API 요청
|
||||
if (hasApiUpdates) {
|
||||
// API 요청 형식으로 변환 (frontend → API)
|
||||
// frontend: template_name, section_type
|
||||
// API: title, type
|
||||
const requestData: any = {};
|
||||
if (updates.template_name !== undefined) requestData.title = updates.template_name;
|
||||
if (updates.section_type !== undefined) {
|
||||
// section_type 변환: 'BASIC' | 'CUSTOM' → 'fields', 'BOM' → 'bom'
|
||||
requestData.type = updates.section_type === 'BOM' ? 'bom' : 'fields';
|
||||
}
|
||||
if (updates.description !== undefined) requestData.description = updates.description;
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
throw new Error(response.message || '섹션 템플릿 수정 실패');
|
||||
const response = await itemMasterApi.templates.update(id, requestData);
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
throw new Error(response.message || '섹션 템플릿 수정 실패');
|
||||
}
|
||||
|
||||
// state 업데이트 (API 응답 → frontend 형식으로 변환)
|
||||
// API 응답: title, type ('fields' | 'bom')
|
||||
// Frontend 형식: template_name, section_type ('BASIC' | 'BOM' | 'CUSTOM')
|
||||
const SECTION_TYPE_MAP: Record<string, 'BASIC' | 'BOM' | 'CUSTOM'> = {
|
||||
fields: 'BASIC',
|
||||
bom: 'BOM',
|
||||
};
|
||||
|
||||
setSectionTemplates(prev => prev.map(template =>
|
||||
template.id === id ? {
|
||||
...template,
|
||||
template_name: response.data!.title,
|
||||
section_type: SECTION_TYPE_MAP[response.data!.type] || 'BASIC',
|
||||
description: response.data!.description,
|
||||
updated_at: response.data!.updated_at,
|
||||
} : template
|
||||
));
|
||||
|
||||
console.log('[ItemMasterContext] 섹션 템플릿 수정 성공 (API):', id);
|
||||
}
|
||||
|
||||
// state 업데이트
|
||||
setSectionTemplates(prev => prev.map(template =>
|
||||
template.id === id ? {
|
||||
...template,
|
||||
title: response.data!.title,
|
||||
type: response.data!.type,
|
||||
description: response.data!.description,
|
||||
is_default: response.data!.is_default,
|
||||
updated_at: response.data!.updated_at,
|
||||
} : template
|
||||
));
|
||||
// 로컬 전용 필드 업데이트 (default_fields, fields, category, bomItems)
|
||||
if (hasLocalUpdates) {
|
||||
setSectionTemplates(prev => prev.map(template => {
|
||||
if (template.id !== id) return template;
|
||||
|
||||
console.log('[ItemMasterContext] 섹션 템플릿 수정 성공:', id);
|
||||
const updatedTemplate = { ...template };
|
||||
|
||||
// default_fields 업데이트 시 fields도 같이 업데이트
|
||||
if (updates.default_fields !== undefined) {
|
||||
updatedTemplate.default_fields = updates.default_fields;
|
||||
updatedTemplate.fields = updates.default_fields as TemplateField[];
|
||||
}
|
||||
if (updates.fields !== undefined) {
|
||||
updatedTemplate.fields = updates.fields;
|
||||
updatedTemplate.default_fields = updates.fields;
|
||||
}
|
||||
if (updates.category !== undefined) {
|
||||
updatedTemplate.category = updates.category;
|
||||
}
|
||||
if (updates.bomItems !== undefined) {
|
||||
updatedTemplate.bomItems = updates.bomItems;
|
||||
}
|
||||
|
||||
return updatedTemplate;
|
||||
}));
|
||||
|
||||
console.log('[ItemMasterContext] 섹션 템플릿 수정 성공 (로컬):', id, Object.keys(updates).filter(k => localOnlyUpdates.includes(k)));
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error);
|
||||
console.error('[ItemMasterContext] 섹션 템플릿 수정 실패:', errorMessage);
|
||||
@@ -1256,7 +1362,22 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
};
|
||||
|
||||
// ItemPage CRUD with API
|
||||
const addItemPage = async (pageData: Omit<ItemPage, 'id' | 'created_at' | 'updated_at'>) => {
|
||||
|
||||
/**
|
||||
* 초기 데이터 로딩용: API 호출 없이 페이지 데이터를 state에 로드 (덮어쓰기)
|
||||
* (이미 백엔드에서 받아온 데이터를 로드할 때 사용)
|
||||
*/
|
||||
const loadItemPages = (pages: ItemPage[]) => {
|
||||
setItemPages(pages); // 덮어쓰기 (append가 아님!)
|
||||
console.log('[ItemMasterContext] 페이지 데이터 로드 완료:', pages.length);
|
||||
};
|
||||
|
||||
/**
|
||||
* 새 페이지 생성: API 호출 + state 업데이트
|
||||
* (사용자가 새 페이지를 만들 때 사용)
|
||||
* @returns 생성된 페이지 반환
|
||||
*/
|
||||
const addItemPage = async (pageData: Omit<ItemPage, 'id' | 'created_at' | 'updated_at'>): Promise<ItemPage> => {
|
||||
try {
|
||||
// API 요청 데이터 변환
|
||||
const requestData: ItemPageRequest = {
|
||||
@@ -1268,15 +1389,12 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
// API 호출
|
||||
const response = await itemMasterApi.pages.create(requestData);
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
throw new Error(response.message || '페이지 생성 실패');
|
||||
}
|
||||
|
||||
// 응답 데이터 변환 및 state 업데이트
|
||||
const newPage = transformPageResponse(response.data);
|
||||
const newPage = transformPageResponse(response);
|
||||
setItemPages(prev => [...prev, newPage]);
|
||||
|
||||
console.log('[ItemMasterContext] 페이지 생성 성공:', newPage);
|
||||
return newPage;
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error);
|
||||
console.error('[ItemMasterContext] 페이지 생성 실패:', errorMessage);
|
||||
@@ -1333,8 +1451,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
const reorderPages = async (newOrder: Array<{ id: number; order_no: number }>) => {
|
||||
try {
|
||||
// Optimistic UI 업데이트 (즉시 반영)
|
||||
const previousPages = [...itemPages];
|
||||
|
||||
setItemPages(prev => {
|
||||
const updated = [...prev];
|
||||
updated.sort((a, b) => {
|
||||
@@ -1456,7 +1572,7 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
try {
|
||||
// API 호출
|
||||
const response = await itemMasterApi.sections.reorder(pageId, {
|
||||
items: sectionIds.map((id, index) => ({ id, order_no: index }))
|
||||
section_orders: sectionIds.map((id, index) => ({ id, order_no: index }))
|
||||
});
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
@@ -1482,17 +1598,17 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
// Field CRUD with API
|
||||
const addFieldToSection = async (sectionId: number, fieldData: Omit<ItemField, 'id' | 'created_at' | 'updated_at'>) => {
|
||||
try {
|
||||
// API 호출
|
||||
// API 호출 (null → undefined 변환)
|
||||
const response = await itemMasterApi.fields.create(sectionId, {
|
||||
field_name: fieldData.field_name,
|
||||
field_type: fieldData.field_type,
|
||||
is_required: fieldData.is_required,
|
||||
default_value: fieldData.default_value,
|
||||
placeholder: fieldData.placeholder,
|
||||
display_condition: fieldData.display_condition,
|
||||
validation_rules: fieldData.validation_rules,
|
||||
options: fieldData.options,
|
||||
properties: fieldData.properties,
|
||||
default_value: fieldData.default_value ?? undefined,
|
||||
placeholder: fieldData.placeholder ?? undefined,
|
||||
display_condition: fieldData.display_condition ?? undefined,
|
||||
validation_rules: fieldData.validation_rules ?? undefined,
|
||||
options: fieldData.options ?? undefined,
|
||||
properties: fieldData.properties ?? undefined,
|
||||
});
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
@@ -1538,17 +1654,20 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
const updateField = async (fieldId: number, updates: Partial<ItemField>) => {
|
||||
try {
|
||||
// API 호출
|
||||
// API 호출 (null → undefined 변환)
|
||||
// 주의: display_condition은 현재 백엔드에서 지원하는 형식과 프론트엔드 형식이 다르므로 API에 전송하지 않음
|
||||
// 백엔드 형식: {"field_id": "1", "operator": "equals", "value": "true"}
|
||||
// 프론트엔드 형식: {"targetType": "field", "fieldConditions": [...], "sectionIds": [...]}
|
||||
const requestData: any = {};
|
||||
if (updates.field_name !== undefined) requestData.field_name = updates.field_name;
|
||||
if (updates.field_type !== undefined) requestData.field_type = updates.field_type;
|
||||
if (updates.is_required !== undefined) requestData.is_required = updates.is_required;
|
||||
if (updates.default_value !== undefined) requestData.default_value = updates.default_value;
|
||||
if (updates.placeholder !== undefined) requestData.placeholder = updates.placeholder;
|
||||
if (updates.display_condition !== undefined) requestData.display_condition = updates.display_condition;
|
||||
if (updates.validation_rules !== undefined) requestData.validation_rules = updates.validation_rules;
|
||||
if (updates.options !== undefined) requestData.options = updates.options;
|
||||
if (updates.properties !== undefined) requestData.properties = updates.properties;
|
||||
if (updates.default_value !== undefined) requestData.default_value = updates.default_value ?? undefined;
|
||||
if (updates.placeholder !== undefined) requestData.placeholder = updates.placeholder ?? undefined;
|
||||
// display_condition은 API 형식 불일치로 전송하지 않음 (로컬 상태에서만 관리)
|
||||
if (updates.validation_rules !== undefined) requestData.validation_rules = updates.validation_rules ?? undefined;
|
||||
if (updates.options !== undefined) requestData.options = updates.options ?? undefined;
|
||||
if (updates.properties !== undefined) requestData.properties = updates.properties ?? undefined;
|
||||
|
||||
const response = await itemMasterApi.fields.update(fieldId, requestData);
|
||||
|
||||
@@ -1617,7 +1736,7 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
try {
|
||||
// API 호출
|
||||
const response = await itemMasterApi.fields.reorder(sectionId, {
|
||||
items: fieldIds.map((id, index) => ({ id, order_no: index }))
|
||||
field_orders: fieldIds.map((id, index) => ({ id, order_no: index }))
|
||||
});
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
@@ -1664,16 +1783,16 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
// BOM CRUD with API
|
||||
const addBOMItem = async (sectionId: number, bomData: Omit<BOMItem, 'id' | 'created_at' | 'updated_at'>) => {
|
||||
try {
|
||||
// API 호출
|
||||
// API 호출 (null → undefined 변환)
|
||||
const response = await itemMasterApi.bomItems.create(sectionId, {
|
||||
item_code: bomData.item_code,
|
||||
item_code: bomData.item_code ?? undefined,
|
||||
item_name: bomData.item_name,
|
||||
quantity: bomData.quantity,
|
||||
unit: bomData.unit,
|
||||
unit_price: bomData.unit_price,
|
||||
total_price: bomData.total_price,
|
||||
spec: bomData.spec,
|
||||
note: bomData.note,
|
||||
unit: bomData.unit ?? undefined,
|
||||
unit_price: bomData.unit_price ?? undefined,
|
||||
total_price: bomData.total_price ?? undefined,
|
||||
spec: bomData.spec ?? undefined,
|
||||
note: bomData.note ?? undefined,
|
||||
});
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
@@ -1716,16 +1835,16 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
const updateBOMItem = async (bomId: number, updates: Partial<BOMItem>) => {
|
||||
try {
|
||||
// API 호출
|
||||
// API 호출 (null → undefined 변환)
|
||||
const requestData: any = {};
|
||||
if (updates.item_code !== undefined) requestData.item_code = updates.item_code;
|
||||
if (updates.item_code !== undefined) requestData.item_code = updates.item_code ?? undefined;
|
||||
if (updates.item_name !== undefined) requestData.item_name = updates.item_name;
|
||||
if (updates.quantity !== undefined) requestData.quantity = updates.quantity;
|
||||
if (updates.unit !== undefined) requestData.unit = updates.unit;
|
||||
if (updates.unit_price !== undefined) requestData.unit_price = updates.unit_price;
|
||||
if (updates.total_price !== undefined) requestData.total_price = updates.total_price;
|
||||
if (updates.spec !== undefined) requestData.spec = updates.spec;
|
||||
if (updates.note !== undefined) requestData.note = updates.note;
|
||||
if (updates.unit !== undefined) requestData.unit = updates.unit ?? undefined;
|
||||
if (updates.unit_price !== undefined) requestData.unit_price = updates.unit_price ?? undefined;
|
||||
if (updates.total_price !== undefined) requestData.total_price = updates.total_price ?? undefined;
|
||||
if (updates.spec !== undefined) requestData.spec = updates.spec ?? undefined;
|
||||
if (updates.note !== undefined) requestData.note = updates.note ?? undefined;
|
||||
|
||||
const response = await itemMasterApi.bomItems.update(bomId, requestData);
|
||||
|
||||
@@ -1837,16 +1956,19 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
deleteMaterialItemName,
|
||||
|
||||
itemMasterFields,
|
||||
loadItemMasterFields,
|
||||
addItemMasterField,
|
||||
updateItemMasterField,
|
||||
deleteItemMasterField,
|
||||
|
||||
sectionTemplates,
|
||||
loadSectionTemplates,
|
||||
addSectionTemplate,
|
||||
updateSectionTemplate,
|
||||
deleteSectionTemplate,
|
||||
|
||||
itemPages,
|
||||
loadItemPages,
|
||||
addItemPage,
|
||||
updateItemPage,
|
||||
deleteItemPage,
|
||||
@@ -1902,6 +2024,8 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
clearCache,
|
||||
resetAllData,
|
||||
|
||||
tenantId,
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user