fix: 당월 예상 지출 모달 API 통합

- 모든 카드(me1~me4)가 expected-expenses/dashboard-detail API 사용
- transaction_type 파라미터로 필터링 (me1=purchase, me2=card, me3=bill)
- cardId별 모달 제목 동적 설정 추가
- 불필요한 import 정리
This commit is contained in:
2026-01-22 23:16:56 +09:00
parent bf11b13aee
commit d824c913e8
2 changed files with 611 additions and 0 deletions

View File

@@ -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 (선택적 사용)
// ============================================