├── README.md └── client └── src ├── App.css ├── App.js ├── components ├── AddTransaction.js ├── Balance.js ├── Header.js ├── IncomeExpenses.js └── TransactionList.js └── context ├── AppReducer.js └── GlobalState.js /README.md: -------------------------------------------------------------------------------- 1 | # Expense-Tracker 2 | An application to track and manage personal expenses. 3 | 4 | # Technologies 5 | React, Node.js, Express, MongoDB 6 | -------------------------------------------------------------------------------- /client/src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Lato', sans-serif; 3 | background: #f5f5f5; 4 | } 5 | 6 | .container { 7 | max-width: 450px; 8 | margin: 30px auto; 9 | padding: 0 20px; 10 | } 11 | 12 | h1 { 13 | text-align: center; 14 | margin-bottom: 20px; 15 | } 16 | 17 | h3 { 18 | border-bottom: 1px solid #bbb; 19 | padding-bottom: 5px; 20 | } 21 | 22 | h4 { 23 | margin: 0; 24 | text-transform: uppercase; 25 | } 26 | 27 | .inc-exp-container { 28 | display: flex; 29 | justify-content: space-between; 30 | margin: 20px 0; 31 | padding: 10px; 32 | border: 1px solid #ddd; 33 | background: #fff; 34 | border-radius: 5px; 35 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 36 | } 37 | 38 | .inc-exp-container div { 39 | flex: 1; 40 | text-align: center; 41 | } 42 | 43 | .money { 44 | font-size: 20px; 45 | letter-spacing: 1px; 46 | margin: 5px 0; 47 | } 48 | 49 | .plus { 50 | color: #2ecc71; 51 | } 52 | 53 | .minus { 54 | color: #c0392b; 55 | } 56 | 57 | .list { 58 | list-style: none; 59 | padding: 0; 60 | margin-bottom: 40px; 61 | } 62 | 63 | .list li { 64 | background: #fff; 65 | margin: 10px 0; 66 | padding: 10px; 67 | border-right: 5px solid #2ecc71; 68 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 69 | display: flex; 70 | justify-content: space-between; 71 | border-radius: 5px; 72 | } 73 | 74 | .list li.minus { 75 | border-right-color: #c0392b; 76 | } 77 | 78 | .delete-btn { 79 | cursor: pointer; 80 | background: #e74c3c; 81 | border: 0; 82 | color: #fff; 83 | padding: 5px 10px; 84 | border-radius: 50%; 85 | margin-left: 10px; 86 | font-size: 20px; 87 | } 88 | 89 | .add-btn { 90 | background: #3498db; 91 | color: #fff; 92 | border: 0; 93 | padding: 10px; 94 | cursor: pointer; 95 | border-radius: 5px; 96 | display: block; 97 | width: 100%; 98 | margin: 10px 0; 99 | text-transform: uppercase; 100 | letter-spacing: 1px; 101 | } 102 | 103 | form { 104 | margin-bottom: 40px; 105 | } 106 | 107 | .form-control { 108 | margin: 10px 0; 109 | } 110 | 111 | .form-control label { 112 | display: inline-block; 113 | margin-bottom: 5px; 114 | } 115 | 116 | .form-control input[type="text"], 117 | .form-control input[type="number"] { 118 | border: 1px solid #ddd; 119 | border-radius: 5px; 120 | display: block; 121 | font-size: 16px; 122 | padding: 10px; 123 | width: 100%; 124 | } 125 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect } from 'react'; 2 | import { GlobalProvider } from './context/GlobalState'; 3 | import { Balance } from './components/Balance'; 4 | import { IncomeExpenses } from './components/IncomeExpenses'; 5 | import { TransactionList } from './components/TransactionList'; 6 | import { AddTransaction } from './components/AddTransaction'; 7 | import './App.css'; 8 | 9 | function App() { 10 | return ( 11 | 12 |
13 |

Expense Tracker

14 | 15 | 16 | 17 | 18 |
19 |
20 | ); 21 | } 22 | 23 | export default App; 24 | -------------------------------------------------------------------------------- /client/src/components/AddTransaction.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext } from 'react'; 2 | import { GlobalContext } from '../context/GlobalState'; 3 | 4 | export const AddTransaction = () => { 5 | const [text, setText] = useState(''); 6 | const [amount, setAmount] = useState(0); 7 | 8 | const { addTransaction } = useContext(GlobalContext); 9 | 10 | const onSubmit = e => { 11 | e.preventDefault(); 12 | 13 | const newTransaction = { 14 | id: Math.floor(Math.random() * 100000000), 15 | text, 16 | amount: +amount 17 | } 18 | 19 | addTransaction(newTransaction); 20 | } 21 | 22 | return ( 23 | <> 24 |

Add new transaction

25 |
26 |
27 | 28 | setText(e.target.value)} placeholder="Enter text..." /> 29 |
30 |
31 | 33 | setAmount(e.target.value)} placeholder="Enter amount..." /> 34 |
35 | 36 |
37 | 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /client/src/components/Balance.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { GlobalContext } from '../context/GlobalState'; 3 | 4 | export const Balance = () => { 5 | const { transactions } = useContext(GlobalContext); 6 | 7 | const amounts = transactions.map(transaction => transaction.amount); 8 | 9 | const total = amounts.reduce((acc, item) => (acc += item), 0).toFixed(2); 10 | 11 | return ( 12 | <> 13 |

Your Balance

14 |

${total}

15 | 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /client/src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const Header = () => { 4 | return ( 5 |

6 | Expense Tracker 7 |

8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /client/src/components/IncomeExpenses.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { GlobalContext } from '../context/GlobalState'; 3 | 4 | export const IncomeExpenses = () => { 5 | const { transactions } = useContext(GlobalContext); 6 | 7 | const amounts = transactions.map(transaction => transaction.amount); 8 | 9 | const income = amounts 10 | .filter(item => item > 0) 11 | .reduce((acc, item) => (acc += item), 0) 12 | .toFixed(2); 13 | 14 | const expense = ( 15 | amounts.filter(item => item < 0).reduce((acc, item) => (acc += item), 0) * -1 16 | ).toFixed(2); 17 | 18 | return ( 19 |
20 |
21 |

Income

22 |

${income}

23 |
24 |
25 |

Expense

26 |

${expense}

27 |
28 |
29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /client/src/components/TransactionList.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { GlobalContext } from '../context/GlobalState'; 3 | import { Transaction } from './Transaction'; 4 | 5 | export const TransactionList = () => { 6 | const { transactions } = useContext(GlobalContext); 7 | 8 | return ( 9 | <> 10 |

History

11 | 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /client/src/context/AppReducer.js: -------------------------------------------------------------------------------- 1 | export default (state, action) => { 2 | switch(action.type) { 3 | case 'DELETE_TRANSACTION': 4 | return { 5 | ...state, 6 | transactions: state.transactions.filter(transaction => transaction.id !== action.payload) 7 | } 8 | case 'ADD_TRANSACTION': 9 | return { 10 | ...state, 11 | transactions: [action.payload, ...state.transactions] 12 | } 13 | default: 14 | return state; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/context/GlobalState.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useReducer } from 'react'; 2 | import AppReducer from './AppReducer'; 3 | 4 | // Initial state 5 | const initialState = { 6 | transactions: [] 7 | } 8 | 9 | // Create context 10 | export const GlobalContext = createContext(initialState); 11 | 12 | // Provider component 13 | export const GlobalProvider = ({ children }) => { 14 | const [state, dispatch] = useReducer(AppReducer, initialState); 15 | 16 | // Actions 17 | function deleteTransaction(id) { 18 | dispatch({ 19 | type: 'DELETE_TRANSACTION', 20 | payload: id 21 | }); 22 | } 23 | 24 | function addTransaction(transaction) { 25 | dispatch({ 26 | type: 'ADD_TRANSACTION', 27 | payload: transaction 28 | }); 29 | } 30 | 31 | return ( 36 | {children} 37 | ); 38 | } 39 | --------------------------------------------------------------------------------