diff --git a/javascript-practice/src/js/helpers/helpers.js b/javascript-practice/src/js/helpers/helpers.js
index 54b8fd5..97b5146 100644
--- a/javascript-practice/src/js/helpers/helpers.js
+++ b/javascript-practice/src/js/helpers/helpers.js
@@ -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);
};
/**
diff --git a/javascript-practice/src/js/services/service.js b/javascript-practice/src/js/services/index.js
similarity index 100%
rename from javascript-practice/src/js/services/service.js
rename to javascript-practice/src/js/services/index.js
diff --git a/javascript-practice/src/js/services/userService.js b/javascript-practice/src/js/services/userService.js
index 9821641..75698e1 100644
--- a/javascript-practice/src/js/services/userService.js
+++ b/javascript-practice/src/js/services/userService.js
@@ -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
diff --git a/javascript-practice/src/js/views/commonLoginRegisterView.js b/javascript-practice/src/js/views/commonLoginRegisterView.js
index 1e5dcf3..5003978 100644
--- a/javascript-practice/src/js/views/commonLoginRegisterView.js
+++ b/javascript-practice/src/js/views/commonLoginRegisterView.js
@@ -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');
}
/**
diff --git a/javascript-practice/src/js/views/commonView.js b/javascript-practice/src/js/views/commonView.js
index 083f01f..f329ae3 100644
--- a/javascript-practice/src/js/views/commonView.js
+++ b/javascript-practice/src/js/views/commonView.js
@@ -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 = '
';
-
- 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}
-
+
`;
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();
+ }
+ });
}
/**
diff --git a/javascript-practice/src/js/views/homeView.js b/javascript-practice/src/js/views/homeView.js
new file mode 100644
index 0000000..0496b0b
--- /dev/null
+++ b/javascript-practice/src/js/views/homeView.js
@@ -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');
+ }
+ });
+ }
+}
diff --git a/javascript-practice/src/js/views/index.js b/javascript-practice/src/js/views/index.js
new file mode 100644
index 0000000..b7a43f5
--- /dev/null
+++ b/javascript-practice/src/js/views/index.js
@@ -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:
+ }
+ }
+}
diff --git a/javascript-practice/src/js/views/loginView.js b/javascript-practice/src/js/views/loginView.js
index 0733561..fb2800f 100644
--- a/javascript-practice/src/js/views/loginView.js
+++ b/javascript-practice/src/js/views/loginView.js
@@ -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();
}
}
diff --git a/javascript-practice/src/js/views/registerView.js b/javascript-practice/src/js/views/registerView.js
index 7470dd2..327433d 100644
--- a/javascript-practice/src/js/views/registerView.js
+++ b/javascript-practice/src/js/views/registerView.js
@@ -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();
}
}
diff --git a/javascript-practice/src/js/views/view.js b/javascript-practice/src/js/views/view.js
deleted file mode 100644
index 333d0a7..0000000
--- a/javascript-practice/src/js/views/view.js
+++ /dev/null
@@ -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();
- }
-}
diff --git a/javascript-practice/src/pages/index.html b/javascript-practice/src/pages/index.html
index 125e10f..840adc0 100644
--- a/javascript-practice/src/pages/index.html
+++ b/javascript-practice/src/pages/index.html
@@ -299,7 +299,7 @@
-
+
diff --git a/javascript-practice/src/styles/pages/_index.scss b/javascript-practice/src/styles/pages/_index.scss
index 868a1ad..0605974 100644
--- a/javascript-practice/src/styles/pages/_index.scss
+++ b/javascript-practice/src/styles/pages/_index.scss
@@ -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;
+ }
}
}