├── .gitignore ├── README.md ├── style.css ├── index.html └── app.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Javascript Expense Manager 2 | An Expense Manager project with graphs 3 | 4 | This repository is a reference codebase for creating an Expense manager with Javascript. 5 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | .clearfix::after { 8 | content: ""; 9 | display: table; 10 | clear: both; 11 | } 12 | 13 | body { 14 | color: #555; 15 | font-family: Open Sans; 16 | font-size: 16px; 17 | position: relative; 18 | height: 100vh; 19 | font-weight: 400; 20 | } 21 | 22 | .left-container { 23 | height: 100vh; 24 | background-image: linear-gradient(#0277BD, #03A9F4); 25 | background-size: cover; 26 | background-position: center; 27 | position: relative; 28 | } 29 | 30 | .right-container { 31 | height: 100vh; 32 | width: 100%; 33 | position: relative; 34 | } 35 | 36 | .header { 37 | font-weight: 700; 38 | font-size: 36px; 39 | } 40 | 41 | .sub-text { 42 | font-size: 22px; 43 | font-weight: 400; 44 | } 45 | 46 | .month-container { 47 | padding-top: 25%; 48 | padding-left: 5%; 49 | padding-right: 5%; 50 | } 51 | 52 | .calc-container { 53 | padding-top: 12%; 54 | padding-left: 5%; 55 | padding-right: 5%; 56 | } 57 | 58 | .fs-white { 59 | color: #ffffff; 60 | } 61 | 62 | .fs-dark-grey { 63 | color: #4e4e4e; 64 | } 65 | 66 | .budget-container { 67 | display: inline-block; 68 | background: #ffffff; 69 | border-radius: 8px; 70 | box-shadow: 0 6px 4px #000000; 71 | } 72 | 73 | .month-amount { 74 | font-size: 36px; 75 | font-weight: 700; 76 | } 77 | 78 | .bottom-border { 79 | border-bottom: 1px solid #00446D; 80 | } 81 | 82 | .expense-row { 83 | padding: 10px; 84 | } 85 | 86 | .expense-date { 87 | color: #077CC1; 88 | } 89 | 90 | .expense-text { 91 | color: #077CC1; 92 | } 93 | 94 | .expense-list { 95 | overflow-y: scroll; 96 | } 97 | 98 | .fs-15 { 99 | font-size: 15px; 100 | } 101 | 102 | .expense-value { 103 | text-align: end; 104 | } 105 | 106 | .expense-saving { 107 | color: #039300; 108 | } 109 | 110 | .expense-cost { 111 | color: #E40000; 112 | } 113 | 114 | .expense-investment { 115 | color: #f48803; 116 | } 117 | 118 | #expense-chart { 119 | margin: 20% 0; 120 | } 121 | 122 | .btn-submit-expense { 123 | border-radius: 50%; 124 | } 125 | 126 | .currency-select { 127 | margin: 0 4%; 128 | } 129 | 130 | .selected-currency { 131 | color: #ffffff; 132 | font-size: 12px; 133 | font-weight: 700; 134 | margin-top: 1%; 135 | } 136 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Expense Manager 6 | 8 | 9 | 10 | 11 | 14 | 17 | 20 | 21 | 22 | 23 | 24 |
25 |
26 |
27 |
Your Budget
28 |
29 |
30 | ₹ 0 31 |
32 |
33 | 34 |
35 | 36 |
37 |
38 |
39 |
40 |
Track Your Budget
41 | 53 |
Tracking Savings 💰
54 | 55 |
56 |
57 | 58 |
59 |
60 | 61 |
62 |
63 | 64 |
65 |
66 |
67 | 68 |
69 | 70 |
71 |
72 |
73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | let ExpenseController = (() => { 2 | let total = 0, savings = 0, expenses = 0, investments = 0; 3 | 4 | return { 5 | inputEntry(userInput) { 6 | if (userInput['expenseType'] === 'savings') { 7 | savings += userInput['value']; 8 | total += userInput['value']; 9 | } 10 | if (userInput['expenseType'] === 'investment') { 11 | investments += userInput['value']; 12 | total -= userInput['value']; 13 | } 14 | if (userInput['expenseType'] === 'expense') { 15 | expenses += userInput['value']; 16 | total -= userInput['value']; 17 | } 18 | }, 19 | 20 | getSavingsData() { 21 | return savings; 22 | }, 23 | 24 | getExpensesData() { 25 | return expenses; 26 | }, 27 | 28 | getInvestmentData() { 29 | return investments; 30 | }, 31 | 32 | getTotalData() { 33 | return total; 34 | } 35 | } 36 | 37 | })(); 38 | 39 | let UIController = (() => { 40 | let expenseType = 'savings'; 41 | 42 | let HTMLStrings = { 43 | inExpenseDescription: '.input-expense-description', 44 | inExpenseValue: '.input-expense-value', 45 | btnSubmitExpense: '.btn-submit-expense', 46 | expenseList: '.expense-list', 47 | currentMonth: '#current-month', 48 | typeExpense: '#type-expense', 49 | typeSavings: '#type-savings', 50 | typeInvestment: '#type-investment', 51 | trackingText: '.tracking-text', 52 | expenseChart: '#expense-chart', 53 | monthBudget: '#month-budget' 54 | }; 55 | 56 | return { 57 | numberFormat(number) { 58 | return Intl.NumberFormat('en-IN').format(number); 59 | }, 60 | showCurrentMonth() { 61 | let now, month, year, months; 62 | 63 | now = new Date(); 64 | month = now.getMonth(); 65 | year = now.getFullYear(); 66 | months = [ 67 | 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 68 | 'November', 'December' 69 | ]; 70 | document.querySelector(HTMLStrings.currentMonth).textContent = months[month] + " " + year; 71 | }, 72 | 73 | getHTMLStrings() { 74 | return HTMLStrings; 75 | }, 76 | 77 | setExpenseType(type) { 78 | console.log('here', type); 79 | this.expenseType = type; 80 | let emoji ="💰"; 81 | if (type === 'savings') { 82 | emoji ="💰"; 83 | if (document.querySelector(HTMLStrings.btnSubmitExpense).classList.contains('btn-warning')) { 84 | document.querySelector(HTMLStrings.btnSubmitExpense).classList.remove('btn-warning'); 85 | } 86 | if (document.querySelector(HTMLStrings.btnSubmitExpense).classList.contains('btn-danger')) { 87 | document.querySelector(HTMLStrings.btnSubmitExpense).classList.remove('btn-danger'); 88 | } 89 | if (!document.querySelector(HTMLStrings.btnSubmitExpense).classList.contains('btn-success')) { 90 | document.querySelector(HTMLStrings.btnSubmitExpense).classList.add('btn-success'); 91 | } 92 | } 93 | 94 | if (type === 'expense') { 95 | emoji = "🧾"; 96 | if (document.querySelector(HTMLStrings.btnSubmitExpense).classList.contains('btn-warning')) { 97 | document.querySelector(HTMLStrings.btnSubmitExpense).classList.remove('btn-warning'); 98 | } 99 | if (document.querySelector(HTMLStrings.btnSubmitExpense).classList.contains('btn-success')) { 100 | document.querySelector(HTMLStrings.btnSubmitExpense).classList.remove('btn-success'); 101 | } 102 | if (!document.querySelector(HTMLStrings.btnSubmitExpense).classList.contains('btn-danger')) { 103 | document.querySelector(HTMLStrings.btnSubmitExpense).classList.add('btn-danger'); 104 | } 105 | } 106 | if (type === 'investment') { 107 | emoji = "🏠"; 108 | if (document.querySelector(HTMLStrings.btnSubmitExpense).classList.contains('btn-danger')) { 109 | document.querySelector(HTMLStrings.btnSubmitExpense).classList.remove('btn-danger'); 110 | } 111 | if (document.querySelector(HTMLStrings.btnSubmitExpense).classList.contains('btn-success')) { 112 | document.querySelector(HTMLStrings.btnSubmitExpense).classList.remove('btn-success'); 113 | } 114 | if (!document.querySelector(HTMLStrings.btnSubmitExpense).classList.contains('btn-warning')) { 115 | document.querySelector(HTMLStrings.btnSubmitExpense).classList.add('btn-warning'); 116 | } 117 | } 118 | 119 | document.querySelector(HTMLStrings.trackingText).textContent = "Tracking " + type + " " + emoji; 120 | 121 | }, 122 | 123 | getUserExpenseInput() { 124 | return { 125 | description: document.querySelector(HTMLStrings.inExpenseDescription).value, 126 | value: parseInt(document.querySelector(HTMLStrings.inExpenseValue).value), 127 | date: new Date().toLocaleDateString(), 128 | expenseType: this.expenseType ? this.expenseType : 'savings' 129 | } 130 | }, 131 | 132 | addListItem (inputObj) { 133 | let html, element; 134 | element = HTMLStrings.expenseList; 135 | 136 | if (inputObj['expenseType'] === 'savings') { 137 | html = '
' + inputObj['date'] + '
' + inputObj['description'] + '
₹ ' + this.numberFormat(inputObj['value']) + '
' 138 | } else if (inputObj['expenseType'] === 'expense') { 139 | html = '
' + inputObj['date'] + '
' + inputObj['description'] + '
₹ ' + this.numberFormat(inputObj['value']) + '
' 140 | } else if (inputObj['expenseType'] === 'investment') { 141 | html = '
' + inputObj['date'] + '
' + inputObj['description'] + '
₹ ' + this.numberFormat(inputObj['value']) + '
' 142 | } 143 | 144 | // Add the new element 145 | document.querySelector(element).insertAdjacentHTML('beforeend', html); 146 | 147 | // Clear the input fields after adding element 148 | document.querySelector(HTMLStrings.inExpenseValue).value = ""; 149 | document.querySelector(HTMLStrings.inExpenseDescription).value = ""; 150 | }, 151 | 152 | updateOverallTotal(totalValue) { 153 | document.querySelector(HTMLStrings.monthBudget).textContent = "₹ " + this.numberFormat(totalValue); 154 | 155 | if (totalValue > 0) { 156 | if (document.querySelector(HTMLStrings.monthBudget).classList.contains('expense-cost')) { 157 | document.querySelector(HTMLStrings.monthBudget).classList.remove('expense-cost'); 158 | } 159 | document.querySelector(HTMLStrings.monthBudget).classList.add('expense-saving'); 160 | } else { 161 | if (document.querySelector(HTMLStrings.monthBudget).classList.contains('expense-saving')) { 162 | document.querySelector(HTMLStrings.monthBudget).classList.remove('expense-saving'); 163 | } 164 | document.querySelector(HTMLStrings.monthBudget).classList.add('expense-cost'); 165 | } 166 | }, 167 | 168 | displayChart(savings = 0, expenses = 0, investments = 0) { 169 | let ctx = document.querySelector(HTMLStrings.expenseChart); 170 | let expenseChart = new Chart(ctx, { 171 | type: 'doughnut', 172 | data: { 173 | labels: ['Savings', 'Expenses', 'Investments'], 174 | datasets: [{ 175 | data: [savings, expenses, investments], 176 | backgroundColor: [ 177 | 'rgba(32, 137, 56, 1)', 178 | 'rgba(255, 84, 98, 1)', 179 | 'rgba(255, 206, 86, 1)' 180 | ], 181 | borderWidth: 0.5 182 | }] 183 | }, 184 | options: { 185 | legend: { 186 | labels: { 187 | fontColor: 'white' 188 | } 189 | } 190 | } 191 | }); 192 | } 193 | } 194 | })(); 195 | 196 | ((UIController, ExpenseController) => { 197 | 198 | let HTMLStrings = UIController.getHTMLStrings(); 199 | let setupEventListeners = () => { 200 | document.querySelector(HTMLStrings.btnSubmitExpense).addEventListener('click', addExpense); 201 | document.querySelector(HTMLStrings.typeExpense).addEventListener('click', () => { 202 | setExpenseType('expense') 203 | }); 204 | document.querySelector(HTMLStrings.typeInvestment).addEventListener('click', () => { 205 | setExpenseType('investment') 206 | }); 207 | document.querySelector(HTMLStrings.typeSavings).addEventListener('click', () => { 208 | setExpenseType('savings') 209 | }); 210 | }; 211 | 212 | let setExpenseType = (type) => { 213 | UIController.setExpenseType(type); 214 | } 215 | 216 | let addExpense = () => { 217 | let input = UIController.getUserExpenseInput(); 218 | console.log(input); 219 | 220 | if (input.description !== "" && !isNaN(input.value) && input.value > 0) { 221 | console.log('Adding item'); 222 | UIController.addListItem(input); 223 | ExpenseController.inputEntry(input); 224 | UIController.updateOverallTotal(ExpenseController.getTotalData()); 225 | UIController.displayChart(ExpenseController.getSavingsData(), ExpenseController.getExpensesData(), 226 | ExpenseController.getInvestmentData()); 227 | } 228 | } 229 | 230 | let init = () => { 231 | console.log('Initializing...'); 232 | setupEventListeners(); 233 | UIController.showCurrentMonth(); 234 | } 235 | 236 | init(); 237 | 238 | })(UIController, ExpenseController); 239 | --------------------------------------------------------------------------------