Refactor useTransactions hook
Refactor the useTransactions hook into smaller, more manageable files to improve code organization and maintainability. All existing functionality is preserved.
This commit is contained in:
@@ -1,218 +1,10 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
|
||||||
import { Transaction } from '@/components/TransactionCard';
|
|
||||||
import { useAuth } from '@/contexts/auth/AuthProvider';
|
|
||||||
import { toast } from '@/hooks/useToast.wrapper';
|
|
||||||
import {
|
|
||||||
getCurrentMonth,
|
|
||||||
getPrevMonth,
|
|
||||||
getNextMonth
|
|
||||||
} from './dateUtils';
|
|
||||||
import {
|
|
||||||
filterTransactionsByMonth,
|
|
||||||
filterTransactionsByQuery,
|
|
||||||
calculateTotalExpenses
|
|
||||||
} from './filterUtils';
|
|
||||||
import {
|
|
||||||
loadTransactionsFromStorage,
|
|
||||||
saveTransactionsToStorage,
|
|
||||||
loadBudgetFromStorage
|
|
||||||
} from './storageUtils';
|
|
||||||
import {
|
|
||||||
syncTransactionsWithSupabase,
|
|
||||||
updateTransactionInSupabase,
|
|
||||||
deleteTransactionFromSupabase
|
|
||||||
} from './supabaseUtils';
|
|
||||||
import { Loader2 } from 'lucide-react';
|
|
||||||
|
|
||||||
// useTransactions 훅
|
import { useTransactionsCore } from './useTransactionsCore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 메인 트랜잭션 훅
|
||||||
|
* useTransactionsCore를 통해 모든 트랜잭션 관련 기능에 접근합니다.
|
||||||
|
*/
|
||||||
export const useTransactions = () => {
|
export const useTransactions = () => {
|
||||||
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
return useTransactionsCore();
|
||||||
const [filteredTransactions, setFilteredTransactions] = useState<Transaction[]>([]);
|
|
||||||
const [selectedMonth, setSelectedMonth] = useState(getCurrentMonth());
|
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
const [totalBudget, setTotalBudget] = useState(0);
|
|
||||||
const { user } = useAuth();
|
|
||||||
const [refreshKey, setRefreshKey] = useState(0); // 강제 새로고침을 위한 키
|
|
||||||
|
|
||||||
// 월 변경 처리
|
|
||||||
const handlePrevMonth = () => {
|
|
||||||
setSelectedMonth(getPrevMonth(selectedMonth));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNextMonth = () => {
|
|
||||||
setSelectedMonth(getNextMonth(selectedMonth));
|
|
||||||
};
|
|
||||||
|
|
||||||
// 트랜잭션 로드
|
|
||||||
const loadTransactions = useCallback(() => {
|
|
||||||
setIsLoading(true);
|
|
||||||
setError(null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const localTransactions = loadTransactionsFromStorage();
|
|
||||||
setTransactions(localTransactions);
|
|
||||||
|
|
||||||
// 예산 가져오기
|
|
||||||
const budgetAmount = loadBudgetFromStorage();
|
|
||||||
setTotalBudget(budgetAmount);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('트랜잭션 로드 중 오류:', err);
|
|
||||||
setError('데이터를 불러오는 중 문제가 발생했습니다.');
|
|
||||||
toast({
|
|
||||||
title: "데이터 로드 실패",
|
|
||||||
description: "지출 내역을 불러오는데 실패했습니다.",
|
|
||||||
variant: "destructive",
|
|
||||||
duration: 4000
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
// 로딩 상태를 약간 지연시켜 UI 업데이트가 원활하게 이루어지도록 함
|
|
||||||
setTimeout(() => setIsLoading(false), 300);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 필터 적용
|
|
||||||
useEffect(() => {
|
|
||||||
// 1. 월별 필터링
|
|
||||||
let filtered = filterTransactionsByMonth(transactions, selectedMonth);
|
|
||||||
|
|
||||||
// 2. 검색어 필터링
|
|
||||||
if (searchQuery.trim()) {
|
|
||||||
filtered = filterTransactionsByQuery(filtered, searchQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('필터링 결과:', filtered.length, '트랜잭션');
|
|
||||||
setFilteredTransactions(filtered);
|
|
||||||
}, [transactions, selectedMonth, searchQuery]);
|
|
||||||
|
|
||||||
// 초기 데이터 로드 및 이벤트 리스너 설정
|
|
||||||
useEffect(() => {
|
|
||||||
console.log('useTransactions - 초기 데이터 로드');
|
|
||||||
loadTransactions();
|
|
||||||
|
|
||||||
// 트랜잭션 업데이트 이벤트 리스너
|
|
||||||
const handleTransactionUpdated = () => {
|
|
||||||
console.log('트랜잭션 업데이트 이벤트 감지됨');
|
|
||||||
loadTransactions();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 스토리지 변경 이벤트 리스너
|
|
||||||
const handleStorageChange = (e: StorageEvent) => {
|
|
||||||
if (e.key === 'transactions' || e.key === null) {
|
|
||||||
console.log('로컬 스토리지 변경 감지됨:', e.key);
|
|
||||||
loadTransactions();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 페이지 포커스/가시성 이벤트 리스너
|
|
||||||
const handleFocus = () => {
|
|
||||||
console.log('창 포커스 - 트랜잭션 새로고침');
|
|
||||||
loadTransactions();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleVisibilityChange = () => {
|
|
||||||
if (document.visibilityState === 'visible') {
|
|
||||||
console.log('페이지 가시성 변경 - 트랜잭션 새로고침');
|
|
||||||
loadTransactions();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 이벤트 리스너 등록
|
|
||||||
window.addEventListener('transactionUpdated', handleTransactionUpdated);
|
|
||||||
window.addEventListener('storage', handleStorageChange);
|
|
||||||
window.addEventListener('focus', handleFocus);
|
|
||||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
||||||
|
|
||||||
// 컴포넌트 마운트시에만 수동으로 트랜잭션 업데이트 이벤트 발생
|
|
||||||
window.dispatchEvent(new Event('transactionUpdated'));
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('transactionUpdated', handleTransactionUpdated);
|
|
||||||
window.removeEventListener('storage', handleStorageChange);
|
|
||||||
window.removeEventListener('focus', handleFocus);
|
|
||||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
||||||
};
|
|
||||||
}, [loadTransactions, refreshKey]);
|
|
||||||
|
|
||||||
// 트랜잭션 업데이트
|
|
||||||
const updateTransaction = (updatedTransaction: Transaction) => {
|
|
||||||
const updatedTransactions = transactions.map(transaction =>
|
|
||||||
transaction.id === updatedTransaction.id ? updatedTransaction : transaction
|
|
||||||
);
|
|
||||||
|
|
||||||
// 로컬 스토리지 업데이트
|
|
||||||
saveTransactionsToStorage(updatedTransactions);
|
|
||||||
|
|
||||||
// 상태 업데이트
|
|
||||||
setTransactions(updatedTransactions);
|
|
||||||
|
|
||||||
// Supabase 업데이트 시도
|
|
||||||
if (user) {
|
|
||||||
updateTransactionInSupabase(user, updatedTransaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 이벤트 발생
|
|
||||||
window.dispatchEvent(new Event('transactionUpdated'));
|
|
||||||
|
|
||||||
// 약간의 지연을 두고 토스트 표시
|
|
||||||
setTimeout(() => {
|
|
||||||
toast({
|
|
||||||
title: "지출이 수정되었습니다",
|
|
||||||
description: `${updatedTransaction.title} 항목이 업데이트되었습니다.`,
|
|
||||||
duration: 3000
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 트랜잭션 삭제
|
|
||||||
const deleteTransaction = (id: string) => {
|
|
||||||
const updatedTransactions = transactions.filter(transaction => transaction.id !== id);
|
|
||||||
|
|
||||||
// 로컬 스토리지 업데이트
|
|
||||||
saveTransactionsToStorage(updatedTransactions);
|
|
||||||
|
|
||||||
// 상태 업데이트
|
|
||||||
setTransactions(updatedTransactions);
|
|
||||||
|
|
||||||
// Supabase 삭제 시도
|
|
||||||
if (user) {
|
|
||||||
deleteTransactionFromSupabase(user, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 이벤트 발생
|
|
||||||
window.dispatchEvent(new Event('transactionUpdated'));
|
|
||||||
|
|
||||||
// 약간의 지연을 두고 토스트 표시
|
|
||||||
setTimeout(() => {
|
|
||||||
toast({
|
|
||||||
title: "지출이 삭제되었습니다",
|
|
||||||
description: "선택한 지출 항목이 삭제되었습니다.",
|
|
||||||
duration: 3000
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 데이터 강제 새로고침
|
|
||||||
const refreshTransactions = () => {
|
|
||||||
setRefreshKey(prev => prev + 1);
|
|
||||||
loadTransactions();
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
transactions: filteredTransactions,
|
|
||||||
allTransactions: transactions,
|
|
||||||
isLoading,
|
|
||||||
error,
|
|
||||||
totalBudget,
|
|
||||||
selectedMonth,
|
|
||||||
searchQuery,
|
|
||||||
setSearchQuery,
|
|
||||||
handlePrevMonth,
|
|
||||||
handleNextMonth,
|
|
||||||
updateTransaction,
|
|
||||||
deleteTransaction,
|
|
||||||
totalExpenses: calculateTotalExpenses(filteredTransactions),
|
|
||||||
refreshTransactions
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
100
src/hooks/transactions/useTransactionsCore.ts
Normal file
100
src/hooks/transactions/useTransactionsCore.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useTransactionsState } from './useTransactionsState';
|
||||||
|
import { useTransactionsFiltering } from './useTransactionsFiltering';
|
||||||
|
import { useTransactionsLoader } from './useTransactionsLoader';
|
||||||
|
import { useTransactionsOperations } from './useTransactionsOperations';
|
||||||
|
import { useTransactionsEvents } from './useTransactionsEvents';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 핵심 트랜잭션 훅
|
||||||
|
* 모든 트랜잭션 관련 훅을 통합합니다.
|
||||||
|
*/
|
||||||
|
export const useTransactionsCore = () => {
|
||||||
|
// 상태 관리
|
||||||
|
const {
|
||||||
|
transactions,
|
||||||
|
setTransactions,
|
||||||
|
filteredTransactions,
|
||||||
|
setFilteredTransactions,
|
||||||
|
selectedMonth,
|
||||||
|
setSelectedMonth,
|
||||||
|
searchQuery,
|
||||||
|
setSearchQuery,
|
||||||
|
isLoading,
|
||||||
|
setIsLoading,
|
||||||
|
error,
|
||||||
|
setError,
|
||||||
|
totalBudget,
|
||||||
|
setTotalBudget,
|
||||||
|
refreshKey,
|
||||||
|
setRefreshKey
|
||||||
|
} = useTransactionsState();
|
||||||
|
|
||||||
|
// 데이터 로딩
|
||||||
|
const { loadTransactions } = useTransactionsLoader(
|
||||||
|
setTransactions,
|
||||||
|
setTotalBudget,
|
||||||
|
setIsLoading,
|
||||||
|
setError
|
||||||
|
);
|
||||||
|
|
||||||
|
// 필터링
|
||||||
|
const {
|
||||||
|
handlePrevMonth,
|
||||||
|
handleNextMonth,
|
||||||
|
getTotalExpenses
|
||||||
|
} = useTransactionsFiltering(
|
||||||
|
transactions,
|
||||||
|
selectedMonth,
|
||||||
|
setSelectedMonth,
|
||||||
|
searchQuery,
|
||||||
|
setFilteredTransactions
|
||||||
|
);
|
||||||
|
|
||||||
|
// 트랜잭션 작업
|
||||||
|
const {
|
||||||
|
updateTransaction,
|
||||||
|
deleteTransaction
|
||||||
|
} = useTransactionsOperations(
|
||||||
|
transactions,
|
||||||
|
setTransactions
|
||||||
|
);
|
||||||
|
|
||||||
|
// 이벤트 리스너
|
||||||
|
useTransactionsEvents(loadTransactions, refreshKey);
|
||||||
|
|
||||||
|
// 데이터 강제 새로고침
|
||||||
|
const refreshTransactions = useCallback(() => {
|
||||||
|
setRefreshKey(prev => prev + 1);
|
||||||
|
loadTransactions();
|
||||||
|
}, [loadTransactions, setRefreshKey]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
// 데이터
|
||||||
|
transactions: filteredTransactions,
|
||||||
|
allTransactions: transactions,
|
||||||
|
|
||||||
|
// 상태
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
totalBudget,
|
||||||
|
|
||||||
|
// 필터링
|
||||||
|
selectedMonth,
|
||||||
|
searchQuery,
|
||||||
|
setSearchQuery,
|
||||||
|
handlePrevMonth,
|
||||||
|
handleNextMonth,
|
||||||
|
|
||||||
|
// 작업
|
||||||
|
updateTransaction,
|
||||||
|
deleteTransaction,
|
||||||
|
|
||||||
|
// 합계
|
||||||
|
totalExpenses: getTotalExpenses(filteredTransactions),
|
||||||
|
|
||||||
|
// 새로고침
|
||||||
|
refreshTransactions
|
||||||
|
};
|
||||||
|
};
|
||||||
59
src/hooks/transactions/useTransactionsEvents.ts
Normal file
59
src/hooks/transactions/useTransactionsEvents.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 트랜잭션 이벤트 관련 훅
|
||||||
|
* 각종 이벤트 리스너를 설정합니다.
|
||||||
|
*/
|
||||||
|
export const useTransactionsEvents = (
|
||||||
|
loadTransactions: () => void,
|
||||||
|
refreshKey: number
|
||||||
|
) => {
|
||||||
|
// 이벤트 리스너 설정
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('useTransactions - 이벤트 리스너 설정');
|
||||||
|
|
||||||
|
// 트랜잭션 업데이트 이벤트 리스너
|
||||||
|
const handleTransactionUpdated = () => {
|
||||||
|
console.log('트랜잭션 업데이트 이벤트 감지됨');
|
||||||
|
loadTransactions();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 스토리지 변경 이벤트 리스너
|
||||||
|
const handleStorageChange = (e: StorageEvent) => {
|
||||||
|
if (e.key === 'transactions' || e.key === null) {
|
||||||
|
console.log('로컬 스토리지 변경 감지됨:', e.key);
|
||||||
|
loadTransactions();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 페이지 포커스/가시성 이벤트 리스너
|
||||||
|
const handleFocus = () => {
|
||||||
|
console.log('창 포커스 - 트랜잭션 새로고침');
|
||||||
|
loadTransactions();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleVisibilityChange = () => {
|
||||||
|
if (document.visibilityState === 'visible') {
|
||||||
|
console.log('페이지 가시성 변경 - 트랜잭션 새로고침');
|
||||||
|
loadTransactions();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 이벤트 리스너 등록
|
||||||
|
window.addEventListener('transactionUpdated', handleTransactionUpdated);
|
||||||
|
window.addEventListener('storage', handleStorageChange);
|
||||||
|
window.addEventListener('focus', handleFocus);
|
||||||
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||||
|
|
||||||
|
// 컴포넌트 마운트시에만 수동으로 트랜잭션 업데이트 이벤트 발생
|
||||||
|
window.dispatchEvent(new Event('transactionUpdated'));
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('transactionUpdated', handleTransactionUpdated);
|
||||||
|
window.removeEventListener('storage', handleStorageChange);
|
||||||
|
window.removeEventListener('focus', handleFocus);
|
||||||
|
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||||
|
};
|
||||||
|
}, [loadTransactions, refreshKey]);
|
||||||
|
};
|
||||||
55
src/hooks/transactions/useTransactionsFiltering.ts
Normal file
55
src/hooks/transactions/useTransactionsFiltering.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { Transaction } from '@/components/TransactionCard';
|
||||||
|
import {
|
||||||
|
filterTransactionsByMonth,
|
||||||
|
filterTransactionsByQuery,
|
||||||
|
calculateTotalExpenses
|
||||||
|
} from './filterUtils';
|
||||||
|
import { getPrevMonth, getNextMonth } from './dateUtils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 트랜잭션 필터링 관련 훅
|
||||||
|
* 월별 및 검색어 필터링 기능을 제공합니다.
|
||||||
|
*/
|
||||||
|
export const useTransactionsFiltering = (
|
||||||
|
transactions: Transaction[],
|
||||||
|
selectedMonth: string,
|
||||||
|
setSelectedMonth: (month: string) => void,
|
||||||
|
searchQuery: string,
|
||||||
|
setFilteredTransactions: (transactions: Transaction[]) => void
|
||||||
|
) => {
|
||||||
|
// 월 변경 처리
|
||||||
|
const handlePrevMonth = () => {
|
||||||
|
setSelectedMonth(getPrevMonth(selectedMonth));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNextMonth = () => {
|
||||||
|
setSelectedMonth(getNextMonth(selectedMonth));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 필터 적용
|
||||||
|
useEffect(() => {
|
||||||
|
// 1. 월별 필터링
|
||||||
|
let filtered = filterTransactionsByMonth(transactions, selectedMonth);
|
||||||
|
|
||||||
|
// 2. 검색어 필터링
|
||||||
|
if (searchQuery.trim()) {
|
||||||
|
filtered = filterTransactionsByQuery(filtered, searchQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('필터링 결과:', filtered.length, '트랜잭션');
|
||||||
|
setFilteredTransactions(filtered);
|
||||||
|
}, [transactions, selectedMonth, searchQuery, setFilteredTransactions]);
|
||||||
|
|
||||||
|
// 필터링된 트랜잭션의 총 지출 계산
|
||||||
|
const getTotalExpenses = (filteredTransactions: Transaction[]) => {
|
||||||
|
return calculateTotalExpenses(filteredTransactions);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
handlePrevMonth,
|
||||||
|
handleNextMonth,
|
||||||
|
getTotalExpenses
|
||||||
|
};
|
||||||
|
};
|
||||||
49
src/hooks/transactions/useTransactionsLoader.ts
Normal file
49
src/hooks/transactions/useTransactionsLoader.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { toast } from '@/hooks/useToast.wrapper';
|
||||||
|
import {
|
||||||
|
loadTransactionsFromStorage,
|
||||||
|
loadBudgetFromStorage
|
||||||
|
} from './storageUtils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 트랜잭션 로딩 관련 훅
|
||||||
|
* 로컬 스토리지에서 트랜잭션 데이터를 로드합니다.
|
||||||
|
*/
|
||||||
|
export const useTransactionsLoader = (
|
||||||
|
setTransactions: (transactions: any[]) => void,
|
||||||
|
setTotalBudget: (budget: number) => void,
|
||||||
|
setIsLoading: (isLoading: boolean) => void,
|
||||||
|
setError: (error: string | null) => void
|
||||||
|
) => {
|
||||||
|
// 트랜잭션 로드
|
||||||
|
const loadTransactions = useCallback(() => {
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const localTransactions = loadTransactionsFromStorage();
|
||||||
|
setTransactions(localTransactions);
|
||||||
|
|
||||||
|
// 예산 가져오기
|
||||||
|
const budgetAmount = loadBudgetFromStorage();
|
||||||
|
setTotalBudget(budgetAmount);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('트랜잭션 로드 중 오류:', err);
|
||||||
|
setError('데이터를 불러오는 중 문제가 발생했습니다.');
|
||||||
|
toast({
|
||||||
|
title: "데이터 로드 실패",
|
||||||
|
description: "지출 내역을 불러오는데 실패했습니다.",
|
||||||
|
variant: "destructive",
|
||||||
|
duration: 4000
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
// 로딩 상태를 약간 지연시켜 UI 업데이트가 원활하게 이루어지도록 함
|
||||||
|
setTimeout(() => setIsLoading(false), 300);
|
||||||
|
}
|
||||||
|
}, [setTransactions, setTotalBudget, setIsLoading, setError]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
loadTransactions
|
||||||
|
};
|
||||||
|
};
|
||||||
84
src/hooks/transactions/useTransactionsOperations.ts
Normal file
84
src/hooks/transactions/useTransactionsOperations.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { Transaction } from '@/components/TransactionCard';
|
||||||
|
import { useAuth } from '@/contexts/auth/AuthProvider';
|
||||||
|
import { toast } from '@/hooks/useToast.wrapper';
|
||||||
|
import { saveTransactionsToStorage } from './storageUtils';
|
||||||
|
import {
|
||||||
|
updateTransactionInSupabase,
|
||||||
|
deleteTransactionFromSupabase
|
||||||
|
} from './supabaseUtils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 트랜잭션 작업 관련 훅
|
||||||
|
* 트랜잭션 업데이트, 삭제 기능을 제공합니다.
|
||||||
|
*/
|
||||||
|
export const useTransactionsOperations = (
|
||||||
|
transactions: Transaction[],
|
||||||
|
setTransactions: (transactions: Transaction[]) => void
|
||||||
|
) => {
|
||||||
|
const { user } = useAuth();
|
||||||
|
|
||||||
|
// 트랜잭션 업데이트
|
||||||
|
const updateTransaction = useCallback((updatedTransaction: Transaction) => {
|
||||||
|
const updatedTransactions = transactions.map(transaction =>
|
||||||
|
transaction.id === updatedTransaction.id ? updatedTransaction : transaction
|
||||||
|
);
|
||||||
|
|
||||||
|
// 로컬 스토리지 업데이트
|
||||||
|
saveTransactionsToStorage(updatedTransactions);
|
||||||
|
|
||||||
|
// 상태 업데이트
|
||||||
|
setTransactions(updatedTransactions);
|
||||||
|
|
||||||
|
// Supabase 업데이트 시도
|
||||||
|
if (user) {
|
||||||
|
updateTransactionInSupabase(user, updatedTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 이벤트 발생
|
||||||
|
window.dispatchEvent(new Event('transactionUpdated'));
|
||||||
|
|
||||||
|
// 약간의 지연을 두고 토스트 표시
|
||||||
|
setTimeout(() => {
|
||||||
|
toast({
|
||||||
|
title: "지출이 수정되었습니다",
|
||||||
|
description: `${updatedTransaction.title} 항목이 업데이트되었습니다.`,
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
}, [transactions, setTransactions, user]);
|
||||||
|
|
||||||
|
// 트랜잭션 삭제
|
||||||
|
const deleteTransaction = useCallback((id: string) => {
|
||||||
|
const updatedTransactions = transactions.filter(transaction => transaction.id !== id);
|
||||||
|
|
||||||
|
// 로컬 스토리지 업데이트
|
||||||
|
saveTransactionsToStorage(updatedTransactions);
|
||||||
|
|
||||||
|
// 상태 업데이트
|
||||||
|
setTransactions(updatedTransactions);
|
||||||
|
|
||||||
|
// Supabase 삭제 시도
|
||||||
|
if (user) {
|
||||||
|
deleteTransactionFromSupabase(user, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 이벤트 발생
|
||||||
|
window.dispatchEvent(new Event('transactionUpdated'));
|
||||||
|
|
||||||
|
// 약간의 지연을 두고 토스트 표시
|
||||||
|
setTimeout(() => {
|
||||||
|
toast({
|
||||||
|
title: "지출이 삭제되었습니다",
|
||||||
|
description: "선택한 지출 항목이 삭제되었습니다.",
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
}, [transactions, setTransactions, user]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
updateTransaction,
|
||||||
|
deleteTransaction
|
||||||
|
};
|
||||||
|
};
|
||||||
56
src/hooks/transactions/useTransactionsState.ts
Normal file
56
src/hooks/transactions/useTransactionsState.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { Transaction } from '@/components/TransactionCard';
|
||||||
|
import { getCurrentMonth } from './dateUtils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 트랜잭션 관련 상태 관리 훅
|
||||||
|
* 트랜잭션, 필터링, 로딩 상태 등을 관리합니다.
|
||||||
|
*/
|
||||||
|
export const useTransactionsState = () => {
|
||||||
|
// 트랜잭션 상태
|
||||||
|
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
||||||
|
const [filteredTransactions, setFilteredTransactions] = useState<Transaction[]>([]);
|
||||||
|
|
||||||
|
// 필터링 상태
|
||||||
|
const [selectedMonth, setSelectedMonth] = useState(getCurrentMonth());
|
||||||
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
|
|
||||||
|
// 로딩 및 에러 상태
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// 예산 상태
|
||||||
|
const [totalBudget, setTotalBudget] = useState(0);
|
||||||
|
|
||||||
|
// 새로고침 키
|
||||||
|
const [refreshKey, setRefreshKey] = useState(0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
// 상태
|
||||||
|
transactions,
|
||||||
|
setTransactions,
|
||||||
|
filteredTransactions,
|
||||||
|
setFilteredTransactions,
|
||||||
|
|
||||||
|
// 필터링 상태
|
||||||
|
selectedMonth,
|
||||||
|
setSelectedMonth,
|
||||||
|
searchQuery,
|
||||||
|
setSearchQuery,
|
||||||
|
|
||||||
|
// 로딩 및 에러 상태
|
||||||
|
isLoading,
|
||||||
|
setIsLoading,
|
||||||
|
error,
|
||||||
|
setError,
|
||||||
|
|
||||||
|
// 예산 상태
|
||||||
|
totalBudget,
|
||||||
|
setTotalBudget,
|
||||||
|
|
||||||
|
// 새로고침 키
|
||||||
|
refreshKey,
|
||||||
|
setRefreshKey
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user