parent
0ec74296b3
commit
245b7752ae
@ -0,0 +1,65 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { AppContext } from '../context/AppContext';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const AddExpenseForm = (props) => {
|
||||
const { dispatch } = useContext(AppContext);
|
||||
|
||||
const [name, setName] = useState('');
|
||||
const [cost, setCost] = useState('');
|
||||
|
||||
const onSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
const expense = {
|
||||
id: uuidv4(),
|
||||
name,
|
||||
cost: parseInt(cost),
|
||||
};
|
||||
|
||||
dispatch({
|
||||
type: 'ADD_EXPENSE',
|
||||
payload: expense,
|
||||
});
|
||||
|
||||
setName('');
|
||||
setCost('');
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={onSubmit}>
|
||||
<div class='row'>
|
||||
<div class='col-sm col-lg-4'>
|
||||
<label for='name'>Name</label>
|
||||
<input
|
||||
required='required'
|
||||
type='text'
|
||||
class='form-control'
|
||||
id='name'
|
||||
value={name}
|
||||
onChange={(event) => setName(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div class='col-sm col-lg-4'>
|
||||
<label for='cost'>Cost</label>
|
||||
<input
|
||||
required='required'
|
||||
type='number'
|
||||
class='form-control'
|
||||
id='cost'
|
||||
value={cost}
|
||||
onChange={(event) => setCost(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class='row mt-3'>
|
||||
<div class='col-sm'>
|
||||
<button type='submit' class='btn btn-primary'>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddExpenseForm;
|
||||
@ -1,58 +0,0 @@
|
||||
.add_bill_widget {
|
||||
background-color: rgb(223, 245, 229);
|
||||
max-height: 50%;
|
||||
}
|
||||
.widget_header {
|
||||
margin: 0px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
input {
|
||||
max-width: 50%;
|
||||
}
|
||||
button {
|
||||
margin-left: 20%;
|
||||
margin-right: auto;
|
||||
}
|
||||
.buttons{
|
||||
}
|
||||
/* Style The Dropdown Button */
|
||||
.dropbtn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* The container <div> - needed to position the dropdown content */
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Dropdown Content (Hidden by Default) */
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #f9f9f9;
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Links inside the dropdown */
|
||||
.dropdown-content a {
|
||||
color: black;
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Change color of dropdown links on hover */
|
||||
.dropdown-content a:hover {background-color: #f1f1f1}
|
||||
|
||||
/* Show the dropdown menu on hover */
|
||||
.dropdown:hover .dropdown-content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Change the background color of the dropdown button when the dropdown content is shown */
|
||||
.dropdown:hover .dropbtn {
|
||||
background-color: #3e8e41;
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
import React from 'react'
|
||||
import './AddNewBill.css'
|
||||
|
||||
export default function AddNewBill() {
|
||||
return (
|
||||
<>
|
||||
<div class="add_bill_widget">
|
||||
<h4 class="widget_header">Add New Bill</h4>
|
||||
<input type="number"></input>
|
||||
<div class="buttons">
|
||||
<div class="dropdown">
|
||||
<button class="dropbtn">Categories</button>
|
||||
<div class="dropdown-content">
|
||||
<p>Bills</p>
|
||||
<p>Groceries</p>
|
||||
<p>Rent</p>
|
||||
<p>Leisure</p>
|
||||
<p>Gas</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<button>Add Bill</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
.current_balance_widget {
|
||||
background-color: rgb(223, 245, 229);
|
||||
}
|
||||
.widget_header {
|
||||
}
|
||||
|
||||
img {
|
||||
float: left;
|
||||
height: 40px;
|
||||
margin-left: 5%;
|
||||
}
|
||||
|
||||
.current_balance_widget h1 {
|
||||
float: left;
|
||||
margin: 0;
|
||||
margin-left: 10%;
|
||||
}
|
||||
.current_balance_widget h6 {
|
||||
color: green;
|
||||
float: left;
|
||||
margin: 0;
|
||||
margin-left: 33%;
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
import React from 'react'
|
||||
import './CurrentBalance.css'
|
||||
import logo from '../assets/widget_logos/current_balance_logo.png';
|
||||
|
||||
export default function CurrentBalance() {
|
||||
return (
|
||||
<>
|
||||
<div class="current_balance_widget">
|
||||
<h4 class="widget_header">Current Balance</h4>
|
||||
<img src={logo} />
|
||||
<h1>$1238.56</h1>
|
||||
<h6>+ $179.97 (17%) last month</h6>
|
||||
</div>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const EditBudget = (props) => {
|
||||
const [value, setValue] = useState(props.budget);
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
required='required'
|
||||
type='number'
|
||||
class='form-control mr-3'
|
||||
id='name'
|
||||
value={value}
|
||||
onChange={(event) => setValue(event.target.value)}
|
||||
/>
|
||||
<button
|
||||
type='button'
|
||||
class='btn btn-primary'
|
||||
onClick={() => props.handleSaveClick(value)}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditBudget;
|
||||
@ -0,0 +1,24 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { AppContext } from '../context/AppContext';
|
||||
|
||||
const ExpenseItem = (props) => {
|
||||
const { dispatch } = useContext(AppContext);
|
||||
|
||||
const handleDeleteExpense = () => {
|
||||
dispatch({
|
||||
type: 'DELETE_EXPENSE',
|
||||
payload: props.id,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<li class='list-group-item d-flex justify-content-between align-items-center'>
|
||||
{props.name}
|
||||
<div>
|
||||
<span class='text-primary badge badge-primary badge-pill mr-3'>${props.cost}</span>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExpenseItem;
|
||||
@ -0,0 +1,42 @@
|
||||
import React, { useContext, useState, useEffect } from 'react';
|
||||
import ExpenseItem from './ExpenseItem';
|
||||
import { AppContext } from '../context/AppContext';
|
||||
|
||||
const ExpenseList = () => {
|
||||
const { expenses } = useContext(AppContext);
|
||||
|
||||
const [filteredExpenses, setfilteredExpenses] = useState(expenses || []);
|
||||
|
||||
useEffect(() => {
|
||||
setfilteredExpenses(expenses);
|
||||
}, [expenses]);
|
||||
|
||||
const handleChange = (event) => {
|
||||
const searchResults = expenses.filter((filteredExpense) =>
|
||||
filteredExpense.name.toLowerCase().includes(event.target.value)
|
||||
);
|
||||
setfilteredExpenses(searchResults);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type='text'
|
||||
class='form-control mb-2 mr-sm-2'
|
||||
placeholder='Type to search...'
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<ul class='list-group mt-3 mb-3'>
|
||||
{filteredExpenses.map((expense) => (
|
||||
<ExpenseItem
|
||||
id={expense.id}
|
||||
name={expense.name}
|
||||
cost={expense.cost}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExpenseList;
|
||||
@ -0,0 +1,18 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { AppContext } from '../context/AppContext';
|
||||
|
||||
const ExpenseTotal = () => {
|
||||
const { expenses } = useContext(AppContext);
|
||||
|
||||
const total = expenses.reduce((total, item) => {
|
||||
return (total += item.cost);
|
||||
}, 0);
|
||||
|
||||
return (
|
||||
<div class='alert alert-secondary p-4'>
|
||||
<span>This Month's Expenses: ${total}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExpenseTotal;
|
||||
@ -1,20 +0,0 @@
|
||||
.expenses_widget {
|
||||
background-color: rgb(223, 245, 229);
|
||||
}
|
||||
.widget_header {
|
||||
}
|
||||
|
||||
img {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.expenses_widget h1 {
|
||||
float: left;
|
||||
margin: 0;
|
||||
margin-left: 10%;
|
||||
}
|
||||
.expenses_widget h6 {
|
||||
color: red;
|
||||
float: right;
|
||||
margin: 0;
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
import React from 'react'
|
||||
import './Expenses.css'
|
||||
import logo from '../assets/widget_logos/expenses_logo.png';
|
||||
|
||||
export default function Expenses() {
|
||||
return (
|
||||
<>
|
||||
<div class="expenses_widget">
|
||||
<h4 class="widget_header">This Month's Expenses</h4>
|
||||
<img src={logo} />
|
||||
<h1>$456.78</h1>
|
||||
<h6>+ $359.97 (26%) last month (17%)</h6>
|
||||
</div>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
.income_widget {
|
||||
background-color: rgb(223, 245, 229);
|
||||
}
|
||||
.widget_header {
|
||||
}
|
||||
|
||||
img {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.income_widget h1 {
|
||||
float: left;
|
||||
margin: 0;
|
||||
margin-left: 10%;
|
||||
}
|
||||
.income_widget h6 {
|
||||
color: green;
|
||||
margin: 0;
|
||||
float: right;
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
import React from 'react'
|
||||
import './Income.css'
|
||||
import logo from '../assets/widget_logos/income_logo.png';
|
||||
|
||||
export default function Income() {
|
||||
return (
|
||||
<>
|
||||
<div class="income_widget">
|
||||
<h4 class="widget_header">This Month's Income</h4>
|
||||
<img src={logo} />
|
||||
<h1>$1,525.00</h1>
|
||||
<h6>+ $1,037.51 (42%) last month</h6>
|
||||
</div>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
.transactions_widget {
|
||||
background-color: rgb(223, 245, 229);
|
||||
}
|
||||
|
||||
.transaction_table {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-rows: 30px 30px 30px 30px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.transaction_table p {
|
||||
margin: 0;
|
||||
float: left;
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
import React from 'react'
|
||||
import './RecentTransaction.css'
|
||||
|
||||
export default function RecentTransactions() {
|
||||
return (
|
||||
<>
|
||||
<div class="transactions_widget">
|
||||
<h4 class="widget_header">Recent Transactions</h4>
|
||||
<div class="transaction_table">
|
||||
<p>Bill</p>
|
||||
<p>$35.00</p>
|
||||
<p>2/15/23</p>
|
||||
<p>Food</p>
|
||||
<p>$29.95</p>
|
||||
<p>2/11/23</p>
|
||||
<p>Wage</p>
|
||||
<p>$723.40</p>
|
||||
<p>2/10/23</p>
|
||||
<p>Pet</p>
|
||||
<p>$56.98</p>
|
||||
<p>2/5/23</p>
|
||||
<p>Leisure</p>
|
||||
<p>$60.04</p>
|
||||
<p>2/03/23</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { AppContext } from '../context/AppContext';
|
||||
|
||||
const RemainingBudget = () => {
|
||||
const { expenses, budget } = useContext(AppContext);
|
||||
|
||||
const totalExpenses = expenses.reduce((total, item) => {
|
||||
return (total += item.cost);
|
||||
}, 0);
|
||||
|
||||
const alertType = totalExpenses > budget ? 'alert-danger' : 'alert-success';
|
||||
|
||||
return (
|
||||
<div class={`alert p-4 ${alertType}`}>
|
||||
<span>Budget Remaining: ${budget - totalExpenses}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RemainingBudget;
|
||||
@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
const ViewBudget = (props) => {
|
||||
return (
|
||||
<>
|
||||
<span>Current Balance: ${props.budget}</span>
|
||||
<button type='button' class='btn btn-primary' onClick={props.handleEditClick}>
|
||||
Edit
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewBudget;
|
||||
@ -1,23 +0,0 @@
|
||||
.budget_widget {
|
||||
background-color: rgb(223, 245, 229);
|
||||
}
|
||||
.widget_header {
|
||||
}
|
||||
|
||||
img {
|
||||
float: left;
|
||||
height: 40px;
|
||||
margin-left: 5%;
|
||||
}
|
||||
|
||||
.budget_widget h1 {
|
||||
float: left;
|
||||
margin: 0;
|
||||
margin-left: 10%;
|
||||
}
|
||||
.budget_widget h6 {
|
||||
color: green;
|
||||
float: left;
|
||||
margin: 0;
|
||||
margin-left: 37%;
|
||||
}
|
||||
@ -1,17 +1,33 @@
|
||||
import React from 'react'
|
||||
import './budget.css'
|
||||
import logo from '../assets/widget_logos/budget_logo.png';
|
||||
import React, { useState, useContext } from 'react';
|
||||
import ViewBudget from './ViewBudget';
|
||||
import EditBudget from './EditBudget';
|
||||
import { AppContext } from '../context/AppContext';
|
||||
|
||||
const Budget = () => {
|
||||
const { budget, dispatch } = useContext(AppContext);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
|
||||
const handleEditClick = () => {
|
||||
setIsEditing(true);
|
||||
};
|
||||
|
||||
const handleSaveClick = (value) => {
|
||||
dispatch({
|
||||
type: 'SET_BUDGET',
|
||||
payload: value,
|
||||
});
|
||||
setIsEditing(false);
|
||||
};
|
||||
|
||||
export default function Budget() {
|
||||
return (
|
||||
<>
|
||||
<div class="budget_widget">
|
||||
<h4 class="widget_header">Budget Remaining</h4>
|
||||
<img src={logo} />
|
||||
<h1>$456.78</h1>
|
||||
<h6>Still on budget</h6>
|
||||
<div class='alert alert-secondary p-3 d-flex align-items-center justify-content-between'>
|
||||
{isEditing ? (
|
||||
<EditBudget handleSaveClick={handleSaveClick} budget={budget} />
|
||||
) : (
|
||||
<ViewBudget handleEditClick={handleEditClick} budget={budget} />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
)
|
||||
}
|
||||
export default Budget;
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
import React, { createContext, useReducer } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export const AppReducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'ADD_EXPENSE':
|
||||
return {
|
||||
...state,
|
||||
expenses: [...state.expenses, action.payload],
|
||||
};
|
||||
case 'DELETE_EXPENSE':
|
||||
return {
|
||||
...state,
|
||||
expenses: state.expenses.filter(
|
||||
(expense) => expense.id !== action.payload
|
||||
),
|
||||
};
|
||||
case 'SET_BUDGET':
|
||||
return {
|
||||
...state,
|
||||
budget: action.payload,
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const initialState = {
|
||||
budget: 2000,
|
||||
expenses: [
|
||||
{ id: uuidv4(), name: 'Groceries', cost: 50 },
|
||||
{ id: uuidv4(), name: 'Leisure', cost: 300 },
|
||||
{ id: uuidv4(), name: 'Pet', cost: 70 },
|
||||
{ id: uuidv4(), name: 'Gas', cost: 40 },
|
||||
{ id: uuidv4(), name: 'Rent', cost: 500 },
|
||||
],
|
||||
};
|
||||
|
||||
export const AppContext = createContext();
|
||||
|
||||
export const AppProvider = (props) => {
|
||||
const [state, dispatch] = useReducer(AppReducer, initialState);
|
||||
|
||||
return (
|
||||
<AppContext.Provider
|
||||
value={{
|
||||
expenses: state.expenses,
|
||||
budget: state.budget,
|
||||
dispatch,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</AppContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -1,19 +1,42 @@
|
||||
import AddNewBill from '../components/AddNewBill'
|
||||
import CurrentBalance from '../components/CurrentBalance'
|
||||
import Expenses from '../components/Expenses'
|
||||
import Income from '../components/Income'
|
||||
import Budget from '../components/budget'
|
||||
import RecentTransactions from '../components/RecentTransactions'
|
||||
import React from 'react';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
|
||||
import { AppProvider } from '../context/AppContext';
|
||||
import Budget from '../components/Budget';
|
||||
import ExpenseTotal from '../components/ExpenseTotal';
|
||||
import ExpenseList from '../components/ExpenseList';
|
||||
import AddExpenseForm from '../components/AddExpenseForm';
|
||||
import RemainingBudget from '../components/Remaining';
|
||||
|
||||
export default function Dashboard() {
|
||||
return (
|
||||
<>
|
||||
<CurrentBalance />
|
||||
<AppProvider>
|
||||
<div className='container'>
|
||||
<h1 className='mt-3'>My Budget Planner</h1>
|
||||
<div className='row mt-3'>
|
||||
<div className='col-sm'>
|
||||
<Budget />
|
||||
<Expenses />
|
||||
<Income />
|
||||
<RecentTransactions />
|
||||
<AddNewBill />
|
||||
</>
|
||||
)
|
||||
</div>
|
||||
<div className='col-sm'>
|
||||
<RemainingBudget />
|
||||
</div>
|
||||
<div className='col-sm'>
|
||||
<ExpenseTotal />
|
||||
</div>
|
||||
</div>
|
||||
<h3 className='mt-3'>Expenses</h3>
|
||||
<div className='row '>
|
||||
<div className='col-sm'>
|
||||
<ExpenseList />
|
||||
</div>
|
||||
</div>
|
||||
<h3 className='mt-3'>Add Expense</h3>
|
||||
<div className='row mt-3'>
|
||||
<div className='col-sm'>
|
||||
<AddExpenseForm />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AppProvider>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in new issue