aboutsummaryrefslogtreecommitdiffstats
path: root/public/projects/js-small-apps/budget-app/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'public/projects/js-small-apps/budget-app/app.js')
-rw-r--r--public/projects/js-small-apps/budget-app/app.js311
1 files changed, 311 insertions, 0 deletions
diff --git a/public/projects/js-small-apps/budget-app/app.js b/public/projects/js-small-apps/budget-app/app.js
new file mode 100644
index 0000000..2fb0cba
--- /dev/null
+++ b/public/projects/js-small-apps/budget-app/app.js
@@ -0,0 +1,311 @@
+import BudgetApp from "./lib/class-budget-app.js";
+import Notification from "./lib/class-notification.js";
+import getCurrencyFormat from "./lib/utils/currency.js";
+
+const app = new BudgetApp("Budget", "Anonymous");
+const ui = {
+ budget: {
+ remaining: document.getElementById("budget-remaining"),
+ spent: document.getElementById("budget-spent"),
+ },
+ buttons: {
+ categories: {
+ add: document.querySelector(".manage-categories .btn--add"),
+ delete: document.querySelector(".manage-categories .btn--delete"),
+ rename: document.querySelector(".manage-categories .btn--rename"),
+ },
+ register: document.querySelector(".register .btn--register"),
+ reset: document.querySelector(".footer .btn--reset"),
+ transactions: {
+ update: document.querySelector(".manage-transactions .btn--update"),
+ },
+ },
+ form: {
+ categories: {
+ add: document.getElementById("add-category"),
+ rename: document.getElementById("rename-category"),
+ select: document.getElementById("select-category"),
+ },
+ register: {
+ budget: document.getElementById("register-budget"),
+ language: document.getElementById("register-locale"),
+ username: document.getElementById("register-username"),
+ },
+ transactions: {
+ amount: document.getElementById("transaction-amount"),
+ category: document.getElementById("transaction-category"),
+ date: document.getElementById("transaction-date"),
+ id: document.getElementById("transaction-id"),
+ name: document.getElementById("transaction-name"),
+ type: document.getElementById("transaction-type"),
+ },
+ },
+ history: {
+ body: document.querySelector(".app__history .table__body"),
+ },
+ title: document.querySelector(".branding__title"),
+};
+
+function initApp() {
+ const register = document.querySelector(".register");
+ const application = document.querySelector(".app");
+ const budget = ui.form.register.budget.value;
+ const locale = ui.form.register.language.value;
+ const username = ui.form.register.username.value;
+
+ if (budget && locale && username) {
+ app.user.username = username;
+ app.user.budget = budget;
+ app.user.locale = locale;
+ register.style.display = "none";
+ application.style.display = "block";
+ } else {
+ notify("You must complete all fields!", "error", 3000);
+ }
+}
+
+function notify(message, type, duration, position = "bottom") {
+ const notification = new Notification(message, type);
+ notification.duration = duration;
+ notification.position = position;
+ return notification.notify();
+}
+
+function getSelectOptions(select, options) {
+ select.innerHTML = "";
+ options.forEach((option) => {
+ select.add(new Option(option.name, option.id));
+ });
+
+ return select;
+}
+
+function findName(id, array) {
+ const object = array.find((item) => item.id === Number(id));
+ if (object) {
+ return object.name;
+ } else {
+ return "(deleted)";
+ }
+}
+
+function resetTransactionForm() {
+ ui.form.transactions.amount.value = "";
+ ui.form.transactions.category.value = "";
+ ui.form.transactions.date.value = "";
+ ui.form.transactions.id.value = "";
+ ui.form.transactions.name.value = "";
+ ui.form.transactions.type.value = "";
+}
+
+function setTransactionForm(transaction) {
+ ui.form.transactions.amount.value = transaction.amount;
+ ui.form.transactions.category.value = transaction.category;
+ ui.form.transactions.date.valueAsDate = transaction.date;
+ ui.form.transactions.id.value = transaction.id;
+ ui.form.transactions.name.value = transaction.name;
+ ui.form.transactions.type.value = transaction.type;
+}
+
+function manageHistory(target) {
+ const tr = target.parentElement.parentElement;
+ const transactionId = Number(tr.id.replace("transaction-", ""));
+ const array = tr.classList.contains("table__row--expense")
+ ? app.expenses
+ : app.incomes;
+ if (target.classList.contains("btn--delete")) {
+ const tbody = tr.parentElement;
+ app.remove(transactionId, array);
+ updateBudget();
+ updateHistory(tbody);
+ } else if (target.classList.contains("btn--edit")) {
+ const index = array.findIndex(
+ (transaction) => transaction.id === transactionId
+ );
+ setTransactionForm(array[index]);
+ }
+}
+
+function getTransactionButton(type) {
+ const btn = document.createElement("button");
+ let text = "";
+
+ switch (type) {
+ case "delete":
+ text = "Delete";
+ break;
+ case "edit":
+ text = "Edit";
+ break;
+ default:
+ break;
+ }
+
+ btn.textContent = text;
+ btn.classList.add("btn", `btn--${type}`);
+ btn.addEventListener("click", (event) => manageHistory(event.target));
+
+ return btn;
+}
+
+function getTransactionCell(data = "") {
+ const td = document.createElement("td");
+ td.classList.add("table__item");
+ td.textContent = data;
+
+ return td;
+}
+
+function getTransactionRow(transaction) {
+ const tr = document.createElement("tr");
+ const amount =
+ transaction.type === "expense"
+ ? transaction.amount * -1
+ : transaction.amount;
+ const localizedAmount = getCurrencyFormat(amount, app.user.locale);
+ const categoryName = findName(transaction.category, app.categories);
+ const date = transaction.date.toLocaleDateString(app.user.locale);
+
+ const dateCell = getTransactionCell(date);
+ const nameCell = getTransactionCell(transaction.name);
+ const categoryCell = getTransactionCell(categoryName);
+ const typeCell = getTransactionCell(transaction.type);
+ const amountCell = getTransactionCell(localizedAmount);
+ const manageCell = getTransactionCell();
+ const editButton = getTransactionButton("edit");
+ const deleteButton = getTransactionButton("delete");
+
+ manageCell.append(editButton, deleteButton);
+ tr.classList.add("table__row", `table__row--${transaction.type}`);
+ tr.id = `transaction-${transaction.id}`;
+ tr.append(dateCell, nameCell, categoryCell, typeCell, amountCell, manageCell);
+
+ return tr;
+}
+
+function getHistory(tbody) {
+ const transactions = app.getOrderedTransactions("oldest");
+ transactions.forEach((transaction) => {
+ tbody.appendChild(getTransactionRow(transaction));
+ });
+}
+
+function updateBudget() {
+ app.updateUserBudget();
+ ui.budget.remaining.textContent = getCurrencyFormat(
+ app.user.budget.remaining(),
+ app.user.locale
+ );
+ ui.budget.spent.textContent = getCurrencyFormat(
+ app.user.budget.spent,
+ app.user.locale
+ );
+}
+
+function updateCategories() {
+ getSelectOptions(ui.form.categories.select, app.categories);
+ getSelectOptions(ui.form.transactions.category, app.categories);
+}
+
+function updateHistory() {
+ ui.history.body.innerHTML = "";
+ getHistory(ui.history.body, app);
+}
+
+function updateAll() {
+ ui.title.textContent = `${app.user.username} ${app.title}`;
+ updateBudget();
+ updateCategories();
+ updateHistory();
+}
+
+function listen() {
+ for (const [name, element] of Object.entries(ui.buttons.categories)) {
+ element.addEventListener("click", (event) => {
+ event.preventDefault();
+ const id = Number(ui.form.categories.select.value);
+
+ switch (name) {
+ case "add":
+ if (ui.form.categories.add.value) {
+ app.addCategory(ui.form.categories.add.value);
+ ui.form.categories.add.value = "";
+ } else {
+ notify("Category name must be filled!", "error", 3000);
+ }
+ break;
+ case "delete":
+ if (id) {
+ app.remove(id, app.categories);
+ updateHistory();
+ } else {
+ notify("A category must be selected!", "error", 3000);
+ }
+ break;
+ case "rename":
+ const newName = ui.form.categories.rename.value;
+ if (newName && id) {
+ app.renameCategory(id, newName);
+ updateHistory();
+ ui.form.categories.rename.value = "";
+ } else {
+ notify(
+ "You need to select a category and enter a new name first!",
+ "error",
+ 3000
+ );
+ }
+ break;
+ default:
+ break;
+ }
+
+ updateCategories();
+ });
+ }
+
+ ui.buttons.transactions.update.addEventListener("click", (event) => {
+ event.preventDefault();
+ const transactionId = Number(ui.form.transactions.id.value);
+ const transaction = {
+ date: ui.form.transactions.date.value,
+ name: ui.form.transactions.name.value,
+ type: ui.form.transactions.type.value,
+ category: ui.form.transactions.category.value,
+ amount: ui.form.transactions.amount.value,
+ };
+ const error = [];
+ for (const value in transaction) {
+ const element = transaction[value];
+ !element ? error.push(value) : "";
+ }
+ if (error.length === 0) {
+ transactionId
+ ? app.editTransaction({ id: transactionId, ...transaction })
+ : app.addTransaction(transaction);
+ updateBudget();
+ updateHistory();
+ resetTransactionForm();
+ } else {
+ const errorMsg = `These fields (${error.join(", ")}) must be filled!`;
+ notify(errorMsg, "error", 3000);
+ }
+ });
+
+ ui.buttons.register.addEventListener("click", (event) => {
+ event.preventDefault();
+ initApp();
+ updateAll();
+ });
+
+ ui.buttons.reset.addEventListener("click", (event) => {
+ event.preventDefault();
+ if (confirm("Are you sure?")) {
+ notify("Reset!", "warning", 2000);
+ app.reset();
+ }
+ updateAll();
+ });
+}
+
+listen();