fix(WEB): 대시보드 모달 데이터 연동 오류 수정
- transformers.ts: footer_summary destructuring 누락 수정 - cardManagementConfigTransformers.ts: items, by_user, monthly_trend 방어적 코드 추가 - undefined 배열 접근 시 빈 배열 기본값 적용 - 카드 사용 상세(cm1), 가지급금 상세(cm2) 모달 에러 해결
This commit is contained in:
@@ -63,7 +63,7 @@ function formatPercentage(value: number, showSign = true): string {
|
||||
export function transformCm1ModalConfig(
|
||||
data: CardDashboardDetailApiResponse
|
||||
): DetailModalConfig {
|
||||
const { summary, monthly_trend, by_user, items } = data;
|
||||
const { summary, monthly_trend = [], by_user = [], items = [] } = data;
|
||||
|
||||
// 전월 대비 증감률 계산
|
||||
const changeRate = calculateChangeRate(
|
||||
@@ -72,13 +72,14 @@ export function transformCm1ModalConfig(
|
||||
);
|
||||
|
||||
// 미정리 건수 계산 (usage_type이 '미설정'인 항목)
|
||||
const unprocessedCount = items.filter(
|
||||
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) => ({
|
||||
const safeByUser = by_user || [];
|
||||
const totalAmount = safeByUser.reduce((sum, user) => sum + user.amount, 0);
|
||||
const pieChartData = safeByUser.map((user) => ({
|
||||
name: user.user_name,
|
||||
value: user.amount,
|
||||
percentage: totalAmount > 0 ? Math.round((user.amount / totalAmount) * 100) : 0,
|
||||
@@ -88,14 +89,14 @@ export function transformCm1ModalConfig(
|
||||
// 사용자 필터 옵션 동적 생성
|
||||
const userFilterOptions = [
|
||||
{ value: 'all', label: '전체' },
|
||||
...by_user.map((user) => ({
|
||||
...safeByUser.map((user) => ({
|
||||
value: user.user_name,
|
||||
label: user.user_name,
|
||||
})),
|
||||
];
|
||||
|
||||
// 테이블 데이터 매핑
|
||||
const tableData = items.map((item) => ({
|
||||
const tableData = (items || []).map((item) => ({
|
||||
cardName: item.card_name,
|
||||
user: item.user_name,
|
||||
date: item.transaction_date,
|
||||
@@ -118,7 +119,7 @@ export function transformCm1ModalConfig(
|
||||
],
|
||||
barChart: {
|
||||
title: '월별 카드 사용 추이',
|
||||
data: monthly_trend.map((trend) => ({
|
||||
data: (monthly_trend || []).map((trend) => ({
|
||||
name: trend.label,
|
||||
value: trend.amount,
|
||||
})),
|
||||
@@ -189,10 +190,10 @@ export function transformCm1ModalConfig(
|
||||
export function transformCm2ModalConfig(
|
||||
data: LoanDashboardApiResponse
|
||||
): DetailModalConfig {
|
||||
const { summary, items } = data;
|
||||
const { summary, items = [] } = data;
|
||||
|
||||
// 테이블 데이터 매핑
|
||||
const tableData = items.map((item) => ({
|
||||
const tableData = (items || []).map((item) => ({
|
||||
date: item.loan_date,
|
||||
target: item.user_name,
|
||||
category: '-', // API에서 별도 필드 없음
|
||||
@@ -202,7 +203,7 @@ export function transformCm2ModalConfig(
|
||||
}));
|
||||
|
||||
// 대상 필터 옵션 동적 생성
|
||||
const uniqueTargets = [...new Set(items.map((item) => item.user_name))];
|
||||
const uniqueTargets = [...new Set((items || []).map((item) => item.user_name))];
|
||||
const targetFilterOptions = [
|
||||
{ value: 'all', label: '전체' },
|
||||
...uniqueTargets.map((target) => ({
|
||||
|
||||
@@ -1189,12 +1189,57 @@ export function transformBillDetailResponse(api: BillDashboardDetailApiResponse)
|
||||
// 16. Expected Expense Dashboard Detail 변환 (me1~me4 통합)
|
||||
// ============================================
|
||||
|
||||
// cardId별 제목 매핑
|
||||
const EXPENSE_CARD_TITLES: Record<string, { title: string; tableTitle: string; summaryLabel: string }> = {
|
||||
me1: { title: '당월 매입 상세', tableTitle: '매입 내역', summaryLabel: '당월 총 매입' },
|
||||
me2: { title: '당월 카드결제 상세', tableTitle: '카드결제 내역', summaryLabel: '당월 총 카드결제' },
|
||||
me3: { title: '당월 발행어음 상세', tableTitle: '발행어음 내역', summaryLabel: '당월 총 발행어음' },
|
||||
me4: { title: '지출예상 상세', tableTitle: '지출예상 내역', summaryLabel: '당월 총 지출예상' },
|
||||
// cardId별 제목 및 차트 설정 매핑
|
||||
const EXPENSE_CARD_CONFIG: Record<string, {
|
||||
title: string;
|
||||
tableTitle: string;
|
||||
summaryLabel: string;
|
||||
barChartTitle: string;
|
||||
pieChartTitle?: string;
|
||||
horizontalBarChartTitle?: string;
|
||||
hasBarChart: boolean;
|
||||
hasPieChart: boolean;
|
||||
hasHorizontalBarChart: boolean;
|
||||
}> = {
|
||||
me1: {
|
||||
title: '당월 매입 상세',
|
||||
tableTitle: '일별 매입 내역',
|
||||
summaryLabel: '당월 매입',
|
||||
barChartTitle: '월별 매입 추이',
|
||||
pieChartTitle: '거래처별 매입 비율',
|
||||
hasBarChart: true,
|
||||
hasPieChart: true,
|
||||
hasHorizontalBarChart: false,
|
||||
},
|
||||
me2: {
|
||||
title: '당월 카드 상세',
|
||||
tableTitle: '일별 카드 사용 내역',
|
||||
summaryLabel: '당월 카드 사용',
|
||||
barChartTitle: '월별 카드 사용 추이',
|
||||
pieChartTitle: '거래처별 카드 사용 비율',
|
||||
hasBarChart: true,
|
||||
hasPieChart: true,
|
||||
hasHorizontalBarChart: false,
|
||||
},
|
||||
me3: {
|
||||
title: '당월 발행어음 상세',
|
||||
tableTitle: '일별 발행어음 내역',
|
||||
summaryLabel: '당월 발행어음 사용',
|
||||
barChartTitle: '월별 발행어음 추이',
|
||||
horizontalBarChartTitle: '당월 거래처별 발행어음',
|
||||
hasBarChart: true,
|
||||
hasPieChart: false,
|
||||
hasHorizontalBarChart: true,
|
||||
},
|
||||
me4: {
|
||||
title: '당월 지출 예상 상세',
|
||||
tableTitle: '당월 지출 승인 내역서',
|
||||
summaryLabel: '당월 지출 예상',
|
||||
barChartTitle: '',
|
||||
hasBarChart: false,
|
||||
hasPieChart: false,
|
||||
hasHorizontalBarChart: false,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1208,24 +1253,31 @@ export function transformExpectedExpenseDetailResponse(
|
||||
api: ExpectedExpenseDashboardDetailApiResponse,
|
||||
cardId: string = 'me4'
|
||||
): DetailModalConfig {
|
||||
const { summary, items } = api;
|
||||
const { summary, monthly_trend, vendor_distribution, items, footer_summary } = api;
|
||||
const changeRateText = summary.change_rate >= 0
|
||||
? `+${summary.change_rate.toFixed(1)}%`
|
||||
: `${summary.change_rate.toFixed(1)}%`;
|
||||
|
||||
// cardId별 제목 가져오기 (기본값: me4)
|
||||
const titles = EXPENSE_CARD_TITLES[cardId] || EXPENSE_CARD_TITLES.me4;
|
||||
// cardId별 설정 가져오기 (기본값: me4)
|
||||
const config = EXPENSE_CARD_CONFIG[cardId] || EXPENSE_CARD_CONFIG.me4;
|
||||
|
||||
return {
|
||||
title: titles.title,
|
||||
// 거래처 필터 옵션 생성
|
||||
const vendorOptions = [{ value: 'all', label: '전체' }];
|
||||
const uniqueVendors = [...new Set(items.map(item => item.vendor_name).filter(Boolean))];
|
||||
uniqueVendors.forEach(vendor => {
|
||||
vendorOptions.push({ value: vendor, label: vendor });
|
||||
});
|
||||
|
||||
// 결과 객체 생성
|
||||
const result: DetailModalConfig = {
|
||||
title: config.title,
|
||||
summaryCards: [
|
||||
{ label: titles.summaryLabel, value: summary.total_amount, unit: '원' },
|
||||
{ label: config.summaryLabel, value: summary.total_amount, unit: '원' },
|
||||
{ label: '전월 대비', value: changeRateText },
|
||||
{ label: '지출 후 예상 잔액', value: summary.remaining_balance, unit: '원' },
|
||||
],
|
||||
// 차트 없음
|
||||
table: {
|
||||
title: titles.tableTitle,
|
||||
title: config.tableTitle,
|
||||
columns: [
|
||||
{ key: 'no', label: 'No.', align: 'center' },
|
||||
{ key: 'paymentDate', label: '결제예정일', align: 'center', format: 'date' },
|
||||
@@ -1245,9 +1297,7 @@ export function transformExpectedExpenseDetailResponse(
|
||||
filters: [
|
||||
{
|
||||
key: 'vendor',
|
||||
options: [
|
||||
{ value: 'all', label: '전체' },
|
||||
],
|
||||
options: vendorOptions,
|
||||
defaultValue: 'all',
|
||||
},
|
||||
{
|
||||
@@ -1265,10 +1315,50 @@ export function transformExpectedExpenseDetailResponse(
|
||||
totalLabel: '합계',
|
||||
totalValue: footer_summary.total_amount,
|
||||
totalColumnKey: 'amount',
|
||||
footerSummary: {
|
||||
label: `총 ${footer_summary.item_count}건`,
|
||||
value: footer_summary.total_amount,
|
||||
},
|
||||
footerSummary: [
|
||||
{ label: `총 ${footer_summary.item_count}건`, value: footer_summary.total_amount },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
// barChart 추가 (me1, me2, me3)
|
||||
if (config.hasBarChart && monthly_trend && monthly_trend.length > 0) {
|
||||
result.barChart = {
|
||||
title: config.barChartTitle,
|
||||
data: monthly_trend.map(item => ({
|
||||
name: item.label,
|
||||
value: item.amount,
|
||||
})),
|
||||
dataKey: 'value',
|
||||
xAxisKey: 'name',
|
||||
color: '#60A5FA',
|
||||
};
|
||||
}
|
||||
|
||||
// pieChart 추가 (me1, me2)
|
||||
if (config.hasPieChart && vendor_distribution && vendor_distribution.length > 0) {
|
||||
result.pieChart = {
|
||||
title: config.pieChartTitle || '분포',
|
||||
data: vendor_distribution.map(item => ({
|
||||
name: item.name,
|
||||
value: item.value,
|
||||
percentage: item.percentage,
|
||||
color: item.color,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
// horizontalBarChart 추가 (me3)
|
||||
if (config.hasHorizontalBarChart && vendor_distribution && vendor_distribution.length > 0) {
|
||||
result.horizontalBarChart = {
|
||||
title: config.horizontalBarChartTitle || '거래처별 분포',
|
||||
data: vendor_distribution.map(item => ({
|
||||
name: item.name,
|
||||
value: item.value,
|
||||
})),
|
||||
color: '#60A5FA',
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user