diff --git a/javascript-practice/src/js/helpers/helpers.js b/javascript-practice/src/js/helpers/helpers.js index 54b8fd5..97b5146 100644 --- a/javascript-practice/src/js/helpers/helpers.js +++ b/javascript-practice/src/js/helpers/helpers.js @@ -1,7 +1,6 @@ import * as MESSAGE from '../constants/message'; -import { TIME_OUT_SEC } from '../constants/config'; +import { TIME_OUT_SEC, REGEX } from '../constants/config'; import FirebaseService from '../services/firebaseService'; -import { REGEX_PASSWORD } from '../constants/variable'; /** * Validate password @@ -9,7 +8,7 @@ import { REGEX_PASSWORD } from '../constants/variable'; * @returns {boolean} Return true if validate password success, otherwise return false */ export const validatePassword = (password) => { - return REGEX_PASSWORD.test(password); + return REGEX.PASSWORD.test(password); }; /** diff --git a/javascript-practice/src/js/services/service.js b/javascript-practice/src/js/services/index.js similarity index 100% rename from javascript-practice/src/js/services/service.js rename to javascript-practice/src/js/services/index.js diff --git a/javascript-practice/src/js/services/userService.js b/javascript-practice/src/js/services/userService.js index 9821641..75698e1 100644 --- a/javascript-practice/src/js/services/userService.js +++ b/javascript-practice/src/js/services/userService.js @@ -1,4 +1,4 @@ -import { LOCAL_STORAGE } from '../constants/constant'; +import { LOCAL_STORAGE } from '../constants/config'; import { createToken } from '../helpers/helpers'; import CommonService from './commonService'; import LocalStorageService from './localStorageService'; @@ -54,7 +54,7 @@ export default class UserService extends CommonService { * @param {*} password The password user input * @returns {boolean} Return true if match info on database, otherwise return false */ - async validateUser(email, password) { + async loginUser(email, password) { const user = await this.getUserByEmail(email); // Check password diff --git a/javascript-practice/src/js/views/commonLoginRegisterView.js b/javascript-practice/src/js/views/commonLoginRegisterView.js index 1e5dcf3..5003978 100644 --- a/javascript-practice/src/js/views/commonLoginRegisterView.js +++ b/javascript-practice/src/js/views/commonLoginRegisterView.js @@ -1,5 +1,5 @@ import CommonView from './commonView'; -import * as CONSTANT from '../constants/constant'; +import * as MESSAGE from '../constants/message'; import { validatePassword } from '../helpers/helpers'; export default class CommonLoginRegisterView extends CommonView { @@ -7,7 +7,7 @@ export default class CommonLoginRegisterView extends CommonView { super(); this.parentElement = document.querySelector('.form'); - this.messageDefault = CONSTANT.MESSAGE.ERROR_MESSAGE_DEFAULT; + this.messageDefault = MESSAGE.ERROR_MESSAGE_DEFAULT; this.inputPassword = document.querySelector('input[name="password"]'); this.inputPasswordConfirm = document.querySelector( 'input[name="password_confirm"]', @@ -24,17 +24,17 @@ export default class CommonLoginRegisterView extends CommonView { if (validatePassword(account.passwordConfirm)) { return true; } - this.showError(CONSTANT.MESSAGE.PASSWORD_NOT_STRONG); + this.showError(MESSAGE.PASSWORD_NOT_STRONG); return false; } - this.showError(CONSTANT.MESSAGE.PASSWORD_NOT_MATCH); + this.showError(MESSAGE.PASSWORD_NOT_MATCH); return false; } /** * Show or hide style error input password */ - toogleErrorStyleInputPass() { + toggleErrorStyleInputPass() { this.inputPassword.classList.toggle('error-input'); this.inputPasswordConfirm.classList.toggle('error-input'); } @@ -49,7 +49,7 @@ export default class CommonLoginRegisterView extends CommonView { // If have error message on page, remove it with style error input password if (this.errorMessageEl) { this.errorMessageEl.remove(); - this.toogleErrorStyleInputPass(); + this.toggleErrorStyleInputPass(); } } @@ -68,7 +68,11 @@ export default class CommonLoginRegisterView extends CommonView { */ showError(message) { this.renderError(message); - this.toogleErrorStyleInputPass(); + this.toggleErrorStyleInputPass(); + } + + toggleDialog() { + this.dialog.classList.toggle('active'); } /** diff --git a/javascript-practice/src/js/views/commonView.js b/javascript-practice/src/js/views/commonView.js index 083f01f..f329ae3 100644 --- a/javascript-practice/src/js/views/commonView.js +++ b/javascript-practice/src/js/views/commonView.js @@ -1,102 +1,108 @@ -import { MARK_ICON, TYPE_POPUP } from '../constants/constant'; +import { MARK_ICON, TYPE_TOAST } from '../constants/config'; export default class CommonView { constructor() { - this.overlayMarkup = '
'; - - this.initPopup(); - this.initElementPopup(); + this.initToast(); + this.initElementToast(); this.initLoader(); - this.handleEventBtnPopupAndOverlay(); + this.handleEventToast(); } /** - * Implement popup in site + * Implement toast in site */ - initPopup() { + initToast() { this.rootElement = document.querySelector('body'); const markup = ` - ${this.overlayMarkup} - + +
+
+

+

+ +
+
`; this.rootElement.insertAdjacentHTML('afterbegin', markup); } /** - * Assgign element in popup to property + * Assign element in toast to property */ - initElementPopup() { - this.modalBox = document.querySelector('.modal-box'); - this.popupIcon = document.querySelector('.mark'); - this.popupBtn = document.querySelector('.modal-box__redirect-btn'); - this.popupTitle = document.querySelector('.modal-box__title'); - this.popupContent = document.querySelector('.modal-box__message'); - this.overlay = document.querySelector('.overlay'); + initElementToast() { + this.toastDialog = document.querySelector('.dialog'); + this.toast = document.querySelector('.toast'); + this.toastIcon = this.toast.querySelector('.mark'); + this.toastBtn = this.toast.querySelector('.toast__redirect-btn'); + this.toastTitle = this.toast.querySelector('.toast__title'); + this.toastContent = this.toast.querySelector('.toast__message'); } /** * Show or hide loader screen */ - toogleLoaderSpinner() { + toggleLoaderSpinner() { this.spinner.classList.toggle('hidden'); } /** - * Show or hide popup - */ - tooglePopupForm() { - this.overlay.classList.toggle('active'); - this.modalBox.classList.toggle('active'); - } - - /** - * Add popup content - * @param {TYPE_POPUP} typePopup Type of the popup - * @param {string} title Title of popup - * @param {string} content Content of popup + * Add toast content + * @param {TYPE_TOAST} typeToast Type of the toast + * @param {string} title Title of toast + * @param {string} content Content of toast * @param {string} btnContent Content of button */ - initPopupContent(typePopup, title, content, btnContent) { - // Remove old typePopup class if haved - this.modalBox.classList.forEach((classItem) => - classItem === TYPE_POPUP.success || classItem === TYPE_POPUP.error - ? this.modalBox.classList.remove(classItem) - : '', - ); + initToastContent(typeToast, title, content, btnContent) { + // Remove old typeToast class if haved + Object.keys(TYPE_TOAST).forEach((value) => { + CommonView.removeClassElement(value, this.toast); + }); - // Remove old icon popup if haved - this.popupIcon.classList.forEach((classItem) => - classItem === MARK_ICON.success || classItem === MARK_ICON.error - ? this.popupIcon.classList.remove(classItem) - : '', - ); + // Remove old icon toast if haved + Object.keys(MARK_ICON).forEach((value) => { + CommonView.removeClassElement(value, this.toastIcon); + }); - // Init content popup - this.modalBox.classList.add( - typePopup === TYPE_POPUP.success ? TYPE_POPUP.success : TYPE_POPUP.error, + // Init content toast + this.toast.classList.add( + typeToast === TYPE_TOAST.success ? TYPE_TOAST.success : TYPE_TOAST.error, ); - this.popupIcon.classList.add( - typePopup === TYPE_POPUP.success ? MARK_ICON.success : MARK_ICON.error, + this.toastIcon.classList.add( + typeToast === TYPE_TOAST.success ? MARK_ICON.success : MARK_ICON.error, ); - this.popupTitle.textContent = title; - this.popupContent.textContent = content; - this.popupBtn.textContent = btnContent; + this.toastTitle.textContent = title; + this.toastContent.textContent = content; + this.toastBtn.textContent = btnContent; + } + + static removeClassElement(classEl, el) { + if (el.classList.contains(classEl)) { + el.classList.remove(classEl); + } } /** - * Add event listener for popup and overlay + * Add event listener for toast */ - handleEventBtnPopupAndOverlay() { - this.popupBtn.addEventListener('click', this.tooglePopupForm.bind(this)); - this.overlay.addEventListener('click', this.tooglePopupForm.bind(this)); + handleEventToast() { + this.toastBtn.addEventListener('click', () => { + this.toastDialog.close(); + }); + + // Add event close dialog when click outside + this.toastDialog.addEventListener('click', (e) => { + const dialogDimensions = this.toastDialog.getBoundingClientRect(); + if ( + e.clientX < dialogDimensions.left || + e.clientX > dialogDimensions.right || + e.clientY < dialogDimensions.top || + e.clientY > dialogDimensions.bottom + ) { + this.toastDialog.close(); + } + }); } /** diff --git a/javascript-practice/src/js/views/homeView.js b/javascript-practice/src/js/views/homeView.js new file mode 100644 index 0000000..0496b0b --- /dev/null +++ b/javascript-practice/src/js/views/homeView.js @@ -0,0 +1,90 @@ +import CommonView from './commonView'; + +export default class HomeView extends CommonView { + constructor() { + super(); + + this.tabs = document.querySelectorAll('.app__tab-item'); + this.allContent = document.querySelectorAll('.app__content-item'); + this.addTransactionBtn = document.getElementById('addTransaction'); + this.addBudgetBtn = document.getElementById('addBudget'); + this.dialogs = document.querySelectorAll('.dialog'); + this.categoryField = document.getElementById('selectCategory'); + this.closeIcon = document.querySelector('.close-icon'); + + this.budgetDialog = document.getElementById('budgetDialog'); + this.transactionDialog = document.getElementById('transactionDialog'); + this.categoryDialog = document.getElementById('categoryDialog'); + } + + /** + * Handle event when click on tabs + */ + handlerTabsTransfer() { + this.tabs.forEach((tab, index) => { + tab.addEventListener('click', () => { + this.removeActiveTab(); + tab.classList.add('active'); + + const line = document.querySelector('.app__line'); + + line.classList.toggle('right', line.classList.contains('left')); + line.classList.toggle('left', !line.classList.contains('right')); + + this.allContent.forEach((content) => { + content.classList.remove('active'); + }); + this.allContent[index].classList.add('active'); + }); + }); + } + + addCommonEventPage() { + // Add event close dialog when click outside + this.dialogs.forEach((dialog) => { + dialog.addEventListener('click', (e) => { + const dialogDimensions = dialog.getBoundingClientRect(); + if ( + e.clientX < dialogDimensions.left || + e.clientX > dialogDimensions.right || + e.clientY < dialogDimensions.top || + e.clientY > dialogDimensions.bottom + ) { + dialog.close(); + } + }); + }); + + this.addTransactionBtn.addEventListener('click', () => { + this.transactionDialog.showModal(); + }); + + this.addBudgetBtn.addEventListener('click', () => { + this.budgetDialog.showModal(); + }); + } + + addEventSelectCategoryDialog() { + this.categoryField.addEventListener('click', () => { + this.categoryDialog.showModal(); + }); + + this.closeIcon.addEventListener('click', () => { + this.categoryDialog.close(); + }); + } + + removeActiveTab() { + this.tabs.forEach((tab) => { + tab.classList.remove('active'); + }); + } + + toggleDialog() { + this.dialog.forEach((item) => { + if (item.classList.contains('active')) { + item.classList.remove('active'); + } + }); + } +} diff --git a/javascript-practice/src/js/views/index.js b/javascript-practice/src/js/views/index.js new file mode 100644 index 0000000..b7a43f5 --- /dev/null +++ b/javascript-practice/src/js/views/index.js @@ -0,0 +1,22 @@ +import RegisterView from './registerView'; +import LoginView from './loginView'; +import { getSubdirectoryURL } from '../helpers/helpers'; +import { URL } from '../constants/config'; +import HomeView from './homeView'; + +export default class View { + constructor() { + switch (getSubdirectoryURL()) { + case URL.LOGIN: + this.loginView = new LoginView(); + break; + case URL.REGISTER: + this.registerView = new RegisterView(); + break; + case URL.HOME: + this.homeView = new HomeView(); + break; + default: + } + } +} diff --git a/javascript-practice/src/js/views/loginView.js b/javascript-practice/src/js/views/loginView.js index 0733561..fb2800f 100644 --- a/javascript-practice/src/js/views/loginView.js +++ b/javascript-practice/src/js/views/loginView.js @@ -1,12 +1,13 @@ import CommonLoginRegisterView from './commonLoginRegisterView'; -import { MESSAGE, TYPE_POPUP, BTN_CONTENT } from '../constants/constant'; +import { TYPE_TOAST, BTN_CONTENT } from '../constants/config'; +import * as MESSAGE from '../constants/message'; export default class LoginView extends CommonLoginRegisterView { constructor() { super(); this.parentElement = document.querySelector('.form'); - this.loginPage = document.URL.includes('/login'); + this.dialog = document.querySelector('.toast'); } /** @@ -24,17 +25,17 @@ export default class LoginView extends CommonLoginRegisterView { } /** - * Implement error popup in site - * @param {string} content The content will show in error popup + * Implement error toast in site + * @param {string} content The content will show in error toast */ - initErrorPopup(error) { - const title = error.title ? error.title : MESSAGE.DEFAULT_TITLE_ERROR_POPUP; + initErrorToast(error) { + const title = error.title ? error.title : MESSAGE.DEFAULT_TITLE_ERROR_TOAST; const content = error.message ? error.message : error; - this.initPopupContent(TYPE_POPUP.error, title, content, BTN_CONTENT.GOT_IT); + this.initToastContent(TYPE_TOAST.error, title, content, BTN_CONTENT.GOT_IT); - // Show popup - this.tooglePopupForm(); + // Show toast + this.toastDialog.showModal(); } /** @@ -51,17 +52,18 @@ export default class LoginView extends CommonLoginRegisterView { /** * The action when submit form - * @param {Function} validateUser The function need to be set event + * @param {Function} loginUser The function need to be set event + * * @param {event} event The event target */ - async submitForm(validateUser, event) { + async submitForm(loginUser, event) { try { // Load spinner - this.toogleLoaderSpinner(); + this.toggleLoaderSpinner(); // Get data from form const userInput = this.getDataFromForm(event); // Check user exist - const results = await validateUser(userInput.email, userInput.password); + const results = await loginUser(userInput.email, userInput.password); if (results) { window.location.replace('/'); @@ -70,14 +72,10 @@ export default class LoginView extends CommonLoginRegisterView { } throw MESSAGE.ERROR_CREDENTIAL; } catch (error) { - // Show popup error - this.initErrorPopup(error); + // Show toast error + this.initErrorToast(error); } // Close spinner - this.toogleLoaderSpinner(); - } - - isLoginPage() { - return this.loginPage; + this.toggleLoaderSpinner(); } } diff --git a/javascript-practice/src/js/views/registerView.js b/javascript-practice/src/js/views/registerView.js index 7470dd2..327433d 100644 --- a/javascript-practice/src/js/views/registerView.js +++ b/javascript-practice/src/js/views/registerView.js @@ -1,4 +1,5 @@ -import { TYPE_POPUP, MESSAGE, BTN_CONTENT } from '../constants/constant'; +import { TYPE_TOAST, BTN_CONTENT } from '../constants/config'; +import * as MESSAGE from '../constants/message'; import CommonLoginRegisterView from './commonLoginRegisterView'; import User from '../models/user'; @@ -6,7 +7,7 @@ export default class RegisterView extends CommonLoginRegisterView { constructor() { super(); - this.registerPage = document.URL.includes('/register'); + this.dialog = document.querySelector('.toast'); } /** @@ -32,32 +33,32 @@ export default class RegisterView extends CommonLoginRegisterView { } /** - * Implement register success popup in site + * Implement register success toast in site */ - showRegisterSuccessPopup() { - const typePopup = TYPE_POPUP.success; + showRegisterSuccessToast() { + const typeToast = TYPE_TOAST.success; const title = 'Register Commpleted'; const content = 'Please login to continue!'; const btnContent = 'OK'; - this.initPopupContent(typePopup, title, content, btnContent); + this.initToastContent(typeToast, title, content, btnContent); - // Show popup - this.tooglePopupForm(); + // Show toast + this.toastDialog.showModal(); } /** - * Implement error popup in site - * @param {string} content The content will show in error popup + * Implement error toast in site + * @param {string} content The content will show in error toast */ - initErrorPopup(error) { - const title = error.title ? error.title : MESSAGE.DEFAULT_TITLE_ERROR_POPUP; + initErrorToast(error) { + const title = error.title ? error.title : MESSAGE.DEFAULT_TITLE_ERROR_TOAST; const content = error.message ? error.message : error; - this.initPopupContent(TYPE_POPUP.error, title, content, BTN_CONTENT.OK); + this.initToastContent(TYPE_TOAST.error, title, content, BTN_CONTENT.OK); - // Show popup - this.tooglePopupForm(); + // Show toast + this.toastDialog.showModal(); } /** @@ -75,7 +76,7 @@ export default class RegisterView extends CommonLoginRegisterView { async submitForm(checkExistUser, saveUser) { try { // Load spinner - this.toogleLoaderSpinner(); + this.toggleLoaderSpinner(); // Get data from form const user = this.getDataFromForm(); @@ -88,20 +89,16 @@ export default class RegisterView extends CommonLoginRegisterView { throw Error(MESSAGE.USER_EXIST_ERROR); } else { await saveUser(user); - // Show popup success - this.showRegisterSuccessPopup(); + // Show toast success + this.showRegisterSuccessToast(); } } } catch (error) { - // Show popup error - this.initErrorPopup(error); + // Show toast error + this.initErrorToast(error); } // Close spinner - this.toogleLoaderSpinner(); - } - - isRegisterPage() { - return this.registerPage; + this.toggleLoaderSpinner(); } } diff --git a/javascript-practice/src/js/views/view.js b/javascript-practice/src/js/views/view.js deleted file mode 100644 index 333d0a7..0000000 --- a/javascript-practice/src/js/views/view.js +++ /dev/null @@ -1,9 +0,0 @@ -import RegisterView from './registerView'; -import LoginView from './loginView'; - -export default class View { - constructor() { - this.registerView = new RegisterView(); - this.loginView = new LoginView(); - } -} diff --git a/javascript-practice/src/pages/index.html b/javascript-practice/src/pages/index.html index 125e10f..840adc0 100644 --- a/javascript-practice/src/pages/index.html +++ b/javascript-practice/src/pages/index.html @@ -299,7 +299,7 @@
-
+
diff --git a/javascript-practice/src/styles/pages/_index.scss b/javascript-practice/src/styles/pages/_index.scss index 868a1ad..0605974 100644 --- a/javascript-practice/src/styles/pages/_index.scss +++ b/javascript-practice/src/styles/pages/_index.scss @@ -109,12 +109,20 @@ &__line { position: absolute; top: 66px; - left: 150px; - width: 115px; height: 2px; background-color: $primary-color; border-radius: 10px; transition: all 0.3s ease-in-out; + + &.left { + left: 152px; + width: 116px; + } + + &.right { + left: 272px; + width: 200px; + } } }