Merge code from feat/javascript-practice branch

This commit is contained in:
2023-09-06 10:04:55 +07:00
13 changed files with 154 additions and 82 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -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' };
@@ -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));
}
}
}
@@ -50,3 +50,15 @@ export const createToken = () => {
}
return token;
};
export const convertModelToDataObject = (model) => {
const { id, ...data } = model;
return { id, data };
};
export const convertDataObjectToModel = (data) => {
const { id, ...object } = data;
return { id, ...object.data };
};
+1
View File
@@ -3,5 +3,6 @@ import App from './app';
// Sure that scripts called after DOM loaded
document.addEventListener('DOMContentLoaded', () => {
const myApp = new App();
myApp.start();
});
@@ -1,5 +1,6 @@
export default class User {
constructor({ email, password, walletName = '', accessToken = '' }) {
this.id = User.createIdUser();
this.email = email;
this.password = password;
this.walletName = walletName;
@@ -1,4 +1,8 @@
import { timeOutConnect } from '../helpers/helpers';
import {
convertDataObjectToModel,
convertModelToDataObject,
timeOutConnect,
} from '../helpers/helpers';
import FirebaseService from './firebaseService';
export default class CommonService {
@@ -7,32 +11,59 @@ export default class CommonService {
this.firebaseService = FirebaseService;
}
/**
* Connect to Firebase Databse
*/
connectToDb() {
this.firebaseService.reconnect();
}
async findKeyByProperty(property, value, path = this.defaultPath) {
async getDataFromProp(property, value, path = this.defaultPath) {
this.connectToDb();
const existUser = this.firebaseService.findKeyByPropery(
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;
}
async save(data, path = this.defaultPath) {
/**
* Save data on database
* @param {*} data The data wants to save on database
* @param {string} path The path of database
*/
async save(model) {
this.connectToDb();
const saveUser = this.firebaseService.save(data, path);
await timeOutConnect(saveUser);
const results = convertModelToDataObject(model);
const saveData = this.firebaseService.save(
results.data,
this.defaultPath + results.id,
);
await timeOutConnect(saveData);
}
/**
*
* @param {string} id The string of data object
* @param {string} path The path of database
* @returns {Object || null} Return the object if has, otherwise return null
*/
async getDataFromId(id, path = this.defaultPath) {
this.connectToDb();
const result = await this.firebaseService.getDataFromId(id, path);
const data = await timeOutConnect(result);
if (data) return data;
return null;
}
}
@@ -43,28 +43,30 @@ class FirebaseService {
}
/**
* Find the key of value by property in database
* Find id of value by property in database
* @param {string} path The path of database to be found
* @param {string} property The property of the value need to be found
* @param {value} value The value to compare in database
* @returns {Promise} Return the relsoves when find completed
*/
findKeyByPropery(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,
@@ -73,6 +75,12 @@ class FirebaseService {
});
}
/**
* Get data object from Id
* @param {string} id The id of data object
* @param {string} path The path of data save in database
* @returns {Promise} Return new Promise
*/
getDataFromId(id, path) {
return new Promise((resolve) => {
onValue(
@@ -1,5 +1,4 @@
import { createToken } from '../helpers/helpers';
import User from '../models/user';
import CommonService from './commonService';
import LocalStorageService from './localStorageService';
@@ -15,17 +14,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.findKeyByProperty('email', email);
this.save(user);
}
/**
@@ -34,10 +23,12 @@ 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;
}
return false;
}
@@ -47,23 +38,49 @@ 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);
if (id) {
const user = await this.getDataFromId(id);
return new User(user);
const result = await this.getDataFromProp('email', email);
if (result) {
return result;
}
return null;
}
async createTokenUser(user) {
const id = await this.getUserIdByEmail(user.email);
/**
* Validate user info
* @param {string} email The email user input
* @param {*} password The password user input
* @returns {boolean} Return true if match info on database, otherwise return false
*/
async validateUser(email, password) {
const user = await this.getUserByEmail(email);
// Check password
if (user && user.password === password) {
// Create token for user
await this.createTokenUser(email);
return true;
}
return false;
}
/**
* Create token for user on database
* @param {string} email The email user need to be create token
*/
async createTokenUser(email) {
const user = await this.getUserByEmail(email);
const newUserData = user;
// Add token to user object
const newUserData = new User(user);
newUserData.accessToken = createToken();
this.save(newUserData, this.defaultPath + id);
this.save(newUserData);
// Add access token to local storage
LocalStorageService.add('accessToken', newUserData.accessToken);
}
}
+24 -25
View File
@@ -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 Credential!';
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,35 +39,36 @@ export default class LoginView extends CommonLoginRegisterView {
/**
* Add event listener for form input
* @param {Function} handler 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);
});
}
async submitForm(getUserByEmail, createTokenUser) {
/**
* The action when submit form
* @param {Function} validateUser The function need to be set event
*/
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('/');
return;
}
if (results) {
window.location.replace('/');
return;
}
throw Error(CONSTANT.MESSAGE.ERROR_CREDENTIAL);
throw MESSAGE.ERROR_CREDENTIAL;
} catch (error) {
// Show popup error
this.initErrorPopup(error);
@@ -79,6 +78,6 @@ export default class LoginView extends CommonLoginRegisterView {
}
isLoginPage() {
return this.loginPage !== null;
return this.loginPage;
}
}
@@ -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');
}
/**
@@ -24,8 +24,10 @@ export default class RegisterView extends CommonLoginRegisterView {
if (this.validateForm(account)) {
const user = new User(account);
return user;
}
return null;
}
@@ -48,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();
@@ -84,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
@@ -101,6 +102,6 @@ export default class RegisterView extends CommonLoginRegisterView {
}
isRegisterPage() {
return this.registerPage !== null;
return this.registerPage;
}
}
+1 -1
View File
@@ -7,7 +7,7 @@
<title>Login - Money Lover</title>
</head>
<body>
<main id="loginPage">
<main>
<div class="background">
<div class="wrapper">
<img
+1 -1
View File
@@ -7,7 +7,7 @@
<title>Register - Money Lover</title>
</head>
<body>
<main id="registerPage">
<main>
<div class="background">
<div class="wrapper">
<img