Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/df 80 getbudgetbyparams #19

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/AddExpense/AddExpense.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const AddExpense = ({ totalExpenses, setTotalExpenses, setTransactions }) => {
const [amount, setAmount] = useState(0);
const [date, setDate] = useState("");
// Bilbo's UID
const userId = "2"
const userId = 3

const { createExpense } = useCreateExpense();

Expand All @@ -43,7 +43,7 @@ const AddExpense = ({ totalExpenses, setTotalExpenses, setTransactions }) => {
userId,
vendor,
category,
amount: amountCents,
amount: amountCents / 100,
date,
},
});
Expand Down
4 changes: 2 additions & 2 deletions src/components/AddIncome/AddIncome.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const AddIncome = ({ totalIncome, setTotalIncome, setTransactions }) => {
const [date, setDate] = useState("");

// Bilbo's UID
const userId = "2"
const userId = 3

const { createIncome } = useCreateIncome();

Expand All @@ -51,7 +51,7 @@ const AddIncome = ({ totalIncome, setTotalIncome, setTransactions }) => {
id: data.createIncome.id,
vendor: source,
date,
amount: amountCents,
amount: amountCents / 100,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has an issue with being displayed on Transactions as $5.00 if the user actually wrote $500.00.

Could you look into this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not able to address this today...whoever coded the original logic might be able to lend some advice but there is certainly a disconnect between prop passing and the mutation/queries. This referenced line of code ensures the proper value is sent back through the mutation. Not sure if the issue is closer to data manipulation on the query (for transactions) or just on how it's displayed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's my thought. @trpubz your branch still does not appear have the correct useCreateIncome or useCreateExpense mutation that is on main and main seems to be functioning correctly updating state just not posting since the mutation merge yesterday.
Screenshot 2024-02-11 at 12 35 02 PM
Here's how the FE app is handling floats for example with totalIncome:
The getIncomes hook fetches and returns the income as a float
Screenshot 2024-02-11 at 12 36 40 PM
Then to avoid rounding errors, the useEffect handling setting income state coverts the float into a cents integer:
Screenshot 2024-02-11 at 12 43 44 PM
Then whenever any amount needs to be displayed in the App (income, expenses, transaction etc.) it's formatted into a USD currency string:
Screenshot 2024-02-11 at 12 44 27 PM
This means we need to adhere to how floats are handled on main.
A few more thoughts:

  1. Pretty sure this is a bug, it's a matter of this branch not having parts of main like the useMutation fixes from Friday.
  2. We are allowed to fix bugs, but this is a feature branch.
  3. Lets leave this alone, I'm at a loss of words to be honest at this point..

status: "credited",
};

Expand Down
14 changes: 10 additions & 4 deletions src/components/App/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,33 @@ import Dashboard from '../Dashboard/Dashboard'
// import transactionsFixtureData from "../sample-data/TransactionsData.json"
// import incomeData from "../sample-data/IncomeData.json"
// import expensesData from "../sample-data/ExpensesData.json"
// import budgetsData from "../sample-data/BudgetsData.json"
import './App.css';
import { useGetIncomes } from '../apollo-client/queries/getIncomes';
import { useGetExpenses } from '../apollo-client/queries/getExpenses';
import { useGetTransactions } from '../apollo-client/queries/getTransactions';
import { useGetCashFlow } from '../apollo-client/queries/getCashFlow';

import { useGetBudgetsByParams } from '../apollo-client/queries/getBudgetsByParams';

const App = () => {
const [transactions, setTransactions] = useState([]);
const [totalIncome, setTotalIncome] = useState(0);
const [totalExpenses, setTotalExpenses] = useState(0);
const [cashFlow, setCashFlow] = useState(null);
const [budgets, setBudgets] = useState(null);

// Hardcoded user, will pull from getUser endpoint soon
const month = "2024-02";
// const category = "Travel";
const userName = "Powdered Toast Man";
const email = "[email protected]"
localStorage.setItem('email', '[email protected]');
localStorage.setItem('email', email);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line can be removed since the localStorage object is not being retrieved anywhere and email is set in the line above.


const { totalIncomeData } = useGetIncomes(email);
const { totalExpensesData } = useGetExpenses(email);
const { transactionsData } = useGetTransactions(email);
const { cashFlowData } = useGetCashFlow(email);
// const { budgetsData } = useGetBudgetsByParams(month, category, email);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like all other useQuery hooks that retrieve data when the App mounts, the useGetBudgetByParams should follow the DDAU design convention.


useEffect(() => {
if (totalIncomeData) {
Expand All @@ -40,7 +45,8 @@ const App = () => {
if (totalExpensesData) setTotalExpenses(totalExpensesData);
if (transactionsData) setTransactions(transactionsData);
if (cashFlowData) setCashFlow(cashFlowData);
}, [totalIncomeData, totalExpensesData, transactionsData, cashFlowData]);
// if (budgetsData) setBudgets(budgetsData);
}, [totalIncomeData, totalExpensesData, transactionsData, cashFlowData]);

return (
<main className='app'>
Expand Down
43 changes: 25 additions & 18 deletions src/components/Budget/BasicPie.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
import * as React from 'react';
import { PieChart } from '@mui/x-charts/PieChart';

const data = [
// Default data for fallback
const defaultData = [
{ id: 0, value: 25 },
{ id: 1, value: 15 },
{ id: 1, value: 75 },
];

export default function PieActiveArc() {
export default function BasicPie({ data }) {
// Validate incoming data - simple example
const isValidData = data && Array.isArray(data) && data.length > 0 && data.every(d => d.hasOwnProperty('value') && typeof d.value === 'number');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the budgetData is properly drilled to this component, the chained conditionals can likely be reduced to something similar in the BarChart MUI component.


// Use incoming data if valid, otherwise use default
const pieData = isValidData ? data : defaultData;

return (
<PieChart
series={[
{
data,
highlightScope: { faded: 'global', highlighted: 'item' },
faded: { innerRadius: 30, additionalRadius: -30, color: 'gray' },
cx: 140,
innerRadius: 60,
outerRadius: 100,
paddingAngle: 5,
cornerRadius: 5,
},
]}
height={200}
/>
<PieChart
series={[
{
data: pieData,
highlightScope: { faded: 'global', highlighted: 'item' },
faded: { innerRadius: 30, additionalRadius: -30, color: 'gray' },
cx: 140,
innerRadius: 60,
outerRadius: 100,
paddingAngle: 5,
cornerRadius: 5,
},
]}
height={200}
/>
);
}
149 changes: 124 additions & 25 deletions src/components/Budget/Budget.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,148 @@
import React from 'react'
import React, { useState, useRef, useEffect, useMemo } from 'react'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove unused hook.

import './Budget.css'
import BasicPie from './BasicPie'
import './budgetSelectModal.css'
import DropDownIcon from '../../assets/icons/dropdown-icon.svg'
import EllipsePurple from '../../assets/icons/Ellipse-purple.svg'
import EllipseBlue from '../../assets/icons/Ellipse-blue.svg'
import PlusIcon from '../../assets/icons/plus-icon.svg'
import { useGetBudgetsByParams } from "../apollo-client/queries/getBudgetsByParams";
import { useGetBudgetCategories } from "../apollo-client/queries/getBudgetCategories";

const Budget = () => {
return (
const email = "[email protected]";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire blocked dedicated to fetching budgets and budgetCategories should be be properly drilled through from App. Those states should not be set inside this child component.

const { loading: loadingCategories, error: errorCategories, budgetCategoriesData } = useGetBudgetCategories(email);
console.log("Fetched budgetCategoriesData:", budgetCategoriesData);
const categories = loadingCategories || errorCategories ? [] : budgetCategoriesData || [];

const [category, setCategory] = useState();
const [month, setMonth] = useState(getCurrentMonth());
const { loading, error, budgetsData } = useGetBudgetsByParams(month, category, email);
// debugger;
if (error) {
console.error("Error fetching data:", error);
}
console.log("Fetched budgetData:", budgetsData);
const pctRemaining = Math.round(budgetsData?.budgets[0]?.pctRemaining) || 'Loading...';
const amount = budgetsData?.budgets[0]?.amount || 'Loading...';
const amountRemaining = Math.round(budgetsData?.budgets[0]?.amountRemaining) || 'Loading...';

// State to manage dropdown visibility
const [isDropdownVisible, setIsDropdownVisible] = useState(false);
const [dropDownStyle, setDropdownStyle] = useState({}); // State to hold modal's dynamic style
// references
const dropdownRef = useRef(null); // Ref for the dropdown icon to position the modal
const modalRef = useRef(null); // Add a ref for the modal

// Handler functions for updating state
const handleCategoryChange = (selectedCategory) => {
setCategory(selectedCategory);
setIsDropdownVisible(false); // Hide the modal
};
// Handler to toggle dropdown visibility
const toggleDropdownVisibility = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider refactoring this to utilize a BasicSelect MUI component, there is no need to make these kinds of functions.

setIsDropdownVisible(!isDropdownVisible);

if (dropdownRef.current) {
const { bottom, right } = dropdownRef.current.getBoundingClientRect();
const rightOffset = window.innerWidth - right; // Calculate the right offset from the viewport

setDropdownStyle({
position: 'absolute',
top: `${bottom}px`,
right: `${rightOffset}px`,
// Adjustments might be needed based on actual layout and styling
});
}
};
const handleMonthChange = (event) => {
setMonth(event.target.value);
};
// Utility function to get current month, implementation depends on your needs
function getCurrentMonth() {
const date = new Date();
const year = date.getFullYear(); // Get current year
let month = date.getMonth() + 1; // Get current month (0-11, hence +1)
month = month < 10 ? `0${month}` : month; // Ensure month is in two digits
return `${year}-${month}`; // Concatenate to get "YYYY-MM" format
}

useEffect(() => {
if (categories.length > 0) {
setCategory(categories[0]);
}
}, [categories]);

useEffect(() => {
const handleClickOutside = (event) => {
if (modalRef.current && !modalRef.current.contains(event.target) &&
dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsDropdownVisible(false);
}
};

document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The BasicSelect component will remove the need for EventListeners like this. VanillaJS in React is not maintainable/conventional

}, []); // This effect does not depend on `categories`

return (
<aside className='budget'>
<header className='budget-header'>
<h2>Budget</h2>
<img className='budget-dropdown-button' src={DropDownIcon} alt='dropdown icon' />
</header>
<summary className='budget-pie-chart'>
<BasicPie />
</summary>
<section className='budget-percentage-breakdown'>
<div className='percentage-container'>
<div className='percentage-description'>
<img src={EllipsePurple} alt='purple ellipse' />
<p>Daily payment</p>
<h2>{category}</h2>
<div ref={dropdownRef} onClick={toggleDropdownVisibility}>
<img className='budget-dropdown-button' src={DropDownIcon} alt='dropdown icon' />
</div>
<p className='percentage'>25%</p>
</div>
<div className='percentage-container'>
{/* Conditionally render select dropdown */}
{isDropdownVisible && (
<div ref={modalRef} className="select-modal" style={dropDownStyle}>
{/* Dynamically generated modal content with options */}
{categories.length > 0 ? (
categories.map((category, index) => (
<div key={index} onClick={() => handleCategoryChange(category)}>{category}</div>
))
) : (
<div>No categories found.</div> // Or handle the empty state differently
)}
</div>
)}
</header>
<summary className='budget-pie-chart'>
<BasicPie
data={[
{ id: 0, value: (100 - pctRemaining) },
{ id: 1, value: pctRemaining },
]}
/>
</summary>
<section className='budget-percentage-breakdown'>
<div className='percentage-container'>
<div className='percentage-description'>
<img src={EllipsePurple} alt='purple ellipse'/>
<p>Budget Remaining</p>
</div>
<p className='percentage'>{pctRemaining}%</p>
</div>
<div className='percentage-container'>
<div className='percentage-description'>
<img src={EllipseBlue} alt='purple ellipse' />
<p>Hobby</p>
<p>Budget Used</p>
</div>
<p className='percentage'>15%</p>
<p className='percentage'>{100 - pctRemaining}%</p>
</div>
</section>
<section className='budget-details-container'>
<div className='budget-details'>
<h3 className='budget-details-h3'>Set Budget</h3>
<h3 className='budget-details-h3'>Budgeted Amount</h3>
<div className='budget-details-flex'>
<p className='budget-details-amount'>$200</p>
<p className='budget-details-amount'>${amount}</p>
</div>
</div>
<div className='budget-details'>
<h3 className='budget-details-h3'>Remaining Budget</h3>
<div className='budget-details-flex'>
<p className='budget-details-amount'>$200</p>
<p className='budget-details-amount'>${amountRemaining}</p>
</div>
</div>
</section>
Expand All @@ -53,7 +153,6 @@ const Budget = () => {
</div>
</section>
</aside>
)
}

export default Budget
)
}
export default Budget
20 changes: 20 additions & 0 deletions src/components/Budget/budgetSelectModal.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.select-modal {
cursor: pointer;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
background-color: rgba(255, 255, 255, 0.77);
padding: 10px 0;
border-radius: 4px;
z-index: 100; /* Ensure it sits above other content */
}

.select-modal div {
padding: 7px 7px; /* Apply horizontal padding here for content alignment */
margin: 0; /* Remove margin to allow hover to fill from edge to edge */
width: 100%; /* Adjust width to account for padding */
box-sizing: border-box; /* Ensure padding is included in the width calculation */
}

.select-modal div:hover {
background-color: rgba(86, 77, 201, 0.7); /* Updated color for visibility */
/* Ensures hover effect extends to full width of each option */
}
5 changes: 5 additions & 0 deletions src/components/Dashboard/Dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ const Dashboard = ({
setTotalIncome,
totalExpenses,
setTotalExpenses,
incomeTransactions,
setIncomeTransactions,
expensesTransactions,
setExpensesTransactions,
budgets
}) => {


Expand Down
21 changes: 21 additions & 0 deletions src/components/apollo-client/queries/getBudgetCategories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { gql, useQuery } from "@apollo/client";

export const GET_BUDGET_CATEGORIES = gql`
query GetBudgetCategories($email: String!) {
user(email: $email) {
budgetCategories
}
}`

export const useGetBudgetCategories= (email) => {
const { loading, error, data } = useQuery(GET_BUDGET_CATEGORIES, {
variables: { email: email },
fetchPolicy: "no-cache"
});
let budgetCategoriesData = null;
if (!loading && data) {
budgetCategoriesData = data?.user?.budgetCategories || [];
}

return { loading, error, budgetCategoriesData };
};
Loading