diff --git a/src/components/quality/InspectionManagement/actions.ts b/src/components/quality/InspectionManagement/actions.ts index b8669063..03f8b719 100644 --- a/src/components/quality/InspectionManagement/actions.ts +++ b/src/components/quality/InspectionManagement/actions.ts @@ -4,15 +4,15 @@ * 제품검사 관리 Server Actions * * API Endpoints: - * - GET /api/v1/inspections - 목록 조회 - * - GET /api/v1/inspections/stats - 통계 조회 - * - GET /api/v1/inspections/calendar - 캘린더 스케줄 조회 - * - GET /api/v1/inspections/{id} - 상세 조회 - * - POST /api/v1/inspections - 등록 - * - PUT /api/v1/inspections/{id} - 수정 - * - DELETE /api/v1/inspections/{id} - 삭제 - * - PATCH /api/v1/inspections/{id}/complete - 검사 완료 처리 - * - GET /api/v1/orders/select - 수주 선택 목록 조회 + * - GET /api/v1/quality/documents - 목록 조회 + * - GET /api/v1/quality/documents/stats - 통계 조회 + * - GET /api/v1/quality/documents/calendar - 캘린더 스케줄 조회 + * - GET /api/v1/quality/documents/{id} - 상세 조회 + * - POST /api/v1/quality/documents - 등록 + * - PUT /api/v1/quality/documents/{id} - 수정 + * - DELETE /api/v1/quality/documents/{id} - 삭제 + * - PATCH /api/v1/quality/documents/{id}/complete - 검사 완료 처리 + * - GET /api/v1/quality/documents/available-orders - 수주 선택 목록 조회 */ import { executeServerAction } from '@/lib/api/execute-server-action'; @@ -33,7 +33,7 @@ import { } from './mockData'; // 개발환경 Mock 데이터 fallback 플래그 -const USE_MOCK_FALLBACK = true; +const USE_MOCK_FALLBACK = false; // ===== API 응답 타입 ===== @@ -237,53 +237,49 @@ function transformApiToFrontend(api: ProductInspectionApi): ProductInspection { function transformFormToApi(data: InspectionFormData): Record { return { - quality_doc_number: data.qualityDocNumber, site_name: data.siteName, - client: data.client, - manager: data.manager, - manager_contact: data.managerContact, - construction_site: { - site_name: data.constructionSite.siteName, - land_location: data.constructionSite.landLocation, - lot_number: data.constructionSite.lotNumber, + client_id: data.clientId ?? null, + inspector_id: data.inspectorId ?? null, + received_date: data.receptionDate ?? null, + options: { + manager: { + name: data.manager || '', + phone: data.managerContact || '', + }, + inspection: { + request_date: data.scheduleInfo?.visitRequestDate || '', + start_date: data.scheduleInfo?.startDate || '', + end_date: data.scheduleInfo?.endDate || '', + }, + site_address: { + postal_code: data.scheduleInfo?.sitePostalCode || '', + address: data.scheduleInfo?.siteAddress || '', + detail: data.scheduleInfo?.siteAddressDetail || '', + }, + construction_site: { + name: data.constructionSite?.siteName || '', + land_location: data.constructionSite?.landLocation || '', + lot_number: data.constructionSite?.lotNumber || '', + }, + material_distributor: { + company: data.materialDistributor?.companyName || '', + address: data.materialDistributor?.companyAddress || '', + ceo: data.materialDistributor?.representativeName || '', + phone: data.materialDistributor?.phone || '', + }, + contractor: { + company: data.constructorInfo?.companyName || '', + address: data.constructorInfo?.companyAddress || '', + name: data.constructorInfo?.name || '', + phone: data.constructorInfo?.phone || '', + }, + supervisor: { + office: data.supervisor?.officeName || '', + address: data.supervisor?.officeAddress || '', + name: data.supervisor?.name || '', + phone: data.supervisor?.phone || '', + }, }, - material_distributor: { - company_name: data.materialDistributor.companyName, - company_address: data.materialDistributor.companyAddress, - representative_name: data.materialDistributor.representativeName, - phone: data.materialDistributor.phone, - }, - constructor_info: { - company_name: data.constructorInfo.companyName, - company_address: data.constructorInfo.companyAddress, - name: data.constructorInfo.name, - phone: data.constructorInfo.phone, - }, - supervisor: { - office_name: data.supervisor.officeName, - office_address: data.supervisor.officeAddress, - name: data.supervisor.name, - phone: data.supervisor.phone, - }, - schedule_info: { - visit_request_date: data.scheduleInfo.visitRequestDate, - start_date: data.scheduleInfo.startDate, - end_date: data.scheduleInfo.endDate, - inspector: data.scheduleInfo.inspector, - site_postal_code: data.scheduleInfo.sitePostalCode, - site_address: data.scheduleInfo.siteAddress, - site_address_detail: data.scheduleInfo.siteAddressDetail, - }, - order_items: data.orderItems.map((item) => ({ - order_number: item.orderNumber, - floor: item.floor, - symbol: item.symbol, - order_width: item.orderWidth, - order_height: item.orderHeight, - construction_width: item.constructionWidth, - construction_height: item.constructionHeight, - change_reason: item.changeReason, - })), }; } @@ -306,7 +302,7 @@ export async function getInspections(params?: { const defaultPagination = { currentPage: 1, lastPage: 1, perPage: 20, total: 0 }; const result = await executeServerAction({ - url: buildApiUrl('/api/v1/inspections', { + url: buildApiUrl('/api/v1/quality/documents', { page: params?.page, per_page: params?.size, q: params?.q, @@ -369,7 +365,7 @@ export async function getInspectionStats(params?: { __authError?: boolean; }> { const result = await executeServerAction({ - url: buildApiUrl('/api/v1/inspections/stats', { + url: buildApiUrl('/api/v1/quality/documents/stats', { date_from: params?.dateFrom, date_to: params?.dateTo, }), @@ -406,7 +402,7 @@ export async function getInspectionCalendar(params?: { __authError?: boolean; }> { const result = await executeServerAction({ - url: buildApiUrl('/api/v1/inspections/calendar', { + url: buildApiUrl('/api/v1/quality/documents/calendar', { year: params?.year, month: params?.month, inspector: params?.inspector, @@ -443,7 +439,7 @@ export async function getInspectionById(id: string): Promise<{ __authError?: boolean; }> { const result = await executeServerAction({ - url: buildApiUrl(`/api/v1/inspections/${id}`), + url: buildApiUrl(`/api/v1/quality/documents/${id}`), errorMessage: '제품검사 상세 조회에 실패했습니다.', }); @@ -471,7 +467,7 @@ export async function createInspection(data: InspectionFormData): Promise<{ }> { const apiData = transformFormToApi(data); const result = await executeServerAction({ - url: buildApiUrl('/api/v1/inspections'), + url: buildApiUrl('/api/v1/quality/documents'), method: 'POST', body: apiData, errorMessage: '제품검사 등록에 실패했습니다.', @@ -496,69 +492,70 @@ export async function updateInspection( }> { const apiData: Record = {}; - if (data.qualityDocNumber !== undefined) apiData.quality_doc_number = data.qualityDocNumber; if (data.siteName !== undefined) apiData.site_name = data.siteName; - if (data.client !== undefined) apiData.client = data.client; - if (data.manager !== undefined) apiData.manager = data.manager; - if (data.managerContact !== undefined) apiData.manager_contact = data.managerContact; + if (data.clientId !== undefined) apiData.client_id = data.clientId; + if (data.inspectorId !== undefined) apiData.inspector_id = data.inspectorId; + if (data.receptionDate !== undefined) apiData.received_date = data.receptionDate; - if (data.constructionSite) { - apiData.construction_site = { - site_name: data.constructionSite.siteName, - land_location: data.constructionSite.landLocation, - lot_number: data.constructionSite.lotNumber, - }; - } - if (data.materialDistributor) { - apiData.material_distributor = { - company_name: data.materialDistributor.companyName, - company_address: data.materialDistributor.companyAddress, - representative_name: data.materialDistributor.representativeName, - phone: data.materialDistributor.phone, - }; - } - if (data.constructorInfo) { - apiData.constructor_info = { - company_name: data.constructorInfo.companyName, - company_address: data.constructorInfo.companyAddress, - name: data.constructorInfo.name, - phone: data.constructorInfo.phone, - }; - } - if (data.supervisor) { - apiData.supervisor = { - office_name: data.supervisor.officeName, - office_address: data.supervisor.officeAddress, - name: data.supervisor.name, - phone: data.supervisor.phone, + // options 필드들은 백엔드에서 array_replace_recursive로 병합됨 + const options: Record = {}; + + if (data.manager !== undefined || data.managerContact !== undefined) { + options.manager = { + name: data.manager || '', + phone: data.managerContact || '', }; } if (data.scheduleInfo) { - apiData.schedule_info = { - visit_request_date: data.scheduleInfo.visitRequestDate, - start_date: data.scheduleInfo.startDate, - end_date: data.scheduleInfo.endDate, - inspector: data.scheduleInfo.inspector, - site_postal_code: data.scheduleInfo.sitePostalCode, - site_address: data.scheduleInfo.siteAddress, - site_address_detail: data.scheduleInfo.siteAddressDetail, + options.inspection = { + request_date: data.scheduleInfo.visitRequestDate || '', + start_date: data.scheduleInfo.startDate || '', + end_date: data.scheduleInfo.endDate || '', + }; + options.site_address = { + postal_code: data.scheduleInfo.sitePostalCode || '', + address: data.scheduleInfo.siteAddress || '', + detail: data.scheduleInfo.siteAddressDetail || '', }; } - if (data.orderItems) { - apiData.order_items = data.orderItems.map((item) => ({ - order_number: item.orderNumber, - floor: item.floor, - symbol: item.symbol, - order_width: item.orderWidth, - order_height: item.orderHeight, - construction_width: item.constructionWidth, - construction_height: item.constructionHeight, - change_reason: item.changeReason, - })); + if (data.constructionSite) { + options.construction_site = { + name: data.constructionSite.siteName || '', + land_location: data.constructionSite.landLocation || '', + lot_number: data.constructionSite.lotNumber || '', + }; + } + if (data.materialDistributor) { + options.material_distributor = { + company: data.materialDistributor.companyName || '', + address: data.materialDistributor.companyAddress || '', + ceo: data.materialDistributor.representativeName || '', + phone: data.materialDistributor.phone || '', + }; + } + if (data.constructorInfo) { + options.contractor = { + company: data.constructorInfo.companyName || '', + address: data.constructorInfo.companyAddress || '', + name: data.constructorInfo.name || '', + phone: data.constructorInfo.phone || '', + }; + } + if (data.supervisor) { + options.supervisor = { + office: data.supervisor.officeName || '', + address: data.supervisor.officeAddress || '', + name: data.supervisor.name || '', + phone: data.supervisor.phone || '', + }; + } + + if (Object.keys(options).length > 0) { + apiData.options = options; } const result = await executeServerAction({ - url: buildApiUrl(`/api/v1/inspections/${id}`), + url: buildApiUrl(`/api/v1/quality/documents/${id}`), method: 'PUT', body: apiData, errorMessage: '제품검사 수정에 실패했습니다.', @@ -578,7 +575,7 @@ export async function deleteInspection(id: string): Promise<{ __authError?: boolean; }> { const result = await executeServerAction({ - url: buildApiUrl(`/api/v1/inspections/${id}`), + url: buildApiUrl(`/api/v1/quality/documents/${id}`), method: 'DELETE', errorMessage: '제품검사 삭제에 실패했습니다.', }); @@ -626,7 +623,7 @@ export async function getOrderSelectList(params?: { __authError?: boolean; }> { const result = await executeServerAction({ - url: buildApiUrl('/api/v1/orders/select', { q: params?.q }), + url: buildApiUrl('/api/v1/quality/documents/available-orders', { q: params?.q }), errorMessage: '수주 선택 목록 조회에 실패했습니다.', }); diff --git a/src/components/quality/InspectionManagement/types.ts b/src/components/quality/InspectionManagement/types.ts index 4a53ff0f..78e1bffd 100644 --- a/src/components/quality/InspectionManagement/types.ts +++ b/src/components/quality/InspectionManagement/types.ts @@ -181,6 +181,9 @@ export interface InspectionFormData { qualityDocNumber: string; siteName: string; client: string; + clientId?: number; // 수주처 ID (API용) + inspectorId?: number; // 검사자 ID (API용) + receptionDate?: string; // 접수일 (API용) manager: string; managerContact: string; constructionSite: ConstructionSiteInfo; diff --git a/src/components/quality/PerformanceReportManagement/actions.ts b/src/components/quality/PerformanceReportManagement/actions.ts index 51c57246..1d330625 100644 --- a/src/components/quality/PerformanceReportManagement/actions.ts +++ b/src/components/quality/PerformanceReportManagement/actions.ts @@ -4,13 +4,12 @@ * 실적신고관리 Server Actions * * API Endpoints: - * - GET /api/v1/performance-reports - 분기별 실적신고 목록 - * - GET /api/v1/performance-reports/stats - 통계 - * - GET /api/v1/performance-reports/missed - 누락체크 목록 - * - PATCH /api/v1/performance-reports/confirm - 선택 확정 - * - PATCH /api/v1/performance-reports/unconfirm - 확정 해제 - * - POST /api/v1/performance-reports/distribute - 배포 - * - PATCH /api/v1/performance-reports/memo - 메모 일괄 적용 + * - GET /api/v1/quality/performance-reports - 분기별 실적신고 목록 + * - GET /api/v1/quality/performance-reports/stats - 통계 + * - GET /api/v1/quality/performance-reports/missing - 누락체크 목록 + * - PATCH /api/v1/quality/performance-reports/confirm - 선택 확정 + * - PATCH /api/v1/quality/performance-reports/unconfirm - 확정 해제 + * - PATCH /api/v1/quality/performance-reports/memo - 메모 일괄 적용 */ import { executeServerAction } from '@/lib/api/execute-server-action'; @@ -28,7 +27,7 @@ import { } from './mockData'; // 개발환경 Mock 데이터 fallback 플래그 -const USE_MOCK_FALLBACK = true; +const USE_MOCK_FALLBACK = false; // ===== 페이지네이션 ===== @@ -58,7 +57,7 @@ export async function getPerformanceReports(params?: { interface ApiListData { items?: PerformanceReport[]; current_page?: number; last_page?: number; per_page?: number; total?: number } const result = await executeServerAction({ - url: buildApiUrl('/api/v1/performance-reports', { + url: buildApiUrl('/api/v1/quality/performance-reports', { page: params?.page, per_page: params?.size, q: params?.q, @@ -116,7 +115,7 @@ export async function getPerformanceReportStats(params?: { __authError?: boolean; }> { const result = await executeServerAction({ - url: buildApiUrl('/api/v1/performance-reports/stats', { + url: buildApiUrl('/api/v1/quality/performance-reports/stats', { year: params?.year, quarter: params?.quarter && params.quarter !== '전체' ? params.quarter : undefined, }), @@ -147,7 +146,7 @@ export async function getMissedReports(params?: { interface ApiMissedData { items?: MissedReport[]; current_page?: number; last_page?: number; per_page?: number; total?: number } const result = await executeServerAction({ - url: buildApiUrl('/api/v1/performance-reports/missed', { + url: buildApiUrl('/api/v1/quality/performance-reports/missing', { page: params?.page, per_page: params?.size, q: params?.q, @@ -197,7 +196,7 @@ export async function confirmReports(ids: string[]): Promise<{ __authError?: boolean; }> { const result = await executeServerAction({ - url: buildApiUrl('/api/v1/performance-reports/confirm'), + url: buildApiUrl('/api/v1/quality/performance-reports/confirm'), method: 'PATCH', body: { ids }, errorMessage: '확정 처리에 실패했습니다.', @@ -214,7 +213,7 @@ export async function unconfirmReports(ids: string[]): Promise<{ __authError?: boolean; }> { const result = await executeServerAction({ - url: buildApiUrl('/api/v1/performance-reports/unconfirm'), + url: buildApiUrl('/api/v1/quality/performance-reports/unconfirm'), method: 'PATCH', body: { ids }, errorMessage: '확정 해제에 실패했습니다.', @@ -223,7 +222,7 @@ export async function unconfirmReports(ids: string[]): Promise<{ return { success: result.success, error: result.error, __authError: result.__authError }; } -// ===== 배포 ===== +// ===== 배포 (TODO: 백엔드 API 미구현 - 추후 추가 예정) ===== export async function distributeReports(ids: string[]): Promise<{ success: boolean; @@ -231,12 +230,11 @@ export async function distributeReports(ids: string[]): Promise<{ __authError?: boolean; }> { const result = await executeServerAction({ - url: buildApiUrl('/api/v1/performance-reports/distribute'), + url: buildApiUrl('/api/v1/quality/performance-reports/distribute'), method: 'POST', body: { ids }, errorMessage: '배포에 실패했습니다.', }); - if (!result.success && USE_MOCK_FALLBACK) return { success: true }; return { success: result.success, error: result.error, __authError: result.__authError }; } @@ -248,7 +246,7 @@ export async function updateMemo(ids: string[], memo: string): Promise<{ __authError?: boolean; }> { const result = await executeServerAction({ - url: buildApiUrl('/api/v1/performance-reports/memo'), + url: buildApiUrl('/api/v1/quality/performance-reports/memo'), method: 'PATCH', body: { ids, memo }, errorMessage: '메모 저장에 실패했습니다.',