diff --git a/frontend/src/assets/css/currencyDropdown.css b/frontend/src/assets/css/currencyDropdown.css new file mode 100644 index 0000000..147b95b --- /dev/null +++ b/frontend/src/assets/css/currencyDropdown.css @@ -0,0 +1,41 @@ +@import url("variables.css"); + +.currencyOptionList_open, .currencyOptionList_closed { + width: 100% +} + +.currencyOptionList_open { + transition-duration: 50ms; + height: calc(100vh - 5 * var(--itemHeight) - 3.5 * var(--subSectionPadding) - var(--topBarHeight) - 40px); + padding-top: calc(var(--subSectionPadding) * 2); +} + +.currencyOptionList_closed { + transition-duration: 50ms; + height: 0; + padding-top: 0; + overflow: hidden; +} + +.currencyOption { + width: 100%; +} + +.defaultButton { + position: relative; + z-index: 2; +} + +.defaultContainer { + position: relative; + z-index: 1; + float: left; + margin-left: var(--subSectionPadding); + margin-top: calc(var(--subSectionPadding) * -1 - var(--standardRadius) * 2); + min-height: calc(var(--standardRadius) * 2); +} + +.currencyOption { + width: 100%; + float: left; +} \ No newline at end of file diff --git a/frontend/src/assets/css/dashboard.css b/frontend/src/assets/css/dashboard.css index 14615d2..dd0ac3c 100644 --- a/frontend/src/assets/css/dashboard.css +++ b/frontend/src/assets/css/dashboard.css @@ -1,9 +1,11 @@ @import url("variables.css"); body { - background: linear-gradient(90deg, rgba(42,4,126,1) 0%, rgba(77,4,126,1) 100%); + /* background: linear-gradient(90deg, rgba(42,4,126,1) 0%, rgba(77,4,126,1) 100%); */ + background: var(--color1); overflow: hidden; font-family: "Roboto Mono", monospace; + font-size: var(--standardFontSize); } .topBar { @@ -26,19 +28,21 @@ body { } .sideBar { - background: linear-gradient(180deg, rgba(42,4,126,1) 0%, rgba(77,4,126,1) 100%); width: var(--sideBarWidth); height: calc(100vh - var(--topBarHeight)); float: right; } .scrollableContent { + display: flex; + flex-direction: column; background-color: var(--color2); box-shadow: inset 0px 10px 20px 0px rgba(0,0,0,0.5); border-radius: var(--standardRadius); width: calc(100vw - var(--sideBarWidth)); height: calc(100vh - var(--topBarHeight)); float: left; - padding: calc( 2 * var(--subSectionPadding)); + padding: calc( 4 * var(--subSectionPadding)); + gap: calc( 4 * var(--subSectionPadding)); overflow: scroll; } \ No newline at end of file diff --git a/frontend/src/assets/css/default.css b/frontend/src/assets/css/default.css new file mode 100644 index 0000000..915dcde --- /dev/null +++ b/frontend/src/assets/css/default.css @@ -0,0 +1,106 @@ +@import url("variables.css"); + +/* ---- ---- Default Container ---- ---- */ + +.defaultContainer, .defaultButtonContainer, .defaultDropdownContainer { + width: calc(var(--sideBarWidth) - 2 * var(--subSectionPadding)); + padding: calc(var(--subSectionPadding) * 0.5); + border-radius: var(--standardRadius); + background: var(--noAffordanceBackground); +} + +/* ---- ---- Default Button and Default Dropdown ---- ---- */ + +.defaultButtonContainer, .defaultDropdownContainer { + transition-duration: 100ms; + margin-left: var(--subSectionPadding); + margin-bottom: var(--subSectionPadding); + overflow: hidden; + box-shadow: var(--noAffordanceShadow); + float: left; +} + +.defaultButton { + transition-duration: 100ms; + width: 100%; + height: calc(var(--itemHeight) - var(--subSectionPadding)); + border-radius: calc(var(--standardRadius) * 0.75); + background: var(--physicalAffordanceBackground); + box-shadow: var(--physicalAffordanceShadow); + color: var(--color2); + font-size: var(--standardFontSize); +} + +.defaultButtonContainer:hover, .defaultDropdownContainer:hover { + transition-duration: 100ms; + padding: 0; +} + +.defaultButtonContainer:hover { + box-shadow: var(--noAddordanceShadow_hover); + background: rgb(100, 100, 100); +} + +.defaultButtonContainer:hover .defaultButton, .defaultDropdownContainer:hover .defaultButton { + transition-duration: 100ms; + background: var(--physicalAffordanceBackground_hover); + box-shadow: var(--physicalAffordanceShadow_hover); + border-radius: var(--standardRadius); + font-size: calc(var(--standardFontSize) + 1px); + height: var(--itemHeight); +} + +/* ---- ---- Goal Bar ---- ---- */ + +.goalBarContainer, .goalDivider, .subGoalContainer { + height: calc((var(--topBarHeight) - 3 * var(--subSectionPadding)) * 0.5); +} + +.goalBarContainer { + /* Origonal styling => w-full bg-gray-200 rounded-lg h-6 relative */ + width: 100%; + background: rgb(211, 211, 211); + border-radius: var(--standardRadius); + position: relative; +} + +.goalBarContainer { + padding: 3px; + background: var(--noAffordanceBackground); + box-shadow: var(--noAffordanceShadow); +} + +.goalBar { + /* Origonal styling => bg-gradient-to-r from-blue-400 bg-primary h-full rounded-lg */ + height: 100%; + border-radius: var(--standardRadius); + border-top: 2px; + background: var(--visualAffordanceBackground); + box-shadow: var(--visualAffordanceShadow); +} + +.goalDivider { +/* Origonal styling => absolute border-l border-gray-400 */ + position: absolute; + border: 1px solid rgba(255, 255, 255, 0.3); + top: 0; +} + +.subGoalContainer { +/* Origonal styling => absolute w-full flex justify-between -top-6 */ + position: absolute; + width: 100%; + display: flex; + justify-content: space-between; + top: calc((var(--topBarHeight) - var(--subSectionPadding)) * 0.5); +} + +/* Origonal styling => {`absolute left-1/2 transform -translate-x-1/2 text-xs + ${Number(balance) >= Number(subgoal) ? "text-green-700 border-green-500" : "text-gray-300 border-gray-300"} + border rounded px-1`} */ + +.subGoal, .subGoalCompleted { + text-align: center; + width: 0; + position: relative; +} \ No newline at end of file diff --git a/frontend/src/assets/css/defaultButton.css b/frontend/src/assets/css/defaultButton.css deleted file mode 100644 index d0bdcf1..0000000 --- a/frontend/src/assets/css/defaultButton.css +++ /dev/null @@ -1,22 +0,0 @@ -@import url("variables.css"); - -.defaultButton { - background-color: var(--color2); - width: calc(var(--sideBarWidth) - 2 * var(--subSectionPadding)); - height: var(--itemHeight); - border-radius: var(--standardRadius); - margin-left: var(--subSectionPadding); - margin-right: var(--subSectionPadding); - margin-top: var(--subSectionPadding); - margin-bottom: var(--subSectionPadding); -} - -.updateGoalButton { - background-color: var(--color2); - width: var(--topBarBtnWidth); - height: var(--topBarBtnHeight); - border-radius: var(--standardRadius); - margin-left: var(--subSectionPadding); - margin-right: var(--subSectionPadding); - margin-bottom: var(--subSectionPadding); -} \ No newline at end of file diff --git a/frontend/src/assets/css/savingsTracker.css b/frontend/src/assets/css/savingsTracker.css index 1f931c4..89dd618 100644 --- a/frontend/src/assets/css/savingsTracker.css +++ b/frontend/src/assets/css/savingsTracker.css @@ -2,47 +2,30 @@ .trackerContainer { position: absolute; - width: calc(100vw - 2 * var(--sideBarWidth) - 2 * var(--subSectionPadding)); - right: var(--sideBarWidth); + width: calc(100vw - 2 * var(--sideBarWidth) - 2 * var(--subSectionPadding) - 10vw); + right: calc(var(--sideBarWidth) + 5vw); height: calc(var(--topBarHeight) - 2 * var(--subSectionPadding)); margin-top: var(--subSectionPadding); margin-left: var(--subSectionPadding); float: left; } -.trackerTextContainer, .progressBarContainer { +.balanceCounterContainer { + display: flex; + justify-content: center; position: absolute; - left: 0; + height: calc((var(--topBarHeight) - 3 * var(--subSectionPadding)) * 0.5); top: 0; + left: 0; width: 100%; - height: 100%; -} - -.trackerTextContainer { - display: flex; - justify-content: center; - align-items: center; } -.trackerText { +.balanceCounter { + padding-left: var(--subSectionPadding); + padding-right: var(--subSectionPadding); + width: fit-content; + height: calc(100% - var); + background-color: rgba(255, 255, 255, 0.5); text-align: center; - color: var(--color2); - z-index: 2; -} - -.progressBarContainer { - box-sizing: border-box; - border-width: 2px; - border-color: var(--color1); - border-radius: var(--standardRadius); - overflow: hidden; -} - -.progressBar { - position: absolute; - height: 100%; - left: 0; - top: 0; - background-color: var(--color1); - z-index: 1; + font-size: 0.75rem; } \ No newline at end of file diff --git a/frontend/src/assets/css/transactions.css b/frontend/src/assets/css/transactions.css new file mode 100644 index 0000000..627980a --- /dev/null +++ b/frontend/src/assets/css/transactions.css @@ -0,0 +1,14 @@ +@import url("variables.css"); + +.transactionLog { + display: flex; + justify-content: space-between; + flex-direction: column; + align-items: center; + border: solid 4px var(--color3); + border-radius: var(--standardRadius); +} + +.transaction { + +} \ No newline at end of file diff --git a/frontend/src/assets/css/variables.css b/frontend/src/assets/css/variables.css index c66d8d8..c0c9e45 100644 --- a/frontend/src/assets/css/variables.css +++ b/frontend/src/assets/css/variables.css @@ -1,11 +1,27 @@ :root { - --color1: black; - --color2: white; + --color1: rgb(50, 48, 50); + --color2: rgb(222, 218, 225); + --color3: rgb(9, 158, 106); --topBarHeight: 64px; - --topBarBtnHeight: 32px; - --topBarBtnWidth: 64px; --sideBarWidth: calc(16vw); --subSectionPadding: 8px; --itemHeight: 10vh; - --standardRadius: 10px + --standardRadius: 10px; + --standardFontSize: 16px; + + --color1_gradientInner: rgb(40, 39, 40); + --color1_hover: rgb(54, 52, 54); + --color1_gradientInner_hover: rgb(44, 43, 44); + + --noAffordanceBackground: var(--color2); + --noAffordanceShadow: inset 0px 2px 3px 0px rgba(0,0,0,0.5); + --noAddordanceShadow_hover: 0px 4px 4px 0px rgba(0,0,0,0.25); + + --visualAffordanceBackground: linear-gradient(90deg, rgb(5, 87, 68), rgb(43, 177, 99)); + --visualAffordanceShadow: inset -3px 3px 4px 2px rgba(200, 255, 184, 0.4); + + --physicalAffordanceBackground: linear-gradient(var(--color1), var(--color1_gradientInner)); + --physicalAffordanceShadow: inset 0px 1px 4px 1px rgba(153, 148, 155, 0.3); + --physicalAffordanceBackground_hover: linear-gradient(var(--color1_hover), var(--color1_gradientInner_hover)); + --physicalAffordanceShadow_hover: inset 0px 1.5px 5px 1px rgba(153, 148, 155, 0.2); } \ No newline at end of file diff --git a/frontend/src/components/GenericButton.jsx b/frontend/src/components/GenericButton.jsx deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/components/Transaction.jsx b/frontend/src/components/Transaction.jsx index 9146bea..a4cfe47 100644 --- a/frontend/src/components/Transaction.jsx +++ b/frontend/src/components/Transaction.jsx @@ -9,7 +9,6 @@ const Transaction = ({ transaction }) => { const handleCheckboxChange = (e) => { handleSelect(transaction.id, e.target.checked); - console.log("clicked: ", transaction.id); }; return (
diff --git a/frontend/src/components/default/DefaultButton.jsx b/frontend/src/components/default/DefaultButton.jsx new file mode 100644 index 0000000..8bf544a --- /dev/null +++ b/frontend/src/components/default/DefaultButton.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import '../../assets/css/default.css'; + +export default function DefaultButton(props) { + return ( +
+ +
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/metrics/BalanceGraph.jsx b/frontend/src/components/metrics/BalanceGraph.jsx index 9033601..87b90d8 100644 --- a/frontend/src/components/metrics/BalanceGraph.jsx +++ b/frontend/src/components/metrics/BalanceGraph.jsx @@ -24,11 +24,9 @@ export default function BalanceGraph() { const parseDate = (dateString) => { // Parse the date string const date = new Date(dateString); - console.log(date) // Create a new date object for the start of the year const startOfYear = new Date(date.getFullYear(), 0, 0); - console.log(startOfYear) // Calculate the difference in milliseconds const diff = date - startOfYear; @@ -36,16 +34,12 @@ export default function BalanceGraph() { // Convert milliseconds to days and return the day of the year const oneDay = 1000 * 60 * 60 * 24; const parseDate = Math.floor(diff / oneDay); - console.log(parseDate) return parseDate; } useEffect(() => { - console.log(balance) - console.log(transactions) - let pastBalance = balance const newBalanceData = transactions.reduce((acc, transaction) => { @@ -67,9 +61,6 @@ export default function BalanceGraph() { newBalanceData.push({x: 0, y: pastBalance}) setBalanceData(newBalanceData) - - console.log(newBalanceData) - }, [balance, transactions]) return ( diff --git a/frontend/src/components/metrics/CurrencyDropdown.jsx b/frontend/src/components/metrics/CurrencyDropdown.jsx index a369b3d..e669163 100644 --- a/frontend/src/components/metrics/CurrencyDropdown.jsx +++ b/frontend/src/components/metrics/CurrencyDropdown.jsx @@ -1,79 +1,50 @@ import { FaSortDown } from "react-icons/fa"; import { useState, useContext } from "react"; import TransactionContext from "../../context/TransactionContext"; -import '../../assets/css/defaultButton.css'; +import '../../assets/css/currencyDropdown.css'; -/* - * When adding new currencies to the dropdown: - * Remember to edit the border radius of the last item in the dropdown to match - * Do this by adding "hover:rounded-bl-3xl hover:rounded-br-3xl" in the label class -*/ +export default function CurrencyDropdown(props) { + const [isOpen, setIsOpen] = useState(false); + const [selectedCurrency, setSelectedCurrency] = useState('NZD'); + const { setCurrency } = useContext(TransactionContext); -const CurrencyDropdown = () => { - - const [isOpen, setIsOpen] = useState(false); - const [selectedCurrency, setSelectedCurrency] = useState('NZD'); - const { setCurrency } = useContext(TransactionContext); - - const handleOpen = () => { - setIsOpen(!isOpen); - } - - // grabs the id of the selected currency from the selection and sets it as the selected currency - const handleSelect = (event) => { - const { id } = event.target; - setSelectedCurrency(id); - setIsOpen(false); - setCurrency(id); - }; + const handleOpen = () => { + setIsOpen(!isOpen); + } + function currencyOption (currencyId) { return ( -
- - {isOpen && ( -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - )} -
+ ); -} - -export default CurrencyDropdown; \ No newline at end of file + } + + function selectOption(currencyId) { + setCurrency(currencyId); + setSelectedCurrency(currencyId); + setIsOpen(false); + } + + return ( + <> +
+ +
+
+
+ {currencyOption("NZD")} + {currencyOption("AUD")} + {currencyOption("USD")} + {currencyOption("GBP")} + {currencyOption("HKD")} +
+
+ + ); +} \ No newline at end of file diff --git a/frontend/src/components/metrics/FinancialMetrics.jsx b/frontend/src/components/metrics/FinancialMetrics.jsx index 6b9a098..45b5a5e 100644 --- a/frontend/src/components/metrics/FinancialMetrics.jsx +++ b/frontend/src/components/metrics/FinancialMetrics.jsx @@ -1,6 +1,7 @@ import React, { useState, useContext } from 'react'; import TransactionContext from '../../context/TransactionContext'; -import '../../assets/css/defaultButton.css'; +import '../../assets/css/default.css'; +import DefaultButton from '../default/DefaultButton.jsx'; export default function FinancialMetrics() { const [showMetrics, setShowMetrics] = useState(false); @@ -50,9 +51,9 @@ export default function FinancialMetrics() { return (
- + {/* Conditionally render the modal */} {showMetrics && ( diff --git a/frontend/src/components/metrics/SavingsTracker.jsx b/frontend/src/components/metrics/SavingsTracker.jsx index 8be90fe..095a864 100644 --- a/frontend/src/components/metrics/SavingsTracker.jsx +++ b/frontend/src/components/metrics/SavingsTracker.jsx @@ -1,30 +1,13 @@ import { useState, useEffect, useContext } from "react"; -import axios from "axios"; -import SetGoal from "./SetGoal"; import TransactionContext from "../../context/TransactionContext"; -import { refreshDisplayBalance } from "../../utility/CurrencyUtil"; -import '../../assets/css/savingsTracker.css'; import GoalBar from '../progress-bar/GoalBar'; -import CompletionMsg from "../progress-bar/CompletionMsg"; +import '../../assets/css/savingsTracker.css' export default function SavingsTracker() { - const { currency, balance, goal, setGoal } = useContext(TransactionContext); + const { balance, goal, currencySymbol } = useContext(TransactionContext); const [progress, setProgress] = useState(0); - const [displayBalance, setDisplayBalance] = useState(0); - const [showSetGoal, setShowSetGoal] = useState(false); - const [newGoal, setNewGoal] = useState(0); - // Variables for axios instance - const token = localStorage.getItem("token"); - const axiosInstance = axios.create({ - baseURL: "http://localhost:4000", - headers: { Authorization: `Bearer ${token}` }, - }); - - // Fetch the user's current balance and convert to specified currency when the component mounts - useEffect(() => { refreshDisplayBalance(setDisplayBalance, currency); }, [balance, currency]); - - // Dynamic progress bar whenver the balance or goal changes + /* Dynamic progress bar whenver the balance or goal changes */ useEffect(() => { if (Number(balance) > Number(goal)) { setProgress(100); @@ -39,79 +22,21 @@ export default function SavingsTracker() { const update = (Number(balance) / Number(goal)) * 100; setProgress(update); console.log("Progress: ", update); + console.log("AKA: ", balance, " / ", goal); }, [balance, goal]); - // Updates the user's savings goal via the Set New Goal button - const updateGoal = () => { - //checks if newGoal is null if it is replace it with 0 - //new val updated goal is used because setNewGoal is Aync and might not update in time for sending data to the db - let updatedGoal = newGoal; - if (newGoal === '') { - updatedGoal = 0; - setNewGoal(0) - } - - //saves newGoal to the DB - const token = localStorage.getItem("token"); - const axiosInstance = axios.create({ - baseURL: "http://localhost:4000", - headers: { Authorization: `Bearer ${token}` }, - }); - - axiosInstance - .patch("/user/goal", { - goal: updatedGoal, - }) - .then((response) => { - setGoal(updatedGoal); - setShowSetGoal(false); - }) - .catch((error) => { - console.log(error); - }); - }; - return ( -
- {/*

Current Savings Goal:

-

- ${balance}/${goal} -

*/} - - {/* Use flexbox to align GoalBar and button */} -
- - - - - - {progress >= 0 && ( -
{/* Margin left to separate the button from GoalBar */} - -
- )} +
+ +
+

+ {Number.parseFloat(balance).toFixed(2)} / {Number.parseFloat(goal).toFixed(2)} ({currencySymbol}) +

- - {showSetGoal && ( - setShowSetGoal(false)} - /> - )}
); -} +} \ No newline at end of file diff --git a/frontend/src/components/metrics/SetGoal.jsx b/frontend/src/components/metrics/SetGoal.jsx index 9c2e47b..a5fefeb 100644 --- a/frontend/src/components/metrics/SetGoal.jsx +++ b/frontend/src/components/metrics/SetGoal.jsx @@ -1,7 +1,7 @@ import React from "react"; import { useEffect } from "react"; import "../../App.css"; -import '../../assets/css/defaultButton.css' +import '../../assets/css/default.css' export default function SetGoal({ newGoal, diff --git a/frontend/src/components/metrics/UpdateSavingGoal.jsx b/frontend/src/components/metrics/UpdateSavingGoalButton.jsx similarity index 50% rename from frontend/src/components/metrics/UpdateSavingGoal.jsx rename to frontend/src/components/metrics/UpdateSavingGoalButton.jsx index 08e8cc5..70b6e61 100644 --- a/frontend/src/components/metrics/UpdateSavingGoal.jsx +++ b/frontend/src/components/metrics/UpdateSavingGoalButton.jsx @@ -3,9 +3,14 @@ import axios from "axios"; import SetGoal from "./SetGoal"; import TransactionContext from "../../context/TransactionContext"; import { refreshDisplayGoal } from "../../utility/CurrencyUtil"; +import getAxiosInstance from "../../utility/AxiosUtil"; +import DefaultButton from "../default/DefaultButton"; -export default function UpdateSavingGoal() { - const { currency, goal, setGoal } = useContext(TransactionContext); +export default function UpdateSavingGoalButton() { + const { currency, setGoal } = useContext(TransactionContext); + const [newGoal, setNewGoal] = useState(0); + const [showSetGoal, setShowSetGoal] = useState(false); + const [progress, setProgress] = useState(0); // Updates the user's savings goal via the Set New Goal button const updateGoal = () => { @@ -18,11 +23,7 @@ export default function UpdateSavingGoal() { } //saves newGoal to the DB - const token = localStorage.getItem("token"); - const axiosInstance = axios.create({ - baseURL: "http://localhost:4000", - headers: { Authorization: `Bearer ${token}` }, - }); + const axiosInstance = getAxiosInstance(); axiosInstance .patch("/user/goal", { @@ -36,4 +37,25 @@ export default function UpdateSavingGoal() { console.log(error); }); }; + + return ( +
+ { + setShowSetGoal(true); + }} + > + Update Savings Goal + + {showSetGoal && ( + setShowSetGoal(false)} + /> + )} +
+ ); } \ No newline at end of file diff --git a/frontend/src/components/progress-bar/GoalBar.jsx b/frontend/src/components/progress-bar/GoalBar.jsx index cc9d58c..f1d7eed 100644 --- a/frontend/src/components/progress-bar/GoalBar.jsx +++ b/frontend/src/components/progress-bar/GoalBar.jsx @@ -1,58 +1,59 @@ -import React from 'react'; +import {React, useContext} from 'react'; import PropTypes from 'prop-types'; +import '../../assets/css/default.css' +import '../../assets/css/variables.css' +import TransactionContext from "../../context/TransactionContext"; function GoalBar({ progress, balance, goal, subgoals }) { const hasReachedGoal = Number(balance) >= Number(goal); const filteredSubgoals = subgoals.slice(1, -1); const subgoalPositions = filteredSubgoals.map(subgoal => (Number(subgoal) / Number(goal)) * 100); + const { currencySymbol } = useContext(TransactionContext); return ( -
+
{/* Progress Bar */} -
+
-
- {/* need to update the dollar sign with currency change */} - {/* need to updaate balance with currency change */} - ${balance}/${goal} -
-
- + className="goalBar" + style={{ width: `${progress}%` }} + >
{/* Grey Lines for Subgoals */} -
+
{subgoalPositions.map((position, index) => (
))}
{/* Labels for Subgoals */} -
+
{subgoals.map((subgoal, index) => (
= Number(subgoal) ? "text-green-700 border-green-500" : "text-red-500 border-red-500"} + ${Number(balance) >= Number(subgoal) ? "text-green-700 border-green-500" : "text-gray-300 border-gray-300"} border rounded px-1`} > - ${subgoal} + {currencySymbol}{Number.parseFloat(subgoal).toFixed(2)}
))}
+ {/* Completion Message */} + {hasReachedGoal && ( +
+

🎉 Congratulations! 🎉

+

You have reached your goal of ${goal}.

+
+ )} +
); } diff --git a/frontend/src/components/transactions/AddTransactionButton.jsx b/frontend/src/components/transactions/AddTransactionButton.jsx index d6982d7..43fa639 100644 --- a/frontend/src/components/transactions/AddTransactionButton.jsx +++ b/frontend/src/components/transactions/AddTransactionButton.jsx @@ -1,12 +1,14 @@ import { useState, useContext } from "react"; -import axios from "axios"; +import getAxiosInstance from "../../utility/AxiosUtil.jsx"; +import { refreshDisplayBalance, convertCurrency } from "../../utility/CurrencyUtil.jsx"; import TransactionForm from "./TransactionForm"; import TransactionContext from "../../context/TransactionContext"; -import '../../assets/css/defaultButton.css'; +import '../../assets/css/default.css'; +import DefaultButton from '../default/DefaultButton.jsx'; export default function AddTransactionButton() { const [showForm, setShowForm] = useState(false); - const { balance, setBalance } = useContext(TransactionContext); + const { balance, setBalance, currency } = useContext(TransactionContext); const handleFormSubmit = async ({ title, @@ -14,50 +16,31 @@ export default function AddTransactionButton() { description, transactionType, }) => { - const token = localStorage.getItem("token"); - - if (!token) { - console.error("User is not authenticated."); - return; - } - - const axiosInstance = axios.create({ - baseURL: "http://localhost:4000", - headers: { Authorization: `Bearer ${token}` }, - }); - - console.log("amount: ", amount); + amount = await convertCurrency("NZD", currency, amount); + console.log("That is ", amount, " in NZD!"); try { // Post request to create the transaction - const response = await axiosInstance.post("/transaction", { + const response = await getAxiosInstance().post("/transaction", { title, amount, description, }); - console.log("Transaction created successfully.", response.data); - - // Update the balance after the transaction is created - // PATCH request to update the balance const updateBalance = Number(balance) + Number(amount); // use Number() for strings - await axiosInstance.patch("/user/balance", { balance: updateBalance }); - console.log("Balance updated successfully."); - setBalance(balance + amount); // Update the balance state in the context + await getAxiosInstance().patch("/user/balance", { balance: updateBalance }); + refreshDisplayBalance(setBalance, currency); // Update the balance state in the context } catch (error) { console.error("Error occurred:", error); } finally { - console.log("Im at finally"); setShowForm(false); } }; return ( - <> - + <> + setShowForm(true)}>Add Transaction {showForm && (
); -} +} \ No newline at end of file diff --git a/frontend/src/components/transactions/DeleteTransactionButton.jsx b/frontend/src/components/transactions/DeleteTransactionButton.jsx index 865a8f3..3c7d935 100644 --- a/frontend/src/components/transactions/DeleteTransactionButton.jsx +++ b/frontend/src/components/transactions/DeleteTransactionButton.jsx @@ -1,11 +1,13 @@ import { useContext, useState, useRef } from "react"; +import getAxiosInstance from "../../utility/AxiosUtil.jsx"; import TransactionContext from "../../context/TransactionContext"; -import axios from "axios"; -import '../../assets/css/defaultButton.css'; +import '../../assets/css/default.css'; +import DefaultButton from '../default/DefaultButton.jsx'; +import { refreshDisplayBalance } from "../../utility/CurrencyUtil.jsx"; export default function DeleteTransactionButton() { - const { selectedTransactions, setSelectedTransactions, requestUiUpdate } = + const { selectedTransactions, setSelectedTransactions, requestUiUpdate, setBalance, currency } = useContext(TransactionContext); const [disableClick, setDisableClick] = useState(false); @@ -16,8 +18,6 @@ export default function DeleteTransactionButton() { disableClickSync.current = true; setDisableClick(true); - console.log("Transactions to delete:", selectedTransactions); - const token = localStorage.getItem("token"); if (!token) { @@ -25,10 +25,7 @@ export default function DeleteTransactionButton() { return; } - const axiosInstance = axios.create({ - baseURL: "http://localhost:4000", - headers: { Authorization: `Bearer ${token}` }, - }); + const axiosInstance = getAxiosInstance(); try { // Assuming you have an API function to delete a transaction by ID @@ -38,8 +35,8 @@ export default function DeleteTransactionButton() { .then(requestUiUpdate()) ) ); - console.log("Transactions deleted successfully."); setSelectedTransactions([]); // Clear the selected transactions + refreshDisplayBalance(setBalance, currency); } catch (error) { console.error("Failed to delete transactions", error); } finally { @@ -48,12 +45,8 @@ export default function DeleteTransactionButton() { } }; return ( - + ); } diff --git a/frontend/src/components/transactions/Transaction.jsx b/frontend/src/components/transactions/Transaction.jsx index 8191ead..f38b7cd 100644 --- a/frontend/src/components/transactions/Transaction.jsx +++ b/frontend/src/components/transactions/Transaction.jsx @@ -8,7 +8,7 @@ const Transaction = ({ transaction }) => { const [isAmountNegative, setIsAmountNegative] = useState(false); const [convertedAmount, setConvertedAmount] = useState(transaction.amount); const [showDetails, setShowDetails] = useState(false); - const { currency, handleSelect } = + const { currency, handleSelect, currencySymbol } = useContext(TransactionContext); // useEffect to check if each transaction is negative and then convert the currency @@ -27,7 +27,6 @@ const Transaction = ({ transaction }) => { const handleCheckboxChange = (e) => { handleSelect(transaction.id, e.target.checked); - console.log("clicked: ", transaction.id); }; return ( @@ -41,7 +40,7 @@ const Transaction = ({ transaction }) => { onClick={() => setShowDetails(true)}>

- {isAmountNegative ? convertedAmount : `+${convertedAmount}`}:{" "} + {isAmountNegative ? `-${currencySymbol}${convertedAmount.slice(1)}` : `+${currencySymbol}${convertedAmount}`}:{" "} {transaction.title}

diff --git a/frontend/src/components/transactions/TransactionLog.jsx b/frontend/src/components/transactions/TransactionLog.jsx index 75cc5b2..704fdfd 100644 --- a/frontend/src/components/transactions/TransactionLog.jsx +++ b/frontend/src/components/transactions/TransactionLog.jsx @@ -4,6 +4,8 @@ import { IoIosArrowForward, IoIosArrowBack } from "react-icons/io"; import TransactionContext from "../../context/TransactionContext"; import { useContext, useState, useEffect } from "react"; +import '../../assets/css/transactions.css' + import { LoadingSpinner } from "../LoadingSpinner"; export default function TransactionList() { @@ -99,7 +101,7 @@ export default function TransactionList() { Last year
-
+
{loading ? ( diff --git a/frontend/src/context/TransactionContextProvider.jsx b/frontend/src/context/TransactionContextProvider.jsx index 9fb7848..54ef925 100644 --- a/frontend/src/context/TransactionContextProvider.jsx +++ b/frontend/src/context/TransactionContextProvider.jsx @@ -5,7 +5,7 @@ import { filterPastYearTransactions, } from "../utility/transactionFilters.js"; import { useEffect, useState } from "react"; -import { refreshDisplayGoal } from "../utility/CurrencyUtil"; +import { refreshDisplayGoal, refreshDisplayBalance } from "../utility/CurrencyUtil"; import axios from "axios"; // Context provider for transactions. Allows for the sharing of transaction data between components @@ -18,6 +18,7 @@ export function TransactionContextProvider({ children }) { const [filter, setFilter] = useState(""); const [balance, setBalance] = useState(0); const [goal, setGoal] = useState(0); + const [currencySymbol, setCurrencySymbol] = useState("$"); const [uiUpdateRequest, setUiUpdateRequest] = useState(false); const [loading, setLoading] = useState(true); @@ -90,6 +91,18 @@ export function TransactionContextProvider({ children }) { setUiUpdateRequest(true); }; + useEffect(() => { + const currencySymbols = { + "NZD": "NZ$", + "AUD": "AU$", + "USD": "US$", + "GBP": "£", + "HKD": "HK$" + } + + setCurrencySymbol(currencySymbols[currency]); + }, currency) + //functions to filter transactions const filterYear = () => { if (filter === "year") { @@ -127,6 +140,7 @@ export function TransactionContextProvider({ children }) { // all values and functions that can be accessed when consuming this context provider const contextValue = { currency, // the currency to convert to i.e NZD, USD, EUR + currencySymbol, transactions, // the transactions to display (after filtering) allTransactions, // all transactions of the user selectedTransactions, // the transactions selected by the user for deletion diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index 36c16d2..49c029b 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -1,3 +1,4 @@ + import { useContext } from "react"; import Banner from "../components/Banner"; import BalanceGraph from "../components/metrics/BalanceGraph" @@ -7,8 +8,7 @@ import SavingsTracker from "../components/metrics/SavingsTracker"; import TransactionLog from "../components/transactions/TransactionLog"; import AddTransactionButton from "../components/transactions/AddTransactionButton"; import DeleteTransactionButton from "../components/transactions/DeleteTransactionButton"; -import TransactionContext from "../context/TransactionContext"; -import GoalBar from "../components/progress-bar/GoalBar"; +import UpdateSavingGoalButton from "../components/metrics/UpdateSavingGoalButton"; import FintrackLogo from "../assets/images/FintrackLogo.png"; import FinancialMetrics from "../components/metrics/FinancialMetrics"; @@ -25,61 +25,27 @@ import LuckyAdviser from "../components/gemini/LuckyAdviser"; export default function Dashboard() { - const { goal, balance } = useContext(TransactionContext); // Access goal and balance from context - return ( <> -
+
- logo + logo
- +
-
+
-