/** * 품목기준관리 API 클라이언트 * * Laravel 백엔드와 통신하는 API 함수들 * 동적 페이지 구성, 버전 관리, 멀티테넌시 지원 */ import type { PageConfig, PageConfigRevision, PageType, DynamicFormData, FetchPageConfigParams, MasterDataApiResponse, VersionComparisonResult, } from '@/types/master-data'; // ===== 환경 변수 ===== const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; // ===== 유틸리티 함수 ===== /** * 인증 토큰 가져오기 */ function getAuthToken(): string | null { if (typeof window !== 'undefined') { return localStorage.getItem('auth_token'); } return null; } /** * Fetch 옵션 생성 */ function createFetchOptions(options: RequestInit = {}): RequestInit { const token = getAuthToken(); const headers: Record = { 'Content-Type': 'application/json', }; // Merge existing headers if they are a plain object if (options.headers && typeof options.headers === 'object' && !Array.isArray(options.headers)) { Object.assign(headers, options.headers); } if (token) { headers['Authorization'] = `Bearer ${token}`; } return { ...options, headers, credentials: 'include', }; } /** * API 에러 처리 */ async function handleApiResponse(response: Response): Promise { if (!response.ok) { const error = await response.json().catch(() => ({ message: 'API 요청 실패', })); throw new Error(error.message || `HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); return data; } // ===== 페이지 구성 CRUD ===== /** * 페이지 구성 목록 조회 * * @example * const configs = await fetchPageConfigs({ pageType: 'item-master' }); */ export async function fetchPageConfigs( params?: FetchPageConfigParams ): Promise { const queryParams = new URLSearchParams(); if (params) { Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== null) { queryParams.append(key, String(value)); } }); } const url = `${API_URL}/api/master-data/pages${queryParams.toString() ? `?${queryParams}` : ''}`; const response = await fetch(url, createFetchOptions()); const data = await handleApiResponse>(response); return data.data; } /** * 특정 페이지 타입의 최신 구성 조회 * * @param pageType - 페이지 타입 * * @example * const config = await fetchPageConfigByType('item-master'); */ export async function fetchPageConfigByType( pageType: PageType ): Promise { const response = await fetch( `${API_URL}/api/master-data/pages/${pageType}`, createFetchOptions() ); if (response.status === 404) { return null; } const data = await handleApiResponse>(response); return data.data; } /** * 페이지 구성 상세 조회 (특정 버전) * * @param id - 페이지 구성 ID * @param version - 버전 번호 (선택사항, 기본: 최신) * * @example * const config = await fetchPageConfigById('uuid', 2); */ export async function fetchPageConfigById( id: string, version?: number ): Promise { const url = version ? `${API_URL}/api/master-data/pages/${id}?version=${version}` : `${API_URL}/api/master-data/pages/${id}`; const response = await fetch(url, createFetchOptions()); const data = await handleApiResponse>(response); return data.data; } /** * 페이지 구성 생성 * * @param configData - 페이지 구성 데이터 * * @example * const newConfig = await createPageConfig({ * pageName: '품목기준정보', * pageType: 'item-master', * sections: [...], * isActive: true, * }); */ export async function createPageConfig( configData: Omit ): Promise { const response = await fetch( `${API_URL}/api/master-data/pages`, createFetchOptions({ method: 'POST', body: JSON.stringify(configData), }) ); const data = await handleApiResponse>(response); return data.data; } /** * 페이지 구성 수정 * * @param id - 페이지 구성 ID * @param updates - 수정할 필드들 * * @example * const updatedConfig = await updatePageConfig('uuid', { * sections: [...], * }); */ export async function updatePageConfig( id: string, updates: Partial ): Promise { const response = await fetch( `${API_URL}/api/master-data/pages/${id}`, createFetchOptions({ method: 'PUT', body: JSON.stringify(updates), }) ); const data = await handleApiResponse>(response); return data.data; } /** * 페이지 구성 삭제 * * @param id - 페이지 구성 ID */ export async function deletePageConfig(id: string): Promise { const response = await fetch( `${API_URL}/api/master-data/pages/${id}`, createFetchOptions({ method: 'DELETE', }) ); await handleApiResponse>(response); } // ===== 버전 관리 ===== /** * 페이지 구성 버전 이력 조회 * * @param pageConfigId - 페이지 구성 ID * * @example * const revisions = await fetchPageConfigRevisions('uuid'); */ export async function fetchPageConfigRevisions( pageConfigId: string ): Promise { const response = await fetch( `${API_URL}/api/master-data/pages/${pageConfigId}/revisions`, createFetchOptions() ); const data = await handleApiResponse>(response); return data.data; } /** * 특정 버전 상세 조회 * * @param pageConfigId - 페이지 구성 ID * @param version - 버전 번호 */ export async function fetchPageConfigRevisionByVersion( pageConfigId: string, version: number ): Promise { const response = await fetch( `${API_URL}/api/master-data/pages/${pageConfigId}/revisions/${version}`, createFetchOptions() ); const data = await handleApiResponse>(response); return data.data; } /** * 버전 비교 * * @param pageConfigId - 페이지 구성 ID * @param version1 - 비교할 버전 1 * @param version2 - 비교할 버전 2 * * @example * const comparison = await comparePageConfigVersions('uuid', 1, 2); */ export async function comparePageConfigVersions( pageConfigId: string, version1: number, version2: number ): Promise { const response = await fetch( `${API_URL}/api/master-data/pages/${pageConfigId}/compare?v1=${version1}&v2=${version2}`, createFetchOptions() ); const data = await handleApiResponse>(response); return data.data; } /** * 버전 승인 * * @param revisionId - 버전 ID * * @example * await approvePageConfigRevision('revision-uuid'); */ export async function approvePageConfigRevision( revisionId: string ): Promise { const response = await fetch( `${API_URL}/api/master-data/revisions/${revisionId}/approve`, createFetchOptions({ method: 'POST', }) ); const data = await handleApiResponse>(response); return data.data; } /** * 특정 버전으로 롤백 * * @param pageConfigId - 페이지 구성 ID * @param version - 롤백할 버전 */ export async function rollbackPageConfig( pageConfigId: string, version: number ): Promise { const response = await fetch( `${API_URL}/api/master-data/pages/${pageConfigId}/rollback`, createFetchOptions({ method: 'POST', body: JSON.stringify({ version }), }) ); const data = await handleApiResponse>(response); return data.data; } // ===== 동적 폼 데이터 ===== /** * 동적 폼 데이터 조회 * * @param pageType - 페이지 타입 * @param id - 데이터 ID (선택사항) */ export async function fetchFormData( pageType: PageType, id?: string ): Promise { const url = id ? `${API_URL}/api/master-data/form-data/${pageType}/${id}` : `${API_URL}/api/master-data/form-data/${pageType}`; const response = await fetch(url, createFetchOptions()); const data = await handleApiResponse>(response); return data.data; } /** * 동적 폼 데이터 저장 * * @param formData - 폼 데이터 */ export async function saveFormData( formData: DynamicFormData ): Promise { const response = await fetch( `${API_URL}/api/master-data/form-data`, createFetchOptions({ method: 'POST', body: JSON.stringify(formData), }) ); const data = await handleApiResponse>(response); return data.data; } /** * 동적 폼 데이터 수정 * * @param id - 데이터 ID * @param updates - 수정할 필드들 */ export async function updateFormData( id: string, updates: Partial ): Promise { const response = await fetch( `${API_URL}/api/master-data/form-data/${id}`, createFetchOptions({ method: 'PUT', body: JSON.stringify(updates), }) ); const data = await handleApiResponse>(response); return data.data; } /** * 동적 폼 데이터 삭제 * * @param id - 데이터 ID */ export async function deleteFormData(id: string): Promise { const response = await fetch( `${API_URL}/api/master-data/form-data/${id}`, createFetchOptions({ method: 'DELETE', }) ); await handleApiResponse>(response); } // ===== 캐싱 ===== /** * 페이지 구성 캐시 무효화 * * @param pageType - 페이지 타입 (선택사항, 전체 무효화 가능) */ export async function invalidatePageConfigCache( pageType?: PageType ): Promise { const url = pageType ? `${API_URL}/api/master-data/cache/invalidate/${pageType}` : `${API_URL}/api/master-data/cache/invalidate`; const response = await fetch( url, createFetchOptions({ method: 'POST', }) ); await handleApiResponse>(response); } /** * Next.js revalidation 트리거 */ export async function revalidateMasterData(): Promise { if (typeof window === 'undefined') { const { revalidatePath } = await import('next/cache'); revalidatePath('/master-data'); } }