'use server'; import { serverFetch } from '@/lib/api/fetch-wrapper'; import type { Process, ProcessFormData, ClassificationRule } from '@/types/process'; // ============================================================================ // API 타입 정의 // ============================================================================ interface ApiProcess { id: number; tenant_id: number; process_code: string; process_name: string; description: string | null; process_type: string; department: string | null; work_log_template: string | null; required_workers: number; equipment_info: string | null; work_steps: string[] | null; note: string | null; is_active: boolean; created_at: string; updated_at: string; classification_rules?: ApiClassificationRule[]; } interface ApiClassificationRule { id: number; process_id: number; registration_type: string; rule_type: string; matching_type: string; condition_value: string; priority: number; description: string | null; is_active: boolean; created_at: string; updated_at: string; } interface ApiResponse { success: boolean; message: string; data: T; } interface PaginatedResponse { current_page: number; data: T[]; last_page: number; per_page: number; total: number; } // ============================================================================ // 데이터 변환 함수 // ============================================================================ function transformApiToFrontend(apiData: ApiProcess): Process { return { id: String(apiData.id), processCode: apiData.process_code, processName: apiData.process_name, description: apiData.description ?? undefined, processType: apiData.process_type as Process['processType'], department: apiData.department ?? '', workLogTemplate: apiData.work_log_template ?? undefined, classificationRules: (apiData.classification_rules ?? []).map(transformRuleApiToFrontend), requiredWorkers: apiData.required_workers, equipmentInfo: apiData.equipment_info ?? undefined, workSteps: apiData.work_steps ?? [], note: apiData.note ?? undefined, status: apiData.is_active ? '사용중' : '미사용', createdAt: apiData.created_at, updatedAt: apiData.updated_at, }; } function transformRuleApiToFrontend(apiRule: ApiClassificationRule): ClassificationRule { return { id: String(apiRule.id), registrationType: apiRule.registration_type as ClassificationRule['registrationType'], ruleType: apiRule.rule_type as ClassificationRule['ruleType'], matchingType: apiRule.matching_type as ClassificationRule['matchingType'], conditionValue: apiRule.condition_value, priority: apiRule.priority, description: apiRule.description ?? undefined, isActive: apiRule.is_active, createdAt: apiRule.created_at, }; } function transformFrontendToApi(data: ProcessFormData): Record { return { process_name: data.processName, process_type: data.processType, department: data.department || null, work_log_template: data.workLogTemplate || null, required_workers: data.requiredWorkers, equipment_info: data.equipmentInfo || null, work_steps: data.workSteps ? data.workSteps.split(',').map((s) => s.trim()).filter(Boolean) : [], note: data.note || null, is_active: data.isActive, classification_rules: data.classificationRules.map((rule) => ({ registration_type: rule.registrationType, rule_type: rule.ruleType, matching_type: rule.matchingType, condition_value: rule.conditionValue, priority: rule.priority, description: rule.description || null, is_active: rule.isActive, })), }; } // ============================================================================ // API 함수 // ============================================================================ /** * 공정 목록 조회 */ export async function getProcessList(params?: { page?: number; size?: number; q?: string; status?: string; process_type?: string; }): Promise<{ success: boolean; data?: { items: Process[]; total: number; page: number; totalPages: number }; error?: string; __authError?: boolean }> { try { const searchParams = new URLSearchParams(); if (params?.page) searchParams.set('page', String(params.page)); if (params?.size) searchParams.set('size', String(params.size)); if (params?.q) searchParams.set('q', params.q); if (params?.status) searchParams.set('status', params.status); if (params?.process_type) searchParams.set('process_type', params.process_type); const { response, error } = await serverFetch( `${process.env.NEXT_PUBLIC_API_URL}/v1/processes?${searchParams.toString()}`, { method: 'GET', cache: 'no-store' } ); if (error) { return { success: false, error: error.message, __authError: error.code === 'UNAUTHORIZED' }; } if (!response) { return { success: false, error: '목록 조회에 실패했습니다.' }; } const result: ApiResponse> = await response.json(); if (!response.ok || !result.success) { return { success: false, error: result.message || '목록 조회에 실패했습니다.' }; } return { success: true, data: { items: result.data.data.map(transformApiToFrontend), total: result.data.total, page: result.data.current_page, totalPages: result.data.last_page, }, }; } catch (error) { console.error('[getProcessList] Error:', error); return { success: false, error: '서버 오류가 발생했습니다.' }; } } /** * 공정 상세 조회 */ export async function getProcessById(id: string): Promise<{ success: boolean; data?: Process; error?: string; __authError?: boolean }> { try { const { response, error } = await serverFetch(`${process.env.NEXT_PUBLIC_API_URL}/v1/processes/${id}`, { method: 'GET', cache: 'no-store', }); if (error) { return { success: false, error: error.message, __authError: error.code === 'UNAUTHORIZED' }; } if (!response) { return { success: false, error: '조회에 실패했습니다.' }; } const result: ApiResponse = await response.json(); if (!response.ok || !result.success) { return { success: false, error: result.message || '조회에 실패했습니다.' }; } return { success: true, data: transformApiToFrontend(result.data) }; } catch (error) { console.error('[getProcessById] Error:', error); return { success: false, error: '서버 오류가 발생했습니다.' }; } } /** * 공정 생성 */ export async function createProcess(data: ProcessFormData): Promise<{ success: boolean; data?: Process; error?: string; __authError?: boolean }> { try { const apiData = transformFrontendToApi(data); const { response, error } = await serverFetch(`${process.env.NEXT_PUBLIC_API_URL}/v1/processes`, { method: 'POST', body: JSON.stringify(apiData), }); if (error) { return { success: false, error: error.message, __authError: error.code === 'UNAUTHORIZED' }; } if (!response) { return { success: false, error: '등록에 실패했습니다.' }; } const result: ApiResponse = await response.json(); if (!response.ok || !result.success) { return { success: false, error: result.message || '등록에 실패했습니다.' }; } return { success: true, data: transformApiToFrontend(result.data) }; } catch (error) { console.error('[createProcess] Error:', error); return { success: false, error: '서버 오류가 발생했습니다.' }; } } /** * 공정 수정 */ export async function updateProcess(id: string, data: ProcessFormData): Promise<{ success: boolean; data?: Process; error?: string; __authError?: boolean }> { try { const apiData = transformFrontendToApi(data); const { response, error } = await serverFetch(`${process.env.NEXT_PUBLIC_API_URL}/v1/processes/${id}`, { method: 'PUT', body: JSON.stringify(apiData), }); if (error) { return { success: false, error: error.message, __authError: error.code === 'UNAUTHORIZED' }; } if (!response) { return { success: false, error: '수정에 실패했습니다.' }; } const result: ApiResponse = await response.json(); if (!response.ok || !result.success) { return { success: false, error: result.message || '수정에 실패했습니다.' }; } return { success: true, data: transformApiToFrontend(result.data) }; } catch (error) { console.error('[updateProcess] Error:', error); return { success: false, error: '서버 오류가 발생했습니다.' }; } } /** * 공정 삭제 */ export async function deleteProcess(id: string): Promise<{ success: boolean; error?: string; __authError?: boolean }> { try { const { response, error } = await serverFetch(`${process.env.NEXT_PUBLIC_API_URL}/v1/processes/${id}`, { method: 'DELETE', }); if (error) { return { success: false, error: error.message, __authError: error.code === 'UNAUTHORIZED' }; } if (!response) { return { success: false, error: '삭제에 실패했습니다.' }; } const result: ApiResponse = await response.json(); if (!response.ok || !result.success) { return { success: false, error: result.message || '삭제에 실패했습니다.' }; } return { success: true }; } catch (error) { console.error('[deleteProcess] Error:', error); return { success: false, error: '서버 오류가 발생했습니다.' }; } } /** * 공정 일괄 삭제 */ export async function deleteProcesses(ids: string[]): Promise<{ success: boolean; deletedCount?: number; error?: string; __authError?: boolean }> { try { const { response, error } = await serverFetch(`${process.env.NEXT_PUBLIC_API_URL}/v1/processes`, { method: 'DELETE', body: JSON.stringify({ ids: ids.map((id) => parseInt(id, 10)) }), }); if (error) { return { success: false, error: error.message, __authError: error.code === 'UNAUTHORIZED' }; } if (!response) { return { success: false, error: '일괄 삭제에 실패했습니다.' }; } const result: ApiResponse<{ deleted_count: number }> = await response.json(); if (!response.ok || !result.success) { return { success: false, error: result.message || '일괄 삭제에 실패했습니다.' }; } return { success: true, deletedCount: result.data.deleted_count }; } catch (error) { console.error('[deleteProcesses] Error:', error); return { success: false, error: '서버 오류가 발생했습니다.' }; } } /** * 공정 상태 토글 */ export async function toggleProcessActive(id: string): Promise<{ success: boolean; data?: Process; error?: string; __authError?: boolean }> { try { const { response, error } = await serverFetch(`${process.env.NEXT_PUBLIC_API_URL}/v1/processes/${id}/toggle`, { method: 'PATCH', }); if (error) { return { success: false, error: error.message, __authError: error.code === 'UNAUTHORIZED' }; } if (!response) { return { success: false, error: '상태 변경에 실패했습니다.' }; } const result: ApiResponse = await response.json(); if (!response.ok || !result.success) { return { success: false, error: result.message || '상태 변경에 실패했습니다.' }; } return { success: true, data: transformApiToFrontend(result.data) }; } catch (error) { console.error('[toggleProcessActive] Error:', error); return { success: false, error: '서버 오류가 발생했습니다.' }; } } /** * 공정 옵션 목록 (드롭다운용) */ export async function getProcessOptions(): Promise<{ success: boolean; data?: Array<{ id: string; processCode: string; processName: string; processType: string; department: string }>; error?: string; __authError?: boolean; }> { try { const { response, error } = await serverFetch(`${process.env.NEXT_PUBLIC_API_URL}/v1/processes/options`, { method: 'GET', cache: 'no-store', }); if (error) { return { success: false, error: error.message, __authError: error.code === 'UNAUTHORIZED' }; } if (!response) { return { success: false, error: '옵션 조회에 실패했습니다.' }; } const result: ApiResponse> = await response.json(); if (!response.ok || !result.success) { return { success: false, error: result.message || '옵션 조회에 실패했습니다.' }; } return { success: true, data: result.data.map((item) => ({ id: String(item.id), processCode: item.process_code, processName: item.process_name, processType: item.process_type, department: item.department ?? '', })), }; } catch (error) { console.error('[getProcessOptions] Error:', error); return { success: false, error: '서버 오류가 발생했습니다.' }; } } /** * 공정 통계 */ export async function getProcessStats(): Promise<{ success: boolean; data?: { total: number; active: number; inactive: number; byType: Record }; error?: string; __authError?: boolean; }> { try { const { response, error } = await serverFetch(`${process.env.NEXT_PUBLIC_API_URL}/v1/processes/stats`, { method: 'GET', cache: 'no-store', }); if (error) { return { success: false, error: error.message, __authError: error.code === 'UNAUTHORIZED' }; } if (!response) { return { success: false, error: '통계 조회에 실패했습니다.' }; } const result: ApiResponse<{ total: number; active: number; inactive: number; by_type: Record }> = await response.json(); if (!response.ok || !result.success) { return { success: false, error: result.message || '통계 조회에 실패했습니다.' }; } return { success: true, data: { total: result.data.total, active: result.data.active, inactive: result.data.inactive, byType: result.data.by_type, }, }; } catch (error) { console.error('[getProcessStats] Error:', error); return { success: false, error: '서버 오류가 발생했습니다.' }; } }