FRONTEND-467: new look for deposits filters (#420)

This commit is contained in:
Aleksandra Usacheva 2021-04-06 11:06:33 +03:00 committed by GitHub
parent 2659b40a66
commit 783eb16d8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 774 additions and 240 deletions

View File

@ -5,7 +5,10 @@
<dsh-row-label fxFlex="25">
<dsh-status [color]="deposit.status | depositStatusColor">{{ deposit.status | depositStatusName }}</dsh-status>
</dsh-row-label>
<dsh-row-label fxFlex="25">{{ deposit.createdAt | date: 'dd MMMM yyyy, HH:mm' }}</dsh-row-label>
<dsh-row-label fxFlex="25">
<span fxHide.lt-md>{{ deposit.createdAt | date: 'dd MMMM yyyy, HH:mm' }}</span>
<span fxHide.gt-sm>{{ deposit.createdAt | date: 'dd.MM.yyyy, HH:mm' }}</span>
</dsh-row-label>
<dsh-row-label fxFlex="25">
{{ deposit.wallet | walletDetails }}
</dsh-row-label>

View File

@ -0,0 +1,30 @@
import { NgModule } from '@angular/core';
import { FlexModule } from '@angular/flex-layout';
import { MatDividerModule } from '@angular/material/divider';
import { TranslocoModule } from '@ngneat/transloco';
import { DialogModule } from '@dsh/app/shared/components/dialog';
import { ButtonModule } from '@dsh/components/buttons';
import { AdditionalFiltersService } from './additional-filters.service';
import { DialogFiltersComponent } from './components/dialog-filters/dialog-filters.component';
import { DepositStatusFilterModule } from './deposit-status-filter/deposit-status-filter.module';
import { DepositSumFilterModule } from './deposit-sum-filter';
import { MainFiltersModule } from './main-filters';
@NgModule({
declarations: [DialogFiltersComponent],
entryComponents: [DialogFiltersComponent],
providers: [AdditionalFiltersService],
imports: [
DialogModule,
FlexModule,
MatDividerModule,
MainFiltersModule,
DepositSumFilterModule,
TranslocoModule,
DepositStatusFilterModule,
ButtonModule,
],
})
export class AdditionalFiltersModule {}

View File

@ -0,0 +1,25 @@
import { Inject, Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Observable } from 'rxjs';
import { take } from 'rxjs/operators';
import { DialogConfig, DIALOG_CONFIG } from '@dsh/app/sections/tokens';
import { DialogFiltersComponent } from './components/dialog-filters/dialog-filters.component';
import { AdditionalFilters } from './types/additional-filters';
@Injectable()
export class AdditionalFiltersService {
constructor(@Inject(DIALOG_CONFIG) private dialogConfig: DialogConfig, private dialog: MatDialog) {}
openFiltersDialog(data: AdditionalFilters): Observable<AdditionalFilters> {
return this.dialog
.open<DialogFiltersComponent, AdditionalFilters>(DialogFiltersComponent, {
panelClass: 'fill-bleed-dialog',
...this.dialogConfig.medium,
data,
})
.afterClosed()
.pipe(take(1));
}
}

View File

@ -0,0 +1,21 @@
<ng-container *transloco="let t; read: 'filters'">
<dsh-base-dialog hasDivider [title]="t('additionalFilters')" (cancel)="close()">
<div class="dialog-filters-main">
<dsh-main-filters [form]="mainFiltersGroup"></dsh-main-filters>
<mat-divider></mat-divider>
<dsh-deposit-status-filter [control]="statusFilterControl"></dsh-deposit-status-filter>
<mat-divider></mat-divider>
<dsh-deposit-sum-filter [form]="depositSumFiltersGroup"></dsh-deposit-sum-filter>
</div>
<dshBaseDialogActions>
<div fxLayout="row" fxLayoutAlign="space-between center">
<button dsh-stroked-button color="accent" (click)="clear()">
{{ t('clearParams') }}
</button>
<button dsh-button color="accent" (click)="confirm()">
{{ t('save') }}
</button>
</div>
</dshBaseDialogActions>
</dsh-base-dialog>
</ng-container>

View File

@ -0,0 +1,24 @@
:host {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.dialog-filters {
&-main {
display: flex;
flex-direction: column;
& > * {
margin-bottom: 24px;
&:last-child {
margin-bottom: 0;
}
}
& > dsh-main-filters {
margin-bottom: 10px;
}
}
}

View File

@ -0,0 +1,125 @@
import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { AbstractControl, FormBuilder, FormControl, FormGroup } from '@ngneat/reactive-forms';
import { getAbstractControl } from '@dsh/app/shared/utils';
import { formatMajorAmountToStr, getAmountNum } from '@dsh/app/shared/utils/amount-formatters';
import { removeDictEmptyFields } from '@dsh/utils';
import { DepositStatusFilterValue } from '../../deposit-status-filter/types/deposit-status-filter-value';
import { depositStatusValidator } from '../../deposit-status-filter/validators/deposit-status-validator';
import { DepositSumFilter } from '../../deposit-sum-filter';
import { MainFilters } from '../../main-filters';
import { AdditionalFilters } from '../../types/additional-filters';
import { AdditionalFiltersForm } from '../../types/additional-filters-form';
@Component({
selector: 'dsh-dialog-filters',
templateUrl: 'dialog-filters.component.html',
styleUrls: ['dialog-filters.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DialogFiltersComponent implements OnInit {
form: FormGroup<AdditionalFiltersForm> = this.formBuilder.group({
main: this.formBuilder.group<MainFilters>({
depositID: [''],
walletID: [''],
identityID: [''],
sourceID: [''],
}),
depositStatus: [null, depositStatusValidator],
depositSum: this.formBuilder.group<DepositSumFilter>({
min: [''],
max: [''],
}),
});
get mainFiltersGroup(): FormGroup<MainFilters> {
return getAbstractControl<FormGroup<MainFilters>>(this.form, 'main');
}
get statusFilterControl(): FormControl<DepositStatusFilterValue> {
return getAbstractControl<FormControl<DepositStatusFilterValue>>(this.form, 'depositStatus');
}
get depositSumFiltersGroup(): FormGroup<DepositSumFilter> {
return getAbstractControl<FormGroup<DepositSumFilter>>(this.form, 'depositSum');
}
constructor(
@Inject(MAT_DIALOG_DATA) private data: AdditionalFilters,
private dialogRef: MatDialogRef<DialogFiltersComponent, AdditionalFilters>,
private formBuilder: FormBuilder
) {}
ngOnInit(): void {
this.initForm();
}
clear(): void {
this.resetFiltersData();
}
close(): void {
this.dialogRef.close(this.data);
}
confirm(): void {
this.dialogRef.close(this.getFiltersData());
}
private initForm(): void {
this.form.setValue(this.getInitFormValues());
}
private getInitFormValues(): AdditionalFiltersForm {
const {
depositID = '',
walletID = '',
identityID = '',
sourceID = '',
depositStatus = null,
depositAmountFrom = null,
depositAmountTo = null,
} = this.data;
return {
main: {
depositID,
walletID,
identityID,
sourceID,
},
depositStatus,
depositSum: {
min: formatMajorAmountToStr(depositAmountFrom),
max: formatMajorAmountToStr(depositAmountTo),
},
};
}
private getFiltersData(): AdditionalFilters {
const { min, max } = this.extractGroupValidFields(this.depositSumFiltersGroup);
return removeDictEmptyFields({
...this.extractGroupValidFields(this.mainFiltersGroup),
...removeDictEmptyFields({
depositAmountFrom: getAmountNum(String(min)),
depositAmountTo: getAmountNum(String(max)),
}),
depositStatus: this.statusFilterControl.value,
});
}
private extractGroupValidFields<T>(group: FormGroup<T>): Partial<T> {
return Object.entries(group.controls).reduce((acc: Partial<T>, [key, control]: [string, AbstractControl]) => {
if (control.valid) {
acc[key] = control.value;
}
return acc;
}, {});
}
private resetFiltersData(): void {
this.form.reset();
}
}

View File

@ -0,0 +1,3 @@
import { DepositStatus } from '@dsh/api-codegen/wallet-api/swagger-codegen/model/depositStatus';
export const DEPOSIT_STATUSES_LIST: DepositStatus.StatusEnum[] = ['Succeeded', 'Failed', 'Pending'];

View File

@ -0,0 +1,12 @@
<ng-container *transloco="let t; scope: 'deposits'; read: 'deposits.filter'">
<div fxLayout="column" fxLayoutGap="24px">
<div class="dsh-title" fxFlex>{{ t('depositStatus') }}</div>
<dsh-expandable-radio-group fxFlex [control]="control" [choices]="statuses" anyResponse>
<ng-container *ngFor="let status of statuses">
<ng-template [dshExpandableRadioGroupItem]="status">
{{ status | depositStatusName }}
</ng-template>
</ng-container>
</dsh-expandable-radio-group>
</div>
</ng-container>

View File

@ -0,0 +1,18 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { FormControl } from '@ngneat/reactive-forms';
import { DepositStatus } from '@dsh/api-codegen/wallet-api/swagger-codegen/model/depositStatus';
import { DEPOSIT_STATUSES_LIST } from './consts';
import { DepositStatusFilterValue } from './types/deposit-status-filter-value';
@Component({
selector: 'dsh-deposit-status-filter',
templateUrl: './deposit-status-filter.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DepositStatusFilterComponent {
@Input() control: FormControl<DepositStatusFilterValue>;
statuses: DepositStatus.StatusEnum[] = DEPOSIT_STATUSES_LIST;
}

View File

@ -0,0 +1,16 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FlexModule } from '@angular/flex-layout';
import { TranslocoModule } from '@ngneat/transloco';
import { ApiModelTypesModule } from '@dsh/app/shared';
import { RadioButtonsModule } from '@dsh/app/shared/components/radio-buttons';
import { DepositStatusFilterComponent } from './deposit-status-filter.component';
@NgModule({
declarations: [DepositStatusFilterComponent],
imports: [FlexModule, CommonModule, TranslocoModule, RadioButtonsModule, ApiModelTypesModule],
exports: [DepositStatusFilterComponent],
})
export class DepositStatusFilterModule {}

View File

@ -0,0 +1,3 @@
import { DepositStatus } from '@dsh/api-codegen/wallet-api/swagger-codegen/model/depositStatus';
export type DepositStatusFilterValue = DepositStatus.StatusEnum | null;

View File

@ -0,0 +1,16 @@
import { FormControl, ValidatorFn } from '@ngneat/reactive-forms';
import isNil from 'lodash.isnil';
import { DEPOSIT_STATUSES_LIST } from '../consts';
import { DepositStatusFilterValue } from '../types/deposit-status-filter-value';
export const depositStatusValidator: ValidatorFn = (control: FormControl<DepositStatusFilterValue>) => {
const value = control.value;
const isValid = isNil(value) || DEPOSIT_STATUSES_LIST.includes(value);
return isValid
? null
: {
depositStatus: true,
};
};

View File

@ -0,0 +1,15 @@
<ng-container *transloco="let t; scope: 'deposits'; read: 'deposits.filter'">
<div fxLayout="column" fxLayoutGap="24px">
<div class="dsh-title" fxFlex>{{ t('depositAmount') }}</div>
<form [formGroup]="form" fxLayout="row" fxLayoutGap="24px">
<mat-form-field fxFlex>
<mat-label>{{ t('depositAmountFrom') }}</mat-label>
<dsh-format-input format="amount" formControlName="min"></dsh-format-input>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ t('depositAmountTo') }}</mat-label>
<dsh-format-input format="amount" formControlName="max"></dsh-format-input>
</mat-form-field>
</form>
</div>
</ng-container>

View File

@ -0,0 +1,13 @@
import { Component, Input } from '@angular/core';
import { FormGroup } from '@ngneat/reactive-forms';
import { DepositSumFilter } from './types/deposit-sum-filter';
@Component({
selector: 'dsh-deposit-sum-filter',
templateUrl: './deposit-sum-filter.component.html',
styleUrls: ['./deposit-sum-filter.component.scss'],
})
export class DepositSumFilterComponent {
@Input() form: FormGroup<DepositSumFilter>;
}

View File

@ -0,0 +1,24 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FlexLayoutModule } from '@angular/flex-layout';
import { ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { TranslocoModule } from '@ngneat/transloco';
import { FormatInputModule } from '@dsh/components/form-controls';
import { DepositSumFilterComponent } from './deposit-sum-filter.component';
@NgModule({
imports: [
CommonModule,
ReactiveFormsModule,
FormatInputModule,
FlexLayoutModule,
TranslocoModule,
MatFormFieldModule,
],
declarations: [DepositSumFilterComponent],
exports: [DepositSumFilterComponent],
})
export class DepositSumFilterModule {}

View File

@ -0,0 +1,3 @@
export * from './deposit-sum-filter.module';
export * from './deposit-sum-filter.component';
export * from './types/deposit-sum-filter';

View File

@ -0,0 +1,5 @@
export interface DepositSumFilter {
// format input gets string and should return number but it's not always happening
min: string | number;
max: string | number;
}

View File

@ -0,0 +1 @@
export * from './additional-filters.module';

View File

@ -0,0 +1,3 @@
export * from './main-filters.module';
export * from './main-filters.component';
export * from './types/main-filters';

View File

@ -0,0 +1,24 @@
<ng-container *transloco="let t; scope: 'deposits'; read: 'deposits.filter'">
<form [formGroup]="form">
<div fxLayout="row" fxLayoutAlign="space-between" fxLayoutGap="24px">
<mat-form-field fxFlex>
<mat-label>{{ t('depositID') }}</mat-label>
<input aria-label="depositID" matInput type="text" formControlName="depositID" />
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ t('walletID') }}</mat-label>
<input aria-label="walletID" matInput type="text" formControlName="walletID" />
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutAlign="space-between" fxLayoutGap="24px">
<mat-form-field fxFlex>
<mat-label>{{ t('identityID') }}</mat-label>
<input aria-label="identityID" matInput type="text" formControlName="identityID" />
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ t('sourceID') }}</mat-label>
<input aria-label="sourceID" matInput type="text" formControlName="sourceID" />
</mat-form-field>
</div>
</form>
</ng-container>

View File

@ -0,0 +1,13 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { FormGroup } from '@ngneat/reactive-forms';
import { MainFilters } from './types/main-filters';
@Component({
selector: 'dsh-main-filters',
templateUrl: './main-filters.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MainFiltersComponent {
@Input() form: FormGroup<MainFilters>;
}

View File

@ -0,0 +1,16 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FlexLayoutModule } from '@angular/flex-layout';
import { ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { TranslocoModule } from '@ngneat/transloco';
import { MainFiltersComponent } from './main-filters.component';
@NgModule({
imports: [CommonModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule, FlexLayoutModule, TranslocoModule],
declarations: [MainFiltersComponent],
exports: [MainFiltersComponent],
})
export class MainFiltersModule {}

View File

@ -0,0 +1,6 @@
export interface MainFilters {
depositID?: string;
walletID?: string;
identityID?: string;
sourceID?: string;
}

View File

@ -0,0 +1,9 @@
import { DepositStatusFilterValue } from '../deposit-status-filter/types/deposit-status-filter-value';
import { DepositSumFilter } from '../deposit-sum-filter';
import { MainFilters } from '../main-filters';
export interface AdditionalFiltersForm {
main: MainFilters;
depositStatus: DepositStatusFilterValue;
depositSum: DepositSumFilter;
}

View File

@ -0,0 +1,9 @@
import { DepositStatus } from '@dsh/api-codegen/wallet-api';
import { MainFilters } from '../main-filters';
import { DepositAmountFilterData } from './deposit-amount-filter-data';
export type AdditionalFilters = Partial<MainFilters> &
Partial<DepositAmountFilterData> & {
depositStatus?: DepositStatus.StatusEnum;
};

View File

@ -0,0 +1,4 @@
export interface DepositAmountFilterData {
depositAmountFrom: number;
depositAmountTo: number;
}

View File

@ -0,0 +1,13 @@
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<ng-container *ngIf="filtersData$ | async as filters">
<dsh-daterange-filter
[selected]="filters.daterange"
(selectedChange)="dateRangeChange($event)"
></dsh-daterange-filter>
<ng-container *transloco="let t; read: 'filters'">
<dsh-filter-button [active]="isAdditionalFilterApplied" (click)="openFiltersDialog()">
{{ t('additionalFilters') }}
</dsh-filter-button>
</ng-container>
</ng-container>
</div>

View File

@ -0,0 +1,70 @@
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import isEmpty from 'lodash.isempty';
import { Observable } from 'rxjs';
import { map, switchMap, take } from 'rxjs/operators';
import { Daterange } from '@dsh/pipes/daterange';
import { AdditionalFiltersService } from './additional-filters/additional-filters.service';
import { AdditionalFilters } from './additional-filters/types/additional-filters';
import { DepositsFiltersStoreService } from './services/deposits-filters-store/deposits-filters-store.service';
import { DepositsFiltersService } from './services/deposits-filters/deposits-filters.service';
import { DepositsFiltersData } from './types/deposits-filters-data';
@UntilDestroy()
@Component({
templateUrl: 'deposits-filters.component.html',
selector: 'dsh-deposits-filters',
providers: [DepositsFiltersService, DepositsFiltersStoreService],
})
export class DepositsFiltersComponent implements OnInit {
@Output() filtersChanged = new EventEmitter<DepositsFiltersData>();
filtersData$: Observable<DepositsFiltersData> = this.filtersHandler.filtersData$;
isAdditionalFilterApplied: boolean;
constructor(private filtersHandler: DepositsFiltersService, private additionalFilters: AdditionalFiltersService) {}
ngOnInit(): void {
this.filtersData$.pipe(untilDestroyed(this)).subscribe((filtersData: DepositsFiltersData) => {
this.filtersChanged.emit(filtersData);
const { additional = {} } = filtersData;
this.updateAdditionalFiltersStatus(additional);
});
}
dateRangeChange(dateRange: Daterange): void {
this.updateFilters({ daterange: dateRange });
}
openFiltersDialog(): void {
this.filtersData$
.pipe(
take(1),
map((filtersData: DepositsFiltersData) => filtersData.additional ?? {}),
switchMap((filters: AdditionalFilters) => this.additionalFilters.openFiltersDialog(filters)),
untilDestroyed(this)
)
.subscribe((filters: AdditionalFilters) => {
this.updateAdditionalFiltersValues(filters);
});
}
private updateFilters(change: Partial<DepositsFiltersData>): void {
this.filtersHandler.changeFilters({
...change,
});
}
private updateAdditionalFiltersValues(additional: AdditionalFilters): void {
this.updateFilters({
additional,
});
}
private updateAdditionalFiltersStatus(additional: AdditionalFilters): void {
this.isAdditionalFilterApplied = !isEmpty(additional);
}
}

View File

@ -0,0 +1,24 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FlexModule } from '@angular/flex-layout';
import { TranslocoModule } from '@ngneat/transloco';
import { DaterangeManagerModule } from '@dsh/app/shared/services/date-range-manager';
import { FiltersModule } from '@dsh/components/filters';
import { AdditionalFiltersModule } from './additional-filters';
import { DepositsFiltersComponent } from './deposits-filters.component';
@NgModule({
imports: [
DaterangeManagerModule,
FlexModule,
CommonModule,
FiltersModule,
TranslocoModule,
AdditionalFiltersModule,
],
declarations: [DepositsFiltersComponent],
exports: [DepositsFiltersComponent],
})
export class DepositsFiltersModule {}

View File

@ -0,0 +1 @@
export * from './deposits-filters.module';

View File

@ -0,0 +1,60 @@
import { Injectable } from '@angular/core';
import { ActivatedRoute, Params, Router } from '@angular/router';
import isNil from 'lodash.isnil';
import { QueryParamsStore } from '@dsh/app/shared/services';
import { DaterangeManagerService } from '@dsh/app/shared/services/date-range-manager';
import { Daterange } from '@dsh/pipes/daterange';
import { removeDictEmptyFields } from '@dsh/utils';
import { DepositsFiltersData } from '../../types/deposits-filters-data';
import { formatDepositAmountDataToParams } from '../../utils/format-deposit-amount-data-to-params';
import { getDepositAmountDataFromParams } from '../../utils/get-deposit-amount-data-from-params';
@Injectable()
export class DepositsFiltersStoreService extends QueryParamsStore<DepositsFiltersData> {
constructor(
private daterangeManager: DaterangeManagerService,
protected router: Router,
protected route: ActivatedRoute
) {
super(router, route);
}
mapToData(params: Params): Partial<DepositsFiltersData> {
const { fromTime, toTime, depositAmountFrom, depositAmountTo, ...restParams } = params;
return removeDictEmptyFields({
daterange: this.formatDaterange(fromTime, toTime),
additional: {
...getDepositAmountDataFromParams({
depositAmountTo,
depositAmountFrom,
}),
...restParams,
},
});
}
mapToParams({ daterange, additional, ...restData }: DepositsFiltersData): Params {
const { begin: fromTime, end: toTime } = this.daterangeManager.serializeDateRange(daterange);
const { depositAmountFrom, depositAmountTo, ...restAdditional } = additional ?? {};
return removeDictEmptyFields({
fromTime,
toTime,
...formatDepositAmountDataToParams({
depositAmountFrom,
depositAmountTo,
}),
...restAdditional,
...restData,
});
}
private formatDaterange(fromTime: string | undefined, toTime: string | undefined): Daterange | null {
return isNil(fromTime) || isNil(toTime)
? null
: this.daterangeManager.deserializeDateRange({ begin: fromTime, end: toTime });
}
}

View File

@ -0,0 +1,57 @@
import { Injectable } from '@angular/core';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Observable, ReplaySubject } from 'rxjs';
import { map, withLatestFrom } from 'rxjs/operators';
import { DaterangeManagerService } from '@dsh/app/shared/services/date-range-manager';
import { DepositsFiltersData } from '../../types/deposits-filters-data';
import { DepositsFiltersStoreService } from '../deposits-filters-store/deposits-filters-store.service';
@UntilDestroy()
@Injectable()
export class DepositsFiltersService {
filtersData$: Observable<DepositsFiltersData>;
private filtersChange$ = new ReplaySubject<Partial<DepositsFiltersData>>(1);
constructor(
private daterangeManager: DaterangeManagerService,
private filtersParamsStore: DepositsFiltersStoreService
) {
this.initFiltersData();
this.initUpdatesData();
}
changeFilters(dataChange: Partial<DepositsFiltersData>): void {
this.filtersChange$.next(dataChange);
}
private initFiltersData(): void {
this.filtersData$ = this.filtersParamsStore.data$.pipe(
map((storeData: Partial<DepositsFiltersData>) => {
return {
daterange: this.daterangeManager.defaultDaterange,
...storeData,
};
})
);
}
private initUpdatesData(): void {
this.filtersChange$
.pipe(
withLatestFrom(this.filtersData$),
map(([dataChange, filtersData]: [Partial<DepositsFiltersData>, DepositsFiltersData]) => {
return {
...filtersData,
...dataChange,
};
}),
untilDestroyed(this)
)
.subscribe((updatedData: DepositsFiltersData) => {
this.filtersParamsStore.preserve(updatedData);
});
}
}

View File

@ -0,0 +1,4 @@
export interface DepositAmountParams {
depositAmountFrom: string;
depositAmountTo: string;
}

View File

@ -0,0 +1,8 @@
import { Daterange } from '@dsh/pipes/daterange';
import { AdditionalFilters } from '../additional-filters/types/additional-filters';
export interface DepositsFiltersData {
daterange: Daterange;
additional?: AdditionalFilters;
}

View File

@ -0,0 +1,16 @@
import { isNumber } from '@dsh/app/shared/utils';
import { toMinor } from '@dsh/utils';
import { DepositAmountFilterData } from '../additional-filters/types/deposit-amount-filter-data';
import { DepositAmountParams } from '../types/deposit-amount-params';
export function formatDepositAmountDataToParams({
depositAmountFrom,
depositAmountTo,
}: Partial<DepositAmountFilterData>): DepositAmountParams {
return {
depositAmountFrom:
isNumber(depositAmountFrom) && !isNaN(depositAmountFrom) ? String(toMinor(depositAmountFrom)) : null,
depositAmountTo: isNumber(depositAmountTo) && !isNaN(depositAmountTo) ? String(toMinor(depositAmountTo)) : null,
};
}

View File

@ -0,0 +1,19 @@
import isNil from 'lodash.isnil';
import { removeDictEmptyFields, toMajor } from '@dsh/utils';
import { DepositAmountFilterData } from '../additional-filters/types/deposit-amount-filter-data';
import { DepositAmountParams } from '../types/deposit-amount-params';
export function getDepositAmountDataFromParams({
depositAmountFrom,
depositAmountTo,
}: Partial<DepositAmountParams>): Partial<DepositAmountFilterData> {
const amountFromNum = Number(depositAmountFrom);
const amountToNum = Number(depositAmountTo);
return removeDictEmptyFields({
depositAmountFrom: isNil(depositAmountFrom) || isNaN(amountFromNum) ? null : toMajor(amountFromNum),
depositAmountTo: isNil(depositAmountTo) || isNaN(amountToNum) ? null : toMajor(amountToNum),
});
}

View File

@ -1,13 +1,17 @@
<div class="dsh-deposits" fxLayout="column" fxLayoutGap="16px">
<div class="dsh-deposits" fxLayout="column" fxLayoutGap="48px">
<h1 class="mat-display-1" *transloco="let t; scope: 'deposits'; read: 'deposits'">{{ t('title') }}</h1>
<dsh-search-form></dsh-search-form>
<dsh-last-updated [lastUpdated]="lastUpdated$ | async" (update)="refreshList()"></dsh-last-updated>
<dsh-deposit-panels
[list]="deposits$ | async"
[isLoading]="isLoading$ | async"
[hasMore]="hasMore$ | async"
[expandedId]="expandedId$ | async"
(showMore)="requestNextPage()"
(expandedIdChanged)="expandedIdChange($event)"
></dsh-deposit-panels>
<div fxLayout="column" fxLayoutGap="24px">
<dsh-deposits-filters (filtersChanged)="filtersChanged($event)"></dsh-deposits-filters>
<div fxLayout="column" fxLayoutGap="16px">
<dsh-last-updated [lastUpdated]="lastUpdated$ | async" (update)="refreshList()"></dsh-last-updated>
<dsh-deposit-panels
[list]="deposits$ | async"
[isLoading]="isLoading$ | async"
[hasMore]="hasMore$ | async"
[expandedId]="expandedId$ | async"
(showMore)="requestNextPage()"
(expandedIdChanged)="expandedIdChange($event)"
></dsh-deposit-panels>
</div>
</div>
</div>

View File

@ -1,9 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { DepositsSearchParams } from '@dsh/api';
import { ErrorService } from '@dsh/app/shared';
import { DepositsFiltersData } from './deposits-filters/types/deposits-filters-data';
import { DepositsExpandedIdManagerService } from './services/deposits-expanded-id-manager.service';
import { FetchDepositsService } from './services/fetch-deposits.service';
@ -38,7 +38,7 @@ export class DepositsComponent implements OnInit {
this.fetchDepositsService.fetchMore();
}
filtersChanged(filtersData: DepositsSearchParams): void {
filtersChanged(filtersData: DepositsFiltersData): void {
this.requestList(filtersData);
}
@ -50,7 +50,23 @@ export class DepositsComponent implements OnInit {
this.depositsExpandedIdManagerService.expandedIdChange(id);
}
private requestList(filtersData: DepositsSearchParams = null): void {
this.fetchDepositsService.search(filtersData);
private requestList(filtersData: DepositsFiltersData = null): void {
const {
daterange: { begin, end },
additional,
} = filtersData;
const { depositAmountTo, depositAmountFrom, depositID, walletID, identityID, sourceID, depositStatus } =
additional || {};
this.fetchDepositsService.search({
fromTime: begin.utc().format(),
toTime: end.utc().format(),
walletID,
identityID,
sourceID,
depositID,
status: depositStatus,
amountFrom: depositAmountFrom,
amountTo: depositAmountTo,
});
}
}

View File

@ -17,9 +17,9 @@ import { ShowMorePanelModule } from '@dsh/components/show-more-panel';
import { DEPOSITS_UPDATE_DELAY, UPDATE_DELAY_TOKEN } from './consts';
import { DepositPanelsModule } from './deposit-panels';
import { DepositsFiltersModule } from './deposits-filters';
import { DepositsRoutingModule } from './deposits-routing.module';
import { DepositsComponent } from './deposits.component';
import { SearchFormComponent } from './search-form';
@NgModule({
imports: [
@ -39,8 +39,9 @@ import { SearchFormComponent } from './search-form';
ButtonModule,
MatInputModule,
IndicatorsModule,
DepositsFiltersModule,
],
declarations: [DepositsComponent, SearchFormComponent],
declarations: [DepositsComponent],
providers: [
DepositsService,
{ provide: SEARCH_LIMIT, useValue: 10 },

View File

@ -1,14 +0,0 @@
import { DepositStatus } from '@dsh/api-codegen/wallet-api/swagger-codegen';
import { Range } from '@dsh/components/form-controls';
export interface FormParams {
date: Range;
walletID?: string;
identityID?: string;
depositID?: string;
sourceID?: string;
status?: DepositStatus.StatusEnum;
amountFrom?: number;
amountTo?: number;
currencyID?: string;
}

View File

@ -1 +0,0 @@
export * from './search-form.component';

View File

@ -1,14 +0,0 @@
import { DepositStatus } from '@dsh/api-codegen/wallet-api/swagger-codegen';
export interface QueryParams {
fromTime: string;
toTime: string;
walletID?: string;
identityID?: string;
depositID?: string;
sourceID?: string;
status?: DepositStatus.StatusEnum;
amountFrom?: number;
amountTo?: number;
currencyID?: string;
}

View File

@ -1,65 +0,0 @@
<dsh-float-panel
*transloco="let t; scope: 'deposits'; read: 'deposits.filter'"
[formGroup]="form"
[(expanded)]="expanded"
>
<ng-container *transloco="let c">
<dsh-justify-wrapper fxLayout fxLayout.sm="column" fxLayoutGap="20px">
<dsh-range-datepicker fxFlex formControlName="date"></dsh-range-datepicker>
<mat-form-field fxFlex>
<mat-label>{{ t('depositStatus') }}</mat-label>
<mat-select formControlName="status">
<mat-option>
{{ c('any') }}
</mat-option>
<mat-option *ngFor="let status of depositStatuses" [value]="status">
<span *transloco="let depositStatus; read: 'depositStatus'">{{ depositStatus(status) }}</span>
</mat-option>
</mat-select>
</mat-form-field>
</dsh-justify-wrapper>
<dsh-float-panel-actions>
<button dsh-button (click)="reset()">
{{ c('resetSearchParams') }}
</button>
</dsh-float-panel-actions>
<dsh-float-panel-more>
<div fxLayout fxLayout.sm="column" fxLayoutGap="20px">
<mat-form-field fxFlex>
<mat-label>{{ t('depositID') }}</mat-label>
<input matInput type="text" formControlName="depositID" />
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ t('walletID') }}</mat-label>
<input matInput type="text" formControlName="walletID" />
</mat-form-field>
</div>
<div fxLayout fxLayout.sm="column" fxLayoutGap="20px">
<mat-form-field fxFlex>
<mat-label>{{ t('identityID') }}</mat-label>
<input matInput type="text" formControlName="identityID" />
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ t('sourceID') }}</mat-label>
<input matInput type="text" formControlName="sourceID" />
</mat-form-field>
</div>
<div fxLayout fxLayout.sm="column" fxLayoutGap="20px">
<mat-form-field fxFlex>
<mat-label>{{ t('amountFrom') }}</mat-label>
<input matInput type="number" formControlName="amountFrom" />
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ t('amountTo') }}</mat-label>
<input matInput type="number" formControlName="amountTo" />
</mat-form-field>
</div>
<div fxLayout fxLayout.sm="column" fxLayoutGap="20px">
<mat-form-field fxFlex>
<mat-label>{{ t('currencyID') }}</mat-label>
<input matInput type="text" formControlName="currencyID" />
</mat-form-field>
</div>
</dsh-float-panel-more>
</ng-container>
</dsh-float-panel>

View File

@ -1,21 +0,0 @@
import { Component } from '@angular/core';
import { DepositStatus } from '@dsh/api-codegen/wallet-api/swagger-codegen';
import { SearchFormService } from './search-form.service';
@Component({
selector: 'dsh-search-form',
templateUrl: 'search-form.component.html',
providers: [SearchFormService],
})
export class SearchFormComponent {
form = this.searchFormService.form;
reset = this.searchFormService.reset;
depositStatuses: DepositStatus.StatusEnum[] = ['Pending', 'Succeeded', 'Failed'];
expanded = false;
constructor(private searchFormService: SearchFormService) {}
}

View File

@ -1,61 +0,0 @@
import { Injectable } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import moment from 'moment';
import { map, startWith } from 'rxjs/operators';
import { FetchDepositsService } from '../services/fetch-deposits.service';
import { FormParams } from './form-params';
import { toFormValue } from './to-form-value';
import { toQueryParams } from './to-query-params';
import { toSearchParams } from './to-search-params';
@Injectable()
export class SearchFormService {
static defaultParams: FormParams = {
date: {
begin: moment().startOf('month'),
end: moment().endOf('month'),
},
walletID: null,
identityID: null,
depositID: null,
sourceID: null,
status: null,
amountFrom: null,
amountTo: null,
currencyID: null,
};
form = this.fb.group(SearchFormService.defaultParams);
constructor(
private fb: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private depositsService: FetchDepositsService
) {
this.init();
}
search(value) {
this.depositsService.search(toSearchParams(value));
}
reset() {
this.form.setValue(SearchFormService.defaultParams);
}
private init() {
this.syncQueryParams();
this.form.valueChanges.pipe(startWith(this.form.value)).subscribe((v) => this.search(v));
}
private syncQueryParams() {
const formValue = toFormValue(this.route.snapshot.queryParams, SearchFormService.defaultParams);
this.form.setValue(formValue);
this.form.valueChanges.pipe(startWith(formValue), map(toQueryParams)).subscribe((queryParams) => {
this.router.navigate([location.pathname], { queryParams });
});
}
}

View File

@ -1,15 +0,0 @@
import { Params } from '@angular/router';
import moment from 'moment';
import { FormParams } from './form-params';
export function toFormValue({ fromTime, toTime, ...params }: Params, defaultParams: FormParams): FormParams {
return {
...defaultParams,
...params,
date: {
begin: fromTime ? moment(fromTime) : defaultParams.date.begin,
end: toTime ? moment(toTime) : defaultParams.date.end,
},
};
}

View File

@ -1,10 +0,0 @@
import { FormParams } from './form-params';
import { QueryParams } from './query-params';
export function toQueryParams({ date, ...params }: FormParams): QueryParams {
return {
...params,
fromTime: date.begin.utc().format(),
toTime: date.end.utc().format(),
};
}

View File

@ -1,11 +0,0 @@
import { DepositsSearchParams } from '@dsh/api/deposits';
import { FormParams } from './form-params';
export function toSearchParams({ date, ...params }: FormParams): DepositsSearchParams {
return {
...params,
fromTime: date.begin.utc().format(),
toTime: date.end.utc().format(),
};
}

View File

@ -7,6 +7,8 @@ import { catchError } from 'rxjs/operators';
import { Deposit } from '@dsh/api-codegen/wallet-api/swagger-codegen';
import { DepositsSearchParams, DepositsService as DepositsApiService } from '@dsh/api/deposits';
import { SEARCH_LIMIT } from '@dsh/app/sections/tokens';
import { isNumber } from '@dsh/app/shared/utils';
import { toMinor } from '@dsh/utils';
import { DEBOUNCE_FETCHER_ACTION_TIME, IndicatorsPartialFetcher } from '../../../partial-fetcher';
@ -24,12 +26,22 @@ export class FetchDepositsService extends IndicatorsPartialFetcher<Deposit, Depo
super(searchLimit, debounceActionTime);
}
protected fetch(params: DepositsSearchParams, continuationToken: string) {
return this.depositsService.listDeposits({ ...params }, this.searchLimit, continuationToken).pipe(
catchError(() => {
this.snackBar.open(this.transloco.translate('httpError'), 'OK');
return of({ result: [] });
})
);
protected fetch({ amountTo, amountFrom, ...params }: DepositsSearchParams, continuationToken: string) {
return this.depositsService
.listDeposits(
{
...params,
amountFrom: isNumber(amountFrom) ? toMinor(amountFrom) : undefined,
amountTo: isNumber(amountTo) ? toMinor(amountTo) : undefined,
},
this.searchLimit,
continuationToken
)
.pipe(
catchError(() => {
this.snackBar.open(this.transloco.translate('httpError'), 'OK');
return of({ result: [] });
})
);
}
}

View File

@ -6,9 +6,9 @@
"identityID": "Идентификатор владельца",
"depositID": "Идентификатор пополнения",
"sourceID": "Идентификатор источника",
"amountFrom": "Сумма от",
"amountTo": "Сумма до",
"currencyID": "Валюта"
"depositAmount": "Сумма пополнения",
"depositAmountFrom": "Мин. сумма",
"depositAmountTo": "Макс. сумма"
},
"headerRow": {
"amount": "Сумма",