diff --git a/src/common/pages/BasePageElement/BasePageElement.ts b/src/common/pages/BasePageElement/BasePageElement.ts index f998dc5..c0859e1 100644 --- a/src/common/pages/BasePageElement/BasePageElement.ts +++ b/src/common/pages/BasePageElement/BasePageElement.ts @@ -7,6 +7,7 @@ class BasePageElement extends BaseElement { public pageTitle: string = ''; @attr hidetitle: string; @attr customtitle: string; + private _data: any; constructor(options: OptionType) { super(); if (options?.title) { @@ -34,6 +35,14 @@ class BasePageElement extends BaseElement { this.appMain.setTitle(this.pageTitle); super.connectedCallback(); } + + setData = (data: any) => { + this._data = data; + }; + + getData = () => { + return this._data; + }; } export default BasePageElement; diff --git a/src/components/app-dropdown/AppDropdownElement.ts b/src/components/app-dropdown/AppDropdownElement.ts index 554ba11..d8b5430 100644 --- a/src/components/app-dropdown/AppDropdownElement.ts +++ b/src/components/app-dropdown/AppDropdownElement.ts @@ -46,9 +46,8 @@ class AppDropdownElement extends BaseComponentElement { this.validator = new Validator(this, this.appForm, this.rules); this.randId = `${name}${randomId()}`; this.update(); - this.appMain.addEventListener('click', this.outsideClick); + this.appMain?.addEventListener('click', this.outsideClick); this.elementDisconnectCallbacks.push((appMain) => { - console.log('oslo'); appMain.removeEventListener('click', this.outsideClick); }); @@ -62,7 +61,6 @@ class AppDropdownElement extends BaseComponentElement { elementDisconnected = (): void => {}; outsideClick = (e) => { - console.log(e.target); this.closeDropdown(e); }; @@ -209,7 +207,7 @@ class AppDropdownElement extends BaseComponentElement { diff --git a/src/components/app-main/AppMainElement.ts b/src/components/app-main/AppMainElement.ts index 8e0f302..a283158 100644 --- a/src/components/app-main/AppMainElement.ts +++ b/src/components/app-main/AppMainElement.ts @@ -5,6 +5,7 @@ import { AppModalElement, AppRootElement } from 'components/'; import { closest } from 'core/utils'; import { AppLoaderElement } from 'components/app-loader/AppLoaderElement'; import { ToastPortalElement } from 'components/toast-portal/ToastPortalElement'; +import { BasePageElement } from 'common/'; @controller class AppMainElement extends HTMLElement { @@ -22,6 +23,7 @@ class AppMainElement extends HTMLElement { routechanged: new Event('routechanged'), tokenchange: new Event('tokenchange'), walletupdate: new Event('walletupdate'), + transactionupdate: new Event('transactionupdate'), }; constructor() { @@ -112,12 +114,12 @@ class AppMainElement extends HTMLElement { } }; - createModal = (element: string) => { + createModal = (element: string, data?: any) => { this.closeModal(); this.appMain.addEventListener('routechanged', this.closeModal); const _appModal = this.createAppModal(); - const _modalContent = this.createModalContent(element); + const _modalContent = this.createModalContent(element, data); const _modalOverlay = this.createModalOverlay(); _modalOverlay.appendChild(_modalContent); @@ -129,6 +131,10 @@ class AppMainElement extends HTMLElement { this.dispatchEvent(this.domEvents.walletupdate); }; + public triggerTransactionUpdate = () => { + this.dispatchEvent(this.domEvents.transactionupdate); + }; + public setTitle = (title: string): void => { if (!title) title = __CONFIG__.appName; window.document.title = title; @@ -146,10 +152,11 @@ class AppMainElement extends HTMLElement { this.appendChild(_loader); }; - private createModalContent = (element: string) => { + private createModalContent = (element: string, data?: any) => { const _modalElement = document.createElement(element); const _divEl = document.createElement('div'); _modalElement.setAttribute('data-target', 'app-modal.modalElement'); + (_modalElement as BasePageElement).setData({ ...data }); _divEl.setAttribute('data-target', 'app-modal.modalContent'); //_divEl.setAttribute('data-action', 'click:app-main#preventClosing'); _divEl.appendChild(_modalElement); diff --git a/src/components/app-pagination/AppPaginationElement.ts b/src/components/app-pagination/AppPaginationElement.ts index 8ab078d..1ca173e 100644 --- a/src/components/app-pagination/AppPaginationElement.ts +++ b/src/components/app-pagination/AppPaginationElement.ts @@ -11,6 +11,7 @@ class AppPaginationElement extends BaseComponentElement { @attr totalItems: number; @attr autoInit: string; @attr tableLayout: string = 'transactions-table'; + @attr colLayout: string = 'col-transactions'; initial: boolean = false; private customRenderItems: () => TemplateResult; @@ -95,14 +96,20 @@ class AppPaginationElement extends BaseComponentElement { render = (): TemplateResult => { const { rpp, totalItems, page, items } = this; + console.log(items); const renderItem = this.customRenderItem ? this.customRenderItem - : (item, iter) => html` + : (item, iter) => html` ${iter + 1 + rpp * (page - 1)} ${item.description} - + + ${item?.transactionType?.type == 'expense' ? '- ' : ''} ${Number(item.amount).toLocaleString('en-US', { maximumFractionDigits: 2, minimumFractionDigits: 2, @@ -119,11 +126,11 @@ class AppPaginationElement extends BaseComponentElement { return html``; } else { if (items?.length > 0) { - return html` - ${items?.map((item, iter) => renderItem(item, iter))} ${renderPagination()} -
`; + return items?.map((item, iter) => renderItem(item, iter)); } - return html``; + return html` + No data + `; } }; @@ -151,7 +158,11 @@ class AppPaginationElement extends BaseComponentElement { } }; - return html`
${renderItems()}
`; + return html`
+ + ${renderItems()} ${renderPagination()} +
+
`; }; } diff --git a/src/core/utils/timer.ts b/src/core/utils/timer.ts index 6c32a6c..1e0d24f 100644 --- a/src/core/utils/timer.ts +++ b/src/core/utils/timer.ts @@ -17,7 +17,6 @@ class Timer { resume = () => { this.start = Date.now(); window.clearTimeout(this.timerId); - console.log(this.remaining); this.timerId = window.setTimeout(this.callback, this.remaining, ...this.args); }; diff --git a/src/pages/history-page/HistoryPageElement.ts b/src/pages/history-page/HistoryPageElement.ts index 8ec2773..597143b 100644 --- a/src/pages/history-page/HistoryPageElement.ts +++ b/src/pages/history-page/HistoryPageElement.ts @@ -19,10 +19,16 @@ class HistoryPageElement extends BasePageElement { this.update(); this.pagination?.setFetchFunc?.(this.getTransactions, true)!; this.appMain.addEventListener('tokenchange', this.update); + this.appMain.addEventListener('transactionupdate', this.transactionUpdated); }; elementDisconnected = (appMain: AppMainElement): void => { appMain?.removeEventListener('tokenchange', this.update); + appMain?.removeEventListener('transactionupdate', this.transactionUpdated); + }; + + transactionUpdated = () => { + this.pagination?.executeFetch(); }; getTransactions = async (options): Promise => { diff --git a/src/pages/transaction-create/TransactionCreateElement.ts b/src/pages/transaction-create/TransactionCreateElement.ts index ad1f60d..b49e076 100644 --- a/src/pages/transaction-create/TransactionCreateElement.ts +++ b/src/pages/transaction-create/TransactionCreateElement.ts @@ -1,7 +1,7 @@ -import { targets, controller } from '@github/catalyst'; +import { targets, controller, target } from '@github/catalyst'; import { html, TemplateResult } from 'core/utils'; -import { AuthService, TransactionsService, WalletService } from 'services/'; -import { InputFieldElement } from 'components/'; +import { AuthService, TransactionsService, TransactionTypeService, WalletService } from 'services/'; +import { AppFormElement, InputFieldElement } from 'components/'; import { RouterService } from 'core/services'; import { BasePageElement } from 'common/'; import { AppDropdownElement } from 'components/app-dropdown/AppDropdownElement'; @@ -9,10 +9,14 @@ import { AppDropdownElement } from 'components/app-dropdown/AppDropdownElement'; @controller class TransactionCreateElement extends BasePageElement { @targets inputs: Array; + @target appForm: AppFormElement; private transactionService: TransactionsService; + private transactionTypeService: TransactionTypeService; private walletService: WalletService; + walletData: any = null; authService: AuthService; errorMessage: string; + private initial: boolean = false; constructor() { super({ title: 'New Transaction', @@ -21,8 +25,15 @@ class TransactionCreateElement extends BasePageElement { elementConnected = (): void => { this.walletService = new WalletService(this.appMain?.appService); this.transactionService = new TransactionsService(this.appMain?.appService); + this.transactionTypeService = new TransactionTypeService(this.appMain?.appService); this.authService = new AuthService(this.appMain.appService); + this.walletData = this.getData(); this.update(); + if (this.walletData && this.walletData.walletId) { + this.setTransactionType(); + } else { + this.initial = true; + } }; get nameInput(): InputFieldElement | AppDropdownElement { @@ -42,6 +53,17 @@ class TransactionCreateElement extends BasePageElement { return formObject; } + setTransactionType = async () => { + this.appForm.isValid = false; + try { + const response = await this.transactionTypeService.getAll(); + this.walletData.transactionTypeId = response?.find((type) => type.type == this.walletData.transactionType)?.id; + } catch (err) { + } finally { + this.appForm.isValid = true; + } + }; + getWallets = async (options): Promise => { try { const response = await this.walletService.getAll(options); @@ -49,26 +71,43 @@ class TransactionCreateElement extends BasePageElement { } catch (err) {} }; + getTypes = async (options): Promise => { + try { + const response = await this.transactionTypeService.getAll(options); + return response; + } catch (err) {} + }; + onSubmit = async (values): Promise => { try { if (!this.validate()) { return; } - const { description: description, wallet: walletId, amount } = values; + const { description: description, wallet: walletId, amount, transactionType: transactionTypeId } = values; - const response = await this.transactionService.post({ + const walletData = this.walletData; + + const formData = { description, - walletId, amount, - }); + walletId: walletData && walletData.walletId ? walletData.walletId : walletId, + transactionTypeId: + walletData && walletData.transactionTypeId ? walletData.transactionTypeId : transactionTypeId, + }; + const response = await this.transactionService.post(formData); if (response?.id) { - this.appMain.triggerWalletUpdate(); + this.appMain.triggerTransactionUpdate(); this.appMain.pushToast('success', 'Transaction created successfully!'); - this.routerService.goTo('/history', { - walletId: response.id, - }); + + if (walletData.walletId) { + this.appMain?.closeModal(); + } else { + this.routerService.goTo('/history', { + walletId: response.walletId, + }); + } } } catch (err) { this.errorMessage = 'Unable to create transaction!'; @@ -86,31 +125,55 @@ class TransactionCreateElement extends BasePageElement { } render = (): TemplateResult => { + const renderInput = (type, name, label, rules, hide?) => { + if (hide) { + return html``; + } + return html``; + }; + + const renderDropdown = (fetch, name, label, rules, hide?) => { + if (hide) { + return html``; + } + return html``; + }; + return html`
Create wallet
- - - - - + + ${renderInput('number', 'amount', 'Amount', 'required')} + ${renderInput('text', 'description', 'Description', 'required')} + ${renderDropdown( + 'transaction-create#getWallets', + 'wallet', + 'Wallet', + 'required', + this.walletData && this.walletData.walletId + )} + ${renderDropdown( + 'transaction-create#getTypes', + 'transactionType', + 'Transaction Type', + 'required', + this.walletData && this.walletData.walletId + )} ${this.errorMessage ? html`
${this.errorMessage}
` : html``}
`; diff --git a/src/pages/wallet-page/WalletPageElement.ts b/src/pages/wallet-page/WalletPageElement.ts index 70c822e..f6bb602 100644 --- a/src/pages/wallet-page/WalletPageElement.ts +++ b/src/pages/wallet-page/WalletPageElement.ts @@ -29,10 +29,17 @@ class WalletPageElement extends BasePageElement { this.update(); this.pagination?.setFetchFunc?.(this.getTransactions, true)!; this.appMain.addEventListener('tokenchange', this.update); + this.appMain.addEventListener('transactionupdate', this.transactionUpdated); }; elementDisconnected = (appMain: AppMainElement): void => { appMain?.removeEventListener('tokenchange', this.update); + appMain?.removeEventListener('transactionupdate', this.transactionUpdated); + }; + + transactionUpdated = () => { + this.walletHeader?.executeFetch(); + this.pagination?.executeFetch(); }; getTransactions = async (options): Promise => { @@ -43,6 +50,7 @@ class WalletPageElement extends BasePageElement { options['walletId'] = walletId; } } + options.embed = 'TransactionType'; const response = await this.transactionsService.getAll(options); return response; } catch (err) { @@ -69,6 +77,30 @@ class WalletPageElement extends BasePageElement { this.walletHeader.nextMonth = header.nextMonth || '0'; }; + newExpense = (s): void => { + const _modal = this.appMain.appModal; + if (_modal) { + this.appMain.closeModal(); + } else { + this.appMain.createModal('transaction-create', { + walletId: this.routerService?.routerState?.data?.walletId, + transactionType: 'expense', + }); + } + }; + + newGain = (s): void => { + const _modal = this.appMain.appModal; + if (_modal) { + this.appMain.closeModal(); + } else { + this.appMain.createModal('transaction-create', { + walletId: this.routerService?.routerState?.data?.walletId, + transactionType: 'gain', + }); + } + }; + render = (): TemplateResult => { const renderHeader = () => html` { if (this.routerService?.routerState?.data?.walletId) { - return html`${this.routerService?.routerState?.data?.walletId}`; + return html`
+ + +
`; } return html``; }; diff --git a/src/services/TransactionTypeService.ts b/src/services/TransactionTypeService.ts new file mode 100644 index 0000000..87e32ed --- /dev/null +++ b/src/services/TransactionTypeService.ts @@ -0,0 +1,9 @@ +import { AppService, BaseService } from 'core/services'; + +class TransactionTypeService extends BaseService { + constructor(appService: AppService) { + super('/transaction-type', appService); + } +} + +export default TransactionTypeService; diff --git a/src/services/index.ts b/src/services/index.ts index 15a8c52..50bb30e 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -2,3 +2,4 @@ export { default as PingService } from './PingService'; export { default as AuthService } from './AuthService'; export { default as WalletService } from './WalletService'; export { default as TransactionsService } from './TransactionsService'; +export { default as TransactionTypeService } from './TransactionTypeService'; diff --git a/src/styles/app-dropdown/app-dropdown.scss b/src/styles/app-dropdown/app-dropdown.scss index df939cb..e8b059e 100644 --- a/src/styles/app-dropdown/app-dropdown.scss +++ b/src/styles/app-dropdown/app-dropdown.scss @@ -63,6 +63,7 @@ border-bottom-right-radius: 0.2em; border-bottom-left-radius: 0.2em; font-size: 16px; + z-index: 1500; input.dropdown-custom-search { position: relative; box-sizing: border-box; diff --git a/src/styles/app-pagination/app-pagination.scss b/src/styles/app-pagination/app-pagination.scss index e73372f..de092d5 100644 --- a/src/styles/app-pagination/app-pagination.scss +++ b/src/styles/app-pagination/app-pagination.scss @@ -13,8 +13,28 @@ app-pagination { padding: 4px 12px; border-radius: 4px; display: grid; - grid-template-columns: auto 1fr auto; margin: 6px 8px; + &.col-3 { + grid-template-columns: repeat(3, 1fr); + } + &.col-2 { + grid-template-columns: repeat(2, 1fr); + } + &.col-1 { + grid-template-columns: 1fr; + } + &.col-4 { + grid-template-columns: repeat(4, 1fr); + } + &.col-5 { + grid-template-columns: repeat(5, 1fr); + } + &.col-6 { + grid-template-columns: repeat(6, 1fr); + } + &.col-transactions { + grid-template-columns: auto 1fr auto; + } td, th { margin: 0 12px; diff --git a/src/styles/core/variables.scss b/src/styles/core/variables.scss index b3f6621..dfdda2b 100644 --- a/src/styles/core/variables.scss +++ b/src/styles/core/variables.scss @@ -5,32 +5,40 @@ $font-weight-semibold: 400; $font-weight-bold: 600; $button-map: ( - "primary": ( - $blue-08, - $white, - ), - "disabled": ( - $gray-08, - $white, - ), - "secondary": ( - $orange-08, - $white, - ), - "alert": ( - $red-08, - $white, - ), - "black": ( - $black, - $white, - ), - "white": ( - $white, - $black, - ), - "yellow": ( - $yellow-06, - $white, - ), + 'primary': ( + $blue-08, + $white, + ), + 'disabled': ( + $gray-08, + $white, + ), + 'secondary': ( + $orange-08, + $white, + ), + 'alert': ( + $red-08, + $white, + ), + 'black': ( + $black, + $white, + ), + 'white': ( + $white, + $black, + ), + 'yellow': ( + $yellow-06, + $white, + ), + 'red': ( + $red-08, + $white, + ), + 'green': ( + $green-08, + $white, + ), );