diff --git a/src/components/app-dropdown/AppDropdownElement.ts b/src/components/app-dropdown/AppDropdownElement.ts index 3bc04d1..4d38f3d 100644 --- a/src/components/app-dropdown/AppDropdownElement.ts +++ b/src/components/app-dropdown/AppDropdownElement.ts @@ -1,5 +1,5 @@ import { attr, controller, target } from "@github/catalyst"; -import { firstUpper } from "core/utils"; +import { findMethod, firstUpper } from "core/utils"; import { html, TemplateResult } from "@github/jtml"; import { RouterService } from "core/services"; import randomId from "core/utils/random-id"; @@ -10,20 +10,98 @@ import { BaseComponentElement } from "common/"; @controller class AppDropdownElement extends BaseComponentElement { @attr name: string; - @attr type: string; @attr label: string; @attr rules: string; @target main: HTMLElement; @target inp: HTMLElement; + @attr displaykey: string; + @attr valuekey: string; + @attr fetch: string; + fetchFunc: any; error: string; randId: string; + + items: Array; + totalItems: number; + page: number = 1; + rpp: number = 30; + constructor() { super(); } + getItems = async (options?: any): Promise => { + if (typeof this.fetchFunc !== "function") return; + try { + const response = await this.fetchFunc(options); + this.setItems(response); + } catch (err) { + this.update(); + } + }; + + setItems = (response): void => { + 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.renderOptions(items); + this.renderOptions(items); + } + }; + + private renderOptions = (items) => { + const displayKey = this.displaykey || "name"; + const valueKey = this.valuekey || "id"; + + const options = items?.map((item) => { + const val = { name: item[displayKey], value: item[valueKey] }; + if ( + this.optionValues.some((value) => { + if (value.name == val.name && value.value == val.value) { + return true; + } + return false; + }) + ) + return; + const _option = document.createElement("option"); + _option.setAttribute("value", val.value); + _option.innerText = val.name; + this.inp?.appendChild(_option); + }); + }; + + get optionValues() { + let values = []; + this.inp.childNodes.forEach((item: HTMLElement) => { + const value = item.getAttribute("value"); + const name = item.innerText; + values.push({ name, value }); + }); + return values; + } + + onChange = () => { + this.renderOptions(this.items); + }; public elementConnected = (): void => { this.randId = `${name}${randomId()}`; + this.fetchFunc = findMethod(this.fetch, this.appMain); this.update(); + + const options = { + rpp: this.rpp, + page: this.page, + }; + this.getItems(options); }; get valid(): boolean { @@ -37,7 +115,8 @@ class AppDropdownElement extends BaseComponentElement { validate(): boolean { let _return = true; const rules = this.rules?.split("|").filter((a) => a); - const value = (this.inp as HTMLInputElement)?.value; + const value = (this.inp as HTMLSelectElement)?.value; + console.log(_return, rules, value); rules .slice() .reverse() @@ -67,7 +146,12 @@ class AppDropdownElement extends BaseComponentElement { } render = (): TemplateResult => { - return html``; + return html`
+ +
`; }; } diff --git a/src/components/app-form/AppFormElement.ts b/src/components/app-form/AppFormElement.ts index a22c6b4..42ebc24 100644 --- a/src/components/app-form/AppFormElement.ts +++ b/src/components/app-form/AppFormElement.ts @@ -2,7 +2,7 @@ import { attr, controller, target } from "@github/catalyst"; import { html, TemplateResult, unsafeHTML } from "@github/jtml"; import { BaseComponentElement } from "common/"; import { InputFieldElement } from "components/input-field/InputFieldElement"; -import { isTrue, querys } from "core/utils"; +import { findMethod, isTrue, querys } from "core/utils"; @controller class AppFormElement extends BaseComponentElement { @@ -29,16 +29,8 @@ class AppFormElement extends BaseComponentElement { return; } const actionString = this.custom; - if (actionString) { - const methodSep = actionString.lastIndexOf("#"); - const tag = actionString.slice(0, methodSep); - const method = actionString.slice(methodSep + 1); - - const element = this.appMain.querySelector(tag); - if (element) { - element?.[method]?.(e); - } - } + const submitFunc = findMethod(actionString, this.appMain); + submitFunc?.(e); return false; }; @@ -71,7 +63,7 @@ class AppFormElement extends BaseComponentElement { get valid() { let _valid = 0; this.inputField?.forEach((input) => { - if (input.required && (input.inp as HTMLInputElement).value) { + if (input.required && (input.inp as HTMLSelectElement).value) { _valid++; } }); diff --git a/src/components/app-menu/AppMenuElement.ts b/src/components/app-menu/AppMenuElement.ts index fb2b7a8..8801d11 100644 --- a/src/components/app-menu/AppMenuElement.ts +++ b/src/components/app-menu/AppMenuElement.ts @@ -71,7 +71,7 @@ class AppMenuElement extends BaseComponentElement { return null; }; - openModal = (): void => { + modalWallet = (): void => { const _modal = this.appMain.appModal; if (_modal) { this.appMain.closeModal(); @@ -80,6 +80,15 @@ class AppMenuElement extends BaseComponentElement { } }; + modalTransaction = (): void => { + const _modal = this.appMain.appModal; + if (_modal) { + this.appMain.closeModal(); + } else { + this.appMain.createModal("transaction-create"); + } + }; + render = (): TemplateResult => { const { isAuth, totalWallets, walletData } = this; @@ -132,11 +141,15 @@ class AppMenuElement extends BaseComponentElement { return html`
${menuHeader(__CONFIG__.appName)} ${regularMenu("/", "Home")} - ${authMenu("/history", "Transaction History")} + ${authMenu( + "/history", + "Transaction History", + "click:app-menu#modalTransaction" + )} ${authMenu( "/wallet/all", "My Wallets", - "click:app-menu#openModal" + "click:app-menu#modalWallet" )} ${renderWallets(walletData)} diff --git a/src/components/index.ts b/src/components/index.ts index fb25dd1..0455125 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -6,6 +6,7 @@ export * from "./app-root/AppRootElement"; export * from "./app-slot/AppSlotElement"; export * from "./app-menu/AppMenuElement"; export * from "./input-field/InputFieldElement"; +export * from "./app-dropdown/AppDropdownElement"; export * from "./app-loader/AppLoaderElement"; export * from "./circle-loader/CircleLoaderElement"; export * from "./app-form/AppFormElement"; diff --git a/src/components/input-field/InputFieldElement.ts b/src/components/input-field/InputFieldElement.ts index 88f7542..b5bce83 100644 --- a/src/components/input-field/InputFieldElement.ts +++ b/src/components/input-field/InputFieldElement.ts @@ -27,6 +27,7 @@ class InputFieldElement extends BaseComponentElement { public elementConnected = (): void => { this.randId = `${name}${randomId()}`; this.update(); + this.validate(); }; get valid(): boolean { diff --git a/src/core/utils/find-method.ts b/src/core/utils/find-method.ts new file mode 100644 index 0000000..63ddf2c --- /dev/null +++ b/src/core/utils/find-method.ts @@ -0,0 +1,18 @@ +import { AppMainElement } from "components/"; + +export default function findMethod( + actionString: string, + appMain: AppMainElement +): Function { + if (actionString) { + const methodSep = actionString.lastIndexOf("#"); + const tag = actionString.slice(0, methodSep); + const method = actionString.slice(methodSep + 1); + + const element = appMain.querySelector(tag); + if (element) { + return element?.[method]; + } + } + return () => {}; +} diff --git a/src/core/utils/index.ts b/src/core/utils/index.ts index 1e9f53e..791790e 100644 --- a/src/core/utils/index.ts +++ b/src/core/utils/index.ts @@ -6,3 +6,4 @@ export { default as isTrue } from "./isTrue"; 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"; diff --git a/src/pages/index.ts b/src/pages/index.ts index 1f69361..a1ba7d7 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -6,3 +6,4 @@ export * from "./not-found/NotFoundElement"; export * from "./history-page/HistoryPageElement"; export * from "./wallet-list/WalletListElement"; export * from "./wallet-create/WalletCreateElement"; +export * from "./transaction-create/TransactionCreateElement"; diff --git a/src/pages/transaction-create/TransactionCreateElement.ts b/src/pages/transaction-create/TransactionCreateElement.ts new file mode 100644 index 0000000..8c2280c --- /dev/null +++ b/src/pages/transaction-create/TransactionCreateElement.ts @@ -0,0 +1,116 @@ +import { targets, controller } from "@github/catalyst"; +import { html, TemplateResult } from "@github/jtml"; +import { AuthService, TransactionsService, WalletService } from "services/"; +import { InputFieldElement } from "components/"; +import { RouterService } from "core/services"; +import { BasePageElement } from "common/"; +import { AppDropdownElement } from "components/app-dropdown/AppDropdownElement"; + +@controller +class TransactionCreateElement extends BasePageElement { + @targets inputs: Array; + private transactionService: TransactionsService; + private walletService: WalletService; + authService: AuthService; + errorMessage: string; + constructor() { + super({ + title: "New Transaction", + }); + } + elementConnected = (): void => { + this.walletService = new WalletService(this.appMain?.appService); + this.transactionService = new TransactionsService( + this.appMain?.appService + ); + this.authService = new AuthService(this.appMain.appService); + this.update(); + }; + + get nameInput(): InputFieldElement | AppDropdownElement { + for (const i in this.inputs) { + if (this.inputs[i]?.name == "name") { + return this.inputs[i]; + } + } + } + + get values(): any { + const formObject: any = {}; + this.inputs.forEach((input: InputFieldElement) => { + const inputType = input.inp; + formObject[input.name] = (inputType as HTMLInputElement).value; + }); + return formObject; + } + + getWallets = async (options): Promise => { + try { + const response = await this.walletService.getAll(options); + return response; + } catch (err) {} + }; + + onSubmit = async (): Promise => { + try { + if (!this.validate()) { + return; + } + const { name: description, wallet: walletId } = this.values; + const response = await this.transactionService.post({ + description, + walletId, + }); + + if (response?.id) { + this.appMain.triggerWalletUpdate(); + this.routerService.goTo("/history", { + walletId: response.id, + }); + } + } catch (err) { + this.errorMessage = "Unable to create transaction!"; + this.update(); + } + }; + + validate(): boolean { + let _return = true; + this.inputs.forEach((input: InputFieldElement) => { + const valid: boolean = input.validate(); + if (!valid) _return = false; + }); + return _return; + } + + render = (): TemplateResult => { + return html` +
Create wallet
+ + + + + ${this.errorMessage + ? html`
${this.errorMessage}
` + : html``} +
+ `; + }; +} + +export type { TransactionCreateElement }; diff --git a/src/styles/core/main.scss b/src/styles/core/main.scss index ef3436c..bc4d8f8 100644 --- a/src/styles/core/main.scss +++ b/src/styles/core/main.scss @@ -2,7 +2,6 @@ app-main { * { font-family: Roboto; font-size: 14px; - color: $white; } input,