From befc683990ff59a4a6ea2075a4f119c4366518b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Phan=20H=E1=BB=AFu=20L=E1=BB=A3i?= Date: Sun, 10 Sep 2023 22:19:17 +0700 Subject: [PATCH] Add wallet method --- .../src/js/constants/constant.js | 5 + .../src/js/controllers/homeController.js | 23 +++- javascript-practice/src/js/helpers/helpers.js | 5 + javascript-practice/src/js/models/user.js | 3 +- javascript-practice/src/js/models/wallet.js | 20 ++++ .../src/js/services/commonService.js | 4 +- .../src/js/services/localStorageService.js | 2 +- .../src/js/services/service.js | 2 + .../src/js/services/userService.js | 36 ++++++ .../src/js/services/walletService.js | 47 ++++++++ .../src/js/views/commonLoginRegisterView.js | 14 +++ .../src/js/views/commonView.js | 23 ---- javascript-practice/src/js/views/homeView.js | 111 +++++++++++++++++- javascript-practice/src/pages/index.html | 15 ++- 14 files changed, 274 insertions(+), 36 deletions(-) create mode 100644 javascript-practice/src/js/models/wallet.js create mode 100644 javascript-practice/src/js/services/walletService.js diff --git a/javascript-practice/src/js/constants/constant.js b/javascript-practice/src/js/constants/constant.js index 3397fc0..e9dbaac 100644 --- a/javascript-practice/src/js/constants/constant.js +++ b/javascript-practice/src/js/constants/constant.js @@ -10,6 +10,10 @@ export const MESSAGE = { }, DEFAULT_TITLE_ERROR_POPUP: 'Error', USER_EXIST_ERROR: 'User is exists! Please try another email!', + WELCOME: { + title: 'Login Successful!', + message: 'Welcome to Money Lover!', + }, }; export const URL = { @@ -25,6 +29,7 @@ export const BTN_CONTENT = { export const LOCAL_STORAGE = { ACCESS_TOKEN: 'accessToken', + IS_FIRST_LOGIN: 'isFirstLogin', }; export const TYPE_POPUP = { success: 'success', error: 'error' }; diff --git a/javascript-practice/src/js/controllers/homeController.js b/javascript-practice/src/js/controllers/homeController.js index 18ac55f..7397d8c 100644 --- a/javascript-practice/src/js/controllers/homeController.js +++ b/javascript-practice/src/js/controllers/homeController.js @@ -4,11 +4,28 @@ export default class HomeController { this.homeView = view.homeView; } + handlerGetInfoUserLogin() { + return this.service.userService.getInfoUserLogin(); + } + + handlerCheckWalletExist(idUser) { + return this.service.walletService.checkWalletExist(idUser); + } + + handlerSaveWallet(wallet) { + return this.service.walletService.saveWallet(wallet); + } + init() { if (this.homeView) { - this.homeView.handlerTabsTransfer(); - this.homeView.addCommonEventPage(); - this.homeView.addEventSelectCategoryDialog(); + this.homeView.addHandlerInputChangeWalletForm(); + this.homeView.addHandlerSubmitWalletForm( + this.handlerSaveWallet.bind(this), + ); + this.homeView.loadPage( + this.handlerGetInfoUserLogin.bind(this), + this.handlerCheckWalletExist.bind(this), + ); } } } diff --git a/javascript-practice/src/js/helpers/helpers.js b/javascript-practice/src/js/helpers/helpers.js index 4a8aa6a..c8a8a72 100644 --- a/javascript-practice/src/js/helpers/helpers.js +++ b/javascript-practice/src/js/helpers/helpers.js @@ -70,5 +70,10 @@ export const getSubdirectoryURL = () => { const subDirectory = parts[3]; // Get subdirectory url only + const index = subDirectory.indexOf('?'); + if (index !== -1) { + return subDirectory.substring(0, index); + } + return subDirectory; }; diff --git a/javascript-practice/src/js/models/user.js b/javascript-practice/src/js/models/user.js index d46bb8a..b3ed5e7 100644 --- a/javascript-practice/src/js/models/user.js +++ b/javascript-practice/src/js/models/user.js @@ -1,9 +1,8 @@ export default class User { - constructor({ email, password, walletName = '', accessToken = '' }) { + constructor({ email, password, accessToken = '' }) { this.id = User.createIdUser(); this.email = email; this.password = password; - this.walletName = walletName; this.accessToken = accessToken; } diff --git a/javascript-practice/src/js/models/wallet.js b/javascript-practice/src/js/models/wallet.js new file mode 100644 index 0000000..ff48f73 --- /dev/null +++ b/javascript-practice/src/js/models/wallet.js @@ -0,0 +1,20 @@ +export default class Wallet { + constructor({ + walletName = '', + amount = '', + inflow = 0, + outflow = 0, + idUser, + }) { + this.id = Wallet.createIdWallet(); + this.walletName = walletName; + this.amout = amount; + this.inflow = inflow; + this.outflow = outflow; + this.idUser = idUser; + } + + static createIdWallet() { + return new Date().getTime(); + } +} diff --git a/javascript-practice/src/js/services/commonService.js b/javascript-practice/src/js/services/commonService.js index 717d424..cce9cb5 100644 --- a/javascript-practice/src/js/services/commonService.js +++ b/javascript-practice/src/js/services/commonService.js @@ -20,12 +20,12 @@ export default class CommonService { async getDataFromProp(property, value, path = this.defaultPath) { this.connectToDb(); - const existUser = this.firebaseService.getDataFromProp( + const dataExists = this.firebaseService.getDataFromProp( path, property, value, ); - const result = await timeOutConnect(existUser); + const result = await timeOutConnect(dataExists); if (result.id && result.data) { return convertDataObjectToModel(result); diff --git a/javascript-practice/src/js/services/localStorageService.js b/javascript-practice/src/js/services/localStorageService.js index e8d36cb..8412ec8 100644 --- a/javascript-practice/src/js/services/localStorageService.js +++ b/javascript-practice/src/js/services/localStorageService.js @@ -8,7 +8,7 @@ class LocalStorageService { } get(key) { - this.localStorage.getItem(key); + return this.localStorage.getItem(key); } remove(key) { diff --git a/javascript-practice/src/js/services/service.js b/javascript-practice/src/js/services/service.js index dd10508..87116f4 100644 --- a/javascript-practice/src/js/services/service.js +++ b/javascript-practice/src/js/services/service.js @@ -1,7 +1,9 @@ import UserService from './userService'; +import WalletService from './walletService'; export default class Service { constructor() { this.userService = new UserService(); + this.walletService = new WalletService(); } } diff --git a/javascript-practice/src/js/services/userService.js b/javascript-practice/src/js/services/userService.js index 9821641..6a07c1b 100644 --- a/javascript-practice/src/js/services/userService.js +++ b/javascript-practice/src/js/services/userService.js @@ -86,5 +86,41 @@ export default class UserService extends CommonService { LOCAL_STORAGE.ACCESS_TOKEN, newUserData.accessToken, ); + + LocalStorageService.add(LOCAL_STORAGE.IS_FIRST_LOGIN, true); + } + + async getInfoUserLogin() { + // Find access token + const accessToken = LocalStorageService.get(LOCAL_STORAGE.ACCESS_TOKEN); + + if (accessToken) { + const user = await this.getUserByToken(accessToken); + + if (LocalStorageService.get(LOCAL_STORAGE.IS_FIRST_LOGIN)) { + user.isFirstLogin = true; + + LocalStorageService.remove(LOCAL_STORAGE.IS_FIRST_LOGIN); + } + + return user; + } + + return null; + } + + /** + * Get user data from token access + * @param {string} accessToken Access token user + * @returns {Object || null} Return new User Object if find, otherwise return null. + */ + async getUserByToken(accessToken) { + const result = await this.getDataFromProp('accessToken', accessToken); + + if (result) { + return result; + } + + return null; } } diff --git a/javascript-practice/src/js/services/walletService.js b/javascript-practice/src/js/services/walletService.js new file mode 100644 index 0000000..5331bcb --- /dev/null +++ b/javascript-practice/src/js/services/walletService.js @@ -0,0 +1,47 @@ +import CommonService from './commonService'; + +export default class WalletService extends CommonService { + constructor() { + super(); + + this.defaultPath = 'wallets/'; + } + + /** + * Save wallet into database + * @param {Object} wallet The wallet object need to be saved into databae + */ + async saveWallet(wallet) { + await this.save(wallet); + } + + /** + * Check wallet exist in database + * @param {string} idUser The id user to find user's wallet + * @returns {boolean} Return true if find, otherwise return false + */ + async checkWalletExist(idUser) { + const walletExist = await this.getWalletByIdUser(idUser); + + if (walletExist) { + return true; + } + + return false; + } + + /** + * Get wallet data from idUser + * @param {string} email The id user to find user's wallet + * @returns {Object || null} Return new Wallet Object if find, otherwise return null. + */ + async getWalletByIdUser(idUser) { + const result = await this.getDataFromProp('idUser', idUser); + + if (result) { + return result; + } + + return null; + } +} diff --git a/javascript-practice/src/js/views/commonLoginRegisterView.js b/javascript-practice/src/js/views/commonLoginRegisterView.js index 1e5dcf3..a58683b 100644 --- a/javascript-practice/src/js/views/commonLoginRegisterView.js +++ b/javascript-practice/src/js/views/commonLoginRegisterView.js @@ -6,6 +6,8 @@ export default class CommonLoginRegisterView extends CommonView { constructor() { super(); + this.handleEventBtnPopupAndOverlay(); + this.parentElement = document.querySelector('.form'); this.messageDefault = CONSTANT.MESSAGE.ERROR_MESSAGE_DEFAULT; this.inputPassword = document.querySelector('input[name="password"]'); @@ -84,4 +86,16 @@ export default class CommonLoginRegisterView extends CommonView { .querySelector('.form__title') .insertAdjacentHTML('afterend', markup); } + + /** + * Add event listener for popup and overlay + */ + handleEventBtnPopupAndOverlay() { + this.popupBtn.addEventListener('click', this.tooglePopupForm.bind(this)); + this.overlay.addEventListener('click', this.toogleDialog.bind(this)); + } + + toogleDialog() { + this.dialog.classList.toggle('active'); + } } diff --git a/javascript-practice/src/js/views/commonView.js b/javascript-practice/src/js/views/commonView.js index 933b6fb..d83153f 100644 --- a/javascript-practice/src/js/views/commonView.js +++ b/javascript-practice/src/js/views/commonView.js @@ -7,7 +7,6 @@ export default class CommonView { this.initPopup(); this.initElementPopup(); this.initLoader(); - this.handleEventBtnPopupAndOverlay(); } /** @@ -91,28 +90,6 @@ export default class CommonView { this.popupBtn.textContent = btnContent; } - /** - * Add event listener for popup and overlay - */ - handleEventBtnPopupAndOverlay() { - this.popupBtn.addEventListener('click', this.tooglePopupForm.bind(this)); - this.overlay.addEventListener('click', this.toogleDialog.bind(this)); - } - - toogleDialog() { - this.overlay.classList.toggle('active'); - if (this.dialog.length) { - // If it have more dialog - this.dialog.forEach((item) => { - if (item.classList.contains('active')) { - item.classList.remove('active'); - } - }); - } else { - this.dialog.classList.toggle('active'); - } - } - /** * Implement loader screen */ diff --git a/javascript-practice/src/js/views/homeView.js b/javascript-practice/src/js/views/homeView.js index 4a0636c..f9b882f 100644 --- a/javascript-practice/src/js/views/homeView.js +++ b/javascript-practice/src/js/views/homeView.js @@ -1,4 +1,6 @@ +import { TYPE_POPUP, MESSAGE, BTN_CONTENT } from '../constants/constant'; import CommonView from './commonView'; +import Wallet from '../models/wallet'; export default class HomeView extends CommonView { constructor() { @@ -11,6 +13,7 @@ export default class HomeView extends CommonView { this.overlay = document.querySelector('.overlay'); this.darkOverlay = document.querySelector('.dark-overlay'); this.dialog = document.querySelectorAll('.dialog'); + this.saveBtn = document.querySelectorAll('.form__save-btn'); this.cancelBtn = document.querySelectorAll('.form__cancel-btn'); this.categoryField = document.getElementById('selectCategory'); this.closeIcon = document.querySelector('.close-icon'); @@ -18,6 +21,110 @@ export default class HomeView extends CommonView { this.budgetForm = document.getElementById('budgetForm'); this.transactionForm = document.getElementById('transactionForm'); this.categoryForm = document.getElementById('categoryForm'); + this.walletForm = document.getElementById('walletForm'); + + this.walletDialog = document.getElementById('walletDialog'); + } + + async loadPage(getInfoUserLogin, checkWalletExist) { + const user = await getInfoUserLogin(); + + if (!user) { + window.location.replace('/login'); + } else { + this.user = user; + const walletExist = await checkWalletExist(user.id); + + // Check user's wallet if have or not + if (!walletExist) { + // Show add wallet dialog + this.walletDialog.classList.add('active'); + // If overlay is not exist on page + if (!this.overlay.classList.contains('active')) { + this.toggleActiveOverlay(); + } + } else { + this.loadEvent(); + } + } + } + + addHandlerSubmitWalletForm(saveWallet) { + this.walletForm.addEventListener('submit', (e) => { + e.preventDefault(); + + this.submitWalletForm(saveWallet); + }); + } + + async submitWalletForm(saveWallet) { + try { + const wallet = new Wallet({ + walletName: this.walletName, + amount: this.amount, + idUser: this.user.id, + }); + + await saveWallet(wallet); + + this.hideDialog(); + this.showSuccessPopup('Add wallet success', 'Click ok to continue!'); + this.loadEvent(); + } catch (error) { + // Show popup error + this.initErrorPopup(error); + } + } + + addHandlerInputChangeWalletForm() { + this.walletForm.addEventListener('input', (e) => { + const bodyDialog = e.target.closest('.dialog__body'); + this.validateWalletForm(bodyDialog); + }); + } + + validateWalletForm(bodyDialog) { + this.walletName = bodyDialog.querySelector('.form__input-text').value; + this.amount = bodyDialog.querySelector('.form__input-balance').value; + const saveBtn = bodyDialog.querySelector('.form__save-btn'); + + if (this.walletName.length >= 3 && this.amount.length >= 1) { + saveBtn.classList.add('active'); + } else { + saveBtn.classList.remove('active'); + } + } + + showSuccessPopup(title, message) { + const typePopup = TYPE_POPUP.success; + const btnContent = BTN_CONTENT.OK; + + this.initPopupContent(typePopup, title, message, btnContent); + + // Show popup + this.tooglePopupForm(); + } + + /** + * Implement error popup in site + * @param {string} content The content will show in error popup + */ + initErrorPopup(error) { + const title = error.title ? error.title : MESSAGE.DEFAULT_TITLE_ERROR_POPUP; + const content = error.message ? error.message : error; + + this.initPopupContent(TYPE_POPUP.error, title, content, BTN_CONTENT.GOT_IT); + + // Show popup + this.tooglePopupForm(); + } + + /* ------------------------------- HANDLER EVENT ------------------------------- */ + + loadEvent() { + this.addCommonEventPage(); + this.handlerTabsTransfer(); + this.addEventSelectCategoryDialog(); } /** @@ -55,13 +162,14 @@ export default class HomeView extends CommonView { this.cancelBtn.forEach((item) => { item.addEventListener('click', () => { this.hideDialog(); - this.toggleActiveOverlay(); }); }); this.overlay.addEventListener('click', () => { this.hideDialog(); }); + + this.popupBtn.addEventListener('click', this.tooglePopupForm.bind(this)); } addEventSelectCategoryDialog() { @@ -96,6 +204,7 @@ export default class HomeView extends CommonView { item.classList.remove('active'); } }); + this.toggleActiveOverlay(); } toggleActiveOverlay() { diff --git a/javascript-practice/src/pages/index.html b/javascript-practice/src/pages/index.html index 1457d43..492bb2f 100644 --- a/javascript-practice/src/pages/index.html +++ b/javascript-practice/src/pages/index.html @@ -17,7 +17,7 @@ --> -
+
@@ -25,14 +25,16 @@
-
+

Wallet name

@@ -48,7 +50,12 @@

Initial Balance

- +