├── .babelrc ├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── public ├── _redirects └── index.html └── src ├── components ├── ExpenseCard.jsx ├── Main.jsx ├── Modal.jsx ├── NavBar.jsx ├── RadioGroup.jsx ├── Select.jsx └── Tabs.jsx ├── index.js ├── pages ├── Dashboard.jsx ├── Home.jsx ├── Login.jsx └── SignUp.jsx ├── queries ├── AddExpense.js ├── CurrentUser.js ├── DeleteExpense.js ├── LoginMutation.js ├── SignUpMutation.js ├── UpdateUser.js └── UsersExpenses.js └── styles ├── base ├── grid.scss ├── reset.scss └── typography.scss ├── components ├── button.scss ├── modal.scss └── radiogroup.scss ├── helpers ├── mixins.scss └── variables.scss ├── index.scss └── layout ├── account.scss ├── addexpense.scss ├── form.scss ├── insights.scss ├── main.scss ├── navbar.scss ├── select.scss └── tabs.scss /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["import", { 4 | "libraryName": "antd", 5 | "libraryDirectory": "es", 6 | "style": "css" 7 | }] 8 | ] 9 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | debug.log 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Praveen Bisht 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Expense App - Front End 2 | 3 | Links - [Demo](https://expense-app.netlify.com/) | [Backend](https://github.com/prvnbist/expense-app-backend) 4 | 5 | Status - Online 6 | 7 | ![alt text](https://res.cloudinary.com/prvnbist/image/upload/v1562781804/Expense%20App/2019-07-10_233310.png "Expense App Homepage") 8 | 9 | #### Tech Stack 10 | - ReactJs 11 | - Apollo Client 12 | 13 | #### Getting Started 14 | - `git clone https://github.com/prvnbist/expense-app-frontend.git` 15 | - `npm install` to install all the required packages. 16 | - Go into the `.env` file and replace the value of `REACT_APP_SERVER_URL` with the url where your server is running. 17 | - `npm start` to run the server locally on `http://localhost:3000` 18 | - `npm run build` to build the project for production. 19 | --- 20 | *Happy Coding!* 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expense-app-frontend", 3 | "version": "1.0.0", 4 | "homepage": ".", 5 | "dependencies": { 6 | "apollo-boost": "^0.4.0", 7 | "chart.js": "^2.8.0", 8 | "dotenv": "^8.0.0", 9 | "formik": "^1.5.7", 10 | "graphql": "^14.3.1", 11 | "graphql-tag": "^2.10.1", 12 | "node-sass": "^4.12.0", 13 | "react": "^16.8.6", 14 | "react-apollo": "^2.5.6", 15 | "react-chartjs-2": "^2.7.6", 16 | "react-dom": "^16.8.6", 17 | "react-router-dom": "^5.0.0", 18 | "react-scripts": "3.0.1", 19 | "yup": "^0.27.0" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "eslintConfig": { 28 | "extends": "react-app" 29 | }, 30 | "browserslist": [ 31 | ">0.2%", 32 | "not dead", 33 | "not ie <= 11", 34 | "not op_mini all" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Expense Manager 9 | 10 | 11 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /src/components/ExpenseCard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Mutation} from 'react-apollo'; 3 | import DELETE_EXPENSE_MUTATION from '../queries/DeleteExpense'; 4 | 5 | const Expense = props => { 6 | return ( 7 |
15 |
16 | 17 | {props.item.spentOn.length > 30 18 | ? this 19 | .props 20 | .item 21 | .spentOn 22 | .slice(0, 20) + '...' 23 | : props.item.spentOn} 24 | 25 |
30 | {props.item.type === "plus" 37 | ? '+' 38 | : '-'} {parseInt(props.item.amount).toLocaleString("en-IN", { 39 | style: 'currency', 40 | currency: "INR" 41 | })} 42 | ['usersExpenses']}> 48 | {mutation => } 51 | 52 |
53 |
54 |
55 | 56 | category 57 | {props.item.category} 58 | 59 | 60 | access_time 61 | 62 | {new Date(Number(props.item.createdAt)) 63 | .toTimeString() 64 | .slice(0, 5)} 65 | 66 | 67 | 68 | event 69 | {new Date(Number(props.item.createdAt)) 70 | .toDateString() 71 | .replace(' 2018', '')} 72 | 73 |
74 |
75 | 76 | description 77 | {props.item.description 78 | ? props.item.description 79 | : 'No description.'} 80 | 81 |
82 |
83 | ); 84 | } 85 | 86 | export default Expense; -------------------------------------------------------------------------------- /src/components/Main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Query, Mutation} from 'react-apollo'; 3 | import {Formik} from 'formik'; 4 | import * as Yup from 'yup'; 5 | import {Pie, Line} from "react-chartjs-2"; 6 | 7 | import ADD_EXPENSE_MUTATION from '../queries/AddExpense'; 8 | import USERS_EXPENSES from '../queries/UsersExpenses'; 9 | 10 | import Tabs from './Tabs'; 11 | import Expense from './ExpenseCard'; 12 | import Select from './Select'; 13 | import RadioGroup from './RadioGroup'; 14 | import Modal from './Modal'; 15 | 16 | const Main = () => { 17 | const [filters, 18 | setFilters] = React.useState({search: "", category: "", type: ""}); 19 | const [modal, 20 | showModal] = React.useState(false); 21 | const selected = option => setFilters({ 22 | ...filters, 23 | category: option 24 | }); 25 | const typeSelected = option => setFilters({ 26 | ...filters, 27 | type: option 28 | }); 29 | const closeModal = value => showModal(value); 30 | 31 | const categories = [ 32 | "Mortgage", 33 | "Rent", 34 | "Property Taxes", 35 | "House/Tenant Insurance", 36 | "Utility bills", 37 | "Lease/Car Loan Payment", 38 | "Vehicle Insurance", 39 | "Life/Disability/Extended Health", 40 | "Bank Fees", 41 | "Debt Payments", 42 | "Salary", 43 | "Electronics", 44 | "Groceries", 45 | "Personal Care", 46 | "Fuel/Public Transportation", 47 | "Parking", 48 | "Clothing & Shoes", 49 | "Daycare", 50 | "Work Lunches & Snacks", 51 | "Eating Out", 52 | "Entertainment", 53 | "Tobacco/Alcohol", 54 | "Lottery", 55 | "Babysitting", 56 | "Sports & Recreation", 57 | "Hair Care/Salon Services", 58 | "Magazines/Newspapers/Books", 59 | "Children’s Lessons & Activities", 60 | "Furniture" 61 | ]; 62 | const radioOptions = [ 63 | { 64 | value: "plus", 65 | text: "Earned" 66 | }, { 67 | value: "minus", 68 | text: "Spent" 69 | } 70 | ]; 71 | const expenseSchema = Yup 72 | .object() 73 | .shape({ 74 | spentOn: Yup 75 | .string() 76 | .required(), 77 | amount: Yup 78 | .number() 79 | .typeError() 80 | .required(), 81 | description: Yup 82 | .string() 83 | .max(80) 84 | }); 85 | return ( 86 |
87 | {modal && ( 88 | 89 | ['usersExpenses']}> 92 | {addExpense => ( 93 | { 103 | addExpense({ 104 | variables: { 105 | spentOn: values.spentOn, 106 | category: values.category, 107 | amount: values.amount, 108 | type: values.type, 109 | description: values.description 110 | } 111 | }); 112 | setSubmitting(false); 113 | }}> 114 | {({ 115 | values, 116 | errors, 117 | touched, 118 | handleChange, 119 | handleBlur, 120 | handleSubmit, 121 | isSubmitting 122 | }) => ( 123 | 124 |
125 | 126 |
127 |
128 | 129 | 136 |
137 |
138 |
139 |
140 | 141 | 151 |
152 | 153 |
154 |
155 |
156 | 157 | 165 |
166 |
167 | 168 | 178 |
179 |
180 |
181 |
182 | 183 |