fix: 당월 예상 지출 모달 API 통합
- 모든 카드(me1~me4)가 expected-expenses/dashboard-detail API 사용 - transaction_type 파라미터로 필터링 (me1=purchase, me2=card, me3=bill) - cardId별 모달 제목 동적 설정 추가 - 불필요한 import 정리
This commit is contained in:
@@ -22,6 +22,7 @@ import type {
|
||||
EntertainmentApiResponse,
|
||||
WelfareApiResponse,
|
||||
WelfareDetailApiResponse,
|
||||
ExpectedExpenseDashboardDetailApiResponse,
|
||||
} from '@/lib/api/dashboard/types';
|
||||
|
||||
import {
|
||||
@@ -37,6 +38,7 @@ import {
|
||||
transformEntertainmentResponse,
|
||||
transformWelfareResponse,
|
||||
transformWelfareDetailResponse,
|
||||
transformExpectedExpenseDetailResponse,
|
||||
} from '@/lib/api/dashboard/transformers';
|
||||
|
||||
import type {
|
||||
@@ -621,6 +623,254 @@ export function useWelfareDetail(options: UseWelfareDetailOptions = {}) {
|
||||
return { modalConfig, loading, error, refetch: fetchData };
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 13. PurchaseDetail Hook (매입 상세 - me1 모달용)
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 매입 상세 데이터 Hook (me1 모달용)
|
||||
* API에서 상세 데이터를 가져와 DetailModalConfig로 변환
|
||||
*/
|
||||
export function usePurchaseDetail() {
|
||||
const [modalConfig, setModalConfig] = useState<DetailModalConfig | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await fetch('/api/v1/purchases/dashboard-detail');
|
||||
if (!response.ok) {
|
||||
throw new Error(`API 오류: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (!result.success) {
|
||||
throw new Error(result.message || '데이터 조회 실패');
|
||||
}
|
||||
|
||||
const transformed = transformPurchaseDetailResponse(result.data);
|
||||
setModalConfig(transformed);
|
||||
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : '데이터 로딩 실패';
|
||||
setError(errorMessage);
|
||||
console.error('PurchaseDetail API Error:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { modalConfig, loading, error, refetch: fetchData };
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 14. CardDetail Hook (카드 상세 - me2 모달용)
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 카드 상세 데이터 Hook (me2 모달용)
|
||||
* API에서 상세 데이터를 가져와 DetailModalConfig로 변환
|
||||
*/
|
||||
export function useCardDetail() {
|
||||
const [modalConfig, setModalConfig] = useState<DetailModalConfig | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await fetch('/api/v1/card-transactions/dashboard');
|
||||
if (!response.ok) {
|
||||
throw new Error(`API 오류: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (!result.success) {
|
||||
throw new Error(result.message || '데이터 조회 실패');
|
||||
}
|
||||
|
||||
const transformed = transformCardDetailResponse(result.data);
|
||||
setModalConfig(transformed);
|
||||
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : '데이터 로딩 실패';
|
||||
setError(errorMessage);
|
||||
console.error('CardDetail API Error:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { modalConfig, loading, error, refetch: fetchData };
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 15. BillDetail Hook (발행어음 상세 - me3 모달용)
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 발행어음 상세 데이터 Hook (me3 모달용)
|
||||
* API에서 상세 데이터를 가져와 DetailModalConfig로 변환
|
||||
*/
|
||||
export function useBillDetail() {
|
||||
const [modalConfig, setModalConfig] = useState<DetailModalConfig | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await fetch('/api/v1/bills/dashboard-detail');
|
||||
if (!response.ok) {
|
||||
throw new Error(`API 오류: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (!result.success) {
|
||||
throw new Error(result.message || '데이터 조회 실패');
|
||||
}
|
||||
|
||||
const transformed = transformBillDetailResponse(result.data);
|
||||
setModalConfig(transformed);
|
||||
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : '데이터 로딩 실패';
|
||||
setError(errorMessage);
|
||||
console.error('BillDetail API Error:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { modalConfig, loading, error, refetch: fetchData };
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 16. ExpectedExpenseDetail Hook (지출예상 상세 - me4 모달용)
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 지출예상 상세 데이터 Hook (me4 모달용)
|
||||
* API에서 상세 데이터를 가져와 DetailModalConfig로 변환
|
||||
*/
|
||||
export function useExpectedExpenseDetail() {
|
||||
const [modalConfig, setModalConfig] = useState<DetailModalConfig | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await fetch('/api/v1/expected-expenses/dashboard-detail');
|
||||
if (!response.ok) {
|
||||
throw new Error(`API 오류: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (!result.success) {
|
||||
throw new Error(result.message || '데이터 조회 실패');
|
||||
}
|
||||
|
||||
const transformed = transformExpectedExpenseDetailResponse(result.data);
|
||||
setModalConfig(transformed);
|
||||
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : '데이터 로딩 실패';
|
||||
setError(errorMessage);
|
||||
console.error('ExpectedExpenseDetail API Error:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { modalConfig, loading, error, refetch: fetchData };
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 17. MonthlyExpenseDetail Hook (당월 예상 지출 상세 - 통합 모달용)
|
||||
// ============================================
|
||||
|
||||
export type MonthlyExpenseCardId = 'me1' | 'me2' | 'me3' | 'me4';
|
||||
|
||||
/**
|
||||
* 당월 예상 지출 상세 데이터 Hook (통합 모달용)
|
||||
* cardId에 따라 다른 API를 호출하고 DetailModalConfig로 변환
|
||||
*
|
||||
* @example
|
||||
* const { modalConfig, loading, error, fetchData } = useMonthlyExpenseDetail();
|
||||
* await fetchData('me1'); // 매입 상세 API 호출
|
||||
*/
|
||||
export function useMonthlyExpenseDetail() {
|
||||
const [modalConfig, setModalConfig] = useState<DetailModalConfig | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchData = useCallback(async (cardId: MonthlyExpenseCardId) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
// 모든 카드가 expected-expenses API를 사용하여 데이터 일관성 보장
|
||||
// transaction_type: me1=purchase, me2=card, me3=bill, me4=전체
|
||||
let endpoint: string;
|
||||
let transactionType: string | null = null;
|
||||
|
||||
switch (cardId) {
|
||||
case 'me1':
|
||||
transactionType = 'purchase';
|
||||
break;
|
||||
case 'me2':
|
||||
transactionType = 'card';
|
||||
break;
|
||||
case 'me3':
|
||||
transactionType = 'bill';
|
||||
break;
|
||||
case 'me4':
|
||||
transactionType = null; // 전체 조회
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown cardId: ${cardId}`);
|
||||
}
|
||||
|
||||
// 단일 API 엔드포인트 사용 (transaction_type으로 필터링)
|
||||
endpoint = transactionType
|
||||
? `/api/v1/expected-expenses/dashboard-detail?transaction_type=${transactionType}`
|
||||
: '/api/v1/expected-expenses/dashboard-detail';
|
||||
|
||||
const transformer = (data: unknown) =>
|
||||
transformExpectedExpenseDetailResponse(data as ExpectedExpenseDashboardDetailApiResponse, cardId);
|
||||
|
||||
const response = await fetch(endpoint);
|
||||
if (!response.ok) {
|
||||
throw new Error(`API 오류: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (!result.success) {
|
||||
throw new Error(result.message || '데이터 조회 실패');
|
||||
}
|
||||
|
||||
const transformed = transformer(result.data);
|
||||
setModalConfig(transformed);
|
||||
|
||||
return transformed;
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : '데이터 로딩 실패';
|
||||
setError(errorMessage);
|
||||
console.error('MonthlyExpenseDetail API Error:', err);
|
||||
return null;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { modalConfig, loading, error, fetchData };
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 통합 Dashboard Hook (선택적 사용)
|
||||
// ============================================
|
||||
|
||||
@@ -891,4 +891,365 @@ export function transformWelfareDetailResponse(api: WelfareDetailApiResponse): D
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 13. Purchase Dashboard Detail 변환 (me1)
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Purchase Dashboard Detail API 응답 → DetailModalConfig 변환
|
||||
* 매입 상세 모달 설정 생성 (me1)
|
||||
*/
|
||||
export function transformPurchaseDetailResponse(api: PurchaseDashboardDetailApiResponse): DetailModalConfig {
|
||||
const { summary, monthly_trend, by_type, items } = api;
|
||||
const changeRateText = summary.change_rate >= 0
|
||||
? `+${summary.change_rate.toFixed(1)}%`
|
||||
: `${summary.change_rate.toFixed(1)}%`;
|
||||
|
||||
return {
|
||||
title: '매입 상세',
|
||||
summaryCards: [
|
||||
{ label: '당월 매입액', value: summary.current_month_amount, unit: '원' },
|
||||
{ label: '전월 대비', value: changeRateText },
|
||||
],
|
||||
barChart: {
|
||||
title: '월별 매입 추이',
|
||||
data: monthly_trend.map(item => ({
|
||||
name: item.label,
|
||||
value: item.amount,
|
||||
})),
|
||||
dataKey: 'value',
|
||||
xAxisKey: 'name',
|
||||
color: '#60A5FA',
|
||||
},
|
||||
pieChart: {
|
||||
title: '유형별 매입 비율',
|
||||
data: by_type.map(item => ({
|
||||
name: item.type_label,
|
||||
value: item.amount,
|
||||
percentage: 0, // API에서 계산하거나 프론트에서 계산
|
||||
color: item.color,
|
||||
})),
|
||||
},
|
||||
table: {
|
||||
title: '매입 내역',
|
||||
columns: [
|
||||
{ key: 'no', label: 'No.', align: 'center' },
|
||||
{ key: 'date', label: '매입일자', align: 'center', format: 'date' },
|
||||
{ key: 'vendor', label: '거래처명', align: 'left' },
|
||||
{ key: 'amount', label: '금액', align: 'right', format: 'currency' },
|
||||
{ key: 'type', label: '유형', align: 'center' },
|
||||
],
|
||||
data: items.map((item, idx) => ({
|
||||
no: idx + 1,
|
||||
date: item.purchase_date,
|
||||
vendor: item.vendor_name,
|
||||
amount: item.amount,
|
||||
type: item.type_label,
|
||||
})),
|
||||
filters: [
|
||||
{
|
||||
key: 'type',
|
||||
options: [
|
||||
{ value: 'all', label: '전체' },
|
||||
...by_type.map(t => ({ value: t.type, label: t.type_label })),
|
||||
],
|
||||
defaultValue: 'all',
|
||||
},
|
||||
{
|
||||
key: 'sortOrder',
|
||||
options: [
|
||||
{ value: 'latest', label: '최신순' },
|
||||
{ value: 'oldest', label: '등록순' },
|
||||
{ value: 'amountDesc', label: '금액 높은순' },
|
||||
{ value: 'amountAsc', label: '금액 낮은순' },
|
||||
],
|
||||
defaultValue: 'latest',
|
||||
},
|
||||
],
|
||||
showTotal: true,
|
||||
totalLabel: '합계',
|
||||
totalValue: items.reduce((sum, item) => sum + item.amount, 0),
|
||||
totalColumnKey: 'amount',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 14. Card Dashboard Detail 변환 (me2)
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Card Dashboard Detail API 응답 → DetailModalConfig 변환
|
||||
* 카드 상세 모달 설정 생성 (me2)
|
||||
*/
|
||||
export function transformCardDetailResponse(api: CardDashboardDetailApiResponse): DetailModalConfig {
|
||||
const { summary, monthly_trend, by_user, items } = api;
|
||||
const changeRate = summary.previous_month_total > 0
|
||||
? ((summary.current_month_total - summary.previous_month_total) / summary.previous_month_total * 100)
|
||||
: 0;
|
||||
const changeRateText = changeRate >= 0
|
||||
? `+${changeRate.toFixed(1)}%`
|
||||
: `${changeRate.toFixed(1)}%`;
|
||||
|
||||
return {
|
||||
title: '카드 사용 상세',
|
||||
summaryCards: [
|
||||
{ label: '당월 사용액', value: summary.current_month_total, unit: '원' },
|
||||
{ label: '전월 대비', value: changeRateText },
|
||||
{ label: '당월 건수', value: summary.current_month_count, unit: '건' },
|
||||
],
|
||||
barChart: {
|
||||
title: '월별 카드 사용 추이',
|
||||
data: monthly_trend.map(item => ({
|
||||
name: item.label,
|
||||
value: item.amount,
|
||||
})),
|
||||
dataKey: 'value',
|
||||
xAxisKey: 'name',
|
||||
color: '#34D399',
|
||||
},
|
||||
pieChart: {
|
||||
title: '사용자별 사용 비율',
|
||||
data: by_user.map(item => ({
|
||||
name: item.user_name,
|
||||
value: item.amount,
|
||||
percentage: 0,
|
||||
color: item.color,
|
||||
})),
|
||||
},
|
||||
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' },
|
||||
],
|
||||
data: items.map((item, idx) => ({
|
||||
no: idx + 1,
|
||||
cardName: item.card_name,
|
||||
user: item.user_name,
|
||||
date: item.transaction_date,
|
||||
store: item.merchant_name,
|
||||
amount: item.amount,
|
||||
usageType: item.usage_type,
|
||||
})),
|
||||
filters: [
|
||||
{
|
||||
key: 'user',
|
||||
options: [
|
||||
{ value: 'all', label: '전체' },
|
||||
...by_user.map(u => ({ value: u.user_name, label: u.user_name })),
|
||||
],
|
||||
defaultValue: 'all',
|
||||
},
|
||||
{
|
||||
key: 'sortOrder',
|
||||
options: [
|
||||
{ value: 'latest', label: '최신순' },
|
||||
{ value: 'oldest', label: '등록순' },
|
||||
{ value: 'amountDesc', label: '금액 높은순' },
|
||||
{ value: 'amountAsc', label: '금액 낮은순' },
|
||||
],
|
||||
defaultValue: 'latest',
|
||||
},
|
||||
],
|
||||
showTotal: true,
|
||||
totalLabel: '합계',
|
||||
totalValue: items.reduce((sum, item) => sum + item.amount, 0),
|
||||
totalColumnKey: 'amount',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 15. Bill Dashboard Detail 변환 (me3)
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Bill Dashboard Detail API 응답 → DetailModalConfig 변환
|
||||
* 발행어음 상세 모달 설정 생성 (me3)
|
||||
*/
|
||||
export function transformBillDetailResponse(api: BillDashboardDetailApiResponse): DetailModalConfig {
|
||||
const { summary, monthly_trend, by_vendor, items } = api;
|
||||
const changeRateText = summary.change_rate >= 0
|
||||
? `+${summary.change_rate.toFixed(1)}%`
|
||||
: `${summary.change_rate.toFixed(1)}%`;
|
||||
|
||||
// 거래처별 가로 막대 차트 데이터
|
||||
const horizontalBarData = by_vendor.map(item => ({
|
||||
name: item.vendor_name,
|
||||
value: item.amount,
|
||||
}));
|
||||
|
||||
return {
|
||||
title: '발행어음 상세',
|
||||
summaryCards: [
|
||||
{ label: '당월 발행어음', value: summary.current_month_amount, unit: '원' },
|
||||
{ label: '전월 대비', value: changeRateText },
|
||||
],
|
||||
barChart: {
|
||||
title: '월별 발행어음 추이',
|
||||
data: monthly_trend.map(item => ({
|
||||
name: item.label,
|
||||
value: item.amount,
|
||||
})),
|
||||
dataKey: 'value',
|
||||
xAxisKey: 'name',
|
||||
color: '#F59E0B',
|
||||
},
|
||||
horizontalBarChart: {
|
||||
title: '거래처별 발행어음',
|
||||
data: horizontalBarData,
|
||||
dataKey: 'value',
|
||||
yAxisKey: 'name',
|
||||
color: '#8B5CF6',
|
||||
},
|
||||
table: {
|
||||
title: '발행어음 내역',
|
||||
columns: [
|
||||
{ key: 'no', label: 'No.', align: 'center' },
|
||||
{ key: 'vendor', label: '거래처명', align: 'left' },
|
||||
{ key: 'issueDate', label: '발행일', align: 'center', format: 'date' },
|
||||
{ key: 'dueDate', label: '만기일', align: 'center', format: 'date' },
|
||||
{ key: 'amount', label: '금액', align: 'right', format: 'currency' },
|
||||
{ key: 'status', label: '상태', align: 'center' },
|
||||
],
|
||||
data: items.map((item, idx) => ({
|
||||
no: idx + 1,
|
||||
vendor: item.vendor_name,
|
||||
issueDate: item.issue_date,
|
||||
dueDate: item.due_date,
|
||||
amount: item.amount,
|
||||
status: item.status_label,
|
||||
})),
|
||||
filters: [
|
||||
{
|
||||
key: 'vendor',
|
||||
options: [
|
||||
{ value: 'all', label: '전체' },
|
||||
...by_vendor.map(v => ({ value: v.vendor_name, label: v.vendor_name })),
|
||||
],
|
||||
defaultValue: 'all',
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
options: [
|
||||
{ value: 'all', label: '전체' },
|
||||
{ value: 'pending', label: '대기중' },
|
||||
{ value: 'paid', label: '결제완료' },
|
||||
{ value: 'overdue', label: '연체' },
|
||||
],
|
||||
defaultValue: 'all',
|
||||
},
|
||||
{
|
||||
key: 'sortOrder',
|
||||
options: [
|
||||
{ value: 'latest', label: '최신순' },
|
||||
{ value: 'oldest', label: '등록순' },
|
||||
{ value: 'amountDesc', label: '금액 높은순' },
|
||||
{ value: 'amountAsc', label: '금액 낮은순' },
|
||||
],
|
||||
defaultValue: 'latest',
|
||||
},
|
||||
],
|
||||
showTotal: true,
|
||||
totalLabel: '합계',
|
||||
totalValue: items.reduce((sum, item) => sum + item.amount, 0),
|
||||
totalColumnKey: 'amount',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 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: '당월 총 지출예상' },
|
||||
};
|
||||
|
||||
/**
|
||||
* ExpectedExpense Dashboard Detail API 응답 → DetailModalConfig 변환
|
||||
* 카드별 지출 상세 모달 설정 생성 (me1: 매입, me2: 카드, me3: 발행어음, me4: 전체)
|
||||
*
|
||||
* @param api API 응답 데이터
|
||||
* @param cardId 카드 ID (me1~me4), 기본값 me4
|
||||
*/
|
||||
export function transformExpectedExpenseDetailResponse(
|
||||
api: ExpectedExpenseDashboardDetailApiResponse,
|
||||
cardId: string = 'me4'
|
||||
): DetailModalConfig {
|
||||
const { summary, items } = 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;
|
||||
|
||||
return {
|
||||
title: titles.title,
|
||||
summaryCards: [
|
||||
{ label: titles.summaryLabel, value: summary.total_amount, unit: '원' },
|
||||
{ label: '전월 대비', value: changeRateText },
|
||||
{ label: '지출 후 예상 잔액', value: summary.remaining_balance, unit: '원' },
|
||||
],
|
||||
// 차트 없음
|
||||
table: {
|
||||
title: titles.tableTitle,
|
||||
columns: [
|
||||
{ key: 'no', label: 'No.', align: 'center' },
|
||||
{ key: 'paymentDate', label: '결제예정일', align: 'center', format: 'date' },
|
||||
{ key: 'item', label: '항목', align: 'left' },
|
||||
{ key: 'amount', label: '금액', align: 'right', format: 'currency' },
|
||||
{ key: 'vendor', label: '거래처', align: 'left' },
|
||||
{ key: 'account', label: '계정과목', align: 'center' },
|
||||
],
|
||||
data: items.map((item, idx) => ({
|
||||
no: idx + 1,
|
||||
paymentDate: item.payment_date,
|
||||
item: item.item_name,
|
||||
amount: item.amount,
|
||||
vendor: item.vendor_name,
|
||||
account: item.account_title,
|
||||
})),
|
||||
filters: [
|
||||
{
|
||||
key: 'vendor',
|
||||
options: [
|
||||
{ value: 'all', label: '전체' },
|
||||
],
|
||||
defaultValue: 'all',
|
||||
},
|
||||
{
|
||||
key: 'sortOrder',
|
||||
options: [
|
||||
{ value: 'latest', label: '최신순' },
|
||||
{ value: 'oldest', label: '등록순' },
|
||||
{ value: 'amountDesc', label: '금액 높은순' },
|
||||
{ value: 'amountAsc', label: '금액 낮은순' },
|
||||
],
|
||||
defaultValue: 'latest',
|
||||
},
|
||||
],
|
||||
showTotal: true,
|
||||
totalLabel: '합계',
|
||||
totalValue: footer_summary.total_amount,
|
||||
totalColumnKey: 'amount',
|
||||
footerSummary: {
|
||||
label: `총 ${footer_summary.item_count}건`,
|
||||
value: footer_summary.total_amount,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user