Skip to content

Commit

Permalink
Merge pull request #20 from DoughFin/feature/df-98-debug-displays-for…
Browse files Browse the repository at this point in the history
…-data-for-expenses-and-income

Feature/df 98 debug displays for data for expenses and income
  • Loading branch information
shawndcarpenter authored Feb 23, 2024
2 parents a4cfd1e + e122db8 commit ba4343b
Show file tree
Hide file tree
Showing 15 changed files with 349 additions and 57 deletions.
2 changes: 1 addition & 1 deletion 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 Down
2 changes: 1 addition & 1 deletion 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 Down
17 changes: 12 additions & 5 deletions src/components/App/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,48 @@ 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);

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

useEffect(() => {
if (totalIncomeData) {
const totalIncomeCents = Math.round(parseFloat(totalIncomeData) * 100);
const totalIncomeCents = totalIncomeData;
setTotalIncome(totalIncomeCents);
}
if (totalExpensesData) {
const totalExpensesCents = Math.round(parseFloat(totalExpensesData) * 100);
const totalExpensesCents = totalExpensesData;
setTotalExpenses(totalExpensesCents);
}
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');

// 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'
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]";
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 = () => {
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);
};
}, []); // 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 */
}
12 changes: 9 additions & 3 deletions src/components/CashFlow/BarChart.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import * as React from 'react';
import { BarChart } from '@mui/x-charts/BarChart';

const sortMonths = (a, b) => {
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
return months.indexOf(a.month.slice(0, 3)) - months.indexOf(b.month.slice(0, 3));
};

export default function BasicBars({ cashFlow }) {
const xAxisData = cashFlow ? cashFlow.map(item => item.month.slice(0, 3)) : [];
const incomeData = cashFlow ? cashFlow.map(item => item.totalIncome) : [];
const expenseData = cashFlow ? cashFlow.map(item => item.totalExpense) : [];
const sortedCashFlow = cashFlow ? [...cashFlow].sort(sortMonths) : [];

const xAxisData = sortedCashFlow ? sortedCashFlow.map(item => item.month.slice(0, 3)) : [];
const incomeData = sortedCashFlow ? sortedCashFlow.map(item => parseFloat((item.totalIncome / 100).toFixed(2))) : [];
const expenseData = sortedCashFlow ? sortedCashFlow.map(item => parseFloat((item.totalExpense / 100).toFixed(2))) : [];

let series = []

if (cashFlow) {
Expand Down
2 changes: 1 addition & 1 deletion src/components/CashFlow/CashFlow.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const CashFlow = ({ cashFlow }) => {
</div>
<div className='cashflow-header-income'>
<img src={EllipseBlue} alt='purple ellipse' />
<p className='cashflow-header-text'>Expenses</p>
<p className='cashflow-header-text'>Expense</p>
</div>
<BasicSelect />
</header>
Expand Down
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
Loading

0 comments on commit ba4343b

Please sign in to comment.