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

@@ -12,6 +12,7 @@
import { executeServerAction, type ActionResult } from '@/lib/api/execute-server-action';
import { buildApiUrl } from '@/lib/api/query-params';
import type { PaginatedApiResponse } from '@/lib/api/types';
import type { ApprovalRecord, ApprovalType, ApprovalStatus } from './types';
@@ -105,8 +106,6 @@ function transformApiToFrontend(data: InboxApiData): ApprovalRecord {
};
}
const API_URL = process.env.NEXT_PUBLIC_API_URL;
// ============================================
// API 함수
// ============================================
@@ -115,20 +114,16 @@ export async function getInbox(params?: {
page?: number; per_page?: number; search?: string; status?: string;
approval_type?: string; sort_by?: string; sort_dir?: 'asc' | 'desc';
}): Promise<{ data: ApprovalRecord[]; total: number; lastPage: number; __authError?: boolean }> {
const searchParams = new URLSearchParams();
if (params?.page) searchParams.set('page', String(params.page));
if (params?.per_page) searchParams.set('per_page', String(params.per_page));
if (params?.search) searchParams.set('search', params.search);
if (params?.status && params.status !== 'all') {
const apiStatus = mapTabToApiStatus(params.status);
if (apiStatus) searchParams.set('status', apiStatus);
}
if (params?.approval_type && params.approval_type !== 'all') searchParams.set('approval_type', params.approval_type);
if (params?.sort_by) searchParams.set('sort_by', params.sort_by);
if (params?.sort_dir) searchParams.set('sort_dir', params.sort_dir);
const result = await executeServerAction<PaginatedApiResponse<InboxApiData>>({
url: `${API_URL}/api/v1/approvals/inbox?${searchParams.toString()}`,
url: buildApiUrl('/api/v1/approvals/inbox', {
page: params?.page,
per_page: params?.per_page,
search: params?.search,
status: params?.status && params.status !== 'all' ? mapTabToApiStatus(params.status) : undefined,
approval_type: params?.approval_type !== 'all' ? params?.approval_type : undefined,
sort_by: params?.sort_by,
sort_dir: params?.sort_dir,
}),
errorMessage: '결재함 목록 조회에 실패했습니다.',
});
@@ -144,7 +139,7 @@ export async function getInbox(params?: {
export async function getInboxSummary(): Promise<InboxSummary | null> {
const result = await executeServerAction<InboxSummary>({
url: `${API_URL}/api/v1/approvals/inbox/summary`,
url: buildApiUrl('/api/v1/approvals/inbox/summary'),
errorMessage: '결재함 통계 조회에 실패했습니다.',
});
return result.success ? result.data || null : null;
@@ -152,7 +147,7 @@ export async function getInboxSummary(): Promise<InboxSummary | null> {
export async function approveDocument(id: string, comment?: string): Promise<ActionResult> {
return executeServerAction({
url: `${API_URL}/api/v1/approvals/${id}/approve`,
url: buildApiUrl(`/api/v1/approvals/${id}/approve`),
method: 'POST',
body: { comment: comment || '' },
errorMessage: '승인 처리에 실패했습니다.',
@@ -162,7 +157,7 @@ export async function approveDocument(id: string, comment?: string): Promise<Act
export async function rejectDocument(id: string, comment: string): Promise<ActionResult> {
if (!comment?.trim()) return { success: false, error: '반려 사유를 입력해주세요.' };
return executeServerAction({
url: `${API_URL}/api/v1/approvals/${id}/reject`,
url: buildApiUrl(`/api/v1/approvals/${id}/reject`),
method: 'POST',
body: { comment },
errorMessage: '반려 처리에 실패했습니다.',