Files
sam-react-prod/src/lib/api/execute-paginated-action.ts

91 lines
2.5 KiB
TypeScript
Raw Normal View History

/**
* Server Action
*
* executeServerAction + toPaginationMeta
* 50+ 15~25 5~8 .
*
* 범위: 신규 ( )
*
* @example
* ```typescript
* // Before: ~20줄
* const result = await executeServerAction({
* url: `${API_URL}/api/v1/bills?${queryString}`,
* transform: (data: BillPaginatedResponse) => ({
* items: (data?.data || []).map(transformApiToFrontend),
* pagination: { currentPage: data?.current_page || 1, ... },
* }),
* errorMessage: '어음 목록 조회에 실패했습니다.',
* });
* return { success: result.success, data: result.data?.items || [], ... };
*
* // After: ~5줄
* return executePaginatedAction({
* url: buildApiUrl('/api/v1/bills', params),
* transform: transformApiToFrontend,
* errorMessage: '어음 목록 조회에 실패했습니다.',
* });
* ```
*/
import { executeServerAction } from './execute-server-action';
import { toPaginationMeta, type PaginatedApiResponse, type PaginationMeta } from './types';
// ===== 반환 타입 =====
export interface PaginatedActionResult<T> {
success: boolean;
data: T[];
pagination: PaginationMeta;
error?: string;
__authError?: boolean;
}
// ===== 옵션 타입 =====
interface PaginatedActionOptions<TApi, TResult> {
/** API URL (전체 경로) */
url: string;
/** 개별 아이템 변환 함수 (API 응답 아이템 → 프론트엔드 타입) */
transform: (item: TApi) => TResult;
/** 실패 시 기본 에러 메시지 */
errorMessage: string;
}
const DEFAULT_PAGINATION: PaginationMeta = {
currentPage: 1,
lastPage: 1,
perPage: 20,
total: 0,
};
/**
* Server Action
*
* executeServerAction으로 API data transform toPaginationMeta
*/
export async function executePaginatedAction<TApi, TResult>(
options: PaginatedActionOptions<TApi, TResult>
): Promise<PaginatedActionResult<TResult>> {
const { url, transform, errorMessage } = options;
const result = await executeServerAction<PaginatedApiResponse<TApi>>({
url,
errorMessage,
});
if (!result.success || !result.data) {
return {
success: result.success,
data: [],
pagination: DEFAULT_PAGINATION,
error: result.error,
__authError: result.__authError,
};
}
return {
success: true,
data: (result.data.data || []).map(transform),
pagination: toPaginationMeta(result.data),
};
}