From bf11b13aee5439ab4fd7e3131d3cbfb480e99f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Thu, 22 Jan 2026 23:11:19 +0900 Subject: [PATCH] =?UTF-8?q?feat(CEODashboard):=20Phase=203=20-=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C/=EA=B0=80=EC=A7=80=EA=B8=89=EA=B8=88=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=AA=A8=EB=8B=AC=20API=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 3: Modal Component Integration (cm1-cm4) ### 새 파일 - cardManagementConfigTransformers.ts: API 응답을 모달 설정으로 변환 - transformCm1ModalConfig: 카드 사용액 상세 - transformCm2ModalConfig: 가지급금 상세 - transformCm3ModalConfig: 법인세 시뮬레이션 - transformCm4ModalConfig: 종합소득세 시뮬레이션 - 유틸리티: formatKoreanCurrency, calculateChangeRate, formatPercentage ### 수정 파일 - cardManagementConfigs.ts: - getCardManagementModalConfigWithData() 추가 - API 데이터 우선, 없을 시 fallback 설정 사용 - useCardManagementModals.ts: - fetchModalData()가 데이터 직접 반환하도록 수정 - CardManagementModalData 타입 추가 - CEODashboard.tsx: - useCardManagementModals 훅 연동 - handleCardManagementCardClick에서 API 데이터 사용 --- .../business/CEODashboard/CEODashboard.tsx | 32 +- .../cardManagementConfigTransformers.ts | 477 ++++++++++++++++++ .../modalConfigs/cardManagementConfigs.ts | 71 ++- .../CEODashboard/modalConfigs/index.ts | 2 +- src/hooks/useCardManagementModals.ts | 48 +- 5 files changed, 608 insertions(+), 22 deletions(-) create mode 100644 src/components/business/CEODashboard/modalConfigs/cardManagementConfigTransformers.ts diff --git a/src/components/business/CEODashboard/CEODashboard.tsx b/src/components/business/CEODashboard/CEODashboard.tsx index f1063822..41f1cab7 100644 --- a/src/components/business/CEODashboard/CEODashboard.tsx +++ b/src/components/business/CEODashboard/CEODashboard.tsx @@ -25,10 +25,13 @@ import { DEFAULT_DASHBOARD_SETTINGS } from './types'; import { ScheduleDetailModal, DetailModal } from './modals'; import { DashboardSettingsDialog } from './dialogs/DashboardSettingsDialog'; import { mockData } from './mockData'; -import { useCEODashboard, useTodayIssue, useCalendar, useVat, useEntertainment, useWelfare, useWelfareDetail } from '@/hooks/useCEODashboard'; +import { useCEODashboard, useTodayIssue, useCalendar, useVat, useEntertainment, useWelfare, useWelfareDetail, useMonthlyExpenseDetail } from '@/hooks/useCEODashboard'; +import { useCardManagementModals, type CardManagementCardId } from '@/hooks/useCardManagementModals'; +import type { MonthlyExpenseCardId } from '@/hooks/useCEODashboard'; import { getMonthlyExpenseModalConfig, getCardManagementModalConfig, + getCardManagementModalConfigWithData, getEntertainmentModalConfig, getWelfareModalConfig, getVatModalConfig, @@ -57,6 +60,9 @@ export function CEODashboard() { // Welfare API Hook (Phase 2) const welfareData = useWelfare(); + // Card Management Modal API Hook (Phase 3) + const cardManagementModals = useCardManagementModals(); + // 전체 로딩 상태 (하나라도 로딩 중이면 스켈레톤 표시) const isLoading = useMemo(() => { return ( @@ -105,6 +111,9 @@ export function CEODashboard() { calculationType: dashboardSettings.welfare.calculationType, }); + // MonthlyExpenseDetail Hook (당월 예상 지출 모달용 상세 API) + const monthlyExpenseDetailData = useMonthlyExpenseDetail(); + // 상세 모달 상태 const [isDetailModalOpen, setIsDetailModalOpen] = useState(false); const [detailModalConfig, setDetailModalConfig] = useState(null); @@ -152,13 +161,17 @@ export function CEODashboard() { }, []); // 당월 예상 지출 카드 클릭 (개별 카드 클릭 시 상세 모달) - const handleMonthlyExpenseCardClick = useCallback((cardId: string) => { - const config = getMonthlyExpenseModalConfig(cardId); + const handleMonthlyExpenseCardClick = useCallback(async (cardId: string) => { + // 1. 먼저 API에서 데이터 fetch 시도 + const apiConfig = await monthlyExpenseDetailData.fetchData(cardId as MonthlyExpenseCardId); + + // 2. API 데이터가 있으면 사용, 없으면 fallback config 사용 + const config = apiConfig ?? getMonthlyExpenseModalConfig(cardId); if (config) { setDetailModalConfig(config); setIsDetailModalOpen(true); } - }, []); + }, [monthlyExpenseDetailData]); // 당월 예상 지출 클릭 (deprecated - 개별 카드 클릭으로 대체) const handleMonthlyExpenseClick = useCallback(() => { @@ -166,13 +179,18 @@ export function CEODashboard() { }, []); // 카드/가지급금 관리 카드 클릭 (개별 카드 클릭 시 상세 모달) - const handleCardManagementCardClick = useCallback((cardId: string) => { - const config = getCardManagementModalConfig(cardId); + const handleCardManagementCardClick = useCallback(async (cardId: string) => { + // 1. API에서 데이터 fetch (데이터 직접 반환) + const modalData = await cardManagementModals.fetchModalData(cardId as CardManagementCardId); + + // 2. API 데이터로 config 생성 (데이터 없으면 fallback) + const config = getCardManagementModalConfigWithData(cardId, modalData); + if (config) { setDetailModalConfig(config); setIsDetailModalOpen(true); } - }, []); + }, [cardManagementModals]); // 접대비 현황 카드 클릭 (개별 카드 클릭 시 상세 모달) const handleEntertainmentCardClick = useCallback((cardId: string) => { diff --git a/src/components/business/CEODashboard/modalConfigs/cardManagementConfigTransformers.ts b/src/components/business/CEODashboard/modalConfigs/cardManagementConfigTransformers.ts new file mode 100644 index 00000000..287e1a13 --- /dev/null +++ b/src/components/business/CEODashboard/modalConfigs/cardManagementConfigTransformers.ts @@ -0,0 +1,477 @@ +'use client'; + +/** + * Card Management Modal Config Transformers + * + * API 응답 데이터를 DetailModalConfig로 변환하는 함수들 + * + * cm1: CardDashboardDetailApiResponse → DetailModalConfig + * cm2: LoanDashboardApiResponse → DetailModalConfig + * cm3: TaxSimulationApiResponse → DetailModalConfig (법인세) + * cm4: TaxSimulationApiResponse → DetailModalConfig (종합소득세) + */ + +import type { DetailModalConfig } from '../types'; +import type { + CardDashboardDetailApiResponse, + LoanDashboardApiResponse, + TaxSimulationApiResponse, +} from '@/lib/api/dashboard/types'; + +// ============================================ +// 유틸리티 함수 +// ============================================ + +/** + * 숫자를 한국식 단위로 포맷팅 (억, 만) + */ +function formatKoreanCurrency(value: number): string { + if (value >= 100000000) { + const billions = value / 100000000; + return `${billions.toFixed(1)}억원`; + } + if (value >= 10000) { + const thousands = value / 10000; + return `${thousands.toFixed(0)}만원`; + } + return `${value.toLocaleString()}원`; +} + +/** + * 전월 대비 증감률 계산 + */ +function calculateChangeRate(current: number, previous: number): number { + if (previous === 0) return current > 0 ? 100 : 0; + return ((current - previous) / previous) * 100; +} + +/** + * 퍼센트 값을 포맷팅 + */ +function formatPercentage(value: number, showSign = true): string { + const sign = showSign && value > 0 ? '+' : ''; + return `${sign}${value.toFixed(1)}%`; +} + +// ============================================ +// cm1: 카드 사용 상세 모달 변환기 +// ============================================ + +/** + * 카드 대시보드 API 응답을 cm1 모달 설정으로 변환 + */ +export function transformCm1ModalConfig( + data: CardDashboardDetailApiResponse +): DetailModalConfig { + const { summary, monthly_trend, by_user, items } = data; + + // 전월 대비 증감률 계산 + const changeRate = calculateChangeRate( + summary.current_month_total, + summary.previous_month_total + ); + + // 미정리 건수 계산 (usage_type이 '미설정'인 항목) + const unprocessedCount = items.filter( + (item) => item.usage_type === '미설정' || !item.usage_type + ).length; + + // 사용자별 비율 계산 + const totalAmount = by_user.reduce((sum, user) => sum + user.amount, 0); + const pieChartData = by_user.map((user) => ({ + name: user.user_name, + value: user.amount, + percentage: totalAmount > 0 ? Math.round((user.amount / totalAmount) * 100) : 0, + color: user.color, + })); + + // 사용자 필터 옵션 동적 생성 + const userFilterOptions = [ + { value: 'all', label: '전체' }, + ...by_user.map((user) => ({ + value: user.user_name, + label: user.user_name, + })), + ]; + + // 테이블 데이터 매핑 + const tableData = items.map((item) => ({ + cardName: item.card_name, + user: item.user_name, + date: item.transaction_date, + store: item.merchant_name, + amount: item.amount, + usageType: item.usage_type || '미설정', + })); + + return { + title: '카드 사용 상세', + summaryCards: [ + { label: '당월 카드 사용', value: summary.current_month_total, unit: '원' }, + { + label: '전월 대비', + value: formatPercentage(changeRate), + isComparison: true, + isPositive: changeRate >= 0, + }, + { label: '미정리 건수', value: `${unprocessedCount}건` }, + ], + barChart: { + title: '월별 카드 사용 추이', + data: monthly_trend.map((trend) => ({ + name: trend.label, + value: trend.amount, + })), + dataKey: 'value', + xAxisKey: 'name', + color: '#60A5FA', + }, + pieChart: { + title: '사용자별 카드 사용 비율', + data: pieChartData, + }, + table: { + title: '카드 사용 내역', + columns: [ + { key: 'no', label: 'No.', align: 'center' }, + { key: 'cardName', label: '카드명', align: 'left' }, + { key: 'user', label: '사용자', align: 'center' }, + { key: 'date', label: '사용일시', align: 'center', format: 'date' }, + { key: 'store', label: '가맹점명', align: 'left' }, + { key: 'amount', label: '사용금액', align: 'right', format: 'currency' }, + { key: 'usageType', label: '사용유형', align: 'center', highlightValue: '미설정' }, + ], + data: tableData, + filters: [ + { + key: 'user', + options: userFilterOptions, + defaultValue: 'all', + }, + { + key: 'usageType', + options: [ + { value: 'all', label: '전체' }, + { value: '미설정', label: '미설정' }, + { value: '복리후생비', label: '복리후생비' }, + { value: '접대비', label: '접대비' }, + { value: '소모품비', label: '소모품비' }, + { value: '교통비', label: '교통비' }, + ], + defaultValue: 'all', + }, + { + key: 'sortOrder', + options: [ + { value: 'latest', label: '최신순' }, + { value: 'oldest', label: '등록순' }, + { value: 'amountDesc', label: '금액 높은순' }, + { value: 'amountAsc', label: '금액 낮은순' }, + ], + defaultValue: 'latest', + }, + ], + showTotal: true, + totalLabel: '합계', + totalValue: summary.current_month_total, + totalColumnKey: 'amount', + }, + }; +} + +// ============================================ +// cm2: 가지급금 상세 모달 변환기 +// ============================================ + +/** + * 가지급금 대시보드 API 응답을 cm2 모달 설정으로 변환 + */ +export function transformCm2ModalConfig( + data: LoanDashboardApiResponse +): DetailModalConfig { + const { summary, items } = data; + + // 테이블 데이터 매핑 + const tableData = items.map((item) => ({ + date: item.loan_date, + target: item.user_name, + category: '-', // API에서 별도 필드 없음 + amount: item.amount, + status: item.status_label || item.status, + content: item.description, + })); + + // 대상 필터 옵션 동적 생성 + const uniqueTargets = [...new Set(items.map((item) => item.user_name))]; + const targetFilterOptions = [ + { value: 'all', label: '전체' }, + ...uniqueTargets.map((target) => ({ + value: target, + label: target, + })), + ]; + + return { + title: '가지급금 상세', + summaryCards: [ + { label: '가지급금', value: formatKoreanCurrency(summary.total_outstanding) }, + { label: '인정이자 4.6%', value: summary.recognized_interest, unit: '원' }, + { label: '미정정', value: `${summary.pending_count}건` }, + ], + table: { + title: '가지급금 관련 내역', + columns: [ + { key: 'no', label: 'No.', align: 'center' }, + { key: 'date', label: '발생일시', align: 'center' }, + { key: 'target', label: '대상', align: 'center' }, + { key: 'category', label: '구분', align: 'center' }, + { key: 'amount', label: '금액', align: 'right', format: 'currency' }, + { key: 'status', label: '상태', align: 'center', highlightValue: '미정정' }, + { key: 'content', label: '내용', align: 'left' }, + ], + data: tableData, + filters: [ + { + key: 'target', + options: targetFilterOptions, + defaultValue: 'all', + }, + { + key: 'category', + options: [ + { value: 'all', label: '전체' }, + { value: '카드명', label: '카드명' }, + { value: '계좌명', label: '계좌명' }, + ], + defaultValue: 'all', + }, + { + key: 'sortOrder', + options: [ + { value: 'latest', label: '최신순' }, + { value: 'oldest', label: '등록순' }, + { value: 'amountDesc', label: '금액 높은순' }, + { value: 'amountAsc', label: '금액 낮은순' }, + ], + defaultValue: 'latest', + }, + ], + showTotal: true, + totalLabel: '합계', + totalValue: summary.total_outstanding, + totalColumnKey: 'amount', + }, + }; +} + +// ============================================ +// cm3: 법인세 예상 가중 상세 모달 변환기 +// ============================================ + +/** + * 세금 시뮬레이션 API 응답을 cm3 (법인세) 모달 설정으로 변환 + */ +export function transformCm3ModalConfig( + data: TaxSimulationApiResponse +): DetailModalConfig { + const { loan_summary, corporate_tax } = data; + + return { + title: '법인세 예상 가중 상세', + summaryCards: [ + { label: '법인세 예상 증가', value: corporate_tax.difference, unit: '원' }, + { label: '인정 이자', value: loan_summary.recognized_interest, unit: '원' }, + { label: '가지급금', value: formatKoreanCurrency(loan_summary.total_outstanding) }, + { + label: `인정이자 ${(loan_summary.interest_rate * 100).toFixed(1)}%`, + value: loan_summary.recognized_interest, + unit: '원', + }, + ], + comparisonSection: { + leftBox: { + title: '없을때 법인세', + items: [ + { + label: '과세표준', + value: formatKoreanCurrency(corporate_tax.without_loan.taxable_income), + }, + { label: '법인세', value: corporate_tax.without_loan.tax_amount, unit: '원' }, + ], + borderColor: 'orange', + }, + rightBox: { + title: '있을때 법인세', + items: [ + { + label: '과세표준', + value: formatKoreanCurrency(corporate_tax.with_loan.taxable_income), + }, + { label: '법인세', value: corporate_tax.with_loan.tax_amount, unit: '원' }, + ], + borderColor: 'blue', + }, + vsLabel: '법인세 예상 증가', + vsValue: corporate_tax.difference, + vsSubLabel: corporate_tax.rate_info, + }, + referenceTable: { + title: '법인세 과세표준 (2024년 기준)', + columns: [ + { key: 'bracket', label: '과세표준', align: 'left' }, + { key: 'rate', label: '세율', align: 'center' }, + { key: 'formula', label: '계산식', align: 'left' }, + ], + data: [ + { bracket: '2억원 이하', rate: '9%', formula: '과세표준 × 9%' }, + { + bracket: '2억원 초과 ~ 200억원 이하', + rate: '19%', + formula: '1,800만원 + (2억원 초과분 × 19%)', + }, + { + bracket: '200억원 초과 ~ 3,000억원 이하', + rate: '21%', + formula: '37.62억원 + (200억원 초과분 × 21%)', + }, + { + bracket: '3,000억원 초과', + rate: '24%', + formula: '625.62억원 + (3,000억원 초과분 × 24%)', + }, + ], + }, + }; +} + +// ============================================ +// cm4: 대표자 종합소득세 예상 가중 상세 모달 변환기 +// ============================================ + +/** + * 세금 시뮬레이션 API 응답을 cm4 (종합소득세) 모달 설정으로 변환 + */ +export function transformCm4ModalConfig( + data: TaxSimulationApiResponse +): DetailModalConfig { + const { loan_summary, income_tax } = data; + + // 세금 증가율 계산 + const taxIncreaseRate = + income_tax.without_loan.tax_amount > 0 + ? ((income_tax.difference / income_tax.without_loan.tax_amount) * 100).toFixed(1) + : '0'; + + return { + title: '대표자 종합소득세 예상 가중 상세', + summaryCards: [ + { label: '대표자 종합세 예상 가중', value: income_tax.difference, unit: '원' }, + { + label: '추가 세금', + value: `+${taxIncreaseRate}%`, + isComparison: true, + isPositive: false, + }, + { label: '가지급금', value: formatKoreanCurrency(loan_summary.total_outstanding) }, + { + label: `인정이자 ${(loan_summary.interest_rate * 100).toFixed(1)}%`, + value: loan_summary.recognized_interest, + unit: '원', + }, + ], + comparisonSection: { + leftBox: { + title: '가지급금 인정이자가 반영된 종합소득세', + items: [ + { + label: '현재 예상 과세표준 (근로소득+상여)', + value: income_tax.with_loan.taxable_income, + unit: '원', + }, + { label: '현재 적용 세율', value: income_tax.with_loan.tax_rate }, + { label: '현재 예상 세액', value: income_tax.with_loan.tax_amount, unit: '원' }, + ], + borderColor: 'orange', + }, + rightBox: { + title: '가지급금 인정이자가 정리된 종합소득세', + items: [ + { + label: '가지급금 정리 시 예상 과세표준 (근로소득+상여)', + value: income_tax.without_loan.taxable_income, + unit: '원', + }, + { label: '가지급금 정리 시 적용 세율', value: income_tax.without_loan.tax_rate }, + { + label: '가지급금 정리 시 예상 세액', + value: income_tax.without_loan.tax_amount, + unit: '원', + }, + ], + borderColor: 'blue', + }, + vsLabel: '종합소득세 예상 절감', + vsValue: income_tax.difference, + vsSubLabel: `감소 세금 -${taxIncreaseRate}%`, + vsBreakdown: [ + { label: '종합소득세', value: -income_tax.breakdown.income_tax, unit: '원' }, + { label: '지방소득세', value: -income_tax.breakdown.local_tax, unit: '원' }, + { label: '4대 보험', value: -income_tax.breakdown.insurance, unit: '원' }, + ], + }, + referenceTable: { + title: '종합소득세 과세표준 (2024년 기준)', + columns: [ + { key: 'bracket', label: '과세표준', align: 'left' }, + { key: 'rate', label: '세율', align: 'center' }, + { key: 'deduction', label: '누진공제', align: 'right' }, + { key: 'formula', label: '계산식', align: 'left' }, + ], + data: [ + { bracket: '1,400만원 이하', rate: '6%', deduction: '-', formula: '과세표준 × 6%' }, + { + bracket: '1,400만원 초과 ~ 5,000만원 이하', + rate: '15%', + deduction: '126만원', + formula: '과세표준 × 15% - 126만원', + }, + { + bracket: '5,000만원 초과 ~ 8,800만원 이하', + rate: '24%', + deduction: '576만원', + formula: '과세표준 × 24% - 576만원', + }, + { + bracket: '8,800만원 초과 ~ 1.5억원 이하', + rate: '35%', + deduction: '1,544만원', + formula: '과세표준 × 35% - 1,544만원', + }, + { + bracket: '1.5억원 초과 ~ 3억원 이하', + rate: '38%', + deduction: '1,994만원', + formula: '과세표준 × 38% - 1,994만원', + }, + { + bracket: '3억원 초과 ~ 5억원 이하', + rate: '40%', + deduction: '2,594만원', + formula: '과세표준 × 40% - 2,594만원', + }, + { + bracket: '5억원 초과 ~ 10억원 이하', + rate: '42%', + deduction: '3,594만원', + formula: '과세표준 × 42% - 3,594만원', + }, + { + bracket: '10억원 초과', + rate: '45%', + deduction: '6,594만원', + formula: '과세표준 × 45% - 6,594만원', + }, + ], + }, + }; +} diff --git a/src/components/business/CEODashboard/modalConfigs/cardManagementConfigs.ts b/src/components/business/CEODashboard/modalConfigs/cardManagementConfigs.ts index 27707e9c..c0704a78 100644 --- a/src/components/business/CEODashboard/modalConfigs/cardManagementConfigs.ts +++ b/src/components/business/CEODashboard/modalConfigs/cardManagementConfigs.ts @@ -1,4 +1,15 @@ import type { DetailModalConfig } from '../types'; +import type { + CardDashboardDetailApiResponse, + LoanDashboardApiResponse, + TaxSimulationApiResponse, +} from '@/lib/api/dashboard/types'; +import { + transformCm1ModalConfig, + transformCm2ModalConfig, + transformCm3ModalConfig, + transformCm4ModalConfig, +} from './cardManagementConfigTransformers'; /** * 카드/가지급금 관리 모달 설정 @@ -7,6 +18,64 @@ import type { DetailModalConfig } from '../types'; * cm3: 법인세 예상 가중 상세 * cm4: 대표자 종합소득세 예상 가중 상세 */ + +// ============================================ +// API 데이터 기반 모달 설정 생성 +// ============================================ + +export interface CardManagementModalData { + cm1Data?: CardDashboardDetailApiResponse | null; + cm2Data?: LoanDashboardApiResponse | null; + cm3Data?: TaxSimulationApiResponse | null; + cm4Data?: TaxSimulationApiResponse | null; +} + +/** + * API 데이터를 사용하여 모달 설정을 동적으로 생성 + * 데이터가 없는 경우 fallback 설정 사용 + */ +export function getCardManagementModalConfigWithData( + cardId: string, + data: CardManagementModalData +): DetailModalConfig | null { + switch (cardId) { + case 'cm1': + if (data.cm1Data) { + return transformCm1ModalConfig(data.cm1Data); + } + return getCardManagementModalConfig(cardId); + + case 'cm2': + if (data.cm2Data) { + return transformCm2ModalConfig(data.cm2Data); + } + return getCardManagementModalConfig(cardId); + + case 'cm3': + if (data.cm3Data) { + return transformCm3ModalConfig(data.cm3Data); + } + return getCardManagementModalConfig(cardId); + + case 'cm4': + if (data.cm4Data) { + return transformCm4ModalConfig(data.cm4Data); + } + return getCardManagementModalConfig(cardId); + + default: + return null; + } +} + +// ============================================ +// Fallback 모달 설정 (API 데이터 없을 때 사용) +// ============================================ + +/** + * Fallback: 정적 목업 데이터 기반 모달 설정 + * API 데이터가 없을 때 사용 + */ export function getCardManagementModalConfig(cardId: string): DetailModalConfig | null { const configs: Record = { cm1: { @@ -266,4 +335,4 @@ export function getCardManagementModalConfig(cardId: string): DetailModalConfig }; return configs[cardId] || null; -} \ No newline at end of file +} diff --git a/src/components/business/CEODashboard/modalConfigs/index.ts b/src/components/business/CEODashboard/modalConfigs/index.ts index 7dd79754..5021a2fb 100644 --- a/src/components/business/CEODashboard/modalConfigs/index.ts +++ b/src/components/business/CEODashboard/modalConfigs/index.ts @@ -1,5 +1,5 @@ export { getMonthlyExpenseModalConfig } from './monthlyExpenseConfigs'; -export { getCardManagementModalConfig } from './cardManagementConfigs'; +export { getCardManagementModalConfig, getCardManagementModalConfigWithData } from './cardManagementConfigs'; export { getEntertainmentModalConfig } from './entertainmentConfigs'; export { getWelfareModalConfig } from './welfareConfigs'; export { getVatModalConfig } from './vatConfigs'; \ No newline at end of file diff --git a/src/hooks/useCardManagementModals.ts b/src/hooks/useCardManagementModals.ts index d2384e28..16c1b7d9 100644 --- a/src/hooks/useCardManagementModals.ts +++ b/src/hooks/useCardManagementModals.ts @@ -30,6 +30,14 @@ import type { /** 카드 ID 타입 */ export type CardManagementCardId = 'cm1' | 'cm2' | 'cm3' | 'cm4'; +/** 모달 데이터 반환 타입 */ +export interface CardManagementModalData { + cm1Data?: CardDashboardDetailApiResponse | null; + cm2Data?: LoanDashboardApiResponse | null; + cm3Data?: TaxSimulationApiResponse | null; + cm4Data?: TaxSimulationApiResponse | null; +} + /** Hook 반환 타입 */ export interface UseCardManagementModalsReturn { /** cm1: 카드 사용액 상세 데이터 */ @@ -44,8 +52,8 @@ export interface UseCardManagementModalsReturn { loading: boolean; /** 에러 메시지 */ error: string | null; - /** 특정 카드의 모달 데이터 조회 */ - fetchModalData: (cardId: CardManagementCardId) => Promise; + /** 특정 카드의 모달 데이터 조회 - 데이터 직접 반환 */ + fetchModalData: (cardId: CardManagementCardId) => Promise; /** 모든 카드 데이터 조회 */ fetchAllData: () => Promise; /** 데이터 초기화 */ @@ -78,12 +86,14 @@ export function useCardManagementModals(): UseCardManagementModalsReturn { /** * cm1: 카드 사용액 상세 데이터 조회 + * @returns 조회된 데이터 (실패 시 null) */ - const fetchCm1Data = useCallback(async () => { + const fetchCm1Data = useCallback(async (): Promise => { try { const response = await fetchCardTransactionDashboard(); - if (response.success) { + if (response.success && response.data) { setCm1Data(response.data); + return response.data; } else { throw new Error(response.message || '카드 거래 데이터 조회 실패'); } @@ -96,12 +106,14 @@ export function useCardManagementModals(): UseCardManagementModalsReturn { /** * cm2: 가지급금 상세 데이터 조회 + * @returns 조회된 데이터 (실패 시 null) */ - const fetchCm2Data = useCallback(async () => { + const fetchCm2Data = useCallback(async (): Promise => { try { const response = await fetchLoanDashboard(); - if (response.success) { + if (response.success && response.data) { setCm2Data(response.data); + return response.data; } else { throw new Error(response.message || '가지급금 데이터 조회 실패'); } @@ -115,14 +127,16 @@ export function useCardManagementModals(): UseCardManagementModalsReturn { /** * cm3 & cm4: 세금 시뮬레이션 데이터 조회 * cm3은 법인세 (corporate_tax), cm4는 소득세 (income_tax) 사용 + * @returns 조회된 데이터 (실패 시 null) */ - const fetchTaxData = useCallback(async () => { + const fetchTaxData = useCallback(async (): Promise => { try { const response = await fetchTaxSimulation(); - if (response.success) { + if (response.success && response.data) { // cm3, cm4 모두 같은 데이터 소스 사용 (표시만 다름) setCm3Data(response.data); setCm4Data(response.data); + return response.data; } else { throw new Error(response.message || '세금 시뮬레이션 데이터 조회 실패'); } @@ -135,25 +149,31 @@ export function useCardManagementModals(): UseCardManagementModalsReturn { /** * 특정 카드의 모달 데이터 조회 + * @returns 조회된 모달 데이터 객체 (카드 ID에 해당하는 데이터만 포함) */ const fetchModalData = useCallback( - async (cardId: CardManagementCardId) => { + async (cardId: CardManagementCardId): Promise => { setLoading(true); setError(null); + const result: CardManagementModalData = {}; + try { switch (cardId) { case 'cm1': - await fetchCm1Data(); + result.cm1Data = await fetchCm1Data(); break; case 'cm2': - await fetchCm2Data(); + result.cm2Data = await fetchCm2Data(); break; case 'cm3': - case 'cm4': + case 'cm4': { // cm3, cm4는 같은 API 사용 - await fetchTaxData(); + const taxData = await fetchTaxData(); + result.cm3Data = taxData; + result.cm4Data = taxData; break; + } default: throw new Error(`알 수 없는 카드 ID: ${cardId}`); } @@ -163,6 +183,8 @@ export function useCardManagementModals(): UseCardManagementModalsReturn { } finally { setLoading(false); } + + return result; }, [fetchCm1Data, fetchCm2Data, fetchTaxData] );