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:
유병철
2026-02-12 20:59:59 +09:00
parent 31be9d4a25
commit cbb38d48b9
51 changed files with 1050 additions and 1405 deletions

View File

@@ -1,6 +1,7 @@
'use server';
import { executeServerAction } from '@/lib/api/execute-server-action';
import { buildApiUrl } from '@/lib/api/query-params';
import type { PaginatedApiResponse } from '@/lib/api/types';
// ============================================================================
@@ -777,8 +778,6 @@ function transformQuoteItemForSelect(apiItem: ApiQuoteItem): QuotationItem {
// API 함수
// ============================================================================
const API_URL = process.env.NEXT_PUBLIC_API_URL;
/**
* 수주 목록 조회
*/
@@ -797,21 +796,18 @@ export async function getOrders(params?: {
error?: string;
__authError?: boolean;
}> {
const searchParams = new URLSearchParams();
if (params?.page) searchParams.set('page', String(params.page));
if (params?.size) searchParams.set('size', String(params.size));
if (params?.q) searchParams.set('q', params.q);
if (params?.status) {
const apiStatus = FRONTEND_TO_API_STATUS[params.status as OrderStatus];
if (apiStatus) searchParams.set('status', apiStatus);
}
if (params?.order_type) searchParams.set('order_type', params.order_type);
if (params?.client_id) searchParams.set('client_id', String(params.client_id));
if (params?.date_from) searchParams.set('date_from', params.date_from);
if (params?.date_to) searchParams.set('date_to', params.date_to);
const apiStatus = params?.status ? FRONTEND_TO_API_STATUS[params.status as OrderStatus] : undefined;
const result = await executeServerAction<PaginatedApiResponse<ApiOrder>>({
url: `${API_URL}/api/v1/orders?${searchParams.toString()}`,
url: buildApiUrl('/api/v1/orders', {
page: params?.page,
size: params?.size,
q: params?.q,
status: apiStatus,
order_type: params?.order_type,
client_id: params?.client_id,
date_from: params?.date_from,
date_to: params?.date_to,
}),
errorMessage: '목록 조회에 실패했습니다.',
});
if (result.__authError) return { success: false, __authError: true };
@@ -837,7 +833,7 @@ export async function getOrderById(id: string): Promise<{
__authError?: boolean;
}> {
const result = await executeServerAction({
url: `${API_URL}/api/v1/orders/${id}`,
url: buildApiUrl(`/api/v1/orders/${id}`),
transform: (data: ApiOrder) => transformApiToFrontend(data),
errorMessage: '조회에 실패했습니다.',
});
@@ -856,7 +852,7 @@ export async function createOrder(data: OrderFormData | Record<string, unknown>)
}> {
const apiData = transformFrontendToApi(data);
const result = await executeServerAction({
url: `${API_URL}/api/v1/orders`,
url: buildApiUrl('/api/v1/orders'),
method: 'POST',
body: apiData,
transform: (d: ApiOrder) => transformApiToFrontend(d),
@@ -877,7 +873,7 @@ export async function updateOrder(id: string, data: OrderFormData | Record<strin
}> {
const apiData = transformFrontendToApi(data);
const result = await executeServerAction({
url: `${API_URL}/api/v1/orders/${id}`,
url: buildApiUrl(`/api/v1/orders/${id}`),
method: 'PUT',
body: apiData,
transform: (d: ApiOrder) => transformApiToFrontend(d),
@@ -896,7 +892,7 @@ export async function deleteOrder(id: string): Promise<{
__authError?: boolean;
}> {
const result = await executeServerAction({
url: `${API_URL}/api/v1/orders/${id}`,
url: buildApiUrl(`/api/v1/orders/${id}`),
method: 'DELETE',
errorMessage: '삭제에 실패했습니다.',
});
@@ -918,7 +914,7 @@ export async function updateOrderStatus(id: string, status: OrderStatus): Promis
return { success: false, error: '유효하지 않은 상태입니다.' };
}
const result = await executeServerAction({
url: `${API_URL}/api/v1/orders/${id}/status`,
url: buildApiUrl(`/api/v1/orders/${id}/status`),
method: 'PATCH',
body: { status: apiStatus },
transform: (d: ApiOrder) => transformApiToFrontend(d),
@@ -938,7 +934,7 @@ export async function getOrderStats(): Promise<{
__authError?: boolean;
}> {
const result = await executeServerAction<ApiOrderStats>({
url: `${API_URL}/api/v1/orders/stats`,
url: buildApiUrl('/api/v1/orders/stats'),
errorMessage: '통계 조회에 실패했습니다.',
});
if (result.__authError) return { success: false, __authError: true };
@@ -1009,7 +1005,7 @@ export async function createOrderFromQuote(
if (data?.memo) apiData.memo = data.memo;
const result = await executeServerAction({
url: `${API_URL}/api/v1/orders/from-quote/${quoteId}`,
url: buildApiUrl(`/api/v1/orders/from-quote/${quoteId}`),
method: 'POST',
body: apiData,
transform: (d: ApiOrder) => transformApiToFrontend(d),
@@ -1049,7 +1045,7 @@ export async function createProductionOrder(
if (data?.memo) apiData.memo = data.memo;
const result = await executeServerAction<ApiProductionOrderResponse>({
url: `${API_URL}/api/v1/orders/${orderId}/production-order`,
url: buildApiUrl(`/api/v1/orders/${orderId}/production-order`),
method: 'POST',
body: apiData,
errorMessage: '생산지시 생성에 실패했습니다.',
@@ -1087,7 +1083,7 @@ export async function revertProductionOrder(orderId: string): Promise<{
previous_status: string;
}
const result = await executeServerAction<RevertResponse>({
url: `${API_URL}/api/v1/orders/${orderId}/revert-production`,
url: buildApiUrl(`/api/v1/orders/${orderId}/revert-production`),
method: 'POST',
errorMessage: '생산지시 되돌리기에 실패했습니다.',
});
@@ -1118,7 +1114,7 @@ export async function revertOrderConfirmation(orderId: string): Promise<{
}> {
interface RevertConfirmResponse { order: ApiOrder; previous_status: string }
const result = await executeServerAction<RevertConfirmResponse>({
url: `${API_URL}/api/v1/orders/${orderId}/revert-confirmation`,
url: buildApiUrl(`/api/v1/orders/${orderId}/revert-confirmation`),
method: 'POST',
errorMessage: '수주확정 되돌리기에 실패했습니다.',
});
@@ -1144,7 +1140,7 @@ export async function getQuoteByIdForSelect(id: string): Promise<{
__authError?: boolean;
}> {
const result = await executeServerAction({
url: `${API_URL}/api/v1/quotes/${id}?with_items=true`,
url: buildApiUrl(`/api/v1/quotes/${id}`, { with_items: true }),
transform: (data: ApiQuoteForSelect) => transformQuoteForSelect(data),
errorMessage: '견적 조회에 실패했습니다.',
});
@@ -1166,16 +1162,15 @@ export async function getQuotesForSelect(params?: {
error?: string;
__authError?: boolean;
}> {
const searchParams = new URLSearchParams();
searchParams.set('status', 'finalized');
searchParams.set('with_items', 'true');
searchParams.set('for_order', 'true');
if (params?.q) searchParams.set('q', params.q);
if (params?.page) searchParams.set('page', String(params.page));
if (params?.size) searchParams.set('size', String(params.size || 50));
const result = await executeServerAction<PaginatedApiResponse<ApiQuoteForSelect>>({
url: `${API_URL}/api/v1/quotes?${searchParams.toString()}`,
url: buildApiUrl('/api/v1/quotes', {
status: 'finalized',
with_items: 'true',
for_order: 'true',
q: params?.q,
page: params?.page,
size: params?.size || 50,
}),
errorMessage: '견적 목록 조회에 실패했습니다.',
});
if (result.__authError) return { success: false, __authError: true };