feat: Phase 2 프론트엔드 타입 및 API 연동

- API 타입 정의 추가 (LoanDashboard, TaxSimulation)
- API 엔드포인트 함수 추가 (endpoints.ts)
- 모달 데이터 훅 생성 (useCardManagementModals.ts)

관련: docs/plans/card-management-section-plan.md
This commit is contained in:
2026-01-22 23:01:22 +09:00
parent f8d93e2851
commit fe845227df
4 changed files with 575 additions and 1 deletions

View File

@@ -0,0 +1,229 @@
'use client';
/**
* Card Management Modals Hook
*
* CEO 대시보드 카드/가지급금 관리 섹션 (cm1-cm4) 모달 데이터 관리
*
* cm1: 카드 사용액 상세 → CardTransactionDashboard API
* cm2: 가지급금 상세 → LoanDashboard API
* cm3: 법인세 예상 가중 → TaxSimulation API (corporate_tax)
* cm4: 대표자 종합세 예상 가중 → TaxSimulation API (income_tax)
*/
import { useState, useCallback } from 'react';
import {
fetchCardTransactionDashboard,
fetchLoanDashboard,
fetchTaxSimulation,
} from '@/lib/api/dashboard/endpoints';
import type {
CardDashboardDetailApiResponse,
LoanDashboardApiResponse,
TaxSimulationApiResponse,
} from '@/lib/api/dashboard/types';
// ============================================
// 타입 정의
// ============================================
/** 카드 ID 타입 */
export type CardManagementCardId = 'cm1' | 'cm2' | 'cm3' | 'cm4';
/** Hook 반환 타입 */
export interface UseCardManagementModalsReturn {
/** cm1: 카드 사용액 상세 데이터 */
cm1Data: CardDashboardDetailApiResponse | null;
/** cm2: 가지급금 상세 데이터 */
cm2Data: LoanDashboardApiResponse | null;
/** cm3: 법인세 시뮬레이션 데이터 */
cm3Data: TaxSimulationApiResponse | null;
/** cm4: 소득세 시뮬레이션 데이터 (cm3와 동일 소스, 다른 표시) */
cm4Data: TaxSimulationApiResponse | null;
/** 로딩 상태 */
loading: boolean;
/** 에러 메시지 */
error: string | null;
/** 특정 카드의 모달 데이터 조회 */
fetchModalData: (cardId: CardManagementCardId) => Promise<void>;
/** 모든 카드 데이터 조회 */
fetchAllData: () => Promise<void>;
/** 데이터 초기화 */
clearData: (cardId?: CardManagementCardId) => void;
}
// ============================================
// Hook 구현
// ============================================
/**
* 카드/가지급금 관리 섹션 모달 데이터 관리 Hook
*
* @example
* const { cm1Data, cm2Data, loading, fetchModalData } = useCardManagementModals();
*
* // 카드 클릭 시
* await fetchModalData('cm1');
*/
export function useCardManagementModals(): UseCardManagementModalsReturn {
// 각 카드별 데이터 상태
const [cm1Data, setCm1Data] = useState<CardDashboardDetailApiResponse | null>(null);
const [cm2Data, setCm2Data] = useState<LoanDashboardApiResponse | null>(null);
const [cm3Data, setCm3Data] = useState<TaxSimulationApiResponse | null>(null);
const [cm4Data, setCm4Data] = useState<TaxSimulationApiResponse | null>(null);
// 공통 상태
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
/**
* cm1: 카드 사용액 상세 데이터 조회
*/
const fetchCm1Data = useCallback(async () => {
try {
const response = await fetchCardTransactionDashboard();
if (response.success) {
setCm1Data(response.data);
} else {
throw new Error(response.message || '카드 거래 데이터 조회 실패');
}
} catch (err) {
const errorMessage = err instanceof Error ? err.message : '카드 거래 데이터 조회 실패';
console.error('[useCardManagementModals] cm1 error:', err);
throw new Error(errorMessage);
}
}, []);
/**
* cm2: 가지급금 상세 데이터 조회
*/
const fetchCm2Data = useCallback(async () => {
try {
const response = await fetchLoanDashboard();
if (response.success) {
setCm2Data(response.data);
} else {
throw new Error(response.message || '가지급금 데이터 조회 실패');
}
} catch (err) {
const errorMessage = err instanceof Error ? err.message : '가지급금 데이터 조회 실패';
console.error('[useCardManagementModals] cm2 error:', err);
throw new Error(errorMessage);
}
}, []);
/**
* cm3 & cm4: 세금 시뮬레이션 데이터 조회
* cm3은 법인세 (corporate_tax), cm4는 소득세 (income_tax) 사용
*/
const fetchTaxData = useCallback(async () => {
try {
const response = await fetchTaxSimulation();
if (response.success) {
// cm3, cm4 모두 같은 데이터 소스 사용 (표시만 다름)
setCm3Data(response.data);
setCm4Data(response.data);
} else {
throw new Error(response.message || '세금 시뮬레이션 데이터 조회 실패');
}
} catch (err) {
const errorMessage = err instanceof Error ? err.message : '세금 시뮬레이션 데이터 조회 실패';
console.error('[useCardManagementModals] tax simulation error:', err);
throw new Error(errorMessage);
}
}, []);
/**
* 특정 카드의 모달 데이터 조회
*/
const fetchModalData = useCallback(
async (cardId: CardManagementCardId) => {
setLoading(true);
setError(null);
try {
switch (cardId) {
case 'cm1':
await fetchCm1Data();
break;
case 'cm2':
await fetchCm2Data();
break;
case 'cm3':
case 'cm4':
// cm3, cm4는 같은 API 사용
await fetchTaxData();
break;
default:
throw new Error(`알 수 없는 카드 ID: ${cardId}`);
}
} catch (err) {
const errorMessage = err instanceof Error ? err.message : '데이터 조회 실패';
setError(errorMessage);
} finally {
setLoading(false);
}
},
[fetchCm1Data, fetchCm2Data, fetchTaxData]
);
/**
* 모든 카드 데이터 조회 (초기 로드용)
*/
const fetchAllData = useCallback(async () => {
setLoading(true);
setError(null);
try {
// 병렬로 모든 데이터 조회
await Promise.all([fetchCm1Data(), fetchCm2Data(), fetchTaxData()]);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : '데이터 조회 실패';
setError(errorMessage);
} finally {
setLoading(false);
}
}, [fetchCm1Data, fetchCm2Data, fetchTaxData]);
/**
* 데이터 초기화
*/
const clearData = useCallback((cardId?: CardManagementCardId) => {
if (!cardId) {
// 전체 초기화
setCm1Data(null);
setCm2Data(null);
setCm3Data(null);
setCm4Data(null);
setError(null);
} else {
// 특정 카드만 초기화
switch (cardId) {
case 'cm1':
setCm1Data(null);
break;
case 'cm2':
setCm2Data(null);
break;
case 'cm3':
setCm3Data(null);
break;
case 'cm4':
setCm4Data(null);
break;
}
}
}, []);
return {
cm1Data,
cm2Data,
cm3Data,
cm4Data,
loading,
error,
fetchModalData,
fetchAllData,
clearData,
};
}