Merge pull request #20 from Nez27/feat/refactor-services-views

Refactor services, views and fix typo in project.
This commit is contained in:
Loi Phan
2023-09-12 17:14:37 +07:00
committed by GitHub
12 changed files with 246 additions and 131 deletions
@@ -1,7 +1,6 @@
import * as MESSAGE from '../constants/message'; 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 FirebaseService from '../services/firebaseService';
import { REGEX_PASSWORD } from '../constants/variable';
/** /**
* Validate password * Validate password
@@ -9,7 +8,7 @@ import { REGEX_PASSWORD } from '../constants/variable';
* @returns {boolean} Return true if validate password success, otherwise return false * @returns {boolean} Return true if validate password success, otherwise return false
*/ */
export const validatePassword = (password) => { export const validatePassword = (password) => {
return REGEX_PASSWORD.test(password); return REGEX.PASSWORD.test(password);
}; };
/** /**
@@ -1,4 +1,4 @@
import { LOCAL_STORAGE } from '../constants/constant'; import { LOCAL_STORAGE } from '../constants/config';
import { createToken } from '../helpers/helpers'; import { createToken } from '../helpers/helpers';
import CommonService from './commonService'; import CommonService from './commonService';
import LocalStorageService from './localStorageService'; import LocalStorageService from './localStorageService';
@@ -54,7 +54,7 @@ export default class UserService extends CommonService {
* @param {*} password The password user input * @param {*} password The password user input
* @returns {boolean} Return true if match info on database, otherwise return false * @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); const user = await this.getUserByEmail(email);
// Check password // Check password
@@ -1,5 +1,5 @@
import CommonView from './commonView'; import CommonView from './commonView';
import * as CONSTANT from '../constants/constant'; import * as MESSAGE from '../constants/message';
import { validatePassword } from '../helpers/helpers'; import { validatePassword } from '../helpers/helpers';
export default class CommonLoginRegisterView extends CommonView { export default class CommonLoginRegisterView extends CommonView {
@@ -7,7 +7,7 @@ export default class CommonLoginRegisterView extends CommonView {
super(); super();
this.parentElement = document.querySelector('.form'); 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.inputPassword = document.querySelector('input[name="password"]');
this.inputPasswordConfirm = document.querySelector( this.inputPasswordConfirm = document.querySelector(
'input[name="password_confirm"]', 'input[name="password_confirm"]',
@@ -24,17 +24,17 @@ export default class CommonLoginRegisterView extends CommonView {
if (validatePassword(account.passwordConfirm)) { if (validatePassword(account.passwordConfirm)) {
return true; return true;
} }
this.showError(CONSTANT.MESSAGE.PASSWORD_NOT_STRONG); this.showError(MESSAGE.PASSWORD_NOT_STRONG);
return false; return false;
} }
this.showError(CONSTANT.MESSAGE.PASSWORD_NOT_MATCH); this.showError(MESSAGE.PASSWORD_NOT_MATCH);
return false; return false;
} }
/** /**
* Show or hide style error input password * Show or hide style error input password
*/ */
toogleErrorStyleInputPass() { toggleErrorStyleInputPass() {
this.inputPassword.classList.toggle('error-input'); this.inputPassword.classList.toggle('error-input');
this.inputPasswordConfirm.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 have error message on page, remove it with style error input password
if (this.errorMessageEl) { if (this.errorMessageEl) {
this.errorMessageEl.remove(); this.errorMessageEl.remove();
this.toogleErrorStyleInputPass(); this.toggleErrorStyleInputPass();
} }
} }
@@ -68,7 +68,11 @@ export default class CommonLoginRegisterView extends CommonView {
*/ */
showError(message) { showError(message) {
this.renderError(message); this.renderError(message);
this.toogleErrorStyleInputPass(); this.toggleErrorStyleInputPass();
}
toggleDialog() {
this.dialog.classList.toggle('active');
} }
/** /**
+68 -62
View File
@@ -1,102 +1,108 @@
import { MARK_ICON, TYPE_POPUP } from '../constants/constant'; import { MARK_ICON, TYPE_TOAST } from '../constants/config';
export default class CommonView { export default class CommonView {
constructor() { constructor() {
this.overlayMarkup = '<div class="overlay"></div>'; this.initToast();
this.initElementToast();
this.initPopup();
this.initElementPopup();
this.initLoader(); this.initLoader();
this.handleEventBtnPopupAndOverlay(); this.handleEventToast();
} }
/** /**
* Implement popup in site * Implement toast in site
*/ */
initPopup() { initToast() {
this.rootElement = document.querySelector('body'); this.rootElement = document.querySelector('body');
const markup = ` const markup = `
${this.overlayMarkup} <dialog class="dialog">
<div class="modal-box"> <div class="toast">
<div class="mark"></div> <div class="mark"></div>
<h2 class="modal-box__title"></h2> <h2 class="toast__title"></h2>
<p class="modal-box__message"></p> <p class="toast__message"></p>
<button class="toast__redirect-btn">OK</button>
<button class="modal-box__redirect-btn"></button> </div>
</div> </dialog>
`; `;
this.rootElement.insertAdjacentHTML('afterbegin', markup); this.rootElement.insertAdjacentHTML('afterbegin', markup);
} }
/** /**
* Assgign element in popup to property * Assign element in toast to property
*/ */
initElementPopup() { initElementToast() {
this.modalBox = document.querySelector('.modal-box'); this.toastDialog = document.querySelector('.dialog');
this.popupIcon = document.querySelector('.mark'); this.toast = document.querySelector('.toast');
this.popupBtn = document.querySelector('.modal-box__redirect-btn'); this.toastIcon = this.toast.querySelector('.mark');
this.popupTitle = document.querySelector('.modal-box__title'); this.toastBtn = this.toast.querySelector('.toast__redirect-btn');
this.popupContent = document.querySelector('.modal-box__message'); this.toastTitle = this.toast.querySelector('.toast__title');
this.overlay = document.querySelector('.overlay'); this.toastContent = this.toast.querySelector('.toast__message');
} }
/** /**
* Show or hide loader screen * Show or hide loader screen
*/ */
toogleLoaderSpinner() { toggleLoaderSpinner() {
this.spinner.classList.toggle('hidden'); this.spinner.classList.toggle('hidden');
} }
/** /**
* Show or hide popup * Add toast content
*/ * @param {TYPE_TOAST} typeToast Type of the toast
tooglePopupForm() { * @param {string} title Title of toast
this.overlay.classList.toggle('active'); * @param {string} content Content of toast
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
* @param {string} btnContent Content of button * @param {string} btnContent Content of button
*/ */
initPopupContent(typePopup, title, content, btnContent) { initToastContent(typeToast, title, content, btnContent) {
// Remove old typePopup class if haved // Remove old typeToast class if haved
this.modalBox.classList.forEach((classItem) => Object.keys(TYPE_TOAST).forEach((value) => {
classItem === TYPE_POPUP.success || classItem === TYPE_POPUP.error CommonView.removeClassElement(value, this.toast);
? this.modalBox.classList.remove(classItem) });
: '',
);
// Remove old icon popup if haved // Remove old icon toast if haved
this.popupIcon.classList.forEach((classItem) => Object.keys(MARK_ICON).forEach((value) => {
classItem === MARK_ICON.success || classItem === MARK_ICON.error CommonView.removeClassElement(value, this.toastIcon);
? this.popupIcon.classList.remove(classItem) });
: '',
);
// Init content popup // Init content toast
this.modalBox.classList.add( this.toast.classList.add(
typePopup === TYPE_POPUP.success ? TYPE_POPUP.success : TYPE_POPUP.error, typeToast === TYPE_TOAST.success ? TYPE_TOAST.success : TYPE_TOAST.error,
); );
this.popupIcon.classList.add( this.toastIcon.classList.add(
typePopup === TYPE_POPUP.success ? MARK_ICON.success : MARK_ICON.error, typeToast === TYPE_TOAST.success ? MARK_ICON.success : MARK_ICON.error,
); );
this.popupTitle.textContent = title; this.toastTitle.textContent = title;
this.popupContent.textContent = content; this.toastContent.textContent = content;
this.popupBtn.textContent = btnContent; 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() { handleEventToast() {
this.popupBtn.addEventListener('click', this.tooglePopupForm.bind(this)); this.toastBtn.addEventListener('click', () => {
this.overlay.addEventListener('click', this.tooglePopupForm.bind(this)); 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();
}
});
} }
/** /**
@@ -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');
}
});
}
}
+22
View File
@@ -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:
}
}
}
+18 -20
View File
@@ -1,12 +1,13 @@
import CommonLoginRegisterView from './commonLoginRegisterView'; 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 { export default class LoginView extends CommonLoginRegisterView {
constructor() { constructor() {
super(); super();
this.parentElement = document.querySelector('.form'); 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 * Implement error toast in site
* @param {string} content The content will show in error popup * @param {string} content The content will show in error toast
*/ */
initErrorPopup(error) { initErrorToast(error) {
const title = error.title ? error.title : MESSAGE.DEFAULT_TITLE_ERROR_POPUP; const title = error.title ? error.title : MESSAGE.DEFAULT_TITLE_ERROR_TOAST;
const content = error.message ? error.message : error; 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 // Show toast
this.tooglePopupForm(); this.toastDialog.showModal();
} }
/** /**
@@ -51,17 +52,18 @@ export default class LoginView extends CommonLoginRegisterView {
/** /**
* The action when submit form * 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 { try {
// Load spinner // Load spinner
this.toogleLoaderSpinner(); this.toggleLoaderSpinner();
// Get data from form // Get data from form
const userInput = this.getDataFromForm(event); const userInput = this.getDataFromForm(event);
// Check user exist // Check user exist
const results = await validateUser(userInput.email, userInput.password); const results = await loginUser(userInput.email, userInput.password);
if (results) { if (results) {
window.location.replace('/'); window.location.replace('/');
@@ -70,14 +72,10 @@ export default class LoginView extends CommonLoginRegisterView {
} }
throw MESSAGE.ERROR_CREDENTIAL; throw MESSAGE.ERROR_CREDENTIAL;
} catch (error) { } catch (error) {
// Show popup error // Show toast error
this.initErrorPopup(error); this.initErrorToast(error);
} }
// Close spinner // Close spinner
this.toogleLoaderSpinner(); this.toggleLoaderSpinner();
}
isLoginPage() {
return this.loginPage;
} }
} }
@@ -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 CommonLoginRegisterView from './commonLoginRegisterView';
import User from '../models/user'; import User from '../models/user';
@@ -6,7 +7,7 @@ export default class RegisterView extends CommonLoginRegisterView {
constructor() { constructor() {
super(); 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() { showRegisterSuccessToast() {
const typePopup = TYPE_POPUP.success; const typeToast = TYPE_TOAST.success;
const title = 'Register Commpleted'; const title = 'Register Commpleted';
const content = 'Please login to continue!'; const content = 'Please login to continue!';
const btnContent = 'OK'; const btnContent = 'OK';
this.initPopupContent(typePopup, title, content, btnContent); this.initToastContent(typeToast, title, content, btnContent);
// Show popup // Show toast
this.tooglePopupForm(); this.toastDialog.showModal();
} }
/** /**
* Implement error popup in site * Implement error toast in site
* @param {string} content The content will show in error popup * @param {string} content The content will show in error toast
*/ */
initErrorPopup(error) { initErrorToast(error) {
const title = error.title ? error.title : MESSAGE.DEFAULT_TITLE_ERROR_POPUP; const title = error.title ? error.title : MESSAGE.DEFAULT_TITLE_ERROR_TOAST;
const content = error.message ? error.message : error; 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 // Show toast
this.tooglePopupForm(); this.toastDialog.showModal();
} }
/** /**
@@ -75,7 +76,7 @@ export default class RegisterView extends CommonLoginRegisterView {
async submitForm(checkExistUser, saveUser) { async submitForm(checkExistUser, saveUser) {
try { try {
// Load spinner // Load spinner
this.toogleLoaderSpinner(); this.toggleLoaderSpinner();
// Get data from form // Get data from form
const user = this.getDataFromForm(); const user = this.getDataFromForm();
@@ -88,20 +89,16 @@ export default class RegisterView extends CommonLoginRegisterView {
throw Error(MESSAGE.USER_EXIST_ERROR); throw Error(MESSAGE.USER_EXIST_ERROR);
} else { } else {
await saveUser(user); await saveUser(user);
// Show popup success // Show toast success
this.showRegisterSuccessPopup(); this.showRegisterSuccessToast();
} }
} }
} catch (error) { } catch (error) {
// Show popup error // Show toast error
this.initErrorPopup(error); this.initErrorToast(error);
} }
// Close spinner // Close spinner
this.toogleLoaderSpinner(); this.toggleLoaderSpinner();
}
isRegisterPage() {
return this.registerPage;
} }
} }
-9
View File
@@ -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();
}
}
+1 -1
View File
@@ -299,7 +299,7 @@
<div class="app__tabs"> <div class="app__tabs">
<button class="app__tab-item active">SUMMARY</button> <button class="app__tab-item active">SUMMARY</button>
<button class="app__tab-item">TRANSACTION DETAILS</button> <button class="app__tab-item">TRANSACTION DETAILS</button>
<div class="app__line"></div> <div class="app__line left"></div>
</div> </div>
<!-- Start content app --> <!-- Start content app -->
<div class="app__contents"> <div class="app__contents">
@@ -109,12 +109,20 @@
&__line { &__line {
position: absolute; position: absolute;
top: 66px; top: 66px;
left: 150px;
width: 115px;
height: 2px; height: 2px;
background-color: $primary-color; background-color: $primary-color;
border-radius: 10px; border-radius: 10px;
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
&.left {
left: 152px;
width: 116px;
}
&.right {
left: 272px;
width: 200px;
}
} }
} }