Merge branch 'feature/WW-26-architecture'

This commit is contained in:
Fran Jurmanović
2021-06-12 11:54:22 +02:00
81 changed files with 3567 additions and 21158 deletions

View File

@@ -1,7 +1,7 @@
{ {
"presets": [ "presets": [
"@babel/preset-env", ["@babel/preset-env", {"modules": false}],
"@babel/preset-typescript" ["@babel/preset-typescript", {"modules": false}]
], ],
"plugins": [ "plugins": [
[ [

View File

@@ -1,20 +1,37 @@
{ {
"env": {
"browser": true,
"es2021": true
},
"extends": [ "extends": [
"eslint:recommended", "eslint:recommended",
"plugin:@typescript-eslint/recommended" "prettier"
], ],
"parser": "@typescript-eslint/parser", "env": {
"parserOptions": { "browser": true,
"ecmaVersion": 12, "node": true,
"sourceType": "module" "es6": true
},
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"modules": true,
"experimentalObjectRestSpread": true
}
},
"globals": {
"__CONFIG__": true
}, },
"plugins": [
"@typescript-eslint"
],
"rules": { "rules": {
"no-multiple-empty-lines": [
2,
{
"max": 1
}
],
"no-unused-vars": [
2,
{
"ignoreRestSiblings": true
}
]
} }
} }

10
.prettierrc Normal file
View File

@@ -0,0 +1,10 @@
{
"printWidth": 120,
"tabWidth": 2,
"useTabs": true,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": true,
"jsxBracketSameLine": false
}

18126
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,36 +7,42 @@
"scripts": { "scripts": {
"start": "webpack serve --mode development --hot", "start": "webpack serve --mode development --hot",
"build": "webpack --mode production", "build": "webpack --mode production",
"format": "eslint" "format": "prettier --write src/**/*.{js,ts,html}"
}, },
"dependencies": { "dependencies": {
"@github/catalyst": "^1.1.3",
"core/utils": "^1.4.1",
"validator": "^13.6.0"
},
"devDependencies": {
"@babel/core": "^7.14.3", "@babel/core": "^7.14.3",
"@babel/plugin-proposal-class-properties": "^7.13.0", "@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/plugin-proposal-decorators": "^7.14.2", "@babel/plugin-proposal-decorators": "^7.14.2",
"@babel/plugin-proposal-object-rest-spread": "^7.14.2", "@babel/plugin-proposal-object-rest-spread": "^7.14.2",
"@babel/preset-env": "^7.14.2", "@babel/preset-env": "^7.14.2",
"@babel/preset-typescript": "^7.13.0", "@babel/preset-typescript": "^7.13.0",
"@github/catalyst": "^1.1.3", "@typescript-eslint/eslint-plugin": "^4.26.0",
"@github/jtml": "^0.4.0", "@typescript-eslint/parser": "^4.26.0",
"babel-loader": "^8.2.2", "babel-loader": "^8.2.2",
"babel-plugin-module-resolver": "^4.1.0", "babel-plugin-module-resolver": "^4.1.0",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
"compression-webpack-plugin": "^8.0.0",
"connect-history-api-fallback": "^1.6.0", "connect-history-api-fallback": "^1.6.0",
"css-loader": "^5.2.6", "css-loader": "^5.2.6",
"eslint": "^7.28.0",
"eslint-config-prettier": "^8.3.0",
"eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.23.4",
"html-webpack-plugin": "^5.3.1", "html-webpack-plugin": "^5.3.1",
"node-sass": "^6.0.0", "node-sass": "^6.0.0",
"prettier": "^2.3.1",
"sass-loader": "^11.1.1", "sass-loader": "^11.1.1",
"sass-to-string": "^1.5.1", "sass-to-string": "^1.5.1",
"validator": "^13.6.0",
"webpack": "^5.38.1",
"webpack-dev-server": "^3.11.2"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0",
"compression-webpack-plugin": "^8.0.0",
"eslint": "^7.27.0",
"terser-webpack-plugin": "^5.1.3", "terser-webpack-plugin": "^5.1.3",
"webpack-cli": "^4.7.0" "uglify-js": "^3.13.9",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^5.38.1",
"webpack-cli": "^4.7.0",
"webpack-dev-server": "^3.11.2"
} }
} }

View File

@@ -1,4 +1,4 @@
import { BaseElement } from "common/"; import { BaseElement } from 'common/';
class BaseComponentElement extends BaseElement {} class BaseComponentElement extends BaseElement {}

View File

@@ -1 +1 @@
export { default as BaseComponentElement } from "./BaseComponentElement/BaseComponentElement"; export { default as BaseComponentElement } from './BaseComponentElement/BaseComponentElement';

View File

@@ -1,142 +1,133 @@
import { html, render, TemplateResult } from "@github/jtml"; import { html, render, TemplateResult } from 'core/utils';
import { import { AppLoaderElement, AppMainElement, AppModalElement, AppRootElement } from 'components/';
AppLoaderElement, import { AppService, RouterService } from 'core/services';
AppMainElement, import { AuthStore } from 'core/store';
AppModalElement, import { closest } from 'core/utils';
AppRootElement,
} from "components/";
import { AppService, RouterService } from "core/services";
import { AuthStore } from "core/store";
import { closest } from "core/utils";
class BaseElement extends HTMLElement { class BaseElement extends HTMLElement {
@closest appMain: AppMainElement; @closest appMain: AppMainElement;
private _appMain: AppMainElement; private _appMain: AppMainElement;
public loader: Loader; public loader: Loader;
private elementDisconnectCallbacks: Array<Function> = []; private elementDisconnectCallbacks: Array<Function> = [];
constructor() { constructor() {
super(); super();
this.connectedCallback = this.connectedCallback.bind(this); this.connectedCallback = this.connectedCallback.bind(this);
this.disconnectedCallback = this.disconnectedCallback.bind(this); this.disconnectedCallback = this.disconnectedCallback.bind(this);
this.loader = new Loader(this); this.loader = new Loader(this);
} }
public get routerService(): RouterService { public get routerService(): RouterService {
return this.appMain?.routerService; return this.appMain?.routerService;
} }
public get authStore(): AuthStore { public get authStore(): AuthStore {
return this.appMain?.authStore; return this.appMain?.authStore;
} }
public get appService(): AppService { public get appService(): AppService {
return this.appMain?.appService; return this.appMain?.appService;
} }
public get appModal(): AppModalElement { public get appModal(): AppModalElement {
return this.appMain?.appModal; return this.appMain?.appModal;
} }
public get mainRoot(): AppRootElement { public get mainRoot(): AppRootElement {
return this.appMain?.mainRoot; return this.appMain?.mainRoot;
} }
public get appLoader(): AppLoaderElement { public get appLoader(): AppLoaderElement {
return this.appMain?.appLoader; return this.appMain?.appLoader;
} }
public get isAuth(): boolean { public get isAuth(): boolean {
return this.appMain?.isAuth(); return this.appMain?.isAuth();
} }
public bindEvents = (attrName): void => { public bindEvents = (attrName): void => {
const _elems = this.querySelectorAll(`[${attrName}]`); const _elems = this.querySelectorAll(`[${attrName}]`);
_elems?.forEach((el) => { _elems?.forEach((el) => {
for (const action of (el.getAttribute(attrName) || "") for (const action of (el.getAttribute(attrName) || '').trim().split(/\s+/)) {
.trim() const eventSep = action.lastIndexOf(':');
.split(/\s+/)) { const methodSep = action.lastIndexOf('#');
const eventSep = action.lastIndexOf(":"); const tag = action.slice(eventSep + 1, methodSep);
const methodSep = action.lastIndexOf("#");
const tag = action.slice(eventSep + 1, methodSep);
const type = action.slice(0, eventSep); const type = action.slice(0, eventSep);
const method = action.slice(methodSep + 1); const method = action.slice(methodSep + 1);
if (tag.toUpperCase() === this.tagName) { if (tag.toUpperCase() === this.tagName) {
el.addEventListener(type, this?.[method]); el.addEventListener(type, this?.[method]);
const _callback = () => const _callback = () => el.removeEventListener(type, this?.[method]);
el.removeEventListener(type, this?.[method]); this.elementDisconnectCallbacks.push(_callback);
this.elementDisconnectCallbacks.push(_callback); } else {
} else { this.childNodes.forEach((child: HTMLElement) => {
this.childNodes.forEach((child: HTMLElement) => { if (child.tagName == tag.toUpperCase()) {
if (child.tagName == tag.toUpperCase()) { el.addEventListener(type, child?.[method]);
el.addEventListener(type, child?.[method]); const _callback = () => el.removeEventListener(type, child?.[method]);
const _callback = () => this.elementDisconnectCallbacks.push(_callback);
el.removeEventListener(type, child?.[method]); }
this.elementDisconnectCallbacks.push(_callback); });
} }
}); }
} });
} };
});
};
render = (): TemplateResult => { render = (): TemplateResult => {
return html``; return html``;
}; };
updateCallback = (): void => {}; updateCallback = (): void => {};
update = (): void => { update = (): void => {
render(this.render(), this); render(this.render(), this);
this.bindEvents("data-action"); this.bindEvents('data-action');
this.bindEvents("app-action"); this.bindEvents('app-action');
this.updateCallback(); this.updateCallback();
}; };
connectedCallback(): void { connectedCallback(): void {
this.elementConnected(); this.elementConnected();
this._appMain = this.appMain; this._appMain = this.appMain;
} }
disconnectedCallback(): void { disconnectedCallback(): void {
const { _appMain } = this; const { _appMain } = this;
this.elementDisconnected(_appMain); this.elementDisconnected(_appMain);
if (Array.isArray(this.elementDisconnectCallbacks)) { if (Array.isArray(this.elementDisconnectCallbacks)) {
this.elementDisconnectCallbacks.forEach((callback: Function) => { this.elementDisconnectCallbacks.forEach((callback: Function) => {
if (typeof callback == "function") { if (typeof callback == 'function') {
callback(_appMain); callback(_appMain);
} }
}); });
} }
} }
elementConnected = (): void => { elementConnected = (): void => {
this.update(); this.update();
}; };
elementDisconnected = (appMain: AppMainElement): void => {}; elementDisconnected = (appMain: AppMainElement): void => {};
} }
export default BaseElement; export default BaseElement;
class Loader { class Loader {
private _loading: number = 0; private _loading: number = 0;
constructor(private _main: BaseElement) {} constructor(private _main: BaseElement) {}
public start = () => { public start = () => {
this._loading++; this._loading++;
this._main?.update?.(); this._main?.update?.();
}; };
public stop = () => { public stop = () => {
if (this._loading > 0) { if (this._loading > 0) {
this._loading--; this._loading--;
this._main?.update?.(); this._main?.update?.();
} }
}; };
public get loading() { public get loading() {
return this._loading > 0; return this._loading > 0;
} }
} }

View File

@@ -0,0 +1,86 @@
import { AppFormElement } from 'components/';
import { validatorErrors } from 'core/constants';
import { firstUpper, validator } from 'core/utils';
class Validator {
public _error: string;
public _valid: boolean;
constructor(public input: any, public form: AppFormElement, public rules: string) {}
get value() {
return this.input?._value;
}
get name() {
return this.input?.name;
}
get error() {
return this._error;
}
set error(error) {
this._error = error;
}
get valid() {
return this._valid;
}
set valid(error) {
this._valid = error;
}
validate = () => {
const rules = this.rules?.split('|').filter((a) => a);
const value = this.value;
const validArr = rules
.slice()
.reverse()
.map((rule) => {
let args = [];
if (rule.includes('[') && rule.includes(']')) {
const begSep = rule.lastIndexOf('[');
const endSep = rule.lastIndexOf(']');
args = rule
.slice(begSep + 1, endSep)
.split(',')
.map((arg: string) => {
if (this.form && arg?.includes?.('field')) {
const begBr = arg.lastIndexOf('(');
const endBr = arg.lastIndexOf(')');
const field = arg.slice(begBr + 1, endBr);
return this.form?.values[field];
}
});
rule = rule.slice(0, begSep);
}
let validRule = validator?.[rule];
let ruleArray = validRule ? Array.isArray(validRule) : false;
let valid = true;
if (validRule) {
if (ruleArray) {
valid = validRule?.[0]?.(value, ...args);
} else {
valid = validRule?.(value, ...args);
}
}
if (!valid) {
const error = validatorErrors[rule]?.replaceAll('{- name}', firstUpper(this.name?.toString()));
this.error = ruleArray ? validRule?.[1]?.replaceAll('{- name}', firstUpper(this.name?.toString())) : error;
}
return valid;
});
const _return = validArr?.includes(false);
if (_return) {
this.error = null;
}
this.valid = !_return;
return !_return;
};
}
export default Validator;

View File

@@ -1,4 +1,6 @@
export { default as BaseElement } from "./core/BaseElement/BaseElement"; export { default as BaseElement } from './core/BaseElement/BaseElement';
export * from "./layouts"; export { default as Validator } from './core/Validator/Validator';
export * from "./components";
export * from "./pages"; export * from './layouts';
export * from './components';
export * from './pages';

View File

@@ -1,30 +1,30 @@
import { target } from "@github/catalyst"; import { target } from '@github/catalyst';
import { BaseElement } from "common/"; import { BaseElement } from 'common/';
class BaseLayoutElement extends BaseElement { class BaseLayoutElement extends BaseElement {
@target appSlot: HTMLElement; @target appSlot: HTMLElement;
public isLayout: boolean = true; public isLayout: boolean = true;
public _appSlot: string; public _appSlot: string;
constructor() { constructor() {
super(); super();
} }
get slotTag(): string { get slotTag(): string {
return this.appSlot?.firstElementChild?.tagName; return this.appSlot?.firstElementChild?.tagName;
} }
compareTags = (tag: string | HTMLElement): boolean => { compareTags = (tag: string | HTMLElement): boolean => {
if (typeof tag === "string") { if (typeof tag === 'string') {
return this.slotTag === tag; return this.slotTag === tag;
} }
return tag?.tagName === this.slotTag; return tag?.tagName === this.slotTag;
}; };
setElement = (newTag: string): void => { setElement = (newTag: string): void => {
const _appSlot = `<div data-target="base-layout.content"><${newTag}></${newTag}></div>`; const _appSlot = `<div data-target="base-layout.content"><${newTag}></${newTag}></div>`;
this._appSlot = _appSlot; this._appSlot = _appSlot;
this.appSlot.innerHTML = _appSlot; this.appSlot.innerHTML = _appSlot;
}; };
} }
export default BaseLayoutElement; export default BaseLayoutElement;

View File

@@ -1 +1 @@
export { default as BaseLayoutElement } from "./BaseLayoutElement/BaseLayoutElement"; export { default as BaseLayoutElement } from './BaseLayoutElement/BaseLayoutElement';

View File

@@ -1,45 +1,43 @@
import { attr } from "@github/catalyst"; import { attr } from '@github/catalyst';
import { html, render } from "@github/jtml"; import { html, render } from 'core/utils';
import { BaseElement } from "common/"; import { BaseElement } from 'common/';
import { isTrue } from "core/utils"; import { isTrue } from 'core/utils';
class BasePageElement extends BaseElement { class BasePageElement extends BaseElement {
public pageTitle: string = ""; public pageTitle: string = '';
@attr hidetitle: string; @attr hidetitle: string;
@attr customtitle: string; @attr customtitle: string;
constructor(options: OptionType) { constructor(options: OptionType) {
super(); super();
if (options?.title) { if (options?.title) {
this.pageTitle = options?.title; this.pageTitle = options?.title;
} }
this.connectedCallback = this.connectedCallback.bind(this); this.connectedCallback = this.connectedCallback.bind(this);
this.disconnectedCallback = this.disconnectedCallback.bind(this); this.disconnectedCallback = this.disconnectedCallback.bind(this);
} }
public renderTitle = () => { public renderTitle = () => {
if (!isTrue(this.hidetitle)) { if (!isTrue(this.hidetitle)) {
return html`<div class="page --title"> return html`<div class="page --title">${this.customtitle ? this.customtitle : this.pageTitle}</div>`;
${this.customtitle ? this.customtitle : this.pageTitle} }
</div>`; return html``;
} };
return html``;
};
update = (): void => { update = (): void => {
const _render = () => html` ${this.renderTitle()} ${this.render()} `; const _render = () => html` ${this.renderTitle()} ${this.render()} `;
render(_render(), this); render(_render(), this);
this.bindEvents(); this.bindEvents('app-action');
this.updateCallback(); this.updateCallback();
}; };
connectedCallback() { connectedCallback() {
this.appMain.setTitle(this.pageTitle); this.appMain.setTitle(this.pageTitle);
super.connectedCallback(); super.connectedCallback();
} }
} }
export default BasePageElement; export default BasePageElement;
export type OptionType = { export type OptionType = {
title?: string; title?: string;
}; };

View File

@@ -1 +1 @@
export { default as BasePageElement } from "./BasePageElement/BasePageElement"; export { default as BasePageElement } from './BasePageElement/BasePageElement';

View File

@@ -1,241 +1,212 @@
import { attr, controller, target } from "@github/catalyst"; import { attr, controller, target } from '@github/catalyst';
import { findMethod, firstUpper } from "core/utils"; import { closest, findMethod, firstUpper } from 'core/utils';
import { html } from "@github/jtml"; import { html } from 'core/utils';
import randomId from "core/utils/random-id"; import randomId from 'core/utils/random-id';
import validator from "validator"; import { validatorErrors } from 'core/constants';
import { validatorErrors } from "core/constants"; import { BaseComponentElement, Validator } from 'common/';
import { BaseComponentElement } from "common/"; import { AppFormElement } from 'components/app-form/AppFormElement';
@controller @controller
class AppDropdownElement extends BaseComponentElement { class AppDropdownElement extends BaseComponentElement {
@attr name: string; @attr name: string;
@attr label: string; @attr label: string;
@attr rules: string; @attr rules: string;
@target main: HTMLElement; @target main: HTMLElement;
@target inp: HTMLElement; @target inp: HTMLElement;
@attr displaykey: string = "name"; @target dropdowncontainer: HTMLElement;
@attr valuekey: string = "id"; @attr displaykey: string = 'name';
@attr fetch: string; @attr valuekey: string = 'id';
fetchFunc: any; @attr fetch: string;
@closest appForm: AppFormElement;
error: boolean; errorMessage: string;
errorMessage: string;
searchPhrase: string; searchPhrase: string;
randId: string; randId: string;
value: string; value: string;
@attr isOpen: boolean = false; @attr isOpen: boolean = false;
items: Array<any>; items: Array<any>;
totalItems: number; totalItems: number;
page: number = 1; page: number = 1;
rpp: number = 30; rpp: number = 30;
validator: Validator;
constructor() { constructor() {
super(); super();
} }
getItems = async (options?: any): Promise<void> => {
if (typeof this.fetchFunc !== "function") return;
try {
const response = await this.fetchFunc(options);
this.setItems(response);
} catch (err) {
this.update();
}
};
setItems = (response): void => { updateCallback = () => {
if (response) { this.dropdowncontainer?.scrollIntoView();
let items = []; };
if (Array.isArray(response)) {
items = response;
} else if (Array.isArray(response.items)) {
items = response.items;
this.totalItems = response?.totalRecords;
} else {
items = [];
}
this.items = items;
this.update();
}
};
get selectedItem() { public elementConnected = (): void => {
const { value, valuekey, items } = this; this.validator = new Validator(this, this.appForm, this.rules);
const item = items?.find((item) => { this.randId = `${name}${randomId()}`;
return value == item[valuekey]; this.update();
});
console.log(item, value, valuekey); const options = {
rpp: this.rpp,
page: this.page,
};
this.getItems(options);
};
return item; attributeChangedCallback(): void {
} this.update();
}
get optionValues() { setError = (error) => {
let values = []; this.validator.error = error;
this.inp.childNodes.forEach((item: HTMLElement) => { };
const value = item.getAttribute("value");
const name = item.innerText;
values.push({ name, value });
});
return values;
}
public elementConnected = (): void => { get error(): string {
this.randId = `${name}${randomId()}`; return this.validator?.error;
this.fetchFunc = findMethod(this.fetch, this.appMain); }
this.update();
const options = { get isValid(): boolean {
rpp: this.rpp, return this.validator?.valid;
page: this.page, }
};
this.getItems(options);
};
attributeChangedCallback(): void { get required(): boolean {
this.update(); return this.rules.includes('required');
} }
get valid(): boolean { get _value() {
return !!this.error; return this.value;
} }
get required(): boolean { validate = (): boolean => {
return this.rules.includes("required"); return this.validator.validate();
} };
validate(): boolean { getItems = async (options?: any): Promise<void> => {
let _return = true; if (typeof this.fetchFunc !== 'function') return;
const rules = this.rules?.split("|").filter((a) => a); try {
const value = (this.inp as HTMLSelectElement)?.value; const response = await this.fetchFunc(options);
rules this.setItems(response);
.slice() } catch (err) {
.reverse() this.update();
.forEach((rule) => { }
let valid = true; };
if (rule == "required") {
if (value === "") valid = false;
} else {
if (validator.hasOwnProperty(rule)) {
valid = validator?.[rule]?.(value);
}
}
if (!valid) {
const error = validatorErrors[rule]?.replaceAll(
"{- name}",
firstUpper(this.name?.toString())
);
_return = false;
this.error = error;
}
});
if (_return) {
this.error = null;
}
this.update();
return _return;
}
openDropdown = () => { setItems = (response): void => {
this.isOpen = true; if (response) {
}; let items = [];
if (Array.isArray(response)) {
items = response;
} else if (Array.isArray(response.items)) {
items = response.items;
this.totalItems = response?.totalRecords;
} else {
items = [];
}
this.items = items;
this.update();
}
};
stopPropagation = (e) => { get selectedItem() {
e.stopPropagation(); const { value, valuekey, items } = this;
}; const item = items?.find((item) => {
return value == item[valuekey];
});
toggleDropdown = () => { return item;
const isOpen = this.isOpen; }
this.isOpen = !isOpen;
};
itemSelected = (e) => { get optionValues() {
const value = (e.target as HTMLSpanElement).getAttribute("data-value"); let values = [];
this.value = value; this.inp.childNodes.forEach((item: HTMLElement) => {
this.isOpen = false; const value = item.getAttribute('value');
}; const name = item.innerText;
values.push({ name, value });
});
return values;
}
get _value() { get fetchFunc() {
return this.value; return findMethod(this.fetch, this.appMain);
} }
render = () => { setOpen = (isOpen) => {
const { this.isOpen = isOpen;
label, };
error,
errorMessage,
isOpen,
searchPhrase,
items,
selectedItem,
displaykey,
valuekey,
} = this;
console.log(isOpen); openDropdown = () => {
this.setOpen(true);
};
const renderItem = (item) => { stopPropagation = (e) => {
return html` <li e.stopPropagation();
class="dropdown-custom-listitem ${selectedItem?.[valuekey] == };
item[valuekey]
? "--selected"
: ""}"
app-action="click:app-dropdown#itemSelected"
data-value="${item[valuekey]}"
>
${item[displaykey]}
</li>`;
};
const renderItems = (_items) => { toggleDropdown = () => {
return _items.map((item) => renderItem(item)); const isOpen = this.isOpen;
}; this.setOpen(!isOpen);
};
return html` itemSelected = (e) => {
<div> const value = (e.target as HTMLSpanElement).getAttribute('data-value');
<label app-action="click:app-dropdown#openDropdown"> this.setOpen(false);
${label ? html`<div>${label}</div>` : html``} this.setValue(value);
<div this.appForm?.inputChange(e);
class="dropdown-custom" };
app-action="click:app-dropdown#stopPropagation"
> setValue = (value) => {
<div this.value = value;
class="dropdown-custom-top" this.update();
app-action="click:app-dropdown#toggleDropdown" };
>
<span class="dropdown-custom-fieldname" render = () => {
>${selectedItem const { label, error, errorMessage, isOpen, searchPhrase, items, selectedItem, displaykey, valuekey } = this;
? selectedItem[displaykey]
: "Select"}</span const renderItem = (item) => {
> return html` <li
</div> class="dropdown-custom-listitem ${selectedItem?.[valuekey] == item[valuekey] ? '--selected' : ''}"
${isOpen app-action="click:app-dropdown#itemSelected"
? html` data-value="${item[valuekey]}"
<div class="dropdown-custom-open"> >
<input ${item[displaykey]}
class="dropdown-custom-search" </li>`;
type="text" };
value="${searchPhrase}"
app-action="input:app-dropdown#phraseChange" const renderItems = (_items) => {
autofocus return _items?.map((item) => renderItem(item));
/> };
<ul class="dropdown-custom-list">
${renderItems(items)} return html`
</ul> <div>
</div> <label app-action="click:app-dropdown#openDropdown">
` ${label ? html`<div>${label}</div>` : html``}
: html``} <div class="dropdown-custom" app-action="click:app-dropdown#stopPropagation">
</div> <div class="dropdown-custom-top${isOpen ? ' --open' : ''}" app-action="click:app-dropdown#toggleDropdown">
${error <span class="dropdown-custom-fieldname">${selectedItem ? selectedItem[displaykey] : 'Select'}</span>
? html` <div class="h5 text-red">${errorMessage}</div>` </div>
: html``} ${isOpen
</label> ? html`
</div> <div class="dropdown-custom-open" data-target="app-dropdown.dropdowncontainer">
`; <input
}; class="dropdown-custom-search"
type="text"
value="${searchPhrase}"
app-action="input:app-dropdown#phraseChange"
autofocus
/>
<ul class="dropdown-custom-list">
${renderItems(items)}
</ul>
</div>
`
: html``}
</div>
${error ? html` <div class="h5 text-red">${errorMessage}</div>` : html``}
</label>
</div>
`;
};
} }
export type { AppDropdownElement }; export type { AppDropdownElement };

View File

@@ -1,135 +1,142 @@
import { attr, controller, target } from "@github/catalyst"; import { attr, controller, target } from '@github/catalyst';
import { html, TemplateResult, unsafeHTML } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { BaseComponentElement } from "common/"; import { BaseComponentElement } from 'common/';
import { AppDropdownElement } from "components/app-dropdown/AppDropdownElement"; import { AppDropdownElement } from 'components/app-dropdown/AppDropdownElement';
import { InputFieldElement } from "components/input-field/InputFieldElement"; import { InputFieldElement } from 'components/input-field/InputFieldElement';
import { findMethod, isTrue, querys } from "core/utils"; import { findMethod, isTrue, querys } from 'core/utils';
@controller @controller
class AppFormElement extends BaseComponentElement { class AppFormElement extends BaseComponentElement {
@target formElement: HTMLElement; @target formElement: HTMLElement;
@target innerSlot: HTMLElement; @target innerSlot: HTMLElement;
@querys inputField: NodeListOf<InputFieldElement>; @querys inputField: NodeListOf<InputFieldElement>;
@querys appDropdown: NodeListOf<AppDropdownElement>; @querys appDropdown: NodeListOf<AppDropdownElement>;
@attr custom: string; @attr custom: string;
@attr hasCancel: string; @attr hasCancel: string;
slotted: any; slotted: any;
isValid: boolean = false; isValid: boolean = false;
error: string; error: string;
constructor() { constructor() {
super(); super();
} }
public inputChange = (e) => { get submitFunc() {
this.validate(); return findMethod(this.custom, this.appMain);
this.update(); }
};
public onSubmit = (e) => { public inputChange = (e) => {
e.preventDefault(); this.validate();
if (!this.valid) { this.update();
return; };
}
const actionString = this.custom;
const submitFunc = findMethod(actionString, this.appMain);
submitFunc?.(this.values);
return false;
};
public validate = () => { public onSubmit = (e) => {
this.isValid = true; e.preventDefault();
this.inputField?.forEach((input) => { if (!this.valid) {
if (input?.error) { return;
this.isValid = false; }
} this.submitFunc?.(this.values);
}); return false;
}; };
public setError = (error) => { public validate = () => {
this.error = error; const validArr = [];
this.update(); this.inputField?.forEach((input) => {
}; validArr.push(input?.validate());
});
this.appDropdown?.forEach((input) => {
validArr.push(input?.validate());
});
this.isValid = !validArr?.includes(false);
};
public goBack = (e) => { public setError = (error) => {
e.preventDefault(); this.error = error;
this.update();
};
if (this.appMain?.appModal) { public goBack = (e) => {
this.appMain?.closeModal?.(); e.preventDefault();
} else if (this.routerService?.canGoBack) {
this.routerService?.goBack();
} else {
this.routerService?.goTo("/");
}
};
get values(): any { if (this.appMain?.appModal) {
const formObject: any = {}; this.appMain?.closeModal?.();
this.inputField.forEach((input: InputFieldElement) => { } else if (this.routerService?.canGoBack) {
formObject[input.name] = input._value; this.routerService?.goBack();
}); } else {
this.appDropdown?.forEach((input: AppDropdownElement) => { this.routerService?.goTo('/');
if (input.required && input.value) { }
formObject[input.name] = input._value; };
}
});
return formObject;
}
get valid() { get values(): any {
let _valid = 0; const formObject: any = {};
this.inputField?.forEach((input) => { this.inputField.forEach((input: InputFieldElement) => {
if (input.required && (input.inp as HTMLSelectElement).value) { formObject[input.name] = input._value;
_valid++; });
} this.appDropdown?.forEach((input: AppDropdownElement) => {
}); if (input.required && input.value) {
return _valid == this.inputField?.length; formObject[input.name] = input._value;
} }
});
return formObject;
}
elementConnected = (): void => { getInput = (name: string): InputFieldElement | AppDropdownElement => {
const _template = document.createElement("template"); let formObject;
const _slot = this.innerHTML; this.inputField.forEach((input: InputFieldElement) => {
_template.innerHTML = _slot; const inputType = input;
this.innerHTML = null; if (inputType.name === name) formObject = inputType;
this.update(); });
this.appDropdown.forEach((input: AppDropdownElement) => {
const inputType = input;
if (inputType.name === name) formObject = inputType;
});
return formObject;
};
this.formElement.replaceChild(_template.content, this.innerSlot); get valid() {
}; let _valid = 0;
this.inputField?.forEach((input) => {
if (input.required && (input.inp as HTMLSelectElement).value) {
_valid++;
}
});
return _valid == this.inputField?.length;
}
render = (): TemplateResult => { elementConnected = (): void => {
const renderSubmit = (valid: boolean) => { const _template = document.createElement('template');
if (!valid) { const _slot = this.innerHTML;
return html` <button type="submit" disabled>Submit</button> `; _template.innerHTML = _slot;
} this.innerHTML = null;
return html` <button type="submit">Submit</button> `; this.update();
};
const renderError = (error: string) => {
if (error) {
return html`<span>${error}</span>`;
}
return html``;
};
const renderCancel = (hasCancel: boolean) => {
if (hasCancel) {
return html`<button
type="button"
app-action="click:app-form#goBack"
>
Cancel
</button>`;
}
return html``;
};
return html`<form this.formElement.replaceChild(_template.content, this.innerSlot);
app-action="submit:app-form#onSubmit" };
data-target="app-form.formElement"
> render = (): TemplateResult => {
<slot data-target="app-form.innerSlot"></slot> const renderSubmit = (valid: boolean) => {
${renderError(this.error)}${renderSubmit( if (!valid) {
this.isValid return html` <button type="submit" disabled>Submit</button> `;
)}${renderCancel(isTrue(this.hasCancel))} }
</form>`; return html` <button type="submit">Submit</button> `;
}; };
const renderError = (error: string) => {
if (error) {
return html`<span>${error}</span>`;
}
return html``;
};
const renderCancel = (hasCancel: boolean) => {
if (hasCancel) {
return html`<button type="button" app-action="click:app-form#goBack">Cancel</button>`;
}
return html``;
};
return html`<form app-action="submit:app-form#onSubmit" data-target="app-form.formElement">
<slot data-target="app-form.innerSlot"></slot>
${renderError(this.error)}${renderSubmit(this.isValid)}${renderCancel(isTrue(this.hasCancel))}
</form>`;
};
} }
export type { AppFormElement }; export type { AppFormElement };

View File

@@ -1,72 +1,67 @@
import { attr, controller, target } from "@github/catalyst"; import { attr, controller, target } from '@github/catalyst';
import { isTrue } from "core/utils"; import { isTrue } from 'core/utils';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { AppMainElement } from "components/app-main/AppMainElement"; import { AppMainElement } from 'components/app-main/AppMainElement';
import { RouterService } from "core/services"; import { RouterService } from 'core/services';
import { BaseComponentElement } from "common/"; import { BaseComponentElement } from 'common/';
@controller @controller
class AppLinkElement extends BaseComponentElement { class AppLinkElement extends BaseComponentElement {
@attr to: string; @attr to: string;
@attr goBack: string; @attr goBack: string;
@attr title: string; @attr title: string;
@target main: Element; @target main: Element;
constructor() { constructor() {
super(); super();
} }
elementConnected = (): void => { elementConnected = (): void => {
if (!this.title && this.innerText) { if (!this.title && this.innerText) {
const _slottedText = this.innerText; const _slottedText = this.innerText;
this.innerText = null; this.innerText = null;
this.title = _slottedText; this.title = _slottedText;
} }
this.update(); this.update();
if (isTrue(this.goBack)) { if (isTrue(this.goBack)) {
this.appMain.addEventListener("routechanged", this.update); this.appMain.addEventListener('routechanged', this.update);
} }
}; };
elementDisconnected = (appMain: AppMainElement): void => { elementDisconnected = (appMain: AppMainElement): void => {
if (isTrue(this.goBack)) { if (isTrue(this.goBack)) {
appMain?.removeEventListener("routechanged", this.update); appMain?.removeEventListener('routechanged', this.update);
} }
}; };
goTo = (e: Event): void => { goTo = (e: Event): void => {
e.preventDefault(); e.preventDefault();
if (!isTrue(this.goBack) && this.to) { if (!isTrue(this.goBack) && this.to) {
this.routerService.goTo(this.to); this.routerService.goTo(this.to);
} else { } else {
this.routerService.goBack(); this.routerService.goBack();
} }
this.update(); this.update();
}; };
get disabled(): boolean { get disabled(): boolean {
if (isTrue(this.goBack)) { if (isTrue(this.goBack)) {
return this.routerService.emptyState; return this.routerService.emptyState;
} }
return false; return false;
} }
render = (): TemplateResult => { render = (): TemplateResult => {
return html`${this.disabled return html`${this.disabled
? html`<a ? html`<a class="btn btn-link btn-disabled" data-target="app-link.main" style="color:grey">${this.title}</a>`
class="btn btn-link btn-disabled" : html`<a
data-target="app-link.main" class="btn btn-link"
style="color:grey" data-target="app-link.main"
>${this.title}</a app-action="click:app-link#goTo"
>` href="${this.to}"
: html`<a style="text-decoration: underline; cursor: pointer;"
class="btn btn-link" >${this.title}</a
data-target="app-link.main" >`}`;
app-action="click:app-link#goTo" };
href="${this.to}"
style="text-decoration: underline; cursor: pointer;"
>${this.title}</a
>`}`;
};
} }
export type { AppLinkElement }; export type { AppLinkElement };

View File

@@ -1,58 +1,58 @@
import { controller, target } from "@github/catalyst"; import { controller, target } from '@github/catalyst';
import { html } from "@github/jtml"; import { html } from 'core/utils';
import { BaseComponentElement } from "common/"; import { BaseComponentElement } from 'common/';
@controller @controller
class AppLoaderElement extends BaseComponentElement { class AppLoaderElement extends BaseComponentElement {
private finished: boolean = true; private finished: boolean = true;
private _loading: number = 0; private _loading: number = 0;
constructor() { constructor() {
super(); super();
} }
public start = () => { public start = () => {
this.finished = false; this.finished = false;
this._loading++; this._loading++;
this.update(); this.update();
}; };
public stop = () => { public stop = () => {
if (this._loading > 0) { if (this._loading > 0) {
this._loading--; this._loading--;
} }
if (this._loading == 0) { if (this._loading == 0) {
this.finishInitiate(); this.finishInitiate();
} }
this.update(); this.update();
}; };
public get loading() { public get loading() {
return this._loading > 0; return this._loading > 0;
} }
private finishInitiate = () => { private finishInitiate = () => {
setTimeout(() => { setTimeout(() => {
this.finished = true; this.finished = true;
this.update(); this.update();
}, 300); }, 300);
}; };
elementConnected = (): void => { elementConnected = (): void => {
this.update(); this.update();
}; };
render = () => { render = () => {
const renderLoader = (finished: boolean, loading: boolean) => { const renderLoader = (finished: boolean, loading: boolean) => {
if (!finished && !loading) { if (!finished && !loading) {
return html`<div class="loader --removing"></div>`; return html`<div class="loader --removing"></div>`;
} else if (loading) { } else if (loading) {
return html`<div class="loader --loading"></div>`; return html`<div class="loader --loading"></div>`;
} }
return html``; return html``;
}; };
return html`${renderLoader(this.finished, this.loading)}`; return html`${renderLoader(this.finished, this.loading)}`;
}; };
} }
export type { AppLoaderElement }; export type { AppLoaderElement };

View File

@@ -1,185 +1,185 @@
import { controller, target } from "@github/catalyst"; import { controller, target } from '@github/catalyst';
import { AppService, HttpClient, RouterService } from "core/services"; import { AppService, HttpClient, RouterService } from 'core/services';
import { AuthStore } from "core/store"; import { AuthStore } from 'core/store';
import { AppModalElement, AppRootElement } from "components/"; import { AppModalElement, AppRootElement } from 'components/';
import { closest } from "core/utils"; import { closest } from 'core/utils';
import { AppLoaderElement } from "components/app-loader/AppLoaderElement"; import { AppLoaderElement } from 'components/app-loader/AppLoaderElement';
@controller @controller
class AppMainElement extends HTMLElement { class AppMainElement extends HTMLElement {
public routerService: RouterService; public routerService: RouterService;
public authStore: AuthStore; public authStore: AuthStore;
private httpClient: HttpClient; private httpClient: HttpClient;
public appService: AppService; public appService: AppService;
@target appModal: AppModalElement; @target appModal: AppModalElement;
@target mainRoot: AppRootElement; @target mainRoot: AppRootElement;
@target appLoader: AppLoaderElement; @target appLoader: AppLoaderElement;
@closest appMain: AppMainElement; @closest appMain: AppMainElement;
public domEvents: any = { public domEvents: any = {
routechanged: new Event("routechanged"), routechanged: new Event('routechanged'),
tokenchange: new Event("tokenchange"), tokenchange: new Event('tokenchange'),
walletupdate: new Event("walletupdate"), walletupdate: new Event('walletupdate'),
}; };
constructor() { constructor() {
super(); super();
} }
connectedCallback() { connectedCallback() {
if (this.appMain !== this) return; if (this.appMain !== this) return;
this.createLoader(); this.createLoader();
const mainRoot = this.createMainRoot(); const mainRoot = this.createMainRoot();
this.httpClient = new HttpClient(); this.httpClient = new HttpClient();
this.appService = new AppService(this, this.httpClient); this.appService = new AppService(this, this.httpClient);
this.routerService = new RouterService(this, mainRoot); this.routerService = new RouterService(this, mainRoot);
this.authStore = new AuthStore(this, this.appService); this.authStore = new AuthStore(this, this.appService);
this.routerService.setRoutes([ this.routerService.setRoutes([
{ {
path: "/", path: '/',
component: "home-page", component: 'home-page',
layout: "menu-layout", layout: 'menu-layout',
middleware: this.isAuth, middleware: this.isAuth,
}, },
{ {
path: "/home", path: '/home',
component: "home-page", component: 'home-page',
layout: "menu-layout", layout: 'menu-layout',
middleware: this.isAuth, middleware: this.isAuth,
}, },
{ {
path: "/history", path: '/history',
component: "history-page", component: 'history-page',
layout: "menu-layout", layout: 'menu-layout',
middleware: this.isAuth, middleware: this.isAuth,
}, },
{ {
path: "/wallet", path: '/wallet',
component: "history-page", component: 'history-page',
layout: "menu-layout", layout: 'menu-layout',
middleware: this.isAuth, middleware: this.isAuth,
children: [ children: [
{ {
path: "/all", path: '/all',
component: "wallet-list", component: 'wallet-list',
layout: "menu-layout", layout: 'menu-layout',
}, },
{ {
path: "/:walletId", path: '/:walletId',
component: "wallet-page", component: 'wallet-page',
layout: "menu-layout", layout: 'menu-layout',
}, },
], ],
}, },
{ {
path: "/register", path: '/register',
component: "register-page", component: 'register-page',
layout: "initial-layout", layout: 'initial-layout',
}, },
{ {
path: "/login", path: '/login',
component: "login-page", component: 'login-page',
layout: "initial-layout", layout: 'initial-layout',
}, },
{ {
path: "/unauthorized", path: '/unauthorized',
component: "login-page", component: 'login-page',
layout: "initial-layout", layout: 'initial-layout',
}, },
{ {
path: "/token-expired", path: '/token-expired',
component: "login-page", component: 'login-page',
layout: "initial-layout", layout: 'initial-layout',
}, },
{ {
path: "/not-found", path: '/not-found',
component: "not-found", component: 'not-found',
layout: "initial-layout", layout: 'initial-layout',
}, },
{ {
path: "/logout", path: '/logout',
component: "logout-page", component: 'logout-page',
}, },
]); ]);
this.routerService.init(); this.routerService.init();
} }
middleAuth = () => { middleAuth = () => {
if (!this.isAuth) { if (!this.isAuth) {
this.routerService.goTo("/unauthorized"); this.routerService.goTo('/unauthorized');
return true; return true;
} }
}; };
createModal = (element: string) => { createModal = (element: string) => {
this.closeModal(); this.closeModal();
this.appMain.addEventListener("routechanged", this.closeModal); this.appMain.addEventListener('routechanged', this.closeModal);
const _appModal = this.createAppModal(); const _appModal = this.createAppModal();
const _modalContent = this.createModalContent(element); const _modalContent = this.createModalContent(element);
const _modalOverlay = this.createModalOverlay(); const _modalOverlay = this.createModalOverlay();
_modalOverlay.appendChild(_modalContent); _modalOverlay.appendChild(_modalContent);
_appModal.appendChild(_modalOverlay); _appModal.appendChild(_modalOverlay);
this.appendChild(_appModal); this.appendChild(_appModal);
}; };
public triggerWalletUpdate = () => { public triggerWalletUpdate = () => {
this.dispatchEvent(this.domEvents.walletupdate); this.dispatchEvent(this.domEvents.walletupdate);
}; };
public setTitle = (title: string): void => { public setTitle = (title: string): void => {
if (!title) title = __CONFIG__.appName; if (!title) title = __CONFIG__.appName;
window.document.title = title; window.document.title = title;
}; };
private createAppModal = () => { private createAppModal = () => {
const _appModal = document.createElement("app-modal"); const _appModal = document.createElement('app-modal');
_appModal.setAttribute("data-target", "app-main.appModal"); _appModal.setAttribute('data-target', 'app-main.appModal');
return _appModal; return _appModal;
}; };
private createLoader = () => { private createLoader = () => {
const _loader = document.createElement("app-loader"); const _loader = document.createElement('app-loader');
_loader.setAttribute("data-target", "app-main.appLoader"); _loader.setAttribute('data-target', 'app-main.appLoader');
this.appendChild(_loader); this.appendChild(_loader);
}; };
private createModalContent = (element: string) => { private createModalContent = (element: string) => {
const _modalElement = document.createElement(element); const _modalElement = document.createElement(element);
const _divEl = document.createElement("div"); const _divEl = document.createElement('div');
_modalElement.setAttribute("data-target", "app-modal.modalElement"); _modalElement.setAttribute('data-target', 'app-modal.modalElement');
_divEl.setAttribute("data-target", "app-modal.modalContent"); _divEl.setAttribute('data-target', 'app-modal.modalContent');
_divEl.setAttribute("data-action", "click:app-main#preventClosing"); _divEl.setAttribute('data-action', 'click:app-main#preventClosing');
_divEl.appendChild(_modalElement); _divEl.appendChild(_modalElement);
return _divEl; return _divEl;
}; };
private createModalOverlay = () => { private createModalOverlay = () => {
const _divOverlay = document.createElement("div"); const _divOverlay = document.createElement('div');
_divOverlay.setAttribute("data-target", "app-modal.modalOverlay"); _divOverlay.setAttribute('data-target', 'app-modal.modalOverlay');
_divOverlay.setAttribute("data-action", "click:app-main#closeModal"); _divOverlay.setAttribute('data-action', 'click:app-main#closeModal');
return _divOverlay; return _divOverlay;
}; };
private createMainRoot = () => { private createMainRoot = () => {
if (this.mainRoot) this.removeChild(this.mainRoot); if (this.mainRoot) this.removeChild(this.mainRoot);
const _mainRoot = document.createElement("app-root"); const _mainRoot = document.createElement('app-root');
_mainRoot.setAttribute("data-target", "app-main.mainRoot"); _mainRoot.setAttribute('data-target', 'app-main.mainRoot');
this.appendChild(_mainRoot); this.appendChild(_mainRoot);
return _mainRoot; return _mainRoot;
}; };
preventClosing = (e: Event) => { preventClosing = (e: Event) => {
e.stopPropagation(); e.stopPropagation();
}; };
closeModal = () => { closeModal = () => {
if (this.appModal) this.removeChild(this.appModal); if (this.appModal) this.removeChild(this.appModal);
this.appMain.removeEventListener("routechanged", this.closeModal); this.appMain.removeEventListener('routechanged', this.closeModal);
}; };
isAuth = (): boolean => { isAuth = (): boolean => {
return this.authStore && !!this.authStore.token; return this.authStore && !!this.authStore.token;
}; };
} }
export type { AppMainElement }; export type { AppMainElement };

View File

@@ -1,164 +1,132 @@
import { controller, target } from "@github/catalyst"; import { controller, target } from '@github/catalyst';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { BaseComponentElement } from "common/"; import { BaseComponentElement } from 'common/';
import { AppMainElement } from "components/app-main/AppMainElement"; import { AppMainElement } from 'components/app-main/AppMainElement';
import { MenuItemElement } from "components/menu-item/MenuItemElement"; import { MenuItemElement } from 'components/menu-item/MenuItemElement';
import { WalletService } from "services/"; import { WalletService } from 'services/';
@controller @controller
class AppMenuElement extends BaseComponentElement { class AppMenuElement extends BaseComponentElement {
private walletService: WalletService; private walletService: WalletService;
private walletData: Array<any>; private walletData: Array<any>;
private totalWallets: number; private totalWallets: number;
@target walletlist: MenuItemElement; @target walletlist: MenuItemElement;
constructor() { constructor() {
super(); super();
} }
elementConnected = (): void => { elementConnected = (): void => {
this.walletService = new WalletService(this.appMain?.appService); this.walletService = new WalletService(this.appMain?.appService);
if (this.appMain.isAuth) { if (this.appMain.isAuth) {
this.getWallets(); this.getWallets();
} }
this.update(); this.update();
this.appMain.addEventListener("tokenchange", this.updateToken); this.appMain.addEventListener('tokenchange', this.updateToken);
this.appMain.addEventListener("walletupdate", this.updateToken); this.appMain.addEventListener('walletupdate', this.updateToken);
}; };
elementDisconnected = (appMain: AppMainElement): void => { elementDisconnected = (appMain: AppMainElement): void => {
appMain?.removeEventListener("tokenchange", this.updateToken); appMain?.removeEventListener('tokenchange', this.updateToken);
appMain?.removeEventListener("walletupdate", this.updateToken); appMain?.removeEventListener('walletupdate', this.updateToken);
}; };
getWallets = async (): Promise<void> => { getWallets = async (): Promise<void> => {
try { try {
const response = await this.walletService.getAll({ rpp: 3 }); const response = await this.walletService.getAll({ rpp: 3 });
this.setWallets(response.items, response.totalRecords); this.setWallets(response.items, response.totalRecords);
} catch (err) { } catch (err) {
this.update(); this.update();
} }
}; };
setWallets = (wallets: Array<any>, totalWallets: number): void => { setWallets = (wallets: Array<any>, totalWallets: number): void => {
this.walletData = wallets; this.walletData = wallets;
this.totalWallets = totalWallets; this.totalWallets = totalWallets;
this.update(); this.update();
}; };
updateToken = (): void => { updateToken = (): void => {
if (this.isAuth) { if (this.isAuth) {
this.getWallets(); this.getWallets();
} else { } else {
this.update(); this.update();
} }
}; };
get isAuth(): boolean { get isAuth(): boolean {
if (this.appMain?.isAuth) { if (this.appMain?.isAuth) {
return true; return true;
} }
return false; return false;
} }
renderWallets = (): Array<TemplateResult> => { renderWallets = (): Array<TemplateResult> => {
if (this.isAuth && this.totalWallets > 0) { if (this.isAuth && this.totalWallets > 0) {
return this.walletData.map( return this.walletData.map(
(wallet) => html`<menu-item data-path="/wallet/${wallet.id}" (wallet) => html`<menu-item data-path="/wallet/${wallet.id}">${wallet.name}</menu-item>`
>${wallet.name}</menu-item );
>` }
); return null;
} };
return null;
};
modalWallet = (): void => { modalWallet = (): void => {
const _modal = this.appMain.appModal; const _modal = this.appMain.appModal;
if (_modal) { if (_modal) {
this.appMain.closeModal(); this.appMain.closeModal();
} else { } else {
this.appMain.createModal("wallet-create"); this.appMain.createModal('wallet-create');
} }
}; };
modalTransaction = (): void => { modalTransaction = (s): void => {
const _modal = this.appMain.appModal; const _modal = this.appMain.appModal;
if (_modal) { if (_modal) {
this.appMain.closeModal(); this.appMain.closeModal();
} else { } else {
this.appMain.createModal("transaction-create"); this.appMain.createModal('transaction-create');
} }
}; };
render = (): TemplateResult => { render = (): TemplateResult => {
const { isAuth, totalWallets, walletData } = this; const { isAuth, totalWallets, walletData } = this;
const regularMenu = ( const regularMenu = (path: string, title: string, action?: string): TemplateResult => {
path: string, if (action) {
title: string, return html` <menu-item data-path="${path}" data-customaction="${action}">${title}</menu-item> `;
action?: string }
): TemplateResult => { return html`<menu-item data-path="${path}">${title}</menu-item>`;
if (action) { };
return html` const authMenu = (path: string, title: string, action?: string): TemplateResult => {
<menu-item data-path="${path}" data-customaction="${action}" if (isAuth) {
>${title}</menu-item return regularMenu(path, title, action);
> }
`; return html``;
} };
return html`<menu-item data-path="${path}">${title}</menu-item>`; const notAuthMenu = (path: string, title: string, action?: string): TemplateResult => {
}; if (!isAuth) {
const authMenu = ( return regularMenu(path, title, action);
path: string, }
title: string, return html``;
action?: string };
): TemplateResult => { const renderWallets = (wallets: Array<any>) => {
if (isAuth) { if (isAuth && totalWallets > 0) {
return regularMenu(path, title, action); return html`<div class="menu-item divider"></div>
} ${wallets.map((wallet) => regularMenu(`/wallet/${wallet.id}`, wallet.name))}`;
return html``; }
}; return html``;
const notAuthMenu = ( };
path: string, const menuHeader = (title) => html`<div class="menu-item menu-header">${title}</div>`;
title: string,
action?: string
): TemplateResult => {
if (!isAuth) {
return regularMenu(path, title, action);
}
return html``;
};
const renderWallets = (wallets: Array<any>) => {
if (isAuth && totalWallets > 0) {
return html`<div class="menu-item divider"></div>
${wallets.map((wallet) =>
regularMenu(`/wallet/${wallet.id}`, wallet.name)
)}`;
}
return html``;
};
const menuHeader = (title) =>
html`<div class="menu-item menu-header">${title}</div>`;
return html` return html`
<div data-target="app-menu.sidebar"> <div data-target="app-menu.sidebar">
${menuHeader(__CONFIG__.appName)} ${regularMenu("/", "Home")} ${menuHeader(__CONFIG__.appName)} ${regularMenu('/', 'Home')}
${authMenu( ${authMenu('/history', 'Transaction History', 'click:app-menu#modalTransaction')}
"/history", ${authMenu('/wallet/all', 'My Wallets', 'click:app-menu#modalWallet')} ${renderWallets(walletData)}
"Transaction History", <span class="menu-item divider"></span>
"click:app-menu#modalTransaction" ${authMenu('/logout', 'Logout')} ${notAuthMenu('/login', 'Login')} ${notAuthMenu('/register', 'Register')}
)} </div>
${authMenu( `;
"/wallet/all", };
"My Wallets",
"click:app-menu#modalWallet"
)}
${renderWallets(walletData)}
<span class="menu-item divider"></span>
${authMenu("/logout", "Logout")}
${notAuthMenu("/login", "Login")}
${notAuthMenu("/register", "Register")}
</div>
`;
};
} }
export type { AppMenuElement }; export type { AppMenuElement };

View File

@@ -1,14 +1,14 @@
import { controller, target } from "@github/catalyst"; import { controller, target } from '@github/catalyst';
import { BaseComponentElement } from "common/"; import { BaseComponentElement } from 'common/';
@controller @controller
class AppModalElement extends BaseComponentElement { class AppModalElement extends BaseComponentElement {
@target modalElement: HTMLElement; @target modalElement: HTMLElement;
constructor() { constructor() {
super(); super();
} }
elementConnected = (): void => {}; elementConnected = (): void => {};
} }
export type { AppModalElement }; export type { AppModalElement };

View File

@@ -1,148 +1,143 @@
import { attr, controller, target } from "@github/catalyst"; import { attr, controller, target } from '@github/catalyst';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { BaseComponentElement } from "common/"; import { BaseComponentElement } from 'common/';
import { CircleLoaderElement } from "components/circle-loader/CircleLoaderElement"; import { CircleLoaderElement } from 'components/circle-loader/CircleLoaderElement';
@controller @controller
class AppPaginationElement extends BaseComponentElement { class AppPaginationElement extends BaseComponentElement {
public items: Array<any>; public items: Array<any>;
@attr page: number; @attr page: number;
@attr rpp: number; @attr rpp: number;
@attr totalItems: number; @attr totalItems: number;
@attr autoInit: string; @attr autoInit: string;
private customRenderItems: () => TemplateResult; private customRenderItems: () => TemplateResult;
private customRenderItem: (item: any) => TemplateResult; private customRenderItem: (item: any) => TemplateResult;
fetchFunc: Function = () => {}; fetchFunc: Function = () => {};
constructor() { constructor() {
super(); super();
} }
elementConnected = (): void => {}; elementConnected = (): void => {};
attributeChangedCallback(): void { attributeChangedCallback(): void {
this.update(); this.update();
} }
setItems = (items): void => { setItems = (items): void => {
this.items = items; this.items = items;
this.update(); this.update();
}; };
setFetchFunc = async (fetchFunc: Function, autoInit?): Promise<void> => { setFetchFunc = async (fetchFunc: Function, autoInit?): Promise<void> => {
this.fetchFunc = fetchFunc; this.fetchFunc = fetchFunc;
if (autoInit) { if (autoInit) {
const options = { const options = {
rpp: this.rpp || 5, rpp: this.rpp || 5,
page: this.page || 1, page: this.page || 1,
}; };
this.executeFetch(options); this.executeFetch(options);
} }
}; };
setCustomRenderItems = (customRenderItems: () => TemplateResult) => { setCustomRenderItems = (customRenderItems: () => TemplateResult) => {
this.customRenderItems = customRenderItems; this.customRenderItems = customRenderItems;
this.update(); this.update();
}; };
setCustomRenderItem = (customRenderItem: (item: any) => TemplateResult) => { setCustomRenderItem = (customRenderItem: (item: any) => TemplateResult) => {
this.customRenderItem = customRenderItem; this.customRenderItem = customRenderItem;
this.update(); this.update();
}; };
executeFetch = async (options?): Promise<void> => { executeFetch = async (options?): Promise<void> => {
if (!options) { if (!options) {
options = { options = {
rpp: this.rpp || 5, rpp: this.rpp || 5,
page: this.page || 1, page: this.page || 1,
}; };
} }
try { try {
this.loader?.start?.(); this.loader?.start?.();
const response = await this.fetchFunc(options); const response = await this.fetchFunc(options);
this.loader?.stop?.(); this.loader?.stop?.();
this.setItems(response?.items); this.setItems(response?.items);
this.totalItems = response?.totalRecords; this.totalItems = response?.totalRecords;
this.page = response?.page; this.page = response?.page;
this.rpp = response?.rpp; this.rpp = response?.rpp;
} catch (err) { } catch (err) {
this.loader?.stop?.(); this.loader?.stop?.();
console.error(err); console.error(err);
} }
}; };
pageBack = (): void => { pageBack = (): void => {
const { page } = this; const { page } = this;
if (page > 1) { if (page > 1) {
this.page--; this.page--;
this.executeFetch(); this.executeFetch();
} }
}; };
pageNext = (): void => { pageNext = (): void => {
const { rpp, totalItems, page } = this; const { rpp, totalItems, page } = this;
const pageRange = Math.ceil(totalItems / rpp); const pageRange = Math.ceil(totalItems / rpp);
if (page < pageRange) { if (page < pageRange) {
this.page++; this.page++;
this.executeFetch(); this.executeFetch();
} }
}; };
render = (): TemplateResult => { render = (): TemplateResult => {
const { rpp, totalItems, page, items } = this; const { rpp, totalItems, page, items } = this;
const renderItem = this.customRenderItem const renderItem = this.customRenderItem
? this.customRenderItem ? this.customRenderItem
: (item) => html`<tr> : (item) => html`<tr>
<td>${item.description}</td> <td>${item.description}</td>
<td>${item.amount}</td> <td>${item.amount}</td>
</tr>`; </tr>`;
const renderItems = this.customRenderItems const renderItems = this.customRenderItems
? this.customRenderItems ? this.customRenderItems
: () => { : () => {
if (this.loader && this.loader.loading) { if (this.loader && this.loader.loading) {
return html`<circle-loader></circle-loader>`; return html`<circle-loader></circle-loader>`;
} else { } else {
if (items?.length > 0) { if (items?.length > 0) {
return html`<div class="table"> return html`<table>
${items?.map((item) => renderItem(item))} ${items?.map((item) => renderItem(item))}
</div>`; </table>`;
} }
return html``; return html``;
} }
}; };
const renderPagination = () => { const renderPagination = () => {
if (totalItems > items?.length) { if (totalItems > items?.length) {
const pageRange = Math.ceil(totalItems / rpp); const pageRange = Math.ceil(totalItems / rpp);
return html` return html`
<div> <div>
<button <button
class="btn btn-primary btn-squared ${page <= 1 class="btn btn-primary btn-squared ${page <= 1 ? 'disabled' : ''}"
? "disabled" app-action="click:app-pagination#pageBack"
: ""}" >
app-action="click:app-pagination#pageBack" Prev
> </button>
Prev <button
</button> class="btn btn-primary btn-squared ${page >= pageRange ? 'disabled' : ''}"
<button app-action="click:app-pagination#pageNext"
class="btn btn-primary btn-squared ${page >= >
pageRange Next
? "disabled" </button>
: ""}" </div>
app-action="click:app-pagination#pageNext" `;
> }
Next };
</button>
</div>
`;
}
};
return html`<div>${renderItems()} ${renderPagination()}</div>`; return html`<div>${renderItems()} ${renderPagination()}</div>`;
}; };
} }
export type { AppPaginationElement }; export type { AppPaginationElement };

View File

@@ -1,14 +1,14 @@
import { controller, target } from "@github/catalyst"; import { controller, target } from '@github/catalyst';
import { BaseComponentElement } from "common/"; import { BaseComponentElement } from 'common/';
@controller @controller
class AppRootElement extends BaseComponentElement { class AppRootElement extends BaseComponentElement {
@target rootElement: HTMLElement; @target rootElement: HTMLElement;
constructor() { constructor() {
super(); super();
} }
elementConnected = (): void => {}; elementConnected = (): void => {};
} }
export type { AppRootElement }; export type { AppRootElement };

View File

@@ -1,24 +1,24 @@
import { controller } from "@github/catalyst"; import { controller } from '@github/catalyst';
import style from "styles/main.scss"; import style from 'styles/main.scss';
(function () { (function () {
const _shadow = new WeakMap(); const _shadow = new WeakMap();
@controller @controller
class AppShadowElement extends HTMLElement { class AppShadowElement extends HTMLElement {
constructor() { constructor() {
super(); super();
_shadow.set(this, this.attachShadow({ mode: "closed" })); _shadow.set(this, this.attachShadow({ mode: 'closed' }));
} }
connectedCallback() { connectedCallback() {
const _root = _shadow.get(this); const _root = _shadow.get(this);
const _appMain = document.createElement("app-main"); const _appMain = document.createElement('app-main');
const _style = document.createElement("style"); const _style = document.createElement('style');
_style.innerHTML = style; _style.innerHTML = style;
_root.appendChild(_style); _root.appendChild(_style);
_root.appendChild(_appMain); _root.appendChild(_appMain);
} }
} }
})(); })();

View File

@@ -1,14 +1,14 @@
import { controller, target } from "@github/catalyst"; import { controller, target } from '@github/catalyst';
import { BaseComponentElement } from "common/"; import { BaseComponentElement } from 'common/';
@controller @controller
class AppSlotElement extends BaseComponentElement { class AppSlotElement extends BaseComponentElement {
@target slotElement: HTMLElement; @target slotElement: HTMLElement;
constructor() { constructor() {
super(); super();
} }
elementConnected = (): void => {}; elementConnected = (): void => {};
} }
export type { AppSlotElement }; export type { AppSlotElement };

View File

@@ -1,23 +1,21 @@
import { attr, controller } from "@github/catalyst"; import { attr, controller } from '@github/catalyst';
import { html } from "@github/jtml"; import { html } from 'core/utils';
import { BaseComponentElement } from "common/"; import { BaseComponentElement } from 'common/';
@controller @controller
class CircleLoaderElement extends BaseComponentElement { class CircleLoaderElement extends BaseComponentElement {
@attr size: string; @attr size: string;
constructor() { constructor() {
super(); super();
} }
elementConnected = (): void => { elementConnected = (): void => {
this.update(); this.update();
}; };
render = () => { render = () => {
return html`<div return html`<div class="circle-loader ${this.size ? `-${this.size}` : ''}"></div>`;
class="circle-loader ${this.size ? `-${this.size}` : ""}" };
></div>`;
};
} }
export type { CircleLoaderElement }; export type { CircleLoaderElement };

View File

@@ -1,17 +1,17 @@
export * from "./app-link/AppLinkElement"; export * from './app-link/AppLinkElement';
export * from "./menu-item/MenuItemElement"; export * from './menu-item/MenuItemElement';
export * from "./app-pagination/AppPaginationElement"; export * from './app-pagination/AppPaginationElement';
export * from "./app-modal/AppModalElement"; export * from './app-modal/AppModalElement';
export * from "./app-root/AppRootElement"; export * from './app-root/AppRootElement';
export * from "./app-slot/AppSlotElement"; export * from './app-slot/AppSlotElement';
export * from "./app-menu/AppMenuElement"; export * from './app-menu/AppMenuElement';
export * from "./input-field/InputFieldElement"; export * from './input-field/InputFieldElement';
export * from "./app-dropdown/AppDropdownElement"; export * from './app-dropdown/AppDropdownElement';
export * from "./app-loader/AppLoaderElement"; export * from './app-loader/AppLoaderElement';
export * from "./circle-loader/CircleLoaderElement"; export * from './circle-loader/CircleLoaderElement';
export * from "./app-form/AppFormElement"; export * from './app-form/AppFormElement';
export * from "./wallet-header/WalletHeaderElement"; export * from './wallet-header/WalletHeaderElement';
// LAST // LAST
export * from "./app-main/AppMainElement"; export * from './app-main/AppMainElement';
export * from "./app-shadow/AppShadowElement"; export * from './app-shadow/AppShadowElement';

View File

@@ -1,125 +1,106 @@
import { attr, controller, target } from "@github/catalyst"; import { attr, controller, target } from '@github/catalyst';
import { closest, firstUpper } from "core/utils"; import { closest, firstUpper } from 'core/utils';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { RouterService } from "core/services"; import randomId from 'core/utils/random-id';
import randomId from "core/utils/random-id"; import { validatorErrors } from 'core/constants';
import validator from "validator"; import { BaseComponentElement, Validator } from 'common/';
import { validatorErrors } from "core/constants"; import { AppFormElement } from 'components/app-form/AppFormElement';
import { BaseComponentElement } from "common/"; import { validator } from 'core/utils';
import { AppFormElement } from "components/app-form/AppFormElement";
@controller @controller
class InputFieldElement extends BaseComponentElement { class InputFieldElement extends BaseComponentElement {
@attr name: string; @attr name: string;
@attr type: string; @attr type: string;
@attr label: string; @attr label: string;
@attr rules: string; @attr rules: string;
@target main: HTMLElement; @target main: HTMLElement;
@target inp: HTMLElement; @target inp: HTMLElement;
@closest appForm: AppFormElement; @closest appForm: AppFormElement;
error: string; valid: boolean;
displayError: boolean; displayError: boolean;
randId: string; randId: string;
constructor() {
super();
}
public elementConnected = (): void => { validator: Validator;
this.randId = `${name}${randomId()}`;
this.update();
this.validate();
};
get valid(): boolean { constructor() {
return !!this.error; super();
} }
get required(): boolean { public elementConnected = (): void => {
return this.rules.includes("required"); this.validator = new Validator(this, this.appForm, this.rules);
} this.randId = `${name}${randomId()}`;
this.update();
this.validate();
};
get _value() { setError = (error) => {
return (this.inp as HTMLInputElement)?.value; this.validator.error = error;
} };
validate = (): boolean => { get error(): string {
let _return = true; return this.validator.error;
const rules = this.rules?.split("|").filter((a) => a); }
const value = (this.inp as HTMLInputElement)?.value;
rules
.slice()
.reverse()
.forEach((rule) => {
let valid = true;
if (rule == "required") {
if (value === "") valid = false;
} else {
if (validator.hasOwnProperty(rule)) {
valid = validator?.[rule]?.(value);
}
}
if (!valid) {
const error = validatorErrors[rule]?.replaceAll(
"{- name}",
firstUpper(this.name?.toString())
);
_return = false;
this.error = error;
}
});
if (_return) {
this.error = null;
}
return _return;
};
validateDisplay = () => { get isValid(): boolean {
if (!this.validate()) { return this.validator.valid;
this.displayError = true; }
} else {
this.displayError = false;
}
this.update();
};
inputChange = (e) => { get required(): boolean {
this.validate(); return this.rules.includes('required');
this.appForm?.inputChange(e); }
};
render = (): TemplateResult => { get _value() {
const renderMessage = (label: string) => { return (this.inp as HTMLInputElement)?.value;
if (this.label) { }
return html`<label for="${this.randId}"
>${this.label}${this.required ? " (*)" : ""}</label
>`;
}
return html``;
};
const renderError = (displayError: boolean, error: string) => { validate = (): boolean => {
if (displayError) { return this.validator.validate();
return html`<span>${error}</span>`; };
}
return html``;
};
const renderInput = (type) => { validateDisplay = () => {
return html` <input if (!this.validate()) {
type="${this.type}" this.displayError = true;
data-target="input-field.inp" } else {
app-action=" this.displayError = false;
}
this.update();
};
inputChange = (e) => {
this.validate();
this.appForm?.inputChange(e);
};
render = (): TemplateResult => {
const renderMessage = (label: string) => {
if (this.label) {
return html`<label for="${this.randId}">${this.label}${this.required ? ' (*)' : ''}</label>`;
}
return html``;
};
const renderError = (displayError: boolean, error: string) => {
if (displayError) {
return html`<span>${error}</span>`;
}
return html``;
};
const renderInput = (type) => {
return html` <input
type="${this.type}"
data-target="input-field.inp"
app-action="
input:input-field#inputChange input:input-field#inputChange
blur:input-field#validateDisplay blur:input-field#validateDisplay
" "
/>`; />`;
}; };
return html`<div class="input-main" data-target="input-field.main"> return html`<div class="input-main" data-target="input-field.main">
${renderMessage(this.label)} ${renderInput(this.type)} ${renderMessage(this.label)} ${renderInput(this.type)} ${renderError(this.displayError, this.error)}
${renderError(this.displayError, this.error)} </div>`;
</div>`; };
};
} }
export type { InputFieldElement }; export type { InputFieldElement };

View File

@@ -1,55 +1,47 @@
import { attr, controller, target } from "@github/catalyst"; import { attr, controller, target } from '@github/catalyst';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { AppMainElement } from "components/app-main/AppMainElement"; import { AppMainElement } from 'components/app-main/AppMainElement';
import { BaseComponentElement } from "common/"; import { BaseComponentElement } from 'common/';
@controller @controller
class MenuItemElement extends BaseComponentElement { class MenuItemElement extends BaseComponentElement {
@attr path: string; @attr path: string;
@attr title: string; @attr title: string;
@attr customaction: string; @attr customaction: string;
@target itemEl: HTMLElement; @target itemEl: HTMLElement;
@target customButton: HTMLDivElement; @target customButton: HTMLDivElement;
constructor() { constructor() {
super(); super();
} }
public elementConnected = (): void => { public elementConnected = (): void => {
if (!this.title && this.innerText) { if (!this.title && this.innerText) {
const _slottedText = this.innerText; const _slottedText = this.innerText;
this.innerText = null; this.innerText = null;
this.title = _slottedText; this.title = _slottedText;
} }
this.update(); this.update();
this.appMain.addEventListener("routechanged", this.update); this.appMain.addEventListener('routechanged', this.update);
}; };
public elementDisconnected = (appMain: AppMainElement): void => { public elementDisconnected = (appMain: AppMainElement): void => {
appMain?.removeEventListener("routechanged", this.update); appMain?.removeEventListener('routechanged', this.update);
}; };
get current(): boolean { get current(): boolean {
return this.routerService.comparePath(this.path); return this.routerService.comparePath(this.path);
} }
render = (): TemplateResult => { render = (): TemplateResult => {
return html` return html`
<div <div class="${this.current ? 'selected ' : ''}menu-item" data-target="menu-item.itemEl">
class="${this.current ? "selected " : ""}menu-item" <app-link data-to="${this.path}">${this.title}</app-link>
data-target="menu-item.itemEl" ${this.customaction
> ? html`<div data-target="menu-item.customButton" app-action="${this.customaction}">+</div>`
<app-link data-to="${this.path}">${this.title}</app-link> : html``}
${this.customaction </div>
? html`<div `;
data-target="menu-item.customButton" };
app-action="${this.customaction}"
>
+
</div>`
: html``}
</div>
`;
};
} }
export type { MenuItemElement }; export type { MenuItemElement };

View File

@@ -1,70 +1,67 @@
import { attr, controller, target } from "@github/catalyst"; import { attr, controller, target } from '@github/catalyst';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { BaseComponentElement } from "common/"; import { BaseComponentElement } from 'common/';
import { CircleLoaderElement } from "components/circle-loader/CircleLoaderElement"; import { CircleLoaderElement } from 'components/circle-loader/CircleLoaderElement';
import { findMethod } from "core/utils"; import { findMethod } from 'core/utils';
@controller @controller
class WalletHeaderElement extends BaseComponentElement { class WalletHeaderElement extends BaseComponentElement {
@attr currentBalance: number; @attr currentBalance: number;
@attr lastMonth: number; @attr lastMonth: number;
@attr nextMonth: number; @attr nextMonth: number;
@attr currency: string; @attr currency: string;
@attr custom: string; @attr custom: string;
fetchFunc: Function = () => {}; fetchFunc: Function = () => {};
constructor() { constructor() {
super(); super();
} }
elementConnected = (): void => { elementConnected = (): void => {
this.executeFetch(); this.executeFetch();
this.update(); this.update();
}; };
attributeChangedCallback(): void { attributeChangedCallback(): void {
this.update(); this.update();
} }
executeFetch = async (options?): Promise<void> => { get submitFunc() {
const actionString = this.custom; return findMethod(this.custom, this.appMain);
const submitFunc = findMethod(actionString, this.appMain); }
try { executeFetch = async (options?): Promise<void> => {
this.loader?.start?.(); try {
await submitFunc(options); this.loader?.start?.();
this.loader?.stop?.(); await this.submitFunc(options);
} catch (err) { this.loader?.stop?.();
this.loader?.stop?.(); } catch (err) {
console.error(err); this.loader?.stop?.();
} console.error(err);
}; }
};
render = (): TemplateResult => { render = (): TemplateResult => {
const { currentBalance, currency, lastMonth, nextMonth } = this; const { currentBalance, currency, lastMonth, nextMonth } = this;
const renderItem = (header, balance, currency) => html`<div> const renderItem = (header, balance, currency) => html`<div>
<div>${header}</div> <div>${header}</div>
<div><span>${balance}</span><span>${currency}</span></div> <div><span>${balance}</span><span>${currency}</span></div>
</div>`; </div>`;
const renderHeader = () => { const renderHeader = () => {
if (this.loader && this.loader.loading) { if (this.loader && this.loader.loading) {
return html`<circle-loader></circle-loader>`; return html`<circle-loader></circle-loader>`;
} }
return html`${renderItem( return html`${renderItem('Last Month', lastMonth, currency)}${renderItem(
"Last Month", 'Current Balance',
lastMonth, currentBalance,
currency currency
)}${renderItem( )}${renderItem('Next Month', nextMonth, currency)}`;
"Current Balance", };
currentBalance,
currency
)}${renderItem("Next Month", nextMonth, currency)}`;
};
return html`<div>${renderHeader()}</div>`; return html`<div>${renderHeader()}</div>`;
}; };
} }
export type { WalletHeaderElement }; export type { WalletHeaderElement };

View File

@@ -1 +1 @@
export * from "./validatorErrors"; export * from './validatorErrors';

View File

@@ -1,4 +1,4 @@
export const validatorErrors = { export const validatorErrors = {
required: "{- name} is required.", required: '{- name} is required.',
isEmail: "{- name} needs to be email format.", isEmail: '{- name} needs to be email format.',
}; };

View File

@@ -1,147 +1,100 @@
import { AppMainElement } from "components/"; import { AppMainElement } from 'components/';
import { HttpClient } from ".."; import { HttpClient } from '..';
class AppService { class AppService {
constructor( constructor(public appMain: AppMainElement, public httpClient: HttpClient) {}
public appMain: AppMainElement,
public httpClient: HttpClient
) {}
post = async ( post = async (url: string, data: Object, headersParam: HeadersInit): Promise<any> => {
url: string, headersParam = {
data: Object, ...headersParam,
headersParam: HeadersInit Authorization: `BEARER ${this.appMain?.authStore?.token}`,
): Promise<any> => { };
headersParam = { try {
...headersParam, this?.appMain?.appLoader?.start?.();
Authorization: `BEARER ${this.appMain?.authStore?.token}`, const response = await this.httpClient.post(url, data, headersParam);
}; this?.appMain?.appLoader?.stop?.();
try { if (response?.statusCode == 400 || response?.statusCode == 500 || response?.statusCode == 401) {
this?.appMain?.appLoader?.start?.(); if (response?.statusCode == 401) {
const response = await this.httpClient.post( this.appMain.authStore.token = null;
url, this.appMain.routerService.goTo('/token-expired');
data, }
headersParam throw response;
); }
this?.appMain?.appLoader?.stop?.(); return response;
if ( } catch (err) {
response?.statusCode == 400 || this?.appMain?.appLoader?.stop?.();
response?.statusCode == 500 || throw err;
response?.statusCode == 401 }
) { };
if (response?.statusCode == 401) {
this.appMain.authStore.token = null;
this.appMain.routerService.goTo("/token-expired");
}
throw response;
}
return response;
} catch (err) {
this?.appMain?.appLoader?.stop?.();
throw err;
}
};
put = async ( put = async (url: string, data: Object, headersParam: HeadersInit): Promise<any> => {
url: string, headersParam = {
data: Object, ...headersParam,
headersParam: HeadersInit Authorization: `BEARER ${this.appMain?.authStore?.token}`,
): Promise<any> => { };
headersParam = { try {
...headersParam, this?.appMain?.appLoader?.start?.();
Authorization: `BEARER ${this.appMain?.authStore?.token}`, const response = await this.httpClient.put(url, data, headersParam);
}; this?.appMain?.appLoader?.stop?.();
try { if (response?.statusCode == 400 || response?.statusCode == 500 || response?.statusCode == 401) {
this?.appMain?.appLoader?.start?.(); if (response?.statusCode == 401) {
const response = await this.httpClient.put(url, data, headersParam); this.appMain.authStore.token = null;
this?.appMain?.appLoader?.stop?.(); this.appMain.routerService.goTo('/token-expired');
if ( }
response?.statusCode == 400 || throw response;
response?.statusCode == 500 || }
response?.statusCode == 401 return response;
) { } catch (err) {
if (response?.statusCode == 401) { this?.appMain?.appLoader?.stop?.();
this.appMain.authStore.token = null; throw err;
this.appMain.routerService.goTo("/token-expired"); }
} };
throw response;
}
return response;
} catch (err) {
this?.appMain?.appLoader?.stop?.();
throw err;
}
};
delete = async ( delete = async (url: string, data: Object, headersParam: HeadersInit): Promise<any> => {
url: string, headersParam = {
data: Object, ...headersParam,
headersParam: HeadersInit Authorization: `BEARER ${this.appMain?.authStore?.token}`,
): Promise<any> => { };
headersParam = { try {
...headersParam, this?.appMain?.appLoader?.start?.();
Authorization: `BEARER ${this.appMain?.authStore?.token}`, const response = await this.httpClient.delete(url, data, headersParam);
}; this?.appMain?.appLoader?.stop?.();
try { if (response?.statusCode == 400 || response?.statusCode == 500 || response?.statusCode == 401) {
this?.appMain?.appLoader?.start?.(); if (response?.statusCode == 401) {
const response = await this.httpClient.delete( this.appMain.authStore.token = null;
url, this.appMain.routerService.goTo('/token-expired');
data, }
headersParam throw response;
); }
this?.appMain?.appLoader?.stop?.(); return response;
if ( } catch (err) {
response?.statusCode == 400 || this?.appMain?.appLoader?.stop?.();
response?.statusCode == 500 || throw err;
response?.statusCode == 401 }
) { };
if (response?.statusCode == 401) {
this.appMain.authStore.token = null;
this.appMain.routerService.goTo("/token-expired");
}
throw response;
}
return response;
} catch (err) {
this?.appMain?.appLoader?.stop?.();
throw err;
}
};
get = async ( get = async (url: string, params: Object, headersParam: HeadersInit): Promise<any> => {
url: string, headersParam = {
params: Object, ...headersParam,
headersParam: HeadersInit Authorization: `BEARER ${this.appMain?.authStore?.token}`,
): Promise<any> => { };
headersParam = { try {
...headersParam, this?.appMain?.appLoader?.start?.();
Authorization: `BEARER ${this.appMain?.authStore?.token}`, const response = await this.httpClient.get(url, params, headersParam);
}; this?.appMain?.appLoader?.stop?.();
try { if (response?.statusCode == 400 || response?.statusCode == 500 || response?.statusCode == 401) {
this?.appMain?.appLoader?.start?.(); if (response?.statusCode == 401) {
const response = await this.httpClient.get( this.appMain.routerService.goTo('/token-expired');
url, this.appMain.authStore.token = null;
params, }
headersParam throw response;
); }
this?.appMain?.appLoader?.stop?.(); return response;
if ( } catch (err) {
response?.statusCode == 400 || this?.appMain?.appLoader?.stop?.();
response?.statusCode == 500 || throw err;
response?.statusCode == 401 }
) { };
if (response?.statusCode == 401) {
this.appMain.routerService.goTo("/token-expired");
this.appMain.authStore.token = null;
}
throw response;
}
return response;
} catch (err) {
this?.appMain?.appLoader?.stop?.();
throw err;
}
};
} }
export default AppService; export default AppService;

View File

@@ -1,27 +1,27 @@
import { AppService } from "core/services"; import { AppService } from 'core/services';
class BaseService { class BaseService {
constructor(public endpoint: string, public appService: AppService) {} constructor(public endpoint: string, public appService: AppService) {}
getAll = (params?: Object, headers?: HeadersInit) => { getAll = (params?: Object, headers?: HeadersInit) => {
return this.appService.get(this.endpoint, params, headers); return this.appService.get(this.endpoint, params, headers);
}; };
get = (params?: Object, headers?: HeadersInit) => { get = (params?: Object, headers?: HeadersInit) => {
return this.appService.get(this.endpoint, params, headers); return this.appService.get(this.endpoint, params, headers);
}; };
put = (data?: Object, headers?: HeadersInit) => { put = (data?: Object, headers?: HeadersInit) => {
return this.appService.put(this.endpoint, data, headers); return this.appService.put(this.endpoint, data, headers);
}; };
post = (data?: Object, headers?: HeadersInit) => { post = (data?: Object, headers?: HeadersInit) => {
return this.appService.post(this.endpoint, data, headers); return this.appService.post(this.endpoint, data, headers);
}; };
delete = (data?: Object, headers?: HeadersInit) => { delete = (data?: Object, headers?: HeadersInit) => {
return this.appService.delete(this.endpoint, data, headers); return this.appService.delete(this.endpoint, data, headers);
}; };
} }
export default BaseService; export default BaseService;

View File

@@ -1,124 +1,119 @@
class HttpClient { class HttpClient {
private url: string; private url: string;
constructor() { constructor() {
this.url = `${__CONFIG__.ssl ? "https" : "http"}://${ this.url = `${__CONFIG__.ssl ? 'https' : 'http'}://${__CONFIG__.apiUrl}/${__CONFIG__.apiVersion}`;
__CONFIG__.apiUrl }
}/${__CONFIG__.apiVersion}`;
}
post(url: string, data: Object, headersParam: HeadersInit): Promise<any> { post(url: string, data: Object, headersParam: HeadersInit): Promise<any> {
let headers: Headers = new Headers(headersParam); let headers: Headers = new Headers(headersParam);
let body: BodyType = null; let body: BodyType = null;
if (data instanceof FormData) { if (data instanceof FormData) {
body = data; body = data;
} else { } else {
body = JSON.stringify(data); body = JSON.stringify(data);
headers.append("Content-Type", "application/json"); headers.append('Content-Type', 'application/json');
} }
let options: OptionsType = { let options: OptionsType = {
method: "POST", method: 'POST',
headers: headers, headers: headers,
body: body, body: body,
}; };
const req: Request = new Request(resolveUrl(this.url, url), options); const req: Request = new Request(resolveUrl(this.url, url), options);
return createRequest(req); return createRequest(req);
} }
put(url: string, data: Object, headersParam: HeadersInit): Promise<any> { put(url: string, data: Object, headersParam: HeadersInit): Promise<any> {
let headers: Headers = new Headers(headersParam); let headers: Headers = new Headers(headersParam);
headers.append("Content-Type", "application/json"); headers.append('Content-Type', 'application/json');
let options: OptionsType = { let options: OptionsType = {
method: "PUT", method: 'PUT',
headers: headers, headers: headers,
body: JSON.stringify(data), body: JSON.stringify(data),
}; };
const req: Request = new Request(resolveUrl(this.url, url), options); const req: Request = new Request(resolveUrl(this.url, url), options);
return createRequest(req); return createRequest(req);
} }
delete(url: string, data: Object, headersParam: HeadersInit): Promise<any> { delete(url: string, data: Object, headersParam: HeadersInit): Promise<any> {
let headers: Headers = new Headers(headersParam); let headers: Headers = new Headers(headersParam);
headers.append("Content-Type", "application/json"); headers.append('Content-Type', 'application/json');
let options: OptionsType = { let options: OptionsType = {
method: "DELETE", method: 'DELETE',
headers: headers, headers: headers,
body: JSON.stringify(data), body: JSON.stringify(data),
}; };
const req: Request = new Request(resolveUrl(this.url, url), options); const req: Request = new Request(resolveUrl(this.url, url), options);
return createRequest(req); return createRequest(req);
} }
get(url: string, params: Object, headersParam: HeadersInit): Promise<any> { get(url: string, params: Object, headersParam: HeadersInit): Promise<any> {
let headers: Headers = new Headers(headersParam); let headers: Headers = new Headers(headersParam);
let options: OptionsType = { let options: OptionsType = {
method: "GET", method: 'GET',
headers: headers, headers: headers,
}; };
let paramsPath: string = ""; let paramsPath: string = '';
if (params) { if (params) {
let urlParams = new URLSearchParams(Object.entries(params)); let urlParams = new URLSearchParams(Object.entries(params));
paramsPath = "?" + urlParams; paramsPath = '?' + urlParams;
} }
const req: Request = new Request( const req: Request = new Request(resolveUrl(this.url, url + paramsPath), options);
resolveUrl(this.url, url + paramsPath),
options
);
return createRequest(req); return createRequest(req);
} }
} }
export default HttpClient; export default HttpClient;
async function createRequest(request: Request): Promise<Response> { async function createRequest(request: Request): Promise<Response> {
let response: Response = await fetch(request); let response: Response = await fetch(request);
if ( if (
!response.ok && !response.ok &&
response.status !== 403 && response.status !== 403 &&
response.status !== 400 && response.status !== 400 &&
response.status !== 401 && response.status !== 401 &&
response.status !== 500 response.status !== 500
) { ) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} else { } else {
if (response.headers.get("Content-Type") !== null) { if (response.headers.get('Content-Type') !== null) {
let newResponse: Response = await createResponse(response); let newResponse: Response = await createResponse(response);
return newResponse; return newResponse;
} }
return response; return response;
} }
} }
async function createResponse(response: Response): Promise<any> { async function createResponse(response: Response): Promise<any> {
const type: string = response.headers.get("Content-Type"); const type: string = response.headers.get('Content-Type');
const body = (): Promise<any> => { const body = (): Promise<any> => {
if (type.indexOf("application/json") !== -1) { if (type.indexOf('application/json') !== -1) {
return response.json(); return response.json();
} }
return response.text(); return response.text();
}; };
return body(); return body();
} }
function resolveUrl(url: string, path: string): string { function resolveUrl(url: string, path: string): string {
if (path.includes("http") || path.includes("://")) { if (path.includes('http') || path.includes('://')) {
return path; return path;
} }
const fixedPath = path const fixedPath = path
.split("/") .split('/')
.filter((i) => i) .filter((i) => i)
.join("/"); .join('/');
const urlWithPath = `${url.endsWith("/") ? url : `${url}/`}${fixedPath}`; const urlWithPath = `${url.endsWith('/') ? url : `${url}/`}${fixedPath}`;
return urlWithPath; return urlWithPath;
} }
type BodyType = string | FormData; type BodyType = string | FormData;
type OptionsType = { type OptionsType = {
method: string; method: string;
headers?: Headers; headers?: Headers;
body?: BodyType; body?: BodyType;
}; };

View File

@@ -1,4 +1,4 @@
export { default as HttpClient } from "./http-service/HttpClient"; export { default as HttpClient } from './http-service/HttpClient';
export { default as BaseService } from "./base-service/BaseService"; export { default as BaseService } from './base-service/BaseService';
export { default as RouterService } from "./router-service/RouterService"; export { default as RouterService } from './router-service/RouterService';
export { default as AppService } from "./app-service/AppService"; export { default as AppService } from './app-service/AppService';

View File

@@ -1,344 +1,284 @@
import { BaseLayoutElement } from "common/layouts"; import { BaseLayoutElement } from 'common/layouts';
import { AppMainElement } from "components/"; import { AppMainElement } from 'components/';
class RouterService { class RouterService {
private historyStack: Array<RouteState> = []; private historyStack: Array<RouteState> = [];
private _routes: Array<RouteState> = []; private _routes: Array<RouteState> = [];
constructor( constructor(private appMain: AppMainElement, private mainRoot: ShadowRoot | HTMLElement) {}
private appMain: AppMainElement,
private mainRoot: ShadowRoot | HTMLElement
) {}
get routerState(): RouteState { get routerState(): RouteState {
const historyLen = this.historyStack?.length; const historyLen = this.historyStack?.length;
if (historyLen < 1) { if (historyLen < 1) {
return null; return null;
} }
return this.historyStack[historyLen - 1]; return this.historyStack[historyLen - 1];
} }
get emptyState(): boolean { get emptyState(): boolean {
const historyLen = this.historyStack?.length; const historyLen = this.historyStack?.length;
if (historyLen < 2) { if (historyLen < 2) {
return true; return true;
} else { } else {
return false; return false;
} }
} }
public setRoutes = (routes: Array<any>): void => { public setRoutes = (routes: Array<any>): void => {
if (!Array.isArray(this._routes)) this._routes = []; if (!Array.isArray(this._routes)) this._routes = [];
routes.forEach((route) => { routes.forEach((route) => {
const { path, component, data, layout, middleware, children } = const { path, component, data, layout, middleware, children } = route;
route; const _pathArr = path?.split?.('/').filter((a) => a);
const _pathArr = path?.split?.("/").filter((a) => a); let newPath = ['', ..._pathArr].join('/');
let newPath = ["", ..._pathArr].join("/"); if (newPath == '') newPath = '/';
if (newPath == "") newPath = "/"; const _routeState: RouteState = new RouteState(newPath, component, data, layout, middleware);
const _routeState: RouteState = new RouteState( if (Array.isArray(children) && children?.length > 0) {
newPath, children.forEach((child) => {
component, const _childState: RouteState = this.createChildState(child, route);
data, this._routes?.push(_childState);
layout, });
middleware }
); this._routes?.push(_routeState);
if (Array.isArray(children) && children?.length > 0) { });
children.forEach((child) => { };
const _childState: RouteState = this.createChildState(
child,
route
);
this._routes?.push(_childState);
});
}
this._routes?.push(_routeState);
});
};
public update = (): void => { public update = (): void => {
if (!this._routes) return; if (!this._routes) return;
const path = window.location.pathname; const path = window.location.pathname;
const [hasDynamic, _dynamicIndex] = this.hasDynamicPath(path); const [hasDynamic, _dynamicIndex] = this.hasDynamicPath(path);
const _mainRoot = this.mainRoot; const _mainRoot = this.mainRoot;
let route: RouteState = this.routerState; let route: RouteState = this.routerState;
if (route?.middleware) { if (route?.middleware) {
if ( if (!(typeof route?.middleware == 'function' && route.middleware()) || route.middleware === false) {
!( return this.goTo('/unauthorized');
typeof route?.middleware == "function" && route.middleware() }
) || }
route.middleware === false if (
) { path == route?.path ||
return this.goTo("/unauthorized"); route?.path == '/not-found' ||
} (hasDynamic && this?._routes?.[_dynamicIndex]?.path == route?.path)
} ) {
if ( let changed: boolean = false;
path == route?.path || if (_mainRoot?.childNodes.length > 0) {
route?.path == "/not-found" || _mainRoot?.childNodes?.forEach?.((child: BaseLayoutElement) => {
(hasDynamic && this?._routes?.[_dynamicIndex]?.path == route?.path) if (
) { route.layout &&
let changed: boolean = false; route.layout.toUpperCase() === child.tagName &&
if (_mainRoot?.childNodes.length > 0) { !child.compareTags(route.component.toUpperCase())
_mainRoot?.childNodes?.forEach?.((child: BaseLayoutElement) => { ) {
if ( changed = true;
route.layout && child.setElement(route.component);
route.layout.toUpperCase() === child.tagName && } else if (route.layout && route.layout.toUpperCase() !== child.tagName) {
!child.compareTags(route.component.toUpperCase()) changed = true;
) { const _newElement = document.createElement(route.layout);
changed = true; _newElement.setAttribute('data-target', 'app-root.rootElement');
child.setElement(route.component); _mainRoot.replaceChild(_newElement, child);
} else if ( (_newElement as BaseLayoutElement).setElement(route.component);
route.layout && } else if (!route.layout && child.tagName !== route.component) {
route.layout.toUpperCase() !== child.tagName const _newElement = document.createElement(route.component);
) { _newElement.setAttribute('data-target', 'app-root.rootElement');
changed = true; changed = true;
const _newElement = document.createElement( _mainRoot.replaceChild(_newElement, child);
route.layout }
); });
_newElement.setAttribute( } else {
"data-target", if (route.layout) {
"app-root.rootElement" changed = true;
); const _newElement = document.createElement(route.layout);
_mainRoot.replaceChild(_newElement, child); _newElement.setAttribute('data-target', 'app-root.rootElement');
(_newElement as BaseLayoutElement).setElement( _mainRoot.appendChild(_newElement);
route.component (_newElement as BaseLayoutElement).setElement(route.component);
); } else {
} else if ( const _newElement = document.createElement(route.component);
!route.layout && _newElement.setAttribute('data-target', 'app-root.rootElement');
child.tagName !== route.component changed = true;
) { _mainRoot.appendChild(_newElement);
const _newElement = document.createElement( }
route.component }
); } else {
_newElement.setAttribute( const [isDynamic, _dynamicIndex, dynamicProps] = this.hasDynamicPath(path);
"data-target", let newRoute: RouteState;
"app-root.rootElement" if (isDynamic && _dynamicIndex !== -1) {
); newRoute = this._routes[_dynamicIndex];
changed = true; newRoute.data = dynamicProps;
_mainRoot.replaceChild(_newElement, child); } else {
} newRoute = this.findByPath();
}); }
} else { this.historyStack.push(newRoute);
if (route.layout) { this.update();
changed = true; }
const _newElement = document.createElement(route.layout); this.appMain.dispatchEvent(this.appMain?.domEvents.routechanged);
_newElement.setAttribute( };
"data-target",
"app-root.rootElement"
);
_mainRoot.appendChild(_newElement);
(_newElement as BaseLayoutElement).setElement(
route.component
);
} else {
const _newElement = document.createElement(route.component);
_newElement.setAttribute(
"data-target",
"app-root.rootElement"
);
changed = true;
_mainRoot.appendChild(_newElement);
}
}
} else {
const [isDynamic, _dynamicIndex, dynamicProps] =
this.hasDynamicPath(path);
let newRoute: RouteState;
if (isDynamic && _dynamicIndex !== -1) {
newRoute = this._routes[_dynamicIndex];
newRoute.data = dynamicProps;
} else {
newRoute = this.findByPath();
}
this.historyStack.push(newRoute);
this.update();
}
this.appMain.dispatchEvent(this.appMain?.domEvents.routechanged);
};
public goTo = (path: string, data?: any): void => { public goTo = (path: string, data?: any): void => {
if (!Array.isArray(this.historyStack)) this.historyStack = []; if (!Array.isArray(this.historyStack)) this.historyStack = [];
const currentPath = window.location.pathname; const currentPath = window.location.pathname;
if (path == currentPath) return; if (path == currentPath) return;
if (path.includes(":") && data) { if (path.includes(':') && data) {
path = resolvePath(path, data); path = resolvePath(path, data);
} }
const _index = this._routes.findIndex((route) => route.path === path); const _index = this._routes.findIndex((route) => route.path === path);
const _indexOfEmpty = this._routes.findIndex( const _indexOfEmpty = this._routes.findIndex((route) => route.path === '/not-found');
(route) => route.path === "/not-found" const [isDynamic, _dynamicIndex, dynamicProps] = this.hasDynamicPath(path);
); if (isDynamic) {
const [isDynamic, _dynamicIndex, dynamicProps] = const [isCurrentDynamic, currIndex] = this.hasDynamicPath(currentPath);
this.hasDynamicPath(path); if (path == currentPath) return;
if (isDynamic) { }
const [isCurrentDynamic, currIndex] = let newRoute: RouteState;
this.hasDynamicPath(currentPath); if (isDynamic && _dynamicIndex !== -1) {
if (path == currentPath) return; newRoute = this._routes[_dynamicIndex];
} newRoute.data = dynamicProps;
let newRoute: RouteState; } else if (_index === -1 && _indexOfEmpty !== -1) {
if (isDynamic && _dynamicIndex !== -1) { newRoute = this._routes[_indexOfEmpty];
newRoute = this._routes[_dynamicIndex]; } else if (_index === -1 && _indexOfEmpty === -1) {
newRoute.data = dynamicProps; newRoute = new RouteState('/not-found', 'not-found');
} else if (_index === -1 && _indexOfEmpty !== -1) { } else {
newRoute = this._routes[_indexOfEmpty]; newRoute = this._routes[_index];
} else if (_index === -1 && _indexOfEmpty === -1) { }
newRoute = new RouteState("/not-found", "not-found");
} else {
newRoute = this._routes[_index];
}
this.historyStack.push(newRoute); this.historyStack.push(newRoute);
const url = new URL(window.location.toString()); const url = new URL(window.location.toString());
url.pathname = path; url.pathname = path;
window.history.pushState({}, "", url.toString()); window.history.pushState({}, '', url.toString());
this.update(); this.update();
}; };
public goBack = (): void => { public goBack = (): void => {
if (!Array.isArray(this.historyStack)) this.historyStack = []; if (!Array.isArray(this.historyStack)) this.historyStack = [];
const lenHistory = this.historyStack.length; const lenHistory = this.historyStack.length;
if (this.canGoBack) { if (this.canGoBack) {
const nextRoute = this.historyStack[lenHistory - 2]; const nextRoute = this.historyStack[lenHistory - 2];
const url = new URL(window.location.toString()); const url = new URL(window.location.toString());
url.pathname = nextRoute.path; url.pathname = nextRoute.path;
window.history.pushState({}, "", url.toString()); window.history.pushState({}, '', url.toString());
this.historyStack.pop(); this.historyStack.pop();
} }
this.update(); this.update();
}; };
public get canGoBack(): boolean { public get canGoBack(): boolean {
const lenHistory = this.historyStack.length; const lenHistory = this.historyStack.length;
if (lenHistory > 2) { if (lenHistory > 2) {
return true; return true;
} }
return false; return false;
} }
public init = (): void => { public init = (): void => {
window.addEventListener("popstate", () => { window.addEventListener('popstate', () => {
this.historyStack.pop(); this.historyStack.pop();
this.update(); this.update();
}); });
this.update(); this.update();
}; };
public findByPath = (): RouteState => { public findByPath = (): RouteState => {
const path = window.location.pathname; const path = window.location.pathname;
const _index = this._routes.findIndex((route) => route.path === path); const _index = this._routes.findIndex((route) => route.path === path);
const _indexOfEmpty = this._routes.findIndex( const _indexOfEmpty = this._routes.findIndex((route) => route.path === '/not-found');
(route) => route.path === "/not-found" if (_index === -1 && _indexOfEmpty !== -1) {
); return this._routes[_indexOfEmpty];
if (_index === -1 && _indexOfEmpty !== -1) { } else if (_index === -1 && _indexOfEmpty === -1) {
return this._routes[_indexOfEmpty]; return new RouteState('/not-found', 'not-found');
} else if (_index === -1 && _indexOfEmpty === -1) { }
return new RouteState("/not-found", "not-found"); return this._routes[_index];
} };
return this._routes[_index];
};
public comparePath = (path: string): boolean => { public comparePath = (path: string): boolean => {
if (this.routerState?.fullPath === path) { if (this.routerState?.fullPath === path) {
return true; return true;
} }
return false; return false;
}; };
private createChildState = (child: any, parent: any): RouteState => { private createChildState = (child: any, parent: any): RouteState => {
const { path, middleware, layout, component, data, children } = child; const { path, middleware, layout, component, data, children } = child;
const _pathArr = path?.split?.("/").filter((a) => a); const _pathArr = path?.split?.('/').filter((a) => a);
const _parentArr = parent?.path?.split?.("/").filter((a) => a); const _parentArr = parent?.path?.split?.('/').filter((a) => a);
const newPath = ["", ..._parentArr, ..._pathArr].join("/"); const newPath = ['', ..._parentArr, ..._pathArr].join('/');
const _child = new RouteState( const _child = new RouteState(newPath, component, data, layout, middleware ? middleware : parent?.middleware);
newPath,
component,
data,
layout,
middleware ? middleware : parent?.middleware
);
if (Array.isArray(children) && children?.length > 0) { if (Array.isArray(children) && children?.length > 0) {
children.forEach((child2) => { children.forEach((child2) => {
const _childState: RouteState = this.createChildState( const _childState: RouteState = this.createChildState(child2, _child);
child2, this._routes?.push(_childState);
_child });
); }
this._routes?.push(_childState); return _child;
}); };
}
return _child;
};
private hasDynamicPath = (path: string): [boolean, number, any] => { private hasDynamicPath = (path: string): [boolean, number, any] => {
const _pathArr = path.split("/").filter((a) => a); const _pathArr = path.split('/').filter((a) => a);
let matchedIndex: number = 0; let matchedIndex: number = 0;
let matched: boolean = false; let matched: boolean = false;
let dynamicProps: any = {}; let dynamicProps: any = {};
let shouldSkip = false; let shouldSkip = false;
this._routes.forEach((route, _routeId) => { this._routes.forEach((route, _routeId) => {
if (shouldSkip) { if (shouldSkip) {
return; return;
} }
if (path == route.path) { if (path == route.path) {
matched = path?.includes(":") ? true : false; matched = path?.includes(':') ? true : false;
matchedIndex = _routeId; matchedIndex = _routeId;
shouldSkip = true; shouldSkip = true;
} }
const _routeArr = route.path.split("/").filter((a) => a); const _routeArr = route.path.split('/').filter((a) => a);
if (_pathArr.length === _routeArr.length) { if (_pathArr.length === _routeArr.length) {
let pathMatches: number = 0; let pathMatches: number = 0;
let hasDynamic: boolean = false; let hasDynamic: boolean = false;
_pathArr.forEach((pathr, i) => { _pathArr.forEach((pathr, i) => {
if (pathr == _routeArr[i]) { if (pathr == _routeArr[i]) {
pathMatches++; pathMatches++;
} else if (_routeArr[i].startsWith?.(":")) { } else if (_routeArr[i].startsWith?.(':')) {
pathMatches++; pathMatches++;
hasDynamic = true; hasDynamic = true;
dynamicProps[_routeArr[i].substr(1)] = pathr; dynamicProps[_routeArr[i].substr(1)] = pathr;
} }
}); });
if (pathMatches === _pathArr.length && hasDynamic) { if (pathMatches === _pathArr.length && hasDynamic) {
matchedIndex = _routeId; matchedIndex = _routeId;
matched = true; matched = true;
shouldSkip = true; shouldSkip = true;
} }
} }
}); });
return [matched, matchedIndex, dynamicProps]; return [matched, matchedIndex, dynamicProps];
}; };
} }
class RouteState { class RouteState {
constructor( constructor(
public path: string, public path: string,
public component: string, public component: string,
public data?: any, public data?: any,
public layout?: string, public layout?: string,
public middleware?: any public middleware?: any
) {} ) {}
get fullPath(): string { get fullPath(): string {
return resolvePath(this.path, this.data); return resolvePath(this.path, this.data);
} }
} }
type DynamicProp = { type DynamicProp = {
index: string; index: string;
path: string; path: string;
}; };
function resolvePath(path: string, data: any): string { function resolvePath(path: string, data: any): string {
const _pathArr = path const _pathArr = path
.split("/") .split('/')
.filter((a) => a) .filter((a) => a)
.map((pathPart) => { .map((pathPart) => {
if (pathPart.startsWith(":")) { if (pathPart.startsWith(':')) {
pathPart = data?.[pathPart.substr(1)]; pathPart = data?.[pathPart.substr(1)];
} }
return pathPart; return pathPart;
}); });
let _return = ["", ..._pathArr].join("/"); let _return = ['', ..._pathArr].join('/');
if (_return == "") { if (_return == '') {
_return = "/"; _return = '/';
} }
return _return; return _return;
} }
export default RouterService; export default RouterService;

View File

@@ -1,98 +1,94 @@
import { AppMainElement } from "components/"; import { AppMainElement } from 'components/';
import { AppService } from "core/services"; import { AppService } from 'core/services';
import { isTrue } from "core/utils"; import { isTrue } from 'core/utils';
import { AuthService } from "services/"; import { AuthService } from 'services/';
class AuthStore { class AuthStore {
private _token: string; private _token: string;
private _userDetails: UserDetails; private _userDetails: UserDetails;
private authService: AuthService; private authService: AuthService;
constructor( constructor(private appMain: AppMainElement, private appService: AppService) {
private appMain: AppMainElement, const _token = localStorage.getItem('token');
private appService: AppService if (_token) this.token = _token;
) { this.authService = new AuthService(this.appService);
const _token = localStorage.getItem("token"); this.checkToken(_token);
if (_token) this.token = _token; }
this.authService = new AuthService(this.appService);
this.checkToken(_token);
}
get token(): string { get token(): string {
if (this._token == "null") return null; if (this._token == 'null') return null;
if (this._token == "undefined") return undefined; if (this._token == 'undefined') return undefined;
return this._token; return this._token;
} }
set token(token: string) { set token(token: string) {
const { _token } = this; const { _token } = this;
const _changed = token != _token; const _changed = token != _token;
console.log(token); if (_changed) {
if (_changed) { this._token = token;
this._token = token; localStorage.setItem('token', token);
localStorage.setItem("token", token); this.appMain.dispatchEvent(this.appMain?.domEvents.tokenchange);
this.appMain.dispatchEvent(this.appMain?.domEvents.tokenchange); }
} }
}
get user(): UserDetails { get user(): UserDetails {
return this._userDetails; return this._userDetails;
} }
set user(userDetails: UserDetails) { set user(userDetails: UserDetails) {
this._userDetails = userDetails; this._userDetails = userDetails;
} }
checkToken = async (token: string) => { checkToken = async (token: string) => {
try { try {
if (token && token !== "null") { if (token && token !== 'null') {
const response = await this.authService.checkToken({ token }); const response = await this.authService.checkToken({ token });
if (!(response && response.valid)) { if (!(response && response.valid)) {
this.token = null; this.token = null;
this.appMain.routerService.goTo("/token-expired"); this.appMain.routerService.goTo('/token-expired');
} }
} }
} catch (err) { } catch (err) {
this.token = null; this.token = null;
this.appMain.routerService.goTo("/token-expired"); this.appMain.routerService.goTo('/token-expired');
} }
}; };
userLogin = async (formObject) => { userLogin = async (formObject) => {
try { try {
const response = await this.authService.login(formObject); const response = await this.authService.login(formObject);
if (response?.token) { if (response?.token) {
this.token = response.token; this.token = response.token;
} else { } else {
this.token = null; this.token = null;
localStorage.removeItem("token"); localStorage.removeItem('token');
} }
return response; return response;
} catch (err) { } catch (err) {
throw err; throw err;
} }
}; };
userRegister = async (formObject) => { userRegister = async (formObject) => {
try { try {
const response = await this.authService.register(formObject); const response = await this.authService.register(formObject);
return response; return response;
} catch (err) { } catch (err) {
throw err; throw err;
} }
}; };
userLogout = (): void => { userLogout = (): void => {
this.token = null; this.token = null;
localStorage.removeItem("token"); localStorage.removeItem('token');
}; };
} }
export default AuthStore; export default AuthStore;
export type UserDetails = { export type UserDetails = {
id: string; id: string;
username: string; username: string;
email: string; email: string;
dateCreated: string; dateCreated: string;
dateUpdated: string; dateUpdated: string;
}; };

View File

@@ -1 +1 @@
export { default as AuthStore } from "./AuthStore"; export { default as AuthStore } from './AuthStore';

View File

@@ -1,15 +1,15 @@
import { toKebabCase } from "core/utils"; import { toKebabCase } from 'core/utils';
export default function closest(proto: Object, key: string): any { export default function closest(proto: Object, key: string): any {
const kebab: string = toKebabCase(key); const kebab: string = toKebabCase(key);
return Object.defineProperty(proto, key, { return Object.defineProperty(proto, key, {
configurable: true, configurable: true,
get() { get() {
return findClosest(this, kebab); return findClosest(this, kebab);
}, },
}); });
} }
function findClosest(element: HTMLElement, key: string): HTMLElement { function findClosest(element: HTMLElement, key: string): HTMLElement {
return element.closest(key); return element.closest(key);
} }

View File

@@ -1,18 +1,15 @@
import { AppMainElement } from "components/"; import { AppMainElement } from 'components/';
export default function findMethod( export default function findMethod(actionString: string, appMain: AppMainElement): Function {
actionString: string, if (actionString && appMain) {
appMain: AppMainElement const methodSep = actionString.lastIndexOf('#');
): Function { const tag = actionString.slice(0, methodSep);
if (actionString) { const method = actionString.slice(methodSep + 1);
const methodSep = actionString.lastIndexOf("#");
const tag = actionString.slice(0, methodSep);
const method = actionString.slice(methodSep + 1);
const element = appMain.querySelector(tag); const element = appMain.querySelector(tag);
if (element) { if (element) {
return element?.[method]; return element?.[method];
} }
} }
return () => {}; return () => {};
} }

View File

@@ -1,4 +1,4 @@
export default function firstUpper(s: string): string { export default function firstUpper(s: string): string {
if (typeof s !== "string") return ""; if (typeof s !== 'string') return '';
return s.charAt(0).toUpperCase() + s.slice(1); return s.charAt(0).toUpperCase() + s.slice(1);
} }

View File

@@ -1,8 +1,8 @@
export default function index(proto: Object, key: string): Object { export default function index(proto: Object, key: string): Object {
return Object.defineProperty(proto, key, { return Object.defineProperty(proto, key, {
configurable: true, configurable: true,
get() { get() {
return Array.from(this.parentNode.children).indexOf(this); return Array.from(this.parentNode.children).indexOf(this);
}, },
}); });
} }

View File

@@ -1,9 +1,13 @@
export { default as toKebabCase } from "./toKebabCase"; export * from './library';
export { default as update } from "./update-deco"; export * from './templating';
export { default as index } from "./index-deco";
export { default as closest } from "./closest-deco"; export { default as toKebabCase } from './toKebabCase';
export { default as isTrue } from "./isTrue"; export { default as update } from './update-deco';
export { default as firstUpper } from "./first-upper"; export { default as index } from './index-deco';
export { default as query } from "./query-deco"; export { default as closest } from './closest-deco';
export { default as querys } from "./querys-deco"; export { default as isTrue } from './isTrue';
export { default as findMethod } from "./find-method"; export { default as firstUpper } from './first-upper';
export { default as query } from './query-deco';
export { default as querys } from './querys-deco';
export { default as findMethod } from './find-method';
export { default as validator } from './validator';

View File

@@ -1,3 +1,3 @@
export default function isTrue(text: string): boolean { export default function isTrue(text: string): boolean {
return text === "true"; return text === 'true';
} }

View File

@@ -0,0 +1,3 @@
import { attr, controller, target, targets} from '@github/catalyst';
export { attr, controller, target, targets }

View File

@@ -1,15 +1,15 @@
import { toKebabCase } from "core/utils"; import { toKebabCase } from 'core/utils';
export default function query(proto: Object, key: string): any { export default function query(proto: Object, key: string): any {
const kebab: string = toKebabCase(key); const kebab: string = toKebabCase(key);
return Object.defineProperty(proto, key, { return Object.defineProperty(proto, key, {
configurable: true, configurable: true,
get() { get() {
return findQuery(this, kebab); return findQuery(this, kebab);
}, },
}); });
} }
function findQuery(element: HTMLElement, key: string): HTMLElement { function findQuery(element: HTMLElement, key: string): HTMLElement {
return element.querySelector(key); return element.querySelector(key);
} }

View File

@@ -1,18 +1,15 @@
import { toKebabCase } from "core/utils"; import { toKebabCase } from 'core/utils';
export default function querys(proto: Object, key: string): any { export default function querys(proto: Object, key: string): any {
const kebab: string = toKebabCase(key); const kebab: string = toKebabCase(key);
return Object.defineProperty(proto, key, { return Object.defineProperty(proto, key, {
configurable: true, configurable: true,
get() { get() {
return findQuerys(this, kebab); return findQuerys(this, kebab);
}, },
}); });
} }
function findQuerys( function findQuerys(element: HTMLElement, key: string): NodeListOf<HTMLElement> {
element: HTMLElement, return element.querySelectorAll(key);
key: string
): NodeListOf<HTMLElement> {
return element.querySelectorAll(key);
} }

View File

@@ -1,3 +1,3 @@
export default function randomId(): string { export default function randomId(): string {
return "_" + Math.random().toString(36).substr(2, 5); return '_' + Math.random().toString(36).substr(2, 5);
} }

View File

@@ -0,0 +1,3 @@
import { render, html, TemplateResult } from 'lit-html';
export { render, html, TemplateResult };

View File

@@ -1,6 +1,6 @@
export default function toKebabCase(text: string): string { export default function toKebabCase(text: string): string {
return text return text
.replace(/([a-z])([A-Z])/g, "$1-$2") .replace(/([a-z])([A-Z])/g, '$1-$2')
.replace(/\s+/g, "-") .replace(/\s+/g, '-')
.toLowerCase(); .toLowerCase();
} }

View File

@@ -1,8 +1,8 @@
export default function update(proto: any, key?: string, dir?: any): any { export default function update(proto: any, key?: string, dir?: any): any {
const method: Function = dir.value!; const method: Function = dir.value!;
dir.value = function () { dir.value = function () {
const _return = method.apply(this, arguments); const _return = method.apply(this, arguments);
if (proto.update) proto.update.call(this); if (proto.update) proto.update.call(this);
return _return; return _return;
}; };
} }

View File

@@ -0,0 +1,25 @@
import isEmail from 'validator/lib/isEmail';
import isDate from 'validator/lib/isDate';
import isNumeric from 'validator/lib/isNumeric';
import matches from 'validator/lib/matches';
const validator = {
is_email: [isEmail, '{- name} needs to be email format.'],
is_date: isDate,
is_numeric: isNumeric,
matches: matches,
is_same: [isSame, '{- name} needs to be same to {- field}.'],
required: [required, '{- name} is required.'],
};
function required(str: string): boolean {
if (!str || str == '') return false;
return true;
}
function isSame(str: string, field: string): boolean {
if (str === field) return true;
return false;
}
export default validator;

View File

@@ -1,21 +1,21 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Wallet Web</title> <title>Wallet Web</title>
</head> </head>
<body> <body>
<style> <style>
body { body {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
padding: 0; padding: 0;
background-color: #303030; background-color: #303030;
color: #ffffff; color: #ffffff;
} }
</style> </style>
<app-shadow></app-shadow> <app-shadow></app-shadow>
</body> </body>
</html> </html>

View File

@@ -1,3 +1,3 @@
import "layouts"; import 'layouts';
import "components"; import 'components';
import "pages"; import 'pages';

View File

@@ -1,2 +1,2 @@
export * from "./menu-layout/MenuLayoutElement"; export * from './menu-layout/MenuLayoutElement';
export * from "./initial-layout/InitialLayoutElement"; export * from './initial-layout/InitialLayoutElement';

View File

@@ -1,31 +1,31 @@
import { controller, target } from "@github/catalyst"; import { controller, target } from '@github/catalyst';
import { closest } from "core/utils"; import { closest } from 'core/utils';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { BaseLayoutElement } from "common/layouts"; import { BaseLayoutElement } from 'common/layouts';
import { AppMainElement } from "components/"; import { AppMainElement } from 'components/';
@controller @controller
class InitialLayoutElement extends BaseLayoutElement { class InitialLayoutElement extends BaseLayoutElement {
@closest appMain: AppMainElement; @closest appMain: AppMainElement;
@target appPage: HTMLDivElement; @target appPage: HTMLDivElement;
constructor() { constructor() {
super(); super();
} }
elementConnected = (): void => { elementConnected = (): void => {
this.update(); this.update();
}; };
elementDisconnected = (appMain: AppMainElement): void => {}; elementDisconnected = (appMain: AppMainElement): void => {};
render = (): TemplateResult => { render = (): TemplateResult => {
return html` return html`
<div data-target="initial-layout.appPage"> <div data-target="initial-layout.appPage">
<app-slot data-target="initial-layout.appSlot"></app-slot> <app-slot data-target="initial-layout.appSlot"></app-slot>
</div> </div>
`; `;
}; };
} }
export type { InitialLayoutElement }; export type { InitialLayoutElement };

View File

@@ -1,50 +1,50 @@
import { controller, target } from "@github/catalyst"; import { controller, target } from '@github/catalyst';
import { closest } from "core/utils"; import { closest } from 'core/utils';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { BaseLayoutElement } from "common/layouts"; import { BaseLayoutElement } from 'common/layouts';
import { AppMainElement } from "components/"; import { AppMainElement } from 'components/';
@controller @controller
class MenuLayoutElement extends BaseLayoutElement { class MenuLayoutElement extends BaseLayoutElement {
@closest appMain: AppMainElement; @closest appMain: AppMainElement;
@target appPage: HTMLDivElement; @target appPage: HTMLDivElement;
constructor() { constructor() {
super(); super();
} }
elementConnected = (): void => { elementConnected = (): void => {
this.update(); this.update();
this.appMain.addEventListener("tokenchange", this.updateAuth); this.appMain.addEventListener('tokenchange', this.updateAuth);
this.appMain.addEventListener("routechanged", this.updateAuth); this.appMain.addEventListener('routechanged', this.updateAuth);
}; };
elementDisconnected = (appMain: AppMainElement): void => { elementDisconnected = (appMain: AppMainElement): void => {
appMain?.removeEventListener("tokenchange", this.updateAuth); appMain?.removeEventListener('tokenchange', this.updateAuth);
appMain?.removeEventListener("routechanged", this.updateAuth); appMain?.removeEventListener('routechanged', this.updateAuth);
}; };
get isAuth(): boolean { get isAuth(): boolean {
const _is = this.appMain?.routerService?.routerState?.middleware; const _is = this.appMain?.routerService?.routerState?.middleware;
if (typeof _is == "function") { if (typeof _is == 'function') {
return _is(); return _is();
} }
return !!_is; return !!_is;
} }
updateAuth = (): void => { updateAuth = (): void => {
this.update(); this.update();
}; };
render = (): TemplateResult => { render = (): TemplateResult => {
const _isAuth = this.isAuth; const _isAuth = this.isAuth;
return html` return html`
<div data-target="menu-layout.appPage"> <div data-target="menu-layout.appPage">
${_isAuth ? html`<app-menu></app-menu>` : html``} ${_isAuth ? html`<app-menu></app-menu>` : html``}
<app-slot data-target="menu-layout.appSlot"></app-slot> <app-slot data-target="menu-layout.appSlot"></app-slot>
</div> </div>
`; `;
}; };
} }
export type { MenuLayoutElement }; export type { MenuLayoutElement };

View File

@@ -1,63 +1,57 @@
import { controller, target } from "@github/catalyst"; import { controller, target } from '@github/catalyst';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { TransactionsService } from "services/"; import { TransactionsService } from 'services/';
import { AppMainElement, AppPaginationElement } from "components/"; import { AppMainElement, AppPaginationElement } from 'components/';
import { BasePageElement } from "common/"; import { BasePageElement } from 'common/';
@controller @controller
class HistoryPageElement extends BasePageElement { class HistoryPageElement extends BasePageElement {
private transactionsService: TransactionsService; private transactionsService: TransactionsService;
@target pagination: AppPaginationElement; @target pagination: AppPaginationElement;
constructor() { constructor() {
super({ super({
title: "Transaction History", title: 'Transaction History',
}); });
} }
elementConnected = (): void => { elementConnected = (): void => {
this.transactionsService = new TransactionsService( this.transactionsService = new TransactionsService(this.appMain?.appService);
this.appMain?.appService this.update();
); this.pagination?.setFetchFunc?.(this.getTransactions, true)!;
this.update(); this.appMain.addEventListener('tokenchange', this.update);
this.pagination?.setFetchFunc?.(this.getTransactions, true)!; };
this.appMain.addEventListener("tokenchange", this.update);
};
elementDisconnected = (appMain: AppMainElement): void => { elementDisconnected = (appMain: AppMainElement): void => {
appMain?.removeEventListener("tokenchange", this.update); appMain?.removeEventListener('tokenchange', this.update);
}; };
getTransactions = async (options): Promise<any> => { getTransactions = async (options): Promise<any> => {
try { try {
if (this?.routerService?.routerState?.data) { if (this?.routerService?.routerState?.data) {
const { walletId } = this?.routerService?.routerState?.data; const { walletId } = this?.routerService?.routerState?.data;
if (walletId) { if (walletId) {
options["walletId"] = walletId; options['walletId'] = walletId;
} }
} }
const response = await this.transactionsService.getAll(options); const response = await this.transactionsService.getAll(options);
return response; return response;
} catch (err) { } catch (err) {
throw err; throw err;
} }
}; };
render = (): TemplateResult => { render = (): TemplateResult => {
const renderWallet = () => { const renderWallet = () => {
if (this.routerService?.routerState?.data?.walletId) { if (this.routerService?.routerState?.data?.walletId) {
return html`<span return html`<span>${this.routerService?.routerState?.data?.walletId}</span>`;
>${this.routerService?.routerState?.data?.walletId}</span }
>`; return html``;
} };
return html``; return html`<div>
}; ${renderWallet()}
return html`<div> <app-pagination data-target="history-page.pagination"></app-pagination>
${renderWallet()} </div>`;
<app-pagination };
data-target="history-page.pagination"
></app-pagination>
</div>`;
};
} }
export type { HistoryPageElement }; export type { HistoryPageElement };

View File

@@ -1,69 +1,69 @@
import { controller, target } from "@github/catalyst"; import { controller, target } from '@github/catalyst';
import { html, TemplateResult, until } from "@github/jtml"; import { html, TemplateResult, until } from 'core/utils';
import { WalletService } from "services/"; import { WalletService } from 'services/';
import { AppMainElement, WalletHeaderElement } from "components/"; import { AppMainElement, WalletHeaderElement } from 'components/';
import { BasePageElement } from "common/"; import { BasePageElement } from 'common/';
@controller @controller
class HomePageElement extends BasePageElement { class HomePageElement extends BasePageElement {
@target walletHeader: WalletHeaderElement; @target walletHeader: WalletHeaderElement;
private walletService: WalletService; private walletService: WalletService;
constructor() { constructor() {
super({ super({
title: "Home", title: 'Home',
}); });
} }
elementConnected = (): void => { elementConnected = (): void => {
this.walletService = new WalletService(this.appMain?.appService); this.walletService = new WalletService(this.appMain?.appService);
this.update(); this.update();
this.appMain.addEventListener("tokenchange", this.update); this.appMain.addEventListener('tokenchange', this.update);
this.getBalance(); this.getBalance();
}; };
elementDisconnected = (appMain: AppMainElement): void => { elementDisconnected = (appMain: AppMainElement): void => {
appMain?.removeEventListener("tokenchange", this.update); appMain?.removeEventListener('tokenchange', this.update);
}; };
getBalance = async (): Promise<void> => { getBalance = async (): Promise<void> => {
try { try {
const response = await this.walletService.getBalance(); const response = await this.walletService.getBalance();
this.setBalance(response); this.setBalance(response);
} catch (err) { } catch (err) {
throw err; throw err;
} }
}; };
setBalance = (header) => { setBalance = (header) => {
if (!this.walletHeader) return; if (!this.walletHeader) return;
this.walletHeader.currency = header.currency; this.walletHeader.currency = header.currency;
this.walletHeader.currentBalance = header.currentBalance || "0"; this.walletHeader.currentBalance = header.currentBalance || '0';
this.walletHeader.lastMonth = header.lastMonth || "0"; this.walletHeader.lastMonth = header.lastMonth || '0';
this.walletHeader.nextMonth = header.nextMonth || "0"; this.walletHeader.nextMonth = header.nextMonth || '0';
}; };
openModal = (): void => { openModal = (): void => {
const _modal = this.appMain.appModal; const _modal = this.appMain.appModal;
if (_modal) { if (_modal) {
this.appMain.closeModal(); this.appMain.closeModal();
} else { } else {
this.appMain.createModal("wallet-create"); this.appMain.createModal('wallet-create');
} }
}; };
render = (): TemplateResult => { render = (): TemplateResult => {
return html` return html`
<button app-action="click:home-page#openModal">New Wallet</button> <button app-action="click:home-page#openModal">New Wallet</button>
<wallet-header <wallet-header
data-target="home-page.walletHeader" data-target="home-page.walletHeader"
data-current-balance="0" data-current-balance="0"
data-last-month="0" data-last-month="0"
data-next-month="0" data-next-month="0"
data-currency="0" data-currency="0"
data-custom="home-page#getBalance" data-custom="home-page#getBalance"
></wallet-header> ></wallet-header>
`; `;
}; };
} }
export { HomePageElement }; export { HomePageElement };

View File

@@ -1,10 +1,10 @@
export * from "./logout-page/LogoutPageElement"; export * from './logout-page/LogoutPageElement';
export * from "./home-page/HomePageElement"; export * from './home-page/HomePageElement';
export * from "./register-page/RegisterPageElement"; export * from './register-page/RegisterPageElement';
export * from "./login-page/LoginPageElement"; export * from './login-page/LoginPageElement';
export * from "./not-found/NotFoundElement"; export * from './not-found/NotFoundElement';
export * from "./history-page/HistoryPageElement"; export * from './history-page/HistoryPageElement';
export * from "./wallet-list/WalletListElement"; export * from './wallet-list/WalletListElement';
export * from "./wallet-create/WalletCreateElement"; export * from './wallet-create/WalletCreateElement';
export * from "./transaction-create/TransactionCreateElement"; export * from './transaction-create/TransactionCreateElement';
export * from "./wallet-page/WalletPageElement"; export * from './wallet-page/WalletPageElement';

View File

@@ -1,114 +1,107 @@
import { targets, controller, target } from "@github/catalyst"; import { targets, controller, target } from '@github/catalyst';
import { html, TemplateResult } from "@github/jtml"; //import { html, TemplateResult } from "core/utils";
import { AuthService } from "services/"; import { html, render, TemplateResult } from 'core/utils';
import { AppFormElement, InputFieldElement } from "components/"; import { AuthService } from 'services/';
import { RouterService } from "core/services"; import { AppFormElement, InputFieldElement } from 'components/';
import { BasePageElement } from "common/"; import { RouterService } from 'core/services';
import { BasePageElement } from 'common/';
@controller @controller
class LoginPageElement extends BasePageElement { class LoginPageElement extends BasePageElement {
@targets inputs: Array<InputFieldElement>; @targets inputs: Array<InputFieldElement>;
@target appForm: AppFormElement; @target appForm: AppFormElement;
authService: AuthService; authService: AuthService;
constructor() { constructor() {
super({ super({
title: "Login", title: 'Login',
}); });
} }
elementConnected = (): void => { elementConnected = (): void => {
this.authService = new AuthService(this.appMain.appService); this.authService = new AuthService(this.appMain.appService);
this.update(); this.update();
}; };
get emailInput(): InputFieldElement { get emailInput(): InputFieldElement {
for (const i in this.inputs) { for (const i in this.inputs) {
if (this.inputs[i]?.name == "email") { if (this.inputs[i]?.name == 'email') {
return this.inputs[i]; return this.inputs[i];
} }
} }
} }
get passwordInput(): InputFieldElement { get passwordInput(): InputFieldElement {
for (const i in this.inputs) { for (const i in this.inputs) {
if (this.inputs[i]?.name == "password") { if (this.inputs[i]?.name == 'password') {
return this.inputs[i]; return this.inputs[i];
} }
} }
} }
get values(): Object { get values(): Object {
const formObject: any = {}; const formObject: any = {};
this.inputs.forEach((input: InputFieldElement) => { this.inputs.forEach((input: InputFieldElement) => {
const inputType = input.inp; const inputType = input.inp;
formObject[input.name] = (inputType as HTMLInputElement).value; formObject[input.name] = (inputType as HTMLInputElement).value;
}); });
return formObject; return formObject;
} }
onSubmit = async (): Promise<void> => { onSubmit = async (): Promise<void> => {
try { try {
if (!this.validate()) { if (!this.validate()) {
return; return;
} }
const response = await this.appMain.authStore.userLogin( const response = await this.appMain.authStore.userLogin(this.values);
this.values
);
if (response?.token) { if (response?.token) {
this.routerService.goTo("/"); this.routerService.goTo('/');
} }
} catch (err) { } catch (err) {
if (err?.errorCode == 400103) { if (err?.errorCode == 400103) {
this.emailInput.error = err?.message; this.emailInput.setError(err?.message);
this.emailInput.update(); this.emailInput.update();
} else if (err?.errorCode == 400104) { } else if (err?.errorCode == 400104) {
this.passwordInput.error = err?.message; this.passwordInput.setError(err?.message);
this.passwordInput.update(); this.passwordInput.update();
} else { } else {
this.appForm?.setError("Unable to log in!"); this.appForm?.setError('Unable to log in!');
} }
} }
}; };
validate(): boolean { validate(): boolean {
let _return = true; let _return = true;
this.inputs.forEach((input: InputFieldElement) => { this.inputs.forEach((input: InputFieldElement) => {
const valid: boolean = input.validate(); const valid: boolean = input.validate();
if (!valid) _return = false; if (!valid) _return = false;
}); });
return _return; return _return;
} }
render = (): TemplateResult => { render = (): TemplateResult => {
return html` return html`
<app-form <app-form data-custom="login-page#onSubmit" data-target="login-page.appForm">
data-custom="login-page#onSubmit" <input-field
data-target="login-page.appForm" data-type="email"
> data-name="email"
<input-field data-label="E-mail"
data-type="email" data-targets="login-page.inputs"
data-name="email" data-rules="required|is_email"
data-label="E-mail" ></input-field>
data-targets="login-page.inputs" <input-field
data-rules="required|isEmail" data-type="password"
></input-field> data-name="password"
<input-field data-label="Password"
data-type="password" data-targets="login-page.inputs"
data-name="password" data-rules="required"
data-label="Password" >
data-targets="login-page.inputs" </input-field>
data-rules="required" </app-form>
> <div>
</input-field> <app-link data-to="/register" data-title="Create new account"></app-link>
</app-form> </div>
<div> `;
<app-link };
data-to="/register"
data-title="Create new account"
></app-link>
</div>
`;
};
} }
export type { LoginPageElement }; export type { LoginPageElement };

View File

@@ -1,20 +1,20 @@
import { controller } from "@github/catalyst"; import { controller } from '@github/catalyst';
import { AuthService } from "services/"; import { AuthService } from 'services/';
import { BasePageElement } from "common/"; import { BasePageElement } from 'common/';
@controller @controller
class LogoutPageElement extends BasePageElement { class LogoutPageElement extends BasePageElement {
authService: AuthService; authService: AuthService;
constructor() { constructor() {
super({ super({
title: "Logout", title: 'Logout',
}); });
} }
elementConnected = (): void => { elementConnected = (): void => {
this.authService = new AuthService(this.appMain.appService); this.authService = new AuthService(this.appMain.appService);
this.appMain?.authStore?.userLogout(); this.appMain?.authStore?.userLogout();
this.appMain?.routerService.goTo("/login"); this.appMain?.routerService.goTo('/login');
}; };
} }
export type { LogoutPageElement }; export type { LogoutPageElement };

View File

@@ -1,24 +1,24 @@
import { controller } from "@github/catalyst"; import { controller } from '@github/catalyst';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { BasePageElement } from "common/"; import { BasePageElement } from 'common/';
@controller @controller
class NotFoundElement extends BasePageElement { class NotFoundElement extends BasePageElement {
constructor() { constructor() {
super({ super({
title: "404 - Not Found", title: '404 - Not Found',
}); });
} }
elementConnected = (): void => { elementConnected = (): void => {
this.update(); this.update();
}; };
render = (): TemplateResult => { render = (): TemplateResult => {
return html` return html`
<div>404 - Page not found</div> <div>404 - Page not found</div>
<div><app-link data-to="/" data-title="Homepage"></app-link></div> <div><app-link data-to="/" data-title="Homepage"></app-link></div>
`; `;
}; };
} }
export type { NotFoundElement }; export type { NotFoundElement };

View File

@@ -1,87 +1,100 @@
import { targets, controller } from "@github/catalyst"; import { targets, controller, target } from '@github/catalyst';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { AuthService } from "services/"; import { AuthService } from 'services/';
import { InputFieldElement } from "components/"; import { AppFormElement, InputFieldElement } from 'components/';
import { BasePageElement } from "common/"; import { BasePageElement } from 'common/';
@controller @controller
class RegisterPageElement extends BasePageElement { class RegisterPageElement extends BasePageElement {
@targets inputs: Array<InputFieldElement>; @targets inputs: Array<InputFieldElement>;
authService: AuthService; @target appForm: AppFormElement;
constructor() { authService: AuthService;
super({ constructor() {
title: "Register", super({
}); title: 'Register',
} });
elementConnected = (): void => { }
this.authService = new AuthService(this.appMain.appService); elementConnected = (): void => {
this.update(); this.authService = new AuthService(this.appMain.appService);
}; this.update();
};
get values(): Object { get values(): Object {
const formObject = {}; const formObject = {};
this.inputs.forEach((input: InputFieldElement) => { this.inputs.forEach((input: InputFieldElement) => {
const inputType = input.inp; const inputType = input.inp;
formObject[input.name] = (inputType as HTMLInputElement).value; formObject[input.name] = (inputType as HTMLInputElement).value;
}); });
return formObject; return formObject;
} }
onSubmit = async (): Promise<void> => { onSubmit = async (): Promise<void> => {
try { try {
if (!this.validate()) { if (!this.validate()) {
return; return;
} }
const response = await this.appMain.authStore.userRegister( const response = await this.appMain.authStore.userRegister(this.values);
this.values
);
if (response?.id) { if (response?.id) {
this.appMain.routerService.goTo("/login"); this.appMain.routerService.goTo('/login');
} }
} catch (err) {} } catch (err) {
}; if (err?.errorCode == 400103) {
this.appForm?.getInput('email')?.setError(err?.message);
this.appForm?.getInput('email')?.update();
} else if (err?.errorCode == 400104) {
this.appForm?.getInput('password')?.setError(err?.message);
this.appForm?.getInput('password')?.update();
} else {
this.appForm?.setError('Unable to log in!');
}
}
};
validate(): boolean { validate(): boolean {
let _return = true; let _return = true;
this.inputs.forEach((input: InputFieldElement) => { this.inputs.forEach((input: InputFieldElement) => {
const valid: boolean = input.validate(); const valid: boolean = input.validate();
if (!valid) _return = false; if (!valid) _return = false;
}); });
return _return; return _return;
} }
render = (): TemplateResult => { render = (): TemplateResult => {
return html` return html`
<app-form <app-form data-custom="register-page#onSubmit" data-has-cancel="true" data-target="register-page.appForm">
data-custom="register-page#onSubmit" <input-field
data-has-cancel="true" data-type="text"
> data-name="username"
<input-field data-label="Username"
data-type="text" data-targets="register-page.inputs"
data-name="username" data-rules="required"
data-label="Username" ></input-field>
data-targets="register-page.inputs" <input-field
data-rules="required" data-type="email"
></input-field> data-name="email"
<input-field data-label="E-mail"
data-type="email" data-targets="register-page.inputs"
data-name="email" data-rules="required|is_email"
data-label="E-mail" ></input-field>
data-targets="register-page.inputs" <input-field
data-rules="required|isEmail" data-type="password"
></input-field> data-name="password"
<input-field data-label="Password"
data-type="password" data-targets="register-page.inputs"
data-name="password" data-rules="required"
data-label="Password" >
data-targets="register-page.inputs" </input-field>
data-rules="required" <input-field
> data-type="password"
</input-field> data-name="confirmpassword"
</app-form> data-label="Confirm Password"
`; data-targets="register-page.inputs"
}; data-rules="required|is_same[field(password)]"
>
</app-form>
`;
};
} }
export type { RegisterPageElement }; export type { RegisterPageElement };

View File

@@ -1,130 +1,119 @@
import { targets, controller } from "@github/catalyst"; import { targets, controller } from '@github/catalyst';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { AuthService, TransactionsService, WalletService } from "services/"; import { AuthService, TransactionsService, WalletService } from 'services/';
import { InputFieldElement } from "components/"; import { InputFieldElement } from 'components/';
import { RouterService } from "core/services"; import { RouterService } from 'core/services';
import { BasePageElement } from "common/"; import { BasePageElement } from 'common/';
import { AppDropdownElement } from "components/app-dropdown/AppDropdownElement"; import { AppDropdownElement } from 'components/app-dropdown/AppDropdownElement';
@controller @controller
class TransactionCreateElement extends BasePageElement { class TransactionCreateElement extends BasePageElement {
@targets inputs: Array<InputFieldElement | AppDropdownElement>; @targets inputs: Array<InputFieldElement | AppDropdownElement>;
private transactionService: TransactionsService; private transactionService: TransactionsService;
private walletService: WalletService; private walletService: WalletService;
authService: AuthService; authService: AuthService;
errorMessage: string; errorMessage: string;
constructor() { constructor() {
super({ super({
title: "New Transaction", title: 'New Transaction',
}); });
} }
elementConnected = (): void => { elementConnected = (): void => {
this.walletService = new WalletService(this.appMain?.appService); this.walletService = new WalletService(this.appMain?.appService);
this.transactionService = new TransactionsService( this.transactionService = new TransactionsService(this.appMain?.appService);
this.appMain?.appService this.authService = new AuthService(this.appMain.appService);
); this.update();
this.authService = new AuthService(this.appMain.appService); };
this.update();
};
get nameInput(): InputFieldElement | AppDropdownElement { get nameInput(): InputFieldElement | AppDropdownElement {
for (const i in this.inputs) { for (const i in this.inputs) {
if (this.inputs[i]?.name == "name") { if (this.inputs[i]?.name == 'name') {
return this.inputs[i]; return this.inputs[i];
} }
} }
} }
get values(): any { get values(): any {
const formObject: any = {}; const formObject: any = {};
this.inputs.forEach((input: InputFieldElement) => { this.inputs.forEach((input: InputFieldElement) => {
const inputType = input.inp; const inputType = input.inp;
formObject[input.name] = (inputType as HTMLInputElement).value; formObject[input.name] = (inputType as HTMLInputElement).value;
}); });
return formObject; return formObject;
} }
getWallets = async (options): Promise<void> => { getWallets = async (options): Promise<void> => {
try { try {
const response = await this.walletService.getAll(options); const response = await this.walletService.getAll(options);
return response; return response;
} catch (err) {} } catch (err) {}
}; };
onSubmit = async (values): Promise<void> => { onSubmit = async (values): Promise<void> => {
try { try {
if (!this.validate()) { if (!this.validate()) {
return; return;
} }
const { const { description: description, wallet: walletId, amount } = values;
description: description,
wallet: walletId,
amount,
} = values;
const response = await this.transactionService.post({ const response = await this.transactionService.post({
description, description,
walletId, walletId,
amount, amount,
}); });
if (response?.id) { if (response?.id) {
this.appMain.triggerWalletUpdate(); this.appMain.triggerWalletUpdate();
this.routerService.goTo("/history", { this.routerService.goTo('/history', {
walletId: response.id, walletId: response.id,
}); });
} }
} catch (err) { } catch (err) {
this.errorMessage = "Unable to create transaction!"; this.errorMessage = 'Unable to create transaction!';
this.update(); this.update();
} }
}; };
validate(): boolean { validate(): boolean {
let _return = true; let _return = true;
this.inputs.forEach((input: InputFieldElement) => { this.inputs.forEach((input: InputFieldElement) => {
const valid: boolean = input.validate(); const valid: boolean = input.validate();
if (!valid) _return = false; if (!valid) _return = false;
}); });
return _return; return _return;
} }
render = (): TemplateResult => { render = (): TemplateResult => {
return html` return html`
<div>Create wallet</div> <div>Create wallet</div>
<app-form <app-form data-custom="transaction-create#onSubmit" data-has-cancel="true">
data-custom="transaction-create#onSubmit" <input-field
data-has-cancel="true" data-type="number"
> data-name="amount"
<input-field data-label="Amount"
data-type="number" data-targets="transaction-create.inputs"
data-name="amount" data-rules="required"
data-label="Amount" ></input-field>
data-targets="transaction-create.inputs" <input-field
data-rules="required" data-type="text"
></input-field> data-name="description"
<input-field data-label="Description"
data-type="text" data-targets="transaction-create.inputs"
data-name="description" data-rules="required"
data-label="Description" ></input-field>
data-targets="transaction-create.inputs" <app-dropdown
data-rules="required" data-name="wallet"
></input-field> data-label="Wallet"
<app-dropdown data-targets="transaction-create.inputs"
data-name="wallet" data-rules="required"
data-label="Wallet" data-fetch="transaction-create#getWallets"
data-targets="transaction-create.inputs" >
data-rules="required" </app-dropdown>
data-fetch="transaction-create#getWallets" ${this.errorMessage ? html`<div>${this.errorMessage}</div>` : html``}
> </app-form>
</app-dropdown> `;
${this.errorMessage };
? html`<div>${this.errorMessage}</div>`
: html``}
</app-form>
`;
};
} }
export type { TransactionCreateElement }; export type { TransactionCreateElement };

View File

@@ -1,92 +1,87 @@
import { targets, controller } from "@github/catalyst"; import { targets, controller } from '@github/catalyst';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { AuthService, WalletService } from "services/"; import { AuthService, WalletService } from 'services/';
import { InputFieldElement } from "components/"; import { InputFieldElement } from 'components/';
import { RouterService } from "core/services"; import { RouterService } from 'core/services';
import { BasePageElement } from "common/"; import { BasePageElement } from 'common/';
@controller @controller
class WalletCreateElement extends BasePageElement { class WalletCreateElement extends BasePageElement {
@targets inputs: Array<InputFieldElement>; @targets inputs: Array<InputFieldElement>;
private walletService: WalletService; private walletService: WalletService;
authService: AuthService; authService: AuthService;
errorMessage: string; errorMessage: string;
constructor() { constructor() {
super({ super({
title: "New Wallet", title: 'New Wallet',
}); });
} }
elementConnected = (): void => { elementConnected = (): void => {
this.walletService = new WalletService(this.appMain?.appService); this.walletService = new WalletService(this.appMain?.appService);
this.authService = new AuthService(this.appMain.appService); this.authService = new AuthService(this.appMain.appService);
this.update(); this.update();
}; };
get nameInput(): InputFieldElement { get nameInput(): InputFieldElement {
for (const i in this.inputs) { for (const i in this.inputs) {
if (this.inputs[i]?.name == "name") { if (this.inputs[i]?.name == 'name') {
return this.inputs[i]; return this.inputs[i];
} }
} }
} }
get values(): Object { get values(): Object {
const formObject: any = {}; const formObject: any = {};
this.inputs.forEach((input: InputFieldElement) => { this.inputs.forEach((input: InputFieldElement) => {
const inputType = input.inp; const inputType = input.inp;
formObject[input.name] = (inputType as HTMLInputElement).value; formObject[input.name] = (inputType as HTMLInputElement).value;
}); });
return formObject; return formObject;
} }
onSubmit = async (): Promise<void> => { onSubmit = async (): Promise<void> => {
try { try {
if (!this.validate()) { if (!this.validate()) {
return; return;
} }
const response = await this.walletService.post(this.values); const response = await this.walletService.post(this.values);
if (response?.id) { if (response?.id) {
this.appMain.triggerWalletUpdate(); this.appMain.triggerWalletUpdate();
this.routerService.goTo("/wallet/:walletId", { this.routerService.goTo('/wallet/:walletId', {
walletId: response.id, walletId: response.id,
}); });
} }
} catch (err) { } catch (err) {
this.errorMessage = "Unable to create wallet!"; this.errorMessage = 'Unable to create wallet!';
this.update(); this.update();
} }
}; };
validate(): boolean { validate(): boolean {
let _return = true; let _return = true;
this.inputs.forEach((input: InputFieldElement) => { this.inputs.forEach((input: InputFieldElement) => {
const valid: boolean = input.validate(); const valid: boolean = input.validate();
if (!valid) _return = false; if (!valid) _return = false;
}); });
return _return; return _return;
} }
render = (): TemplateResult => { render = (): TemplateResult => {
return html` return html`
<div>Create wallet</div> <div>Create wallet</div>
<app-form <app-form data-custom="wallet-create#onSubmit" data-has-cancel="true">
data-custom="wallet-create#onSubmit" <input-field
data-has-cancel="true" data-type="text"
> data-name="name"
<input-field data-label="Name"
data-type="text" data-targets="wallet-create.inputs"
data-name="name" data-rules="required"
data-label="Name" ></input-field>
data-targets="wallet-create.inputs" ${this.errorMessage ? html`<div>${this.errorMessage}</div>` : html``}
data-rules="required" </app-form>
></input-field> `;
${this.errorMessage };
? html`<div>${this.errorMessage}</div>`
: html``}
</app-form>
`;
};
} }
export type { WalletCreateElement }; export type { WalletCreateElement };

View File

@@ -1,50 +1,48 @@
import { targets, controller, target } from "@github/catalyst"; import { targets, controller, target } from '@github/catalyst';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { AuthService, WalletService } from "services/"; import { AuthService, WalletService } from 'services/';
import { AppPaginationElement, InputFieldElement } from "components/"; import { AppPaginationElement, InputFieldElement } from 'components/';
import { BasePageElement } from "common/"; import { BasePageElement } from 'common/';
@controller @controller
class WalletListElement extends BasePageElement { class WalletListElement extends BasePageElement {
@targets inputs: Array<InputFieldElement>; @targets inputs: Array<InputFieldElement>;
private walletService: WalletService; private walletService: WalletService;
@target pagination: AppPaginationElement; @target pagination: AppPaginationElement;
authService: AuthService; authService: AuthService;
errorMessage: string; errorMessage: string;
constructor() { constructor() {
super({ super({
title: "Wallet List", title: 'Wallet List',
}); });
} }
elementConnected = (): void => { elementConnected = (): void => {
this.walletService = new WalletService(this.appMain?.appService); this.walletService = new WalletService(this.appMain?.appService);
this.authService = new AuthService(this.appMain.appService); this.authService = new AuthService(this.appMain.appService);
this.update(); this.update();
this.pagination?.setCustomRenderItem(this.renderItem); this.pagination?.setCustomRenderItem(this.renderItem);
this.pagination?.setFetchFunc?.(this.getWallets, true)!; this.pagination?.setFetchFunc?.(this.getWallets, true)!;
}; };
getWallets = async (options): Promise<any> => { getWallets = async (options): Promise<any> => {
try { try {
const response = await this.walletService.getAll(options); const response = await this.walletService.getAll(options);
return response; return response;
} catch (err) { } catch (err) {
throw err; throw err;
} }
}; };
renderItem = (item): TemplateResult => html`<tr> renderItem = (item): TemplateResult => html`<tr>
<td><app-link data-to="/wallet/${item.id}">${item.name}</app-link></td> <td><app-link data-to="/wallet/${item.id}">${item.name}</app-link></td>
</tr>`; </tr>`;
render = (): TemplateResult => { render = (): TemplateResult => {
return html` return html`
<div>Wallets</div> <div>Wallets</div>
<app-pagination <app-pagination data-target="wallet-list.pagination"></app-pagination>
data-target="wallet-list.pagination" `;
></app-pagination> };
`;
};
} }
export type { WalletListElement }; export type { WalletListElement };

View File

@@ -1,105 +1,95 @@
import { controller, target } from "@github/catalyst"; import { controller, target } from '@github/catalyst';
import { html, TemplateResult } from "@github/jtml"; import { html, TemplateResult } from 'core/utils';
import { TransactionsService, WalletService } from "services/"; import { TransactionsService, WalletService } from 'services/';
import { import { AppMainElement, AppPaginationElement, WalletHeaderElement } from 'components/';
AppMainElement, import { BasePageElement } from 'common/';
AppPaginationElement,
WalletHeaderElement,
} from "components/";
import { BasePageElement } from "common/";
@controller @controller
class WalletPageElement extends BasePageElement { class WalletPageElement extends BasePageElement {
private transactionsService: TransactionsService; private transactionsService: TransactionsService;
private walletService: WalletService; private walletService: WalletService;
@target pagination: AppPaginationElement; @target pagination: AppPaginationElement;
@target walletHeader: WalletHeaderElement; @target walletHeader: WalletHeaderElement;
walletId: string; walletId: string;
constructor() { constructor() {
super({ super({
title: "Wallet", title: 'Wallet',
}); });
} }
elementConnected = (): void => { elementConnected = (): void => {
this.walletService = new WalletService(this.appMain?.appService); this.walletService = new WalletService(this.appMain?.appService);
this.transactionsService = new TransactionsService( this.transactionsService = new TransactionsService(this.appMain?.appService);
this.appMain?.appService if (this?.routerService?.routerState?.data) {
); const { walletId } = this?.routerService?.routerState?.data;
if (this?.routerService?.routerState?.data) { if (walletId) {
const { walletId } = this?.routerService?.routerState?.data; this.walletId = walletId;
if (walletId) { }
this.walletId = walletId; }
} this.update();
} this.pagination?.setFetchFunc?.(this.getTransactions, true)!;
this.update(); this.appMain.addEventListener('tokenchange', this.update);
this.pagination?.setFetchFunc?.(this.getTransactions, true)!; };
this.appMain.addEventListener("tokenchange", this.update);
};
elementDisconnected = (appMain: AppMainElement): void => { elementDisconnected = (appMain: AppMainElement): void => {
appMain?.removeEventListener("tokenchange", this.update); appMain?.removeEventListener('tokenchange', this.update);
}; };
getTransactions = async (options): Promise<any> => { getTransactions = async (options): Promise<any> => {
try { try {
if (this?.routerService?.routerState?.data) { if (this?.routerService?.routerState?.data) {
const { walletId } = this?.routerService?.routerState?.data; const { walletId } = this?.routerService?.routerState?.data;
if (walletId) { if (walletId) {
options["walletId"] = walletId; options['walletId'] = walletId;
} }
} }
const response = await this.transactionsService.getAll(options); const response = await this.transactionsService.getAll(options);
return response; return response;
} catch (err) { } catch (err) {
throw err; throw err;
} }
}; };
getBalance = async (): Promise<void> => { getBalance = async (): Promise<void> => {
try { try {
const response = await this.walletService.getBalance({ const response = await this.walletService.getBalance({
walletId: this.walletId, walletId: this.walletId,
}); });
this.setBalance(response); this.setBalance(response);
} catch (err) { } catch (err) {
throw err; throw err;
} }
}; };
setBalance = (header) => { setBalance = (header) => {
if (!this.walletHeader) return; if (!this.walletHeader) return;
this.walletHeader.currency = header.currency; this.walletHeader.currency = header.currency;
this.walletHeader.currentBalance = header.currentBalance || "0"; this.walletHeader.currentBalance = header.currentBalance || '0';
this.walletHeader.lastMonth = header.lastMonth || "0"; this.walletHeader.lastMonth = header.lastMonth || '0';
this.walletHeader.nextMonth = header.nextMonth || "0"; this.walletHeader.nextMonth = header.nextMonth || '0';
}; };
render = (): TemplateResult => { render = (): TemplateResult => {
const renderHeader = () => html`<wallet-header const renderHeader = () => html`<wallet-header
data-target="wallet-page.walletHeader" data-target="wallet-page.walletHeader"
data-current-balance="0" data-current-balance="0"
data-last-month="0" data-last-month="0"
data-next-month="0" data-next-month="0"
data-currency="0" data-currency="0"
data-custom="wallet-page#getBalance" data-custom="wallet-page#getBalance"
></wallet-header>`; ></wallet-header>`;
const renderWallet = () => { const renderWallet = () => {
if (this.routerService?.routerState?.data?.walletId) { if (this.routerService?.routerState?.data?.walletId) {
return html`<span return html`<span>${this.routerService?.routerState?.data?.walletId}</span>`;
>${this.routerService?.routerState?.data?.walletId}</span }
>`; return html``;
} };
return html``; return html`<div>
}; ${renderHeader()} ${renderWallet()}
return html`<div> <app-pagination data-target="wallet-page.pagination"></app-pagination>
${renderHeader()} ${renderWallet()} </div>`;
<app-pagination };
data-target="wallet-page.pagination"
></app-pagination>
</div>`;
};
} }
export type { WalletPageElement }; export type { WalletPageElement };

View File

@@ -1,22 +1,18 @@
import { AppService, BaseService } from "core/services"; import { AppService, BaseService } from 'core/services';
class PingService extends BaseService { class PingService extends BaseService {
constructor(appService: AppService) { constructor(appService: AppService) {
super("/auth", appService); super('/auth', appService);
} }
login = (data?: Object, headers?: HeadersInit) => { login = (data?: Object, headers?: HeadersInit) => {
return this.appService.post(this.endpoint + "/login", data, headers); return this.appService.post(this.endpoint + '/login', data, headers);
}; };
register = (data?: Object, headers?: HeadersInit) => { register = (data?: Object, headers?: HeadersInit) => {
return this.appService.post(this.endpoint + "/register", data, headers); return this.appService.post(this.endpoint + '/register', data, headers);
}; };
checkToken = (params?: Object, headers?: HeadersInit) => { checkToken = (params?: Object, headers?: HeadersInit) => {
return this.appService.get( return this.appService.get(this.endpoint + '/check-token', params, headers);
this.endpoint + "/check-token", };
params,
headers
);
};
} }
export default PingService; export default PingService;

View File

@@ -1,9 +1,9 @@
import { AppService, BaseService } from "core/services"; import { AppService, BaseService } from 'core/services';
class PingService extends BaseService { class PingService extends BaseService {
constructor(appService: AppService) { constructor(appService: AppService) {
super("/wallet", appService); super('/wallet', appService);
} }
} }
export default PingService; export default PingService;

View File

@@ -1,9 +1,9 @@
import { AppService, BaseService } from "core/services"; import { AppService, BaseService } from 'core/services';
class TransactionsService extends BaseService { class TransactionsService extends BaseService {
constructor(appService: AppService) { constructor(appService: AppService) {
super("/transaction", appService); super('/transaction', appService);
} }
} }
export default TransactionsService; export default TransactionsService;

View File

@@ -1,17 +1,13 @@
import { AppService, BaseService } from "core/services"; import { AppService, BaseService } from 'core/services';
class WalletService extends BaseService { class WalletService extends BaseService {
constructor(appService: AppService) { constructor(appService: AppService) {
super("/wallet", appService); super('/wallet', appService);
} }
getBalance = (params?: Object, headers?: HeadersInit) => { getBalance = (params?: Object, headers?: HeadersInit) => {
return this.appService.get( return this.appService.get(this.endpoint + '/wallet-header', params, headers);
this.endpoint + "/wallet-header", };
params,
headers
);
};
} }
export default WalletService; export default WalletService;

View File

@@ -1,4 +1,4 @@
export { default as PingService } from "./PingService"; export { default as PingService } from './PingService';
export { default as AuthService } from "./AuthService"; export { default as AuthService } from './AuthService';
export { default as WalletService } from "./WalletService"; export { default as WalletService } from './WalletService';
export { default as TransactionsService } from "./TransactionsService"; export { default as TransactionsService } from './TransactionsService';

View File

@@ -1,107 +1,109 @@
.dropdown-custom { .dropdown-custom {
text-align: center; text-align: center;
text-transform: capitalize; text-transform: capitalize;
div.dropdown-custom-top { position: relative;
position: relative; div.dropdown-custom-top {
background-color: #ffffff; position: relative;
border: 1px solid transparent; background-color: $white;
color: #09090a; color: $black;
border-radius: 2px; border-radius: 2px;
cursor: pointer; cursor: pointer;
padding: 5px 0; padding: 5px 0;
font-weight: 500; font-weight: 500;
font-size: 16px; font-size: 16px;
border: 1px solid #767676; border: 1px solid $white;
border-radius: 3px; border-radius: 5px;
&::after { &::after {
content: ""; content: '';
position: absolute; position: absolute;
right: 0; right: 0;
margin-top: 8px; margin-top: 8px;
margin-right: 4px; margin-right: 4px;
width: 0; width: 0;
height: 0; height: 0;
border-left: 8px solid transparent; border-left: 8px solid transparent;
border-right: 8px solid transparent; border-right: 8px solid transparent;
border-top: 8px solid #2e2e2e; border-top: 8px solid #2e2e2e;
} }
&.--open { &.--open {
border-bottom-right-radius: 0; border-bottom-right-radius: 0 !important;
border-bottom-left-radius: 0; border-bottom-left-radius: 0 !important;
border-bottom: transparent; border-bottom: transparent;
margin-bottom: 0 !important; margin-bottom: 0 !important;
&::after { &::after {
content: ""; content: '';
position: absolute; position: absolute;
right: 0; right: 0;
margin-top: 8px; margin-top: 8px;
margin-right: 4px; margin-right: 4px;
width: 0; width: 0;
height: 0; height: 0;
border-left: 8px solid transparent; border-left: 8px solid transparent;
border-right: 8px solid transparent; border-right: 8px solid transparent;
border-bottom: 8px solid #2e2e2e; border-bottom: 8px solid #2e2e2e;
border-top: none; border-top: none;
} }
} }
span.dropdown-custom-fieldname { span.dropdown-custom-fieldname {
-webkit-touch-callout: none; -webkit-touch-callout: none;
-webkit-user-select: none; -webkit-user-select: none;
-khtml-user-select: none; -khtml-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
user-select: none; user-select: none;
} }
} }
div.dropdown-custom-open { div.dropdown-custom-open {
border: 1px solid transparent; border: 1px solid transparent;
position: absolute; position: absolute;
width: 100%; width: calc(100% - 2 * 1px);
border-top: none; border-top: none;
margin-top: 0 !important; margin-top: 0 !important;
background-color: #fbfafa; background-color: #fbfafa;
border-bottom-right-radius: 0.2em; border-bottom-right-radius: 0.2em;
border-bottom-left-radius: 0.2em; border-bottom-left-radius: 0.2em;
font-size: 16px; font-size: 16px;
input.dropdown-custom-search { input.dropdown-custom-search {
position: relative; position: relative;
width: calc(100% - 2 * 2px); box-sizing: border-box;
margin: 2px; padding: 0;
background-color: #fbfbfb; width: calc(100% - 2 * 2px);
border: 1px solid #9c9c9c; margin: 2px;
border-radius: 0.3em; background-color: $white;
&:hover { border: 1px solid $white;
border: 1px solid #b6b6b6; border-radius: 0.3em;
} &:hover {
} border: 1px solid $white;
ul.dropdown-custom-list { }
padding: 1px 0; }
max-height: 100px; ul.dropdown-custom-list {
overflow-y: scroll; padding: 1px 0;
&::-webkit-scrollbar { max-height: 100px;
display: none; overflow-y: scroll;
} &::-webkit-scrollbar {
-ms-overflow-style: none; display: none;
scrollbar-width: none; }
li.dropdown-custom-listitem { -ms-overflow-style: none;
margin: 2px; scrollbar-width: none;
padding: 1px 0; li.dropdown-custom-listitem {
list-style-type: none; margin: 2px;
color: #0e0d0d; padding: 1px 0;
-webkit-touch-callout: none; list-style-type: none;
-webkit-user-select: none; color: #0e0d0d;
-khtml-user-select: none; -webkit-touch-callout: none;
-moz-user-select: none; -webkit-user-select: none;
-ms-user-select: none; -khtml-user-select: none;
user-select: none; -moz-user-select: none;
cursor: pointer; -ms-user-select: none;
&:hover { user-select: none;
background-color: #c6d8ff; cursor: pointer;
} &:hover {
&.--selected { background-color: #c6d8ff;
background-color: #d8e4ff; }
} &.--selected {
} background-color: #d8e4ff;
} }
} }
}
}
} }

View File

@@ -0,0 +1 @@
@import './input-field.scss';

View File

@@ -0,0 +1,8 @@
input-field {
input {
width: 100%;
box-sizing: border-box;
margin: 0;
padding: 0;
}
}

View File

@@ -1,11 +1,12 @@
@import "./core/index.scss"; @import './core/index.scss';
@import "./menu-item/index.scss"; @import './menu-item/index.scss';
@import "./sidebar/index.scss"; @import './sidebar/index.scss';
@import "./modal/index.scss"; @import './modal/index.scss';
@import "./table/index.scss"; @import './table/index.scss';
@import "./app-loader/index.scss"; @import './app-loader/index.scss';
@import "./circle-loader/index.scss"; @import './circle-loader/index.scss';
@import "./page/index.scss"; @import './page/index.scss';
@import "./app-form/index.scss"; @import './app-form/index.scss';
@import "./layout/index.scss"; @import './layout/index.scss';
@import "./app-dropdown/index.scss"; @import './app-dropdown/index.scss';
@import './input-field/index.scss';

View File

@@ -8,6 +8,8 @@
"es2016" "es2016"
], ],
"moduleResolution": "node", "moduleResolution": "node",
"jsx": "preserve",
"jsxFactory": "h",
"resolveJsonModule": true, "resolveJsonModule": true,
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,

View File

@@ -2,6 +2,7 @@ const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserPlugin = require("terser-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin");
const { DefinePlugin } = require('webpack'); const { DefinePlugin } = require('webpack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const alias = { const alias = {
common: path.resolve(__dirname, '/common'), common: path.resolve(__dirname, '/common'),
@@ -52,7 +53,7 @@ module.exports = (env, args) => {
} }
}, },
minimize: true, minimize: true,
minimizer: [new TerserPlugin()], minimizer: [new TerserPlugin(), new UglifyJsPlugin()],
}, },
output: { output: {
path: path.join(__dirname, 'public'), path: path.join(__dirname, 'public'),
@@ -62,7 +63,7 @@ module.exports = (env, args) => {
module: { module: {
rules: [ rules: [
{ {
test: /\.(js|ts)?$/, test: /\.(js|ts)x?$/,
exclude: /node_modules/, exclude: /node_modules/,
use: { use: {
loader: 'babel-loader' loader: 'babel-loader'
@@ -106,7 +107,7 @@ module.exports = (env, args) => {
}), }),
], ],
resolve: { resolve: {
extensions: ['.js', '.ts'], extensions: ['.js', '.ts', '.jsx', '.tsx'],
alias: alias alias: alias
}, },
devServer: { devServer: {

737
yarn.lock

File diff suppressed because it is too large Load Diff