From f7544b0ed27994ff824ea0004908b0d552f45a42 Mon Sep 17 00:00:00 2001 From: akrck02 Date: Mon, 15 Jan 2024 00:37:49 +0100 Subject: [PATCH 1/6] [refactor] tax service refactor and minor ui tweaks --- app/index.html | 7 +- app/indexProd.html | 2 +- app/styles/src/Gtdf.css | 13 +- app/styles/src/views/home/HomeView.css | 42 ++---- resources/json/irpf/2022/taxes.json | 11 +- resources/json/irpf/2023/taxes.json | 7 +- resources/json/irpf/2024/taxes.json | 7 +- resources/json/irpf/taxes.json | 5 - src/services/tax.service.ts | 42 ++++++ .../{irpf.service.ts => taxes/tax.model.ts} | 134 ++++++++++-------- src/views/home/home.view.core.ts | 21 +-- src/views/home/home.view.ts | 83 ++++------- 12 files changed, 197 insertions(+), 177 deletions(-) delete mode 100644 resources/json/irpf/taxes.json create mode 100644 src/services/tax.service.ts rename src/services/{irpf.service.ts => taxes/tax.model.ts} (51%) diff --git a/app/index.html b/app/index.html index 63196f8..4e115f0 100644 --- a/app/index.html +++ b/app/index.html @@ -1,5 +1,5 @@ - + @@ -7,6 +7,11 @@ GTDF - New app +
diff --git a/app/indexProd.html b/app/indexProd.html index b0908c8..cd365e4 100644 --- a/app/indexProd.html +++ b/app/indexProd.html @@ -1,5 +1,5 @@ - + diff --git a/app/styles/src/Gtdf.css b/app/styles/src/Gtdf.css index 4ccaeee..d9f6475 100644 --- a/app/styles/src/Gtdf.css +++ b/app/styles/src/Gtdf.css @@ -4,13 +4,6 @@ :root { --accent-color : #B4C5FF; - --background: #fff; - --input-bg: #f1f1f1; - --input-bg-hover: #e1e1e1; - --text: #222; -} - -:root[data-theme="dark"] { --background: #1A1A1A; --input-bg: #282828; --input-bg-hover: #303030; @@ -24,6 +17,10 @@ --text: #eee; } +:root[data-grayscale="true"] body { + filter: grayscale(1); +} + body, view { transition: .35s; } @@ -38,7 +35,7 @@ input::-webkit-inner-spin-button { margin: 0; } -/* Firefox */ input[type=number] { + appearance: textfield; -moz-appearance: textfield; } \ No newline at end of file diff --git a/app/styles/src/views/home/HomeView.css b/app/styles/src/views/home/HomeView.css index 78ba909..0ac0594 100644 --- a/app/styles/src/views/home/HomeView.css +++ b/app/styles/src/views/home/HomeView.css @@ -26,6 +26,7 @@ view#home h1#main-title{ view#home * { transition: .35s; + user-select: none; } view#home.showing { @@ -109,18 +110,14 @@ view#home span#value-data label.label { view#home span#value-data span.value { border-radius: 1rem; padding: .2rem .75rem; - background-color: #eee; + background-color: rgba(255,255,255,.055); + color: #ccc; font-size: 1rem; margin-left: .5rem; user-select: none; transition: .15s; } -:root[data-theme=dark] view#home span#value-data span.value { - background-color: rgba(255,255,255,.055); - color: #ccc -} - view#home .theme { position: absolute; top: 20px; @@ -134,21 +131,22 @@ view#home footer { font-size: 1rem; } +view#home.mobile footer { + bottom: 1rem; + font-size: .7rem; +} + view#home #calc-menu { border-radius: 1rem; height: 100%; width: 20rem; min-width: 13rem; - + color: var(--text); overflow-y: auto; padding: 1rem; background-color:rgba(0,0,0,.05); } -:root[data-theme=dark] view#home #calc-menu { - color: var(--text); -} - view#home #calc-frame { width: 80%; padding: 1rem; @@ -172,7 +170,7 @@ view#home:not(.mobile) #calc-menu .button-container { } view#home .menu-option { - background-color: rgba(0,0,0,.05); + background-color: rgba(255,255,255,.05); margin-bottom: .5rem; cursor: pointer; min-width: 6rem; @@ -187,7 +185,7 @@ view#home .menu-option { padding: .5rem 2rem; } -view#home .menu-option.selected, :root[data-theme=dark] view#home .menu-option.selected { +view#home .menu-option.selected { width: 97%; background: rgba(var(--RGB-accent-color),1); color: #434759; @@ -195,16 +193,9 @@ view#home .menu-option.selected, :root[data-theme=dark] view#home .menu-option. view#home .menu-option:not(.selected):hover { - background-color: rgba(0,0,0,.1); -} - -:root[data-theme=dark] view#home .menu-option:not(.selected):hover { background-color: rgba(255,255,255,.2); } -:root[data-theme=dark] view#home .menu-option { - background-color: rgba(255,255,255,.05); -} /** MOBILE STYLES @@ -232,11 +223,11 @@ view#home.mobile #calc-frame { view#home.mobile #calc-menu{ position: fixed; - background: #fff; + background: #222; border-radius: 2rem 2rem 0 0; padding: 1.5rem 1.5rem 0 1.5rem ; z-index: 999; - height: 50%; + height: 87%; width: 100%; top: 100%; box-shadow: 0rem 0rem 1rem rgba(0,0,0,.1); @@ -244,7 +235,7 @@ view#home.mobile #calc-menu{ } view#home.mobile #calc-menu.show{ - top: 50%; + top: 13%; } view#home.mobile #calc-menu .options-title { @@ -270,14 +261,11 @@ view#home.mobile #calc-menu .button-container .region-option { width: auto; } -view#home.mobile #calc-menu .button-container .year-option { +view#home.mobile #calc-menu .button-container .year-option , view#home.mobile #calc-menu .button-container .lang-option { min-width: 5rem; max-width: 7rem; } -:root[data-theme=dark] view#home.mobile #calc-menu { - background: #222; -} view#home.mobile #calc-frame { width: 100%; diff --git a/resources/json/irpf/2022/taxes.json b/resources/json/irpf/2022/taxes.json index e23575a..ba2f140 100644 --- a/resources/json/irpf/2022/taxes.json +++ b/resources/json/irpf/2022/taxes.json @@ -1,5 +1,8 @@ { - "CONTINGENCIAS_COMUNES" : 4.7, - "ATUR" : 1.55, - "FP" : 0.1 -} \ No newline at end of file + "contingenciasComunes" : 4.7, + "atur" : 1.55, + "fp" : 0.1, + "maxSegSocial" : 4139.40 +} + + diff --git a/resources/json/irpf/2023/taxes.json b/resources/json/irpf/2023/taxes.json index a0d67ce..acf26d8 100644 --- a/resources/json/irpf/2023/taxes.json +++ b/resources/json/irpf/2023/taxes.json @@ -1,5 +1,6 @@ { - "CONTINGENCIAS_COMUNES" : 4.8, - "ATUR" : 1.55, - "FP" : 0.1 + "contingenciasComunes" : 4.8, + "atur" : 1.55, + "fp" : 0.1, + "maxSegSocial" : 4495.50 } \ No newline at end of file diff --git a/resources/json/irpf/2024/taxes.json b/resources/json/irpf/2024/taxes.json index a0d67ce..d426051 100644 --- a/resources/json/irpf/2024/taxes.json +++ b/resources/json/irpf/2024/taxes.json @@ -1,5 +1,6 @@ { - "CONTINGENCIAS_COMUNES" : 4.8, - "ATUR" : 1.55, - "FP" : 0.1 + "contingenciasComunes" : 4.8, + "atur" : 1.55, + "fp" : 0.1, + "maxSegSocial" : 4720.5 } \ No newline at end of file diff --git a/resources/json/irpf/taxes.json b/resources/json/irpf/taxes.json deleted file mode 100644 index e23575a..0000000 --- a/resources/json/irpf/taxes.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "CONTINGENCIAS_COMUNES" : 4.7, - "ATUR" : 1.55, - "FP" : 0.1 -} \ No newline at end of file diff --git a/src/services/tax.service.ts b/src/services/tax.service.ts new file mode 100644 index 0000000..2d869b1 --- /dev/null +++ b/src/services/tax.service.ts @@ -0,0 +1,42 @@ +import { Config } from "../config/config.js"; +import TaxModel from "./taxes/tax.model.js"; + +export default class TaxService { + + private static taxModel = new TaxModel(); + + /** + * Load the irpf data + * @param province The province to load the data + * @param year The year to load the data + * @returns {boolean} True if the data is loaded, false otherwise + */ + static async load(province : string, year : string) : Promise { + + try { + const irpfRanges = await fetch(`${Config.Path.irpf_info}${year}/irpfRanges-${province}.json`).then(response => response.json()); + const taxes = await fetch(`${Config.Path.irpf_info}${year}/taxes.json`).then(response => response.json()); + + this.taxModel.setIrpfRanges(irpfRanges); + this.taxModel.setTaxes(taxes); + + return true; + } catch (error) { + return false; + } + + } + + static getTaxModel() : TaxModel { + return TaxService.taxModel; + } + + /** + * Clean the service variables + */ + static clean() { + TaxService.taxModel.clean(); + } + + +} \ No newline at end of file diff --git a/src/services/irpf.service.ts b/src/services/taxes/tax.model.ts similarity index 51% rename from src/services/irpf.service.ts rename to src/services/taxes/tax.model.ts index 631670b..ff10262 100644 --- a/src/services/irpf.service.ts +++ b/src/services/taxes/tax.model.ts @@ -1,4 +1,9 @@ -import { Config } from "../config/config.js"; +interface ITaxes { + contingenciasComunes : number, + atur : number, + fp : number, + maxSegSocial : number +} export interface SalaryTime { salary : number; @@ -6,30 +11,33 @@ export interface SalaryTime { endDate : Date; } -export default class IrpfService { +//BRUTO/12 < x - static IRPF_RANGES; - static TAXES; +export default class TaxModel { - static PAYMENT_NUMBER = 14; - static readonly PAYMENT_NUMBER_TAXES = 12; + private static readonly DEFAULT_PAYMENT_NUMBER = 14; + private static readonly DEFAULT_TAXES_MONTH_NUMBER = 12; - /** - * Load the irpf data - * @param province The province to load the data - * @param year The year to load the data - * @returns {boolean} True if the data is loaded, false otherwise - */ - static async load(province : string, year : string) : Promise { - - try { - IrpfService.IRPF_RANGES = await fetch(`${Config.Path.irpf_info}${year}/irpfRanges-${province}.json`).then(response => response.json()); - IrpfService.TAXES = await fetch(`${Config.Path.irpf_info}${year}/taxes.json`).then(response => response.json()); - return true; - } catch (error) { - return false; + private paymentNumber : number; + private taxesMonthNumber : number; + + private irpfRanges : Map; + private taxes : ITaxes; + + constructor( + paymentNumber : number = TaxModel.DEFAULT_PAYMENT_NUMBER , + taxesMonthNumber : number = TaxModel.DEFAULT_TAXES_MONTH_NUMBER, + ) { + this.paymentNumber = paymentNumber; + this.taxesMonthNumber = taxesMonthNumber; + } + + + checkIfDataIsLoaded() { + + if(this.irpfRanges === undefined || this.taxes === undefined) { + throw new Error("IRPF_RANGES or TAXES are undefined, please load the data first"); } - } /** @@ -37,54 +45,50 @@ export default class IrpfService { * @param salary The salary itself * @returns {number} The salary without taxes */ - static calcWithTaxes(salary : number) : number { + calcWithTaxes(salary : number) : number { if(salary <= 0){ return 0; } - if(IrpfService.IRPF_RANGES === undefined || IrpfService.TAXES === undefined) { - throw new Error("IRPF_RANGES or TAXES are undefined, please load the data first"); - } + this.checkIfDataIsLoaded(); - const irpf = IrpfService.getIrpfValue(salary); - const contingenciasComunes = IrpfService.getContingenciasComunesValue(salary); - const atur = IrpfService.getAturValue(salary); - const fp = IrpfService.getFpValue(salary); + const irpf = this.getIrpfValue(salary); + const contingenciasComunes = this.getContingenciasComunesValue(salary); + const atur = this.getAturValue(salary); + const fp = this.getFpValue(salary); const total_deductions = (irpf + contingenciasComunes + atur + fp); - return Math.ceil(((salary / IrpfService.PAYMENT_NUMBER) - total_deductions) * 100) / 100; + return Math.ceil(((salary / this.paymentNumber) - total_deductions) * 100) / 100; } + /** * Calculate the extra payment * @param salary The salary itself * @returns {number} The extra payment */ - static extraPayment(salary : number) : number { + extraPayment(salary : number) : number { if(salary <= 0){ return 0; } - if(IrpfService.IRPF_RANGES === undefined || IrpfService.TAXES === undefined) { - throw new Error("IRPF_RANGES or TAXES are undefined, please load the data first"); - } - const irpf = IrpfService.getIrpfValue(salary); - return Math.ceil(((salary / IrpfService.PAYMENT_NUMBER) - irpf) * 100) / 100; + this.checkIfDataIsLoaded(); + + const irpf = this.getIrpfValue(salary); + return Math.ceil(((salary / this.paymentNumber) - irpf) * 100) / 100; } - static extraPaymentWithMultipleSalaries(salaries : SalaryTime[]) { + extraPaymentWithMultipleSalaries(salaries : SalaryTime[]) { if(salaries === undefined || salaries.length === 0) { return 0; } - if(IrpfService.IRPF_RANGES === undefined || IrpfService.TAXES === undefined) { - throw new Error("IRPF_RANGES or TAXES are undefined, please load the data first"); - } + this.checkIfDataIsLoaded(); // salario * dias / 180 let totalSalary = 0; @@ -98,7 +102,7 @@ export default class IrpfService { throw new Error("Start date is greater than end date"); } - let extraPayment = IrpfService.extraPayment(salary.salary); + let extraPayment = this.extraPayment(salary.salary); // every month has 30 days let totalDays = (salary.endDate.getMonth() - salary.startDate.getMonth()) * 30; @@ -118,7 +122,7 @@ export default class IrpfService { * @param {number} salary The salary itself * @returns {number} The irpf value */ - static getIrpf(salary : number) : number { + getIrpf(salary : number) : number { if(salary <= 0){ return 0; @@ -127,14 +131,14 @@ export default class IrpfService { let irpf = undefined; // Get irpf on ranges - for (const minimum in IrpfService.IRPF_RANGES) { + for (const minimum in this.irpfRanges) { const range = parseInt(minimum); if (salary <= range) { return irpf; } - irpf = IrpfService.IRPF_RANGES[minimum]; + irpf = this.irpfRanges[minimum]; } return irpf; @@ -145,13 +149,13 @@ export default class IrpfService { * @param {number} salary The salary itself * @returns {number} The irpf value calculated on the salary and the payment number */ - static getIrpfValue(salary : number) : number { + getIrpfValue(salary : number) : number { if(salary <= 0){ return 0; } - return (salary * (IrpfService.getIrpf(salary) / 100)) / IrpfService.PAYMENT_NUMBER; + return (salary * (this.getIrpf(salary) / 100)) / this.paymentNumber; } /** @@ -159,13 +163,13 @@ export default class IrpfService { * @param {number} salary The salary itself * @returns {number} The contingencias comunes value calculated on the salary and the payment number */ - static getContingenciasComunesValue(salary) : number { + getContingenciasComunesValue(salary) : number { if(salary <= 0){ return 0; } - return (salary * (IrpfService.TAXES.CONTINGENCIAS_COMUNES / 100)) / IrpfService.PAYMENT_NUMBER_TAXES; + return (salary * (this.taxes.contingenciasComunes / 100)) / this.taxesMonthNumber; } /** @@ -173,13 +177,13 @@ export default class IrpfService { * @param {number} salary The salary itself * @returns {number} The atur value calculated on the salary and the payment number */ - static getAturValue(salary : number) : number { + getAturValue(salary : number) : number { if(salary <= 0){ return 0; } - return (salary * (IrpfService.TAXES.ATUR / 100)) / IrpfService.PAYMENT_NUMBER_TAXES; + return (salary * (this.taxes.atur / 100)) / this.taxesMonthNumber; } /** @@ -187,25 +191,35 @@ export default class IrpfService { * @param {*} salary The salary itself * @returns {number} The fp value calculated on the salary and the payment number */ - static getFpValue(salary) : number { + getFpValue(salary) : number { if(salary <= 0){ return 0; } - return (salary * (IrpfService.TAXES.FP / 100)) / IrpfService.PAYMENT_NUMBER_TAXES; + return (salary * (this.taxes.fp / 100)) / this.taxesMonthNumber; } - static setPaymentNumber(paymentNumber : number) { - IrpfService.PAYMENT_NUMBER = paymentNumber; + + setIrpfRanges(irpfRanges) { + this.irpfRanges = irpfRanges; + } + + setTaxes(taxes) { + this.taxes = taxes; } - /** - * Clean the service variables - */ - static clean() { - IrpfService.IRPF_RANGES = undefined; - IrpfService.TAXES = undefined; + setPaymentNumber(paymentNumber : number) { + this.paymentNumber = paymentNumber; + } + + setTaxesMonthNumber(taxesMonthNumber : number) { + this.taxesMonthNumber = taxesMonthNumber; + } + + clean() { + this.irpfRanges = undefined; + this.taxes = undefined; } diff --git a/src/views/home/home.view.core.ts b/src/views/home/home.view.core.ts index bd53080..393fd1f 100644 --- a/src/views/home/home.view.core.ts +++ b/src/views/home/home.view.core.ts @@ -1,8 +1,9 @@ import { Config } from "../../config/config.js"; import { StringMap } from "../../lib/gtdf/data/strings.js"; import { ViewCore } from "../../lib/gtdf/views/ViewCore.js"; -import IrpfService, { SalaryTime } from "../../services/irpf.service.js"; +import TaxService from "../../services/tax.service.js"; import LanguageService from "../../services/language.service.js"; +import { SalaryTime } from "../../services/taxes/tax.model.js"; export default class HomeCore extends ViewCore { @@ -29,7 +30,7 @@ export default class HomeCore extends ViewCore { * @returns The irpf percentage */ static getIRPFPercentage(grossSalary : number) { - return IrpfService.getIrpf(grossSalary); + return TaxService.getTaxModel().getIrpf(grossSalary); } /** @@ -38,7 +39,7 @@ export default class HomeCore extends ViewCore { * @returns The extra payment */ static getExtraPayment(grossSalary : number) { - return IrpfService.extraPayment(grossSalary); + return TaxService.getTaxModel().extraPayment(grossSalary); } /** @@ -47,7 +48,7 @@ export default class HomeCore extends ViewCore { * @returns */ static getExtraPaymentWithMultipleSalaries(salaries : SalaryTime[]) { - return IrpfService.extraPaymentWithMultipleSalaries(salaries); + return TaxService.getTaxModel().extraPaymentWithMultipleSalaries(salaries); } @@ -57,14 +58,14 @@ export default class HomeCore extends ViewCore { * @returns The salary with taxes */ static getSalary(grossSalary : number) { - return IrpfService.calcWithTaxes(grossSalary); + return TaxService.getTaxModel().calcWithTaxes(grossSalary); } /** * Get the irpf value */ static cleanIrpfModel() { - IrpfService.clean(); + TaxService.clean(); } /** @@ -98,13 +99,13 @@ export default class HomeCore extends ViewCore { } /** - * Load the irpf model + * Load the tax model * @param region The region to load * @param year The year to load * @returns True if the data is loaded, false otherwise */ - public static async loadIRPFModel(region : string, year : string) { - return await IrpfService.load(region, year); + public static async loadTaxModel(region : string, year : string) { + return await TaxService.load(region, year); } /** @@ -112,7 +113,7 @@ export default class HomeCore extends ViewCore { * @param paymentNumber The payment number */ public static setPaymentNumber(paymentNumber : number) { - IrpfService.setPaymentNumber(paymentNumber); + TaxService.getTaxModel().setPaymentNumber(paymentNumber); } diff --git a/src/views/home/home.view.ts b/src/views/home/home.view.ts index 5b7bb54..dd2d18d 100644 --- a/src/views/home/home.view.ts +++ b/src/views/home/home.view.ts @@ -36,8 +36,8 @@ export default class HomeView extends ViewUI { private static readonly MENU_OPTION_CLASS = "menu-option"; private static readonly REGION_OPTION_CLASS = "region-option"; private static readonly YEAR_OPTION_CLASS = "year-option"; + private static readonly LANG_OPTION_CLASS = "lang-option"; private static readonly SELECTED_CLASS = "selected"; - private static readonly THEME_CLASS = "theme"; private result : UIComponent; @@ -54,11 +54,27 @@ export default class HomeView extends ViewUI { HomeCore.region = params[0] || HomeView.DEFAULT_REGION; HomeCore.year = params[1] || `${new Date().getFullYear()}`; HomeCore.grossSalary = +params[2] || HomeView.MIN_SALARY; - + Config.setTitle(`${Config.Base.app_name} - ${HomeCore.grossSalary > 0 ? HomeCore.grossSalary + "€" : Text.home.title}`); if(Browser.isSmallDevice()){ this.element.classList.add(HomeView.MOBILE_CLASS); + + let scrollstarted; + this.setEvents({ + touchmove: (event) => { + + if(Date.now() - scrollstarted < 1000){ + event.preventDefault(); + return; + } + + + scrollstarted = Date.now(); + this.toggleMobileMenu(); + + } + }); } const calcView = new UIComponent({ @@ -79,8 +95,6 @@ export default class HomeView extends ViewUI { calcMenu.appendTo(this); calcView.appendTo(this); this.appendTo(container); - - this.setClasses(["showing"]); } @@ -134,7 +148,7 @@ export default class HomeView extends ViewUI { }); regionButton.element.classList.add(HomeView.SELECTED_CLASS); - await this.loadIRPFModel(HomeCore.region, HomeCore.year); + await this.loadTaxModel(HomeCore.region, HomeCore.year); this.showCalcResults((document.getElementById(HomeView.SALARY_INPUT_ID) as HTMLInputElement).valueAsNumber); this.toggleMobileMenu(); @@ -162,7 +176,7 @@ export default class HomeView extends ViewUI { HomeCore.AVAILABLE_YEARS.forEach(year => { - const yearButtonClasses = [Gtdf.BOX_ROW,Gtdf.BOX_CENTER,HomeView.MENU_OPTION_CLASS, HomeView.YEAR_OPTION_CLASS]; + const yearButtonClasses = [Gtdf.BOX_ROW,Gtdf.BOX_CENTER,HomeView.MENU_OPTION_CLASS, HomeView.LANG_OPTION_CLASS]; const selected = year == HomeCore.year if(selected) yearButtonClasses.push(HomeView.SELECTED_CLASS); @@ -175,14 +189,11 @@ export default class HomeView extends ViewUI { click: async () => { HomeCore.year = year; - const options = menu.element.querySelectorAll(`.${HomeView.MENU_OPTION_CLASS}.${HomeView.YEAR_OPTION_CLASS}`); - - options.forEach(option => { - option.classList.remove(HomeView.SELECTED_CLASS); - }); - + const options = menu.element.querySelectorAll(`.${HomeView.MENU_OPTION_CLASS}.${HomeView.LANG_OPTION_CLASS}`); + options.forEach(option => option.classList.remove(HomeView.SELECTED_CLASS)); + yearButton.element.classList.add(HomeView.SELECTED_CLASS); - await this.loadIRPFModel(HomeCore.region, HomeCore.year); + await this.loadTaxModel(HomeCore.region, HomeCore.year); this.showCalcResults((document.getElementById(HomeView.SALARY_INPUT_ID) as HTMLInputElement).valueAsNumber); this.toggleMobileMenu(); } @@ -211,7 +222,7 @@ export default class HomeView extends ViewUI { for (const lang in languages) { - const languageButtonClasses = [Gtdf.BOX_ROW,Gtdf.BOX_CENTER,HomeView.MENU_OPTION_CLASS]; + const languageButtonClasses = [Gtdf.BOX_ROW,Gtdf.BOX_CENTER,HomeView.MENU_OPTION_CLASS, HomeView.YEAR_OPTION_CLASS]; const selected = languages[lang] == Config.getLanguage() if(selected) @@ -306,51 +317,14 @@ export default class HomeView extends ViewUI { id: "footer", text: `Akrck02 / Rayxnor - ${new Date().getFullYear()}`, }); - - const darkTheme = Config.isDarkTheme(); - const themeToggle = new UIComponent({ - type: HTML.DIV, - text: MaterialIcons.get(darkTheme ? "light_mode": "dark_mode" , - { - size: "24", - fill: darkTheme ? "#ccc" : "#222", - }).toHTML(), - classes: [Gtdf.BOX_ROW, Gtdf.BOX_CENTER, HomeView.THEME_CLASS], - }); - - themeToggle.setEvents({ - click: () => { - const theme = Config.toggleTheme(); - themeToggle.element.innerHTML = MaterialIcons.get(`${theme}_mode`,{size: "24",fill: Config.isDarkTheme() ? "#fff" : "#222",}).toHTML() - } - }); - - const settingsButton = new UIComponent({ - type: HTML.DIV, - text: MaterialIcons.get("tune", - { - size: "24", - fill: darkTheme ? "#ccc" : "#222", - }).toHTML(), - classes: [Gtdf.BOX_ROW, Gtdf.BOX_CENTER, "settings"], - }); - - settingsButton.appendTo(mainFrame); - const view = this; - settingsButton.setEvents({ - click: () => { - view.toggleMobileMenu(); - } - }); - salaryInput.setEvents({ input: () => { this.showCalcResults(+salaryInput.getValue()); } }); - await this.loadIRPFModel(HomeCore.region, HomeCore.year); + await this.loadTaxModel(HomeCore.region, HomeCore.year); if(HomeCore.grossSalary > 0){ await this.showCalcResults(HomeCore.grossSalary); @@ -359,15 +333,14 @@ export default class HomeView extends ViewUI { title.appendTo(mainFrame); salaryInputPanel.appendTo(mainFrame); this.result.appendTo(mainFrame); - themeToggle.appendTo(mainFrame); mainFrame.appendTo(parent); footer.appendTo(parent); } - async loadIRPFModel(region : string, year : string) { - if(!await HomeCore.loadIRPFModel(HomeCore.region, HomeCore.year)){ + async loadTaxModel(region : string, year : string) { + if(!await HomeCore.loadTaxModel(HomeCore.region, HomeCore.year)){ HomeCore.cleanIrpfModel(); this.result.clean(); From 383307655827be301b194c43a3d111a20831a1d4 Mon Sep 17 00:00:00 2001 From: Akrck02 Date: Mon, 15 Jan 2024 17:53:39 +0100 Subject: [PATCH 2/6] [ui] tweaking slide --- app/styles/src/views/home/HomeView.css | 20 +++++++++- environment.js | 52 ++++++++++++++++++++++++++ src/views/home/home.view.ts | 52 ++++++++++++++++++-------- 3 files changed, 106 insertions(+), 18 deletions(-) create mode 100644 environment.js diff --git a/app/styles/src/views/home/HomeView.css b/app/styles/src/views/home/HomeView.css index 0ac0594..e2ed65d 100644 --- a/app/styles/src/views/home/HomeView.css +++ b/app/styles/src/views/home/HomeView.css @@ -225,15 +225,31 @@ view#home.mobile #calc-menu{ position: fixed; background: #222; border-radius: 2rem 2rem 0 0; - padding: 1.5rem 1.5rem 0 1.5rem ; + padding: 3rem 1.5rem 0 1.5rem ; z-index: 999; height: 87%; width: 100%; - top: 100%; + top: 80%; box-shadow: 0rem 0rem 1rem rgba(0,0,0,.1); transition: top .5s ease-out; } +/** a bar to drag **/ +view#home.mobile #calc-menu::after { + + content: ''; + position: absolute; + top: 1rem; + left: 50%; + transform: translateX(-50%); + width: 4rem; + height: .25rem; + background: rgba(255,255,255,.2); + border-radius: 1rem; + cursor: pointer; + transition: .5s; +} + view#home.mobile #calc-menu.show{ top: 13%; } diff --git a/environment.js b/environment.js new file mode 100644 index 0000000..004d44a --- /dev/null +++ b/environment.js @@ -0,0 +1,52 @@ +const path = require("path"); + +// Redeclaring the Nodejs global variable object +global.root = path.resolve(__dirname + "/"); + +function main(environment = "development"){ + const fs = require("fs"); + + if(update !== true){ + update = false; + } + + // Read package.json + const packageFile = fs.openSync(path.join(global.root,"package.json"), "r"); + const packageJsonFile = JSON.parse(fs.readFileSync(packageFile, "utf8")); + fs.closeSync(packageFile); + + // update version + if(update){ + versionJsonFile.VERSION = semver(versionJsonFile.VERSION, type, prefix); + } + + // Change the environment + if(env == "-d") { + versionJsonFile.ENVIRONMENT = "development"; + } else { + versionJsonFile.ENVIRONMENT = "production"; + } + + // open config.ts + const configFile = fs.openSync(path.join(global.root,"client/src/config/config.ts"), "r"); + const configString = fs.readFileSync(configFile, "utf8"); + fs.closeSync(configFile); + + // update + let configStringUpdated = ""; + + if(env == "-d") { + configStringUpdated = configString.replaceAll("../#/", "index-dev.html#/"); + } + else { + configStringUpdated = configString.replaceAll("index-dev.html#/","../#/"); + } + + const configFileUpdated = fs.openSync("client/src/config/config.ts", "w"); + fs.writeFileSync(configFileUpdated, configStringUpdated, "utf8"); + fs.closeSync(configFileUpdated); + +} + +const args = process.argv.slice(2); +main(args[0], args[1], args[2], args[3]); \ No newline at end of file diff --git a/src/views/home/home.view.ts b/src/views/home/home.view.ts index dd2d18d..ca3062d 100644 --- a/src/views/home/home.view.ts +++ b/src/views/home/home.view.ts @@ -59,22 +59,6 @@ export default class HomeView extends ViewUI { if(Browser.isSmallDevice()){ this.element.classList.add(HomeView.MOBILE_CLASS); - - let scrollstarted; - this.setEvents({ - touchmove: (event) => { - - if(Date.now() - scrollstarted < 1000){ - event.preventDefault(); - return; - } - - - scrollstarted = Date.now(); - this.toggleMobileMenu(); - - } - }); } const calcView = new UIComponent({ @@ -109,6 +93,36 @@ export default class HomeView extends ViewUI { classes: [Gtdf.BOX_COLUMN], }); + let touchStartX = 0; + let touchStartY = 0; + + + + menu.setEvents({ + touchstart: (event) => { + const firstTouch = this.getTouches(event)[0]; + touchStartX = firstTouch.clientX; + touchStartY = firstTouch.clientY; + }, + + touchmove: (event) => { + const currentTouch = this.getTouches(event)[0]; + + const touchMoveX = currentTouch.clientX + const touchMoveY = currentTouch.clientY; + + const deltaX = touchMoveX - touchStartX; + const deltaY = touchMoveY - touchStartY; + + if ( Math.abs(deltaX) > Math.abs(deltaY) ) { + event.preventDefault(); + }else{ + + } + } + + }); + const regionTitle = new UIComponent({ type: HTML.H3, text: `${Text.home.regions}`, @@ -251,6 +265,12 @@ export default class HomeView extends ViewUI { } + getTouches(evt) { + return evt.touches || // browser API + evt.originalEvent.touches; // jQuery + } + + async showCalcView(parent : UIComponent) : Promise { const mainFrame = new UIComponent({ From b64f8b8ae93c87c8d5abfb7b5f1ce63394d5bdda Mon Sep 17 00:00:00 2001 From: akrck02 Date: Tue, 16 Jan 2024 00:22:53 +0100 Subject: [PATCH 3/6] [ui] gestures ? --- app/styles/src/views/home/HomeView.css | 3 +-- src/services/tax.service.ts | 4 ++++ src/services/taxes/tax.model.ts | 4 ++++ src/views/home/home.view.core.ts | 8 ++++++++ src/views/home/home.view.ts | 25 +++++++++++++++++++++---- 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/app/styles/src/views/home/HomeView.css b/app/styles/src/views/home/HomeView.css index e2ed65d..b5d43a7 100644 --- a/app/styles/src/views/home/HomeView.css +++ b/app/styles/src/views/home/HomeView.css @@ -229,14 +229,13 @@ view#home.mobile #calc-menu{ z-index: 999; height: 87%; width: 100%; - top: 80%; + top: 95%; box-shadow: 0rem 0rem 1rem rgba(0,0,0,.1); transition: top .5s ease-out; } /** a bar to drag **/ view#home.mobile #calc-menu::after { - content: ''; position: absolute; top: 1rem; diff --git a/src/services/tax.service.ts b/src/services/tax.service.ts index 2d869b1..4d82edf 100644 --- a/src/services/tax.service.ts +++ b/src/services/tax.service.ts @@ -31,6 +31,10 @@ export default class TaxService { return TaxService.taxModel; } + static isDefaultPaymentNumber() { + return TaxService.taxModel.isDefaultPaymentNumber(); + } + /** * Clean the service variables */ diff --git a/src/services/taxes/tax.model.ts b/src/services/taxes/tax.model.ts index ff10262..2aeddb5 100644 --- a/src/services/taxes/tax.model.ts +++ b/src/services/taxes/tax.model.ts @@ -217,6 +217,10 @@ export default class TaxModel { this.taxesMonthNumber = taxesMonthNumber; } + isDefaultPaymentNumber() { + return this.paymentNumber === TaxModel.DEFAULT_PAYMENT_NUMBER; + } + clean() { this.irpfRanges = undefined; this.taxes = undefined; diff --git a/src/views/home/home.view.core.ts b/src/views/home/home.view.core.ts index 393fd1f..4560355 100644 --- a/src/views/home/home.view.core.ts +++ b/src/views/home/home.view.core.ts @@ -115,6 +115,14 @@ export default class HomeCore extends ViewCore { public static setPaymentNumber(paymentNumber : number) { TaxService.getTaxModel().setPaymentNumber(paymentNumber); } + + /** + * Get the payment number + * @returns The payment number + */ + public static isDefaultPaymentNumber() { + return TaxService.getTaxModel().isDefaultPaymentNumber(); + } } \ No newline at end of file diff --git a/src/views/home/home.view.ts b/src/views/home/home.view.ts index ca3062d..c9f1bf2 100644 --- a/src/views/home/home.view.ts +++ b/src/views/home/home.view.ts @@ -116,11 +116,16 @@ export default class HomeView extends ViewUI { if ( Math.abs(deltaX) > Math.abs(deltaY) ) { event.preventDefault(); + this.openMobileMenu(); }else{ - + this.openMobileMenu(); } + }, + click : () => { + console.log("click"); + + this.toggleMobileMenu(); } - }); const regionTitle = new UIComponent({ @@ -415,8 +420,10 @@ export default class HomeView extends ViewUI { const salaryResult = this.createValueDataComponent(Text.home.salary, `${salary}€`); salaryResult.appendTo(this.result); - const extraPaymentResult = this.createValueDataComponent(Text.home.extra, `${extraPayment}€`); - extraPaymentResult.appendTo(this.result); + if(HomeCore.isDefaultPaymentNumber()){ + const extraPaymentResult = this.createValueDataComponent(Text.home.extra, `${extraPayment}€`); + extraPaymentResult.appendTo(this.result); + } const irpfPercentageResult = this.createValueDataComponent(Text.home.incomeTax, `${irpfPercentage}%`); irpfPercentageResult.appendTo(this.result); @@ -456,4 +463,14 @@ export default class HomeView extends ViewUI { menu.classList.toggle("show"); } + openMobileMenu() { + const menu = document.getElementById("calc-menu"); + menu.classList.add("show"); + } + + closeMobileMenu() { + const menu = document.getElementById("calc-menu"); + menu.classList.remove("show"); + } + } \ No newline at end of file From 9bcf2f2b713120c68d1eedf88195795698744cb9 Mon Sep 17 00:00:00 2001 From: aegusquiza Date: Tue, 16 Jan 2024 10:49:35 +0100 Subject: [PATCH 4/6] [ui] updating menu access --- app/styles/src/views/home/HomeView.css | 4 +- src/views/home/home.view.ts | 66 ++++++++++---------------- 2 files changed, 27 insertions(+), 43 deletions(-) diff --git a/app/styles/src/views/home/HomeView.css b/app/styles/src/views/home/HomeView.css index b5d43a7..33c8b65 100644 --- a/app/styles/src/views/home/HomeView.css +++ b/app/styles/src/views/home/HomeView.css @@ -127,12 +127,12 @@ view#home .theme { view#home footer { position: absolute; - bottom: 20px; + bottom: 1.5rem; font-size: 1rem; } view#home.mobile footer { - bottom: 1rem; + bottom: 4rem; font-size: .7rem; } diff --git a/src/views/home/home.view.ts b/src/views/home/home.view.ts index c9f1bf2..ef68260 100644 --- a/src/views/home/home.view.ts +++ b/src/views/home/home.view.ts @@ -71,8 +71,19 @@ export default class HomeView extends ViewUI { type: HTML.DIV, id: HomeView.CALC_MENU_ID, classes: [Gtdf.BOX_COLUMN,Gtdf.BOX_X_START,Gtdf.BOX_X_CENTER], + attributes : { + draggable : "true" + } + }); + + calcMenu.setEvents({ + click : ()=> { + this.toggleMobileMenu(); + + } }); + await this.showMenu(calcMenu); await this.showCalcView(calcView); @@ -92,42 +103,7 @@ export default class HomeView extends ViewUI { id: HomeView.MENU_ID, classes: [Gtdf.BOX_COLUMN], }); - - let touchStartX = 0; - let touchStartY = 0; - - - menu.setEvents({ - touchstart: (event) => { - const firstTouch = this.getTouches(event)[0]; - touchStartX = firstTouch.clientX; - touchStartY = firstTouch.clientY; - }, - - touchmove: (event) => { - const currentTouch = this.getTouches(event)[0]; - - const touchMoveX = currentTouch.clientX - const touchMoveY = currentTouch.clientY; - - const deltaX = touchMoveX - touchStartX; - const deltaY = touchMoveY - touchStartY; - - if ( Math.abs(deltaX) > Math.abs(deltaY) ) { - event.preventDefault(); - this.openMobileMenu(); - }else{ - this.openMobileMenu(); - } - }, - click : () => { - console.log("click"); - - this.toggleMobileMenu(); - } - }); - const regionTitle = new UIComponent({ type: HTML.H3, text: `${Text.home.regions}`, @@ -157,8 +133,10 @@ export default class HomeView extends ViewUI { classes: regionButtonClasses, events: { - click: async () => { - + click: async (e) => { + e.preventDefault(); + e.stopPropagation(); + HomeCore.region = region; const options = menu.element.querySelectorAll(`.${HomeView.MENU_OPTION_CLASS}.${HomeView.REGION_OPTION_CLASS}`); @@ -169,7 +147,7 @@ export default class HomeView extends ViewUI { regionButton.element.classList.add(HomeView.SELECTED_CLASS); await this.loadTaxModel(HomeCore.region, HomeCore.year); this.showCalcResults((document.getElementById(HomeView.SALARY_INPUT_ID) as HTMLInputElement).valueAsNumber); - this.toggleMobileMenu(); + //this.toggleMobileMenu(); } } @@ -206,7 +184,10 @@ export default class HomeView extends ViewUI { classes: yearButtonClasses, events: { - click: async () => { + click: async (e) => { + e.preventDefault(); + e.stopPropagation(); + HomeCore.year = year; const options = menu.element.querySelectorAll(`.${HomeView.MENU_OPTION_CLASS}.${HomeView.LANG_OPTION_CLASS}`); options.forEach(option => option.classList.remove(HomeView.SELECTED_CLASS)); @@ -214,7 +195,7 @@ export default class HomeView extends ViewUI { yearButton.element.classList.add(HomeView.SELECTED_CLASS); await this.loadTaxModel(HomeCore.region, HomeCore.year); this.showCalcResults((document.getElementById(HomeView.SALARY_INPUT_ID) as HTMLInputElement).valueAsNumber); - this.toggleMobileMenu(); + //this.toggleMobileMenu(); } } }); @@ -253,7 +234,10 @@ export default class HomeView extends ViewUI { classes: languageButtonClasses, events: { - click: async () => { + click: async (e) => { + e.preventDefault(); + e.stopPropagation(); + HomeCore.setLanguage(languages[lang]); await TextBundle.reloadSignal.emit(); SignalBuffer.search("changeView").emit("home"); From 53b3ae6da1b0cb3a6138e360d7da1e55be7947ea Mon Sep 17 00:00:00 2001 From: aegusquiza Date: Tue, 16 Jan 2024 13:55:33 +0100 Subject: [PATCH 5/6] [observer][broken] road to observer pattern on UI and tax calculations --- src/lib/gtdf/core/observable/observer.ts | 2 + src/services/tax.service.ts | 12 ++-- src/services/taxes/tax.model.ts | 25 ++------- src/services/taxes/tax.observable.ts | 11 ++++ src/views/home/home.view.core.ts | 18 ++++-- src/views/home/home.view.ts | 7 ++- src/views/router.ts | 3 +- src/views/test/test.view.ts | 70 ++++++++++++++++++++++++ 8 files changed, 112 insertions(+), 36 deletions(-) create mode 100644 src/services/taxes/tax.observable.ts create mode 100644 src/views/test/test.view.ts diff --git a/src/lib/gtdf/core/observable/observer.ts b/src/lib/gtdf/core/observable/observer.ts index be173a2..68aad0b 100644 --- a/src/lib/gtdf/core/observable/observer.ts +++ b/src/lib/gtdf/core/observable/observer.ts @@ -20,6 +20,8 @@ export class Observable implements IObservable { this.content = {}; this.content = new Proxy(this.content, { set: function(target, key, value) { + console.log("aldsklñ"); + target[key] = value; a.notify(); return true; diff --git a/src/services/tax.service.ts b/src/services/tax.service.ts index 4d82edf..fb0db32 100644 --- a/src/services/tax.service.ts +++ b/src/services/tax.service.ts @@ -1,9 +1,10 @@ import { Config } from "../config/config.js"; import TaxModel from "./taxes/tax.model.js"; +import TaxObservableModel from "./taxes/tax.observable.js"; export default class TaxService { - private static taxModel = new TaxModel(); + private static taxModel : TaxModel = new TaxModel(); /** * Load the irpf data @@ -17,8 +18,8 @@ export default class TaxService { const irpfRanges = await fetch(`${Config.Path.irpf_info}${year}/irpfRanges-${province}.json`).then(response => response.json()); const taxes = await fetch(`${Config.Path.irpf_info}${year}/taxes.json`).then(response => response.json()); - this.taxModel.setIrpfRanges(irpfRanges); - this.taxModel.setTaxes(taxes); + this.taxModel.irpfRanges = irpfRanges; + this.taxModel.taxes = taxes; return true; } catch (error) { @@ -27,14 +28,11 @@ export default class TaxService { } - static getTaxModel() : TaxModel { - return TaxService.taxModel; - } - static isDefaultPaymentNumber() { return TaxService.taxModel.isDefaultPaymentNumber(); } + /** * Clean the service variables */ diff --git a/src/services/taxes/tax.model.ts b/src/services/taxes/tax.model.ts index 2aeddb5..858e8fd 100644 --- a/src/services/taxes/tax.model.ts +++ b/src/services/taxes/tax.model.ts @@ -1,3 +1,4 @@ + interface ITaxes { contingenciasComunes : number, atur : number, @@ -18,11 +19,11 @@ export default class TaxModel { private static readonly DEFAULT_PAYMENT_NUMBER = 14; private static readonly DEFAULT_TAXES_MONTH_NUMBER = 12; - private paymentNumber : number; - private taxesMonthNumber : number; + public paymentNumber : number; + public taxesMonthNumber : number; - private irpfRanges : Map; - private taxes : ITaxes; + public irpfRanges : Map; + public taxes : ITaxes; constructor( paymentNumber : number = TaxModel.DEFAULT_PAYMENT_NUMBER , @@ -201,22 +202,6 @@ export default class TaxModel { } - setIrpfRanges(irpfRanges) { - this.irpfRanges = irpfRanges; - } - - setTaxes(taxes) { - this.taxes = taxes; - } - - setPaymentNumber(paymentNumber : number) { - this.paymentNumber = paymentNumber; - } - - setTaxesMonthNumber(taxesMonthNumber : number) { - this.taxesMonthNumber = taxesMonthNumber; - } - isDefaultPaymentNumber() { return this.paymentNumber === TaxModel.DEFAULT_PAYMENT_NUMBER; } diff --git a/src/services/taxes/tax.observable.ts b/src/services/taxes/tax.observable.ts new file mode 100644 index 0000000..ffb1209 --- /dev/null +++ b/src/services/taxes/tax.observable.ts @@ -0,0 +1,11 @@ +import { Observable } from "../../lib/gtdf/core/observable/observer.js"; +import TaxModel from "./tax.model.js"; + +export default class TaxObservableModel extends Observable { + + constructor() { + super(); + this.content = new TaxModel(); + } + +} \ No newline at end of file diff --git a/src/views/home/home.view.core.ts b/src/views/home/home.view.core.ts index 4560355..eb43fe7 100644 --- a/src/views/home/home.view.core.ts +++ b/src/views/home/home.view.core.ts @@ -4,6 +4,7 @@ import { ViewCore } from "../../lib/gtdf/views/ViewCore.js"; import TaxService from "../../services/tax.service.js"; import LanguageService from "../../services/language.service.js"; import { SalaryTime } from "../../services/taxes/tax.model.js"; +import { Observable } from "../../lib/gtdf/core/observable/observer.js"; export default class HomeCore extends ViewCore { @@ -24,13 +25,15 @@ export default class HomeCore extends ViewCore { public static grossSalary : number; public static paymentNumber : number; + public static taxModel : Observable = new Observable(); + /** * Get the irpf percentage * @param grossSalary The gross salary * @returns The irpf percentage */ static getIRPFPercentage(grossSalary : number) { - return TaxService.getTaxModel().getIrpf(grossSalary); + return TaxService.getTaxModel().content.getIrpf(grossSalary); } /** @@ -39,7 +42,7 @@ export default class HomeCore extends ViewCore { * @returns The extra payment */ static getExtraPayment(grossSalary : number) { - return TaxService.getTaxModel().extraPayment(grossSalary); + return TaxService.getTaxModel().content.extraPayment(grossSalary); } /** @@ -48,7 +51,7 @@ export default class HomeCore extends ViewCore { * @returns */ static getExtraPaymentWithMultipleSalaries(salaries : SalaryTime[]) { - return TaxService.getTaxModel().extraPaymentWithMultipleSalaries(salaries); + return TaxService.getTaxModel().content.extraPaymentWithMultipleSalaries(salaries); } @@ -58,13 +61,16 @@ export default class HomeCore extends ViewCore { * @returns The salary with taxes */ static getSalary(grossSalary : number) { - return TaxService.getTaxModel().calcWithTaxes(grossSalary); + return TaxService.getTaxModel().content.calcWithTaxes(grossSalary); } /** * Get the irpf value */ static cleanIrpfModel() { + this.taxModel.content.irpf_ranges = {}; + this.taxModel.content.taxes = {}; + TaxService.clean(); } @@ -113,7 +119,7 @@ export default class HomeCore extends ViewCore { * @param paymentNumber The payment number */ public static setPaymentNumber(paymentNumber : number) { - TaxService.getTaxModel().setPaymentNumber(paymentNumber); + HomeCore.taxModel.content.paymentNumber = paymentNumber; } /** @@ -121,7 +127,7 @@ export default class HomeCore extends ViewCore { * @returns The payment number */ public static isDefaultPaymentNumber() { - return TaxService.getTaxModel().isDefaultPaymentNumber(); + return HomeCore.taxModel.content.isDefaultPaymentNumber(); } diff --git a/src/views/home/home.view.ts b/src/views/home/home.view.ts index ef68260..2d76b03 100644 --- a/src/views/home/home.view.ts +++ b/src/views/home/home.view.ts @@ -10,6 +10,9 @@ import { Browser } from "../../lib/gtdf/components/browser.js"; import HomeCore from "./home.view.core.js"; import MaterialIcons from "../../lib/gtdf/resources/MaterialIcons.js"; import SignalBuffer from "../../core/signal.buffer.js"; +import { IObserver, Observable } from "../../lib/gtdf/core/observable/observer.js"; +import { ObservableUIComponent } from "../../lib/gtdf/core/observable/observable.uicomponent.js"; +import TaxModel from "../../services/taxes/tax.model.js"; @Route(["","calculate", undefined]) @Singleton() @@ -47,6 +50,7 @@ export default class HomeView extends ViewUI { id: HomeView.ID, classes: [Gtdf.BOX_ROW, Gtdf.BOX_X_START, Gtdf.BOX_Y_CENTER], }); + } public async show(params : string[], container : UIComponent) : Promise { @@ -79,7 +83,6 @@ export default class HomeView extends ViewUI { calcMenu.setEvents({ click : ()=> { this.toggleMobileMenu(); - } }); @@ -237,7 +240,7 @@ export default class HomeView extends ViewUI { click: async (e) => { e.preventDefault(); e.stopPropagation(); - + HomeCore.setLanguage(languages[lang]); await TextBundle.reloadSignal.emit(); SignalBuffer.search("changeView").emit("home"); diff --git a/src/views/router.ts b/src/views/router.ts index 8b05463..5024dc7 100644 --- a/src/views/router.ts +++ b/src/views/router.ts @@ -9,6 +9,7 @@ import { ISingleton, Singleton } from "../lib/gtdf/decorators/Singleton.js"; import { StaticImplements } from "../lib/gtdf/core/static/static.interface.js"; import { Routes } from "../lib/gtdf/decorators/Route.js"; import { Signal } from "../lib/gtdf/core/signals/signal.js"; +import TestView from "./test/test.view.js"; @Singleton() @StaticImplements>() @@ -71,7 +72,7 @@ export default class Router implements IObserver { await this.load(params); } - Endpoints = [HomeView, ErrorView]; + Endpoints = [HomeView, ErrorView, TestView]; /** * Load the app state with the given params diff --git a/src/views/test/test.view.ts b/src/views/test/test.view.ts new file mode 100644 index 0000000..d36ad87 --- /dev/null +++ b/src/views/test/test.view.ts @@ -0,0 +1,70 @@ +import { HTML } from "../../lib/gtdf/components/dom.js"; +import { UIComponent } from "../../lib/gtdf/components/uicomponent.js"; +import { ObservableUIComponent } from "../../lib/gtdf/core/observable/observable.uicomponent.js"; +import { Observable } from "../../lib/gtdf/core/observable/observer.js"; +import { Route } from "../../lib/gtdf/decorators/Route.js"; +import { Singleton } from "../../lib/gtdf/decorators/Singleton.js"; +import { ViewUI } from "../../lib/gtdf/views/ViewUI.js"; +import { Gtdf } from "../../lib/others/gtdf.js"; +import TaxModel from "../../services/taxes/tax.model.js"; +import TaxObservableModel from "../../services/taxes/tax.observable.js"; + +@Route(["test"]) +@Singleton() +export default class TestView extends ViewUI { + + public constructor(){ + super({ + type: HTML.VIEW, + classes: [Gtdf.BOX_ROW, Gtdf.BOX_CENTER], + styles : { + width : "100%", + height: "100vh" + } + }); + + } + + public async show(params : string[], container : UIComponent) : Promise { + + const observable = new Observable(); + const input = new ObservableUIComponent({ + type : "div", + text : "Change observable", + classes : [Gtdf.BOX_CENTER,Gtdf.TEXT_CENTER], + styles : { + background : "blue", + width : "15rem", + height : "6rem", + borderRadius : ".35rem", + color: "#1A1A1A", + fontSize : "1.35rem", + transition : ".35s" + }, observable : observable + }) + + input.setEvents({ + click : ()=> { + observable.content.paymentNumber = Math.random() > .5 ? 12:14 + } + }) + + + input.update = async () => { + + const rgb = () => Math.round(Math.random()*255) + + input.setStyles({ + background : `rgb(${rgb()},${rgb()},${rgb()})` + }) + + console.log(observable.content); + + } + input.appendTo(this); + + this.appendTo(container) + + } + +} \ No newline at end of file From 594a89a9d1681b9e7b31ac376aaef9d66508e0b1 Mon Sep 17 00:00:00 2001 From: akrck02 Date: Wed, 17 Jan 2024 23:03:01 +0100 Subject: [PATCH 6/6] [signals] added signals to UI and modulating components --- app/styles/src/views/home/HomeView.css | 8 +- resources/i18n/en/regions.json | 3 + resources/i18n/es/regions.json | 3 + src/app.ts | 4 +- src/core/initializer.ts | 39 +- src/lang/language.ts | 4 +- src/lang/text.ts | 6 +- src/lib/gtdf/core/observable/observer.ts | 4 +- src/services/language.service.ts | 35 +- src/services/tax.service.ts | 8 +- src/services/taxes/tax.model.ts | 16 +- src/start.ts | 2 + .../home/components/home.calculation.panel.ts | 163 +++++++ src/views/home/components/home.menu.ts | 252 ++++++++++ src/views/home/components/home.value.tag.ts | 53 +++ src/views/home/home.view.core.ts | 241 +++++----- src/views/home/home.view.ts | 441 +----------------- src/views/router.ts | 1 - 18 files changed, 697 insertions(+), 586 deletions(-) create mode 100644 resources/i18n/en/regions.json create mode 100644 resources/i18n/es/regions.json create mode 100644 src/views/home/components/home.calculation.panel.ts create mode 100644 src/views/home/components/home.menu.ts create mode 100644 src/views/home/components/home.value.tag.ts diff --git a/app/styles/src/views/home/HomeView.css b/app/styles/src/views/home/HomeView.css index 33c8b65..c60b18d 100644 --- a/app/styles/src/views/home/HomeView.css +++ b/app/styles/src/views/home/HomeView.css @@ -98,16 +98,16 @@ view#home button:hover { background: var(--input-bg-hover); } -view#home span#value-data { +view#home span.value-data { margin-bottom: .5rem; } -view#home span#value-data label.label { +view#home span.value-data label.label { font-size: 1.1rem; color: var(--text); } -view#home span#value-data span.value { +view#home span.value-data span.value { border-radius: 1rem; padding: .2rem .75rem; background-color: rgba(255,255,255,.055); @@ -144,7 +144,7 @@ view#home #calc-menu { color: var(--text); overflow-y: auto; padding: 1rem; - background-color:rgba(0,0,0,.05); + background-color:rgba(255,255,255,.035) } view#home #calc-frame { diff --git a/resources/i18n/en/regions.json b/resources/i18n/en/regions.json new file mode 100644 index 0000000..41247bf --- /dev/null +++ b/resources/i18n/en/regions.json @@ -0,0 +1,3 @@ +{ + "paisvasco": "Basque Country" +} \ No newline at end of file diff --git a/resources/i18n/es/regions.json b/resources/i18n/es/regions.json new file mode 100644 index 0000000..1a4c6ee --- /dev/null +++ b/resources/i18n/es/regions.json @@ -0,0 +1,3 @@ +{ + "paisvasco": "País Vasco" +} \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index ca97b1c..7d87d1e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -51,8 +51,8 @@ export default class App { * app will redirect the user to an 404 error page. */ async load(){ - await Initializer.subscribeInitializables(); - await Initializer.notify(); + await Initializer.instance().subscribeInitializables(); + await Initializer.instance().notify(); const params = URLs.getParametersByIndex(window.location.hash.slice(1).toLowerCase(),1); this.router.load(params); diff --git a/src/core/initializer.ts b/src/core/initializer.ts index 9a39d81..2a807a3 100644 --- a/src/core/initializer.ts +++ b/src/core/initializer.ts @@ -3,32 +3,27 @@ import MaterialIcons from "../lib/gtdf/resources/MaterialIcons.js"; import { IObserver, Observable } from "../lib/gtdf/core/observable/observer.js"; import { Signal } from "../lib/gtdf/core/signals/signal.js"; import { Config, Configuration } from "../config/config.js"; +import { ISingleton, Singleton } from "../lib/gtdf/decorators/Singleton.js"; +import { StaticImplements } from "../lib/gtdf/core/static/static.interface.js"; +@Singleton() +@StaticImplements>() export default class Initializer { private static readonly SIGNAL_ID : string = "init"; - private static performed : boolean = false; - private static _instance : Initializer; + private performed : boolean = false; + static _instance : Initializer; + static instance; + private initSignal : Signal; - private static subscribers : IObserver[] = [ + private subscribers : IObserver[] = [ Configuration.instance, MaterialIcons.instance.loader, TextBundle.instance ]; - /** - * Get an instance of Initializer - */ - public static get instance() : Initializer { - - if (!Initializer._instance) { - Initializer._instance = new Initializer(); - } - - return Initializer._instance; - } private constructor() { this.initSignal = new Signal(Initializer.SIGNAL_ID); @@ -38,26 +33,26 @@ export default class Initializer { * Subscribe to the init signal * @returns The observable instance */ - public static async subscribeInitializables() : Promise { + public async subscribeInitializables() : Promise { - if(Initializer.performed){ + if(this.performed){ return; } - for(let subscriber of Initializer.subscribers){ - await Initializer.instance.initSignal.subscribe(subscriber); + for(let subscriber of this.subscribers){ + await this.initSignal.subscribe(subscriber); } } - public static async notify() : Promise { + public async notify() : Promise { - if(Initializer.performed){ + if(this.performed){ return; } - Initializer.performed = true; - await Initializer.instance.initSignal.emit(); + this.performed = true; + await this.initSignal.emit(); } diff --git a/src/lang/language.ts b/src/lang/language.ts index 266c2e8..7df92db 100644 --- a/src/lang/language.ts +++ b/src/lang/language.ts @@ -39,7 +39,7 @@ export function getLanguage(locale : string) : string { export function getLanguageName(locale: string) : string { if(!locale){ - return Language.ENGLISH; + return Language.SPANISH; } const keys = Object.keys(Language); @@ -53,4 +53,4 @@ export function getLanguageName(locale: string) : string { } return keys[0]; -} \ No newline at end of file +} diff --git a/src/lang/text.ts b/src/lang/text.ts index 46870cb..d168d88 100644 --- a/src/lang/text.ts +++ b/src/lang/text.ts @@ -1,14 +1,14 @@ -import { Config, Configuration } from "../config/config.js"; +import { Config } from "../config/config.js"; import { IObserver } from "../lib/gtdf/core/observable/observer.js"; import { Signal } from "../lib/gtdf/core/signals/signal.js"; - export class TextBundle implements IObserver { private static readonly AVAILABLE_BUNDLES = [ "home", "errors", "info", - "languages" + "languages", + "regions" ]; private static _instance : TextBundle; diff --git a/src/lib/gtdf/core/observable/observer.ts b/src/lib/gtdf/core/observable/observer.ts index 68aad0b..accc944 100644 --- a/src/lib/gtdf/core/observable/observer.ts +++ b/src/lib/gtdf/core/observable/observer.ts @@ -19,9 +19,7 @@ export class Observable implements IObservable { let a = this; this.content = {}; this.content = new Proxy(this.content, { - set: function(target, key, value) { - console.log("aldsklñ"); - + set: function(target, key, value) { target[key] = value; a.notify(); return true; diff --git a/src/services/language.service.ts b/src/services/language.service.ts index c4b830c..8b9adf2 100644 --- a/src/services/language.service.ts +++ b/src/services/language.service.ts @@ -1,13 +1,40 @@ +import { Config } from "../config/config.js"; import { Language } from "../lang/language.js"; +import { StringMap } from "../lib/gtdf/data/strings.js"; export default class LanguageService { - /**et available languages for the application - * @returns The available language list + /** + * Get available languages to add to the select + * @returns The available languages */ - public static getAvailableLanguages() : {[key:string] : string}{ - return Language; + public static getLanguages() : StringMap { + const languages = Language; + const formatted = {}; + + const list = Object.keys(languages) + + list.forEach(lang => { + formatted[lang.toUpperCase().substring(0,1) + lang.toLowerCase().substring(1)] = languages[lang]; + }); + + return formatted; } + /** + * Get available languages to add to the select + * @returns The available languages with names + */ + public static getAvailableLanguagesWithNames() : StringMap { + const languages = Language; + return languages; + } + /** + * Set the app language and reload + * @param selected The selected language + */ + public static setLanguage(selected :string){ + Config.setLanguage(selected); + } } \ No newline at end of file diff --git a/src/services/tax.service.ts b/src/services/tax.service.ts index fb0db32..fdca081 100644 --- a/src/services/tax.service.ts +++ b/src/services/tax.service.ts @@ -25,7 +25,11 @@ export default class TaxService { } catch (error) { return false; } - + + } + + static get() { + return this.taxModel; } static isDefaultPaymentNumber() { @@ -37,7 +41,7 @@ export default class TaxService { * Clean the service variables */ static clean() { - TaxService.taxModel.clean(); + TaxService.taxModel.clear(); } diff --git a/src/services/taxes/tax.model.ts b/src/services/taxes/tax.model.ts index 858e8fd..ca3498f 100644 --- a/src/services/taxes/tax.model.ts +++ b/src/services/taxes/tax.model.ts @@ -48,12 +48,12 @@ export default class TaxModel { */ calcWithTaxes(salary : number) : number { + this.checkIfDataIsLoaded(); + if(salary <= 0){ return 0; } - this.checkIfDataIsLoaded(); - const irpf = this.getIrpfValue(salary); const contingenciasComunes = this.getContingenciasComunesValue(salary); const atur = this.getAturValue(salary); @@ -71,12 +71,12 @@ export default class TaxModel { */ extraPayment(salary : number) : number { + this.checkIfDataIsLoaded(); + if(salary <= 0){ return 0; } - this.checkIfDataIsLoaded(); - const irpf = this.getIrpfValue(salary); return Math.ceil(((salary / this.paymentNumber) - irpf) * 100) / 100; @@ -85,12 +85,12 @@ export default class TaxModel { extraPaymentWithMultipleSalaries(salaries : SalaryTime[]) { + this.checkIfDataIsLoaded(); + if(salaries === undefined || salaries.length === 0) { return 0; } - this.checkIfDataIsLoaded(); - // salario * dias / 180 let totalSalary = 0; for (const salary of salaries) { @@ -125,6 +125,8 @@ export default class TaxModel { */ getIrpf(salary : number) : number { + this.checkIfDataIsLoaded(); + if(salary <= 0){ return 0; } @@ -206,7 +208,7 @@ export default class TaxModel { return this.paymentNumber === TaxModel.DEFAULT_PAYMENT_NUMBER; } - clean() { + clear() { this.irpfRanges = undefined; this.taxes = undefined; } diff --git a/src/start.ts b/src/start.ts index ae16394..09659c3 100644 --- a/src/start.ts +++ b/src/start.ts @@ -1,5 +1,7 @@ +import Initializer from "./core/initializer.js"; import App from "./app.js"; + /** * When the dynamic URL changes loads * the correspoding view from the URL diff --git a/src/views/home/components/home.calculation.panel.ts b/src/views/home/components/home.calculation.panel.ts new file mode 100644 index 0000000..5af79f7 --- /dev/null +++ b/src/views/home/components/home.calculation.panel.ts @@ -0,0 +1,163 @@ +import { Text } from "../../../lang/text.js"; +import { Browser } from "../../../lib/gtdf/components/browser.js"; +import { HTML } from "../../../lib/gtdf/components/dom.js"; +import { UIComponent } from "../../../lib/gtdf/components/uicomponent.js"; +import { Gtdf } from "../../../lib/others/gtdf.js"; +import HomeCore, { ITaxesResult } from "../home.view.core.js"; +import ValueTag from "./home.value.tag.js"; + +export class CalculationPanel extends UIComponent { + + private static readonly CALC_FRAME_ID = "calc-frame"; + private static readonly MAIN_FRAME_ID = "main-frame"; + private static readonly MAIN_TITLE_ID = "main-title"; + private static readonly SALARY_INPUT_PANEL_ID = "salary-input-panel"; + private static readonly SALARY_INPUT_ID = "salary"; + private static readonly PAYMENT_NUMBER_INPUT_ID = "payment-number"; + private static readonly RESULT_ID = "result"; + private static readonly FOOTER_ID = "footer"; + + private result : UIComponent; + + constructor() { + super({ + type: HTML.DIV, + classes: [Gtdf.BOX_COLUMN,Gtdf.BOX_CENTER], + id: CalculationPanel.CALC_FRAME_ID, + }); + + this.show(); + + HomeCore.updateTaxesUISignal.subscribe({ + update: async (data : ITaxesResult) => { + await this.update(data); + } + }); + + HomeCore.taxesCannotBeLoadedSignal.subscribe({ + update: async (data) => { + await this.updateError(data); + } + }); + } + + private show() : void { + + this.clean(); + const mainFrame = new UIComponent({ + type: HTML.DIV, + id: CalculationPanel.MAIN_FRAME_ID, + }); + + const title = new UIComponent({ + type: HTML.H1, + text: Text.home.netSalary, + id : CalculationPanel.MAIN_TITLE_ID, + }); + + const salaryInputPanel = new UIComponent({ + type: HTML.DIV, + id: CalculationPanel.SALARY_INPUT_PANEL_ID, + classes: [Gtdf.BOX_ROW, Gtdf.BOX_CENTER], + }); + + const salaryInput = new UIComponent({ + type: HTML.INPUT, + id: CalculationPanel.SALARY_INPUT_ID, + attributes: { + type: "number", + inputmode:"numeric", + name: "salary", + value : HomeCore.instance().grossSalary > 0 ? HomeCore.instance().grossSalary + "" : "", + min: "0" + }, + }); + salaryInput.appendTo(salaryInputPanel); + + const paymentNumberInput = new UIComponent({ + type: HTML.BUTTON, + text: `${HomeCore.AVAILABLE_PAYMENT_NUMBERS[0]}`, + id: CalculationPanel.PAYMENT_NUMBER_INPUT_ID, + classes: [Gtdf.BOX_ROW, Gtdf.BOX_CENTER], + }); + paymentNumberInput.appendTo(salaryInputPanel); + + paymentNumberInput.setEvents({ + click: async () => { + let paymentNumber = +paymentNumberInput.element.innerHTML; + const index = HomeCore.AVAILABLE_PAYMENT_NUMBERS.indexOf(paymentNumber); + + const newIndex = index + 1 >= HomeCore.AVAILABLE_PAYMENT_NUMBERS.length ? 0 : index + 1; + paymentNumberInput.element.innerHTML = `${HomeCore.AVAILABLE_PAYMENT_NUMBERS[newIndex]}`; + + HomeCore.instance().grossSalary = +salaryInput.getValue(); + await HomeCore.paymentNumberChangedSignal.emit(HomeCore.AVAILABLE_PAYMENT_NUMBERS[newIndex]); + + } + }); + + this.result = new UIComponent({ + type: HTML.DIV, + id: CalculationPanel.RESULT_ID, + }); + + const footer = new UIComponent({ + type: HTML.FOOTER, + id: CalculationPanel.FOOTER_ID, + text: `Akrck02 / Rayxnor - ${new Date().getFullYear()}`, + }); + + salaryInput.setEvents({ + input: () => { + HomeCore.salaryChangedSignal.emit(+salaryInput.getValue()); + } + }); + + title.appendTo(mainFrame); + salaryInputPanel.appendTo(mainFrame); + this.result.appendTo(mainFrame); + + mainFrame.appendTo(this); + footer.appendTo(this); + } + + async update(data : ITaxesResult) : Promise { + + this.result.clean(); + + if(HomeCore.instance().grossSalary > HomeCore.MAX_SALARY){ + + const warning = new UIComponent({ + type: HTML.B, + classes: ["text-error", Gtdf.TEXT_CENTER, Gtdf.BOX_ROW, Gtdf.BOX_CENTER], + text: Text.errors.salaryTooHigh, + }); + warning.appendTo(this.result); + return; + } + + + const salaryResult = new ValueTag(Text.home.salary, `${data.salary}€`); + salaryResult.appendTo(this.result); + + if(HomeCore.instance().isDefaultPaymentNumber()){ + const extraPaymentResult = new ValueTag(Text.home.extra, `${data.extraPayment}€`); + extraPaymentResult.appendTo(this.result); + } + + const irpfPercentageResult = new ValueTag(Text.home.incomeTax, `${data.irpfPercentage}%`); + irpfPercentageResult.appendTo(this.result); + } + + async updateError(data : string) : Promise { + + this.result.clean(); + const warning = new UIComponent({ + type: HTML.B, + classes: ["text-error"], + text: Text.errors.cannotLoadTax, + }); + + warning.appendTo(this.result); + } +} \ No newline at end of file diff --git a/src/views/home/components/home.menu.ts b/src/views/home/components/home.menu.ts new file mode 100644 index 0000000..71672f1 --- /dev/null +++ b/src/views/home/components/home.menu.ts @@ -0,0 +1,252 @@ +import { Config } from "../../../config/config.js"; +import SignalBuffer from "../../../core/signal.buffer.js"; +import { Text, TextBundle } from "../../../lang/text.js"; +import { HTML } from "../../../lib/gtdf/components/dom.js"; +import { UIComponent } from "../../../lib/gtdf/components/uicomponent.js"; +import { Gtdf } from "../../../lib/others/gtdf.js"; +import LanguageService from "../../../services/language.service.js"; +import HomeCore from "../home.view.core.js"; + +export default class TaxMenu extends UIComponent { + + private static readonly MENU_ID = "calc-menu"; + private static readonly INNER_MENU_ID = "menu"; + private static readonly OPTIONS_TITLE_CLASS = "options-title"; + private static readonly BUTTON_CONTAINER_CLASS = "button-container"; + private static readonly MENU_OPTION_CLASS = "menu-option"; + private static readonly REGION_OPTION_CLASS = "region-option"; + private static readonly YEAR_OPTION_CLASS = "year-option"; + private static readonly LANG_OPTION_CLASS = "lang-option"; + private static readonly SELECTED_CLASS = "selected"; + + public innerSpace: UIComponent; + + constructor() { + super({ + type: HTML.DIV, + id: TaxMenu.MENU_ID, + classes: [Gtdf.BOX_COLUMN, Gtdf.BOX_X_START, Gtdf.BOX_X_CENTER], + attributes: { + draggable: "true", + }, + }); + + this.setEvents({ + click : ()=> { + this.toggleMobileMenu(); + } + }); + + this.innerSpace = new UIComponent({ + type: HTML.DIV, + id: TaxMenu.INNER_MENU_ID, + classes: [Gtdf.BOX_COLUMN], + }); + + this.innerSpace.appendTo(this); + this.show(); + } + + /** + * Show the menu + */ + show() { + this.innerSpace.clean(); + this.drawRegionOptions(); + this.drawYearOptions(); + this.drawLanguageOptions(); + } + + /** + * Update the view with the new data + */ + update() {} + + /** + * Draw region options on the menu + */ + drawRegionOptions() { + const regionTitle = new UIComponent({ + type: HTML.H3, + text: `${Text.home.regions}`, + classes: [TaxMenu.OPTIONS_TITLE_CLASS], + }); + + regionTitle.appendTo(this.innerSpace); + + const container = new UIComponent({ + type: HTML.DIV, + classes: [ + Gtdf.BOX_ROW, + Gtdf.BOX_CENTER, + TaxMenu.BUTTON_CONTAINER_CLASS, + ], + }); + + HomeCore.AVAILABLE_REGIONS.forEach((region) => { + + const regionName = Text.regions[region]; + const selected = region == HomeCore.instance().region; + const regionButtonClasses = [ + Gtdf.BOX_ROW, + Gtdf.BOX_CENTER, + TaxMenu.MENU_OPTION_CLASS, + TaxMenu.REGION_OPTION_CLASS, + ]; + + if (selected) { + regionButtonClasses.push(TaxMenu.SELECTED_CLASS); + } + + const regionButton = new UIComponent({ + type: HTML.BUTTON, + text: regionName, + classes: regionButtonClasses, + events: { + click: async (e) => { + e.preventDefault(); + e.stopPropagation(); + + HomeCore.instance().region = region; + const options = this.element.querySelectorAll(`.${TaxMenu.MENU_OPTION_CLASS}.${TaxMenu.REGION_OPTION_CLASS}`); + + options.forEach(option => { + option.classList.remove(TaxMenu.SELECTED_CLASS); + }); + + regionButton.element.classList.add(TaxMenu.SELECTED_CLASS); + await HomeCore.taxModelChangedSignal.emit({ + region: HomeCore.instance().region, + year: HomeCore.instance().year, + }); + }, + }, + }); + + regionButton.appendTo(container); + }); + + container.appendTo(this.innerSpace); + } + + /** + * Draw year options on the menu + */ + drawYearOptions() { + const yearsTitle = new UIComponent({ + type: HTML.H3, + text: `${Text.home.years}`, + classes: [TaxMenu.OPTIONS_TITLE_CLASS], + }); + yearsTitle.appendTo(this.innerSpace); + const container = new UIComponent({ + type: HTML.DIV, + classes: [ + TaxMenu.BUTTON_CONTAINER_CLASS, + Gtdf.BOX_ROW, + Gtdf.BOX_CENTER, + ], + }); + + HomeCore.AVAILABLE_YEARS.forEach((year) => { + const yearButtonClasses = [ + Gtdf.BOX_ROW, + Gtdf.BOX_CENTER, + TaxMenu.MENU_OPTION_CLASS, + TaxMenu.LANG_OPTION_CLASS, + ]; + const selected = year == HomeCore.instance().year; + if (selected) yearButtonClasses.push(TaxMenu.SELECTED_CLASS); + + const yearButton = new UIComponent({ + type: HTML.BUTTON, + text: year, + classes: yearButtonClasses, + events: { + click: async (e) => { + e.preventDefault(); + e.stopPropagation(); + + HomeCore.instance().year = year; + const options = this.element.querySelectorAll(`.${TaxMenu.MENU_OPTION_CLASS}.${TaxMenu.LANG_OPTION_CLASS}`); + options.forEach(option => option.classList.remove(TaxMenu.SELECTED_CLASS)); + + yearButton.element.classList.add(TaxMenu.SELECTED_CLASS); + await HomeCore.taxModelChangedSignal.emit({ + region: HomeCore.instance().region, + year: HomeCore.instance().year, + }); + }, + }, + }); + + yearButton.appendTo(container); + }); + + container.appendTo(this.innerSpace); + } + + /** + * Draw language options on the menu + */ + drawLanguageOptions() { + const languagesTitle = new UIComponent({ + type: HTML.H3, + text: `${Text.home.languages}`, + classes: [TaxMenu.OPTIONS_TITLE_CLASS], + }); + + languagesTitle.appendTo(this.innerSpace); + + const container = new UIComponent({ + type: HTML.DIV, + classes: [ + TaxMenu.BUTTON_CONTAINER_CLASS, + Gtdf.BOX_ROW, + Gtdf.BOX_CENTER, + ], + }); + + const languages = LanguageService.getAvailableLanguagesWithNames(); + for (const lang in languages) { + const languageButtonClasses = [ + Gtdf.BOX_ROW, + Gtdf.BOX_CENTER, + TaxMenu.MENU_OPTION_CLASS, + TaxMenu.YEAR_OPTION_CLASS, + ]; + const selected = languages[lang] == Config.getLanguage(); + + if (selected) languageButtonClasses.push(TaxMenu.SELECTED_CLASS); + + const languageButton = new UIComponent({ + type: HTML.BUTTON, + text: Text.languages[lang.toLocaleLowerCase()], + classes: languageButtonClasses, + events: { + click: async (e) => { + e.preventDefault(); + e.stopPropagation(); + + LanguageService.setLanguage(languages[lang]); + await TextBundle.reloadSignal.emit(); + SignalBuffer.search("changeView").emit("home"); + }, + }, + }); + + languageButton.appendTo(container); + } + + container.appendTo(this.innerSpace); + } + + /** + * Toggle the mobile menu + */ + toggleMobileMenu() { + this.element.classList.toggle("show"); + } + + +} diff --git a/src/views/home/components/home.value.tag.ts b/src/views/home/components/home.value.tag.ts new file mode 100644 index 0000000..4cd9565 --- /dev/null +++ b/src/views/home/components/home.value.tag.ts @@ -0,0 +1,53 @@ +import { HTML } from "../../../lib/gtdf/components/dom.js"; +import { UIComponent } from "../../../lib/gtdf/components/uicomponent.js"; +import { Gtdf } from "../../../lib/others/gtdf.js"; + +export default class ValueTag extends UIComponent { + + private static readonly TAG_CLASS = "value-data"; + private static readonly TAG_VALUE_CLASS = "value"; + private static readonly TAG_LABEL_CLASS = "label"; + + private value : string; + private label : string; + + private labelComponent : UIComponent; + private valueComponent : UIComponent; + + constructor(label : string, value : string) { + super({ + type: HTML.SPAN, + classes: [Gtdf.BOX_ROW, Gtdf.BOX_Y_CENTER, Gtdf.BOX_X_BETWEEN, ValueTag.TAG_CLASS], + }); + + this.value = value; + this.label = label; + + this.labelComponent = new UIComponent({ + type: HTML.LABEL, + text: this.label, + classes: [ValueTag.TAG_LABEL_CLASS], + styles: { + fontSize: "1.1rem", + } + }); + + this.valueComponent = new UIComponent({ + type: HTML.SPAN, + text: `${this.value}`, + classes: [ValueTag.TAG_VALUE_CLASS], + }); + + this.labelComponent.appendTo(this); + this.valueComponent.appendTo(this); + } + + update(value : string, label : string) { + this.value = value; + this.label = label; + + this.valueComponent.element.textContent = `${this.value}`; + this.labelComponent.element.textContent = `${this.label}`; + } + +} \ No newline at end of file diff --git a/src/views/home/home.view.core.ts b/src/views/home/home.view.core.ts index eb43fe7..a35e0f6 100644 --- a/src/views/home/home.view.core.ts +++ b/src/views/home/home.view.core.ts @@ -1,134 +1,151 @@ -import { Config } from "../../config/config.js"; -import { StringMap } from "../../lib/gtdf/data/strings.js"; import { ViewCore } from "../../lib/gtdf/views/ViewCore.js"; import TaxService from "../../services/tax.service.js"; -import LanguageService from "../../services/language.service.js"; -import { SalaryTime } from "../../services/taxes/tax.model.js"; -import { Observable } from "../../lib/gtdf/core/observable/observer.js"; - +import { Signal } from "../../lib/gtdf/core/signals/signal.js"; +import { ISingleton, Singleton } from "../../lib/gtdf/decorators/Singleton.js"; +import { StaticImplements } from "../../lib/gtdf/core/static/static.interface.js"; +import { Text } from "../../lang/text.js"; +@Singleton() +@StaticImplements>() export default class HomeCore extends ViewCore { - - static readonly AVAILABLE_REGIONS ={ - "paisvasco": "País Vasco", - //"catalunya": "Catalunya", - } - - static readonly AVAILABLE_YEARS = [ - "2024", - "2023", - "2022", - ] - - public static region : string; - public static year : string; - public static grossSalary : number; - public static paymentNumber : number; - - public static taxModel : Observable = new Observable(); - - /** - * Get the irpf percentage - * @param grossSalary The gross salary - * @returns The irpf percentage - */ - static getIRPFPercentage(grossSalary : number) { - return TaxService.getTaxModel().content.getIrpf(grossSalary); - } - - /** - * Get the extra payment - * @param grossSalary The gross salary - * @returns The extra payment - */ - static getExtraPayment(grossSalary : number) { - return TaxService.getTaxModel().content.extraPayment(grossSalary); - } - - /** - * - * @param salaries - * @returns - */ - static getExtraPaymentWithMultipleSalaries(salaries : SalaryTime[]) { - return TaxService.getTaxModel().content.extraPaymentWithMultipleSalaries(salaries); - } - - - /** - * Get the salary with taxes - * @param grossSalary The gross salary - * @returns The salary with taxes - */ - static getSalary(grossSalary : number) { - return TaxService.getTaxModel().content.calcWithTaxes(grossSalary); - } - - /** - * Get the irpf value - */ - static cleanIrpfModel() { - this.taxModel.content.irpf_ranges = {}; - this.taxModel.content.taxes = {}; - - TaxService.clean(); - } - - /** - * Get available languages to add to the select - * @returns The available languages - */ - public static getLanguages() : StringMap { - const languages = LanguageService.getAvailableLanguages(); - const formatted = {}; - - const list = Object.keys(languages) - - list.forEach(lang => { - formatted[lang.toUpperCase().substring(0,1) + lang.toLowerCase().substring(1)] = languages[lang]; + public static _instance: HomeCore; + public static instance; + + public static readonly updateTaxesUISignal: Signal = new Signal( + "updateTaxesUI" + ); + public static readonly taxesCannotBeLoadedSignal: Signal = new Signal( + "taxesCannotBeLoaded" + ); + public static readonly taxCalculationRequestedSignal: Signal = new Signal( + "taxCalculationRequested" + ); + + public static readonly taxModelChangedSignal: Signal = new Signal( + "taxModelChanged" + ); + public static readonly salaryChangedSignal: Signal = new Signal( + "salaryChanged" + ); + public static readonly paymentNumberChangedSignal: Signal = new Signal( + "paymentNumberChanged" + ); + + public static readonly AVAILABLE_REGIONS = [ + "paisvasco", + ]; + + public static readonly AVAILABLE_YEARS = ["2024", "2023", "2022"]; + + public static readonly MAX_SALARY = 1000000; + public static readonly MIN_SALARY = 0; + public static readonly DEFAULT_REGION = "paisvasco"; + public static readonly AVAILABLE_PAYMENT_NUMBERS = [14, 12]; + + private region: string; + private year: string; + private grossSalary: number; + private paymentNumber: number; + + constructor() { + super(); + this.region = HomeCore.DEFAULT_REGION; + this.year = HomeCore.AVAILABLE_YEARS[0]; + this.grossSalary = HomeCore.MIN_SALARY; + this.paymentNumber = HomeCore.AVAILABLE_PAYMENT_NUMBERS[0]; + + HomeCore.taxModelChangedSignal.subscribe({ + update: async (data) => { + this.region = data.region; + this.year = data.year; + const loaded = await this.loadTaxModel(); + + if (!loaded) { + await HomeCore.taxesCannotBeLoadedSignal.emit( + Text.error.cannotLoadTax + ); + TaxService.get().clear(); + return; + } + + const taxes = await this.calculateTaxes(); + await HomeCore.updateTaxesUISignal.emit(taxes); + }, }); - return formatted; - } + HomeCore.salaryChangedSignal.subscribe({ + update: async (data) => { + try { + this.grossSalary = data; + const taxes = await this.calculateTaxes(); + await HomeCore.updateTaxesUISignal.emit(taxes); + } catch (error) { + await HomeCore.taxesCannotBeLoadedSignal.emit( + Text.error.cannotLoadTax + ); + } + }, + }); - public static getAvailableLanguagesWithNames() : StringMap { - const languages = LanguageService.getAvailableLanguages(); - return languages; + HomeCore.paymentNumberChangedSignal.subscribe({ + update: async (data) => { + + try { + this.paymentNumber = data; + TaxService.get().paymentNumber = data; + + const taxes = await this.calculateTaxes(); + await HomeCore.updateTaxesUISignal.emit(taxes); + } catch (error) { + await HomeCore.taxesCannotBeLoadedSignal.emit( + Text.error.cannotLoadTax + ); + } + }, + }); } /** - * Set the app language and reload - * @param selected The selected language + * Calculate the salary and the taxes + * and emit the results to the UI + * @returns The taxes result */ - public static setLanguage(selected :string){ - Config.setLanguage(selected); - } + public async calculateTaxes(): Promise { + return { + irpfPercentage: TaxService.get().getIrpf(this.grossSalary), + extraPayment: TaxService.get().extraPayment(this.grossSalary), + salary: TaxService.get().calcWithTaxes(this.grossSalary), + }; + } /** * Load the tax model - * @param region The region to load + * @param region The region to load * @param year The year to load * @returns True if the data is loaded, false otherwise */ - public static async loadTaxModel(region : string, year : string) { - return await TaxService.load(region, year); + public async loadTaxModel() { + return await TaxService.load(this.region, this.year); } /** - * Set the payment number - * @param paymentNumber The payment number + * Check if the tax model is loaded + * @returns if the payment number is the default one */ - public static setPaymentNumber(paymentNumber : number) { - HomeCore.taxModel.content.paymentNumber = paymentNumber; + public isDefaultPaymentNumber(): boolean { + return this.paymentNumber === HomeCore.AVAILABLE_PAYMENT_NUMBERS[0]; } - - /** - * Get the payment number - * @returns The payment number - */ - public static isDefaultPaymentNumber() { - return HomeCore.taxModel.content.isDefaultPaymentNumber(); - } - - -} \ No newline at end of file +} + +/** + * The taxes result interface + * @interface ITaxesResult + * @property {number} irpfPercentage The irpf percentage + * @property {number} extraPayment The extra payment + * @property {number} salary The salary without taxes + */ +export interface ITaxesResult { + irpfPercentage: number; + extraPayment: number; + salary: number; +} diff --git a/src/views/home/home.view.ts b/src/views/home/home.view.ts index 2d76b03..7d1523c 100644 --- a/src/views/home/home.view.ts +++ b/src/views/home/home.view.ts @@ -1,5 +1,5 @@ import { Config } from "../../config/config.js"; -import { Text, TextBundle } from "../../lang/text.js"; +import { Text } from "../../lang/text.js"; import { UIComponent } from "../../lib/gtdf/components/uicomponent.js"; import { ViewUI } from "../../lib/gtdf/views/ViewUI.js"; import { Route } from "../../lib/gtdf/decorators/Route.js"; @@ -8,41 +8,15 @@ import { HTML } from "../../lib/gtdf/components/dom.js"; import { Gtdf } from "../../lib/others/gtdf.js"; import { Browser } from "../../lib/gtdf/components/browser.js"; import HomeCore from "./home.view.core.js"; -import MaterialIcons from "../../lib/gtdf/resources/MaterialIcons.js"; -import SignalBuffer from "../../core/signal.buffer.js"; -import { IObserver, Observable } from "../../lib/gtdf/core/observable/observer.js"; -import { ObservableUIComponent } from "../../lib/gtdf/core/observable/observable.uicomponent.js"; -import TaxModel from "../../services/taxes/tax.model.js"; +import TaxMenu from "./components/home.menu.js"; +import { CalculationPanel } from "./components/home.calculation.panel.js"; @Route(["","calculate", undefined]) @Singleton() export default class HomeView extends ViewUI { - private static readonly MAX_SALARY = 1000000; - private static readonly MIN_SALARY = 0; - private static readonly DEFAULT_REGION = "paisvasco"; - private static readonly AVAILABLE_PAYMENT_NUMBERS = [14,12]; - private static readonly ID = "home"; - private static readonly CALC_FRAME_ID = "calc-frame"; - private static readonly CALC_MENU_ID = "calc-menu"; - private static readonly MENU_ID = "menu"; - private static readonly MAIN_FRAME_ID = "main-frame"; - private static readonly SALARY_INPUT_PANEL_ID = "salary-input-panel"; - private static readonly SALARY_INPUT_ID = "salary"; - private static readonly PAYMENT_NUMBER_INPUT_ID = "payment-number"; - - private static readonly MOBILE_CLASS = "mobile"; - private static readonly OPTIONS_TITLE_CLASS = "options-title"; - private static readonly BUTTON_CONTAINER_CLASS = "button-container"; - private static readonly MENU_OPTION_CLASS = "menu-option"; - private static readonly REGION_OPTION_CLASS = "region-option"; - private static readonly YEAR_OPTION_CLASS = "year-option"; - private static readonly LANG_OPTION_CLASS = "lang-option"; - private static readonly SELECTED_CLASS = "selected"; - - private result : UIComponent; public constructor(){ super({ @@ -50,414 +24,33 @@ export default class HomeView extends ViewUI { id: HomeView.ID, classes: [Gtdf.BOX_ROW, Gtdf.BOX_X_START, Gtdf.BOX_Y_CENTER], }); - + } public async show(params : string[], container : UIComponent) : Promise { - HomeCore.region = params[0] || HomeView.DEFAULT_REGION; - HomeCore.year = params[1] || `${new Date().getFullYear()}`; - HomeCore.grossSalary = +params[2] || HomeView.MIN_SALARY; - - Config.setTitle(`${Config.Base.app_name} - ${HomeCore.grossSalary > 0 ? HomeCore.grossSalary + "€" : Text.home.title}`); + Config.setTitle(`${Config.Base.app_name} - ${HomeCore.instance().grossSalary > 0 ? HomeCore.instance().grossSalary + "€" : Text.home.title}`); if(Browser.isSmallDevice()){ this.element.classList.add(HomeView.MOBILE_CLASS); } - const calcView = new UIComponent({ - type: HTML.DIV, - classes: [Gtdf.BOX_COLUMN,Gtdf.BOX_CENTER], - id: HomeView.CALC_FRAME_ID, - }); - - const calcMenu = new UIComponent({ - type: HTML.DIV, - id: HomeView.CALC_MENU_ID, - classes: [Gtdf.BOX_COLUMN,Gtdf.BOX_X_START,Gtdf.BOX_X_CENTER], - attributes : { - draggable : "true" - } - }); - - calcMenu.setEvents({ - click : ()=> { - this.toggleMobileMenu(); - } - }); - - - await this.showMenu(calcMenu); - await this.showCalcView(calcView); + const menu = new TaxMenu(); + menu.appendTo(this); - calcMenu.appendTo(this); - calcView.appendTo(this); + const calculationPanel = new CalculationPanel(); + calculationPanel.appendTo(this); this.appendTo(container); - } - - - /** - * Show region and year selection menu - * @param parent The parent component - */ - async showMenu(parent : UIComponent) : Promise { - const menu = new UIComponent({ - type: HTML.DIV, - id: HomeView.MENU_ID, - classes: [Gtdf.BOX_COLUMN], - }); - - const regionTitle = new UIComponent({ - type: HTML.H3, - text: `${Text.home.regions}`, - classes: [HomeView.OPTIONS_TITLE_CLASS], - }); - - regionTitle.appendTo(menu); - - const regionsButtonContainer = new UIComponent({ - type: HTML.DIV, - classes: [Gtdf.BOX_ROW,Gtdf.BOX_CENTER,HomeView.BUTTON_CONTAINER_CLASS], - }); - - for (const region in HomeCore.AVAILABLE_REGIONS) { - const regionName = HomeCore.AVAILABLE_REGIONS[region]; - - const selected = region == HomeCore.region - const regionButtonClasses = [Gtdf.BOX_ROW,Gtdf.BOX_CENTER,HomeView.MENU_OPTION_CLASS,HomeView.REGION_OPTION_CLASS]; - - if(selected){ - regionButtonClasses.push(HomeView.SELECTED_CLASS); - } - - const regionButton = new UIComponent({ - type: HTML.BUTTON, - text: regionName, - classes: regionButtonClasses, - events: { - - click: async (e) => { - e.preventDefault(); - e.stopPropagation(); - - HomeCore.region = region; - const options = menu.element.querySelectorAll(`.${HomeView.MENU_OPTION_CLASS}.${HomeView.REGION_OPTION_CLASS}`); - - options.forEach(option => { - option.classList.remove(HomeView.SELECTED_CLASS); - }); - - regionButton.element.classList.add(HomeView.SELECTED_CLASS); - await this.loadTaxModel(HomeCore.region, HomeCore.year); - this.showCalcResults((document.getElementById(HomeView.SALARY_INPUT_ID) as HTMLInputElement).valueAsNumber); - //this.toggleMobileMenu(); - - } - } - }); - - regionButton.appendTo(regionsButtonContainer); - } - - regionsButtonContainer.appendTo(menu); - - const yearsTitle = new UIComponent({ - type: HTML.H3, - text: `${Text.home.years}`, - classes: [HomeView.OPTIONS_TITLE_CLASS], - }); - - yearsTitle.appendTo(menu); - - const yearsButtonContainer = new UIComponent({ - type: HTML.DIV, - classes: [HomeView.BUTTON_CONTAINER_CLASS, Gtdf.BOX_ROW, Gtdf.BOX_CENTER], - }); - - HomeCore.AVAILABLE_YEARS.forEach(year => { - - const yearButtonClasses = [Gtdf.BOX_ROW,Gtdf.BOX_CENTER,HomeView.MENU_OPTION_CLASS, HomeView.LANG_OPTION_CLASS]; - const selected = year == HomeCore.year - if(selected) - yearButtonClasses.push(HomeView.SELECTED_CLASS); - - const yearButton = new UIComponent({ - type: HTML.BUTTON, - text: year, - classes: yearButtonClasses, - events: { - - click: async (e) => { - e.preventDefault(); - e.stopPropagation(); - - HomeCore.year = year; - const options = menu.element.querySelectorAll(`.${HomeView.MENU_OPTION_CLASS}.${HomeView.LANG_OPTION_CLASS}`); - options.forEach(option => option.classList.remove(HomeView.SELECTED_CLASS)); - - yearButton.element.classList.add(HomeView.SELECTED_CLASS); - await this.loadTaxModel(HomeCore.region, HomeCore.year); - this.showCalcResults((document.getElementById(HomeView.SALARY_INPUT_ID) as HTMLInputElement).valueAsNumber); - //this.toggleMobileMenu(); - } - } - }); - - yearButton.appendTo(yearsButtonContainer); - }); - - yearsButtonContainer.appendTo(menu); - - const languagesTitle = new UIComponent({ - type: HTML.H3, - text: `${Text.home.languages}`, - classes: [HomeView.OPTIONS_TITLE_CLASS], - }); - - languagesTitle.appendTo(menu); - - const languagesButtonContainer = new UIComponent({ - type: HTML.DIV, - classes: [HomeView.BUTTON_CONTAINER_CLASS, Gtdf.BOX_ROW, Gtdf.BOX_CENTER], - }); - - const languages = HomeCore.getAvailableLanguagesWithNames(); - - for (const lang in languages) { - - const languageButtonClasses = [Gtdf.BOX_ROW,Gtdf.BOX_CENTER,HomeView.MENU_OPTION_CLASS, HomeView.YEAR_OPTION_CLASS]; - const selected = languages[lang] == Config.getLanguage() - - if(selected) - languageButtonClasses.push(HomeView.SELECTED_CLASS); - - const languageButton = new UIComponent({ - type: HTML.BUTTON, - text: Text.languages[lang.toLocaleLowerCase()], - classes: languageButtonClasses, - events: { - - click: async (e) => { - e.preventDefault(); - e.stopPropagation(); - - HomeCore.setLanguage(languages[lang]); - await TextBundle.reloadSignal.emit(); - SignalBuffer.search("changeView").emit("home"); - } - } - }); - - languageButton.appendTo(languagesButtonContainer); - - } - - languagesButtonContainer.appendTo(menu); - menu.appendTo(parent); - } - - - getTouches(evt) { - return evt.touches || // browser API - evt.originalEvent.touches; // jQuery - } - - - async showCalcView(parent : UIComponent) : Promise { - - const mainFrame = new UIComponent({ - type: HTML.DIV, - id: HomeView.MAIN_FRAME_ID, - }); - const title = new UIComponent({ - type: HTML.H1, - text: Text.home.netSalary, - id : "main-title", + await HomeCore.taxModelChangedSignal.emit({ + region: params[0] || HomeCore.DEFAULT_REGION, + year: params[1] || HomeCore.instance().year }); - const salaryInputPanel = new UIComponent({ - type: HTML.DIV, - id: HomeView.SALARY_INPUT_PANEL_ID, - classes: [Gtdf.BOX_ROW, Gtdf.BOX_CENTER], - }); - - const salaryInput = new UIComponent({ - type: HTML.INPUT, - id: HomeView.SALARY_INPUT_ID, - attributes: { - type: "number", - inputmode:"numeric", - name: "salary", - value : HomeCore.grossSalary > 0 ? HomeCore.grossSalary + "" : "", - min: "0" - }, - }); - salaryInput.appendTo(salaryInputPanel); - - const paymentNumberInput = new UIComponent({ - type: HTML.BUTTON, - text: `${HomeView.AVAILABLE_PAYMENT_NUMBERS[0]}`, - id: HomeView.PAYMENT_NUMBER_INPUT_ID, - classes: [Gtdf.BOX_ROW, Gtdf.BOX_CENTER], - }); - - - paymentNumberInput.setEvents({ - click: () => { - let paymentNumber = +paymentNumberInput.element.innerHTML; - const index = HomeView.AVAILABLE_PAYMENT_NUMBERS.indexOf(paymentNumber); - - const newIndex = index + 1 >= HomeView.AVAILABLE_PAYMENT_NUMBERS.length ? 0 : index + 1; - paymentNumberInput.element.innerHTML = `${HomeView.AVAILABLE_PAYMENT_NUMBERS[newIndex]}`; - - HomeCore.setPaymentNumber(HomeView.AVAILABLE_PAYMENT_NUMBERS[newIndex]); - this.showCalcResults(+salaryInput.getValue()); - - } - }); - - paymentNumberInput.appendTo(salaryInputPanel); - - this.result = new UIComponent({ - type: HTML.DIV, - id: "result", - }); - - const footer = new UIComponent({ - type: HTML.FOOTER, - id: "footer", - text: `Akrck02 / Rayxnor - ${new Date().getFullYear()}`, - }); - - salaryInput.setEvents({ - input: () => { - this.showCalcResults(+salaryInput.getValue()); - } - }); - - await this.loadTaxModel(HomeCore.region, HomeCore.year); - - if(HomeCore.grossSalary > 0){ - await this.showCalcResults(HomeCore.grossSalary); - } - - title.appendTo(mainFrame); - salaryInputPanel.appendTo(mainFrame); - this.result.appendTo(mainFrame); - - mainFrame.appendTo(parent); - footer.appendTo(parent); - } - - - async loadTaxModel(region : string, year : string) { - if(!await HomeCore.loadTaxModel(HomeCore.region, HomeCore.year)){ - - HomeCore.cleanIrpfModel(); - this.result.clean(); - - const warning = new UIComponent({ - type: HTML.B, - classes: ["text-error"], - text: Text.errors.cannotLoadTax, - }); - - warning.appendTo(this.result); - - if(!Browser.isSmallDevice()){ - alert({icon:"block",message: Text.errors.cannotLoadTax}) - } - - return; - - } - this.result.clean(); + const salary = params[2] || HomeCore.instance().grossSalary; + if(salary > 0){ + await HomeCore.salaryChangedSignal.emit(salary); + } + } - - /** - * Show calculation results - * @param parent Parent element - * @param grossSalary Gross salary - */ - showCalcResults(grossSalary) { - - if(isNaN(grossSalary)){ - return; - } - - this.result.clean(); - - if(grossSalary > HomeView.MAX_SALARY){ - - const warning = new UIComponent({ - type: HTML.B, - classes: ["text-error", Gtdf.TEXT_CENTER, Gtdf.BOX_ROW, Gtdf.BOX_CENTER], - text: Text.errors.salaryTooHigh, - }); - warning.appendTo(this.result); - return; - } - - - const salary = HomeCore.getSalary(grossSalary); - const extraPayment = HomeCore.getExtraPayment(grossSalary); - const irpfPercentage = HomeCore.getIRPFPercentage(grossSalary); - - const salaryResult = this.createValueDataComponent(Text.home.salary, `${salary}€`); - salaryResult.appendTo(this.result); - - if(HomeCore.isDefaultPaymentNumber()){ - const extraPaymentResult = this.createValueDataComponent(Text.home.extra, `${extraPayment}€`); - extraPaymentResult.appendTo(this.result); - } - - const irpfPercentageResult = this.createValueDataComponent(Text.home.incomeTax, `${irpfPercentage}%`); - irpfPercentageResult.appendTo(this.result); - - } - - createValueDataComponent(label : string, value : string) : UIComponent { - - const resultComponent = new UIComponent({ - type: HTML.SPAN, - classes: [Gtdf.BOX_ROW, Gtdf.BOX_Y_CENTER, Gtdf.BOX_X_BETWEEN], - id: "value-data", - }); - - const labelComponent = new UIComponent({ - type: HTML.LABEL, - text: label, - classes: ["label"], - styles: { - fontSize: "1.1rem", - } - }); - - const valueComponent = new UIComponent({ - type: HTML.SPAN, - text: `${value}`, - classes: ["value"], - }); - - labelComponent.appendTo(resultComponent); - valueComponent.appendTo(resultComponent); - return resultComponent; - } - - toggleMobileMenu() { - const menu = document.getElementById("calc-menu"); - menu.classList.toggle("show"); - } - - openMobileMenu() { - const menu = document.getElementById("calc-menu"); - menu.classList.add("show"); - } - - closeMobileMenu() { - const menu = document.getElementById("calc-menu"); - menu.classList.remove("show"); - } - } \ No newline at end of file diff --git a/src/views/router.ts b/src/views/router.ts index 5024dc7..d654288 100644 --- a/src/views/router.ts +++ b/src/views/router.ts @@ -60,7 +60,6 @@ export default class Router implements IObserver { async update(data?: any): Promise { console.debug(data); - console.debug(`Router update to /${data.view}`); let params = [];