mirror of
https://github.com/valitydev/koffing.git
synced 2024-11-06 01:05:19 +00:00
FE-756: FE-763: Withdrawal registry reports & Deposits init (#242)
This commit is contained in:
parent
781616ed3e
commit
004a34091b
@ -16,6 +16,7 @@ import { UrlShortenerService } from './url-shortener.service';
|
||||
import { ClaimService } from './claim.service';
|
||||
import { CustomerService } from './customer.service';
|
||||
import { DownloadService } from './download.service';
|
||||
import { WAPIModule } from './wapi/wapi.module';
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
@ -35,6 +36,7 @@ import { DownloadService } from './download.service';
|
||||
ClaimService,
|
||||
CustomerService,
|
||||
DownloadService
|
||||
]
|
||||
],
|
||||
imports: [WAPIModule]
|
||||
})
|
||||
export class BackendModule {}
|
||||
|
3
src/app/backend/helpers/index.ts
Normal file
3
src/app/backend/helpers/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './to-search-params';
|
||||
export * from './applyMixins';
|
||||
export * from './to-utc';
|
20
src/app/backend/helpers/to-create-params.ts
Normal file
20
src/app/backend/helpers/to-create-params.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { isDate, reduce } from 'lodash';
|
||||
import { toUTC } from './to-utc';
|
||||
|
||||
export function toCreateParams(params: object): object {
|
||||
return reduce(
|
||||
params,
|
||||
(acc, value, key) => {
|
||||
if (value) {
|
||||
if (isDate(value)) {
|
||||
return { ...acc, [key]: toUTC(value) };
|
||||
} else {
|
||||
return { ...acc, [key]: value };
|
||||
}
|
||||
} else {
|
||||
return acc;
|
||||
}
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
19
src/app/backend/helpers/to-search-params.ts
Normal file
19
src/app/backend/helpers/to-search-params.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { URLSearchParams } from '@angular/http';
|
||||
import { toString, forEach, isNumber, isDate } from 'lodash';
|
||||
import { toUTC } from './to-utc';
|
||||
|
||||
export function toSearchParams(params: object): URLSearchParams {
|
||||
const result = new URLSearchParams();
|
||||
forEach(params, (value, field) => {
|
||||
if (value) {
|
||||
if (isDate(value)) {
|
||||
result.set(field, toUTC(value));
|
||||
} else if (isNumber(value)) {
|
||||
result.set(field, toString(value));
|
||||
} else {
|
||||
result.set(field, value);
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
6
src/app/backend/helpers/to-utc.ts
Normal file
6
src/app/backend/helpers/to-utc.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import moment = require('moment');
|
||||
|
||||
export const toUTC = (date: Date): string =>
|
||||
moment(date)
|
||||
.utc()
|
||||
.format();
|
@ -25,6 +25,8 @@ import {
|
||||
Withdrawal,
|
||||
WithdrawalSearchResult
|
||||
} from './model';
|
||||
import { SearchDeposits } from './wapi/requests/search-deposits-params';
|
||||
import { SearchDepositResult } from './wapi/requests/search-deposits-result';
|
||||
|
||||
@Injectable()
|
||||
export class SearchService {
|
||||
@ -86,6 +88,11 @@ export class SearchService {
|
||||
.map(res => res.json());
|
||||
}
|
||||
|
||||
public searchWalletDeposits(depositsParams: SearchDeposits): Observable<SearchDepositResult> {
|
||||
const search = this.toSearchParams(depositsParams);
|
||||
return this.http.get(`${this.config.wapiUrl}/deposits`, { search }).map(res => res.json());
|
||||
}
|
||||
|
||||
public searchWalletWithdrawal(withdrawalID: string): Observable<Withdrawal> {
|
||||
return this.http
|
||||
.get(`${this.config.wapiUrl}/withdrawals/${withdrawalID}`)
|
||||
|
28
src/app/backend/wapi/download-file.service.ts
Normal file
28
src/app/backend/wapi/download-file.service.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { KoffingHttp } from '../koffing-http.service';
|
||||
import { ConfigService } from '../config.service';
|
||||
import { DownloadFilePathParams, DownloadFileParams } from './requests/download-file-params';
|
||||
import { File } from './model/file';
|
||||
import { toSearchParams } from '../helpers';
|
||||
|
||||
@Injectable()
|
||||
export class DownloadFileService {
|
||||
constructor(private http: KoffingHttp, private config: ConfigService) {}
|
||||
|
||||
public downloadFile(
|
||||
{ fileID }: DownloadFilePathParams,
|
||||
queryParams: DownloadFileParams
|
||||
): Observable<File> {
|
||||
return this.http
|
||||
.post(
|
||||
`${this.config.wapiUrl}/files/${fileID}/download`,
|
||||
{},
|
||||
{
|
||||
search: toSearchParams(queryParams)
|
||||
}
|
||||
)
|
||||
.map(res => res.json());
|
||||
}
|
||||
}
|
25
src/app/backend/wapi/model/deposit.ts
Normal file
25
src/app/backend/wapi/model/deposit.ts
Normal file
@ -0,0 +1,25 @@
|
||||
export enum DepositStatus {
|
||||
Pending = 'Pending',
|
||||
Succeeded = 'Succeeded',
|
||||
Failed = 'Failed'
|
||||
}
|
||||
|
||||
export interface Deposit {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
wallet: string;
|
||||
source: string;
|
||||
body: {
|
||||
amount: number;
|
||||
currency: string;
|
||||
};
|
||||
fee: {
|
||||
amount: number;
|
||||
currency: string;
|
||||
};
|
||||
externalID: string;
|
||||
status: DepositStatus;
|
||||
failure: {
|
||||
code: string;
|
||||
};
|
||||
}
|
4
src/app/backend/wapi/model/file.ts
Normal file
4
src/app/backend/wapi/model/file.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface File {
|
||||
url: string;
|
||||
expiresAt: string;
|
||||
}
|
25
src/app/backend/wapi/model/report.ts
Normal file
25
src/app/backend/wapi/model/report.ts
Normal file
@ -0,0 +1,25 @@
|
||||
export enum ReportType {
|
||||
withdrawalRegistry = 'withdrawalRegistry'
|
||||
}
|
||||
|
||||
export enum ReportStatus {
|
||||
pending = 'pending',
|
||||
created = 'created',
|
||||
canceled = 'canceled'
|
||||
}
|
||||
|
||||
export interface Report {
|
||||
id: number;
|
||||
createdAt: string;
|
||||
fromTime: string;
|
||||
toTime: string;
|
||||
status: ReportStatus;
|
||||
type: ReportType;
|
||||
files: string[];
|
||||
}
|
||||
|
||||
export interface SearchReportParams {
|
||||
fromTime: string;
|
||||
toTime: string;
|
||||
type: ReportType;
|
||||
}
|
38
src/app/backend/wapi/reports.service.ts
Normal file
38
src/app/backend/wapi/reports.service.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { KoffingHttp } from '../koffing-http.service';
|
||||
import { ConfigService } from '../config.service';
|
||||
import { Report } from './model/report';
|
||||
import { GetReportsQuery, GetReportsPath } from './requests/get-reports-params';
|
||||
import { toSearchParams } from '../helpers';
|
||||
import { CreateReportPath, CreateReportQuery } from './requests/create-report-params';
|
||||
import { toCreateParams } from '../helpers/to-create-params';
|
||||
|
||||
@Injectable()
|
||||
export class ReportsService {
|
||||
constructor(private http: KoffingHttp, private config: ConfigService) {}
|
||||
|
||||
public getReports(
|
||||
{ identityID }: GetReportsPath,
|
||||
queryParams: GetReportsQuery
|
||||
): Observable<Report[]> {
|
||||
return this.http
|
||||
.get(`${this.config.wapiUrl}/identities/${identityID}/reports`, {
|
||||
search: toSearchParams(queryParams)
|
||||
})
|
||||
.map(res => res.json());
|
||||
}
|
||||
|
||||
public createReport(
|
||||
{ identityID }: CreateReportPath,
|
||||
queryParams: CreateReportQuery
|
||||
): Observable<Report> {
|
||||
return this.http
|
||||
.post(
|
||||
`${this.config.wapiUrl}/identities/${identityID}/reports`,
|
||||
toCreateParams(queryParams)
|
||||
)
|
||||
.map(res => res.json());
|
||||
}
|
||||
}
|
11
src/app/backend/wapi/requests/create-report-params.ts
Normal file
11
src/app/backend/wapi/requests/create-report-params.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { ReportType } from '../model/report';
|
||||
|
||||
export interface CreateReportPath {
|
||||
identityID: string;
|
||||
}
|
||||
|
||||
export interface CreateReportQuery {
|
||||
fromTime: Date;
|
||||
toTime: Date;
|
||||
reportType?: ReportType;
|
||||
}
|
7
src/app/backend/wapi/requests/download-file-params.ts
Normal file
7
src/app/backend/wapi/requests/download-file-params.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export interface DownloadFilePathParams {
|
||||
fileID: string;
|
||||
}
|
||||
|
||||
export interface DownloadFileParams {
|
||||
expiresAt: string;
|
||||
}
|
11
src/app/backend/wapi/requests/get-reports-params.ts
Normal file
11
src/app/backend/wapi/requests/get-reports-params.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { ReportType } from '../model/report';
|
||||
|
||||
export interface GetReportsPath {
|
||||
identityID: string;
|
||||
}
|
||||
|
||||
export interface GetReportsQuery {
|
||||
fromTime: Date;
|
||||
toTime: Date;
|
||||
type?: ReportType;
|
||||
}
|
16
src/app/backend/wapi/requests/search-deposits-params.ts
Normal file
16
src/app/backend/wapi/requests/search-deposits-params.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { DepositStatus } from '../model/deposit';
|
||||
|
||||
export interface SearchDeposits {
|
||||
walletID?: string;
|
||||
identityID?: string;
|
||||
depositID?: string;
|
||||
sourceID?: string;
|
||||
status?: DepositStatus;
|
||||
createdAtFrom?: string;
|
||||
createdAtTo?: string;
|
||||
amountFrom?: number;
|
||||
amountTo?: number;
|
||||
currencyID?: string;
|
||||
limit: number;
|
||||
continuationToken?: string;
|
||||
}
|
6
src/app/backend/wapi/requests/search-deposits-result.ts
Normal file
6
src/app/backend/wapi/requests/search-deposits-result.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { Deposit } from '../model/deposit';
|
||||
|
||||
export interface SearchDepositResult {
|
||||
continuationToken: string;
|
||||
result: Deposit[];
|
||||
}
|
9
src/app/backend/wapi/wapi.module.ts
Normal file
9
src/app/backend/wapi/wapi.module.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { ReportsService } from './reports.service';
|
||||
import { DownloadFileService } from './download-file.service';
|
||||
|
||||
@NgModule({
|
||||
providers: [ReportsService, DownloadFileService]
|
||||
})
|
||||
export class WAPIModule {}
|
@ -18,6 +18,7 @@ import { PaymentStatusPipe } from './payment-statuses.pipe';
|
||||
import { CurrencyPipe } from './currency.pipe';
|
||||
import { StepperComponent } from './stepper/stepper.component';
|
||||
import { WithdrawalStatusPipe } from './withdrawal-status.pipe';
|
||||
import { DepositStatusPipe } from './deposit-status.pipe';
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule, FormsModule, ReactiveFormsModule, CalendarModule],
|
||||
@ -35,7 +36,8 @@ import { WithdrawalStatusPipe } from './withdrawal-status.pipe';
|
||||
PaymentStatusPipe,
|
||||
CurrencyPipe,
|
||||
WithdrawalStatusPipe,
|
||||
StepperComponent
|
||||
StepperComponent,
|
||||
DepositStatusPipe
|
||||
],
|
||||
exports: [
|
||||
SelectComponent,
|
||||
@ -51,7 +53,8 @@ import { WithdrawalStatusPipe } from './withdrawal-status.pipe';
|
||||
PaymentStatusPipe,
|
||||
CurrencyPipe,
|
||||
WithdrawalStatusPipe,
|
||||
StepperComponent
|
||||
StepperComponent,
|
||||
DepositStatusPipe
|
||||
],
|
||||
providers: [EventPollerService]
|
||||
})
|
||||
|
12
src/app/common/deposit-status.pipe.ts
Normal file
12
src/app/common/deposit-status.pipe.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { DepositStatus } from 'koffing/backend/wapi/model/deposit';
|
||||
import { DepositStatusLabel } from '../deposits/deposit-status-label';
|
||||
|
||||
@Pipe({
|
||||
name: 'kofDepositStatus'
|
||||
})
|
||||
export class DepositStatusPipe implements PipeTransform {
|
||||
public transform(input: DepositStatus): string {
|
||||
return DepositStatusLabel[input] || input;
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
form.form-horizontal.form-label-left.css-form
|
||||
.form-group(*ngIf="deposit?.id")
|
||||
.col-xs-12.col-sm-4
|
||||
label.text-left Идентификатор пополнения:
|
||||
.col-xs-12.col-sm-8
|
||||
div {{deposit?.id}}
|
||||
.form-group(*ngIf="deposit?.status")
|
||||
.col-xs-12.col-sm-4
|
||||
label.text-left Статус:
|
||||
.col-xs-12.col-sm-8
|
||||
div.label.label-default([ngClass]="getLabelClass(deposit?.status)") {{deposit?.status | kofDepositStatus}}
|
||||
.form-group
|
||||
.col-xs-12.col-sm-4
|
||||
label.text-left Идентификатор кошелька:
|
||||
.col-xs-12.col-sm-8
|
||||
div {{deposit?.wallet}}
|
||||
.form-group
|
||||
.col-xs-12.col-sm-4
|
||||
label.text-left Идентификатор источника денежных средств:
|
||||
.col-xs-12.col-sm-8
|
||||
div {{deposit?.source}}
|
||||
.form-group
|
||||
.col-xs-12.col-sm-4
|
||||
label.text-left Дата и время создания:
|
||||
.col-xs-12.col-sm-8
|
||||
div {{deposit?.createdAt | date: "dd.MM.yyyy HH:mm:ss"}}
|
||||
.form-group
|
||||
.col-xs-12.col-sm-4
|
||||
label.text-left Объем пополнения средств:
|
||||
.col-xs-12.col-sm-8
|
||||
div {{deposit?.body.amount | kofRoubleCurrency}} {{deposit.body.currency | kofCurrency}}
|
||||
.form-group(*ngIf="deposit?.fee")
|
||||
.col-xs-12.col-sm-4
|
||||
label.text-left Коммисия:
|
||||
.col-xs-12.col-sm-8
|
||||
div {{deposit?.fee.amount | kofRoubleCurrency}} {{deposit.fee.currency | kofCurrency}}
|
||||
.form-group(*ngIf="deposit?.failure")
|
||||
.col-xs-12.col-sm-4
|
||||
label.text-left Причина ошибки:
|
||||
.col-xs-12.col-sm-8
|
||||
div {{deposit.failure.code}}
|
20
src/app/deposit/deposit-details/deposit-details.component.ts
Normal file
20
src/app/deposit/deposit-details/deposit-details.component.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
import { DepositStatus, Deposit } from 'koffing/backend/wapi/model/deposit';
|
||||
|
||||
@Component({
|
||||
selector: 'kof-deposit-details',
|
||||
templateUrl: 'deposit-details.component.pug'
|
||||
})
|
||||
export class DepositDetailsComponent {
|
||||
@Input()
|
||||
public deposit: Deposit;
|
||||
|
||||
public getLabelClass(status: DepositStatus) {
|
||||
return {
|
||||
'label-success': status === DepositStatus.Succeeded,
|
||||
'label-danger': status === DepositStatus.Failed,
|
||||
'label-warning': status === DepositStatus.Pending
|
||||
};
|
||||
}
|
||||
}
|
16
src/app/deposit/deposit.component.pug
Normal file
16
src/app/deposit/deposit.component.pug
Normal file
@ -0,0 +1,16 @@
|
||||
kof-loading([isLoading]="!deposit && !depositNotFound")
|
||||
.row(*ngIf="deposit || depositNotFound")
|
||||
.col-xs-12.col-md-10.col-md-offset-1(*ngIf="!depositNotFound")
|
||||
.x_panel
|
||||
.x_title
|
||||
.row
|
||||
.col-xs-6
|
||||
h5 Пополнение
|
||||
.col-xs-6
|
||||
button.btn.btn-default.btn-sm.pull-right((click)="back()") Назад
|
||||
.x_content
|
||||
kof-deposit-details([deposit]="deposit")
|
||||
.col-xs-12.col-md-6.col-md-offset-3(*ngIf="depositNotFound")
|
||||
.x_panel
|
||||
.x_content
|
||||
h5.text-center Пополнение не найдено
|
27
src/app/deposit/deposit.component.ts
Normal file
27
src/app/deposit/deposit.component.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { DepositService } from './deposit.service';
|
||||
import { Deposit } from 'koffing/backend/wapi/model/deposit';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'deposit.component.pug',
|
||||
providers: [DepositService]
|
||||
})
|
||||
export class DepositComponent implements OnInit {
|
||||
public deposit: Deposit;
|
||||
|
||||
public depositNotFound: boolean = false;
|
||||
|
||||
constructor(private depositService: DepositService) {}
|
||||
|
||||
public ngOnInit() {
|
||||
this.depositService.depositSubject.subscribe(
|
||||
(deposit: Deposit) => (this.deposit = deposit),
|
||||
() => (this.depositNotFound = true)
|
||||
);
|
||||
}
|
||||
|
||||
public back() {
|
||||
this.depositService.back();
|
||||
}
|
||||
}
|
12
src/app/deposit/deposit.module.ts
Normal file
12
src/app/deposit/deposit.module.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { CommonModule } from 'koffing/common/common.module';
|
||||
import { DepositComponent } from './deposit.component';
|
||||
import { DepositDetailsComponent } from './deposit-details/deposit-details.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule, CommonModule],
|
||||
declarations: [DepositComponent, DepositDetailsComponent]
|
||||
})
|
||||
export class DepositModule {}
|
51
src/app/deposit/deposit.service.ts
Normal file
51
src/app/deposit/deposit.service.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { isUndefined } from 'lodash';
|
||||
|
||||
import { SearchService } from 'koffing/backend/search.service';
|
||||
import { Deposit } from 'koffing/backend/wapi/model/deposit';
|
||||
|
||||
@Injectable()
|
||||
export class DepositService {
|
||||
public depositSubject: Subject<Deposit> = new Subject();
|
||||
|
||||
private deposit: Deposit;
|
||||
|
||||
constructor(
|
||||
private searchService: SearchService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router
|
||||
) {
|
||||
Observable.combineLatest(this.route.parent.params, this.route.params).subscribe(result => {
|
||||
this.searchDeposit(result[1].depositID);
|
||||
});
|
||||
}
|
||||
|
||||
public back() {
|
||||
this.router.navigate(['wallets', 'deposits']);
|
||||
}
|
||||
|
||||
private searchDeposit(depositID: string) {
|
||||
const searchRetries = 20;
|
||||
this.searchService
|
||||
.searchWalletDeposits({ limit: 1, depositID })
|
||||
.repeatWhen(notifications =>
|
||||
notifications.delay(1000).takeWhile((value, retries) => {
|
||||
if (retries === searchRetries) {
|
||||
this.depositSubject.error({ message: 'Deposit not found' });
|
||||
}
|
||||
return isUndefined(this.deposit) && retries < searchRetries;
|
||||
})
|
||||
)
|
||||
.subscribe(searchResult => {
|
||||
if (searchResult) {
|
||||
this.deposit = searchResult[0];
|
||||
this.depositSubject.next(this.deposit);
|
||||
this.depositSubject.complete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
7
src/app/deposits/deposit-status-label.ts
Normal file
7
src/app/deposits/deposit-status-label.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { DepositStatus } from 'koffing/backend/wapi/model/deposit';
|
||||
|
||||
export const DepositStatusLabel = {
|
||||
[DepositStatus.Pending]: 'В процессе',
|
||||
[DepositStatus.Succeeded]: 'Успешно',
|
||||
[DepositStatus.Failed]: 'Ошибка'
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
div.ui-calendar.search-form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
p-calendar.search-form > span {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
p-calendar.search-form input.ui-inputtext {
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
margin: 0;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
p-calendar.has-error input.ui-inputtext {
|
||||
border-color: #a94442;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
input.has-error {
|
||||
border-color: #a94442;
|
||||
}
|
||||
|
||||
input.ng-dirty.ng-invalid {
|
||||
border-color: #a94442;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
.row
|
||||
.col-xs-12
|
||||
kof-deposits-search-form((onSearch)="onSearch($event)")
|
||||
.ln_solid
|
||||
.row
|
||||
.col-xs-12
|
||||
kof-deposits-search-result([deposits]="deposits")
|
||||
kof-stepper((onChange)="onChangePage($event)", [hasNext]="hasNext()", [page]="page + 1")
|
59
src/app/deposits/deposits-table/deposits-table.component.ts
Normal file
59
src/app/deposits/deposits-table/deposits-table.component.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import { DepositsTableService } from './deposits-table.service';
|
||||
import { SearchService } from 'koffing/backend/search.service';
|
||||
import { SearchFormService } from './search-form/search-form.service';
|
||||
import { Deposit } from 'koffing/backend/wapi/model/deposit';
|
||||
|
||||
@Component({
|
||||
selector: 'kof-wallets-deposits',
|
||||
templateUrl: 'deposits-table.component.pug',
|
||||
styleUrls: ['deposits-table.component.less'],
|
||||
providers: [DepositsTableService, SearchFormService]
|
||||
})
|
||||
export class DepositsTableComponent {
|
||||
public page: number = 0;
|
||||
public limit: number = 20;
|
||||
public deposits: Subject<Deposit[]> = new Subject();
|
||||
private continuationTokens: string[] = [];
|
||||
private formValue: any;
|
||||
|
||||
constructor(
|
||||
private depositsTableService: DepositsTableService,
|
||||
private searchService: SearchService
|
||||
) {}
|
||||
|
||||
public reset() {
|
||||
this.continuationTokens = [];
|
||||
this.page = 0;
|
||||
}
|
||||
|
||||
public onSearch(fromValue: any) {
|
||||
this.reset();
|
||||
this.formValue = fromValue;
|
||||
this.search();
|
||||
}
|
||||
|
||||
public hasNext() {
|
||||
return !!this.continuationTokens[this.page + 1];
|
||||
}
|
||||
|
||||
public onChangePage(num: number) {
|
||||
this.search(num);
|
||||
}
|
||||
|
||||
private search(num: number = 0) {
|
||||
this.page += num;
|
||||
const continuationToken = this.continuationTokens[this.page];
|
||||
const request = this.depositsTableService.toSearchParams(
|
||||
this.limit,
|
||||
continuationToken,
|
||||
this.formValue
|
||||
);
|
||||
this.searchService.searchWalletDeposits(request).subscribe(response => {
|
||||
this.continuationTokens[this.page + 1] = response.continuationToken;
|
||||
this.deposits.next(response.result);
|
||||
});
|
||||
}
|
||||
}
|
28
src/app/deposits/deposits-table/deposits-table.service.ts
Normal file
28
src/app/deposits/deposits-table/deposits-table.service.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { toMinor } from 'koffing/common/amount-utils';
|
||||
import { SearchDeposits } from 'koffing/backend/wapi/requests/search-deposits-params';
|
||||
|
||||
@Injectable()
|
||||
export class DepositsTableService {
|
||||
public toSearchParams(
|
||||
limit: number,
|
||||
continuationToken: string,
|
||||
formParams: any
|
||||
): SearchDeposits {
|
||||
return {
|
||||
limit,
|
||||
walletID: formParams.walletID,
|
||||
continuationToken,
|
||||
identityID: formParams.identityID,
|
||||
sourceID: formParams.sourceID,
|
||||
depositID: formParams.depositID,
|
||||
status: formParams.status,
|
||||
createdAtFrom: formParams.createdAtFrom,
|
||||
createdAtTo: formParams.createdAtTo,
|
||||
amountFrom: toMinor(formParams.amountFrom),
|
||||
amountTo: toMinor(formParams.amountTo),
|
||||
currencyID: formParams.currencyID
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
.reset-button-container {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.toggle-button-container {
|
||||
padding-top: 20px;
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
form([formGroup]="searchForm", novalidate)
|
||||
.row
|
||||
.col-sm-2.col-xs-12
|
||||
.form-group
|
||||
label ID кошелька:
|
||||
input.form-control(formControlName="walletID")
|
||||
.col-sm-2.col-xs-12
|
||||
.form-group
|
||||
label ID личности владельца:
|
||||
input.form-control(formControlName="identityID")
|
||||
.col-sm-2.col-xs-12
|
||||
.form-group
|
||||
label ID ввода:
|
||||
input.form-control(formControlName="depositID")
|
||||
.col-sm-3.col-xs-12
|
||||
.form-group
|
||||
label Статус вывода
|
||||
kof-select(
|
||||
[items]="depositStatuses",
|
||||
[placeholder]="'Любой статус'",
|
||||
formControlName="status")
|
||||
.col-sm-3.col-xs-12
|
||||
.toggle-button-container
|
||||
button.btn.btn-default((click)="toggleAdditionalParamsVisible()") ...
|
||||
|
||||
div(*ngIf="additionalParamsVisible")
|
||||
.row
|
||||
.col-sm-4.col-xs-12
|
||||
.form-group
|
||||
label Временной интервал:
|
||||
.row
|
||||
.col-sm-6.col-xs-12
|
||||
.ui-calendar.search-form
|
||||
p-calendar.ui-inputtext.search-form(
|
||||
formControlName="createdAtFrom",
|
||||
dateFormat = "dd.mm.yy",
|
||||
[maxDate]="searchForm.controls.createdAtTo.value",
|
||||
readonlyInput="true")
|
||||
.col-sm-6.col-xs-12
|
||||
.ui-calendar.search-form
|
||||
p-calendar.ui-inputtext.search-form(
|
||||
formControlName="createdAtTo",
|
||||
dateFormat = "dd.mm.yy",
|
||||
[minDate]="searchForm.controls.createdAtFrom.value",
|
||||
readonlyInput="true")
|
||||
.col-sm-2.col-xs-12
|
||||
.form-group
|
||||
label ID источника средств:
|
||||
input.form-control(formControlName="sourceID")
|
||||
.col-sm-2.col-xs-12
|
||||
.form-group
|
||||
label Сумма от:
|
||||
input.form-control(formControlName="amountFrom", type="number")
|
||||
.col-sm-2.col-xs-12
|
||||
.form-group
|
||||
label Сумма до:
|
||||
input.form-control(formControlName="amountTo", type="number")
|
||||
.col-sm-2.col-xs-12
|
||||
.form-group
|
||||
label Валюта:
|
||||
input.form-control(formControlName="currencyID")
|
||||
|
||||
.row
|
||||
.col-sm-9.col-xs-12
|
||||
.reset-button-container
|
||||
button.btn.btn-default((click)="reset()") Сбросить параметры поиска
|
@ -0,0 +1,43 @@
|
||||
import { Component, Output, OnInit, EventEmitter } from '@angular/core';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
import { map } from 'lodash';
|
||||
|
||||
import { SelectItem } from 'koffing/common/select/select-item';
|
||||
import { SearchFormService } from './search-form.service';
|
||||
import { DepositStatusLabel } from '../../deposit-status-label';
|
||||
|
||||
@Component({
|
||||
selector: 'kof-deposits-search-form',
|
||||
templateUrl: 'search-form.component.pug',
|
||||
providers: [SearchFormService],
|
||||
styleUrls: ['search-form.component.less']
|
||||
})
|
||||
export class SearchFormComponent implements OnInit {
|
||||
@Output()
|
||||
public onSearch: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
public searchForm: FormGroup;
|
||||
public additionalParamsVisible: boolean;
|
||||
public depositStatuses: SelectItem[];
|
||||
|
||||
constructor(private searchFormService: SearchFormService) {}
|
||||
|
||||
public ngOnInit() {
|
||||
this.depositStatuses = map(DepositStatusLabel, (name, key) => new SelectItem(key, name));
|
||||
this.searchForm = this.searchFormService.searchForm;
|
||||
this.onSearch.emit(this.searchForm.value);
|
||||
this.searchForm.valueChanges
|
||||
.filter(() => this.searchForm.status === 'VALID')
|
||||
.debounceTime(300)
|
||||
.subscribe(value => this.onSearch.emit(value));
|
||||
this.additionalParamsVisible = this.searchFormService.hasFormAdditionalParams();
|
||||
}
|
||||
|
||||
public reset() {
|
||||
this.onSearch.emit(this.searchFormService.reset());
|
||||
}
|
||||
|
||||
public toggleAdditionalParamsVisible() {
|
||||
this.additionalParamsVisible = !this.additionalParamsVisible;
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { mapValues, isEqual, chain, keys, difference, clone } from 'lodash';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import * as moment from 'moment';
|
||||
|
||||
@Injectable()
|
||||
export class SearchFormService {
|
||||
public searchForm: FormGroup;
|
||||
|
||||
private urlDateFormat = 'YYYY-MM-DD';
|
||||
private defaultValues: any;
|
||||
private mainSearchFields = ['walletID', 'identityID', 'status', 'depositID'];
|
||||
|
||||
constructor(private fb: FormBuilder, private router: Router, private route: ActivatedRoute) {
|
||||
this.searchForm = this.initForm();
|
||||
this.route.queryParams.subscribe(queryParams => this.updateFormValue(queryParams));
|
||||
this.searchForm.valueChanges.subscribe(values => this.updateQueryParams(values));
|
||||
}
|
||||
|
||||
public hasFormAdditionalParams(): boolean {
|
||||
const formFields = chain(this.searchForm.getRawValue())
|
||||
.map((value: string, key: string) => (isEqual(value, '') ? null : key))
|
||||
.filter(mapped => mapped !== null && mapped !== 'limit')
|
||||
.value();
|
||||
const defaultFields = keys(this.defaultValues);
|
||||
return difference(formFields, defaultFields, this.mainSearchFields).length > 0;
|
||||
}
|
||||
|
||||
public reset(): any {
|
||||
this.searchForm.reset(this.defaultValues);
|
||||
return this.defaultValues;
|
||||
}
|
||||
|
||||
private updateFormValue(queryParams: Params) {
|
||||
if (isEqual(queryParams, {})) {
|
||||
this.updateQueryParams(this.defaultValues);
|
||||
} else {
|
||||
this.searchForm.patchValue(this.queryParamsToFormValue(queryParams));
|
||||
}
|
||||
}
|
||||
|
||||
private updateQueryParams(value: any) {
|
||||
const queryParams = this.formValueToQueryParams(value);
|
||||
this.router.navigate(['wallets', 'deposits'], { queryParams });
|
||||
}
|
||||
|
||||
private initForm(): FormGroup {
|
||||
const form = this.fb.group({
|
||||
walletID: '',
|
||||
identityID: '',
|
||||
sourceID: '',
|
||||
depositID: '',
|
||||
status: '',
|
||||
createdAtFrom: moment()
|
||||
.subtract(1, 'month')
|
||||
.startOf('day')
|
||||
.toDate(),
|
||||
createdAtTo: moment()
|
||||
.endOf('day')
|
||||
.toDate(),
|
||||
amountFrom: '',
|
||||
amountTo: '',
|
||||
currencyID: '',
|
||||
continuationToken: '',
|
||||
limit: [20, Validators.required]
|
||||
});
|
||||
this.defaultValues = clone(form.value);
|
||||
return form;
|
||||
}
|
||||
|
||||
private formValueToQueryParams(formValue: any): Params {
|
||||
const mapped = mapValues(formValue, value => (isEqual(value, '') ? null : value));
|
||||
return {
|
||||
...mapped,
|
||||
createdAtFrom: moment(formValue.createdAtFrom).format(this.urlDateFormat),
|
||||
createdAtTo: moment(formValue.createdAtTo).format(this.urlDateFormat)
|
||||
};
|
||||
}
|
||||
|
||||
private queryParamsToFormValue(params: Params): any {
|
||||
return {
|
||||
...params,
|
||||
createdAtFrom: moment(params.createdAtFrom)
|
||||
.startOf('day')
|
||||
.toDate(),
|
||||
createdAtTo: moment(params.createdAtTo)
|
||||
.endOf('day')
|
||||
.toDate()
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
table.table.table-striped
|
||||
thead
|
||||
tr
|
||||
th ID выплаты
|
||||
th Сумма пополнения
|
||||
th Комиссия
|
||||
th Сумма зачисления
|
||||
th Дата создания
|
||||
th Статус
|
||||
th
|
||||
tbody(*ngFor="let deposit of deposits | async")
|
||||
tr
|
||||
td {{deposit.id}}
|
||||
td {{deposit.body.amount | kofRoubleCurrency}} {{deposit.body.currency | kofCurrency}}
|
||||
td {{deposit.fee.amount | kofRoubleCurrency }} {{deposit.fee.currency | kofCurrency }}
|
||||
td {{deposit.body.amount - deposit.fee.amount | kofRoubleCurrency}} {{deposit.body.currency | kofCurrency}}
|
||||
td {{deposit.createdAt | date: "dd.MM.yyyy HH:mm:ss"}}
|
||||
td
|
||||
span.label.label-default([ngClass]="getLabelClass(deposit.status)") {{deposit.status | kofDepositStatus}}
|
||||
td
|
||||
.pull-right
|
||||
button.btn.btn-xs.btn-default((click)="gotToDepositDetails(deposit.id)") Детали
|
@ -0,0 +1,28 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { DepositStatus, Deposit } from 'koffing/backend/wapi/model/deposit';
|
||||
|
||||
@Component({
|
||||
selector: 'kof-deposits-search-result',
|
||||
templateUrl: 'search-result.component.pug'
|
||||
})
|
||||
export class SearchResultComponent {
|
||||
@Input()
|
||||
public deposits: Observable<Deposit[]>;
|
||||
|
||||
constructor(private router: Router) {}
|
||||
|
||||
public getLabelClass(status: string) {
|
||||
return {
|
||||
'label-success': status === DepositStatus.Succeeded,
|
||||
'label-danger': status === DepositStatus.Failed,
|
||||
'label-warning': status === DepositStatus.Pending
|
||||
};
|
||||
}
|
||||
|
||||
public gotToDepositDetails(depositID: string) {
|
||||
this.router.navigate(['wallets', 'deposits', depositID]);
|
||||
}
|
||||
}
|
0
src/app/deposits/deposits.component.less
Normal file
0
src/app/deposits/deposits.component.less
Normal file
9
src/app/deposits/deposits.component.pug
Normal file
9
src/app/deposits/deposits.component.pug
Normal file
@ -0,0 +1,9 @@
|
||||
.row
|
||||
.col-xs-12.col-md-10.col-md-offset-1
|
||||
.x_panel
|
||||
.x_title
|
||||
h5 Поиск пополнений
|
||||
.x_content
|
||||
.row
|
||||
.col-xs-12
|
||||
kof-wallets-deposits
|
7
src/app/deposits/deposits.component.ts
Normal file
7
src/app/deposits/deposits.component.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'deposits.component.pug',
|
||||
styleUrls: ['deposits.component.less']
|
||||
})
|
||||
export class DepositsComponent {}
|
21
src/app/deposits/deposits.module.ts
Normal file
21
src/app/deposits/deposits.module.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { CalendarModule } from 'primeng/primeng';
|
||||
|
||||
import { CommonModule } from 'koffing/common/common.module';
|
||||
import { DepositsComponent } from './deposits.component';
|
||||
import { DepositsTableComponent } from './deposits-table/deposits-table.component';
|
||||
import { SearchFormComponent } from './deposits-table/search-form/search-form.component';
|
||||
import { SearchResultComponent } from './deposits-table/search-result/search-result.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule, FormsModule, ReactiveFormsModule, CommonModule, CalendarModule],
|
||||
declarations: [
|
||||
DepositsComponent,
|
||||
DepositsTableComponent,
|
||||
SearchFormComponent,
|
||||
SearchResultComponent
|
||||
]
|
||||
})
|
||||
export class DepositsModule {}
|
@ -2,7 +2,7 @@ kof-http-error-handle
|
||||
div([ngClass]="{'nav-md': !isSidebarOpened, 'nav-sm': isSidebarOpened}")
|
||||
.container.body
|
||||
.main_container
|
||||
kof-shop-top-panel
|
||||
kof-top-panel
|
||||
kof-sidebar
|
||||
.right_col
|
||||
.outlet
|
||||
|
@ -1,11 +0,0 @@
|
||||
.top_nav
|
||||
nav.nav_menu
|
||||
.nav.toggle.toggle-menu
|
||||
a((click)="toggleMenu()")
|
||||
i.fa.fa-bars
|
||||
.nav.toggle.logo
|
||||
a([routerLink]=["/"])
|
||||
| RBKmoney
|
||||
.nav.toggle.shop-selector
|
||||
kof-shop-selector
|
||||
kof-top-panel-actions
|
@ -1,15 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { ToggleMenuBroadcaster } from 'koffing/broadcaster';
|
||||
|
||||
@Component({
|
||||
selector: 'kof-shop-top-panel',
|
||||
templateUrl: 'shop-top-panel.component.pug'
|
||||
})
|
||||
export class ShopTopPanelComponent {
|
||||
constructor(private toggleMenuBroadcaster: ToggleMenuBroadcaster) {}
|
||||
|
||||
public toggleMenu() {
|
||||
this.toggleMenuBroadcaster.fire();
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@
|
||||
border: none;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
padding: 8px 15px;
|
@ -1,2 +1,3 @@
|
||||
select.shop-selector([(ngModel)]="selected")
|
||||
option(value="", disabled="disabled") Магазины
|
||||
option(*ngFor="let item of selectorItems", [value]="item.value") {{item.label}}
|
@ -12,7 +12,7 @@ import { SelectItem } from './select-item';
|
||||
export class ShopSelectorComponent implements OnInit {
|
||||
public selectorItems: SelectItem[];
|
||||
|
||||
private selectedShopID: string;
|
||||
private selectedShopID: string = '';
|
||||
|
||||
constructor(private shopSelectorService: ShopSelectorService) {}
|
||||
|
@ -26,15 +26,19 @@ export class ShopSelectorService {
|
||||
|
||||
public getActiveShopID(): string {
|
||||
const routeShopID = this.route.snapshot.params['shopID'];
|
||||
return routeShopID ? routeShopID : this.getFromStorage(this.shops);
|
||||
return routeShopID ? routeShopID : '';
|
||||
}
|
||||
|
||||
public navigateToShop(shopID: string) {
|
||||
ShopIDStorage.set(shopID);
|
||||
const hasChildren = this.route.children.length > 0;
|
||||
const childRoute = hasChildren ? this.route.children[0].routeConfig.path : 'invoices';
|
||||
const childComponents = childRoute.split('/');
|
||||
this.router.navigate(['shop', shopID].concat(childComponents));
|
||||
let childRoute = ['invoices'];
|
||||
if (
|
||||
this.route.children.length &&
|
||||
this.route.children[0].parent.routeConfig.path === 'shops'
|
||||
) {
|
||||
childRoute = this.route.children[0].routeConfig.path.split('/');
|
||||
}
|
||||
this.router.navigate(['shop', shopID].concat(childRoute));
|
||||
}
|
||||
|
||||
private toSelectorItems(shops: Shop[]): SelectItem[] {
|
@ -1,5 +1,4 @@
|
||||
.top-panel-breadcrumb {
|
||||
width: 50%;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
background: none;
|
||||
@ -10,3 +9,14 @@
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.top-panel__link.top-panel__link.top-panel__link {
|
||||
@media (max-width: 600px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/.top-panel__link:hover *,
|
||||
/deep/.top-panel__link_active * {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
@ -1,8 +1,14 @@
|
||||
.top_nav
|
||||
nav.nav_menu
|
||||
.nav.toggle.toggle-menu
|
||||
a((click)="toggleMenu()")
|
||||
i.fa.fa-bars
|
||||
.nav.toggle.logo
|
||||
a([routerLink]=["/"])
|
||||
| RBKmoney
|
||||
a([routerLink]=["/"]) RBKmoney
|
||||
div([class]="'nav toggle shop-selector top-panel__link' + (activeLink === links.shop ? ' top-panel__link_active' : '')")
|
||||
kof-shop-selector
|
||||
div([class]="'nav toggle top-panel__link' + (activeLink === links.wallets ? ' top-panel__link_active' : '')")
|
||||
a([routerLink]=["/wallets"]) Кошельки
|
||||
ol.breadcrumb.top-panel-breadcrumb(*ngIf="breadcrumbConfig.length > 1")
|
||||
li(*ngFor="let config of breadcrumbConfig", [ngClass]="{'active': !config.routerLink}")
|
||||
a(*ngIf="config.routerLink", [routerLink]="config.routerLink") {{config.label}}
|
||||
|
@ -1,6 +1,16 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router, NavigationEnd } from '@angular/router';
|
||||
|
||||
import { BreadcrumbBroadcaster, BreadcrumbConfig } from 'koffing/broadcaster';
|
||||
import {
|
||||
ToggleMenuBroadcaster,
|
||||
BreadcrumbConfig,
|
||||
BreadcrumbBroadcaster
|
||||
} from 'koffing/broadcaster';
|
||||
|
||||
enum Links {
|
||||
shop = 'shop',
|
||||
wallets = 'wallets'
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'kof-top-panel',
|
||||
@ -9,6 +19,8 @@ import { BreadcrumbBroadcaster, BreadcrumbConfig } from 'koffing/broadcaster';
|
||||
})
|
||||
export class TopPanelComponent implements OnInit {
|
||||
public breadcrumbConfig: BreadcrumbConfig[] = [];
|
||||
private activeLink: Links;
|
||||
private links = Links;
|
||||
|
||||
private initialBreadcrumbConfig: BreadcrumbConfig[] = [
|
||||
{
|
||||
@ -17,11 +29,29 @@ export class TopPanelComponent implements OnInit {
|
||||
}
|
||||
];
|
||||
|
||||
constructor(private breadcrumbBroadcaster: BreadcrumbBroadcaster) {}
|
||||
constructor(
|
||||
private toggleMenuBroadcaster: ToggleMenuBroadcaster,
|
||||
private breadcrumbBroadcaster: BreadcrumbBroadcaster,
|
||||
private router: Router
|
||||
) {
|
||||
this.router.events.subscribe(e => {
|
||||
if (e instanceof NavigationEnd) {
|
||||
for (const link in Links) {
|
||||
if (e.url.search(new RegExp(`^/${link}`, 'g')) !== -1) {
|
||||
this.activeLink = link as Links;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
this.breadcrumbBroadcaster.on().subscribe((config: BreadcrumbConfig[]) => {
|
||||
this.breadcrumbConfig = this.initialBreadcrumbConfig.concat(config);
|
||||
});
|
||||
}
|
||||
|
||||
public toggleMenu() {
|
||||
this.toggleMenuBroadcaster.fire();
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,15 @@
|
||||
a(routerLink="/wallets/list")
|
||||
i.fa.fa-suitcase
|
||||
| Кошельки
|
||||
//- li([routerLinkActive]="['active']")
|
||||
//- a(routerLink="/wallets/deposits")
|
||||
//- i.fa.fa-level-down
|
||||
//- | Пополнения
|
||||
li([routerLinkActive]="['active']")
|
||||
a(routerLink="/wallets/withdrawals")
|
||||
i.fa.fa-level-up
|
||||
| Выводы
|
||||
li([routerLinkActive]="['active']")
|
||||
a(routerLink="/wallets/documents")
|
||||
i.fa.fa-file-o
|
||||
| Документы
|
@ -20,10 +20,15 @@ import { DocumentsComponent } from 'koffing/documents/documents.component';
|
||||
import { InitCreateShopComponent } from 'koffing/management/init-create-shop/init-create-shop.component';
|
||||
import { ReportsComponent } from 'koffing/documents/reports/reports.component';
|
||||
import { ReportType } from 'koffing/backend';
|
||||
import { ReportType as WalletsReportType } from 'koffing/backend/wapi/model/report';
|
||||
import { WalletsComponent } from 'koffing/wallets/wallets.component';
|
||||
import { WithdrawalComponent } from 'koffing/withdrawal/withdrawal.component';
|
||||
import { WalletsContainerComponent } from './components/wallets-container/wallets-container.component';
|
||||
import { WithdrawalsComponent } from 'koffing/withdrawals/withdrawals.component';
|
||||
import { WalletsReportsComponent } from 'koffing/wallets-documents/reports/wallets-reports.component';
|
||||
import { WalletsDocumentsComponent } from 'koffing/wallets-documents/wallets-documents.component';
|
||||
import { DepositsComponent } from 'koffing/deposits/deposits.component';
|
||||
import { DepositComponent } from 'koffing/deposit/deposit.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -129,6 +134,14 @@ import { WithdrawalsComponent } from 'koffing/withdrawals/withdrawals.component'
|
||||
path: 'list',
|
||||
component: WalletsComponent
|
||||
},
|
||||
{
|
||||
path: 'deposits',
|
||||
component: DepositsComponent
|
||||
},
|
||||
{
|
||||
path: 'deposits/:depositID',
|
||||
component: DepositComponent
|
||||
},
|
||||
{
|
||||
path: 'withdrawals',
|
||||
component: WithdrawalsComponent
|
||||
@ -136,6 +149,21 @@ import { WithdrawalsComponent } from 'koffing/withdrawals/withdrawals.component'
|
||||
{
|
||||
path: 'withdrawals/:withdrawalID',
|
||||
component: WithdrawalComponent
|
||||
},
|
||||
{
|
||||
path: 'documents',
|
||||
component: WalletsDocumentsComponent,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'reports/' + WalletsReportType.withdrawalRegistry,
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'reports/:type',
|
||||
component: WalletsReportsComponent
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -20,16 +20,18 @@ import { AnalyticsModule } from 'koffing/analytics/analytics.module';
|
||||
import { ShopInfoModule } from 'koffing/shop-info/shop-info.module';
|
||||
import { PayoutsModule } from 'koffing/payouts/payouts.module';
|
||||
import { LandingContainerComponent } from './components/landing-container/landing-container.component';
|
||||
import { TopPanelComponent } from './components/top-panel/top-panel.component';
|
||||
import { TopPanelActionsComponent } from './components/top-panel-actions/top-panel-actions.component';
|
||||
import { ShopSelectorComponent } from 'koffing/root/components/shop-container/shop-top-panel/shop-selector/shop-selector.component';
|
||||
import { ShopTopPanelComponent } from 'koffing/root/components/shop-container/shop-top-panel/shop-top-panel.component';
|
||||
import { ShopSelectorComponent } from 'koffing/root/components/top-panel/shop-selector/shop-selector.component';
|
||||
import { TopPanelComponent } from 'koffing/root/components/top-panel/top-panel.component';
|
||||
import { ShopContainerComponent } from './components/shop-container/shop-container.component';
|
||||
import { WalletsModule } from 'koffing/wallets/wallets.module';
|
||||
import { WithdrawalModule } from 'koffing/withdrawal/withdrawal.module';
|
||||
import { WalletsContainerComponent } from './components/wallets-container/wallets-container.component';
|
||||
import { WalletsSidebarComponent } from './components/wallets-container/wallets-sidebar/wallets-sidebar.component';
|
||||
import { WithdrawalsModule } from 'koffing/withdrawals/withdrawals.module';
|
||||
import { WalletsDocumentsModule } from 'koffing/wallets-documents/wallets-documents.module';
|
||||
import { DepositsModule } from 'koffing/deposits/deposits.module';
|
||||
import { DepositModule } from 'koffing/deposit/deposit.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -51,17 +53,19 @@ import { WithdrawalsModule } from 'koffing/withdrawals/withdrawals.module';
|
||||
PayoutsModule,
|
||||
WalletsModule,
|
||||
WithdrawalModule,
|
||||
WithdrawalsModule
|
||||
WithdrawalsModule,
|
||||
WalletsDocumentsModule,
|
||||
DepositsModule,
|
||||
DepositModule
|
||||
],
|
||||
declarations: [
|
||||
ContainerComponent,
|
||||
LandingContainerComponent,
|
||||
ShopContainerComponent,
|
||||
SidebarComponent,
|
||||
ShopTopPanelComponent,
|
||||
TopPanelComponent,
|
||||
HttpErrorHandleComponent,
|
||||
ShopSelectorComponent,
|
||||
TopPanelComponent,
|
||||
TopPanelActionsComponent,
|
||||
WalletsContainerComponent,
|
||||
WalletsSidebarComponent
|
||||
|
@ -0,0 +1,10 @@
|
||||
div(*ngFor="let file of files")
|
||||
form.form-horizontal.css-form.form-list
|
||||
.form-group
|
||||
.col-xs-12.col-sm-2
|
||||
label.text-left Имя файла:
|
||||
.col-xs-12.col-sm-10
|
||||
div {{file}}
|
||||
.form-group
|
||||
.col-xs-12
|
||||
.btn.btn-sm.btn-primary((click)="downloadFile(file, file)") Загрузить
|
@ -0,0 +1,43 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import * as moment from 'moment';
|
||||
|
||||
import { DownloadFileService } from 'koffing/backend/wapi/download-file.service';
|
||||
import { Report } from 'koffing/backend/wapi/model/report';
|
||||
|
||||
@Component({
|
||||
selector: 'kof-report-files',
|
||||
templateUrl: 'report-files.component.pug'
|
||||
})
|
||||
export class ReportFilesComponent {
|
||||
@Input()
|
||||
public files: Report['files'];
|
||||
|
||||
@Input()
|
||||
public reportID: number;
|
||||
|
||||
constructor(private downloadFileService: DownloadFileService) {}
|
||||
|
||||
public downloadFile(fileID: string, fileName: string) {
|
||||
this.downloadFileService
|
||||
.downloadFile(
|
||||
{ fileID },
|
||||
{
|
||||
expiresAt: moment()
|
||||
.add(1, 'minute')
|
||||
.utc()
|
||||
.format()
|
||||
}
|
||||
)
|
||||
.subscribe(file => this.download(fileName, file.url));
|
||||
}
|
||||
|
||||
private download(fileName: string, url: string) {
|
||||
const a: any = document.createElement('a');
|
||||
document.body.appendChild(a);
|
||||
a.style = 'display: none';
|
||||
a.href = url;
|
||||
a.download = fileName;
|
||||
a.click();
|
||||
a.parentNode.removeChild(a);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { Report } from 'koffing/backend/wapi/model/report';
|
||||
|
||||
export class ReportTableItem {
|
||||
public report: Report;
|
||||
public isVisible: boolean;
|
||||
|
||||
constructor(report: Report, isVisible: boolean) {
|
||||
this.report = report;
|
||||
this.isVisible = isVisible;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'kofReportStatus'
|
||||
})
|
||||
export class ReportStatusPipe implements PipeTransform {
|
||||
private STATUSES = {
|
||||
pending: 'В процессе формирования',
|
||||
created: 'Сформирован'
|
||||
};
|
||||
|
||||
public transform(input: string): string {
|
||||
const status = this.STATUSES[input];
|
||||
return status ? status : input;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
table.table.table-striped
|
||||
thead
|
||||
tr
|
||||
th Идентификатор
|
||||
th.hidden-xs Временной интервал
|
||||
th Время создания
|
||||
th Статус
|
||||
th
|
||||
tbody(*ngFor="let item of filtered(reportItems)")
|
||||
tr
|
||||
td {{item.report.id}}
|
||||
td.hidden-xs {{item.report.fromTime | date: "dd.MM.yyyy"}} - {{item.report.toTime | date: "dd.MM.yyyy"}}
|
||||
td {{item.report.createdAt | date: "dd.MM.yyyy HH:mm:ss"}}
|
||||
td {{item.report.status | kofReportStatus}}
|
||||
td
|
||||
.pull-right(*ngIf="item.report.status === reportStatuses.created")
|
||||
button.btn.btn-xs.btn-default((click)="toggleFilesPanel(item)")
|
||||
| {{item.isVisible ? 'Скрыть' : 'Показать'}} файлы
|
||||
tr(*ngIf="item.isVisible")
|
||||
td.row(colspan="5")
|
||||
kof-report-files([files]="item.report.files", [reportID]="item.report.id")
|
@ -0,0 +1,38 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { get } from 'lodash';
|
||||
|
||||
import { Report, ReportStatus } from 'koffing/backend/wapi/model/report';
|
||||
import { ReportTableItem } from './report-item';
|
||||
import { ReportsFilter } from '../wallets-reports-filter';
|
||||
|
||||
@Component({
|
||||
selector: 'kof-search-reports-result',
|
||||
templateUrl: 'search-reports-result.component.pug'
|
||||
})
|
||||
export class SearchReportsResultComponent implements OnInit {
|
||||
@Input()
|
||||
public reports$: Observable<Report[]>;
|
||||
|
||||
@Input()
|
||||
public filter: ReportsFilter;
|
||||
|
||||
public reportItems: ReportTableItem[];
|
||||
public reportStatuses = ReportStatus;
|
||||
|
||||
public ngOnInit() {
|
||||
this.reports$.subscribe(reports => {
|
||||
this.reportItems = reports.map(report => new ReportTableItem(report, false));
|
||||
});
|
||||
}
|
||||
|
||||
public toggleFilesPanel(item: ReportTableItem) {
|
||||
item.isVisible = !item.isVisible;
|
||||
}
|
||||
|
||||
public filtered(reports: ReportTableItem[]): ReportTableItem[] {
|
||||
return this.filter && reports
|
||||
? reports.filter(report => get(report, this.filter.path) === this.filter.value)
|
||||
: reports;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
export interface ReportsFilter {
|
||||
path: string;
|
||||
value: any;
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
div.ui-calendar.search-form {
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
p-calendar.search-form > span {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
p-calendar.search-form input.ui-inputtext {
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
margin: 0;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
p-calendar.has-error input.ui-inputtext {
|
||||
border-color: #a94442;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
input.has-error {
|
||||
border-color: #a94442;
|
||||
}
|
||||
|
||||
input.ng-dirty.ng-invalid {
|
||||
border-color: #a94442;
|
||||
}
|
||||
|
||||
.create-report {
|
||||
margin-top: 24px;
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
form([formGroup]="form")
|
||||
.row
|
||||
.col-xs-12.col-sm-3
|
||||
.form-group
|
||||
label ID личности владельца:
|
||||
input.form-control(formControlName="identityID")
|
||||
.col-xs-12.col-sm-5
|
||||
kof-date-range((onSelect)="selectDateRange($event)")
|
||||
.col-xs-12.col-sm-4
|
||||
.row
|
||||
.col-xs-12
|
||||
button.btn.btn-default.create-report(
|
||||
(click)="createReports()",
|
||||
[disabled]="isLoading") Создать
|
||||
button.btn.btn-default.create-report(
|
||||
(click)="getReports()",
|
||||
[disabled]="isLoading") Обновить
|
||||
.row
|
||||
.col-xs-12
|
||||
kof-search-reports-result([reports$]="reports$", [filter]="filter")
|
||||
div(*ngIf="isLoading")
|
||||
a.fa.fa-cog.fa-spin.fa-fw.loading_spinner
|
@ -0,0 +1,92 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { FormGroup, FormBuilder } from '@angular/forms';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
|
||||
import { Report, ReportType } from 'koffing/backend/wapi/model/report';
|
||||
import { DateRange } from 'koffing/common/date-range/date-range';
|
||||
import { ReportsFilter } from './wallets-reports-filter';
|
||||
import { ReportsService } from 'koffing/backend/wapi/reports.service';
|
||||
|
||||
@Component({
|
||||
selector: 'kof-wallets-reports',
|
||||
templateUrl: 'wallets-reports.component.pug',
|
||||
styleUrls: ['./wallets-reports.component.less']
|
||||
})
|
||||
export class WalletsReportsComponent implements OnInit {
|
||||
public reports$: Subject<Report[]> = new Subject();
|
||||
public filter: ReportsFilter;
|
||||
public isLoading = false;
|
||||
public reportTypes = ReportType;
|
||||
public reportType: ReportType;
|
||||
|
||||
private dateRange: BehaviorSubject<DateRange> = new BehaviorSubject(null);
|
||||
private form: FormGroup;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private reportsService: ReportsService,
|
||||
private fb: FormBuilder
|
||||
) {
|
||||
this.form = this.fb.group({ identityID: [''] });
|
||||
this.form.valueChanges.subscribe(() => this.getReports());
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
this.route.params.subscribe(params => {
|
||||
this.reportType = params['type'];
|
||||
this.filter = {
|
||||
path: 'report.type',
|
||||
value: params['type']
|
||||
};
|
||||
});
|
||||
this.dateRange.subscribe(() => this.getReports());
|
||||
}
|
||||
|
||||
public selectDateRange(dateRange: DateRange) {
|
||||
this.dateRange.next(dateRange);
|
||||
}
|
||||
|
||||
public createReports() {
|
||||
this.isLoading = true;
|
||||
this.reportsService
|
||||
.createReport(
|
||||
{ identityID: this.form.value.identityID },
|
||||
{
|
||||
...this.dateRange.getValue(),
|
||||
reportType: this.reportType
|
||||
}
|
||||
)
|
||||
.subscribe(
|
||||
() => {
|
||||
this.isLoading = false;
|
||||
this.getReports();
|
||||
},
|
||||
e => this.failed(e)
|
||||
);
|
||||
}
|
||||
|
||||
private getReports() {
|
||||
const identityID = this.form.value.identityID;
|
||||
const dateRange = this.dateRange.getValue();
|
||||
this.reports$.next([]);
|
||||
if (identityID && dateRange && dateRange.fromTime && dateRange.toTime) {
|
||||
this.isLoading = true;
|
||||
const { fromTime, toTime } = dateRange;
|
||||
this.reportsService
|
||||
.getReports({ identityID }, { fromTime, toTime, type: this.reportType })
|
||||
.subscribe(
|
||||
reports => {
|
||||
this.isLoading = false;
|
||||
this.reports$.next(reports);
|
||||
},
|
||||
e => this.failed(e)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private failed(e: any) {
|
||||
console.error(e);
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
.row
|
||||
.col-xs-12.col-md-10.col-md-offset-1
|
||||
.x_panel
|
||||
.x_content
|
||||
ul.nav.nav-tabs.bar_tabs
|
||||
li.hand-cursor([routerLink]="['reports', reportTypes.withdrawalRegistry]", [routerLinkActive]="['active']")
|
||||
a Отчет по выводам
|
||||
.tab-content
|
||||
router-outlet
|
9
src/app/wallets-documents/wallets-documents.component.ts
Normal file
9
src/app/wallets-documents/wallets-documents.component.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ReportType } from 'koffing/backend/wapi/model/report';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'wallets-documents.component.pug'
|
||||
})
|
||||
export class WalletsDocumentsComponent {
|
||||
public reportTypes = ReportType;
|
||||
}
|
33
src/app/wallets-documents/wallets-documents.module.ts
Normal file
33
src/app/wallets-documents/wallets-documents.module.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import { CommonModule } from 'koffing/common/common.module';
|
||||
import { BackendModule } from 'koffing/backend/backend.module';
|
||||
import { WalletsDocumentsComponent } from './wallets-documents.component';
|
||||
import { SearchReportsResultComponent } from './reports/search-result/search-reports-result.component';
|
||||
import { ReportFilesComponent } from './reports/search-result/report-files/report-files.component';
|
||||
import { WalletsReportsComponent } from './reports/wallets-reports.component';
|
||||
import { ReportsService } from 'koffing/backend/wapi/reports.service';
|
||||
import { ReportStatusPipe as WalletsReportStatusPipe } from './reports/search-result/report-status.pipe';
|
||||
|
||||
@NgModule({
|
||||
providers: [ReportsService],
|
||||
imports: [
|
||||
RouterModule,
|
||||
BrowserModule,
|
||||
CommonModule,
|
||||
BackendModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule
|
||||
],
|
||||
declarations: [
|
||||
WalletsDocumentsComponent,
|
||||
SearchReportsResultComponent,
|
||||
ReportFilesComponent,
|
||||
WalletsReportsComponent,
|
||||
WalletsReportStatusPipe
|
||||
]
|
||||
})
|
||||
export class WalletsDocumentsModule {}
|
@ -40,7 +40,7 @@ form {
|
||||
font-weight: 500;
|
||||
}
|
||||
.shop-selector {
|
||||
@media (max-width: 991px) {
|
||||
@media (max-width: 600px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user