feat: [품질관리] 프론트엔드 API 연동 (Mock → 실제 API 전환)
- InspectionManagement/actions.ts: API 경로 /quality/documents로 변경, transformFormToApi options JSON 구조 매핑 - PerformanceReportManagement/actions.ts: API 경로 /quality/performance-reports로 변경, /missed→/missing - InspectionManagement/types.ts: InspectionFormData에 clientId/inspectorId/receptionDate 추가 - USE_MOCK_FALLBACK = false 설정
This commit is contained in:
@@ -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<string, unknown> {
|
||||
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<PaginatedResponse>({
|
||||
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<InspectionStatsApi>({
|
||||
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<CalendarItemApi[]>({
|
||||
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<ProductInspectionApi>({
|
||||
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<ProductInspectionApi>({
|
||||
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<string, unknown> = {};
|
||||
|
||||
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<string, unknown> = {};
|
||||
|
||||
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<ProductInspectionApi>({
|
||||
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<OrderSelectItemApi[]>({
|
||||
url: buildApiUrl('/api/v1/orders/select', { q: params?.q }),
|
||||
url: buildApiUrl('/api/v1/quality/documents/available-orders', { q: params?.q }),
|
||||
errorMessage: '수주 선택 목록 조회에 실패했습니다.',
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<ApiListData>({
|
||||
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<PerformanceReportStats>({
|
||||
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<ApiMissedData>({
|
||||
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: '메모 저장에 실패했습니다.',
|
||||
|
||||
Reference in New Issue
Block a user