diff --git a/back-end/routes/addEventMemberRoute.js b/back-end/routes/addEventMemberRoute.js index ec2b702..718c364 100644 --- a/back-end/routes/addEventMemberRoute.js +++ b/back-end/routes/addEventMemberRoute.js @@ -4,30 +4,7 @@ const { User } = require("../models/User.js"); const { Event } = require("../models/Event.js"); const { body, validationResult } = require("express-validator"); -// Hardcoded user data to simulate a database - -/* -let friendData = [{ - "id":"507f1f77bcf86cd799439011", - "name":"Gaby Coupar", - "avatar":"https://robohash.org/temporererumomnis.png?size=50x50\u0026set=set1", - "phone":"435-715-2899", - "email":"gcoupar0@rakuten.co.jp" - }, { - "id": "507f1f77bcf86cd799439012", - "name": "Andy Gaber", - "avatar": "https://robohash.org/quaeetcorrupti.png?size=50x50&set=set1", - "phone":"425-712-2309", - "email":"gmember0@rakuten.co.jp" - }] - -// Endpoint to get user data after search -router.get('/friend', (req, res) => { - res.json(friendData); -}); -*/ - -router.get("/friendsList/:userId", async (req, res) => { +router.get('/friendsList/:userId', async (req, res) => { try { //fetch all data const userId = req.params.userId; @@ -47,66 +24,4 @@ router.get("/friendsList/:userId", async (req, res) => { } }); -/* -router.get('/friends/:userId', async (req, res) => { - try { - const userId = req.params.userId; - console.log('Fetching user with ID:', userId); - - const user = await User.findById(userId).populate('friends'); - console.log('User:', user); - - if (!user) { - console.log('User not found'); - return res.status(404).json({ message: 'User not found' }); - } - - res.json(user.friends); - } catch (error) { - console.error('Error:', error); - res.status(500).json({ message: 'Server error', error: error.message }); - } -}); - - -router.post('/events/:eventId/participants', async (req, res) => { - try { - const { eventId } = req.params; - const { memberId } = req.body; - - // Find the event and add the new member - const event = await Event.findById(eventId); - const user = await User.findById(userId) - - if (!event) { - return res.status(404).json({ message: 'Event not found' }); - } - if(!user){ - return res.status(404).json({ message: 'User not found'}) - } - - // Check if member is already in the participants list - if (event.participants.includes(memberId)) { - return res.status(400).json({ message: 'Member already in the event' }); - } - - // Validate if the memberId is in the user's friend list - const isFriend = user.friends.some(friend => friend._id.toString() === memberId); - - if (!isFriend) { - return res.status(400).json({ message: 'Member is not in the user\'s friend list' }); - } - - // Add the member to the participants list - event.participants.push(memberId); - await event.save(); - - res.json({message: 'Member added successfully', event}); - } catch (error) { - console.error(error); - res.status(500).json({ message: 'Server error' }); - } -}); -*/ - module.exports = router; diff --git a/back-end/test/expenseStatusRoute.test.js b/back-end/test/expenseStatusRoute.test.js new file mode 100644 index 0000000..e69de29 diff --git a/front-end/src/components/Event.js b/front-end/src/components/Event.js index b1dc6ad..9c7da6d 100644 --- a/front-end/src/components/Event.js +++ b/front-end/src/components/Event.js @@ -52,27 +52,35 @@ const Event = (props) => { const isParticipant = expense.splitDetails.find( (detail) => detail.user === userId ); - //let settlement; let userBalance = 0; - - if (isParticipant) { - if (expense.paidBy === userId) { + + if (expense.paidBy === userId) { + // Check if the currentUser is involved in the split + if (isParticipant) { expense.splitDetails.forEach((split) => { if (!split.settlement.status) { userBalance += split.settlement.amount; } }); } else { - //user is not the one who paid, find what the user owe to the person who paid - const user = expense.splitDetails.find( - (split) => split.user === userId - ); - if (user) { - userBalance = user.settlement.status ? 0 : -user.settlement.amount; - } + let totalSettledAmount = 0; + expense.splitDetails.forEach((split) => { + if (split && split.settlement && split.settlement.status) { + totalSettledAmount += split.settlement.amount; + } + }); + userBalance = (expense.totalAmount ? expense.totalAmount : 0) - totalSettledAmount; + } + } else if (isParticipant) { + // If currentUser is not the one who paid, but is involved in the split + const user = expense.splitDetails.find( + (split) => split.user === userId + ); + if (user) { + userBalance = user.settlement.status ? 0 : -user.settlement.amount; } } else { - // User is not a participant, create a settlement object with amount 0 + // User is neither the payer nor a participant in the split userBalance = 0; } @@ -180,12 +188,10 @@ const Event = (props) => {
{item.expense.name}
-
- {item.settlement.toFixed(2) === "0.00" ? ( - Settled - ) : ( - `$${item.settlement.toFixed(2)}` - )} +
+ {item.settlement.toFixed(2) === "0.00" ? + Settled : + `$${item.settlement.toFixed(2)}`}
))} diff --git a/front-end/src/components/Events.jsx b/front-end/src/components/Events.jsx index b85f844..7cb7d59 100644 --- a/front-end/src/components/Events.jsx +++ b/front-end/src/components/Events.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import "../styles/Events.css"; +import '../styles/Events.css'; import Navbar from "./Navbar"; import { Link } from "react-router-dom"; import axios from "axios"; @@ -7,212 +7,181 @@ import AddEvent from "./AddEvent"; import { jwtDecode } from "jwt-decode"; function Events({ isDarkMode }) { - const [eventData, setEventData] = useState([]); - const [addEvent, setaddEvent] = useState(false); - const [settlements, setSettlements] = useState([]); - const [amountOwed, setAmountOwed] = useState(0); - const [amountOwedBy, setAmountOwedBy] = useState(0); - const [searchTerm, setSearchTerm] = useState(""); - //const[showFilter, setShowFilter] = useState(false); - //const[selectedFilter, setSelectedFilter] = useState('all'); - //const[filteredEvents, setFilteredEvents] = useState([]); - - function reformatDate(dateStr) { - const months = [ - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ]; - const date = new Date(dateStr); - - const monthName = months[date.getMonth()]; - const day = date.getDate(); - const year = date.getFullYear(); - - return `${monthName} ${day} ${year}`; - } - - // Toggle the 'body-dark-mode' class on the body element - useEffect(() => { - if (isDarkMode) { - document.body.classList.add("body-dark-mode"); - } else { - document.body.classList.remove("body-dark-mode"); + const[eventData, setEventData] = useState([]) + const[addEvent, setaddEvent] = useState(false) + const [settlements, setSettlements] = useState([]); + const [amountOwed, setAmountOwed] = useState(0); + const [amountOwedBy, setAmountOwedBy] = useState(0); + const [searchTerm, setSearchTerm] = useState(""); + //const[showFilter, setShowFilter] = useState(false); + //const[selectedFilter, setSelectedFilter] = useState('all'); + //const[filteredEvents, setFilteredEvents] = useState([]); + + function reformatDate(dateStr) { + const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + const date = new Date(dateStr); + + const monthName = months[date.getMonth()]; + const day = date.getDate(); + const year = date.getFullYear(); + + return `${monthName} ${day} ${year}`; } - // Clean up function to remove the class when the component unmounts or when dark mode is turned off - return () => { - document.body.classList.remove("body-dark-mode"); - }; - }, [isDarkMode]); - - const token = localStorage.getItem("token"); - const decode = jwtDecode(token); - - useEffect(() => { - //fetch mock data about a user's events list - async function dataFetch() { - try { - if (!decode.id) { - console.error("No current user found in local storage."); - return; + // Toggle the 'body-dark-mode' class on the body element + useEffect(() => { + if (isDarkMode) { + document.body.classList.add('body-dark-mode'); } else { - console.log(decode.id); + document.body.classList.remove('body-dark-mode'); + } + + // Clean up function to remove the class when the component unmounts or when dark mode is turned off + return () => { + document.body.classList.remove('body-dark-mode'); + }; + }, [isDarkMode]); + + const token = localStorage.getItem("token"); + const decode = jwtDecode(token); + + useEffect(()=>{ + //fetch mock data about a user's events list + async function dataFetch(){ + try{ + if (!decode.id) { + console.error("No current user found in local storage."); + return; + } else { + console.log(decode.id); + } + //requesting data from the mock API endpoint + const response = await axios.get(`${process.env.REACT_APP_BACKEND}/events/for/${decode.id}`); + console.log(response) + //return the data + setEventData(response.data) + + }catch(error){ + console.error("There was an error fetching the data:", error); + } } - //requesting data from the mock API endpoint - const response = await axios.get( - `${process.env.REACT_APP_BACKEND}/events/for/${decode.id}` - ); - console.log(response); - //return the data - setEventData(response.data); - } catch (error) { - console.error("There was an error fetching the data:", error); - } + dataFetch(); + },[]); + + useEffect(() => { + console.log(decode.id) + const fetchSettlements = async () => { + try { + const response = await axios.get(`${process.env.REACT_APP_BACKEND}/settlement/from/${decode.id}`); + console.log("Settlements:", response.data); + setSettlements(response.data); + // Process and use the fetched settlements here + } catch (error) { + console.error("Error fetching settlements:", error.response); + } + }; + + fetchSettlements(); + + }, []); + + function calculateAmounts(expenses, currentUserId) { + let amountOwed = 0; // Amount the current user owes to others + let amountOwedBy = 0; // Amount owed to the current user by others + + expenses.forEach(expense => { + if (expense.settleTo._id !== currentUserId && !expense.status) { + // If the current user is not the one who paid and the status is false (unsettled) + amountOwed += expense.amount; + } + if (expense.settleTo._id === currentUserId && !expense.status) { + // If the current user is the one who paid and the status is false (unsettled) + amountOwedBy += expense.amount; + } + }); + + return { amountOwed, amountOwedBy }; } - dataFetch(); - }, []); - - useEffect(() => { - console.log(decode.id); - const fetchSettlements = async () => { - try { - const response = await axios.get( - `${process.env.REACT_APP_BACKEND}/settlement/from/${decode.id}` - ); - console.log("Settlements:", response.data); - setSettlements(response.data); - // Process and use the fetched settlements here - } catch (error) { - console.error("Error fetching settlements:", error.response); - } - }; - - fetchSettlements(); - }, []); - - function calculateAmounts(expenses, currentUserId) { - let amountOwed = 0; // Amount the current user owes to others - let amountOwedBy = 0; // Amount owed to the current user by others - expenses.forEach((expense) => { - if (expense.settleTo._id !== currentUserId && !expense.status) { - // If the current user is not the one who paid and the status is false (unsettled) - amountOwed += expense.amount; - } - if (expense.settleTo._id === currentUserId && !expense.status) { - // If the current user is the one who paid and the status is false (unsettled) - amountOwedBy += expense.amount; - } - }); - - return { amountOwed, amountOwedBy }; - } + useEffect(() => { + if (settlements.length > 0) { + const { amountOwed, amountOwedBy } = calculateAmounts(settlements, decode.id); + setAmountOwed(amountOwed); + setAmountOwedBy(amountOwedBy); + // Now you can use amountOwed and amountOwedBy in your component + console.log(`Amount Owed: ${amountOwed}, Amount Owed By: ${amountOwedBy}`); + } + }, [settlements, decode.id]); - useEffect(() => { - if (settlements.length > 0) { - const { amountOwed, amountOwedBy } = calculateAmounts( - settlements, - decode.id - ); - setAmountOwed(amountOwed); - setAmountOwedBy(amountOwedBy); - // Now you can use amountOwed and amountOwedBy in your component - console.log( - `Amount Owed: ${amountOwed}, Amount Owed By: ${amountOwedBy}` - ); + function EventClick(eventId){ + console.log(`Event ${eventId} was clicked`) } - }, [settlements, decode.id]); - - function EventClick(eventId) { - console.log(`Event ${eventId} was clicked`); - } - return ( -
-

Events

- + return( +
+

Events

+ + +
+ < img src={eventData.avatar} alt="User's Avatar" className="Total_Balance_avatar"> +
+
Total Balance
+
+ { ( +
You owe ${Math.abs(amountOwed).toFixed(2)}
+ )} + {( +
You are owed ${amountOwedBy.toFixed(2)}
+ )} + {amountOwed === 0 && amountOwedBy === 0 && ( +
All Balances are Settled!
+ )} +
+
+
-
- User's Avatar -
-
Total Balance
-
- {
You owe ${Math.abs(amountOwed).toFixed(2)}
} - {
You are owed ${amountOwedBy.toFixed(2)}
} - {amountOwed === 0 && amountOwedBy === 0 && ( -
All Balances are Settled!
- )} -
-
-
+ setSearchTerm(e.target.value)} + className="mt-4 search-input" + /> + +
+
    + {eventData.events && eventData.events.length > 0 ? (eventData.events + .filter(event => event.name.toLowerCase().includes(searchTerm.toLowerCase())) + .map(event => ( +
  • +
    + {reformatDate(event.date)} +
    +
    + {event.name} +
    + + + +
  • + )) + ) : ( +
    Please add your first event!
    + )} +
+
- setSearchTerm(e.target.value)} - className="mt-4 search-input" - /> -
-
    - {eventData.events && eventData.events.length > 0 ? ( - eventData.events - .filter((event) => - event.name.toLowerCase().includes(searchTerm.toLowerCase()) - ) - .map((event) => ( -
  • -
    {reformatDate(event.date)}
    -
    - {event.name} -
    - - - -
  • - )) - ) : ( -
    - Please add your first event! -
    - )} -
-
+ {addEvent && ( + {setaddEvent(false); window.location.reload();}} /> + )} +
+
- {addEvent && ( - { - setaddEvent(false); - window.location.reload(); - }} - /> - )} -
-
- -
-
- ); +
+ ) } -export default Events; +export default Events \ No newline at end of file diff --git a/front-end/src/components/Expense.jsx b/front-end/src/components/Expense.jsx index bf93f90..f938309 100644 --- a/front-end/src/components/Expense.jsx +++ b/front-end/src/components/Expense.jsx @@ -79,26 +79,33 @@ function Expense({ isDarkMode }) { (split) => split.user._id === userId ); - if (!isParticipant) { - // If not a participant, return an empty array or another appropriate value - return []; - } - let filteredExpenses = []; if (expensesData.paidBy._id === userId) { - filteredExpenses = expensesData.splitDetails - .filter((split) => split.user._id !== userId) - .map((split) => ({ ...split, displayName: split.user.username })); - } else { - filteredExpenses = expensesData.splitDetails - .filter( - (split) => split.user._id === userId && expensesData.paidBy !== userId - ) - .map((split) => ({ + if (!isParticipant) { + // If currentUser is not a participant, then the amount will be negative (owed by others) + filteredExpenses = expensesData.splitDetails.map(split => ({ + ...split, + displayName: split.user.username, + amount: -split.settlement.amount + })); + } else { + // If currentUser is also a participant + filteredExpenses = expensesData.splitDetails.filter(split => split.user._id !== userId).map(split => ({ ...split, - displayName: expensesData.paidBy.username, + displayName: split.user.username, + amount: split.settlement.amount })); + } + } else if (isParticipant) { + // When currentUser is not the one who paid but is a participant + filteredExpenses = expensesData.splitDetails.filter(split => split.user._id === userId && expensesData.paidBy !== userId).map(split => ({ + ...split, + displayName: expensesData.paidBy.username, + amount: -split.settlement.amount + })); + }else{ + filteredExpenses = []; } return filteredExpenses; }; @@ -148,13 +155,7 @@ function Expense({ isDarkMode }) {
{split.displayName} 0 - ? "positive" - : "negative" - } - > - {split.settlement.amount.toFixed(2)} + className={parseFloat(split.amount) > 0 ? 'positive' : 'negative'}>{split.amount.toFixed(2)}