diff --git a/package-lock.json b/package-lock.json index d0c16e8..c373f08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "react-toastify": "^10.0.6", "redux": "^5.0.1", "redux-persist": "^6.0.0", + "reselect": "^5.1.1", "web-vitals": "^2.1.3", "yup": "^1.4.0" } diff --git a/package.json b/package.json index af58f9a..8bf39e6 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "react-toastify": "^10.0.6", "redux": "^5.0.1", "redux-persist": "^6.0.0", + "reselect": "^5.1.1", "web-vitals": "^2.1.3", "yup": "^1.4.0" }, diff --git a/src/Pages/Home/Home.jsx b/src/Pages/Home/Home.jsx index 6363932..15eb9a5 100644 --- a/src/Pages/Home/Home.jsx +++ b/src/Pages/Home/Home.jsx @@ -22,6 +22,7 @@ const Home = () => { const data = useSelector(selectAllTransactions); useEffect(() => { + console.log('Data:', data); dispatch(fetchTransactions()); }, [dispatch]); diff --git a/src/components/StatisticsDashboard/StatisticsDashboard.jsx b/src/components/StatisticsDashboard/StatisticsDashboard.jsx index 74f0a09..d5d0a1f 100644 --- a/src/components/StatisticsDashboard/StatisticsDashboard.jsx +++ b/src/components/StatisticsDashboard/StatisticsDashboard.jsx @@ -1,9 +1,12 @@ -import React, { useState } from 'react'; +import React, { useEffect } from 'react'; +import { useDispatch } from 'react-redux'; import CustomDropdown from '../CustomDropdown/CustomDropdown'; +import { getTransactionsSummaryByPeriod } from '../../redux/Statistics/operations'; const StatisticsDashboard = () => { - const [selectedMonth, setSelectedMonth] = useState(11); - const [selectedYear, setSelectedYear] = useState(2024); + const dispatch = useDispatch(); + const [selectedMonth, setSelectedMonth] = React.useState(11); + const [selectedYear, setSelectedYear] = React.useState(2024); const months = [ { value: 1, label: 'January' }, @@ -28,6 +31,15 @@ const StatisticsDashboard = () => { { value: 2024, label: '2024' }, ]; + useEffect(() => { + dispatch( + getTransactionsSummaryByPeriod({ + month: selectedMonth, + year: selectedYear, + }) + ); + }, [dispatch, selectedMonth, selectedYear]); + return (
{ selectedValue={selectedYear} onSelect={setSelectedYear} /> + {/* Poți folosi transactionsSummary aici sau în StatisticsTable */}
); }; diff --git a/src/components/StatisticsTable/StatisticsTable.jsx b/src/components/StatisticsTable/StatisticsTable.jsx index f1c919d..010cdc0 100644 --- a/src/components/StatisticsTable/StatisticsTable.jsx +++ b/src/components/StatisticsTable/StatisticsTable.jsx @@ -1,31 +1,87 @@ import React from 'react'; +import { useSelector } from 'react-redux'; +import { useEffect } from 'react'; +// import { selectTransactionsSummary } from '../../redux/selectors/transactionsSelector'; +import { selectTransactionsByCategory } from '../../redux/selectors/transactionsSelector'; +// import { getTransactionsSummaryByPeriod } from '../../redux/Statistics/operations'; import styles from './StatisticsTable.module.css'; const StatisticsTable = () => { + // const totalTransactionsAmount = useSelector(selectTransactionsSummary); + const transactionsByCategory = useSelector(selectTransactionsByCategory); + + useEffect(() => { + console.log('Sume pe categorii:', transactionsByCategory); + }, [transactionsByCategory]); + const expenses = [ - { label: 'Main expenses', amount: 8700.00, color: '#fed057'}, - { label: 'Products', amount: 3800.74, color: '#ffd8d0'}, - { label: 'Car', amount: 1500.00, color: '#FFADAD' }, - { label: 'Self care', amount: 800.00, color: '#D0A8FF' }, - { label: 'Child care', amount: 2208.50, color: '#8F9BFF' }, - { label: 'Household products', amount: 300.00, color: '#6E7FFF' }, - { label: 'Education', amount: 3400.00, color: '#65E2FF' }, - { label: 'Leisure', amount: 1230.00, color: '#47D5A4' }, - { label: 'Other expenses', amount: 610.00, color: '#28B491' }, + { + label: 'Main expenses', + amount: transactionsByCategory['Main expenses'] || 0, + color: '#fed057', + }, + { + label: 'Products', + amount: transactionsByCategory['Products'] || 0, + color: '#ffd8d0', + }, + { + label: 'Car', + amount: transactionsByCategory['Car'] || 0, + color: '#FFADAD', + }, + { + label: 'Self care', + amount: transactionsByCategory['Self care'] || 0, + color: '#D0A8FF', + }, + { + label: 'Child care', + amount: transactionsByCategory['Child care'] || 0, + color: '#8F9BFF', + }, + { + label: 'Household products', + amount: transactionsByCategory['Household products'] || 0, + color: '#6E7FFF', + }, + { + label: 'Education', + amount: transactionsByCategory['Education'] || 0, + color: '#65E2FF', + }, + { + label: 'Leisure', + amount: transactionsByCategory['Leisure'] || 0, + color: '#47D5A4', + }, + { + label: 'Other expenses', + amount: transactionsByCategory['Other expenses'] || 0, + color: '#28B491', + }, ]; - // ce este mai sus este doar pentru proba, trebuie modificat cu ce se introduce in modal - - const totalExpenses = expenses.reduce((sum, expense) => sum + expense.amount, 0).toFixed(2); - const income = 27350.00; + const totalExpenses = expenses + .reduce((sum, expense) => sum + expense.amount, 0) + .toFixed(2); + const income = 27350.0; return (
{expenses.map((expense, index) => (
- + {expense.label} - {expense.amount.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + {expense.amount.toLocaleString('en-US', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + })} +
))}
@@ -35,7 +91,12 @@ const StatisticsTable = () => {
Income: - {income.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + {income.toLocaleString('en-US', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + })} +
diff --git a/src/components/TransactionsDesktop/TransactionsDesktop.jsx b/src/components/TransactionsDesktop/TransactionsDesktop.jsx index ede941e..326c7f8 100644 --- a/src/components/TransactionsDesktop/TransactionsDesktop.jsx +++ b/src/components/TransactionsDesktop/TransactionsDesktop.jsx @@ -3,26 +3,20 @@ import TransactionsDesktopRow from '../../components/TransactionsDesktopRow/Tran import styles from './TransactionsDesktop.module.css'; const TransactionsDesktop = ({ data, openDeleteModal, openEditModal }) => { - // Definim funcția de comparație pentru sortare: const compareTransactions = (a, b) => { const dateComparison = new Date(a.transactionDate) - new Date(b.transactionDate); - // Verificăm dacă tranzacțiile au aceeași dată: if (dateComparison === 0) { - // Dacă tranzacția A este de tip "Income", o afișăm înaintea celei de tip "Expense": if (a.type === 'INCOME' && b.type !== 'INCOME') { return -1; } - // Dacă tranzacția B este de tip "Income", o afișăm înaintea celei de tip "Expense": if (a.type !== 'INCOME' && b.type === 'INCOME') { return 1; } } - // Returnăm rezultatul comparării datelor: return dateComparison; }; - // Sortăm datele utilizând funcția de comparație definită mai sus: const sortedData = [...data].sort(compareTransactions); return ( diff --git a/src/index.js b/src/index.js index 3273f03..e3ded25 100644 --- a/src/index.js +++ b/src/index.js @@ -8,10 +8,12 @@ import './index.css'; const container = document.getElementById('root'); const root = createRoot(container); +const basename = + process.env.NODE_ENV === 'production' ? '//team1_MoneyGuard' : '/'; root.render( - + diff --git a/src/redux/operations/authOperations.js b/src/redux/operations/authOperations.js index 0c8985d..28dda8f 100644 --- a/src/redux/operations/authOperations.js +++ b/src/redux/operations/authOperations.js @@ -55,7 +55,7 @@ export const getUserInfo = createAsyncThunk( async (_, thunkAPI) => { try { const response = await axios.get(`${BASE_URL}/users/current`); - + console.log('User Info:', response.data); return response.data; } catch (error) { console.log(error); diff --git a/src/redux/operations/transactionsOperations.js b/src/redux/operations/transactionsOperations.js index 6630f28..e3e75c2 100644 --- a/src/redux/operations/transactionsOperations.js +++ b/src/redux/operations/transactionsOperations.js @@ -84,7 +84,26 @@ export const deleteTransaction = createAsyncThunk( export const fetchCategories = createAsyncThunk( 'categories/fetch', async () => { - const response = await axios.get('/api/categories'); // pt api, inca nu e implementat!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + const response = await axios.get('/api/categories'); return response.data; } ); + +// Fetch Transaction Categories +export const fetchTransactionCategories = createAsyncThunk( + 'transactions/fetchCategories', + async (_, { rejectWithValue }) => { + const token = localStorage.getItem('token'); + try { + const response = await axios.get(`${BASE_URL}/transaction-categories`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + toast.error('Failed to load transaction categories'); + return rejectWithValue(error.response?.data || error.message); + } + } +); diff --git a/src/redux/selectors/transactionsSelector.js b/src/redux/selectors/transactionsSelector.js index 0558b60..5282f31 100644 --- a/src/redux/selectors/transactionsSelector.js +++ b/src/redux/selectors/transactionsSelector.js @@ -1,3 +1,5 @@ +import { createSelector } from 'reselect'; + const selectAllTransactions = state => state.transactions.items; const selectTotalBalance = state => state.transactions.totalBalance; const selectError = state => state.transactions.error; @@ -7,10 +9,36 @@ const selectTrasactionIdForDelete = state => const selectTransactionForUpdate = state => state.transactions.transactionForUpdate; +const selectTransactionsSummary = state => { + const transactions = state.transactions.items; + const totalAmount = transactions.reduce( + (sum, transaction) => sum + transaction.amount, + 0 + ); + + return totalAmount; +}; + +const selectTransactionsByCategory = createSelector( + state => state.transactions.items, + transactions => { + console.log('Tranzacții primite în selector:', transactions); + return transactions.reduce((acc, transaction) => { + const { category, amount } = transaction; + if (category) { + acc[category] = (acc[category] || 0) + amount; + } + return acc; + }, {}); + } +); + export { selectAllTransactions, selectTotalBalance, selectError, selectTransactionForUpdate, selectTrasactionIdForDelete, + selectTransactionsSummary, + selectTransactionsByCategory, };