refactor(WEB): 전체 actions.ts에 공통 API 유틸 적용
- buildApiUrl / executePaginatedAction 패턴으로 전환 (40+ actions 파일) - 직접 URLSearchParams 조립 → buildApiUrl 유틸 사용 - 수동 페이지네이션 메타 변환 → executePaginatedAction 자동 처리 - HandoverReportDocumentModal, OrderDocumentModal 개선 - 급여관리 SalaryManagement 코드 개선 - CLAUDE.md Server Action 공통 유틸 규칙 정리 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,7 +19,8 @@
|
||||
|
||||
|
||||
import { executeServerAction } from '@/lib/api/execute-server-action';
|
||||
import type { PaginatedApiResponse } from '@/lib/api/types';
|
||||
import { executePaginatedAction } from '@/lib/api/execute-paginated-action';
|
||||
import { buildApiUrl } from '@/lib/api/query-params';
|
||||
import type {
|
||||
ShipmentItem,
|
||||
ShipmentDetail,
|
||||
@@ -112,8 +113,6 @@ interface ShipmentItemApiData {
|
||||
remarks?: string;
|
||||
}
|
||||
|
||||
type ShipmentApiPaginatedResponse = PaginatedApiResponse<ShipmentApiData>;
|
||||
|
||||
interface ShipmentApiStatsResponse {
|
||||
today_shipment_count: number;
|
||||
scheduled_count: number;
|
||||
@@ -294,60 +293,36 @@ function transformEditFormToApi(
|
||||
}
|
||||
|
||||
|
||||
// ===== 페이지네이션 타입 =====
|
||||
interface PaginationMeta {
|
||||
currentPage: number;
|
||||
lastPage: number;
|
||||
perPage: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL;
|
||||
|
||||
// ===== 출고 목록 조회 =====
|
||||
export async function getShipments(params?: {
|
||||
page?: number; perPage?: number; search?: string; status?: string;
|
||||
priority?: string; deliveryMethod?: string; scheduledFrom?: string; scheduledTo?: string;
|
||||
canShip?: boolean; depositConfirmed?: boolean; sortBy?: string; sortDir?: string;
|
||||
}): Promise<{ success: boolean; data: ShipmentItem[]; pagination: PaginationMeta; error?: string; __authError?: boolean }> {
|
||||
const emptyPagination = { currentPage: 1, lastPage: 1, perPage: 20, total: 0 };
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params?.page) searchParams.set('page', String(params.page));
|
||||
if (params?.perPage) searchParams.set('per_page', String(params.perPage));
|
||||
if (params?.search) searchParams.set('search', params.search);
|
||||
if (params?.status && params.status !== 'all') searchParams.set('status', params.status);
|
||||
if (params?.priority && params.priority !== 'all') searchParams.set('priority', params.priority);
|
||||
if (params?.deliveryMethod && params.deliveryMethod !== 'all') searchParams.set('delivery_method', params.deliveryMethod);
|
||||
if (params?.scheduledFrom) searchParams.set('scheduled_from', params.scheduledFrom);
|
||||
if (params?.scheduledTo) searchParams.set('scheduled_to', params.scheduledTo);
|
||||
if (params?.canShip !== undefined) searchParams.set('can_ship', String(params.canShip));
|
||||
if (params?.depositConfirmed !== undefined) searchParams.set('deposit_confirmed', String(params.depositConfirmed));
|
||||
if (params?.sortBy) searchParams.set('sort_by', params.sortBy);
|
||||
if (params?.sortDir) searchParams.set('sort_dir', params.sortDir);
|
||||
|
||||
const queryString = searchParams.toString();
|
||||
const result = await executeServerAction<ShipmentApiPaginatedResponse>({
|
||||
url: `${API_URL}/api/v1/shipments${queryString ? `?${queryString}` : ''}`,
|
||||
}) {
|
||||
return executePaginatedAction<ShipmentApiData, ShipmentItem>({
|
||||
url: buildApiUrl('/api/v1/shipments', {
|
||||
page: params?.page,
|
||||
per_page: params?.perPage,
|
||||
search: params?.search,
|
||||
status: params?.status !== 'all' ? params?.status : undefined,
|
||||
priority: params?.priority !== 'all' ? params?.priority : undefined,
|
||||
delivery_method: params?.deliveryMethod !== 'all' ? params?.deliveryMethod : undefined,
|
||||
scheduled_from: params?.scheduledFrom,
|
||||
scheduled_to: params?.scheduledTo,
|
||||
can_ship: params?.canShip,
|
||||
deposit_confirmed: params?.depositConfirmed,
|
||||
sort_by: params?.sortBy,
|
||||
sort_dir: params?.sortDir,
|
||||
}),
|
||||
transform: transformApiToListItem,
|
||||
errorMessage: '출고 목록 조회에 실패했습니다.',
|
||||
});
|
||||
|
||||
if (result.__authError) return { success: false, data: [], pagination: emptyPagination, __authError: true };
|
||||
if (!result.success || !result.data) return { success: false, data: [], pagination: emptyPagination, error: result.error };
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: (result.data.data || []).map(transformApiToListItem),
|
||||
pagination: {
|
||||
currentPage: result.data.current_page, lastPage: result.data.last_page,
|
||||
perPage: result.data.per_page, total: result.data.total,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// ===== 출고 통계 조회 =====
|
||||
export async function getShipmentStats(): Promise<{ success: boolean; data?: ShipmentStats; error?: string; __authError?: boolean }> {
|
||||
const result = await executeServerAction({
|
||||
url: `${API_URL}/api/v1/shipments/stats`,
|
||||
url: buildApiUrl('/api/v1/shipments/stats'),
|
||||
transform: (data: ShipmentApiStatsResponse & { total_count?: number }) => transformApiToStats(data),
|
||||
errorMessage: '출고 통계 조회에 실패했습니다.',
|
||||
});
|
||||
@@ -358,7 +333,7 @@ export async function getShipmentStats(): Promise<{ success: boolean; data?: Shi
|
||||
// ===== 상태별 통계 조회 (탭용) =====
|
||||
export async function getShipmentStatsByStatus(): Promise<{ success: boolean; data?: ShipmentStatusStats; error?: string; __authError?: boolean }> {
|
||||
const result = await executeServerAction({
|
||||
url: `${API_URL}/api/v1/shipments/stats-by-status`,
|
||||
url: buildApiUrl('/api/v1/shipments/stats-by-status'),
|
||||
transform: (data: ShipmentApiStatsByStatusResponse) => transformApiToStatsByStatus(data),
|
||||
errorMessage: '상태별 통계 조회에 실패했습니다.',
|
||||
});
|
||||
@@ -369,7 +344,7 @@ export async function getShipmentStatsByStatus(): Promise<{ success: boolean; da
|
||||
// ===== 출고 상세 조회 =====
|
||||
export async function getShipmentById(id: string): Promise<{ success: boolean; data?: ShipmentDetail; error?: string; __authError?: boolean }> {
|
||||
const result = await executeServerAction({
|
||||
url: `${API_URL}/api/v1/shipments/${id}`,
|
||||
url: buildApiUrl(`/api/v1/shipments/${id}`),
|
||||
transform: (data: ShipmentApiData) => transformApiToDetail(data),
|
||||
errorMessage: '출고 조회에 실패했습니다.',
|
||||
});
|
||||
@@ -383,7 +358,7 @@ export async function createShipment(
|
||||
): Promise<{ success: boolean; data?: ShipmentDetail; error?: string; __authError?: boolean }> {
|
||||
const apiData = transformCreateFormToApi(data);
|
||||
const result = await executeServerAction({
|
||||
url: `${API_URL}/api/v1/shipments`,
|
||||
url: buildApiUrl('/api/v1/shipments'),
|
||||
method: 'POST',
|
||||
body: apiData,
|
||||
transform: (d: ShipmentApiData) => transformApiToDetail(d),
|
||||
@@ -399,7 +374,7 @@ export async function updateShipment(
|
||||
): Promise<{ success: boolean; data?: ShipmentDetail; error?: string; __authError?: boolean }> {
|
||||
const apiData = transformEditFormToApi(data);
|
||||
const result = await executeServerAction({
|
||||
url: `${API_URL}/api/v1/shipments/${id}`,
|
||||
url: buildApiUrl(`/api/v1/shipments/${id}`),
|
||||
method: 'PUT',
|
||||
body: apiData,
|
||||
transform: (d: ShipmentApiData) => transformApiToDetail(d),
|
||||
@@ -426,7 +401,7 @@ export async function updateShipmentStatus(
|
||||
if (additionalData?.confirmedArrival) apiData.confirmed_arrival = additionalData.confirmedArrival;
|
||||
|
||||
const result = await executeServerAction({
|
||||
url: `${API_URL}/api/v1/shipments/${id}/status`,
|
||||
url: buildApiUrl(`/api/v1/shipments/${id}/status`),
|
||||
method: 'PATCH',
|
||||
body: apiData,
|
||||
transform: (d: ShipmentApiData) => transformApiToDetail(d),
|
||||
@@ -439,7 +414,7 @@ export async function updateShipmentStatus(
|
||||
// ===== 출고 삭제 =====
|
||||
export async function deleteShipment(id: string): Promise<{ success: boolean; error?: string; __authError?: boolean }> {
|
||||
const result = await executeServerAction({
|
||||
url: `${API_URL}/api/v1/shipments/${id}`,
|
||||
url: buildApiUrl(`/api/v1/shipments/${id}`),
|
||||
method: 'DELETE',
|
||||
errorMessage: '출고 삭제에 실패했습니다.',
|
||||
});
|
||||
@@ -450,7 +425,7 @@ export async function deleteShipment(id: string): Promise<{ success: boolean; er
|
||||
// ===== LOT 옵션 조회 =====
|
||||
export async function getLotOptions(): Promise<{ success: boolean; data: LotOption[]; error?: string; __authError?: boolean }> {
|
||||
const result = await executeServerAction<LotOption[]>({
|
||||
url: `${API_URL}/api/v1/shipments/options/lots`,
|
||||
url: buildApiUrl('/api/v1/shipments/options/lots'),
|
||||
errorMessage: 'LOT 옵션 조회에 실패했습니다.',
|
||||
});
|
||||
if (result.__authError) return { success: false, data: [], __authError: true };
|
||||
@@ -460,7 +435,7 @@ export async function getLotOptions(): Promise<{ success: boolean; data: LotOpti
|
||||
// ===== 물류사 옵션 조회 =====
|
||||
export async function getLogisticsOptions(): Promise<{ success: boolean; data: LogisticsOption[]; error?: string; __authError?: boolean }> {
|
||||
const result = await executeServerAction<LogisticsOption[]>({
|
||||
url: `${API_URL}/api/v1/shipments/options/logistics`,
|
||||
url: buildApiUrl('/api/v1/shipments/options/logistics'),
|
||||
errorMessage: '물류사 옵션 조회에 실패했습니다.',
|
||||
});
|
||||
if (result.__authError) return { success: false, data: [], __authError: true };
|
||||
@@ -470,7 +445,7 @@ export async function getLogisticsOptions(): Promise<{ success: boolean; data: L
|
||||
// ===== 차량 톤수 옵션 조회 =====
|
||||
export async function getVehicleTonnageOptions(): Promise<{ success: boolean; data: VehicleTonnageOption[]; error?: string; __authError?: boolean }> {
|
||||
const result = await executeServerAction<VehicleTonnageOption[]>({
|
||||
url: `${API_URL}/api/v1/shipments/options/vehicle-tonnage`,
|
||||
url: buildApiUrl('/api/v1/shipments/options/vehicle-tonnage'),
|
||||
errorMessage: '차량 톤수 옵션 조회에 실패했습니다.',
|
||||
});
|
||||
if (result.__authError) return { success: false, data: [], __authError: true };
|
||||
|
||||
Reference in New Issue
Block a user