mirror of
https://github.com/FJurmanovic/wallet-web.git
synced 2026-02-06 06:08:10 +00:00
Merge branch 'develop'
This commit is contained in:
@@ -32,6 +32,8 @@
|
|||||||
{
|
{
|
||||||
"ignoreRestSiblings": true
|
"ignoreRestSiblings": true
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"no-console": "warn",
|
||||||
|
"no-debugger": "warn"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ import { isTrue } from 'core/utils';
|
|||||||
|
|
||||||
class BasePageElement extends BaseElement {
|
class BasePageElement extends BaseElement {
|
||||||
public _pageTitle: string = '';
|
public _pageTitle: string = '';
|
||||||
|
public hideTitleHead: boolean = false;
|
||||||
@attr hidetitle: string;
|
@attr hidetitle: string;
|
||||||
@attr customtitle: string;
|
@attr customtitle: string;
|
||||||
private _data: any;
|
private _data: any;
|
||||||
@@ -13,6 +14,7 @@ class BasePageElement extends BaseElement {
|
|||||||
if (options?.title) {
|
if (options?.title) {
|
||||||
this._pageTitle = options?.title;
|
this._pageTitle = options?.title;
|
||||||
}
|
}
|
||||||
|
this.hideTitleHead = options?.hideTitleHead || false;
|
||||||
this.connectedCallback = this.connectedCallback.bind(this);
|
this.connectedCallback = this.connectedCallback.bind(this);
|
||||||
this.disconnectedCallback = this.disconnectedCallback.bind(this);
|
this.disconnectedCallback = this.disconnectedCallback.bind(this);
|
||||||
}
|
}
|
||||||
@@ -36,7 +38,9 @@ class BasePageElement extends BaseElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
|
if (!this.hideTitleHead) {
|
||||||
this.appMain.setTitle(this.pageTitle);
|
this.appMain.setTitle(this.pageTitle);
|
||||||
|
}
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,4 +57,5 @@ export default BasePageElement;
|
|||||||
|
|
||||||
export type OptionType = {
|
export type OptionType = {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
hideTitleHead?: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { closest, controller, target } from 'core/utils';
|
|||||||
import { AppLoaderElement } from 'components/app-loader/AppLoaderElement';
|
import { AppLoaderElement } from 'components/app-loader/AppLoaderElement';
|
||||||
import { ToastPortalElement } from 'components/toast-portal/ToastPortalElement';
|
import { ToastPortalElement } from 'components/toast-portal/ToastPortalElement';
|
||||||
import { BasePageElement } from 'common/';
|
import { BasePageElement } from 'common/';
|
||||||
|
import { TransactionsService } from 'services/';
|
||||||
|
|
||||||
@controller('app-main')
|
@controller('app-main')
|
||||||
class AppMainElement extends HTMLElement {
|
class AppMainElement extends HTMLElement {
|
||||||
@@ -12,6 +13,8 @@ class AppMainElement extends HTMLElement {
|
|||||||
public authStore: AuthStore;
|
public authStore: AuthStore;
|
||||||
private httpClient: HttpClient;
|
private httpClient: HttpClient;
|
||||||
public appService: AppService;
|
public appService: AppService;
|
||||||
|
private transactionsService: TransactionsService;
|
||||||
|
private subscriptionChecked: boolean = false;
|
||||||
//public shadow: any;
|
//public shadow: any;
|
||||||
@target appModal: AppModalElement;
|
@target appModal: AppModalElement;
|
||||||
@target mainRoot: AppRootElement;
|
@target mainRoot: AppRootElement;
|
||||||
@@ -36,6 +39,7 @@ class AppMainElement extends HTMLElement {
|
|||||||
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.transactionsService = new TransactionsService(this.appService);
|
||||||
this.authStore = new AuthStore(this, this.appService);
|
this.authStore = new AuthStore(this, this.appService);
|
||||||
this.routerService.setRoutes([
|
this.routerService.setRoutes([
|
||||||
{
|
{
|
||||||
@@ -113,6 +117,8 @@ class AppMainElement extends HTMLElement {
|
|||||||
this.routerService.init();
|
this.routerService.init();
|
||||||
this.addEventListener('mousedown', this.setActiveElement, false);
|
this.addEventListener('mousedown', this.setActiveElement, false);
|
||||||
this.addEventListener('tokenchange', this.closeOffToken);
|
this.addEventListener('tokenchange', this.closeOffToken);
|
||||||
|
this.addEventListener('routechanged', this.checkSubscriptions);
|
||||||
|
this.checkSubscriptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
closeOffToken = () => {
|
closeOffToken = () => {
|
||||||
@@ -121,9 +127,22 @@ class AppMainElement extends HTMLElement {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
checkSubscriptions = async () => {
|
||||||
|
if (this.isAuth && !this.subscriptionChecked) {
|
||||||
|
const checked = await this.transactionsService.check({ sortBy: 'transactionDate asc', rpp: 10 });
|
||||||
|
this.createModal('transaction-check', {
|
||||||
|
data: checked,
|
||||||
|
autoInit: false,
|
||||||
|
});
|
||||||
|
this.subscriptionChecked = true;
|
||||||
|
this.removeEventListener('routechanged', this.checkSubscriptions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
disconnectedCallback = () => {
|
disconnectedCallback = () => {
|
||||||
this.removeEventListener('mousedown', this.setActiveElement);
|
this.removeEventListener('mousedown', this.setActiveElement);
|
||||||
this.removeEventListener('tokenchange', this.closeOffToken);
|
this.removeEventListener('tokenchange', this.closeOffToken);
|
||||||
|
this.removeEventListener('routechanged', this.checkSubscriptions);
|
||||||
};
|
};
|
||||||
|
|
||||||
setActiveElement = (e) => {
|
setActiveElement = (e) => {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class AppPaginationElement extends BaseComponentElement {
|
|||||||
this.update();
|
this.update();
|
||||||
};
|
};
|
||||||
|
|
||||||
executeFetch = async (options?): Promise<void> => {
|
executeFetch = async (options?, fetchFunc = this.fetchFunc): Promise<void> => {
|
||||||
if (!options) {
|
if (!options) {
|
||||||
options = {
|
options = {
|
||||||
rpp: this.rpp || 5,
|
rpp: this.rpp || 5,
|
||||||
@@ -70,7 +70,7 @@ class AppPaginationElement extends BaseComponentElement {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
this.loader?.start?.();
|
this.loader?.start?.();
|
||||||
const response = await this.fetchFunc(options);
|
const response = await fetchFunc(options);
|
||||||
this.loader?.stop?.();
|
this.loader?.stop?.();
|
||||||
this.setItems(response?.items);
|
this.setItems(response?.items);
|
||||||
this.totalItems = response?.totalRecords;
|
this.totalItems = response?.totalRecords;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class InputFieldElement extends BaseComponentElement {
|
|||||||
@attr rules: string;
|
@attr rules: string;
|
||||||
@attr pattern: string;
|
@attr pattern: string;
|
||||||
@attr customAction: string;
|
@attr customAction: string;
|
||||||
|
@attr initialValue: string;
|
||||||
@target main: HTMLElement;
|
@target main: HTMLElement;
|
||||||
@target inp: HTMLElement;
|
@target inp: HTMLElement;
|
||||||
@closest appForm: AppFormElement;
|
@closest appForm: AppFormElement;
|
||||||
@@ -31,6 +32,9 @@ class InputFieldElement extends BaseComponentElement {
|
|||||||
this.validator = new Validator(this, this.appForm, this.rules);
|
this.validator = new Validator(this, this.appForm, this.rules);
|
||||||
this.randId = `${name}${randomId()}`;
|
this.randId = `${name}${randomId()}`;
|
||||||
this.update();
|
this.update();
|
||||||
|
if (this.initialValue) {
|
||||||
|
this._value = this.initialValue;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
attributeChangedCallback() {
|
attributeChangedCallback() {
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class HistoryPageElement extends BasePageElement {
|
|||||||
}
|
}
|
||||||
options.embed = 'TransactionType';
|
options.embed = 'TransactionType';
|
||||||
options.sortBy = 'transactionDate|desc';
|
options.sortBy = 'transactionDate|desc';
|
||||||
|
options.noPending = true;
|
||||||
const response = await this.transactionsService.getAll(options);
|
const response = await this.transactionsService.getAll(options);
|
||||||
return response;
|
return response;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -48,6 +49,12 @@ class HistoryPageElement extends BasePageElement {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
transactionCheck = () => {
|
||||||
|
this.appMain.createModal('transaction-check', {
|
||||||
|
autoInit: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
render = (): TemplateResult =>
|
render = (): TemplateResult =>
|
||||||
HistoryPageElementTemplate({ walletId: this.routerService?.routerState?.data?.walletId });
|
HistoryPageElementTemplate({ walletId: this.routerService?.routerState?.data?.walletId });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
import { html, nothing, TemplateResult } from 'core/utils';
|
import { html, nothing, TemplateResult } from 'core/utils';
|
||||||
|
|
||||||
export default (props): TemplateResult => {
|
export default (props): TemplateResult => {
|
||||||
const { walletId } = props;
|
|
||||||
const renderWallet = () => {
|
|
||||||
if (walletId) {
|
|
||||||
return html`<span>${walletId}</span>`;
|
|
||||||
}
|
|
||||||
return nothing;
|
|
||||||
};
|
|
||||||
|
|
||||||
return html`<div>
|
return html`<div>
|
||||||
${renderWallet()}
|
<div class="wallet-buttons">
|
||||||
|
<button class="btn btn-squared btn-primary" app-action="click:history-page#transactionCheck">
|
||||||
|
Check Transactions
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<app-pagination data-target="history-page.pagination"></app-pagination>
|
<app-pagination data-target="history-page.pagination"></app-pagination>
|
||||||
</div>`;
|
</div>`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,3 +13,4 @@ export * from './wallet-page';
|
|||||||
export * from './subscription-edit';
|
export * from './subscription-edit';
|
||||||
export * from './transaction-edit';
|
export * from './transaction-edit';
|
||||||
export * from './wallet-edit';
|
export * from './wallet-edit';
|
||||||
|
export * from './transaction-check';
|
||||||
|
|||||||
141
src/pages/transaction-check/TransactionCheckElement.ts
Normal file
141
src/pages/transaction-check/TransactionCheckElement.ts
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
import { TemplateResult, controller, target, html } from 'core/utils';
|
||||||
|
import { TransactionsService, TransactionStatusService } from 'services/';
|
||||||
|
import { AppPaginationElement } from 'components/';
|
||||||
|
import { BasePageElement } from 'common/';
|
||||||
|
import { TransactionCheckElementTemplate } from 'pages/transaction-check';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
@controller('transaction-check')
|
||||||
|
class TransactionCheckElement extends BasePageElement {
|
||||||
|
private transactionsService: TransactionsService;
|
||||||
|
private transactionStatusService: TransactionStatusService;
|
||||||
|
transactionStatuses: [];
|
||||||
|
@target pagination: AppPaginationElement;
|
||||||
|
modalData: any = null;
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
title: 'Transaction Check',
|
||||||
|
hideTitleHead: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
elementConnected = async (): Promise<void> => {
|
||||||
|
this.transactionStatusService = new TransactionStatusService(this.appMain?.appService);
|
||||||
|
await this.fetchTransactionStatus();
|
||||||
|
this.transactionsService = new TransactionsService(this.appMain?.appService);
|
||||||
|
this.update();
|
||||||
|
this.modalData = this.getData();
|
||||||
|
this.pagination?.setCustomRenderItem?.(this.renderSubscription)!;
|
||||||
|
this.pagination?.setFetchFunc?.(this.getTransactions, this.modalData?.autoInit)!;
|
||||||
|
if (!this.modalData?.autoInit) {
|
||||||
|
this.pagination?.executeFetch?.(null, () => this.mappedData(this.modalData.data));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mappedData = (data) => {
|
||||||
|
for (const item of data?.items) {
|
||||||
|
item.formattedDate = dayjs(item.transactionDate).format('YYYY-MM-DD');
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
renderSubscription = (item) => {
|
||||||
|
const renderEditActions = () => html`<div class="d--flex">
|
||||||
|
<button class="btn btn-rounded btn-red" @click="${() => this.transactionEdit(item)}}">Cancel</button>
|
||||||
|
<button class="btn btn-rounded btn-primary" @click="${() => this.transactionEditSave(item)}}">Save</button>
|
||||||
|
</div>`;
|
||||||
|
const renderRegularActions = () => html`<div class="d--flex">
|
||||||
|
<button class="btn btn-rounded btn-primary" @click="${() => this.transactionEdit(item)}}">Edit</button>
|
||||||
|
<button class="btn btn-rounded btn-green" @click="${() => this.transactionEditComplete(item)}}">Complete</button>
|
||||||
|
</div>`;
|
||||||
|
return html`<tr class="col-checks">
|
||||||
|
${!item.isEdit
|
||||||
|
? html`<td class="--left">${dayjs(item.transactionDate).format("MMM DD 'YY")}</td>`
|
||||||
|
: html`<input-field
|
||||||
|
data-type="date"
|
||||||
|
data-name="${item.name}"
|
||||||
|
data-targets="transaction-check.inputs"
|
||||||
|
data-initial-value="${item.formattedDate}"
|
||||||
|
@change="${(e) => (item.newTransactionDate = e.target.value)}"
|
||||||
|
></input-field>`}
|
||||||
|
<td class="--left">${item.description}</td>
|
||||||
|
<td class="balance-cell --right">
|
||||||
|
<span
|
||||||
|
class="balance ${item.amount > 0 && item?.transactionType?.type != 'expense' ? '--positive' : '--negative'}"
|
||||||
|
>
|
||||||
|
${item?.transactionType?.type == 'expense' ? '- ' : ''}
|
||||||
|
${Number(item.amount).toLocaleString('en-US', {
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
<span class="currency">(${item.currency ? item.currency : 'USD'})</span>
|
||||||
|
</td>
|
||||||
|
<td class="--right">${item.isEdit ? renderEditActions() : renderRegularActions()}</td>
|
||||||
|
</tr>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchTransactionStatus = async () => {
|
||||||
|
this.transactionStatuses = await this.transactionStatusService.getAll();
|
||||||
|
};
|
||||||
|
|
||||||
|
transactionEdit = (item) => {
|
||||||
|
item.isEdit = !item.isEdit;
|
||||||
|
this.pagination?.update();
|
||||||
|
};
|
||||||
|
|
||||||
|
transactionEditSave = async (item) => {
|
||||||
|
const resource = {
|
||||||
|
transactionDate: dayjs(item.newTransactionDate || item.transactionDate)
|
||||||
|
.utc(true)
|
||||||
|
.format(),
|
||||||
|
};
|
||||||
|
await this.updateTransaction(item.id, resource);
|
||||||
|
};
|
||||||
|
|
||||||
|
transactionEditComplete = async (item) => {
|
||||||
|
const completedStatusId = (this.transactionStatuses?.find((item: any) => item.status === 'completed') as any)?.id!;
|
||||||
|
if (completedStatusId) {
|
||||||
|
const resource = {
|
||||||
|
transactionStatusId: completedStatusId,
|
||||||
|
};
|
||||||
|
await this.updateTransaction(item.id, resource);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
transactionUpdated = () => {
|
||||||
|
this.pagination?.executeFetch();
|
||||||
|
};
|
||||||
|
|
||||||
|
getTransactions = async (options): Promise<any> => {
|
||||||
|
try {
|
||||||
|
options.embed = 'TransactionType';
|
||||||
|
options.sortBy = 'transactionDate|asc';
|
||||||
|
const response = await this.transactionsService.check(options);
|
||||||
|
return this.mappedData(response);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
updateTransaction = async (id, resource): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const response = await this.transactionsService.put(id, resource);
|
||||||
|
return response;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
const options = {
|
||||||
|
page: this.pagination?.page || 1,
|
||||||
|
rpp: this.pagination?.rpp || 10,
|
||||||
|
};
|
||||||
|
this.pagination?.executeFetch(options);
|
||||||
|
this.appMain?.triggerTransactionUpdate?.()!;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render = (): TemplateResult =>
|
||||||
|
TransactionCheckElementTemplate({ walletId: this.routerService?.routerState?.data?.walletId });
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { TransactionCheckElement };
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { html, nothing, TemplateResult } from 'core/utils';
|
||||||
|
|
||||||
|
export default (props): TemplateResult => {
|
||||||
|
return html`<div>
|
||||||
|
<app-pagination data-target="transaction-check.pagination"></app-pagination>
|
||||||
|
</div>`;
|
||||||
|
};
|
||||||
2
src/pages/transaction-check/index.ts
Normal file
2
src/pages/transaction-check/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export { default as TransactionCheckElementTemplate } from './TransactionCheckElementTemplate';
|
||||||
|
export * from './TransactionCheckElement';
|
||||||
@@ -70,6 +70,7 @@ class WalletPageElement extends BasePageElement {
|
|||||||
}
|
}
|
||||||
options.embed = 'TransactionType';
|
options.embed = 'TransactionType';
|
||||||
options.sortBy = 'transactionDate|desc';
|
options.sortBy = 'transactionDate|desc';
|
||||||
|
options.noPending = true;
|
||||||
const response = await this.transactionsService.getAll(options);
|
const response = await this.transactionsService.getAll(options);
|
||||||
return response;
|
return response;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
9
src/services/TransactionStatusService.ts
Normal file
9
src/services/TransactionStatusService.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { AppService, BaseService } from 'core/services';
|
||||||
|
|
||||||
|
class TransactionStatusService extends BaseService {
|
||||||
|
constructor(appService: AppService) {
|
||||||
|
super('/transaction-status', appService);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TransactionStatusService;
|
||||||
@@ -4,6 +4,10 @@ class TransactionsService extends BaseService {
|
|||||||
constructor(appService: AppService) {
|
constructor(appService: AppService) {
|
||||||
super('/transaction', appService);
|
super('/transaction', appService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check = (params?: Object, headers?: HeadersInit) => {
|
||||||
|
return this.appService.get(this.endpoint + '/check', params, headers);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TransactionsService;
|
export default TransactionsService;
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ export { default as TransactionsService } from './TransactionsService';
|
|||||||
export { default as TransactionTypeService } from './TransactionTypeService';
|
export { default as TransactionTypeService } from './TransactionTypeService';
|
||||||
export { default as SubscriptionService } from './SubscriptionService';
|
export { default as SubscriptionService } from './SubscriptionService';
|
||||||
export { default as SubscriptionTypeService } from './SubscriptionTypeService';
|
export { default as SubscriptionTypeService } from './SubscriptionTypeService';
|
||||||
|
export { default as TransactionStatusService } from './TransactionStatusService';
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ app-pagination {
|
|||||||
&.col-transactions {
|
&.col-transactions {
|
||||||
grid-template-columns: 1fr 10fr 1fr 1fr;
|
grid-template-columns: 1fr 10fr 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
&.col-checks {
|
||||||
|
grid-template-columns: 3fr 8fr 2fr 3fr;
|
||||||
|
}
|
||||||
&.col-wallet {
|
&.col-wallet {
|
||||||
grid-template-columns: 9fr 1fr;
|
grid-template-columns: 9fr 1fr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,3 +64,13 @@ app-main {
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.d {
|
||||||
|
&--flex {
|
||||||
|
display: flex;
|
||||||
|
> * {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user