├── 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 |
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 |
12 | {transactions.map(transaction => (
13 |
14 | ))}
15 |
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 |
--------------------------------------------------------------------------------