refactor(WEB): SearchableSelectionModal 공통화 및 actions lookup 통합
- SearchableSelectionModal<T> 제네릭 컴포넌트 추출 (organisms) - 검색 모달 5개 리팩토링: SupplierSearch, QuotationSelect, SalesOrderSelect, OrderSelect, ItemSearch - shared-lookups API 유틸 추가 (거래처/품목/수주 등 공통 조회) - create-crud-service 확장 (lookup, search 메서드) - actions.ts 20+개 파일 lookup 패턴 통일 - 공통 페이지 패턴 가이드 문서 추가 - CLAUDE.md Common Component Usage Rules 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
|
||||
import { executeServerAction } from '@/lib/api/execute-server-action';
|
||||
import type { PaginatedApiResponse } from '@/lib/api/types';
|
||||
import { isNextRedirectError } from '@/lib/utils/redirect-error';
|
||||
import { cookies } from 'next/headers';
|
||||
import type {
|
||||
@@ -26,18 +27,6 @@ import type {
|
||||
EmployeeOption,
|
||||
} from './types';
|
||||
|
||||
// ============================================
|
||||
// API 응답 타입 정의
|
||||
// ============================================
|
||||
|
||||
interface PaginatedResponse<T> {
|
||||
current_page: number;
|
||||
data: T[];
|
||||
total: number;
|
||||
per_page: number;
|
||||
last_page: number;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 헬퍼 함수
|
||||
// ============================================
|
||||
@@ -171,7 +160,7 @@ interface EmployeeApiData {
|
||||
}
|
||||
|
||||
export async function getEmployeesForAttendance(): Promise<EmployeeOption[]> {
|
||||
const result = await executeServerAction<PaginatedResponse<EmployeeApiData>>({
|
||||
const result = await executeServerAction<PaginatedApiResponse<EmployeeApiData>>({
|
||||
url: `${API_URL}/v1/employees?per_page=100&status=active`,
|
||||
errorMessage: '사원 목록 조회에 실패했습니다.',
|
||||
});
|
||||
@@ -208,7 +197,7 @@ export async function getAttendances(params?: {
|
||||
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<PaginatedResponse<AttendanceApiData>>({
|
||||
const result = await executeServerAction<PaginatedApiResponse<AttendanceApiData>>({
|
||||
url: `${API_URL}/v1/attendances?${searchParams.toString()}`,
|
||||
errorMessage: '근태 목록 조회에 실패했습니다.',
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
|
||||
import { executeServerAction } from '@/lib/api/execute-server-action';
|
||||
import type { PaginatedApiResponse } from '@/lib/api/types';
|
||||
import { isNextRedirectError } from '@/lib/utils/redirect-error';
|
||||
import { cookies } from 'next/headers';
|
||||
import type { Employee, EmployeeFormData, EmployeeStats } from './types';
|
||||
@@ -32,14 +33,6 @@ interface ApiResponse<T> {
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface PaginatedResponse<T> {
|
||||
current_page: number;
|
||||
data: T[];
|
||||
total: number;
|
||||
per_page: number;
|
||||
last_page: number;
|
||||
}
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL;
|
||||
|
||||
// ============================================
|
||||
@@ -61,7 +54,7 @@ export async function getEmployees(params?: {
|
||||
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<PaginatedResponse<EmployeeApiData>>({
|
||||
const result = await executeServerAction<PaginatedApiResponse<EmployeeApiData>>({
|
||||
url: `${API_URL}/api/v1/employees?${searchParams.toString()}`,
|
||||
errorMessage: '직원 목록 조회에 실패했습니다.',
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
|
||||
import { executeServerAction } from '@/lib/api/execute-server-action';
|
||||
import type { PaginatedApiResponse } from '@/lib/api/types';
|
||||
|
||||
// ============================================
|
||||
// 타입 정의
|
||||
@@ -164,14 +165,6 @@ interface ApiResponse<T> {
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface PaginatedResponse<T> {
|
||||
current_page: number;
|
||||
data: T[];
|
||||
total: number;
|
||||
per_page: number;
|
||||
last_page: number;
|
||||
}
|
||||
|
||||
// API URL
|
||||
const API_URL = `${process.env.NEXT_PUBLIC_API_URL}/api`;
|
||||
|
||||
@@ -259,7 +252,7 @@ export async function getLeaves(params?: GetLeavesParams): Promise<{
|
||||
if (params.page) searchParams.append('page', params.page.toString());
|
||||
}
|
||||
const queryString = searchParams.toString();
|
||||
const result = await executeServerAction<PaginatedResponse<Record<string, unknown>>>({
|
||||
const result = await executeServerAction<PaginatedApiResponse<Record<string, unknown>>>({
|
||||
url: `${API_URL}/v1/leaves${queryString ? `?${queryString}` : ''}`,
|
||||
errorMessage: '휴가 목록 조회에 실패했습니다.',
|
||||
});
|
||||
@@ -420,7 +413,7 @@ export async function getLeaveBalances(params?: GetLeaveBalancesParams): Promise
|
||||
if (params.page) searchParams.append('page', params.page.toString());
|
||||
}
|
||||
const queryString = searchParams.toString();
|
||||
const result = await executeServerAction<PaginatedResponse<Record<string, unknown>>>({
|
||||
const result = await executeServerAction<PaginatedApiResponse<Record<string, unknown>>>({
|
||||
url: `${API_URL}/v1/leaves/balances${queryString ? `?${queryString}` : ''}`,
|
||||
errorMessage: '휴가 사용현황 조회에 실패했습니다.',
|
||||
});
|
||||
@@ -539,7 +532,7 @@ export async function getLeaveGrants(params?: GetLeaveGrantsParams): Promise<{
|
||||
if (params.page) searchParams.append('page', params.page.toString());
|
||||
}
|
||||
const queryString = searchParams.toString();
|
||||
const result = await executeServerAction<PaginatedResponse<Record<string, unknown>>>({
|
||||
const result = await executeServerAction<PaginatedApiResponse<Record<string, unknown>>>({
|
||||
url: `${API_URL}/v1/leaves/grants${queryString ? `?${queryString}` : ''}`,
|
||||
errorMessage: '휴가 부여 이력 조회에 실패했습니다.',
|
||||
});
|
||||
@@ -620,8 +613,8 @@ export interface EmployeeOption {
|
||||
|
||||
/** 활성 직원 목록 조회 (휴가 신청/부여용) */
|
||||
export async function getActiveEmployees(): Promise<{ success: boolean; data?: EmployeeOption[]; error?: string }> {
|
||||
interface EmployeePaginatedResponse { data: Record<string, unknown>[]; total: number }
|
||||
const result = await executeServerAction<EmployeePaginatedResponse>({
|
||||
interface EmployeePaginatedApiResponse { data: Record<string, unknown>[]; total: number }
|
||||
const result = await executeServerAction<EmployeePaginatedApiResponse>({
|
||||
url: `${API_URL}/v1/employees?status=active&per_page=100`,
|
||||
errorMessage: '직원 목록 조회에 실패했습니다.',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user