Design Changes & Expenses by Category

This commit is contained in:
jveebs 2023-04-16 00:48:59 -04:00
parent 6c55e37f20
commit 68368d1c71
17 changed files with 453 additions and 312 deletions

View file

@ -1,16 +1,82 @@
import React, { useContext, useState } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import { AppContext } from '../context/AppContext';
import { v4 as uuidv4 } from 'uuid';
import './AddExpenseForm.css'
import { getSessionKey } from '../utils/utils.js'
const AddExpenseForm = (props) => {
const { dispatch } = useContext(AppContext);
const [name, setName] = useState('');
const [cost, setCost] = useState('');
const [category, setCategory] = useState('');
const [categoryList, setCategoryList] = useState([]);
useEffect(() => {
try {
fetch('http://127.0.0.1:3030/w/budget', {
method: 'GET',
headers: {
'x-session-key' : getSessionKey(),
}
})
.then(response => response.json())
.then(data => {
if (data.status != 200) {
console.log(data.error);
} else {
setCategoryList(data.categories);
}
})
} catch(error) {
console.error(error);
}
}, [])
const onSubmit = (event) => {
event.preventDefault();
const formData = new FormData();
let currency = "USD"
let whole = 0;
let decimal = 0;
let type = 'expenses';
if (cost.includes(".")) {
whole = parseInt(cost.split(".")[0]);
decimal = parseInt(cost.split(".")[1]);
} else {
whole = parseInt(cost);
}
formData.append('category', category)
formData.append('currency', currency);
formData.append('whole', whole);
formData.append('decimal', decimal);
formData.append('type', type)
try {
fetch(`http://127.0.0.1:3030/w/transactions?whole=${whole}&decimal=${decimal}&currency=${currency}&category=${category}&type=${type}`, {
method: 'POST',
body: formData,
headers: {
'x-session-key' : getSessionKey(),
}
})
.then(response => response.json())
.then(data => {
if (data.status != 200) {
console.log(data.error);
} else {
}
})
} catch(error) {
console.error(error);
}
/*
const expense = {
id: uuidv4(),
name,
@ -21,60 +87,82 @@ const AddExpenseForm = (props) => {
type: 'ADD_EXPENSE',
payload: expense,
});
*/
setName('');
setCost('');
};
const handleAddCategory = () => {
const newCategory = prompt('Enter the new category name:');
if (newCategory) {
if (categoryList.indexOf(newCategory) == -1) {
const newCategories = [...categoryList, newCategory];
setCategoryList(newCategories);
}
setCategory(newCategory);
}
}
return (
<form onSubmit={onSubmit}>
<div className='row alert alert-secondary'>
{/* <div className='col-sm col-lg-4'>
<form htmlFor="name">
<label >Choose a category:</label>
<select>
<option required='required'
type='text'
className='form-control'
id='name'
value={name}
onChange={(event) => setName(event.target.value)}>Rent</option>
</select>
</form>
</div> */}
<h3>Add Expense</h3>
<div className='col-sm col-lg-4'>
<label htmlFor='name'>Name</label>
<input
required='required'
type='text'
className='form-control'
id='name'
value={name}
onChange={(event) => setName(event.target.value)}
></input>
</div>
<div className='col-sm col-lg-4'>
<label htmlFor='cost'>Cost</label>
<input
required='required'
type='number'
className='form-control'
id='cost'
value={cost}
onChange={(event) => setCost(event.target.value)}
/>
<div className='widget'>
<h4>Add Expense</h4>
<form onSubmit={onSubmit}>
<div className='row'>
{/*
<div className='col-md col-lg-4'>
<label htmlFor='name'>Name</label>
<input
required='required'
type='text'
className='form-control'
id='name'
value={name}
onChange={(event) => setName(event.target.value)}
></input>
</div>
*/}
</div>
<div className='row mt-3'>
<div className='col-sm'>
<button type='submit' className='btn btn-primary'>
Save
</button>
<div className='row'>
<div className='col-md col-lg-4'>
<label htmlFor='cost'>Cost</label>
<input
required='required'
type='number'
className='form-control'
id='cost'
min="0.00"
step=".01"
value={cost}
onChange={(event) => setCost(event.target.value)}
/>
</div>
<div className='col-md col-lg-4'>
<label htmlFor='category-select'>Category</label>
<select className="form-select" id='category-select'
value={category}
onChange={(event) => setCategory(event.target.value)}>
{categoryList.map((category) => (
<option key={category} value={category}>
{category}
</option>
))}
</select>
</div>
</div>
</div></div>
</form>
<div className='row mt-3'>
<div className='col-sm'>
<button type='submit' className='btn btn-primary'>
Add Expense
</button>
<button className='btn btn-primary mx-3' onClick={handleAddCategory}>
Add New Category
</button>
</div>
</div>
</form>
</div>
);
};

View file

@ -0,0 +1,84 @@
import { Doughnut } from "react-chartjs-2";
import { Chart as ChartJS, ArcElement, Tooltip, Legend, defaults } from 'chart.js';
import { AppProvider } from "../context/AppContext";
import { useState, useEffect } from 'react'
import { getSessionKey } from '../utils/utils.js'
ChartJS.register(ArcElement, Tooltip, Legend);
export default function CategorizedBudget() {
const [chartData, setChartData] = useState(null);
useEffect(() => {
async function getChartData() {
try {
fetch('http://127.0.0.1:3030/w/budget', {
method: 'GET',
headers: {
'x-session-key' : 'b36efa01-7824-4f61-a274-63131b58d8fe',
}
})
.then(response => response.json())
.then(data => {
if (data.status != 200) {
console.log(data.error);
} else {
const chartData = {
labels: [data.categories.length > 0 ? data.categories : "no expenses"],
datasets: [
{
data: data.expenses_by_category.length > 0
? data.expenses_by_category.map(category => {
return category['whole']
})
: [1],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)',
],
borderColor: [
"black"
],
borderWidth: 1,
},
],
}
setChartData(chartData);
}
})
.catch(error => {
console.log(error);
});
} catch(error) {
console.error(error);
}
}
getChartData();
}, []);
if (!chartData) {
return <p>Loading...</p>
}
return (
<div className="widget">
<h4>Expenses by Category</h4>
<Doughnut className="w-100 h-auto"
data={chartData}
options={{
plugins: {
legend: {
position: 'right'
}
},
responsive: true
}}
/>
</div>
)
}

View file

@ -19,13 +19,14 @@ const ExpenseList = () => {
};
return (
<>
<div className='widget'>
{/* <input
type='text'
className='form-control mb-2 mr-sm-2'
placeholder='Type to search...'
onChange={handleChange}
/> */}
<h4>Expenses</h4>
<ul className='list-group mt-3 mb-3'>
{filteredExpenses.map((expense) => (
<ExpenseItem
@ -35,7 +36,7 @@ const ExpenseList = () => {
/>
))}
</ul>
</>
</div>
);
};

View file

@ -11,7 +11,7 @@ const ExpenseTotal = () => {
}, 0);
return (
<div className='alert alert-secondary p-4'>
<div className='widget p-4'>
<img src={logo}></img>
<span>This Month's Expenses: ${total}</span>
</div>

View file

@ -1,6 +1,5 @@
.bar {
padding: 10px;
margin-bottom: 20px;
margin-bottom: 10px;
}
.logo {

View file

@ -11,6 +11,7 @@ import { async } from "q";
export default function NavBar() {
const [name, setName] = useState(null);
useEffect(() => {
async function fetchName() {
@ -21,11 +22,11 @@ export default function NavBar() {
}, []);
return (
<Toolbar position="sticky" className="bar">
<Toolbar position="sticky" className="navbar bar py-3 px-4">
<Link href="/" className="nav-brand" underline="none">
<img src={logo} className="logo w-100"/>
<Typography
variant="h5"
variant="h4"
className="nav-header">
BudgetBuddy
</Typography>

View file

@ -13,7 +13,7 @@ const RemainingBudget = () => {
const alertType = totalExpenses > budget ? 'alert-danger' : 'alert-success';
return (
<div className={`alert p-4 ${alertType}`}>
<div className={`alert p-4 ${alertType} widget`}>
<img src={logo}></img>
<span>Budget Remaining: ${budget - totalExpenses}</span>
</div>

View file

@ -31,7 +31,7 @@ export default function SideNav() {
<Avatar />
</div>
<div className='sidebar-text'>
<Typography className='sidebar-welcome' variant='h6'>Welcome, {firstName}</Typography>
<Typography className='sidebar-welcome' variant='h6'>Welcome, {firstName}!</Typography>
<Typography variant='subtitle2'> Your Budget Overiew</Typography>
</div>
</div>

View file

@ -20,7 +20,7 @@ const Budget = () => {
};
return (
<div className='alert alert-secondary d-flex align-items-center justify-content-between'>
<div className='widget d-flex align-items-center justify-content-between'>
{isEditing ? (
<EditBudget handleSaveClick={handleSaveClick} budget={budget} />
) : (