feat: [outbound] 배차차량관리 목업→API 연동 전환
- mockData 제거, executePaginatedAction/executeServerAction 사용 - buildApiUrl로 쿼리 파라미터 빌드 - API 응답(snake_case) → 프론트 타입(camelCase) 변환 함수 추가
This commit is contained in:
@@ -1,8 +1,5 @@
|
||||
/**
|
||||
* 배차차량관리 서버 액션
|
||||
*
|
||||
* 현재: Mock 데이터 반환
|
||||
* 추후: API 연동 시 serverFetch 사용
|
||||
*/
|
||||
|
||||
'use server';
|
||||
@@ -13,11 +10,9 @@ import type {
|
||||
VehicleDispatchStats,
|
||||
VehicleDispatchEditFormData,
|
||||
} from './types';
|
||||
import {
|
||||
mockVehicleDispatchItems,
|
||||
mockVehicleDispatchDetail,
|
||||
mockVehicleDispatchStats,
|
||||
} from './mockData';
|
||||
import { buildApiUrl } from '@/lib/api/query-params';
|
||||
import { executeServerAction } from '@/lib/api/execute-server-action';
|
||||
import { executePaginatedAction } from '@/lib/api/execute-paginated-action';
|
||||
|
||||
// ===== 페이지네이션 타입 =====
|
||||
interface PaginationMeta {
|
||||
@@ -27,6 +22,59 @@ interface PaginationMeta {
|
||||
total: number;
|
||||
}
|
||||
|
||||
// ===== API 응답 → 프론트 타입 변환 =====
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function transformToListItem(data: any): VehicleDispatchItem {
|
||||
const options = data.options || {};
|
||||
const shipment = data.shipment || {};
|
||||
return {
|
||||
id: String(data.id),
|
||||
dispatchNo: options.dispatch_no || `DC-${data.id}`,
|
||||
shipmentNo: shipment.shipment_no || '',
|
||||
lotNo: shipment.lot_no || '',
|
||||
siteName: shipment.site_name || '',
|
||||
orderCustomer: shipment.customer_name || '',
|
||||
logisticsCompany: data.logistics_company || '',
|
||||
tonnage: data.tonnage || '',
|
||||
supplyAmount: options.supply_amount || 0,
|
||||
vat: options.vat || 0,
|
||||
totalAmount: options.total_amount || 0,
|
||||
freightCostType: options.freight_cost_type || 'prepaid',
|
||||
vehicleNo: data.vehicle_no || '',
|
||||
driverContact: data.driver_contact || '',
|
||||
writer: options.writer || '',
|
||||
arrivalDateTime: data.arrival_datetime || '',
|
||||
status: options.status || 'draft',
|
||||
remarks: data.remarks || '',
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function transformToDetail(data: any): VehicleDispatchDetail {
|
||||
const options = data.options || {};
|
||||
const shipment = data.shipment || {};
|
||||
return {
|
||||
id: String(data.id),
|
||||
dispatchNo: options.dispatch_no || `DC-${data.id}`,
|
||||
shipmentNo: shipment.shipment_no || '',
|
||||
lotNo: shipment.lot_no || '',
|
||||
siteName: shipment.site_name || '',
|
||||
orderCustomer: shipment.customer_name || '',
|
||||
freightCostType: options.freight_cost_type || 'prepaid',
|
||||
status: options.status || 'draft',
|
||||
writer: options.writer || '',
|
||||
logisticsCompany: data.logistics_company || '',
|
||||
arrivalDateTime: data.arrival_datetime || '',
|
||||
tonnage: data.tonnage || '',
|
||||
vehicleNo: data.vehicle_no || '',
|
||||
driverContact: data.driver_contact || '',
|
||||
remarks: data.remarks || '',
|
||||
supplyAmount: options.supply_amount || 0,
|
||||
vat: options.vat || 0,
|
||||
totalAmount: options.total_amount || 0,
|
||||
};
|
||||
}
|
||||
|
||||
// ===== 배차차량 목록 조회 =====
|
||||
export async function getVehicleDispatches(params?: {
|
||||
page?: number;
|
||||
@@ -41,54 +89,18 @@ export async function getVehicleDispatches(params?: {
|
||||
pagination: PaginationMeta;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
let items = [...mockVehicleDispatchItems];
|
||||
|
||||
// 상태 필터
|
||||
if (params?.status && params.status !== 'all') {
|
||||
items = items.filter((item) => item.status === params.status);
|
||||
}
|
||||
|
||||
// 검색 필터
|
||||
if (params?.search) {
|
||||
const s = params.search.toLowerCase();
|
||||
items = items.filter(
|
||||
(item) =>
|
||||
item.dispatchNo.toLowerCase().includes(s) ||
|
||||
item.shipmentNo.toLowerCase().includes(s) ||
|
||||
item.siteName.toLowerCase().includes(s) ||
|
||||
item.orderCustomer.toLowerCase().includes(s) ||
|
||||
item.vehicleNo.toLowerCase().includes(s)
|
||||
);
|
||||
}
|
||||
|
||||
// 페이지네이션
|
||||
const page = params?.page || 1;
|
||||
const perPage = params?.perPage || 20;
|
||||
const total = items.length;
|
||||
const lastPage = Math.ceil(total / perPage);
|
||||
const startIndex = (page - 1) * perPage;
|
||||
const paginatedItems = items.slice(startIndex, startIndex + perPage);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: paginatedItems,
|
||||
pagination: {
|
||||
currentPage: page,
|
||||
lastPage,
|
||||
perPage,
|
||||
total,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[VehicleDispatchActions] getVehicleDispatches error:', error);
|
||||
return {
|
||||
success: false,
|
||||
data: [],
|
||||
pagination: { currentPage: 1, lastPage: 1, perPage: 20, total: 0 },
|
||||
error: '서버 오류가 발생했습니다.',
|
||||
};
|
||||
}
|
||||
return executePaginatedAction({
|
||||
url: buildApiUrl('/api/v1/vehicle-dispatches', {
|
||||
search: params?.search,
|
||||
status: params?.status !== 'all' ? params?.status : undefined,
|
||||
start_date: params?.startDate,
|
||||
end_date: params?.endDate,
|
||||
page: params?.page,
|
||||
per_page: params?.perPage,
|
||||
}),
|
||||
transform: transformToListItem,
|
||||
errorMessage: '배차차량 목록 조회에 실패했습니다.',
|
||||
});
|
||||
}
|
||||
|
||||
// ===== 배차차량 통계 조회 =====
|
||||
@@ -97,12 +109,18 @@ export async function getVehicleDispatchStats(): Promise<{
|
||||
data?: VehicleDispatchStats;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
return { success: true, data: mockVehicleDispatchStats };
|
||||
} catch (error) {
|
||||
console.error('[VehicleDispatchActions] getVehicleDispatchStats error:', error);
|
||||
return { success: false, error: '서버 오류가 발생했습니다.' };
|
||||
}
|
||||
return executeServerAction<
|
||||
{ prepaid_amount: number; collect_amount: number; total_amount: number },
|
||||
VehicleDispatchStats
|
||||
>({
|
||||
url: buildApiUrl('/api/v1/vehicle-dispatches/stats'),
|
||||
transform: (data) => ({
|
||||
prepaidAmount: data.prepaid_amount,
|
||||
collectAmount: data.collect_amount,
|
||||
totalAmount: data.total_amount,
|
||||
}),
|
||||
errorMessage: '배차차량 통계 조회에 실패했습니다.',
|
||||
});
|
||||
}
|
||||
|
||||
// ===== 배차차량 상세 조회 =====
|
||||
@@ -111,51 +129,34 @@ export async function getVehicleDispatchById(id: string): Promise<{
|
||||
data?: VehicleDispatchDetail;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
// Mock: ID로 목록에서 찾아서 상세 데이터 생성
|
||||
const item = mockVehicleDispatchItems.find((i) => i.id === id);
|
||||
if (!item) {
|
||||
// fallback으로 기본 상세 데이터 반환
|
||||
return { success: true, data: { ...mockVehicleDispatchDetail, id } };
|
||||
}
|
||||
|
||||
const detail: VehicleDispatchDetail = {
|
||||
id: item.id,
|
||||
dispatchNo: item.dispatchNo,
|
||||
shipmentNo: item.shipmentNo,
|
||||
siteName: item.siteName,
|
||||
orderCustomer: item.orderCustomer,
|
||||
freightCostType: item.freightCostType,
|
||||
status: item.status,
|
||||
writer: item.writer,
|
||||
logisticsCompany: item.logisticsCompany,
|
||||
arrivalDateTime: item.arrivalDateTime,
|
||||
tonnage: item.tonnage,
|
||||
vehicleNo: item.vehicleNo,
|
||||
driverContact: item.driverContact,
|
||||
remarks: item.remarks,
|
||||
supplyAmount: item.supplyAmount,
|
||||
vat: item.vat,
|
||||
totalAmount: item.totalAmount,
|
||||
};
|
||||
|
||||
return { success: true, data: detail };
|
||||
} catch (error) {
|
||||
console.error('[VehicleDispatchActions] getVehicleDispatchById error:', error);
|
||||
return { success: false, error: '서버 오류가 발생했습니다.' };
|
||||
}
|
||||
return executeServerAction({
|
||||
url: buildApiUrl(`/api/v1/vehicle-dispatches/${id}`),
|
||||
transform: transformToDetail,
|
||||
errorMessage: '배차차량 상세 조회에 실패했습니다.',
|
||||
});
|
||||
}
|
||||
|
||||
// ===== 배차차량 수정 =====
|
||||
export async function updateVehicleDispatch(
|
||||
id: string,
|
||||
_data: VehicleDispatchEditFormData
|
||||
data: VehicleDispatchEditFormData
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
// Mock: 항상 성공 반환
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('[VehicleDispatchActions] updateVehicleDispatch error:', error);
|
||||
return { success: false, error: '서버 오류가 발생했습니다.' };
|
||||
}
|
||||
return executeServerAction({
|
||||
url: buildApiUrl(`/api/v1/vehicle-dispatches/${id}`),
|
||||
method: 'PUT',
|
||||
body: {
|
||||
freight_cost_type: data.freightCostType,
|
||||
logistics_company: data.logisticsCompany,
|
||||
arrival_datetime: data.arrivalDateTime,
|
||||
tonnage: data.tonnage,
|
||||
vehicle_no: data.vehicleNo,
|
||||
driver_contact: data.driverContact,
|
||||
remarks: data.remarks,
|
||||
supply_amount: data.supplyAmount,
|
||||
vat: data.vat,
|
||||
total_amount: data.totalAmount,
|
||||
status: undefined, // 상태는 별도로 관리
|
||||
},
|
||||
errorMessage: '배차차량 수정에 실패했습니다.',
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user