diff --git a/javascript-practice/src/js/constants/constant.js b/javascript-practice/src/js/constants/constant.js index e6a63f2..9a05204 100644 --- a/javascript-practice/src/js/constants/constant.js +++ b/javascript-practice/src/js/constants/constant.js @@ -4,7 +4,16 @@ export const MESSAGE = { 'Password must at least one uppercase, one lowercase letter, one number and one special character!', ERROR_MESSAGE_DEFAULT: 'Something went wrong!', TIME_OUT_ERROR: 'Connection time out! Please try again!', - ERROR_CREDENTIAL: 'Email or password not match! Please try again!', + ERROR_CREDENTIAL: { + title: 'Error Credential', + message: 'Email or password not match! Please try again!', + }, + DEFAULT_TITLE_ERROR_POPUP: 'Error', + USER_EXIST_ERROR: 'User is exists! Please try another email!', +}; +export const BTN_CONTENT = { + GOT_IT: 'Got it!', + OK: 'Ok', }; export const TYPE_POPUP = { success: 'success', error: 'error' }; export const MARK_ICON = { success: 'check', error: 'error' }; diff --git a/javascript-practice/src/js/controllers/loginController.js b/javascript-practice/src/js/controllers/loginController.js index a2ae10d..322b537 100644 --- a/javascript-practice/src/js/controllers/loginController.js +++ b/javascript-practice/src/js/controllers/loginController.js @@ -4,20 +4,13 @@ export default class LoginController { this.loginView = view.loginView; } - handlerGetUserByEmail(email) { - return this.service.userService.getUserByEmail(email); - } - - handlerCreateTokenUser(user) { - return this.service.userService.createTokenUser(user); + handlerValidateUser(email, password) { + return this.service.userService.validateUser(email, password); } init() { if (this.loginView.isLoginPage()) { - this.loginView.addHandlerForm( - this.handlerGetUserByEmail.bind(this), - this.handlerCreateTokenUser.bind(this), - ); + this.loginView.addHandlerForm(this.handlerValidateUser.bind(this)); } } } diff --git a/javascript-practice/src/js/helpers/helpers.js b/javascript-practice/src/js/helpers/helpers.js index f577ee1..c4300e9 100644 --- a/javascript-practice/src/js/helpers/helpers.js +++ b/javascript-practice/src/js/helpers/helpers.js @@ -50,3 +50,17 @@ export const createToken = () => { } return token; }; + +export const convertModelToDataObject = (model) => { + const { id, ...data } = model; + + return { id, data }; +}; + +export const convertDataObjectToModel = (data) => { + const model = data.data; + + model.id = data.id; + + return model; +}; diff --git a/javascript-practice/src/js/models/user.js b/javascript-practice/src/js/models/user.js index 3e6ab49..68e3962 100644 --- a/javascript-practice/src/js/models/user.js +++ b/javascript-practice/src/js/models/user.js @@ -1,5 +1,6 @@ export default class User { constructor({ email, password, walletName = '' }) { + this.id = User.createIdUser(); this.email = email; this.password = password; this.walletName = walletName; diff --git a/javascript-practice/src/js/services/commonService.js b/javascript-practice/src/js/services/commonService.js index ed37218..717d424 100644 --- a/javascript-practice/src/js/services/commonService.js +++ b/javascript-practice/src/js/services/commonService.js @@ -1,4 +1,8 @@ -import { timeOutConnect } from '../helpers/helpers'; +import { + convertDataObjectToModel, + convertModelToDataObject, + timeOutConnect, +} from '../helpers/helpers'; import FirebaseService from './firebaseService'; export default class CommonService { @@ -14,23 +18,20 @@ export default class CommonService { this.firebaseService.reconnect(); } - /** - * Find the id of data on database - * @param {string} property The property want to get value - * @param {string} value The value of property - * @param {path} path The path of datbase - * @returns {string} The id of data object - */ - async findIdByProperty(property, value, path = this.defaultPath) { + async getDataFromProp(property, value, path = this.defaultPath) { this.connectToDb(); - const existUser = this.firebaseService.findIdByProperty( + const existUser = this.firebaseService.getDataFromProp( path, property, value, ); const result = await timeOutConnect(existUser); - return result; + if (result.id && result.data) { + return convertDataObjectToModel(result); + } + + return null; } /** @@ -38,11 +39,16 @@ export default class CommonService { * @param {*} data The data wants to save on database * @param {string} path The path of database */ - async save(data, path = this.defaultPath) { + async save(model) { this.connectToDb(); - const saveUser = this.firebaseService.save(data, path); + const results = convertModelToDataObject(model); - await timeOutConnect(saveUser); + const saveData = this.firebaseService.save( + results.data, + this.defaultPath + results.id, + ); + + await timeOutConnect(saveData); } /** diff --git a/javascript-practice/src/js/services/firebaseService.js b/javascript-practice/src/js/services/firebaseService.js index 9cb2b09..80d2036 100644 --- a/javascript-practice/src/js/services/firebaseService.js +++ b/javascript-practice/src/js/services/firebaseService.js @@ -49,22 +49,24 @@ class FirebaseService { * @param {value} value The value to compare in database * @returns {Promise} Return the relsoves when find completed */ - findIdByProperty(path, property, value) { + getDataFromProp(path, property, value) { return new Promise((resolve) => { onValue( ref(this.db, path), (snapshot) => { - let result; + let id; + let data; // snapshot is a type of data by Firebase define snapshot.forEach((childSnapshot) => { - const data = childSnapshot.val(); + const dataTemp = childSnapshot.val(); - if (data[property] === value) { - result = childSnapshot.key; + if (dataTemp[property] === value) { + id = childSnapshot.key; + data = dataTemp; } }); - resolve(result); + resolve({ id, data }); }, { onlyOnce: true, diff --git a/javascript-practice/src/js/services/userService.js b/javascript-practice/src/js/services/userService.js index 8ebd00e..216bf9f 100644 --- a/javascript-practice/src/js/services/userService.js +++ b/javascript-practice/src/js/services/userService.js @@ -1,5 +1,4 @@ import { createToken } from '../helpers/helpers'; -import User from '../models/user'; import CommonService from './commonService'; export default class UserService extends CommonService { @@ -14,18 +13,7 @@ export default class UserService extends CommonService { * @param {Object} user The user object need to be saved into databae */ saveUser(user) { - const pathData = this.defaultPath + User.createIdUser(); - - this.save(user, pathData); - } - - /** - * Get user id by email - * @param {string} email Email need to be check - * @returns {Promise || number} Return id user when exist, otherwise will undefined - */ - getUserIdByEmail(email) { - return this.findIdByProperty('email', email); + this.save(user); } /** @@ -34,7 +22,7 @@ export default class UserService extends CommonService { * @returns {boolean} Return true if find, otherwise return false */ async checkUserExist(email) { - const userExist = await this.getUserIdByEmail(email); + const userExist = await this.getUserByEmail(email); if (userExist) { return true; @@ -49,24 +37,37 @@ export default class UserService extends CommonService { * @returns {Object || null} Return new User Object if find, otherwise return null. */ async getUserByEmail(email) { - const id = await this.getUserIdByEmail(email); + const result = await this.getDataFromProp('email', email); - if (id) { - const user = await this.getDataFromId(id); - - return new User(user); + if (result) { + return result; } return null; } - async createTokenUser(user) { - const id = await this.getUserIdByEmail(user.email); + async validateUser(email, password) { + const user = await this.getUserByEmail(email); + + if (user) { + // Check password + if (user.password === password) { + // Create token for user + await this.createTokenUser(email); + + return true; + } + return false; + } + return false; + } + + async createTokenUser(email) { + const user = await this.getUserByEmail(email); // Add token to user object - const newUserData = new User(user); + const newUserData = user; newUserData.accessToken = createToken(); - - this.save(newUserData, this.defaultPath + id); + this.save(newUserData); } } diff --git a/javascript-practice/src/js/views/loginView.js b/javascript-practice/src/js/views/loginView.js index ab97fce..0733561 100644 --- a/javascript-practice/src/js/views/loginView.js +++ b/javascript-practice/src/js/views/loginView.js @@ -1,21 +1,20 @@ import CommonLoginRegisterView from './commonLoginRegisterView'; -import * as CONSTANT from '../constants/constant'; +import { MESSAGE, TYPE_POPUP, BTN_CONTENT } from '../constants/constant'; export default class LoginView extends CommonLoginRegisterView { constructor() { super(); this.parentElement = document.querySelector('.form'); - this.loginPage = document.getElementById('loginPage'); + this.loginPage = document.URL.includes('/login'); } /** * Get data from user input * @returns {Object || null} Return object or null */ - getDataFromForm() { - const { loginForm } = document.forms; - const formData = new FormData(loginForm); + getDataFromForm(event) { + const formData = new FormData(event.target); const email = formData.get('email'); const password = formData.get('password'); @@ -28,12 +27,11 @@ export default class LoginView extends CommonLoginRegisterView { * Implement error popup in site * @param {string} content The content will show in error popup */ - initErrorPopup(content) { - const typePopup = CONSTANT.TYPE_POPUP.error; - const title = 'Error!'; - const btnContent = 'Got it!'; + initErrorPopup(error) { + const title = error.title ? error.title : MESSAGE.DEFAULT_TITLE_ERROR_POPUP; + const content = error.message ? error.message : error; - this.initPopupContent(typePopup, title, content, btnContent); + this.initPopupContent(TYPE_POPUP.error, title, content, BTN_CONTENT.GOT_IT); // Show popup this.tooglePopupForm(); @@ -41,42 +39,36 @@ export default class LoginView extends CommonLoginRegisterView { /** * Add event listener for form input - * @param {Function} getUserByEmail The function need to be set event - * @param {Function} createTokenUser The function need to be set event + * @param {Function} validateUser The function need to be set event */ - addHandlerForm(getUserByEmail, createTokenUser) { + addHandlerForm(validateUser) { this.parentElement.addEventListener('submit', (e) => { e.preventDefault(); this.clearErrorMessage(); - this.submitForm(getUserByEmail, createTokenUser); + this.submitForm(validateUser, e); }); } /** * The action when submit form - * @param {Function} getUserByEmail The function need to be set event - * @param {Function} createTokenUser The function need to be set event + * @param {Function} validateUser The function need to be set event */ - async submitForm(getUserByEmail, createTokenUser) { + async submitForm(validateUser, event) { try { // Load spinner this.toogleLoaderSpinner(); // Get data from form - const userInput = this.getDataFromForm(); + const userInput = this.getDataFromForm(event); // Check user exist - const user = await getUserByEmail(userInput.email); + const results = await validateUser(userInput.email, userInput.password); - if (user) { - // If user exist, compare password - if (userInput.password === user.password) { - await createTokenUser(user); - window.location.replace('/'); + if (results) { + window.location.replace('/'); - return; - } + return; } - throw Error(CONSTANT.MESSAGE.ERROR_CREDENTIAL); + throw MESSAGE.ERROR_CREDENTIAL; } catch (error) { // Show popup error this.initErrorPopup(error); @@ -86,6 +78,6 @@ export default class LoginView extends CommonLoginRegisterView { } isLoginPage() { - return this.loginPage !== null; + return this.loginPage; } } diff --git a/javascript-practice/src/js/views/registerView.js b/javascript-practice/src/js/views/registerView.js index b0fb0c0..7470dd2 100644 --- a/javascript-practice/src/js/views/registerView.js +++ b/javascript-practice/src/js/views/registerView.js @@ -1,4 +1,4 @@ -import { TYPE_POPUP } from '../constants/constant'; +import { TYPE_POPUP, MESSAGE, BTN_CONTENT } from '../constants/constant'; import CommonLoginRegisterView from './commonLoginRegisterView'; import User from '../models/user'; @@ -6,7 +6,7 @@ export default class RegisterView extends CommonLoginRegisterView { constructor() { super(); - this.registerPage = document.getElementById('registerPage'); + this.registerPage = document.URL.includes('/register'); } /** @@ -50,12 +50,11 @@ export default class RegisterView extends CommonLoginRegisterView { * Implement error popup in site * @param {string} content The content will show in error popup */ - initErrorPopup(content) { - const typePopup = TYPE_POPUP.error; - const title = 'Error'; - const btnContent = 'Got it!'; + initErrorPopup(error) { + const title = error.title ? error.title : MESSAGE.DEFAULT_TITLE_ERROR_POPUP; + const content = error.message ? error.message : error; - this.initPopupContent(typePopup, title, content, btnContent); + this.initPopupContent(TYPE_POPUP.error, title, content, BTN_CONTENT.OK); // Show popup this.tooglePopupForm(); @@ -86,7 +85,7 @@ export default class RegisterView extends CommonLoginRegisterView { // Check user exist const userExist = await checkExistUser(user.email); if (userExist) { - throw Error('User is exists! Please try another email!'); + throw Error(MESSAGE.USER_EXIST_ERROR); } else { await saveUser(user); // Show popup success @@ -103,6 +102,6 @@ export default class RegisterView extends CommonLoginRegisterView { } isRegisterPage() { - return this.registerPage !== null; + return this.registerPage; } } diff --git a/javascript-practice/src/pages/login.html b/javascript-practice/src/pages/login.html index c502e11..bef7925 100644 --- a/javascript-practice/src/pages/login.html +++ b/javascript-practice/src/pages/login.html @@ -7,7 +7,7 @@ Login - Money Lover -
+
Register - Money Lover -
+