'use server'; import type { Contract, ContractDetail, ContractStats, ContractStageCount, ContractListResponse, ContractFilter, ContractFormData, } from './types'; import { apiClient } from '@/lib/api'; /** * 주일 기업 - 계약관리 Server Actions * 표준화된 apiClient 사용 버전 */ // ======================================== // API 응답 타입 // ======================================== interface ApiContract { id: number; contract_code: string; partner_id: number | null; partner_name: string | null; project_name: string; contract_manager_id: number | null; contract_manager_name: string | null; construction_pm_id: number | null; construction_pm_name: string | null; total_locations: number; contract_amount: number; contract_start_date: string | null; contract_end_date: string | null; status: 'pending' | 'completed'; stage: string; remarks: string | null; created_at: string; updated_at: string; created_by: string | null; bidding_id: number | null; bidding_code: string | null; contract_file?: ApiContractFile | null; attachments?: ApiAttachment[]; } interface ApiContractFile { id: number; file_name: string; file_url: string; uploaded_at: string; } interface ApiAttachment { id: number; file_name: string; file_size: number; file_url: string; uploaded_at: string; } interface ApiContractStats { total_count: number; pending_count: number; completed_count: number; } interface ApiContractStageCount { estimate_selected: number; estimate_progress: number; delivery: number; installation: number; inspection: number; other: number; } // ======================================== // 타입 변환 함수 // ======================================== /** * API 응답 → Contract 타입 변환 */ function transformContract(apiData: ApiContract): Contract { return { id: String(apiData.id), contractCode: apiData.contract_code || '', partnerId: apiData.partner_id ? String(apiData.partner_id) : '', partnerName: apiData.partner_name || '', projectName: apiData.project_name || '', contractManagerId: apiData.contract_manager_id ? String(apiData.contract_manager_id) : '', contractManagerName: apiData.contract_manager_name || '', constructionPMId: apiData.construction_pm_id ? String(apiData.construction_pm_id) : '', constructionPMName: apiData.construction_pm_name || '', totalLocations: apiData.total_locations || 0, contractAmount: apiData.contract_amount || 0, contractStartDate: apiData.contract_start_date || null, contractEndDate: apiData.contract_end_date || null, status: apiData.status || 'pending', stage: (apiData.stage as Contract['stage']) || 'estimate_selected', remarks: apiData.remarks || '', createdAt: apiData.created_at || '', updatedAt: apiData.updated_at || '', createdBy: apiData.created_by || '', biddingId: apiData.bidding_id ? String(apiData.bidding_id) : '', biddingCode: apiData.bidding_code || '', }; } /** * API 응답 → ContractDetail 타입 변환 */ function transformContractDetail(apiData: ApiContract): ContractDetail { const contract = transformContract(apiData); return { ...contract, contractFile: apiData.contract_file ? { id: String(apiData.contract_file.id), fileName: apiData.contract_file.file_name || '', fileUrl: apiData.contract_file.file_url || '', uploadedAt: apiData.contract_file.uploaded_at || '', } : null, attachments: (apiData.attachments || []).map((att) => ({ id: String(att.id), fileName: att.file_name || '', fileSize: att.file_size || 0, fileUrl: att.file_url || '', uploadedAt: att.uploaded_at || '', })), }; } /** * ContractFormData → API 요청 데이터 변환 */ function transformToApiRequest(data: Partial): Record { const apiData: Record = {}; if (data.contractCode !== undefined) apiData.contract_code = data.contractCode; if (data.projectName !== undefined) apiData.project_name = data.projectName; if (data.partnerId !== undefined) apiData.partner_id = data.partnerId || null; if (data.partnerName !== undefined) apiData.partner_name = data.partnerName || null; if (data.contractManagerId !== undefined) apiData.contract_manager_id = data.contractManagerId || null; if (data.contractManagerName !== undefined) apiData.contract_manager_name = data.contractManagerName || null; if (data.totalLocations !== undefined) apiData.total_locations = data.totalLocations; if (data.contractAmount !== undefined) apiData.contract_amount = data.contractAmount; if (data.contractStartDate !== undefined) apiData.contract_start_date = data.contractStartDate || null; if (data.contractEndDate !== undefined) apiData.contract_end_date = data.contractEndDate || null; if (data.status !== undefined) apiData.status = data.status; if (data.remarks !== undefined) apiData.remarks = data.remarks || null; return apiData; } // ======================================== // API 함수 // ======================================== /** * 계약 목록 조회 * GET /api/v1/construction/contracts */ export async function getContractList(filter?: ContractFilter): Promise<{ success: boolean; data?: ContractListResponse; error?: string; }> { try { const queryParams: Record = {}; // 검색 if (filter?.search) queryParams.search = filter.search; // 필터 if (filter?.status && filter.status !== 'all') queryParams.status = filter.status; if (filter?.stage && filter.stage !== 'all') queryParams.stage = filter.stage; if (filter?.partnerId && filter.partnerId !== 'all') queryParams.partner_id = filter.partnerId; if (filter?.contractManagerId && filter.contractManagerId !== 'all') { queryParams.contract_manager_id = filter.contractManagerId; } if (filter?.constructionPMId && filter.constructionPMId !== 'all') { queryParams.construction_pm_id = filter.constructionPMId; } // 날짜 범위 if (filter?.startDate) queryParams.start_date = filter.startDate; if (filter?.endDate) queryParams.end_date = filter.endDate; // 페이지네이션 if (filter?.page) queryParams.page = String(filter.page); if (filter?.size) queryParams.per_page = String(filter.size); // 정렬 if (filter?.sortBy) { const sortMap: Record = { contractDateDesc: { field: 'created_at', dir: 'desc' }, contractDateAsc: { field: 'created_at', dir: 'asc' }, partnerNameAsc: { field: 'partner_name', dir: 'asc' }, partnerNameDesc: { field: 'partner_name', dir: 'desc' }, projectNameAsc: { field: 'project_name', dir: 'asc' }, projectNameDesc: { field: 'project_name', dir: 'desc' }, amountDesc: { field: 'contract_amount', dir: 'desc' }, amountAsc: { field: 'contract_amount', dir: 'asc' }, }; const sort = sortMap[filter.sortBy]; if (sort) { queryParams.sort_by = sort.field; queryParams.sort_dir = sort.dir; } } const response = await apiClient.get<{ success: boolean; data: { data: ApiContract[]; current_page: number; per_page: number; total: number; last_page: number; }; }>('/construction/contracts', { params: queryParams }); const paginatedData = response.data; const items = (paginatedData.data || []).map(transformContract); return { success: true, data: { items, total: paginatedData.total || 0, page: paginatedData.current_page || 1, size: paginatedData.per_page || 20, totalPages: paginatedData.last_page || 1, }, }; } catch (error) { console.error('계약 목록 조회 오류:', error); return { success: false, error: '계약 목록을 불러오는데 실패했습니다.' }; } } /** * 계약 통계 조회 * GET /api/v1/construction/contracts/stats */ export async function getContractStats(): Promise<{ success: boolean; data?: ContractStats; error?: string; }> { try { const response = await apiClient.get<{ success: boolean; data: ApiContractStats; }>('/construction/contracts/stats'); const statsData = response.data; return { success: true, data: { total: statsData.total_count || 0, pending: statsData.pending_count || 0, completed: statsData.completed_count || 0, }, }; } catch (error) { console.error('계약 통계 조회 오류:', error); return { success: false, error: '통계를 불러오는데 실패했습니다.' }; } } /** * 단계별 건수 조회 * GET /api/v1/construction/contracts/stage-counts */ export async function getContractStageCounts(): Promise<{ success: boolean; data?: ContractStageCount; error?: string; }> { try { const response = await apiClient.get<{ success: boolean; data: ApiContractStageCount; }>('/construction/contracts/stage-counts'); const stageData = response.data; return { success: true, data: { estimateSelected: stageData.estimate_selected || 0, estimateProgress: stageData.estimate_progress || 0, delivery: stageData.delivery || 0, installation: stageData.installation || 0, inspection: stageData.inspection || 0, other: stageData.other || 0, }, }; } catch (error) { console.error('단계별 건수 조회 오류:', error); return { success: false, error: '단계별 건수를 불러오는데 실패했습니다.' }; } } /** * 계약 단건 조회 * GET /api/v1/construction/contracts/{id} */ export async function getContract(id: string): Promise<{ success: boolean; data?: Contract; error?: string; }> { try { const response = await apiClient.get<{ success: boolean; data: ApiContract }>( `/construction/contracts/${id}` ); return { success: true, data: transformContract(response.data) }; } catch (error) { console.error('계약 조회 오류:', error); return { success: false, error: '계약 정보를 찾을 수 없습니다.' }; } } /** * 계약 상세 조회 (첨부파일 포함) * GET /api/v1/construction/contracts/{id} */ export async function getContractDetail(id: string): Promise<{ success: boolean; data?: ContractDetail; error?: string; }> { try { const response = await apiClient.get<{ success: boolean; data: ApiContract }>( `/construction/contracts/${id}` ); return { success: true, data: transformContractDetail(response.data) }; } catch (error) { console.error('계약 상세 조회 오류:', error); return { success: false, error: '계약 상세 정보를 불러오는데 실패했습니다.' }; } } /** * 계약 등록 * POST /api/v1/construction/contracts */ export async function createContract(data: ContractFormData): Promise<{ success: boolean; data?: Contract; error?: string; }> { try { const apiData = transformToApiRequest(data); const response = await apiClient.post<{ success: boolean; data: ApiContract }>( '/construction/contracts', apiData ); return { success: true, data: transformContract(response.data) }; } catch (error) { console.error('계약 등록 오류:', error); return { success: false, error: '계약 등록에 실패했습니다.' }; } } /** * 계약 수정 * PUT /api/v1/construction/contracts/{id} */ export async function updateContract( id: string, data: Partial ): Promise<{ success: boolean; data?: Contract; error?: string; }> { try { const apiData = transformToApiRequest(data); const response = await apiClient.put<{ success: boolean; data: ApiContract }>( `/construction/contracts/${id}`, apiData ); return { success: true, data: transformContract(response.data) }; } catch (error) { console.error('계약 수정 오류:', error); return { success: false, error: '계약 수정에 실패했습니다.' }; } } /** * 계약 삭제 * DELETE /api/v1/construction/contracts/{id} */ export async function deleteContract(id: string): Promise<{ success: boolean; error?: string; }> { try { await apiClient.delete(`/construction/contracts/${id}`); return { success: true }; } catch (error) { console.error('계약 삭제 오류:', error); return { success: false, error: '계약 삭제에 실패했습니다.' }; } } /** * 계약 일괄 삭제 * DELETE /api/v1/construction/contracts/bulk */ export async function deleteContracts(ids: string[]): Promise<{ success: boolean; deletedCount?: number; error?: string; }> { try { await apiClient.delete('/construction/contracts/bulk', { data: { ids: ids.map((id) => Number(id)) }, }); return { success: true, deletedCount: ids.length }; } catch (error) { console.error('계약 일괄 삭제 오류:', error); return { success: false, error: '일괄 삭제에 실패했습니다.' }; } }