mirror of
https://github.com/valitydev/dashboard.git
synced 2024-11-06 02:25:23 +00:00
FE-867: Payments search form (#53)
This commit is contained in:
parent
30530003eb
commit
a56093744b
30
package-lock.json
generated
30
package-lock.json
generated
@ -1043,8 +1043,7 @@
|
||||
"@types/lodash": {
|
||||
"version": "4.14.135",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.135.tgz",
|
||||
"integrity": "sha512-Ed+tSZ9qM1oYpi5kzdsBuOzcAIn1wDW+e8TFJ50IMJMlSopGdJgKAbhHzN6h1E1OfjlGOr2JepzEWtg9NIfoNg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Ed+tSZ9qM1oYpi5kzdsBuOzcAIn1wDW+e8TFJ50IMJMlSopGdJgKAbhHzN6h1E1OfjlGOr2JepzEWtg9NIfoNg=="
|
||||
},
|
||||
"@types/lodash.get": {
|
||||
"version": "4.4.6",
|
||||
@ -1055,6 +1054,23 @@
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"@types/lodash.isempty": {
|
||||
"version": "4.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash.isempty/-/lodash.isempty-4.4.6.tgz",
|
||||
"integrity": "sha512-AauKrFlA4z3Usog5HLGDupKzkCP7h5KXGlfAcRGUfvTmL7guVuEzDSNI6lYJ7syO7J2RE2F47179pSLr26UHIw==",
|
||||
"requires": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"@types/lodash.mapvalues": {
|
||||
"version": "4.6.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash.mapvalues/-/lodash.mapvalues-4.6.6.tgz",
|
||||
"integrity": "sha512-Mt9eg3AqwAt5HShuOu8taiIYg0sLl4w3vDi0++E0VtiOtj9DqQHaxVr3wicVop0eDEqr5ENbht7vsLJlkMHL+w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||
@ -6488,6 +6504,16 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
|
||||
},
|
||||
"lodash.isempty": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz",
|
||||
"integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4="
|
||||
},
|
||||
"lodash.mapvalues": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz",
|
||||
"integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw="
|
||||
},
|
||||
"lodash.padend": {
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz",
|
||||
|
@ -31,6 +31,7 @@
|
||||
"@angular/platform-browser": "~8.0.2",
|
||||
"@angular/platform-browser-dynamic": "~8.0.2",
|
||||
"@angular/router": "~8.0.2",
|
||||
"@types/lodash.isempty": "^4.4.6",
|
||||
"acorn": "^6.1.1",
|
||||
"angular2-text-mask": "^9.0.0",
|
||||
"core-js": "^2.5.4",
|
||||
@ -42,6 +43,8 @@
|
||||
"hammerjs": "^2.0.8",
|
||||
"keycloak-angular": "^6.1.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.isempty": "^4.4.0",
|
||||
"lodash.mapvalues": "^4.6.0",
|
||||
"moment": "^2.24.0",
|
||||
"pdfmake": "^0.1.56",
|
||||
"rxjs": "~6.5.2",
|
||||
@ -58,6 +61,7 @@
|
||||
"@types/jasmine": "~3.3.12",
|
||||
"@types/jasminewd2": "~2.0.6",
|
||||
"@types/lodash.get": "^4.4.6",
|
||||
"@types/lodash.mapvalues": "^4.6.6",
|
||||
"@types/moment": "^2.13.0",
|
||||
"@types/node": "^11.13.10",
|
||||
"@types/pdfmake": "^0.1.5",
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { TextMaskConfig } from 'angular2-text-mask';
|
||||
|
||||
export const binMask: TextMaskConfig = {
|
||||
mask: [/\d/, /\d/, /\d/, /\d/, ' ', /\d/, /\d/, /\d/, /\d/],
|
||||
mask: [/\d/, /\d/, /\d/, /\d/, ' ', /\d/, /\d/],
|
||||
guide: false
|
||||
};
|
@ -0,0 +1,9 @@
|
||||
<input
|
||||
class="dsh-card-input-element"
|
||||
size="9"
|
||||
[formControl]="formControl"
|
||||
(change)="writeValue($event.target.value)"
|
||||
placeholder="0000 00"
|
||||
[textMask]="mask"
|
||||
/>
|
||||
<span class="dsh-card-input-spacer">** **** ****</span>
|
32
src/app/form-controls/bank-card-controls/bin-input/bin-input.component.ts
Executable file
32
src/app/form-controls/bank-card-controls/bin-input/bin-input.component.ts
Executable file
@ -0,0 +1,32 @@
|
||||
import { Component, forwardRef, HostBinding } from '@angular/core';
|
||||
import { MatFormFieldControl } from '@angular/material/form-field';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { binMask } from './bin-input-mask';
|
||||
import { CustomFormControl } from '../../custom-form-control';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-card-bin-input',
|
||||
templateUrl: 'bin-input.component.html',
|
||||
styleUrls: ['../card-controls.scss'],
|
||||
providers: [
|
||||
{ provide: MatFormFieldControl, useExisting: BINInputComponent },
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => BINInputComponent),
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class BINInputComponent extends CustomFormControl {
|
||||
static nextId = 0;
|
||||
|
||||
@HostBinding('id')
|
||||
id = `dsh-bin-input-${BINInputComponent.nextId++}`;
|
||||
|
||||
controlType = 'dsh-bin-input';
|
||||
|
||||
get mask() {
|
||||
return binMask;
|
||||
}
|
||||
}
|
59
src/app/form-controls/card-input/card-input.component.scss → src/app/form-controls/bank-card-controls/card-controls.scss
Executable file → Normal file
59
src/app/form-controls/card-input/card-input.component.scss → src/app/form-controls/bank-card-controls/card-controls.scss
Executable file → Normal file
@ -1,30 +1,29 @@
|
||||
:host {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dsh-card-input-element {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
font: inherit;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.dsh-card-input-element::placeholder {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
:host.floating .dsh-card-input-element::placeholder {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dsh-card-input-spacer {
|
||||
opacity: 0;
|
||||
user-select: none;
|
||||
transition: opacity 200ms;
|
||||
}
|
||||
|
||||
:host.floating .dsh-card-input-spacer {
|
||||
opacity: 1;
|
||||
}
|
||||
:host {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dsh-card-input-element {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.dsh-card-input-element::placeholder {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
:host.floating .dsh-card-input-element::placeholder {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dsh-card-input-spacer {
|
||||
opacity: 0;
|
||||
user-select: none;
|
||||
transition: opacity 200ms;
|
||||
}
|
||||
|
||||
:host.floating .dsh-card-input-spacer {
|
||||
opacity: 1;
|
||||
}
|
2
src/app/form-controls/bank-card-controls/index.ts
Normal file
2
src/app/form-controls/bank-card-controls/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './bin-input/bin-input.module';
|
||||
export * from './last-digits-input/last-digits-input.module';
|
@ -0,0 +1,9 @@
|
||||
<span class="dsh-card-input-spacer">**** **** **** </span>
|
||||
<input
|
||||
class="dsh-card-input-element"
|
||||
[formControl]="formControl"
|
||||
(change)="writeValue($event.target.value)"
|
||||
size="4"
|
||||
placeholder="0000"
|
||||
[textMask]="mask"
|
||||
/>
|
@ -0,0 +1,3 @@
|
||||
.dsh-card-input-element {
|
||||
text-align: right;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
import { Component, forwardRef, HostBinding } from '@angular/core';
|
||||
import { MatFormFieldControl } from '@angular/material/form-field';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { cardMask } from './last-digits-input-mask';
|
||||
import { CustomFormControl } from '../../custom-form-control';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-card-last-digits-input',
|
||||
templateUrl: 'last-digits-input.component.html',
|
||||
styleUrls: ['../card-controls.scss', 'last-digits-input.component.scss'],
|
||||
providers: [
|
||||
{ provide: MatFormFieldControl, useExisting: LastDigitsInputComponent },
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => LastDigitsInputComponent),
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class LastDigitsInputComponent extends CustomFormControl {
|
||||
static nextId = 0;
|
||||
|
||||
@HostBinding('id')
|
||||
id = `dsh-card-input-${LastDigitsInputComponent.nextId++}`;
|
||||
|
||||
get mask() {
|
||||
return cardMask;
|
||||
}
|
||||
}
|
@ -3,12 +3,12 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { A11yModule } from '@angular/cdk/a11y';
|
||||
import { TextMaskModule } from 'angular2-text-mask';
|
||||
|
||||
import { CardInputComponent } from './card-input.component';
|
||||
import { LastDigitsInputComponent } from './last-digits-input.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [FormsModule, ReactiveFormsModule, A11yModule, TextMaskModule],
|
||||
entryComponents: [CardInputComponent],
|
||||
declarations: [CardInputComponent],
|
||||
exports: [CardInputComponent]
|
||||
entryComponents: [LastDigitsInputComponent],
|
||||
declarations: [LastDigitsInputComponent],
|
||||
exports: [LastDigitsInputComponent]
|
||||
})
|
||||
export class CardInputModule {}
|
||||
export class LastDigitsInputModule {}
|
@ -1,8 +0,0 @@
|
||||
<input
|
||||
class="dsh-bin-input-element"
|
||||
[formControl]="formControl"
|
||||
size="9"
|
||||
placeholder="0000 0000"
|
||||
[textMask]="mask"
|
||||
/>
|
||||
<span class="dsh-bin-input-spacer"> **** ****</span>
|
@ -1,29 +0,0 @@
|
||||
:host {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dsh-bin-input-element {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.dsh-bin-input-element::placeholder {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
:host.floating .dsh-bin-input-element::placeholder {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dsh-bin-input-spacer {
|
||||
opacity: 0;
|
||||
user-select: none;
|
||||
transition: opacity 200ms;
|
||||
}
|
||||
|
||||
:host.floating .dsh-bin-input-spacer {
|
||||
opacity: 1;
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
import { FocusMonitor } from '@angular/cdk/a11y';
|
||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
import { Component, ElementRef, Input, OnDestroy, HostBinding } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { MatFormFieldControl } from '@angular/material/form-field';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import { binMask } from './bin-input-mask';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-bin-input',
|
||||
templateUrl: 'bin-input.component.html',
|
||||
styleUrls: ['bin-input.component.scss'],
|
||||
providers: [{ provide: MatFormFieldControl, useExisting: BINInputComponent }]
|
||||
})
|
||||
export class BINInputComponent implements MatFormFieldControl<number>, OnDestroy {
|
||||
static nextId = 0;
|
||||
|
||||
@HostBinding('class.floating')
|
||||
get shouldLabelFloat() {
|
||||
return this.focused || !this.empty;
|
||||
}
|
||||
|
||||
@HostBinding('id')
|
||||
id = `dsh-bin-input-${BINInputComponent.nextId++}`;
|
||||
|
||||
@HostBinding('attr.aria-describedby')
|
||||
describedBy = '';
|
||||
|
||||
formControl: FormControl;
|
||||
stateChanges = new Subject<void>();
|
||||
focused = false;
|
||||
ngControl = null;
|
||||
errorState = false;
|
||||
controlType = 'dsh-bin-input';
|
||||
|
||||
get empty() {
|
||||
return !this.formControl.value;
|
||||
}
|
||||
|
||||
@Input()
|
||||
get placeholder(): string {
|
||||
return this._placeholder;
|
||||
}
|
||||
set placeholder(value: string) {
|
||||
this._placeholder = value;
|
||||
this.stateChanges.next();
|
||||
}
|
||||
private _placeholder: string;
|
||||
|
||||
@Input()
|
||||
get required(): boolean {
|
||||
return this._required;
|
||||
}
|
||||
set required(value: boolean) {
|
||||
this._required = coerceBooleanProperty(value);
|
||||
this.stateChanges.next();
|
||||
}
|
||||
private _required = false;
|
||||
|
||||
@Input()
|
||||
get disabled(): boolean {
|
||||
return this._disabled;
|
||||
}
|
||||
set disabled(value: boolean) {
|
||||
this._disabled = coerceBooleanProperty(value);
|
||||
this._disabled ? this.formControl.disable() : this.formControl.enable();
|
||||
this.stateChanges.next();
|
||||
}
|
||||
private _disabled = false;
|
||||
|
||||
@Input()
|
||||
get value(): number {
|
||||
return this.formControl.value.replace(/\D/g, '');
|
||||
}
|
||||
set value(v: number) {
|
||||
this.formControl.setValue(v);
|
||||
this.stateChanges.next();
|
||||
}
|
||||
|
||||
get mask() {
|
||||
return binMask;
|
||||
}
|
||||
|
||||
constructor(private fm: FocusMonitor, private elRef: ElementRef<HTMLElement>) {
|
||||
this.formControl = new FormControl();
|
||||
fm.monitor(elRef, true).subscribe(origin => {
|
||||
this.focused = !!origin;
|
||||
this.stateChanges.next();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.stateChanges.complete();
|
||||
this.fm.stopMonitoring(this.elRef);
|
||||
}
|
||||
|
||||
setDescribedByIds(ids: string[]) {
|
||||
this.describedBy = ids.join(' ');
|
||||
}
|
||||
|
||||
onContainerClick(event: MouseEvent) {
|
||||
if ((event.target as Element).tagName.toLowerCase() !== 'input') {
|
||||
const input = this.elRef.nativeElement.querySelector('input');
|
||||
if (input) {
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
<span class="dsh-card-input-spacer">**** **** **** </span>
|
||||
<input class="dsh-card-input-element" [formControl]="formControl" size="4" placeholder="0000" [textMask]="mask" />
|
236
src/app/form-controls/card-input/card-input.component.ts → src/app/form-controls/custom-form-control.ts
Executable file → Normal file
236
src/app/form-controls/card-input/card-input.component.ts → src/app/form-controls/custom-form-control.ts
Executable file → Normal file
@ -1,110 +1,126 @@
|
||||
import { FocusMonitor } from '@angular/cdk/a11y';
|
||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
import { Component, ElementRef, Input, OnDestroy, HostBinding } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { MatFormFieldControl } from '@angular/material/form-field';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import { cardMask } from './card-input-mask';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-card-input',
|
||||
templateUrl: 'card-input.component.html',
|
||||
styleUrls: ['card-input.component.scss'],
|
||||
providers: [{ provide: MatFormFieldControl, useExisting: CardInputComponent }]
|
||||
})
|
||||
export class CardInputComponent implements MatFormFieldControl<number>, OnDestroy {
|
||||
static nextId = 0;
|
||||
|
||||
@HostBinding('class.floating')
|
||||
get shouldLabelFloat() {
|
||||
return this.focused || !this.empty;
|
||||
}
|
||||
|
||||
@HostBinding('id')
|
||||
id = `dsh-card-input-${CardInputComponent.nextId++}`;
|
||||
|
||||
@HostBinding('attr.aria-describedby')
|
||||
describedBy = '';
|
||||
|
||||
formControl: FormControl;
|
||||
stateChanges = new Subject<void>();
|
||||
focused = false;
|
||||
ngControl = null;
|
||||
errorState = false;
|
||||
controlType = 'dsh-card-input';
|
||||
|
||||
get empty() {
|
||||
return !this.formControl.value;
|
||||
}
|
||||
|
||||
@Input()
|
||||
get placeholder(): string {
|
||||
return this._placeholder;
|
||||
}
|
||||
set placeholder(value: string) {
|
||||
this._placeholder = value;
|
||||
this.stateChanges.next();
|
||||
}
|
||||
private _placeholder: string;
|
||||
|
||||
@Input()
|
||||
get required(): boolean {
|
||||
return this._required;
|
||||
}
|
||||
set required(value: boolean) {
|
||||
this._required = coerceBooleanProperty(value);
|
||||
this.stateChanges.next();
|
||||
}
|
||||
private _required = false;
|
||||
|
||||
@Input()
|
||||
get disabled(): boolean {
|
||||
return this._disabled;
|
||||
}
|
||||
set disabled(value: boolean) {
|
||||
this._disabled = coerceBooleanProperty(value);
|
||||
this._disabled ? this.formControl.disable() : this.formControl.enable();
|
||||
this.stateChanges.next();
|
||||
}
|
||||
private _disabled = false;
|
||||
|
||||
@Input()
|
||||
get value(): number {
|
||||
return this.formControl.value.replace(/\D/g, '');
|
||||
}
|
||||
set value(v: number) {
|
||||
this.formControl.setValue(v);
|
||||
this.stateChanges.next();
|
||||
}
|
||||
|
||||
get mask() {
|
||||
return cardMask;
|
||||
}
|
||||
|
||||
constructor(private fm: FocusMonitor, private elRef: ElementRef<HTMLElement>) {
|
||||
this.formControl = new FormControl();
|
||||
fm.monitor(elRef, true).subscribe(origin => {
|
||||
this.focused = !!origin;
|
||||
this.stateChanges.next();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.stateChanges.complete();
|
||||
this.fm.stopMonitoring(this.elRef);
|
||||
}
|
||||
|
||||
setDescribedByIds(ids: string[]) {
|
||||
this.describedBy = ids.join(' ');
|
||||
}
|
||||
|
||||
onContainerClick(event: MouseEvent) {
|
||||
if ((event.target as Element).tagName.toLowerCase() !== 'input') {
|
||||
const input = this.elRef.nativeElement.querySelector('input');
|
||||
if (input) {
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
import { MatFormFieldControl } from '@angular/material';
|
||||
import { DoCheck, ElementRef, HostBinding, Injector, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ControlValueAccessor, FormControl, NgControl } from '@angular/forms';
|
||||
import { Subject } from 'rxjs';
|
||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
import { FocusMonitor } from '@angular/cdk/a11y';
|
||||
|
||||
export class CustomFormControl implements MatFormFieldControl<any>, OnInit, OnDestroy, DoCheck, ControlValueAccessor {
|
||||
@HostBinding('class.floating')
|
||||
get shouldLabelFloat() {
|
||||
return this.focused || !this.empty;
|
||||
}
|
||||
|
||||
@HostBinding('attr.aria-describedby')
|
||||
describedBy = '';
|
||||
|
||||
formControl: FormControl;
|
||||
stateChanges = new Subject<void>();
|
||||
focused = false;
|
||||
ngControl = null;
|
||||
id = null;
|
||||
errorState = false;
|
||||
|
||||
get empty() {
|
||||
return !this._value;
|
||||
}
|
||||
|
||||
private _placeholder: string;
|
||||
@Input()
|
||||
get placeholder(): string {
|
||||
return this._placeholder;
|
||||
}
|
||||
set placeholder(value: string) {
|
||||
this._placeholder = value;
|
||||
this.stateChanges.next();
|
||||
}
|
||||
|
||||
private _required = false;
|
||||
@Input()
|
||||
get required(): boolean {
|
||||
return this._required;
|
||||
}
|
||||
set required(value: boolean) {
|
||||
this._required = coerceBooleanProperty(value);
|
||||
this.stateChanges.next();
|
||||
}
|
||||
|
||||
private _disabled = false;
|
||||
@Input()
|
||||
get disabled(): boolean {
|
||||
return this._disabled;
|
||||
}
|
||||
set disabled(value: boolean) {
|
||||
this._disabled = coerceBooleanProperty(value);
|
||||
this._disabled ? this.formControl.disable() : this.formControl.enable();
|
||||
this.stateChanges.next();
|
||||
}
|
||||
|
||||
_value: any = '';
|
||||
get value(): any {
|
||||
return this.formControl.value;
|
||||
}
|
||||
set value(value) {
|
||||
this._value = value;
|
||||
this.formControl.setValue(this._value);
|
||||
this.onChange(value);
|
||||
this.stateChanges.next();
|
||||
}
|
||||
|
||||
constructor(private fm: FocusMonitor, private elRef: ElementRef<HTMLElement>, public injector: Injector) {
|
||||
this.formControl = new FormControl();
|
||||
fm.monitor(elRef, true).subscribe(origin => {
|
||||
this.focused = !!origin;
|
||||
this.stateChanges.next();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.ngControl = this.injector.get(NgControl);
|
||||
if (this.ngControl != null) {
|
||||
this.ngControl.valueAccessor = this;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.stateChanges.complete();
|
||||
this.fm.stopMonitoring(this.elRef);
|
||||
}
|
||||
|
||||
ngDoCheck(): void {
|
||||
if (this.ngControl) {
|
||||
this.errorState = this.ngControl.invalid && this.ngControl.touched;
|
||||
this.stateChanges.next();
|
||||
}
|
||||
}
|
||||
|
||||
setDescribedByIds(ids: string[]) {
|
||||
this.describedBy = ids.join(' ');
|
||||
}
|
||||
|
||||
onContainerClick(event: MouseEvent) {
|
||||
if ((event.target as Element).tagName.toLowerCase() !== 'input') {
|
||||
const input = this.elRef.nativeElement.querySelector('input');
|
||||
if (input) {
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onChange = (value: any) => {};
|
||||
|
||||
onTouched = () => {};
|
||||
|
||||
writeValue(value: any): void {
|
||||
this.value = value;
|
||||
this.formControl.setValue(value);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (v: any) => void): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => void): void {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
}
|
@ -1,11 +1,10 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { TextMaskModule } from 'angular2-text-mask';
|
||||
|
||||
import { BinInputModule } from './bin-input/bin-input.module';
|
||||
import { CardInputModule } from './card-input/card-input.module';
|
||||
import { BinInputModule, LastDigitsInputModule } from './bank-card-controls';
|
||||
|
||||
@NgModule({
|
||||
imports: [BinInputModule, CardInputModule, TextMaskModule],
|
||||
exports: [BinInputModule, CardInputModule, TextMaskModule]
|
||||
imports: [BinInputModule, LastDigitsInputModule, TextMaskModule],
|
||||
exports: [BinInputModule, LastDigitsInputModule, TextMaskModule]
|
||||
})
|
||||
export class FormControlsModule {}
|
||||
|
@ -7,26 +7,28 @@
|
||||
(dshResized)="cardHeight = $event.height"
|
||||
>
|
||||
<div (dshResized)="baseContentHeight = $event.height">
|
||||
<div class="dsh-float-panel-base" fxLayout="row" fxLayoutGap="20px" fxLayoutAlign=" center">
|
||||
<div fxFlex><ng-content></ng-content></div>
|
||||
<button
|
||||
dsh-icon-button
|
||||
(click)="expandToggle()"
|
||||
@hide
|
||||
*ngIf="!expanded"
|
||||
class="dsh-float-panel-base-action"
|
||||
>
|
||||
<mat-icon>expand_more</mat-icon>
|
||||
</button>
|
||||
<div fxLayout fxLayout.sm="column" fxLayout.xs="column" [fxLayoutGap]="layoutGap">
|
||||
<!--need this additional div because invisible resize-sensor affects render-->
|
||||
<div fxFlex>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
<div *ngIf="!expanded" fxLayout class="dsh-float-panel-actions" fxLayoutAlign="center center">
|
||||
<button dsh-icon-button (click)="expandToggle()" @hide fxHide.sm fxHide.xs>
|
||||
<mat-icon>expand_more</mat-icon>
|
||||
</button>
|
||||
<button dsh-button (click)="expandToggle()" fxFlex fxHide fxShow.sm fxShow.xs>
|
||||
{{ 'common.showMore' | lc }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dsh-float-panel-more" [@expand]="expandTrigger" *ngIf="expanded">
|
||||
<div (dshResized)="setMoreContentHeight($event.height)">
|
||||
<div fxLayout="column" fxLayoutGap="20px">
|
||||
<div fxLayout="column" [fxLayoutGap]="layoutGap">
|
||||
<div class="dsh-float-panel-more-content">
|
||||
<ng-container *ngTemplateOutlet="floatPanelMore?.templateRef"></ng-container>
|
||||
</div>
|
||||
<div fxLayout="row" fxLayoutGap="20px" fxLayoutAlign="space-between">
|
||||
<div fxLayout [fxLayoutGap]="layoutGap" fxLayoutAlign="space-between">
|
||||
<button dsh-icon-button (click)="pinToggle()">
|
||||
<mat-icon svgIcon="place_outline"></mat-icon>
|
||||
</button>
|
||||
|
@ -15,15 +15,12 @@ $card-without-actions-padding: $card-padding - $actions-padding;
|
||||
}
|
||||
}
|
||||
|
||||
&-base {
|
||||
width: 100%;
|
||||
|
||||
&-action {
|
||||
margin-right: -$card-without-actions-padding;
|
||||
}
|
||||
&-actions {
|
||||
margin-right: -$card-without-actions-padding;
|
||||
}
|
||||
|
||||
&-more {
|
||||
margin-top: $card-padding;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
margin-left: -$card-without-actions-padding;
|
||||
@ -31,7 +28,7 @@ $card-without-actions-padding: $card-padding - $actions-padding;
|
||||
|
||||
&-content {
|
||||
padding-left: $actions-padding;
|
||||
padding-right: $actions-padding;
|
||||
padding-right: $actions-padding; // actions padding + head button width + margin compensation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ export class FloatPanelComponent {
|
||||
@coerce(v => coerceBooleanProperty(v), (v, self) => self.pinnedChange.emit(v))
|
||||
pinned = false;
|
||||
|
||||
@Input() layoutGap = '20px';
|
||||
|
||||
@ContentChild(FloatPanelMoreTemplateComponent, { static: false }) floatPanelMore: FloatPanelMoreTemplateComponent;
|
||||
|
||||
@ContentChild(FloatPanelActionsTemplateComponent, { static: false })
|
||||
|
@ -9,9 +9,10 @@ import { FloatPanelActionsTemplateComponent } from './templates/float-panel-acti
|
||||
import { ButtonModule } from '../../button';
|
||||
import { ResizedModule } from '../../resized';
|
||||
import { CardModule } from '../card';
|
||||
import { LocaleModule } from '../../locale';
|
||||
|
||||
@NgModule({
|
||||
imports: [MatIconModule, FlexLayoutModule, CommonModule, ButtonModule, ResizedModule, CardModule],
|
||||
imports: [MatIconModule, FlexLayoutModule, CommonModule, ButtonModule, ResizedModule, CardModule, LocaleModule],
|
||||
declarations: [FloatPanelComponent, FloatPanelMoreTemplateComponent, FloatPanelActionsTemplateComponent],
|
||||
exports: [FloatPanelComponent, FloatPanelMoreTemplateComponent, FloatPanelActionsTemplateComponent]
|
||||
})
|
||||
|
@ -1,3 +1 @@
|
||||
<div class="dsh-justify-wrapper" fxLayout fxLayoutAlign="start center" fxLayoutGap="20px">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
<ng-content></ng-content>
|
||||
|
@ -1,3 +1,3 @@
|
||||
.dsh-justify-wrapper > mat-form-field.mat-form-field > .mat-form-field-wrapper {
|
||||
dsh-justify-wrapper > mat-form-field.mat-form-field > .mat-form-field-wrapper {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
@ -2,12 +2,12 @@
|
||||
<dsh-card-content fxLayout="column" [formGroup]="formGroup">
|
||||
<mat-form-field>
|
||||
<mat-label>BIN банка-эмитента карты</mat-label>
|
||||
<dsh-bin-input></dsh-bin-input>
|
||||
<dsh-card-bin-input></dsh-card-bin-input>
|
||||
<mat-hint>Первые 4-8 цифр</mat-hint>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>Последние цифры номера карты</mat-label>
|
||||
<dsh-card-input></dsh-card-input>
|
||||
<dsh-card-last-digits-input></dsh-card-last-digits-input>
|
||||
<mat-hint>Последние 2-4 цифр</mat-hint>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
|
@ -0,0 +1,6 @@
|
||||
<dsh-button-toggle-group>
|
||||
<dsh-button-toggle *ngFor="let item of items" [checked]="item.checked" (click)="selectUnit(item.value)">
|
||||
<div *ngIf="item.dicPath; else moreBlock">{{ item.dicPath | lc }}</div>
|
||||
</dsh-button-toggle>
|
||||
</dsh-button-toggle-group>
|
||||
<ng-template #moreBlock> <mat-icon>more_horiz</mat-icon> </ng-template>
|
@ -0,0 +1,37 @@
|
||||
import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
|
||||
|
||||
import { SearchFormValue } from '../search-form-value';
|
||||
import { DaterangeSelectorService } from './daterange-selector.service';
|
||||
import { SelectorItem } from './select-item';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-daterange-selector',
|
||||
templateUrl: 'daterange-selector.component.html',
|
||||
providers: [DaterangeSelectorService]
|
||||
})
|
||||
export class DaterangeSelectorComponent implements OnChanges {
|
||||
@Input() value: SearchFormValue;
|
||||
@Output() selectDaterange: EventEmitter<SearchFormValue> = new EventEmitter();
|
||||
@Output() selectMore: EventEmitter<void> = new EventEmitter();
|
||||
|
||||
items: SelectorItem[];
|
||||
|
||||
constructor(private daterangeSelectorService: DaterangeSelectorService) {}
|
||||
|
||||
ngOnChanges({ value }: SimpleChanges) {
|
||||
if (value) {
|
||||
this.items = this.daterangeSelectorService.changeSelectorItems(this.value);
|
||||
if (this.daterangeSelectorService.isMoreChecked(this.items)) {
|
||||
this.selectMore.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectUnit(unit: 'today' | 'week' | 'month' | 'more') {
|
||||
if (unit === 'more') {
|
||||
this.selectMore.emit();
|
||||
return;
|
||||
}
|
||||
this.selectDaterange.emit(this.daterangeSelectorService.toSearchFormValue(unit));
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { MatIconModule } from '@angular/material';
|
||||
|
||||
import { DaterangeSelectorComponent } from './daterange-selector.component';
|
||||
import { LocaleModule } from '../../../../locale';
|
||||
import { ButtonToggleModule } from '../../../../button-toggle';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, LocaleModule, ButtonToggleModule, MatIconModule],
|
||||
declarations: [DaterangeSelectorComponent],
|
||||
exports: [DaterangeSelectorComponent]
|
||||
})
|
||||
export class DaterangeSelectorModule {}
|
@ -0,0 +1,95 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import * as moment from 'moment';
|
||||
import { Moment } from 'moment';
|
||||
|
||||
import { SearchFormValue } from '../search-form-value';
|
||||
import { SelectorItem } from './select-item';
|
||||
|
||||
@Injectable()
|
||||
export class DaterangeSelectorService {
|
||||
private endOfToday = moment().endOf('d');
|
||||
|
||||
changeSelectorItems(
|
||||
value: SearchFormValue,
|
||||
defaultState: SelectorItem[] = [
|
||||
{
|
||||
value: 'today',
|
||||
checked: false,
|
||||
dicPath: 'common.today'
|
||||
},
|
||||
{
|
||||
value: 'week',
|
||||
checked: false,
|
||||
dicPath: 'common.week'
|
||||
},
|
||||
{
|
||||
value: 'month',
|
||||
checked: false,
|
||||
dicPath: 'common.month'
|
||||
},
|
||||
{
|
||||
value: 'more',
|
||||
checked: true
|
||||
}
|
||||
]
|
||||
): SelectorItem[] {
|
||||
if (!value) {
|
||||
return defaultState;
|
||||
}
|
||||
const today = value.toTime.diff(this.endOfToday, 'd') === 0;
|
||||
if (today) {
|
||||
return this.applyValueToItems(defaultState, value);
|
||||
}
|
||||
return defaultState;
|
||||
}
|
||||
|
||||
toSearchFormValue(unit: 'today' | 'week' | 'month'): SearchFormValue {
|
||||
return {
|
||||
fromTime: this.toFromTime(unit),
|
||||
toTime: this.endOfToday
|
||||
};
|
||||
}
|
||||
|
||||
isMoreChecked(items: SelectorItem[]): boolean {
|
||||
return !!items.find(({ value, checked }) => value === 'more' && checked);
|
||||
}
|
||||
|
||||
private toFromTime(unit: 'today' | 'week' | 'month'): Moment {
|
||||
const m = moment().startOf('d');
|
||||
switch (unit) {
|
||||
case 'today':
|
||||
return m;
|
||||
case 'week':
|
||||
return m.subtract(1, 'w');
|
||||
case 'month':
|
||||
return m.subtract(1, 'M');
|
||||
}
|
||||
}
|
||||
|
||||
private applyValueToItems(items: SelectorItem[], { fromTime, toTime }: SearchFormValue): SelectorItem[] {
|
||||
const days = toTime.diff(fromTime, 'd');
|
||||
if (days < 0) {
|
||||
return items;
|
||||
}
|
||||
if (days === 0) {
|
||||
return this.changeChecked(items, 'today');
|
||||
}
|
||||
if (toTime.diff(fromTime, 'M') === 1) {
|
||||
return this.changeChecked(items, 'month');
|
||||
}
|
||||
if (toTime.diff(fromTime, 'w') === 1) {
|
||||
return this.changeChecked(items, 'week');
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private changeChecked(items: SelectorItem[], changeUnit: 'today' | 'week' | 'month' | 'more'): SelectorItem[] {
|
||||
return items.map(item => {
|
||||
let checked = false;
|
||||
if (item.value === changeUnit) {
|
||||
checked = true;
|
||||
}
|
||||
return { ...item, checked };
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export * from './daterange-selector.module';
|
@ -0,0 +1,5 @@
|
||||
export interface SelectorItem {
|
||||
value: 'today' | 'week' | 'month' | 'more';
|
||||
checked: boolean;
|
||||
dicPath?: string;
|
||||
}
|
@ -1,36 +1,5 @@
|
||||
<div fxLayout="column" fxLayoutGap="20px">
|
||||
<dsh-float-panel>
|
||||
<dsh-justify-wrapper>
|
||||
<dsh-button-toggle-group fxFlex="324px">
|
||||
<dsh-button-toggle value="today">
|
||||
{{ 'common.today' | lc }}
|
||||
</dsh-button-toggle>
|
||||
<dsh-button-toggle value="week">
|
||||
{{ 'common.week' | lc }}
|
||||
</dsh-button-toggle>
|
||||
<dsh-button-toggle value="month">
|
||||
{{ 'common.month' | lc }}
|
||||
</dsh-button-toggle>
|
||||
<dsh-button-toggle value="more">
|
||||
<mat-icon>more_horiz</mat-icon>
|
||||
</dsh-button-toggle>
|
||||
</dsh-button-toggle-group>
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'sections.operations.payments.filter.status' | lc }}</mat-label>
|
||||
<input matInput />
|
||||
</mat-form-field>
|
||||
</dsh-justify-wrapper>
|
||||
<dsh-float-panel-actions>
|
||||
<button dsh-button>
|
||||
{{ 'common.resetSearchParams' | lc }}
|
||||
</button>
|
||||
</dsh-float-panel-actions>
|
||||
<dsh-float-panel-more>
|
||||
<div style="height: 250px">
|
||||
text
|
||||
</div>
|
||||
</dsh-float-panel-more>
|
||||
</dsh-float-panel>
|
||||
<dsh-search-form (formValueChanges)="search($event)"></dsh-search-form>
|
||||
|
||||
<dsh-card>
|
||||
<dsh-card-content fxLayout="column" fxLayoutGap="15px">
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
|
||||
import { PaymentSearchFormValue } from './search-form/payment-search-form-value';
|
||||
|
||||
export interface PeriodicElement {
|
||||
name: string;
|
||||
position: number;
|
||||
@ -65,4 +67,8 @@ const ELEMENT_DATA: PeriodicElement[] = [
|
||||
export class PaymentsComponent {
|
||||
displayedColumns: string[] = ['amount', 'status', 'statusChanged', 'invoice', 'attributes', 'actions'];
|
||||
dataSource = new MatTableDataSource(ELEMENT_DATA);
|
||||
|
||||
search(paymentSearchFormValue: PaymentSearchFormValue) {
|
||||
console.log('Search!', paymentSearchFormValue);
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,24 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { MatFormFieldModule, MatInputModule, MatIconModule } from '@angular/material';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import {
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatIconModule,
|
||||
MatDatepickerModule,
|
||||
MatSelectModule
|
||||
} from '@angular/material';
|
||||
|
||||
import { PaymentsRoutingModule } from './payments-routing.module';
|
||||
import { PaymentsComponent } from './payments.component';
|
||||
import { LayoutModule } from '../../../../layout';
|
||||
import { ButtonModule } from '../../../../button';
|
||||
import { TableModule } from '../../../../table';
|
||||
import { ButtonToggleModule } from '../../../../button-toggle';
|
||||
import { LocaleModule } from '../../../../locale/locale.module';
|
||||
import { LocaleModule } from '../../../../locale';
|
||||
import { SearchFormComponent } from './search-form/search-form.component';
|
||||
import { FormControlsModule } from '../../../../form-controls';
|
||||
import { DaterangeSelectorModule } from '../daterange-selector';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -21,10 +30,14 @@ import { LocaleModule } from '../../../../locale/locale.module';
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
TableModule,
|
||||
ButtonToggleModule,
|
||||
MatIconModule,
|
||||
LocaleModule
|
||||
LocaleModule,
|
||||
ReactiveFormsModule,
|
||||
MatDatepickerModule,
|
||||
MatSelectModule,
|
||||
FormControlsModule,
|
||||
DaterangeSelectorModule
|
||||
],
|
||||
declarations: [PaymentsComponent]
|
||||
declarations: [PaymentsComponent, SearchFormComponent]
|
||||
})
|
||||
export class PaymentsModule {}
|
||||
|
@ -0,0 +1,31 @@
|
||||
import {
|
||||
BankCardPaymentSystem,
|
||||
BankCardTokenProvider,
|
||||
PaymentFlow,
|
||||
PaymentMethod,
|
||||
PaymentStatus,
|
||||
PaymentTerminalProvider
|
||||
} from '../../../../../api/capi/swagger-codegen';
|
||||
import { SearchFormValue } from '../../search-form-value';
|
||||
|
||||
export interface PaymentSearchFormValue extends SearchFormValue {
|
||||
limit: string;
|
||||
shopID?: string;
|
||||
paymentStatus?: PaymentStatus.StatusEnum;
|
||||
paymentFlow?: PaymentFlow.TypeEnum;
|
||||
paymentMethod?: PaymentMethod.MethodEnum;
|
||||
paymentTerminalProvider?: PaymentTerminalProvider;
|
||||
invoiceID?: string;
|
||||
paymentID?: string;
|
||||
payerEmail?: string;
|
||||
payerIP?: string;
|
||||
payerFingerprint?: string;
|
||||
customerID?: string;
|
||||
first6?: string;
|
||||
last4?: string;
|
||||
bankCardTokenProvider?: BankCardTokenProvider;
|
||||
bankCardPaymentSystem?: BankCardPaymentSystem;
|
||||
paymentAmount?: number;
|
||||
continuationToken?: string;
|
||||
rnn?: string;
|
||||
}
|
@ -0,0 +1,186 @@
|
||||
<dsh-float-panel [formGroup]="searchForm" novalidate [(expanded)]="expanded">
|
||||
<div
|
||||
fxLayout
|
||||
[fxLayoutGap]="layoutGap"
|
||||
fxLayout.sm="column"
|
||||
fxLayout.xs="column"
|
||||
fxLayoutAlign="space-between center"
|
||||
fxLayoutAlign.sm="space-between stretch"
|
||||
fxLayoutAlign.xs="space-between stretch"
|
||||
>
|
||||
<dsh-daterange-selector
|
||||
[value]="searchForm.value"
|
||||
(selectDaterange)="selectDaterange($event)"
|
||||
(selectMore)="expanded = true"
|
||||
fxFlex
|
||||
></dsh-daterange-selector>
|
||||
<dsh-justify-wrapper fxFlex fxLayout fxLayout.sm="column" fxLayout.xs="column" [fxLayoutGap]="layoutGap">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.shopID' | lc }}</mat-label>
|
||||
<input matInput formControlName="shopID" />
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.paymentStatus' | lc }}</mat-label>
|
||||
<mat-select formControlName="paymentStatus">
|
||||
<mat-option>
|
||||
{{ 'common.any' | lc }}
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let status of statuses" [value]="status">
|
||||
{{ 'common.paymentStatus.' + status | lc }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</dsh-justify-wrapper>
|
||||
</div>
|
||||
<dsh-float-panel-actions>
|
||||
<button dsh-button (click)="reset()">
|
||||
{{ 'common.resetSearchParams' | lc }}
|
||||
</button>
|
||||
</dsh-float-panel-actions>
|
||||
<dsh-float-panel-more>
|
||||
<div
|
||||
fxLayout
|
||||
fxLayout.sm="column"
|
||||
fxLayout.xs="column"
|
||||
[fxLayoutGap]="layoutGap"
|
||||
fxLayoutGap.sm="0"
|
||||
fxLayoutGap.xs="0"
|
||||
>
|
||||
<div fxFlex fxLayout fxLayout.xs="column" [fxLayoutGap]="layoutGap" fxLayoutGap.xs="0">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.fromTime' | lc }}</mat-label>
|
||||
<input required matInput formControlName="fromTime" [matDatepicker]="fromTime" />
|
||||
<mat-datepicker-toggle matSuffix [for]="fromTime"></mat-datepicker-toggle>
|
||||
<mat-datepicker #fromTime></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.toTime' | lc }}</mat-label>
|
||||
<input required matInput formControlName="toTime" [matDatepicker]="toTime" />
|
||||
<mat-datepicker-toggle matSuffix [for]="toTime"></mat-datepicker-toggle>
|
||||
<mat-datepicker #toTime></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div fxFlex fxLayout fxLayout.xs="column" [fxLayoutGap]="layoutGap" fxLayoutGap.xs="0">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.payerEmail' | lc }}</mat-label>
|
||||
<input matInput type="email" formControlName="payerEmail" />
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.paymentFlow' | lc }}</mat-label>
|
||||
<mat-select formControlName="paymentFlow">
|
||||
<mat-option>
|
||||
{{ 'common.any' | lc }}
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let flow of flows" [value]="flow">
|
||||
{{ 'common.paymentFlow.' + flow | lc }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
fxLayout
|
||||
fxLayout.sm="column"
|
||||
fxLayout.xs="column"
|
||||
[fxLayoutGap]="layoutGap"
|
||||
fxLayoutGap.sm="0"
|
||||
fxLayoutGap.xs="0"
|
||||
>
|
||||
<div fxFlex fxLayout fxLayout.xs="column" [fxLayoutGap]="layoutGap" fxLayoutGap.xs="0">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.rnn' | lc }}</mat-label>
|
||||
<input matInput formControlName="rnn" />
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.invoiceID' | lc }}</mat-label>
|
||||
<input matInput formControlName="invoiceID" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div fxFlex fxLayout fxLayout.xs="column" [fxLayoutGap]="layoutGap" fxLayoutGap.xs="0">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.bankCardTokenProvider' | lc }}</mat-label>
|
||||
<mat-select formControlName="bankCardTokenProvider">
|
||||
<mat-option>
|
||||
{{ 'common.any' | lc }}
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let tokenProvider of tokenProviders" [value]="tokenProvider">
|
||||
{{ 'common.bankCardTokenProvider.' + tokenProvider | lc }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.paymentMethod' | lc }}</mat-label>
|
||||
<mat-select formControlName="paymentMethod">
|
||||
<mat-option>
|
||||
{{ 'common.any' | lc }}
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let method of methods" [value]="method">
|
||||
{{ 'common.paymentMethod.' + method | lc }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
fxLayout
|
||||
fxLayout.sm="column"
|
||||
fxLayout.xs="column"
|
||||
[fxLayoutGap]="layoutGap"
|
||||
fxLayoutGap.sm="0"
|
||||
fxLayoutGap.xs="0"
|
||||
>
|
||||
<div fxFlex fxLayout fxLayout.xs="column" [fxLayoutGap]="layoutGap" fxLayoutGap.xs="0">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.paymentAmount' | lc }}</mat-label>
|
||||
<input matInput type="number" formControlName="paymentAmount" />
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.bankCardPaymentSystem' | lc }}</mat-label>
|
||||
<mat-select formControlName="bankCardPaymentSystem">
|
||||
<mat-option>
|
||||
{{ 'common.any' | lc }}
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let method of bankCardPaymentSystems" [value]="method">
|
||||
{{ 'common.bankCardPaymentSystem.' + method | lc }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div fxFlex fxLayout fxLayout.xs="column" [fxLayoutGap]="layoutGap" fxLayoutGap.xs="0">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.last4' | lc }}</mat-label>
|
||||
<dsh-card-last-digits-input formControlName="last4"></dsh-card-last-digits-input>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.first6' | lc }}</mat-label>
|
||||
<dsh-card-bin-input formControlName="first6"></dsh-card-bin-input>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
fxLayout
|
||||
fxLayout.sm="column"
|
||||
fxLayout.xs="column"
|
||||
[fxLayoutGap]="layoutGap"
|
||||
fxLayoutGap.sm="0"
|
||||
fxLayoutGap.xs="0"
|
||||
>
|
||||
<div fxFlex fxLayout fxLayout.xs="column" [fxLayoutGap]="layoutGap" fxLayoutGap.xs="0">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.paymentID' | lc }}</mat-label>
|
||||
<input matInput formControlName="paymentID" />
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.payerIP' | lc }}</mat-label>
|
||||
<input matInput formControlName="payerIP" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div fxFlex>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ localeBaseDir + '.payerFingerprint' | lc }}</mat-label>
|
||||
<input matInput formControlName="payerFingerprint" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</dsh-float-panel-more>
|
||||
</dsh-float-panel>
|
@ -0,0 +1,57 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
|
||||
import { SearchFormService } from './search-form.service';
|
||||
import { PaymentSearchFormValue } from './payment-search-form-value';
|
||||
import { SearchFormValue } from '../../search-form-value';
|
||||
import { BankCardPaymentSystem, BankCardTokenProvider, PaymentStatus } from '../../../../../api/capi/swagger-codegen';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-search-form',
|
||||
templateUrl: 'search-form.component.html',
|
||||
providers: [SearchFormService]
|
||||
})
|
||||
export class SearchFormComponent implements OnInit {
|
||||
@Input() valueDebounceTime = 300;
|
||||
@Input() layoutGap = '20px';
|
||||
|
||||
@Output() formValueChanges: EventEmitter<PaymentSearchFormValue> = new EventEmitter<PaymentSearchFormValue>();
|
||||
|
||||
localeBaseDir = 'sections.operations.payments.filter';
|
||||
searchForm: FormGroup;
|
||||
expanded = false;
|
||||
statuses: PaymentStatus.StatusEnum[] = ['pending', 'processed', 'captured', 'cancelled', 'refunded', 'failed'];
|
||||
flows = ['instant', 'hold'] as const;
|
||||
methods = ['bankCard', 'paymentTerminal'] as const;
|
||||
tokenProviders: BankCardTokenProvider[] = ['applepay', 'googlepay', 'samsungpay'];
|
||||
bankCardPaymentSystems: BankCardPaymentSystem[] = [
|
||||
'visa',
|
||||
'mastercard',
|
||||
'visaelectron',
|
||||
'maestro',
|
||||
'forbrugsforeningen',
|
||||
'dankort',
|
||||
'amex',
|
||||
'dinersclub',
|
||||
'discover',
|
||||
'unionpay',
|
||||
'jcb',
|
||||
'nspkmir'
|
||||
];
|
||||
|
||||
constructor(private searchFormService: SearchFormService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.searchForm = this.searchFormService.searchForm;
|
||||
this.formValueChanges.emit(this.searchForm.value);
|
||||
this.searchFormService.formValueChanges(this.valueDebounceTime).subscribe(v => this.formValueChanges.emit(v));
|
||||
}
|
||||
|
||||
selectDaterange(v: SearchFormValue) {
|
||||
this.searchFormService.applySearchFormValue(v);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.formValueChanges.emit(this.searchFormService.reset());
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import * as moment from 'moment';
|
||||
import { filter, map, debounceTime } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
|
||||
import { PaymentSearchFormValue } from './payment-search-form-value';
|
||||
import { toQueryParams } from './to-query-params';
|
||||
import { toFormValue } from './to-form-value';
|
||||
import { SearchFormValue } from '../../search-form-value';
|
||||
|
||||
@Injectable()
|
||||
export class SearchFormService {
|
||||
searchForm: FormGroup;
|
||||
private defaultValues: PaymentSearchFormValue;
|
||||
|
||||
constructor(private fb: FormBuilder, private router: Router, private route: ActivatedRoute) {
|
||||
this.searchForm = this.initForm();
|
||||
this.defaultValues = this.searchForm.value;
|
||||
this.route.queryParams
|
||||
.pipe(
|
||||
filter(queryParams => !isEmpty(queryParams)),
|
||||
map(queryParams => toFormValue<PaymentSearchFormValue>(queryParams))
|
||||
)
|
||||
.subscribe(formValue => this.searchForm.patchValue(formValue));
|
||||
this.searchForm.valueChanges
|
||||
.pipe(map(formValues => toQueryParams<PaymentSearchFormValue>(formValues)))
|
||||
.subscribe(queryParams => this.router.navigate([location.pathname], { queryParams }));
|
||||
}
|
||||
|
||||
formValueChanges(valueDebounceTime: number): Observable<PaymentSearchFormValue> {
|
||||
return this.searchForm.valueChanges.pipe(
|
||||
filter(() => this.searchForm.status === 'VALID'),
|
||||
debounceTime(valueDebounceTime)
|
||||
);
|
||||
}
|
||||
|
||||
reset(): PaymentSearchFormValue {
|
||||
this.searchForm.reset(this.defaultValues);
|
||||
return this.defaultValues;
|
||||
}
|
||||
|
||||
applySearchFormValue(v: SearchFormValue) {
|
||||
if (!v || !this.searchForm) {
|
||||
return;
|
||||
}
|
||||
this.searchForm.patchValue(v);
|
||||
}
|
||||
|
||||
private initForm(defaultLimit = 20): FormGroup {
|
||||
const form = this.fb.group({
|
||||
fromTime: moment()
|
||||
.subtract(1, 'month')
|
||||
.startOf('day'),
|
||||
toTime: moment().endOf('day'),
|
||||
limit: [defaultLimit, Validators.required],
|
||||
shopID: '',
|
||||
paymentStatus: '',
|
||||
paymentFlow: '',
|
||||
paymentMethod: '',
|
||||
paymentTerminalProvider: '',
|
||||
invoiceID: '',
|
||||
paymentID: '',
|
||||
payerEmail: '',
|
||||
payerIP: '',
|
||||
payerFingerprint: '',
|
||||
customerID: '',
|
||||
first6: '',
|
||||
last4: '',
|
||||
bankCardTokenProvider: '',
|
||||
bankCardPaymentSystem: '',
|
||||
paymentAmount: '',
|
||||
continuationToken: '',
|
||||
rnn: ''
|
||||
});
|
||||
return form;
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
import * as moment from 'moment';
|
||||
import { Params } from '@angular/router';
|
||||
|
||||
import { SearchFormValue } from '../../search-form-value';
|
||||
|
||||
export function toFormValue<T extends SearchFormValue>(obj: Params): T {
|
||||
return {
|
||||
...obj,
|
||||
fromTime: moment(obj.fromTime),
|
||||
toTime: moment(obj.toTime)
|
||||
} as T;
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import { Params } from '@angular/router';
|
||||
import mapValues from 'lodash.mapvalues';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
|
||||
import { SearchFormValue } from '../../search-form-value';
|
||||
|
||||
export function toQueryParams<T extends SearchFormValue>(obj: T): Params {
|
||||
const mapped = mapValues(obj, value => (isEmpty(value) ? null : value));
|
||||
return {
|
||||
...mapped,
|
||||
fromTime: obj.fromTime.utc().format(),
|
||||
toTime: obj.toTime.utc().format()
|
||||
};
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { Moment } from 'moment';
|
||||
|
||||
export interface SearchFormValue {
|
||||
fromTime: Moment;
|
||||
toTime: Moment;
|
||||
}
|
@ -1,12 +1,49 @@
|
||||
{
|
||||
"common": {
|
||||
"showMore": "Показать еще",
|
||||
"collapse": "Свернуть",
|
||||
"resetSearchParams": "Сбросить параметры поиска",
|
||||
"back": "Назад",
|
||||
"next": "Далее",
|
||||
"today": "Сегодня",
|
||||
"week": "Неделя",
|
||||
"month": "Месяц"
|
||||
"month": "Месяц",
|
||||
"any": "Любой",
|
||||
"paymentStatus": {
|
||||
"pending": "Запущен",
|
||||
"processed": "Обработан",
|
||||
"captured": "Подтвержден",
|
||||
"cancelled": "Отменен",
|
||||
"refunded": "Возвращен",
|
||||
"failed": "Неуспешен"
|
||||
},
|
||||
"paymentFlow": {
|
||||
"instant": "Мгновенный",
|
||||
"hold": "С удержанием"
|
||||
},
|
||||
"bankCardTokenProvider": {
|
||||
"applepay": "Apple Pay",
|
||||
"googlepay": "Google Pay",
|
||||
"samsungpay": "Samsung Pay"
|
||||
},
|
||||
"bankCardPaymentSystem": {
|
||||
"visa": "Visa",
|
||||
"mastercard": "Mastercard",
|
||||
"visaelectron": "Visa Electron",
|
||||
"maestro": "Maestro",
|
||||
"forbrugsforeningen": "Forbrugsforeningen",
|
||||
"dankort": "Dankort",
|
||||
"amex": "American Express",
|
||||
"dinersclub": "Diners Club International",
|
||||
"discover": "Discover Card",
|
||||
"unionpay": "UnionPay",
|
||||
"jcb": "JCB",
|
||||
"nspkmir": "MIR"
|
||||
},
|
||||
"paymentMethod": {
|
||||
"bankCard": "Банковская карта",
|
||||
"paymentTerminal": "Терминал"
|
||||
}
|
||||
},
|
||||
"sections": {
|
||||
"main": {
|
||||
@ -77,7 +114,23 @@
|
||||
"payments": {
|
||||
"title": "Платежи",
|
||||
"filter": {
|
||||
"status": "Статус платежа"
|
||||
"shopID": "Магазин",
|
||||
"fromTime": "Начало периода",
|
||||
"toTime": "Конец периода",
|
||||
"payerEmail": "Email плательщика",
|
||||
"paymentID": "Идентификатор платежа",
|
||||
"invoiceID": "Идентификатор инвойса",
|
||||
"paymentAmount": "Сумма платежа",
|
||||
"first6": "BIN банка-эмитента карты",
|
||||
"last4": "Последние цифры номера карты",
|
||||
"payerFingerprint": "Fingerprint плательщика",
|
||||
"payerIP": "IP-адрес плательщика",
|
||||
"paymentStatus": "Статус платежа",
|
||||
"paymentFlow": "Тип проведения платежа",
|
||||
"bankCardTokenProvider": "Провайдер платежных токенов",
|
||||
"bankCardPaymentSystem": "Платежаня система",
|
||||
"paymentMethod": "Метод оплаты",
|
||||
"rnn": "RNN платежа"
|
||||
},
|
||||
"refresh": "Обновить",
|
||||
"lastUpdate": "Последнее обновление",
|
||||
|
@ -21,6 +21,7 @@
|
||||
@import './overrides/mat-dialog';
|
||||
@import './overrides/mat-snack-bar';
|
||||
@import './overrides/mat-radio-button';
|
||||
@import './overrides/mat-form-field';
|
||||
@import '../app/timeline/timeline-theme';
|
||||
@import '../app/expand-panel/expand-panel-theme';
|
||||
@import '../app/dadata/dadata-theme';
|
||||
@ -31,6 +32,7 @@
|
||||
@include mat-theme-loaded-marker-override();
|
||||
@include mat-dialog-override();
|
||||
@include mat-snack-bar-override();
|
||||
@include mat-form-field-infix-override();
|
||||
}
|
||||
|
||||
@mixin dsh-typography($config) {
|
||||
|
5
src/styles/overrides/_mat-form-field.scss
Normal file
5
src/styles/overrides/_mat-form-field.scss
Normal file
@ -0,0 +1,5 @@
|
||||
@mixin mat-form-field-infix-override() {
|
||||
.mat-form-field-infix {
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user