mirror of
https://github.com/Nezumi-2711/javascript-training.git
synced 2026-09-22 20:01:31 +00:00
Add wallet method
This commit is contained in:
@@ -10,6 +10,10 @@ export const MESSAGE = {
|
|||||||
},
|
},
|
||||||
DEFAULT_TITLE_ERROR_POPUP: 'Error',
|
DEFAULT_TITLE_ERROR_POPUP: 'Error',
|
||||||
USER_EXIST_ERROR: 'User is exists! Please try another email!',
|
USER_EXIST_ERROR: 'User is exists! Please try another email!',
|
||||||
|
WELCOME: {
|
||||||
|
title: 'Login Successful!',
|
||||||
|
message: 'Welcome to Money Lover!',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const URL = {
|
export const URL = {
|
||||||
@@ -25,6 +29,7 @@ export const BTN_CONTENT = {
|
|||||||
|
|
||||||
export const LOCAL_STORAGE = {
|
export const LOCAL_STORAGE = {
|
||||||
ACCESS_TOKEN: 'accessToken',
|
ACCESS_TOKEN: 'accessToken',
|
||||||
|
IS_FIRST_LOGIN: 'isFirstLogin',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TYPE_POPUP = { success: 'success', error: 'error' };
|
export const TYPE_POPUP = { success: 'success', error: 'error' };
|
||||||
|
|||||||
@@ -4,11 +4,28 @@ export default class HomeController {
|
|||||||
this.homeView = view.homeView;
|
this.homeView = view.homeView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlerGetInfoUserLogin() {
|
||||||
|
return this.service.userService.getInfoUserLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerCheckWalletExist(idUser) {
|
||||||
|
return this.service.walletService.checkWalletExist(idUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerSaveWallet(wallet) {
|
||||||
|
return this.service.walletService.saveWallet(wallet);
|
||||||
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
if (this.homeView) {
|
if (this.homeView) {
|
||||||
this.homeView.handlerTabsTransfer();
|
this.homeView.addHandlerInputChangeWalletForm();
|
||||||
this.homeView.addCommonEventPage();
|
this.homeView.addHandlerSubmitWalletForm(
|
||||||
this.homeView.addEventSelectCategoryDialog();
|
this.handlerSaveWallet.bind(this),
|
||||||
|
);
|
||||||
|
this.homeView.loadPage(
|
||||||
|
this.handlerGetInfoUserLogin.bind(this),
|
||||||
|
this.handlerCheckWalletExist.bind(this),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,5 +70,10 @@ export const getSubdirectoryURL = () => {
|
|||||||
|
|
||||||
const subDirectory = parts[3]; // Get subdirectory url only
|
const subDirectory = parts[3]; // Get subdirectory url only
|
||||||
|
|
||||||
|
const index = subDirectory.indexOf('?');
|
||||||
|
if (index !== -1) {
|
||||||
|
return subDirectory.substring(0, index);
|
||||||
|
}
|
||||||
|
|
||||||
return subDirectory;
|
return subDirectory;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
export default class User {
|
export default class User {
|
||||||
constructor({ email, password, walletName = '', accessToken = '' }) {
|
constructor({ email, password, accessToken = '' }) {
|
||||||
this.id = User.createIdUser();
|
this.id = User.createIdUser();
|
||||||
this.email = email;
|
this.email = email;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
this.walletName = walletName;
|
|
||||||
this.accessToken = accessToken;
|
this.accessToken = accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
export default class Wallet {
|
||||||
|
constructor({
|
||||||
|
walletName = '',
|
||||||
|
amount = '',
|
||||||
|
inflow = 0,
|
||||||
|
outflow = 0,
|
||||||
|
idUser,
|
||||||
|
}) {
|
||||||
|
this.id = Wallet.createIdWallet();
|
||||||
|
this.walletName = walletName;
|
||||||
|
this.amout = amount;
|
||||||
|
this.inflow = inflow;
|
||||||
|
this.outflow = outflow;
|
||||||
|
this.idUser = idUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
static createIdWallet() {
|
||||||
|
return new Date().getTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,12 +20,12 @@ export default class CommonService {
|
|||||||
|
|
||||||
async getDataFromProp(property, value, path = this.defaultPath) {
|
async getDataFromProp(property, value, path = this.defaultPath) {
|
||||||
this.connectToDb();
|
this.connectToDb();
|
||||||
const existUser = this.firebaseService.getDataFromProp(
|
const dataExists = this.firebaseService.getDataFromProp(
|
||||||
path,
|
path,
|
||||||
property,
|
property,
|
||||||
value,
|
value,
|
||||||
);
|
);
|
||||||
const result = await timeOutConnect(existUser);
|
const result = await timeOutConnect(dataExists);
|
||||||
|
|
||||||
if (result.id && result.data) {
|
if (result.id && result.data) {
|
||||||
return convertDataObjectToModel(result);
|
return convertDataObjectToModel(result);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class LocalStorageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get(key) {
|
get(key) {
|
||||||
this.localStorage.getItem(key);
|
return this.localStorage.getItem(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(key) {
|
remove(key) {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import UserService from './userService';
|
import UserService from './userService';
|
||||||
|
import WalletService from './walletService';
|
||||||
|
|
||||||
export default class Service {
|
export default class Service {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.userService = new UserService();
|
this.userService = new UserService();
|
||||||
|
this.walletService = new WalletService();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,5 +86,41 @@ export default class UserService extends CommonService {
|
|||||||
LOCAL_STORAGE.ACCESS_TOKEN,
|
LOCAL_STORAGE.ACCESS_TOKEN,
|
||||||
newUserData.accessToken,
|
newUserData.accessToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
LocalStorageService.add(LOCAL_STORAGE.IS_FIRST_LOGIN, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getInfoUserLogin() {
|
||||||
|
// Find access token
|
||||||
|
const accessToken = LocalStorageService.get(LOCAL_STORAGE.ACCESS_TOKEN);
|
||||||
|
|
||||||
|
if (accessToken) {
|
||||||
|
const user = await this.getUserByToken(accessToken);
|
||||||
|
|
||||||
|
if (LocalStorageService.get(LOCAL_STORAGE.IS_FIRST_LOGIN)) {
|
||||||
|
user.isFirstLogin = true;
|
||||||
|
|
||||||
|
LocalStorageService.remove(LOCAL_STORAGE.IS_FIRST_LOGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user data from token access
|
||||||
|
* @param {string} accessToken Access token user
|
||||||
|
* @returns {Object || null} Return new User Object if find, otherwise return null.
|
||||||
|
*/
|
||||||
|
async getUserByToken(accessToken) {
|
||||||
|
const result = await this.getDataFromProp('accessToken', accessToken);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import CommonService from './commonService';
|
||||||
|
|
||||||
|
export default class WalletService extends CommonService {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.defaultPath = 'wallets/';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save wallet into database
|
||||||
|
* @param {Object} wallet The wallet object need to be saved into databae
|
||||||
|
*/
|
||||||
|
async saveWallet(wallet) {
|
||||||
|
await this.save(wallet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check wallet exist in database
|
||||||
|
* @param {string} idUser The id user to find user's wallet
|
||||||
|
* @returns {boolean} Return true if find, otherwise return false
|
||||||
|
*/
|
||||||
|
async checkWalletExist(idUser) {
|
||||||
|
const walletExist = await this.getWalletByIdUser(idUser);
|
||||||
|
|
||||||
|
if (walletExist) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get wallet data from idUser
|
||||||
|
* @param {string} email The id user to find user's wallet
|
||||||
|
* @returns {Object || null} Return new Wallet Object if find, otherwise return null.
|
||||||
|
*/
|
||||||
|
async getWalletByIdUser(idUser) {
|
||||||
|
const result = await this.getDataFromProp('idUser', idUser);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ export default class CommonLoginRegisterView extends CommonView {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this.handleEventBtnPopupAndOverlay();
|
||||||
|
|
||||||
this.parentElement = document.querySelector('.form');
|
this.parentElement = document.querySelector('.form');
|
||||||
this.messageDefault = CONSTANT.MESSAGE.ERROR_MESSAGE_DEFAULT;
|
this.messageDefault = CONSTANT.MESSAGE.ERROR_MESSAGE_DEFAULT;
|
||||||
this.inputPassword = document.querySelector('input[name="password"]');
|
this.inputPassword = document.querySelector('input[name="password"]');
|
||||||
@@ -84,4 +86,16 @@ export default class CommonLoginRegisterView extends CommonView {
|
|||||||
.querySelector('.form__title')
|
.querySelector('.form__title')
|
||||||
.insertAdjacentHTML('afterend', markup);
|
.insertAdjacentHTML('afterend', markup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add event listener for popup and overlay
|
||||||
|
*/
|
||||||
|
handleEventBtnPopupAndOverlay() {
|
||||||
|
this.popupBtn.addEventListener('click', this.tooglePopupForm.bind(this));
|
||||||
|
this.overlay.addEventListener('click', this.toogleDialog.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
toogleDialog() {
|
||||||
|
this.dialog.classList.toggle('active');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ export default class CommonView {
|
|||||||
this.initPopup();
|
this.initPopup();
|
||||||
this.initElementPopup();
|
this.initElementPopup();
|
||||||
this.initLoader();
|
this.initLoader();
|
||||||
this.handleEventBtnPopupAndOverlay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,28 +90,6 @@ export default class CommonView {
|
|||||||
this.popupBtn.textContent = btnContent;
|
this.popupBtn.textContent = btnContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add event listener for popup and overlay
|
|
||||||
*/
|
|
||||||
handleEventBtnPopupAndOverlay() {
|
|
||||||
this.popupBtn.addEventListener('click', this.tooglePopupForm.bind(this));
|
|
||||||
this.overlay.addEventListener('click', this.toogleDialog.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
toogleDialog() {
|
|
||||||
this.overlay.classList.toggle('active');
|
|
||||||
if (this.dialog.length) {
|
|
||||||
// If it have more dialog
|
|
||||||
this.dialog.forEach((item) => {
|
|
||||||
if (item.classList.contains('active')) {
|
|
||||||
item.classList.remove('active');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.dialog.classList.toggle('active');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement loader screen
|
* Implement loader screen
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import { TYPE_POPUP, MESSAGE, BTN_CONTENT } from '../constants/constant';
|
||||||
import CommonView from './commonView';
|
import CommonView from './commonView';
|
||||||
|
import Wallet from '../models/wallet';
|
||||||
|
|
||||||
export default class HomeView extends CommonView {
|
export default class HomeView extends CommonView {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -11,6 +13,7 @@ export default class HomeView extends CommonView {
|
|||||||
this.overlay = document.querySelector('.overlay');
|
this.overlay = document.querySelector('.overlay');
|
||||||
this.darkOverlay = document.querySelector('.dark-overlay');
|
this.darkOverlay = document.querySelector('.dark-overlay');
|
||||||
this.dialog = document.querySelectorAll('.dialog');
|
this.dialog = document.querySelectorAll('.dialog');
|
||||||
|
this.saveBtn = document.querySelectorAll('.form__save-btn');
|
||||||
this.cancelBtn = document.querySelectorAll('.form__cancel-btn');
|
this.cancelBtn = document.querySelectorAll('.form__cancel-btn');
|
||||||
this.categoryField = document.getElementById('selectCategory');
|
this.categoryField = document.getElementById('selectCategory');
|
||||||
this.closeIcon = document.querySelector('.close-icon');
|
this.closeIcon = document.querySelector('.close-icon');
|
||||||
@@ -18,6 +21,110 @@ export default class HomeView extends CommonView {
|
|||||||
this.budgetForm = document.getElementById('budgetForm');
|
this.budgetForm = document.getElementById('budgetForm');
|
||||||
this.transactionForm = document.getElementById('transactionForm');
|
this.transactionForm = document.getElementById('transactionForm');
|
||||||
this.categoryForm = document.getElementById('categoryForm');
|
this.categoryForm = document.getElementById('categoryForm');
|
||||||
|
this.walletForm = document.getElementById('walletForm');
|
||||||
|
|
||||||
|
this.walletDialog = document.getElementById('walletDialog');
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadPage(getInfoUserLogin, checkWalletExist) {
|
||||||
|
const user = await getInfoUserLogin();
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
window.location.replace('/login');
|
||||||
|
} else {
|
||||||
|
this.user = user;
|
||||||
|
const walletExist = await checkWalletExist(user.id);
|
||||||
|
|
||||||
|
// Check user's wallet if have or not
|
||||||
|
if (!walletExist) {
|
||||||
|
// Show add wallet dialog
|
||||||
|
this.walletDialog.classList.add('active');
|
||||||
|
// If overlay is not exist on page
|
||||||
|
if (!this.overlay.classList.contains('active')) {
|
||||||
|
this.toggleActiveOverlay();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.loadEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addHandlerSubmitWalletForm(saveWallet) {
|
||||||
|
this.walletForm.addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.submitWalletForm(saveWallet);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitWalletForm(saveWallet) {
|
||||||
|
try {
|
||||||
|
const wallet = new Wallet({
|
||||||
|
walletName: this.walletName,
|
||||||
|
amount: this.amount,
|
||||||
|
idUser: this.user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
await saveWallet(wallet);
|
||||||
|
|
||||||
|
this.hideDialog();
|
||||||
|
this.showSuccessPopup('Add wallet success', 'Click ok to continue!');
|
||||||
|
this.loadEvent();
|
||||||
|
} catch (error) {
|
||||||
|
// Show popup error
|
||||||
|
this.initErrorPopup(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addHandlerInputChangeWalletForm() {
|
||||||
|
this.walletForm.addEventListener('input', (e) => {
|
||||||
|
const bodyDialog = e.target.closest('.dialog__body');
|
||||||
|
this.validateWalletForm(bodyDialog);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
validateWalletForm(bodyDialog) {
|
||||||
|
this.walletName = bodyDialog.querySelector('.form__input-text').value;
|
||||||
|
this.amount = bodyDialog.querySelector('.form__input-balance').value;
|
||||||
|
const saveBtn = bodyDialog.querySelector('.form__save-btn');
|
||||||
|
|
||||||
|
if (this.walletName.length >= 3 && this.amount.length >= 1) {
|
||||||
|
saveBtn.classList.add('active');
|
||||||
|
} else {
|
||||||
|
saveBtn.classList.remove('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showSuccessPopup(title, message) {
|
||||||
|
const typePopup = TYPE_POPUP.success;
|
||||||
|
const btnContent = BTN_CONTENT.OK;
|
||||||
|
|
||||||
|
this.initPopupContent(typePopup, title, message, btnContent);
|
||||||
|
|
||||||
|
// Show popup
|
||||||
|
this.tooglePopupForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement error popup in site
|
||||||
|
* @param {string} content The content will show in error popup
|
||||||
|
*/
|
||||||
|
initErrorPopup(error) {
|
||||||
|
const title = error.title ? error.title : MESSAGE.DEFAULT_TITLE_ERROR_POPUP;
|
||||||
|
const content = error.message ? error.message : error;
|
||||||
|
|
||||||
|
this.initPopupContent(TYPE_POPUP.error, title, content, BTN_CONTENT.GOT_IT);
|
||||||
|
|
||||||
|
// Show popup
|
||||||
|
this.tooglePopupForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------- HANDLER EVENT ------------------------------- */
|
||||||
|
|
||||||
|
loadEvent() {
|
||||||
|
this.addCommonEventPage();
|
||||||
|
this.handlerTabsTransfer();
|
||||||
|
this.addEventSelectCategoryDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,13 +162,14 @@ export default class HomeView extends CommonView {
|
|||||||
this.cancelBtn.forEach((item) => {
|
this.cancelBtn.forEach((item) => {
|
||||||
item.addEventListener('click', () => {
|
item.addEventListener('click', () => {
|
||||||
this.hideDialog();
|
this.hideDialog();
|
||||||
this.toggleActiveOverlay();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.overlay.addEventListener('click', () => {
|
this.overlay.addEventListener('click', () => {
|
||||||
this.hideDialog();
|
this.hideDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.popupBtn.addEventListener('click', this.tooglePopupForm.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
addEventSelectCategoryDialog() {
|
addEventSelectCategoryDialog() {
|
||||||
@@ -96,6 +204,7 @@ export default class HomeView extends CommonView {
|
|||||||
item.classList.remove('active');
|
item.classList.remove('active');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.toggleActiveOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleActiveOverlay() {
|
toggleActiveOverlay() {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<button class="modal-box__redirect-btn">OK</button>
|
<button class="modal-box__redirect-btn">OK</button>
|
||||||
</div> -->
|
</div> -->
|
||||||
<!-- Add a wallet form -->
|
<!-- Add a wallet form -->
|
||||||
<div class="dialog" id="walletForm">
|
<div class="dialog" id="walletDialog">
|
||||||
<div class="dialog__add-wallet">
|
<div class="dialog__add-wallet">
|
||||||
<!-- Dialog header -->
|
<!-- Dialog header -->
|
||||||
<div class="dialog__header">
|
<div class="dialog__header">
|
||||||
@@ -25,14 +25,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Dialog body -->
|
<!-- Dialog body -->
|
||||||
<div class="dialog__body">
|
<div class="dialog__body">
|
||||||
<form class="form">
|
<form class="form" id="walletForm">
|
||||||
<div class="form__input-field">
|
<div class="form__input-field">
|
||||||
<p class="form__label">Wallet name</p>
|
<p class="form__label">Wallet name</p>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form__input-text"
|
class="form__input-text"
|
||||||
placeholder="Your wallet name?"
|
placeholder="Your wallet name?"
|
||||||
name="wallet-name"
|
name="walletName"
|
||||||
|
required
|
||||||
|
minlength="3"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form__currency-balance">
|
<div class="form__currency-balance">
|
||||||
@@ -48,7 +50,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form__input-field">
|
<div class="form__input-field">
|
||||||
<p class="form__label">Initial Balance</p>
|
<p class="form__label">Initial Balance</p>
|
||||||
<input type="number" class="form__input-balance" value="0" />
|
<input
|
||||||
|
type="number"
|
||||||
|
class="form__input-balance"
|
||||||
|
name="amount"
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="form__save-btn">SAVE</button>
|
<button type="submit" class="form__save-btn">SAVE</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user