Files
zellyy-finance/src/pages/Transactions.tsx
gpt-engineer-app[bot] 017f5615c1 Add bottom margin to pages
Adds a bottom margin to the last card in the Transactions and Analytics pages.
2025-03-16 12:16:14 +00:00

189 lines
6.3 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import NavBar from '@/components/NavBar';
import TransactionCard from '@/components/TransactionCard';
import AddTransactionButton from '@/components/AddTransactionButton';
import { Calendar, Search, ChevronLeft, ChevronRight, Loader2 } from 'lucide-react';
import { formatCurrency } from '@/utils/formatters';
import { useTransactions, MONTHS_KR } from '@/hooks/transactions';
import { useBudget } from '@/contexts/BudgetContext';
const Transactions = () => {
const {
transactions,
isLoading,
selectedMonth,
searchQuery,
setSearchQuery,
handlePrevMonth,
handleNextMonth,
updateTransaction,
deleteTransaction,
totalExpenses,
} = useTransactions();
const { budgetData } = useBudget();
const [isDataLoaded, setIsDataLoaded] = useState(false);
// 데이터 로드 상태 관리
useEffect(() => {
if (budgetData && !isLoading) {
setIsDataLoaded(true);
}
}, [budgetData, isLoading]);
// 트랜잭션을 날짜별로 그룹화
const groupedTransactions: Record<string, typeof transactions> = {};
transactions.forEach(transaction => {
const datePart = transaction.date.split(',')[0];
if (!groupedTransactions[datePart]) {
groupedTransactions[datePart] = [];
}
groupedTransactions[datePart].push(transaction);
});
// 페이지 포커스나 가시성 변경 시 데이터 새로고침
useEffect(() => {
const handleVisibilityChange = () => {
if (document.visibilityState === 'visible') {
console.log('거래내역 페이지 보임 - 데이터 새로고침');
// 상태 업데이트 트리거
setIsDataLoaded(prev => !prev);
}
};
const handleFocus = () => {
console.log('거래내역 페이지 포커스 - 데이터 새로고침');
// 상태 업데이트 트리거
setIsDataLoaded(prev => !prev);
};
document.addEventListener('visibilitychange', handleVisibilityChange);
window.addEventListener('focus', handleFocus);
return () => {
document.removeEventListener('visibilitychange', handleVisibilityChange);
window.removeEventListener('focus', handleFocus);
};
}, []);
return (
<div className="min-h-screen bg-neuro-background pb-24">
<div className="max-w-md mx-auto px-6">
{/* Header */}
<header className="py-8">
<h1 className="text-2xl font-bold neuro-text mb-5"> </h1>
{/* Search */}
<div className="neuro-pressed mb-5 flex items-center px-4 py-3 rounded-xl">
<Search size={18} className="text-gray-500 mr-2" />
<input
type="text"
placeholder="지출 검색..."
className="bg-transparent flex-1 outline-none text-sm"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
{/* Month Selector */}
<div className="flex items-center justify-between mb-5">
<button
className="neuro-flat p-2 rounded-full"
onClick={handlePrevMonth}
>
<ChevronLeft size={20} />
</button>
<div className="flex items-center gap-2">
<Calendar size={18} className="text-neuro-income" />
<span className="font-medium text-lg">{selectedMonth}</span>
</div>
<button
className="neuro-flat p-2 rounded-full"
onClick={handleNextMonth}
>
<ChevronRight size={20} />
</button>
</div>
{/* Summary */}
<div className="grid grid-cols-2 gap-4 mb-8">
<div className="neuro-card">
<p className="text-sm text-gray-500 mb-1"> </p>
<p className="text-lg font-bold text-neuro-income">
{formatCurrency(budgetData?.monthly?.targetAmount || 0)}
</p>
</div>
<div className="neuro-card">
<p className="text-sm text-gray-500 mb-1"> </p>
<p className="text-lg font-bold text-neuro-income">
{formatCurrency(totalExpenses)}
</p>
</div>
</div>
</header>
{/* Loading State */}
{isLoading && (
<div className="flex justify-center items-center py-10">
<Loader2 className="h-8 w-8 animate-spin text-neuro-income" />
<span className="ml-2 text-gray-500"> ...</span>
</div>
)}
{/* Empty State */}
{!isLoading && transactions.length === 0 && (
<div className="text-center py-10">
<p className="text-gray-500 mb-3">
{searchQuery.trim()
? '검색 결과가 없습니다.'
: `${selectedMonth}에 등록된 지출이 없습니다.`}
</p>
{searchQuery.trim() && (
<button
className="text-neuro-income"
onClick={() => setSearchQuery('')}
>
</button>
)}
</div>
)}
{/* Transactions By Date */}
{!isLoading && transactions.length > 0 && (
<div className="space-y-6 mb-[50px]">
{Object.entries(groupedTransactions).map(([date, transactions]) => (
<div key={date}>
<div className="flex items-center gap-2 mb-3">
<div className="h-1 flex-1 neuro-pressed"></div>
<h2 className="text-sm font-medium text-gray-500">{date}</h2>
<div className="h-1 flex-1 neuro-pressed"></div>
</div>
<div className="grid gap-3">
{transactions.map(transaction => (
<TransactionCard
key={transaction.id}
transaction={transaction}
onUpdate={updateTransaction}
/>
))}
</div>
</div>
))}
</div>
)}
</div>
<AddTransactionButton />
<NavBar />
</div>
);
};
export default Transactions;