mirror of
https://github.com/Nezumi-2711/javascript-training.git
synced 2026-09-22 13:38:43 +00:00
Merge pull request #32 from Nez27/feat/fix-comments
Fix comments, refactor code.
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 690 B |
@@ -1,3 +1,4 @@
|
|||||||
|
import Transform from '../helpers/transform';
|
||||||
import UserService from '../services/userService';
|
import UserService from '../services/userService';
|
||||||
|
|
||||||
export default class HomeController {
|
export default class HomeController {
|
||||||
@@ -49,16 +50,13 @@ export default class HomeController {
|
|||||||
this.handlerSaveTransaction.bind(this),
|
this.handlerSaveTransaction.bind(this),
|
||||||
HomeController.handlerClearAccessToken.bind(this),
|
HomeController.handlerClearAccessToken.bind(this),
|
||||||
this.handlerDeleteTransaction.bind(this),
|
this.handlerDeleteTransaction.bind(this),
|
||||||
|
new Transform(),
|
||||||
);
|
);
|
||||||
this.homeView.addHandlerEventWalletForm();
|
|
||||||
|
|
||||||
this.homeView.loadPage();
|
this.homeView.loadPage();
|
||||||
|
|
||||||
this.homeView.addHandlerEventBudgetForm();
|
// Subscribe listener data update
|
||||||
|
this.homeView.subscribeListenerData();
|
||||||
this.homeView.handlerEventCategoryDialog();
|
|
||||||
|
|
||||||
this.homeView.handlerEventTransactionDialog();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { changeDateFormat } from './helpers';
|
||||||
|
|
||||||
|
export const createId = () => {
|
||||||
|
return new Date().getTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createTransactionDetailObject = (category, transactions) => {
|
||||||
|
const totalTransaction = transactions.length;
|
||||||
|
const totalAmount = () => {
|
||||||
|
let amount = 0;
|
||||||
|
|
||||||
|
transactions.forEach((transaction) => {
|
||||||
|
amount += transaction.amount;
|
||||||
|
});
|
||||||
|
|
||||||
|
return amount;
|
||||||
|
};
|
||||||
|
const listTransaction = () => {
|
||||||
|
const results = transactions.map((transaction) => {
|
||||||
|
const dateParts = changeDateFormat(transaction.date).split(','); // ['Monday', '14', 'September', '2023']
|
||||||
|
const day = dateParts[1];
|
||||||
|
const fullDateString = `${dateParts[0]}, ${dateParts[2]} ${dateParts[3]}`;
|
||||||
|
const tempData = {
|
||||||
|
id: transaction.id,
|
||||||
|
day,
|
||||||
|
fullDateString,
|
||||||
|
note: transaction.note,
|
||||||
|
amount: transaction.amount,
|
||||||
|
};
|
||||||
|
|
||||||
|
return tempData;
|
||||||
|
});
|
||||||
|
|
||||||
|
results.sort((a, b) => parseInt(b.id, 10) - parseInt(a.id, 10));
|
||||||
|
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
categoryName: category.name,
|
||||||
|
url: category.url,
|
||||||
|
totalTransaction,
|
||||||
|
totalAmount: totalAmount(),
|
||||||
|
transactions: listTransaction(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllCategoryNameInTransactions = (transactions) => {
|
||||||
|
const categoryName = new Set();
|
||||||
|
|
||||||
|
transactions.forEach((transaction) =>
|
||||||
|
categoryName.add(transaction.categoryName),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Array.from(categoryName);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllTransactionByCategoryName = (categoryName, transactions) => {
|
||||||
|
const results = transactions.filter((transaction) => {
|
||||||
|
return transaction.categoryName === categoryName;
|
||||||
|
});
|
||||||
|
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertModelToDataObject = (model) => {
|
||||||
|
const { id, ...data } = model;
|
||||||
|
|
||||||
|
return { id, data };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertDataObjectToModel = (data) => {
|
||||||
|
const { id, ...object } = data;
|
||||||
|
|
||||||
|
return { id, ...object.data };
|
||||||
|
};
|
||||||
@@ -1,24 +1,7 @@
|
|||||||
import * as MESSAGE from '../constants/message';
|
import * as MESSAGE from '../constants/message';
|
||||||
import { TIME_OUT_SEC, REGEX, DAY, MONTH } from '../constants/config';
|
import { TIME_OUT_SEC, DAY, MONTH } from '../constants/config';
|
||||||
import FirebaseService from '../services/firebaseService';
|
import FirebaseService from '../services/firebaseService';
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate password
|
|
||||||
* @param {string} password Password input
|
|
||||||
* @returns {boolean} Return true if validate password success, otherwise return false
|
|
||||||
*/
|
|
||||||
export const isValidPassword = (password) => {
|
|
||||||
return REGEX.PASSWORD.test(password);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isValidateEmail = (email) => {
|
|
||||||
return String(email).toLowerCase().match(REGEX.EMAIL);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const compare2Password = (password, passwordConfirm) => {
|
|
||||||
return password === passwordConfirm;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A waiting function with s second
|
* A waiting function with s second
|
||||||
* @param {number} s The time will be waiting
|
* @param {number} s The time will be waiting
|
||||||
@@ -59,18 +42,6 @@ export const createToken = () => {
|
|||||||
return token;
|
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 };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getSubdirectoryURL = () => {
|
export const getSubdirectoryURL = () => {
|
||||||
const url = window.location.href;
|
const url = window.location.href;
|
||||||
const parts = url.split('/'); // Results: ['http:', '', 'example.com', '']
|
const parts = url.split('/'); // Results: ['http:', '', 'example.com', '']
|
||||||
@@ -101,73 +72,6 @@ export const changeDateFormat = (oldFormatDate) => {
|
|||||||
return `${day}, ${date}, ${month}, ${year}`;
|
return `${day}, ${date}, ${month}, ${year}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createTransactionDetailObject = (category, transactions) => {
|
|
||||||
const totalTransaction = transactions.length;
|
|
||||||
const totalAmount = () => {
|
|
||||||
let amount = 0;
|
|
||||||
|
|
||||||
transactions.forEach((transaction) => {
|
|
||||||
amount += transaction.amount;
|
|
||||||
});
|
|
||||||
|
|
||||||
return amount;
|
|
||||||
};
|
|
||||||
const listTransaction = () => {
|
|
||||||
const results = transactions.map((transaction) => {
|
|
||||||
const dateParts = changeDateFormat(transaction.date).split(','); // ['Monday', '14', 'September', '2023']
|
|
||||||
const day = dateParts[1];
|
|
||||||
const fullDateString = `${dateParts[0]}, ${dateParts[2]} ${dateParts[3]}`;
|
|
||||||
const tempData = {
|
|
||||||
id: transaction.id,
|
|
||||||
day,
|
|
||||||
fullDateString,
|
|
||||||
note: transaction.note,
|
|
||||||
amount: transaction.amount,
|
|
||||||
};
|
|
||||||
|
|
||||||
return tempData;
|
|
||||||
});
|
|
||||||
|
|
||||||
results.sort((a, b) => parseInt(b.id, 10) - parseInt(a.id, 10));
|
|
||||||
|
|
||||||
return results;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
categoryName: category.name,
|
|
||||||
url: category.url,
|
|
||||||
totalTransaction,
|
|
||||||
totalAmount: totalAmount(),
|
|
||||||
transactions: listTransaction(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getAllCategoryNameInTransactions = (transactions) => {
|
|
||||||
const categoryName = new Set();
|
|
||||||
|
|
||||||
transactions.forEach((transaction) =>
|
|
||||||
categoryName.add(transaction.categoryName),
|
|
||||||
);
|
|
||||||
|
|
||||||
return Array.from(categoryName);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getAllTransactionByCategoryName = (categoryName, transactions) => {
|
|
||||||
const results = transactions.filter((transaction) => {
|
|
||||||
return transaction.categoryName === categoryName;
|
|
||||||
});
|
|
||||||
|
|
||||||
return results;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const renderRequiredText = (field, element) => {
|
|
||||||
const markup = `
|
|
||||||
<p class="error-text">${MESSAGE.REQUIRED_MESSAGE(field)}</p>
|
|
||||||
`;
|
|
||||||
|
|
||||||
element.insertAdjacentHTML('afterend', markup);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const redirectToLoginPage = () => {
|
export const redirectToLoginPage = () => {
|
||||||
window.location.replace('/login');
|
window.location.replace('/login');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
export default class Transform {
|
||||||
|
constructor() {
|
||||||
|
this.signal = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
onSendSignal(fromClass, value) {
|
||||||
|
Object.keys(this.signal).forEach((key) => {
|
||||||
|
const item = this.signal[key];
|
||||||
|
|
||||||
|
// Same receiver
|
||||||
|
if (item.name === fromClass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send signal
|
||||||
|
const { handler } = this.signal[key];
|
||||||
|
if (handler) {
|
||||||
|
handler(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
create(className, handler = null) {
|
||||||
|
this.signal[className] = { name: className, handler };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { REGEX } from '../constants/config';
|
||||||
|
import * as MESSAGE from '../constants/message';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate password
|
||||||
|
* @param {string} password Password input
|
||||||
|
* @returns {boolean} Return true if validate password success, otherwise return false
|
||||||
|
*/
|
||||||
|
export const isValidPassword = (password) => {
|
||||||
|
return REGEX.PASSWORD.test(password);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isValidateEmail = (email) => {
|
||||||
|
return String(email).toLowerCase().match(REGEX.EMAIL);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const compare2Password = (password, passwordConfirm) => {
|
||||||
|
return password === passwordConfirm;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const renderRequiredText = (field, element) => {
|
||||||
|
const markup = `
|
||||||
|
<p class="error-text">${MESSAGE.REQUIRED_MESSAGE(field)}</p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
element.insertAdjacentHTML('afterend', markup);
|
||||||
|
};
|
||||||
@@ -1,21 +1,12 @@
|
|||||||
|
import { createId } from '../helpers/dataProcess';
|
||||||
|
|
||||||
export default class Transaction {
|
export default class Transaction {
|
||||||
constructor({
|
constructor({ id, categoryName, date, note, amount = 0, idUser }) {
|
||||||
id = '',
|
this.id = !id ? createId() : id;
|
||||||
categoryName = '',
|
|
||||||
date = '',
|
|
||||||
note = '',
|
|
||||||
amount = 0,
|
|
||||||
idUser,
|
|
||||||
}) {
|
|
||||||
this.id = id === '' ? Transaction.createIdTransactions() : id;
|
|
||||||
this.categoryName = categoryName;
|
this.categoryName = categoryName;
|
||||||
this.date = date;
|
this.date = date;
|
||||||
this.note = note;
|
this.note = note;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
this.idUser = idUser;
|
this.idUser = idUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createIdTransactions() {
|
|
||||||
return new Date().getTime();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
|
import { createId } from '../helpers/dataProcess';
|
||||||
|
|
||||||
export default class User {
|
export default class User {
|
||||||
constructor({ email, password, accessToken = '' }) {
|
constructor({ email, password, accessToken = '' }) {
|
||||||
this.id = User.createIdUser();
|
this.id = createId();
|
||||||
this.email = email;
|
this.email = email;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
this.accessToken = accessToken;
|
this.accessToken = accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createIdUser() {
|
|
||||||
return new Date().getTime();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
|
import { createId } from '../helpers/dataProcess';
|
||||||
|
|
||||||
export default class Wallet {
|
export default class Wallet {
|
||||||
constructor({ walletName = '', inflow = 0, outflow = 0, idUser }) {
|
constructor({ walletName, inflow = 0, outflow = 0, idUser }) {
|
||||||
this.id = Wallet.createIdWallet();
|
this.id = createId();
|
||||||
this.walletName = walletName;
|
this.walletName = walletName;
|
||||||
this.inflow = inflow;
|
this.inflow = inflow;
|
||||||
this.outflow = outflow;
|
this.outflow = outflow;
|
||||||
this.idUser = idUser;
|
this.idUser = idUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createIdWallet() {
|
amountWallet() {
|
||||||
return new Date().getTime();
|
return +this.inflow + +this.outflow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import { timeOutConnect } from '../helpers/helpers';
|
||||||
import {
|
import {
|
||||||
convertDataObjectToModel,
|
convertDataObjectToModel,
|
||||||
convertModelToDataObject,
|
convertModelToDataObject,
|
||||||
timeOutConnect,
|
} from '../helpers/dataProcess';
|
||||||
} from '../helpers/helpers';
|
|
||||||
import FirebaseService from './firebaseService';
|
import FirebaseService from './firebaseService';
|
||||||
|
|
||||||
export default class CommonService {
|
export default class CommonService {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Wallet from '../models/wallet';
|
||||||
import CommonService from './commonService';
|
import CommonService from './commonService';
|
||||||
|
|
||||||
export default class WalletService extends CommonService {
|
export default class WalletService extends CommonService {
|
||||||
@@ -34,6 +35,10 @@ export default class WalletService extends CommonService {
|
|||||||
async getWalletByIdUser(idUser) {
|
async getWalletByIdUser(idUser) {
|
||||||
const result = await this.getDataFromProp('idUser', idUser);
|
const result = await this.getDataFromProp('idUser', idUser);
|
||||||
|
|
||||||
return result || null;
|
if (result) {
|
||||||
|
return new Wallet(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
isValidPassword,
|
isValidPassword,
|
||||||
isValidateEmail,
|
isValidateEmail,
|
||||||
renderRequiredText,
|
renderRequiredText,
|
||||||
} from '../helpers/helpers';
|
} from '../helpers/validateForm';
|
||||||
|
|
||||||
export default class AuthenticationView extends CommonView {
|
export default class AuthenticationView extends CommonView {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|||||||
@@ -0,0 +1,174 @@
|
|||||||
|
import { DEFAULT_CATEGORY } from '../constants/config';
|
||||||
|
import Transaction from '../models/transaction';
|
||||||
|
import * as MESSAGE from '../constants/message';
|
||||||
|
import { renderRequiredText } from '../helpers/validateForm';
|
||||||
|
|
||||||
|
class BudgetView {
|
||||||
|
constructor() {
|
||||||
|
this.budgetDialog = document.getElementById('budgetDialog');
|
||||||
|
this.addBudgetBtn = document.getElementById('addBudget');
|
||||||
|
|
||||||
|
this.handlerEventBudgetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
initFunction(
|
||||||
|
showErrorToast,
|
||||||
|
showSuccessToast,
|
||||||
|
toggleLoaderSpinner,
|
||||||
|
saveTransaction,
|
||||||
|
loadTransactionData,
|
||||||
|
updateAmountWallet,
|
||||||
|
loadData,
|
||||||
|
transform,
|
||||||
|
) {
|
||||||
|
this.showErrorToast = showErrorToast;
|
||||||
|
this.showSuccessToast = showSuccessToast;
|
||||||
|
this.toggleLoaderSpinner = toggleLoaderSpinner;
|
||||||
|
this.saveTransaction = saveTransaction;
|
||||||
|
this.loadTransactionData = loadTransactionData;
|
||||||
|
this.updateAmountWallet = updateAmountWallet;
|
||||||
|
this.loadData = loadData;
|
||||||
|
this.transform = transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe() {
|
||||||
|
this.transform.create('budgetView', this.updateData.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
sendData() {
|
||||||
|
const data = { wallet: this.wallet, listTransaction: this.listTransaction };
|
||||||
|
|
||||||
|
this.transform.onSendSignal('budgetView', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData(data) {
|
||||||
|
if (data.wallet) this.wallet = data.wallet;
|
||||||
|
|
||||||
|
if (data.listTransactions) this.listTransaction = data.listTransactions;
|
||||||
|
|
||||||
|
if (data.listCategory) this.listCategory = data.listCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
addHandlerEventBudgetForm() {
|
||||||
|
this.budgetDialog.addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.clearErrorStyleBudgetForm();
|
||||||
|
this.submitBudgetForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.budgetDialog.addEventListener('input', () => {
|
||||||
|
this.changeBtnStyle();
|
||||||
|
this.clearErrorStyleBudgetForm();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clearErrorStyleBudgetForm() {
|
||||||
|
// Clear error style
|
||||||
|
const inputFieldEls =
|
||||||
|
this.budgetDialog.querySelectorAll('.form__input-field');
|
||||||
|
const errorTexts = this.budgetDialog.querySelectorAll('.error-text');
|
||||||
|
|
||||||
|
inputFieldEls.forEach((item) => {
|
||||||
|
if (item.classList.contains('error-input'))
|
||||||
|
item.classList.remove('error-input');
|
||||||
|
});
|
||||||
|
|
||||||
|
errorTexts.forEach((item) => {
|
||||||
|
if (item) item.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitBudgetForm() {
|
||||||
|
try {
|
||||||
|
const { formAddBudget } = document.forms;
|
||||||
|
const form = new FormData(formAddBudget);
|
||||||
|
const date = form.get('selected_date');
|
||||||
|
const amount = form.get('amount');
|
||||||
|
const note = form.get('note');
|
||||||
|
|
||||||
|
// Validate data user input
|
||||||
|
|
||||||
|
if (this.validateBudgetForm(date, amount)) {
|
||||||
|
this.toggleLoaderSpinner(); // Enable loader spinner
|
||||||
|
this.budgetDialog.close(); // Close dialog
|
||||||
|
|
||||||
|
const transaction = new Transaction({
|
||||||
|
categoryName: DEFAULT_CATEGORY.INCOME,
|
||||||
|
date,
|
||||||
|
amount: +amount,
|
||||||
|
note,
|
||||||
|
idUser: this.wallet.idUser,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.saveTransaction(transaction);
|
||||||
|
|
||||||
|
// Reload data
|
||||||
|
await this.loadTransactionData();
|
||||||
|
await this.updateAmountWallet();
|
||||||
|
await this.loadData();
|
||||||
|
|
||||||
|
// Hide loader spinner
|
||||||
|
this.toggleLoaderSpinner();
|
||||||
|
|
||||||
|
// Show success message
|
||||||
|
this.showSuccessToast(
|
||||||
|
MESSAGE.ADD_TRANSACTION_SUCCESS,
|
||||||
|
MESSAGE.DEFAULT_MESSAGE,
|
||||||
|
);
|
||||||
|
|
||||||
|
document.getElementById('formAddBudget').reset();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.showErrorToast(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateBudgetForm(date, amount) {
|
||||||
|
const inputFieldEl =
|
||||||
|
this.budgetDialog.querySelectorAll('.form__input-field');
|
||||||
|
|
||||||
|
if (!date || !amount) {
|
||||||
|
if (!date) {
|
||||||
|
renderRequiredText('date', inputFieldEl[0]);
|
||||||
|
inputFieldEl[0].classList.add('error-input');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amount) {
|
||||||
|
renderRequiredText('amount', inputFieldEl[1]);
|
||||||
|
inputFieldEl[1].classList.add('error-input');
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
changeBtnStyle() {
|
||||||
|
const date = this.budgetDialog.querySelector('.input-date').value;
|
||||||
|
const amount = +this.budgetDialog.querySelector('.form__input-balance')
|
||||||
|
.value;
|
||||||
|
const saveBtn = this.budgetDialog.querySelector('.form__save-btn');
|
||||||
|
|
||||||
|
if (date.trim() && amount >= 1) {
|
||||||
|
saveBtn.classList.add('active');
|
||||||
|
} else {
|
||||||
|
saveBtn.classList.remove('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerEventBudgetView() {
|
||||||
|
this.addBudgetBtn.addEventListener('click', () => {
|
||||||
|
// Set default value for date input
|
||||||
|
this.budgetDialog.querySelector("[name='selected_date']").valueAsDate =
|
||||||
|
new Date();
|
||||||
|
|
||||||
|
this.budgetDialog.showModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addHandlerEventBudgetForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new BudgetView();
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import { REMOVE_CATEGORY } from '../constants/config';
|
||||||
|
|
||||||
|
class CategoryView {
|
||||||
|
constructor() {
|
||||||
|
this.categoryDialog = document.getElementById('categoryDialog');
|
||||||
|
this.categoryField = document.getElementById('selectCategory');
|
||||||
|
this.closeIcon = document.querySelector('.close-icon');
|
||||||
|
|
||||||
|
this.handlerEventCategoryDialog();
|
||||||
|
this.addEventSelectCategoryDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
initFunction(getAllCategory, transform) {
|
||||||
|
this.getAllCategory = getAllCategory;
|
||||||
|
this.transform = transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendData() {
|
||||||
|
const data = { listCategory: this.listCategory };
|
||||||
|
|
||||||
|
this.transform.onSendSignal('categoryView', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerEventCategoryDialog() {
|
||||||
|
this.categoryDialog.addEventListener('input', () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.searchCategory();
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load category data
|
||||||
|
* @param {function} getAllCategory Get all category function
|
||||||
|
*/
|
||||||
|
async loadCategory() {
|
||||||
|
if (!this.listCategory) {
|
||||||
|
this.listCategory = await this.getAllCategory();
|
||||||
|
|
||||||
|
this.sendData();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.listCategory) {
|
||||||
|
this.renderCategoryList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
searchCategory() {
|
||||||
|
const searchCategoryEl =
|
||||||
|
this.categoryDialog.querySelector("[name='category']");
|
||||||
|
const searchValue = searchCategoryEl.value.trim().toLowerCase();
|
||||||
|
let newListCategory = [];
|
||||||
|
|
||||||
|
if (searchValue) {
|
||||||
|
this.listCategory.forEach((category) => {
|
||||||
|
const categoryName = category.name.trim().toLowerCase();
|
||||||
|
|
||||||
|
if (categoryName.includes(searchValue)) {
|
||||||
|
newListCategory.unshift(category);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
newListCategory = this.listCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render category item
|
||||||
|
this.renderCategoryList(this.keySearchCategory, newListCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderCategoryList(categorySelected, listCategory = this.listCategory) {
|
||||||
|
// Remove category name unnecessary
|
||||||
|
const newListCategory = listCategory.filter(
|
||||||
|
(item) => !REMOVE_CATEGORY.includes(item.name),
|
||||||
|
);
|
||||||
|
|
||||||
|
const listCategoryEl = document.querySelector('.list-category');
|
||||||
|
|
||||||
|
listCategoryEl.innerHTML = ''; // Remove old category item
|
||||||
|
|
||||||
|
newListCategory.forEach((category) => {
|
||||||
|
const markup = `
|
||||||
|
<div class="category-item ${
|
||||||
|
category.name === categorySelected ? 'selected' : ''
|
||||||
|
}" data-value='${category.name}' data-url='${category.url}'>
|
||||||
|
<img
|
||||||
|
class="icon-category"
|
||||||
|
src="${category.url}"
|
||||||
|
alt="${category.name} Icon"
|
||||||
|
/>
|
||||||
|
<p class="name-category">${category.name}</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
listCategoryEl.insertAdjacentHTML('afterbegin', markup);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventSelectCategoryDialog() {
|
||||||
|
const categoryListEl = document.querySelector('.list-category');
|
||||||
|
const categoryIconEl = this.categoryField.querySelector('.category-icon');
|
||||||
|
const categoryNameEl = this.categoryField.querySelector('.category-name');
|
||||||
|
|
||||||
|
categoryListEl.addEventListener('click', (e) => {
|
||||||
|
const categoryItem = e.target.closest('.category-item');
|
||||||
|
|
||||||
|
if (categoryItem) {
|
||||||
|
const { url } = categoryItem.dataset;
|
||||||
|
const { value } = categoryItem.dataset;
|
||||||
|
|
||||||
|
// Set url and value into category field in transaction dialog
|
||||||
|
categoryIconEl.src = url;
|
||||||
|
categoryNameEl.value = value;
|
||||||
|
|
||||||
|
// Close select category dialog
|
||||||
|
this.categoryDialog.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pass value selected to category dialog
|
||||||
|
categoryNameEl.addEventListener('click', () => {
|
||||||
|
if (categoryNameEl.value) {
|
||||||
|
// Make the keyword search category name into global
|
||||||
|
this.keySearchCategory = categoryNameEl.value;
|
||||||
|
|
||||||
|
this.renderCategoryList(this.keySearchCategory);
|
||||||
|
}
|
||||||
|
this.categoryDialog.showModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.closeIcon.addEventListener('click', () => {
|
||||||
|
this.categoryDialog.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new CategoryView();
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
|||||||
|
import { formatNumber } from '../helpers/helpers';
|
||||||
|
|
||||||
|
class SummaryTabView {
|
||||||
|
initFunction(transform) {
|
||||||
|
this.transform = transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe() {
|
||||||
|
this.transform.create('summaryTabView', this.updateData.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData(data) {
|
||||||
|
if (data.wallet) this.wallet = data.wallet;
|
||||||
|
|
||||||
|
if (data.listTransactions) this.listTransaction = data.listTransactions;
|
||||||
|
|
||||||
|
if (data.listCategory) this.listCategory = data.listCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
load() {
|
||||||
|
const inflowValue = document.querySelector('.inflow__text--income');
|
||||||
|
const outflowValue = document.querySelector('.outflow__text--outcome');
|
||||||
|
const totalValue = document.querySelector('.summary__total');
|
||||||
|
|
||||||
|
const { inflow } = this.wallet;
|
||||||
|
const { outflow } = this.wallet;
|
||||||
|
const total = inflow + outflow;
|
||||||
|
|
||||||
|
inflowValue.textContent = `+$ ${formatNumber(inflow)}`;
|
||||||
|
outflowValue.textContent = `-$ ${formatNumber(Math.abs(outflow))}`;
|
||||||
|
totalValue.textContent = `${total >= 0 ? '+' : '-'}$ ${formatNumber(
|
||||||
|
Math.abs(total),
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new SummaryTabView();
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
import {
|
||||||
|
createTransactionDetailObject,
|
||||||
|
getAllCategoryNameInTransactions,
|
||||||
|
getAllTransactionByCategoryName,
|
||||||
|
} from '../helpers/dataProcess';
|
||||||
|
import { formatNumber } from '../helpers/helpers';
|
||||||
|
import TransactionView from './transactionView';
|
||||||
|
|
||||||
|
class TransactionTabView {
|
||||||
|
constructor() {
|
||||||
|
this.addEventTransactionItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
initFunction(transform) {
|
||||||
|
this.transform = transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe() {
|
||||||
|
this.transform.create('transactionTabView', this.updateData.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData(data) {
|
||||||
|
if (data.wallet) this.wallet = data.wallet;
|
||||||
|
|
||||||
|
if (data.listTransactions) this.listTransactions = data.listTransactions;
|
||||||
|
|
||||||
|
if (data.listCategory) this.listCategory = data.listCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadTransactionTab() {
|
||||||
|
// Load category
|
||||||
|
// Init data first
|
||||||
|
this.transactionDetails = this.loadTransactionDetailsData();
|
||||||
|
const transactionEl = document.querySelector('.transaction');
|
||||||
|
const listTransactionDetailEl =
|
||||||
|
transactionEl.querySelector('.transaction__list');
|
||||||
|
const markup = [];
|
||||||
|
if (this.transactionDetails) {
|
||||||
|
this.transactionDetails.forEach((transactionDetail) => {
|
||||||
|
markup.push(this.transactionDetailMarkup(transactionDetail));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
listTransactionDetailEl.innerHTML = '';
|
||||||
|
listTransactionDetailEl.insertAdjacentHTML('afterbegin', markup.join('\n'));
|
||||||
|
// Load event
|
||||||
|
this.addEventTransactionItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadTransactionDetailsData() {
|
||||||
|
// Get all category user have in transactions
|
||||||
|
const listCategoryInTransaction = getAllCategoryNameInTransactions(
|
||||||
|
this.listTransactions,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create transactions details object
|
||||||
|
const tempList = listCategoryInTransaction.map((categoryName) => {
|
||||||
|
// Get category object from list category has been loaded.
|
||||||
|
const category = this.listCategory.filter(
|
||||||
|
(item) => item.name === categoryName,
|
||||||
|
);
|
||||||
|
|
||||||
|
const transactions = getAllTransactionByCategoryName(
|
||||||
|
categoryName,
|
||||||
|
this.listTransactions,
|
||||||
|
);
|
||||||
|
|
||||||
|
return createTransactionDetailObject(
|
||||||
|
Object.assign({}, ...category),
|
||||||
|
transactions,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
tempList.sort(
|
||||||
|
(a, b) =>
|
||||||
|
parseInt(b.transactions[0].id, 10) - parseInt(a.transactions[0].id, 10),
|
||||||
|
);
|
||||||
|
|
||||||
|
return tempList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
transactionDetailMarkup(transactionDetail) {
|
||||||
|
const transactionTimeMarkup = () => {
|
||||||
|
const listMarkup = [];
|
||||||
|
transactionDetail.transactions.forEach((transaction) => {
|
||||||
|
const markup = `
|
||||||
|
<div class="transaction__time" data-id=${transaction.id}>
|
||||||
|
<div class="transaction__details">
|
||||||
|
<p class="transaction__day">${transaction.day}</p>
|
||||||
|
<div class="transaction__time-details">
|
||||||
|
<p class="transaction__date-time">
|
||||||
|
${transaction.fullDateString}
|
||||||
|
</p>
|
||||||
|
<p class="transaction__note">${
|
||||||
|
transaction.note === '' ? 'None' : transaction.note
|
||||||
|
}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="transaction__${
|
||||||
|
transaction.amount >= 0 ? 'income' : 'outcome'
|
||||||
|
}">${transaction.amount >= 0 ? '+' : '-'}$ ${formatNumber(
|
||||||
|
Math.abs(transaction.amount),
|
||||||
|
)}</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
listMarkup.push(markup);
|
||||||
|
});
|
||||||
|
|
||||||
|
return listMarkup.join('\n');
|
||||||
|
};
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="transaction__item">
|
||||||
|
<div class="transaction__category">
|
||||||
|
<div class="transaction__category-infor">
|
||||||
|
<div class="transaction__category-icon-container">
|
||||||
|
<img
|
||||||
|
class="icon-category"
|
||||||
|
src="${transactionDetail.url}"
|
||||||
|
alt="Transportation icon category"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="transaction__category-content">
|
||||||
|
<p class="transaction__category-name">
|
||||||
|
${transactionDetail.categoryName}
|
||||||
|
</p>
|
||||||
|
<p class="transaction__total">${
|
||||||
|
transactionDetail.totalTransaction
|
||||||
|
} Transactions</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="transaction__category-total">${
|
||||||
|
transactionDetail.totalAmount >= 0 ? '+' : '-'
|
||||||
|
}$ ${formatNumber(Math.abs(transactionDetail.totalAmount))}</p>
|
||||||
|
</div>
|
||||||
|
<div class="transaction__line"></div>
|
||||||
|
<!-- Transaction time item -->
|
||||||
|
${transactionTimeMarkup()}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
addEventTransactionItem() {
|
||||||
|
const transactionItemEl = document.querySelectorAll('.transaction__item');
|
||||||
|
|
||||||
|
if (transactionItemEl) {
|
||||||
|
transactionItemEl.forEach((item) => {
|
||||||
|
item.addEventListener('click', (e) => {
|
||||||
|
const transactionTime = e.target.closest('.transaction__time');
|
||||||
|
const categoryNameEl = item.querySelector(
|
||||||
|
'.transaction__category-name',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (transactionTime) {
|
||||||
|
const idTransaction = transactionTime.dataset.id;
|
||||||
|
|
||||||
|
// If it is income transaction, don't show dialog
|
||||||
|
if (categoryNameEl.textContent.trim() !== 'Income')
|
||||||
|
TransactionView.showTransactionDialog(idTransaction);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new TransactionTabView();
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
import * as MESSAGE from '../constants/message';
|
||||||
|
import { renderRequiredText } from '../helpers/validateForm';
|
||||||
|
import Transaction from '../models/transaction';
|
||||||
|
import CategoryView from './categoryView';
|
||||||
|
|
||||||
|
import defaultCategoryIcon from '../../assets/images/question-icon.svg';
|
||||||
|
|
||||||
|
class TransactionView {
|
||||||
|
constructor() {
|
||||||
|
this.addTransactionBtn = document.getElementById('addTransaction');
|
||||||
|
this.transactionDialog = document.getElementById('transactionDialog');
|
||||||
|
this.transactionForm = document.getElementById('formAddTransaction');
|
||||||
|
|
||||||
|
this.handlerEventTransactionDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
initFunction(
|
||||||
|
toggleLoaderSpinner,
|
||||||
|
deleteTransaction,
|
||||||
|
loadTransactionData,
|
||||||
|
updateAmountWallet,
|
||||||
|
loadData,
|
||||||
|
showSuccessToast,
|
||||||
|
showErrorToast,
|
||||||
|
saveTransaction,
|
||||||
|
transform,
|
||||||
|
) {
|
||||||
|
this.toggleLoaderSpinner = toggleLoaderSpinner;
|
||||||
|
this.deleteTransaction = deleteTransaction;
|
||||||
|
this.loadTransactionData = loadTransactionData;
|
||||||
|
this.updateAmountWallet = updateAmountWallet;
|
||||||
|
this.loadData = loadData;
|
||||||
|
this.showSuccessToast = showSuccessToast;
|
||||||
|
this.showErrorToast = showErrorToast;
|
||||||
|
this.saveTransaction = saveTransaction;
|
||||||
|
this.transform = transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe() {
|
||||||
|
this.transform.create('transactionView', this.updateData.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
sendData() {
|
||||||
|
const data = { wallet: this.wallet, listTransaction: this.listTransaction };
|
||||||
|
|
||||||
|
this.transform.onSendSignal('transactionView', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData(data) {
|
||||||
|
if (data.wallet) this.wallet = data.wallet;
|
||||||
|
|
||||||
|
if (data.listTransactions) this.listTransactions = data.listTransactions;
|
||||||
|
|
||||||
|
if (data.listCategory) this.listCategory = data.listCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerEventTransactionDialog() {
|
||||||
|
this.transactionDialog.addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.clearErrorTransactionDialog();
|
||||||
|
this.submitTransactionDialog();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.transactionDialog.addEventListener('input', () => {
|
||||||
|
this.changeBtnStyleTransactionDialog();
|
||||||
|
this.clearErrorTransactionDialog();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add delete transaction event
|
||||||
|
this.deleteTransactionEvent();
|
||||||
|
|
||||||
|
this.addTransactionBtn.addEventListener('click', () => {
|
||||||
|
this.showTransactionDialog();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.handlerCategoryFieldEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerCategoryFieldEvent() {
|
||||||
|
const categoryField = this.transactionForm.querySelector(
|
||||||
|
"[name='category_name']",
|
||||||
|
);
|
||||||
|
|
||||||
|
categoryField.addEventListener('click', () => {
|
||||||
|
this.clearErrorTransactionDialog();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clearErrorTransactionDialog() {
|
||||||
|
// Clear error style
|
||||||
|
const inputFieldEls =
|
||||||
|
this.transactionDialog.querySelectorAll('.form__input-field');
|
||||||
|
const errorTextEls = this.transactionDialog.querySelectorAll('.error-text');
|
||||||
|
|
||||||
|
inputFieldEls.forEach((item) => {
|
||||||
|
if (item.classList.contains('error-input'))
|
||||||
|
item.classList.remove('error-input');
|
||||||
|
});
|
||||||
|
errorTextEls.forEach((item) => {
|
||||||
|
if (item) item.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTransactionEvent() {
|
||||||
|
const deleteTransactionBtn =
|
||||||
|
this.transactionDialog.querySelector('.form__delete-btn');
|
||||||
|
|
||||||
|
deleteTransactionBtn.addEventListener('click', async () => {
|
||||||
|
const idEl = this.transactionDialog.querySelector(
|
||||||
|
"[name='id_transaction']",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (idEl.value) {
|
||||||
|
try {
|
||||||
|
this.transactionDialog.close();
|
||||||
|
this.toggleLoaderSpinner();
|
||||||
|
|
||||||
|
await this.deleteTransaction(idEl.value);
|
||||||
|
|
||||||
|
// Reload data
|
||||||
|
await this.loadTransactionData();
|
||||||
|
await this.updateAmountWallet();
|
||||||
|
await this.loadData();
|
||||||
|
|
||||||
|
this.showSuccessToast('Delete success!', MESSAGE.DEFAULT_MESSAGE);
|
||||||
|
} catch (error) {
|
||||||
|
this.showErrorToast(error);
|
||||||
|
}
|
||||||
|
this.toggleLoaderSpinner();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
changeBtnStyleTransactionDialog() {
|
||||||
|
const dateInput = this.transactionDialog.querySelector(
|
||||||
|
"[name='selected_date']",
|
||||||
|
).value;
|
||||||
|
const categoryName = this.transactionDialog.querySelector(
|
||||||
|
"[name='category_name']",
|
||||||
|
).value;
|
||||||
|
const amountInput =
|
||||||
|
this.transactionDialog.querySelector("[name='amount']").value;
|
||||||
|
const saveBtn = this.transactionDialog.querySelector('.form__save-btn');
|
||||||
|
|
||||||
|
saveBtn.classList.toggle(
|
||||||
|
'active',
|
||||||
|
dateInput && categoryName && amountInput >= 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
initValueTransactionDialog(idTransaction) {
|
||||||
|
let categoryName;
|
||||||
|
|
||||||
|
if (idTransaction) {
|
||||||
|
// Get transaction object
|
||||||
|
const transactionArr = this.listTransactions.filter(
|
||||||
|
(obj) => obj.id === idTransaction,
|
||||||
|
);
|
||||||
|
const transaction = Object.assign({}, ...transactionArr);
|
||||||
|
// Get category object
|
||||||
|
const categoryArr = this.listCategory.filter(
|
||||||
|
(obj) => obj.name === transaction.categoryName,
|
||||||
|
);
|
||||||
|
const category = Object.assign({}, ...categoryArr);
|
||||||
|
|
||||||
|
const idEl = this.transactionDialog.querySelector(
|
||||||
|
"[name='id_transaction']",
|
||||||
|
);
|
||||||
|
const dateEl = this.transactionDialog.querySelector(
|
||||||
|
"[name='selected_date']",
|
||||||
|
);
|
||||||
|
const categoryEl = this.transactionDialog.querySelector(
|
||||||
|
"[name='category_name']",
|
||||||
|
);
|
||||||
|
const amountEl = this.transactionDialog.querySelector("[name='amount']");
|
||||||
|
const noteEl = this.transactionDialog.querySelector("[name='note']");
|
||||||
|
const iconEl = this.transactionDialog.querySelector('.category-icon');
|
||||||
|
|
||||||
|
idEl.value = transaction.id;
|
||||||
|
dateEl.value = transaction.date;
|
||||||
|
categoryEl.value = transaction.categoryName;
|
||||||
|
amountEl.value = Math.abs(transaction.amount);
|
||||||
|
noteEl.value = transaction.note;
|
||||||
|
iconEl.src = category.url;
|
||||||
|
|
||||||
|
categoryName = categoryEl.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show delete button only if it is a edit form and not a income transaction
|
||||||
|
const deleteBtn = this.transactionDialog.querySelector('.form__delete-btn');
|
||||||
|
const showDeleteBtn = () => {
|
||||||
|
return idTransaction && categoryName !== 'Income';
|
||||||
|
};
|
||||||
|
|
||||||
|
deleteBtn.classList.toggle('hide', !showDeleteBtn());
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitTransactionDialog() {
|
||||||
|
try {
|
||||||
|
const dateEl = this.transactionForm.querySelector(
|
||||||
|
"[name='selected_date']",
|
||||||
|
);
|
||||||
|
const categoryNameEl = this.transactionForm.querySelector(
|
||||||
|
"[name='category_name']",
|
||||||
|
);
|
||||||
|
const amountEl = this.transactionForm.querySelector("[name='amount']");
|
||||||
|
const noteEl = this.transactionForm.querySelector("[name='note']");
|
||||||
|
const idEl = this.transactionForm.querySelector(
|
||||||
|
"[name='id_transaction']",
|
||||||
|
);
|
||||||
|
const amount =
|
||||||
|
categoryNameEl.value === 'Income' ? +amountEl.value : -+amountEl.value; // Check if this is a income transaction, amount must plus
|
||||||
|
|
||||||
|
// Validate data user input
|
||||||
|
if (
|
||||||
|
this.validateTransactionForm(dateEl.value, categoryNameEl.value, amount)
|
||||||
|
) {
|
||||||
|
this.toggleLoaderSpinner();
|
||||||
|
this.transactionDialog.close();
|
||||||
|
|
||||||
|
const transaction = new Transaction({
|
||||||
|
id: idEl.value,
|
||||||
|
categoryName: categoryNameEl.value,
|
||||||
|
date: dateEl.value,
|
||||||
|
amount,
|
||||||
|
note: noteEl.value,
|
||||||
|
idUser: this.wallet.idUser,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.saveTransaction(transaction);
|
||||||
|
|
||||||
|
// Reload data
|
||||||
|
await this.loadTransactionData();
|
||||||
|
this.updateAmountWallet();
|
||||||
|
await this.loadData();
|
||||||
|
|
||||||
|
if (!idEl.value) {
|
||||||
|
// Add success
|
||||||
|
this.showSuccessToast(
|
||||||
|
MESSAGE.ADD_TRANSACTION_SUCCESS,
|
||||||
|
MESSAGE.DEFAULT_MESSAGE,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Update success
|
||||||
|
this.showSuccessToast(
|
||||||
|
MESSAGE.UPDATE_TRANSACTION_SUCCESS,
|
||||||
|
MESSAGE.DEFAULT_MESSAGE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toggleLoaderSpinner();
|
||||||
|
this.clearInputTransactionForm(this.transactionForm);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.showErrorToast(error);
|
||||||
|
this.toggleLoaderSpinner();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateTransactionForm(date, categoryName, amount) {
|
||||||
|
const inputFieldEls =
|
||||||
|
this.transactionDialog.querySelectorAll('.form__input-field');
|
||||||
|
|
||||||
|
if (!date || !categoryName || !amount) {
|
||||||
|
if (!date) {
|
||||||
|
renderRequiredText('date', inputFieldEls[0]);
|
||||||
|
inputFieldEls[0].classList.add('error-input');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!categoryName) {
|
||||||
|
renderRequiredText('category', inputFieldEls[1]);
|
||||||
|
inputFieldEls[1].classList.add('error-input');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amount) {
|
||||||
|
renderRequiredText('amount', inputFieldEls[2]);
|
||||||
|
inputFieldEls[2].classList.add('error-input');
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearInputTransactionForm() {
|
||||||
|
const categoryIcon = this.transactionForm.querySelector('.category-icon');
|
||||||
|
|
||||||
|
categoryIcon.src = defaultCategoryIcon;
|
||||||
|
this.keySearchCategory = null; // Delete keyword search
|
||||||
|
CategoryView.renderCategoryList(this.keySearchCategory);
|
||||||
|
this.transactionForm.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
showTransactionDialog(idTransaction = null) {
|
||||||
|
this.clearInputTransactionForm();
|
||||||
|
|
||||||
|
// Init data transaction to dialog
|
||||||
|
this.initValueTransactionDialog(idTransaction);
|
||||||
|
|
||||||
|
if (!idTransaction)
|
||||||
|
this.transactionDialog.querySelector(
|
||||||
|
"[name='selected_date']",
|
||||||
|
).valueAsDate = new Date(); // Set default value for date input
|
||||||
|
|
||||||
|
// Change style submit btn
|
||||||
|
this.changeBtnStyleTransactionDialog();
|
||||||
|
this.transactionDialog.showModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new TransactionView();
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
import { FIRST_ADD_WALLET_NOTE } from '../constants/config';
|
||||||
|
import Transaction from '../models/transaction';
|
||||||
|
import Wallet from '../models/wallet';
|
||||||
|
import * as MESSAGE from '../constants/message';
|
||||||
|
import { renderRequiredText } from '../helpers/validateForm';
|
||||||
|
|
||||||
|
class WalletView {
|
||||||
|
constructor() {
|
||||||
|
this.walletDialog = document.getElementById('walletDialog');
|
||||||
|
|
||||||
|
this.addHandlerEventWalletForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
initFunction(
|
||||||
|
transform,
|
||||||
|
toggleLoaderSpinner,
|
||||||
|
saveWallet,
|
||||||
|
saveTransaction,
|
||||||
|
loadTransactionData,
|
||||||
|
loadData,
|
||||||
|
loadEvent,
|
||||||
|
showSuccessToast,
|
||||||
|
showErrorToast,
|
||||||
|
) {
|
||||||
|
this.transform = transform;
|
||||||
|
this.toggleLoaderSpinner = toggleLoaderSpinner;
|
||||||
|
this.saveWallet = saveWallet;
|
||||||
|
this.saveTransaction = saveTransaction;
|
||||||
|
this.loadTransactionData = loadTransactionData;
|
||||||
|
this.loadData = loadData;
|
||||||
|
this.loadEvent = loadEvent;
|
||||||
|
this.showSuccessToast = showSuccessToast;
|
||||||
|
this.showErrorToast = showErrorToast;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe() {
|
||||||
|
this.transform.create('walletView', this.updateData.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
sendData() {
|
||||||
|
const data = {
|
||||||
|
wallet: this.wallet,
|
||||||
|
listTransactions: this.listTransactions,
|
||||||
|
user: this.user,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.transform.onSendSignal('walletView', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData(data) {
|
||||||
|
if (data.wallet) this.wallet = data.wallet;
|
||||||
|
|
||||||
|
if (data.listTransactions) this.listTransaction = data.listTransactions;
|
||||||
|
|
||||||
|
if (data.listCategory) this.listCategory = data.listCategory;
|
||||||
|
|
||||||
|
if (data.user) this.user = data.user;
|
||||||
|
}
|
||||||
|
|
||||||
|
showDialog() {
|
||||||
|
this.walletDialog.showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
addHandlerEventWalletForm() {
|
||||||
|
this.walletDialog.addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.clearErrorStyleWalletDialog();
|
||||||
|
this.submitWalletForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.walletDialog.addEventListener('input', (e) => {
|
||||||
|
const bodyDialog = e.target.closest('.dialog__body');
|
||||||
|
|
||||||
|
this.changeBtnStyleWalletDialog(bodyDialog);
|
||||||
|
this.clearErrorStyleWalletDialog();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clearErrorStyleWalletDialog() {
|
||||||
|
const inputFieldEls =
|
||||||
|
this.walletDialog.querySelectorAll('.form__input-field');
|
||||||
|
const errorTextEls = this.walletDialog.querySelectorAll('.error-text');
|
||||||
|
|
||||||
|
inputFieldEls.forEach((item) => {
|
||||||
|
if (item.classList.contains('error-input'))
|
||||||
|
item.classList.remove('error-input');
|
||||||
|
});
|
||||||
|
|
||||||
|
errorTextEls.forEach((item) => {
|
||||||
|
if (item) item.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitWalletForm() {
|
||||||
|
try {
|
||||||
|
// Wallet info
|
||||||
|
const { walletForm } = document.forms;
|
||||||
|
const form = new FormData(walletForm);
|
||||||
|
const walletName = form.get('walletName');
|
||||||
|
const amount = form.get('amount');
|
||||||
|
|
||||||
|
if (this.validateWalletDialog(walletName, amount)) {
|
||||||
|
this.walletDialog.close();
|
||||||
|
this.toggleLoaderSpinner();
|
||||||
|
|
||||||
|
this.wallet = new Wallet({
|
||||||
|
walletName,
|
||||||
|
amount: +amount,
|
||||||
|
idUser: this.user.id,
|
||||||
|
inflow: +amount,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.sendData();
|
||||||
|
|
||||||
|
await this.saveWallet(this.wallet);
|
||||||
|
// Transaction info
|
||||||
|
const transaction = new Transaction({
|
||||||
|
categoryName: 'Income',
|
||||||
|
date: new Date().toISOString().slice(0, 10),
|
||||||
|
note: FIRST_ADD_WALLET_NOTE,
|
||||||
|
amount: +amount,
|
||||||
|
idUser: this.user.id,
|
||||||
|
});
|
||||||
|
await this.saveTransaction(transaction);
|
||||||
|
// Load data and event
|
||||||
|
await this.loadTransactionData();
|
||||||
|
await this.loadData();
|
||||||
|
this.loadEvent();
|
||||||
|
this.showSuccessToast(
|
||||||
|
MESSAGE.ADD_WALLET_SUCCESS,
|
||||||
|
MESSAGE.DEFAULT_MESSAGE,
|
||||||
|
);
|
||||||
|
await this.loadData(); // Load data from database into page
|
||||||
|
|
||||||
|
this.toggleLoaderSpinner();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Show toast error
|
||||||
|
this.showErrorToast(error);
|
||||||
|
this.toggleLoaderSpinner();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateWalletDialog(walletName, amount) {
|
||||||
|
const inputFieldEls =
|
||||||
|
this.walletDialog.querySelectorAll('.form__input-field');
|
||||||
|
|
||||||
|
if (!walletName || !amount) {
|
||||||
|
if (!walletName) {
|
||||||
|
renderRequiredText('wallet name', inputFieldEls[0]);
|
||||||
|
inputFieldEls[0].classList.add('error-input');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amount) {
|
||||||
|
renderRequiredText('amount', inputFieldEls[2]);
|
||||||
|
inputFieldEls[2].classList.add('error-input');
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
changeBtnStyleWalletDialog(bodyDialog) {
|
||||||
|
const walletName = bodyDialog.querySelector('.form__input-text').value;
|
||||||
|
const amount = bodyDialog.querySelector('.form__input-balance').value;
|
||||||
|
const saveBtn = bodyDialog.querySelector('.form__save-btn');
|
||||||
|
|
||||||
|
if (walletName.length >= 3 && amount >= 1) {
|
||||||
|
saveBtn.classList.add('active');
|
||||||
|
} else {
|
||||||
|
saveBtn.classList.remove('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new WalletView();
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
||||||
<link rel="stylesheet" href="../styles/_main.scss" />
|
<link rel="stylesheet" href="../styles/_main.scss" />
|
||||||
|
<meta name="description" content="Money Lover Web" />
|
||||||
<title>Money Lover Web</title>
|
<title>Money Lover Web</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="home-page">
|
<body class="home-page">
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
<form class="form" id="walletForm">
|
<form class="form" id="walletForm">
|
||||||
<div class="form__input-container">
|
<div class="form__input-container">
|
||||||
<div class="form__input-field">
|
<div class="form__input-field">
|
||||||
<p class="form__label">Wallet name</p>
|
<label class="form__label">Wallet name</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form__input-text"
|
class="form__input-text"
|
||||||
@@ -32,7 +33,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form__currency-balance">
|
<div class="form__currency-balance">
|
||||||
<div class="form__input-field">
|
<div class="form__input-field">
|
||||||
<p class="form__label">Currency</p>
|
<label class="form__label">Currency</label>
|
||||||
<div class="form__wrap-text-icon">
|
<div class="form__wrap-text-icon">
|
||||||
<img
|
<img
|
||||||
src="../assets/images/dollar-icon.svg"
|
src="../assets/images/dollar-icon.svg"
|
||||||
@@ -43,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form__input-container">
|
<div class="form__input-container">
|
||||||
<div class="form__input-field">
|
<div class="form__input-field">
|
||||||
<p class="form__label">Initial Balance</p>
|
<label class="form__label">Initial Balance</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
class="form__input-balance"
|
class="form__input-balance"
|
||||||
@@ -52,7 +53,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="form__save-btn">SAVE</button>
|
<button type="submit" class="form__save-btn">
|
||||||
|
<span class="text">SAVE</span>
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,20 +74,20 @@
|
|||||||
<div class="form__date-amount-input">
|
<div class="form__date-amount-input">
|
||||||
<div class="form__input-container">
|
<div class="form__input-container">
|
||||||
<div class="form__input-field">
|
<div class="form__input-field">
|
||||||
<p class="form__label">Date</p>
|
<label for="selected_date" class="form__label">Date</label>
|
||||||
<div class="date-input-container">
|
<div class="date-input-container">
|
||||||
<input
|
<input
|
||||||
class="input-date"
|
class="input-date"
|
||||||
type="date"
|
type="date"
|
||||||
name="selected_date"
|
name="selected_date"
|
||||||
/>
|
/>
|
||||||
<div class="icon-btn"></div>
|
<span class="icon-btn"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form__input-container">
|
<div class="form__input-container">
|
||||||
<div class="form__input-field">
|
<div class="form__input-field">
|
||||||
<p class="form__label">Amount</p>
|
<label class="form__label">Amount</label>
|
||||||
<input
|
<input
|
||||||
class="form__input-balance"
|
class="form__input-balance"
|
||||||
type="number"
|
type="number"
|
||||||
@@ -95,7 +98,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form__note-input">
|
<div class="form__note-input">
|
||||||
<p class="form__label">Note</p>
|
<label class="form__label">Note</label>
|
||||||
<input
|
<input
|
||||||
class="form__input-text"
|
class="form__input-text"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -105,7 +108,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form__action">
|
<div class="form__action">
|
||||||
<button class="form__cancel-btn" type="button">CANCEL</button>
|
<button class="form__cancel-btn" type="button">CANCEL</button>
|
||||||
<button type="submit" class="form__save-btn">SAVE</button>
|
<button type="submit" class="form__save-btn">
|
||||||
|
<span class="text">SAVE</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -127,7 +132,7 @@
|
|||||||
<div class="form__date-category-amount-input">
|
<div class="form__date-category-amount-input">
|
||||||
<div class="form__input-container">
|
<div class="form__input-container">
|
||||||
<div class="form__input-field">
|
<div class="form__input-field">
|
||||||
<p class="form__label">Date</p>
|
<label for="selected_date" class="form__label">Date</label>
|
||||||
<div class="date-input-container">
|
<div class="date-input-container">
|
||||||
<input
|
<input
|
||||||
class="input-date"
|
class="input-date"
|
||||||
@@ -140,7 +145,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form__input-container">
|
<div class="form__input-container">
|
||||||
<div class="form__input-field" id="selectCategory">
|
<div class="form__input-field" id="selectCategory">
|
||||||
<p class="form__label">Category</p>
|
<label for="category-name" class="form__label">
|
||||||
|
Category
|
||||||
|
</label>
|
||||||
<div class="category-input-container">
|
<div class="category-input-container">
|
||||||
<img
|
<img
|
||||||
class="category-icon"
|
class="category-icon"
|
||||||
@@ -153,13 +160,13 @@
|
|||||||
class="category-name"
|
class="category-name"
|
||||||
placeholder="Select Category"
|
placeholder="Select Category"
|
||||||
/>
|
/>
|
||||||
<div class="icon-btn"></div>
|
<span class="icon-btn"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form__input-container">
|
<div class="form__input-container">
|
||||||
<div class="form__input-field">
|
<div class="form__input-field">
|
||||||
<p class="form__label">Amount</p>
|
<label class="form__label">Amount</label>
|
||||||
<input
|
<input
|
||||||
class="form__input-balance"
|
class="form__input-balance"
|
||||||
type="number"
|
type="number"
|
||||||
@@ -170,7 +177,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form__note-input">
|
<div class="form__note-input">
|
||||||
<p class="form__label">Note</p>
|
<label class="form__label">Note</label>
|
||||||
<input
|
<input
|
||||||
class="form__input-text"
|
class="form__input-text"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -180,7 +187,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form__action">
|
<div class="form__action">
|
||||||
<button type="button" class="form__cancel-btn">CANCEL</button>
|
<button type="button" class="form__cancel-btn">CANCEL</button>
|
||||||
<button type="submit" class="form__save-btn">SAVE</button>
|
<button type="submit" class="form__save-btn">
|
||||||
|
<span class="text">SAVE</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -222,7 +231,7 @@
|
|||||||
<div class="header__left-content">
|
<div class="header__left-content">
|
||||||
<div class="wallet">
|
<div class="wallet">
|
||||||
<div class="wallet__icon">
|
<div class="wallet__icon">
|
||||||
<img src="../assets/images/icon.png" alt="Wallet Icon" />
|
<img src="../assets/images/icon.webp" alt="Wallet Icon" />
|
||||||
</div>
|
</div>
|
||||||
<div class="wallet__info">
|
<div class="wallet__info">
|
||||||
<p class="wallet__name">User</p>
|
<p class="wallet__name">User</p>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
||||||
<link rel="stylesheet" href="../styles/_main.scss" />
|
<link rel="stylesheet" href="../styles/_main.scss" />
|
||||||
|
<meta name="description" content="Login Page - Money Lover" />
|
||||||
<title>Login - Money Lover</title>
|
<title>Login - Money Lover</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="login-page">
|
<body class="login-page">
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
||||||
<link rel="stylesheet" href="../styles/_main.scss" />
|
<link rel="stylesheet" href="../styles/_main.scss" />
|
||||||
|
<meta name="description" content="Register Page - Money Lover" />
|
||||||
<title>Register - Money Lover</title>
|
<title>Register - Money Lover</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="register-page">
|
<body class="register-page">
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ $light-primary-color: #00bc2a;
|
|||||||
$primary-color: #2db84c;
|
$primary-color: #2db84c;
|
||||||
$dark-primary-color: #00710f;
|
$dark-primary-color: #00710f;
|
||||||
$primary-text-color: #000;
|
$primary-text-color: #000;
|
||||||
$secondary-text-color: #0000008a;
|
|
||||||
$secondary-light-text-color: #00000042;
|
|
||||||
$amount-outcome-color: #e51c23;
|
$amount-outcome-color: #e51c23;
|
||||||
$amount-income-color: #039be5;
|
$amount-income-color: #039be5;
|
||||||
$background-input-color: #f5f5f5;
|
$background-input-color: #f5f5f5;
|
||||||
@@ -15,10 +13,9 @@ $dark-error-color: #ce1414;
|
|||||||
$light-error-color: #ffc3c3;
|
$light-error-color: #ffc3c3;
|
||||||
$light-gray: #f4f4f4;
|
$light-gray: #f4f4f4;
|
||||||
$gray: #dddddd;
|
$gray: #dddddd;
|
||||||
$dark-gray: #00000042;
|
|
||||||
$dark-x-gray: #0000008a;
|
$dark-x-gray: #0000008a;
|
||||||
$white: #ffffff;
|
$white: #fff;
|
||||||
$black: #000000;
|
$black: #000;
|
||||||
|
|
||||||
// Font size
|
// Font size
|
||||||
$fs-2x-sm: 11px;
|
$fs-2x-sm: 11px;
|
||||||
|
|||||||
@@ -108,6 +108,8 @@
|
|||||||
background-color: $primary-color;
|
background-color: $primary-color;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
transition: all 0.3s ease-in-out;
|
transition: all 0.3s ease-in-out;
|
||||||
|
width: 116px;
|
||||||
|
left: 152px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +247,7 @@
|
|||||||
&__note {
|
&__note {
|
||||||
@extend .transaction__date-time;
|
@extend .transaction__date-time;
|
||||||
|
|
||||||
color: $dark-gray;
|
color: $black;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__outcome {
|
&__outcome {
|
||||||
@@ -506,13 +508,8 @@
|
|||||||
line-height: 16.1px;
|
line-height: 16.1px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
color: $dark-gray;
|
color: $black;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&.active {
|
|
||||||
background-color: $primary-color;
|
|
||||||
color: $white;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__cancel-btn {
|
&__cancel-btn {
|
||||||
@@ -573,14 +570,15 @@
|
|||||||
top: 10px;
|
top: 10px;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border-top: 3px solid $dark-gray;
|
border-top: 3px solid $black;
|
||||||
border-right: 3px solid $dark-gray;
|
border-right: 3px solid $black;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-left: none;
|
border-left: none;
|
||||||
height: 7px;
|
height: 7px;
|
||||||
width: 7px;
|
width: 7px;
|
||||||
rotate: 45deg;
|
rotate: 45deg;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
opacity: 0.56;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-input-container {
|
.category-input-container {
|
||||||
@@ -666,6 +664,22 @@
|
|||||||
@extend %d-flex;
|
@extend %d-flex;
|
||||||
@include flex-layout($direction: column, $gap: 7px);
|
@include flex-layout($direction: column, $gap: 7px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form__save-btn {
|
||||||
|
.text {
|
||||||
|
color: $black;
|
||||||
|
opacity: 0.54;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: $primary-color;
|
||||||
|
|
||||||
|
& .text {
|
||||||
|
color: $white;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-icon {
|
.close-icon {
|
||||||
|
|||||||
@@ -57,7 +57,8 @@
|
|||||||
|
|
||||||
&__description {
|
&__description {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
color: $secondary-text-color;
|
color: $black;
|
||||||
|
opacity: 0.54;
|
||||||
font-size: $fs-md;
|
font-size: $fs-md;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user