mirror of
https://github.com/Nezumi-2711/javascript-training.git
synced 2026-09-22 13:38:43 +00:00
Merge pull request #20 from Nez27/feat/refactor-services-views
Refactor services, views and fix typo in project.
This commit is contained in:
@@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 = '<div class="overlay"></div>';
|
||||
|
||||
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}
|
||||
<div class="modal-box">
|
||||
<div class="mark"></div>
|
||||
<h2 class="modal-box__title"></h2>
|
||||
<p class="modal-box__message"></p>
|
||||
|
||||
<button class="modal-box__redirect-btn"></button>
|
||||
</div>
|
||||
<dialog class="dialog">
|
||||
<div class="toast">
|
||||
<div class="mark"></div>
|
||||
<h2 class="toast__title"></h2>
|
||||
<p class="toast__message"></p>
|
||||
<button class="toast__redirect-btn">OK</button>
|
||||
</div>
|
||||
</dialog>
|
||||
`;
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -299,7 +299,7 @@
|
||||
<div class="app__tabs">
|
||||
<button class="app__tab-item active">SUMMARY</button>
|
||||
<button class="app__tab-item">TRANSACTION DETAILS</button>
|
||||
<div class="app__line"></div>
|
||||
<div class="app__line left"></div>
|
||||
</div>
|
||||
<!-- Start content app -->
|
||||
<div class="app__contents">
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user