ExpenseTracker.js

import React, { useState, useContext } from 'react'; import { FinanceContext } from '../contexts/FinanceContext'; function ExpenseTracker() { const [amount, setAmount] = useState(''); const [category, setCategory] = useState('essentials'); const [details, setDetails] = useState(''); const [location, setLocation] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const [expandedMonths, setExpandedMonths] = useState({}); const { deductFromCategory, transactionHistory } = useContext(FinanceContext); const categories = [ { value: 'essentials', label: 'Tiêu dùng thiết yếu (50%)' }, { value: 'savings', label: 'Tiết kiệm bắt buộc (20%)' }, { value: 'selfInvestment', label: 'Đầu tư bản thân (15%)' }, { value: 'charity', label: 'Từ thiện (5%)' }, { value: 'emergency', label: 'Dự phòng linh hoạt (10%)' }, ]; const handleSubmit = (e) => { e.preventDefault(); const parsedAmount = parseFloat(amount); if (parsedAmount <= 0) { alert('Vui lòng nhập số tiền hợp lệ!'); return; } if (!details.trim()) { alert('Vui lòng nhập chi tiết giao dịch!'); return; } if (!location.trim()) { alert('Vui lòng nhập vị trí giao dịch!'); return; } setIsSubmitting(true); setTimeout(() => { const success = deductFromCategory(category, parsedAmount, details, location); setIsSubmitting(false); if (success) { setAmount(''); setDetails(''); setLocation(''); alert('Giao dịch đã được ghi lại!'); } else { alert('Không đủ số dư trong danh mục này!'); } }, 1000); }; // Nhóm giao dịch theo tháng const groupByMonth = (transactions) => { const grouped = {}; transactions.forEach((transaction) => { const date = new Date(transaction.timestamp.split(',')[0].split('/').reverse().join('-')); const monthYear = date.toLocaleString('vi-VN', { month: 'long', year: 'numeric' }); if (!grouped[monthYear]) { grouped[monthYear] = []; } grouped[monthYear].push(transaction); }); return grouped; }; const groupedTransactions = groupByMonth(transactionHistory); const toggleMonth = (monthYear) => { setExpandedMonths((prev) => ({ ...prev, [monthYear]: !prev[monthYear], })); }; const formatVND = (value) => { return new Intl.NumberFormat('vi-VN', { style: 'currency', currency: 'VND' }).format(value); }; return (

Theo dõi chi tiêu chi tiết

setAmount(e.target.value)} className="w-full p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500 transition" placeholder="Nhập số tiền" required min="1" />
setDetails(e.target.value)} className="w-full p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500 transition" placeholder="Nhập chi tiết (ví dụ: Mua thực phẩm)" required />
setLocation(e.target.value)} className="w-full p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500 transition" placeholder="Nhập vị trí (ví dụ: Siêu thị Coopmart)" required />

Lịch sử chi tiêu

{Object.keys(groupedTransactions).length > 0 ? ( Object.keys(groupedTransactions).sort((a, b) => { const dateA = new Date(a.split(' ')[1], new Date().toLocaleString('vi-VN', { month: 'long' }).indexOf(a.split(' ')[0])); const dateB = new Date(b.split(' ')[1], new Date().toLocaleString('vi-VN', { month: 'long' }).indexOf(b.split(' ')[0])); return dateB - dateA; }).map((monthYear) => (
{expandedMonths[monthYear] && (
{groupedTransactions[monthYear].map((transaction, index) => (

Danh mục: {categories.find(cat => cat.value === transaction.category)?.label}

Số tiền: {formatVND(transaction.amount)}

Chi tiết: {transaction.details}

Vị trí: {transaction.location}

Thời gian: {transaction.timestamp}

))}
)}
)) ) : (

Chưa có giao dịch nào.

)}
); } export default ExpenseTracker;
Huyền

Một Blog Anime chia sẻ những bộ anime hay download về để xem chất lượng cao nhất. neyuhv.blogspot.com does not host any files, it merely links to 3rd party services. Legal issues should be taken up with the file hosts and providers. neyuhv.blogspot.com is not responsible for any media files shown by the video providers.

Mới hơn Cũ hơn