mirror of
https://github.com/valitydev/control-center.git
synced 2024-11-06 02:25:17 +00:00
Chargebacks List & Details (#195)
This commit is contained in:
parent
526b46aa1f
commit
2e477bba09
6
.genryrc.json
Normal file
6
.genryrc.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"include": "node_modules/@rbkmoney/angular-templates/**",
|
||||
"template": {
|
||||
"prefix": "cc"
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
# VSC scaffolding
|
||||
|
||||
- [Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=alfnielsen.vsc-scaffolding)
|
||||
|
||||
- [Documentation](http://vsc-base.org/)
|
||||
- [Source code](https://github.com/alfnielsen/vsc-base)
|
@ -1,75 +0,0 @@
|
||||
import * as vsc from 'vsc-base';
|
||||
|
||||
const PREFIX = 'cc';
|
||||
|
||||
function getComponentName(name: string) {
|
||||
return `${vsc.toPascalCase(name)}Component`;
|
||||
}
|
||||
|
||||
function getComponentPath(name: string) {
|
||||
return `${vsc.toKebabCase(name)}.component`;
|
||||
}
|
||||
|
||||
function getClassName(name: string) {
|
||||
return `${PREFIX}-${vsc.toKebabCase(name)}`;
|
||||
}
|
||||
|
||||
export function Template(path: string, templatePath: string): vsc.vscTemplate {
|
||||
return {
|
||||
userInputs: [
|
||||
{
|
||||
title: 'Component name',
|
||||
argumentName: 'name',
|
||||
defaultValue: '',
|
||||
},
|
||||
],
|
||||
template: [
|
||||
{
|
||||
type: 'folder',
|
||||
name: (inputs) => vsc.toKebabCase(inputs.name),
|
||||
children: [
|
||||
{
|
||||
type: 'file',
|
||||
name: (inputs) => `${getComponentPath(inputs.name)}.html`,
|
||||
content: (inputs) => `
|
||||
<div class="${PREFIX}-${vsc.toKebabCase(inputs.name)}">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
`,
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
name: (inputs) => `${getComponentPath(inputs.name)}.scss`,
|
||||
content: (inputs) => `
|
||||
.${PREFIX}-${vsc.toKebabCase(inputs.name)} {
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
name: (inputs) => `${getComponentPath(inputs.name)}.ts`,
|
||||
content: (inputs) => `
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: '${PREFIX}-${vsc.toKebabCase(inputs.name)}',
|
||||
templateUrl: '${getComponentPath(inputs.name)}.html',
|
||||
styleUrls: ['${getComponentPath(inputs.name)}.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ${getComponentName(inputs.name)} {
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
name: (inputs) => `index.ts`,
|
||||
content: (inputs) => `
|
||||
export * from './${getComponentPath(inputs.name)}'
|
||||
`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
import * as vsc from 'vsc-base';
|
||||
|
||||
const PREFIX = 'cc';
|
||||
|
||||
function getModuletName(name: string) {
|
||||
return `${vsc.toPascalCase(name)}Module`;
|
||||
}
|
||||
|
||||
function getModulePath(name: string) {
|
||||
return `${vsc.toKebabCase(name)}.module`;
|
||||
}
|
||||
|
||||
function getComponentName(name: string) {
|
||||
return `${vsc.toPascalCase(name)}Component`;
|
||||
}
|
||||
|
||||
function getComponentPath(name: string) {
|
||||
return `${vsc.toKebabCase(name)}.component`;
|
||||
}
|
||||
|
||||
function getCssClassName(name: string) {
|
||||
return `${PREFIX}-${vsc.toKebabCase(name)}`;
|
||||
}
|
||||
|
||||
export function Template(path: string, templatePath: string): vsc.vscTemplate {
|
||||
return {
|
||||
userInputs: [
|
||||
{
|
||||
title: 'Component name',
|
||||
argumentName: 'name',
|
||||
defaultValue: '',
|
||||
},
|
||||
],
|
||||
template: [
|
||||
{
|
||||
type: 'folder',
|
||||
name: (inputs) => vsc.toKebabCase(inputs.name),
|
||||
children: [
|
||||
{
|
||||
type: 'file',
|
||||
name: (inputs) => `${getModulePath(inputs.name)}.ts`,
|
||||
content: (inputs) => `
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { ${getComponentName(inputs.name)} } from './${getComponentPath(inputs.name)}';
|
||||
|
||||
@NgModule({
|
||||
imports: [],
|
||||
declarations: [${getComponentName(inputs.name)}],
|
||||
exports: [${getComponentName(inputs.name)}]
|
||||
})
|
||||
export class ${getModuletName(inputs.name)} {}
|
||||
`,
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
name: (inputs) => `${getComponentPath(inputs.name)}.html`,
|
||||
content: (inputs) => `
|
||||
<div class="${PREFIX}-${vsc.toKebabCase(inputs.name)}">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
`,
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
name: (inputs) => `${getComponentPath(inputs.name)}.scss`,
|
||||
content: (inputs) => `
|
||||
.${getCssClassName(inputs.name)} {
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
name: (inputs) => `${getComponentPath(inputs.name)}.ts`,
|
||||
content: (inputs) => `
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: '${PREFIX}-${vsc.toKebabCase(inputs.name)}',
|
||||
templateUrl: '${getComponentPath(inputs.name)}.html',
|
||||
styleUrls: ['${getComponentPath(inputs.name)}.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ${getComponentName(inputs.name)} {
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
name: (inputs) => `index.ts`,
|
||||
content: (inputs) => `
|
||||
export * from './${getModulePath(inputs.name)}';
|
||||
export * from './${getComponentPath(inputs.name)}'
|
||||
`,
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
name: (inputs) => `_${vsc.toKebabCase(inputs.name)}-theme.scss`,
|
||||
content: (inputs) => `
|
||||
@import '~@angular/material/theming';
|
||||
|
||||
@mixin ${getCssClassName(inputs.name)}-theme($theme) {
|
||||
.${getCssClassName(inputs.name)} {
|
||||
}
|
||||
}
|
||||
|
||||
@mixin ${getCssClassName(inputs.name)}-typography($config) {
|
||||
.${getCssClassName(inputs.name)} {
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
416
package-lock.json
generated
416
package-lock.json
generated
@ -3742,6 +3742,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@rbkmoney/angular-templates": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://npm.pkg.github.com/download/@rbkmoney/angular-templates/0.1.2/03923ba6c89fd4ba87e9acf77f1074d3693dbcfdc0e775f707d0338b7ad4b997",
|
||||
"integrity": "sha512-io9DKeEbTQ0/h1UVkl18KjqWg3I2BxwHqvbSTpR7xkJ9J4VKqXS92BB1JYpOinQzY8W7qAxVYDosGBazhRIftA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"genry": "^0.15.0",
|
||||
"lodash": "^4.17.15"
|
||||
}
|
||||
},
|
||||
"@rbkmoney/partial-fetcher": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://npm.pkg.github.com/download/@rbkmoney/partial-fetcher/1.0.4/525ae43ea6950ba9a0282b5396cf094071b619850d6fe1b622d5d5d1c6079021",
|
||||
@ -3785,15 +3795,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/child-process-promise": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/child-process-promise/-/child-process-promise-2.2.1.tgz",
|
||||
"integrity": "sha512-xZ4kkF82YkmqPCERqV9Tj0bVQj3Tk36BqGlNgxv5XhifgDRhwAqp+of+sccksdpZRbbPsNwMOkmUqOnLgxKtGw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/color-name": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||
@ -3808,15 +3809,6 @@
|
||||
"del": "*"
|
||||
}
|
||||
},
|
||||
"@types/fs-extra": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.1.0.tgz",
|
||||
"integrity": "sha512-AInn5+UBFIK9FK5xc9yP5e3TQSPNNgjHByqYcj9g5elVBnDQcQL7PlO1CIRy2gWlbwK7UPYqi7vRvFA44dCmYQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/glob": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz",
|
||||
@ -3887,6 +3879,12 @@
|
||||
"integrity": "sha512-BneGN0J9ke24lBRn44hVHNeDlrXRYF+VRp0HbSUNnEZahXGAysHZIqnf/hER6aabdBgzM4YOV4jrR8gj4Zfi0g==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/pdfmake": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/pdfmake/-/pdfmake-0.1.9.tgz",
|
||||
@ -6135,29 +6133,6 @@
|
||||
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
|
||||
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
|
||||
},
|
||||
"child-process-promise": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/child-process-promise/-/child-process-promise-2.2.1.tgz",
|
||||
"integrity": "sha1-RzChHvYQ+tRQuPIjx50x172tgHQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cross-spawn": "^4.0.2",
|
||||
"node-version": "^1.0.0",
|
||||
"promise-polyfill": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"cross-spawn": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz",
|
||||
"integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lru-cache": "^4.0.1",
|
||||
"which": "^1.2.9"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"chokidar": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.2.1.tgz",
|
||||
@ -6432,6 +6407,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"coerce-property": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/coerce-property/-/coerce-property-0.3.2.tgz",
|
||||
"integrity": "sha512-jPD0ItShAyzLuvwNE9LzSBzcE5n0iD9ob2YaES6EkNhSEm/BZFU+NO9rVU9EL1s+fXesIVc7VP5OHOR7Kl82BA=="
|
||||
},
|
||||
"collection-visit": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
|
||||
@ -7635,6 +7615,12 @@
|
||||
"stream-shift": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"easy-stack": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz",
|
||||
"integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=",
|
||||
"dev": true
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||
@ -8057,6 +8043,12 @@
|
||||
"es5-ext": "~0.10.14"
|
||||
}
|
||||
},
|
||||
"event-pubsub": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz",
|
||||
"integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
@ -8811,17 +8803,6 @@
|
||||
"readable-stream": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
|
||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^4.0.0",
|
||||
"universalify": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"fs-minipass": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||
@ -8864,6 +8845,220 @@
|
||||
"integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==",
|
||||
"dev": true
|
||||
},
|
||||
"genry": {
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/genry/-/genry-0.15.0.tgz",
|
||||
"integrity": "sha512-XWjWNZzeXTKDtGsVyQSS6hnBlBDY/H7wMi0spj3bQv2yZ8+fVZy9M0TaS3W23NvRdNIZWx2iL6WGbLmqDQcNxA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cosmiconfig": "^6.0.0",
|
||||
"glob": "^7.1.6",
|
||||
"node-ipc": "^9.1.1",
|
||||
"ora": "^4.0.3",
|
||||
"pkg-up": "^3.1.0",
|
||||
"prettier": "^2.0.4",
|
||||
"prompts": "^2.3.2",
|
||||
"ts-node": "^8.8.2",
|
||||
"yargs": "^15.3.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"cosmiconfig": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
|
||||
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.1.0",
|
||||
"parse-json": "^5.0.0",
|
||||
"path-type": "^4.0.0",
|
||||
"yaml": "^1.7.2"
|
||||
}
|
||||
},
|
||||
"find-up": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^5.0.0",
|
||||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"dev": true
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
|
||||
"integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-locate": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-limit": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"parse-json": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz",
|
||||
"integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
"json-parse-even-better-errors": "^2.3.0",
|
||||
"lines-and-columns": "^1.1.6"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"dev": true
|
||||
},
|
||||
"require-main-filename": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
|
||||
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"yargs": {
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^6.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^4.1.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^4.2.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^18.1.2"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "18.1.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"gensync": {
|
||||
"version": "1.0.0-beta.1",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
|
||||
@ -10146,6 +10341,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"js-message": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz",
|
||||
"integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=",
|
||||
"dev": true
|
||||
},
|
||||
"js-queue": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz",
|
||||
"integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"easy-stack": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"js-sha256": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
|
||||
@ -10183,6 +10393,12 @@
|
||||
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
|
||||
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
|
||||
},
|
||||
"json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"dev": true
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
|
||||
@ -10592,6 +10808,12 @@
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
||||
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
|
||||
},
|
||||
"kleur": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
|
||||
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
|
||||
"dev": true
|
||||
},
|
||||
"less": {
|
||||
"version": "3.12.2",
|
||||
"resolved": "https://registry.npmjs.org/less/-/less-3.12.2.tgz",
|
||||
@ -10729,6 +10951,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"lines-and-columns": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
|
||||
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
|
||||
"dev": true
|
||||
},
|
||||
"loader-runner": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
|
||||
@ -11551,6 +11779,17 @@
|
||||
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
|
||||
"integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs="
|
||||
},
|
||||
"node-ipc": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz",
|
||||
"integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"event-pubsub": "4.3.0",
|
||||
"js-message": "1.0.5",
|
||||
"js-queue": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node-libs-browser": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
|
||||
@ -11594,12 +11833,6 @@
|
||||
"integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==",
|
||||
"dev": true
|
||||
},
|
||||
"node-version": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/node-version/-/node-version-1.2.0.tgz",
|
||||
"integrity": "sha512-ma6oU4Sk0qOoKEAymVoTvk8EdXEobdS7m/mAGhDJ8Rouugho48crHBORAmy5BoOcv8wraPM6xumapQp5hl4iIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-package-data": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
||||
@ -12459,6 +12692,23 @@
|
||||
"readable-stream": "^2.1.5"
|
||||
}
|
||||
},
|
||||
"parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"callsites": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"parse-asn1": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
|
||||
@ -12645,6 +12895,15 @@
|
||||
"find-up": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"pkg-up": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz",
|
||||
"integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"find-up": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"png-js": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz",
|
||||
@ -13413,12 +13672,6 @@
|
||||
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
|
||||
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM="
|
||||
},
|
||||
"promise-polyfill": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz",
|
||||
"integrity": "sha1-36lpQ+qcEh/KTem1hoyznTRy4Fc=",
|
||||
"dev": true
|
||||
},
|
||||
"promise-retry": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz",
|
||||
@ -13437,6 +13690,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"prompts": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz",
|
||||
"integrity": "sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kleur": "^3.0.3",
|
||||
"sisteransi": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"protoduck": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz",
|
||||
@ -14836,6 +15099,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"sisteransi": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
||||
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
|
||||
"dev": true
|
||||
},
|
||||
"skipper-proto": {
|
||||
"version": "github:rbkmoney/skipper-proto#d33d87cd9080925861f755b11ee1f16378076f74",
|
||||
"from": "github:rbkmoney/skipper-proto#d33d87cd9080925861f755b11ee1f16378076f74"
|
||||
@ -17133,19 +17402,6 @@
|
||||
"integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=",
|
||||
"dev": true
|
||||
},
|
||||
"vsc-base": {
|
||||
"version": "0.9.10",
|
||||
"resolved": "https://registry.npmjs.org/vsc-base/-/vsc-base-0.9.10.tgz",
|
||||
"integrity": "sha512-2fINBTgn4j4COH4vh+VkRiyHjvvyk+uFeSJQtizorqzUox0xocmgCEPTugwusE55rQVet2VqmqDF6QjFQw9pGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/child-process-promise": "^2.2.1",
|
||||
"@types/fs-extra": "^5.1.0",
|
||||
"child-process-promise": "^2.2.1",
|
||||
"fs-extra": "^7.0.1",
|
||||
"typescript": "^3.4.5"
|
||||
}
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz",
|
||||
@ -18578,6 +18834,12 @@
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
|
||||
},
|
||||
"yaml": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz",
|
||||
"integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "12.0.5",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
|
||||
|
@ -34,6 +34,7 @@
|
||||
"angular-file": "3.0.1",
|
||||
"angular2-prettyjson": "3.0.1",
|
||||
"ank-proto": "git+ssh://git@github.com:rbkmoney/ank-proto.git#d638e44eb8632fd62f0d6730294e51637babcc78",
|
||||
"coerce-property": "^0.3.2",
|
||||
"damsel": "git+ssh://git@github.com/rbkmoney/damsel.git#895f01ac1b71e108eb8d5dce1df0ef2e31dc0152",
|
||||
"file-storage-proto": "git+ssh://git@github.com:rbkmoney/file-storage-proto.git#281e1ca4cea9bf32229a6c389f0dcf5d49c05a0b",
|
||||
"fistful-proto": "git+ssh://git@github.com/rbkmoney/fistful-proto.git#c2113c853ed71a34bb6468b9a6cf9b468967af84",
|
||||
@ -61,6 +62,7 @@
|
||||
"@angular-devkit/build-angular": "^0.1000.6",
|
||||
"@angular/cli": "^10.0.6",
|
||||
"@angular/compiler-cli": "^10.0.10",
|
||||
"@rbkmoney/angular-templates": "^0.1.2",
|
||||
"@types/del": "^4.0.0",
|
||||
"@types/glob": "^7.1.3",
|
||||
"@types/humanize-duration": "~3.18.0",
|
||||
@ -87,7 +89,6 @@
|
||||
"ts-node": "~8.8.2",
|
||||
"tslint": "~6.1.0",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"typescript": "~3.9.7",
|
||||
"vsc-base": "^0.9.10"
|
||||
"typescript": "~3.9.7"
|
||||
}
|
||||
}
|
||||
|
42
src/app/query-dsl/chargebacks.ts
Normal file
42
src/app/query-dsl/chargebacks.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import {
|
||||
InvoicePaymentChargebackCategory,
|
||||
InvoicePaymentChargebackStage,
|
||||
InvoicePaymentChargebackStatus,
|
||||
} from '../thrift-services/damsel/gen-model/domain';
|
||||
|
||||
export const chargebackStatuses: (keyof InvoicePaymentChargebackStatus)[] = [
|
||||
'accepted',
|
||||
'cancelled',
|
||||
'pending',
|
||||
'rejected',
|
||||
];
|
||||
|
||||
export const chargebackCategories: (keyof InvoicePaymentChargebackCategory)[] = [
|
||||
'authorisation',
|
||||
'dispute',
|
||||
'fraud',
|
||||
'processing_error',
|
||||
];
|
||||
|
||||
export const chargebackStages: (keyof InvoicePaymentChargebackStage)[] = [
|
||||
'arbitration',
|
||||
'chargeback',
|
||||
'pre_arbitration',
|
||||
];
|
||||
|
||||
// https://github.com/rbkmoney/magista#chargebacks
|
||||
export interface Chargebacks {
|
||||
merchant_id?: string;
|
||||
shop_ids?: string;
|
||||
invoice_id?: string;
|
||||
payment_id?: string;
|
||||
chargeback_id?: string;
|
||||
from_time?: string;
|
||||
to_time?: string;
|
||||
// список интересующих статусов
|
||||
chargeback_statuses?: (keyof InvoicePaymentChargebackStatus)[];
|
||||
// список интересующих категорий
|
||||
chargeback_categories?: (keyof InvoicePaymentChargebackCategory)[];
|
||||
// список интересующих этапов
|
||||
chargeback_stages?: (keyof InvoicePaymentChargebackStage)[];
|
||||
}
|
5
src/app/query-dsl/create-dsl.ts
Normal file
5
src/app/query-dsl/create-dsl.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { QueryDSL } from './query-dsl';
|
||||
|
||||
export function createDSL(query: QueryDSL['query']): string {
|
||||
return JSON.stringify({ query });
|
||||
}
|
@ -1 +1,3 @@
|
||||
export * from './query-dsl';
|
||||
export * from './create-dsl';
|
||||
export * from './chargebacks';
|
||||
|
@ -1,11 +1,15 @@
|
||||
import { Chargebacks } from './chargebacks';
|
||||
import { Deposit } from './deposit';
|
||||
import { ModelParams } from './model-params';
|
||||
import { Params } from './params';
|
||||
import { Payment } from './payment';
|
||||
|
||||
export type ChargebacksParams = Params & ModelParams & Chargebacks;
|
||||
|
||||
export interface QueryDSL {
|
||||
query: {
|
||||
payments?: Payment & Params & ModelParams;
|
||||
deposits?: Deposit & Params & ModelParams;
|
||||
chargebacks?: ChargebacksParams;
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
@import '~@angular/material/theming';
|
||||
|
||||
@mixin cc-chargeback-details-theme($theme) {
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
.cc-chargeback-details-caption {
|
||||
color: mat-color($foreground, secondary-text);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<div fxLayout="column" fxLayoutGap="32px">
|
||||
<h1 class="mat-headline">Change chargeback status</h1>
|
||||
<mat-dialog-content [formGroup]="form" fxLayout="column" fxLayoutGap="24px">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>Status</mat-label>
|
||||
<mat-select formControlName="status" required>
|
||||
<mat-option *ngFor="let c of statuses" [value]="c">
|
||||
{{ c }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<input
|
||||
matInput
|
||||
formControlName="date"
|
||||
[matDatepicker]="datepicker"
|
||||
placeholder="Date of decision (optional)"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="datepicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #datepicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions fxLayout fxLayoutAlign="space-between">
|
||||
<button mat-button (click)="cancel()">CANCEL</button>
|
||||
<button mat-button color="primary" [disabled]="form.invalid" (click)="changeStatus()">
|
||||
CHANGE STATUS
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
</div>
|
@ -0,0 +1,64 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
|
||||
import {
|
||||
ChangeChargebackStatusDialogService,
|
||||
Statuses,
|
||||
} from './change-chargeback-status-dialog.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-change-chargeback-status-dialog',
|
||||
templateUrl: 'change-chargeback-status-dialog.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [ChangeChargebackStatusDialogService],
|
||||
})
|
||||
export class ChangeChargebackStatusDialogComponent {
|
||||
static defaultConfig: MatDialogConfig = {
|
||||
disableClose: true,
|
||||
width: '552px',
|
||||
};
|
||||
|
||||
form = this.fb.group({
|
||||
status: '',
|
||||
date: '',
|
||||
});
|
||||
statuses = Statuses;
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
public params: {
|
||||
invoiceID: string;
|
||||
paymentID: string;
|
||||
chargebackID: string;
|
||||
},
|
||||
private dialogRef: MatDialogRef<ChangeChargebackStatusDialogComponent>,
|
||||
private snackBar: MatSnackBar,
|
||||
private changeChargebackStatusDialogService: ChangeChargebackStatusDialogService
|
||||
) {}
|
||||
|
||||
changeStatus() {
|
||||
const { status, date } = this.form.value;
|
||||
this.changeChargebackStatusDialogService
|
||||
.changeStatus({
|
||||
status,
|
||||
date: date ? date.utc().format() : undefined,
|
||||
invoiceID: this.params.invoiceID,
|
||||
paymentID: this.params.paymentID,
|
||||
chargebackID: this.params.chargebackID,
|
||||
})
|
||||
.subscribe(
|
||||
() => this.dialogRef.close(),
|
||||
(error) => {
|
||||
console.error(error);
|
||||
this.snackBar.open('An error occurred while chargeback changing status', 'OK');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { PaymentProcessingService } from 'src/app/thrift-services/damsel/payment-processing.service';
|
||||
|
||||
export const Statuses = ['Accept', 'Cancell', 'Reject'] as const;
|
||||
|
||||
@Injectable()
|
||||
export class ChangeChargebackStatusDialogService {
|
||||
constructor(private paymentProcessingService: PaymentProcessingService) {}
|
||||
|
||||
changeStatus({
|
||||
status,
|
||||
date,
|
||||
invoiceID,
|
||||
paymentID,
|
||||
chargebackID,
|
||||
}: {
|
||||
status: string;
|
||||
date: string;
|
||||
invoiceID: string;
|
||||
paymentID: string;
|
||||
chargebackID: string;
|
||||
}) {
|
||||
const fnByStatus = {
|
||||
Accept: this.paymentProcessingService.acceptChargeback,
|
||||
Cancell: this.paymentProcessingService.cancelChargeback,
|
||||
Reject: this.paymentProcessingService.rejectChargeback,
|
||||
};
|
||||
return fnByStatus[status as typeof Statuses[number]].bind(this.paymentProcessingService)(
|
||||
invoiceID,
|
||||
paymentID,
|
||||
chargebackID,
|
||||
Object.assign({}, !!date && { occurred_at: date })
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export * from './change-chargeback-status-dialog.component';
|
||||
export * from './change-chargeback-status-dialog.service';
|
@ -0,0 +1,18 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { AppAuthGuardService } from '../../app-auth-guard.service';
|
||||
import { ChargebackDetailsComponent } from './chargeback-details.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: '',
|
||||
component: ChargebackDetailsComponent,
|
||||
canActivate: [AppAuthGuardService],
|
||||
},
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class ChargebackDetailsRoutingModule {}
|
@ -0,0 +1,67 @@
|
||||
<div fxLayout="column" fxLayoutGap="24px">
|
||||
<div fxLayout="column" fxLayoutGap="8px">
|
||||
<div fxLayout fxLayoutAlign="space-between">
|
||||
<cc-headline fxLayout fxLayoutAlign="space-between">Chargeback</cc-headline>
|
||||
<div class="cc-headline">
|
||||
{{ (chargeback$ | async)?.status | ccMapUnion: mapStatus }}
|
||||
</div>
|
||||
</div>
|
||||
<div fxLayout fxLayoutAlign="space-between">
|
||||
<div class="cc-subheading-1 cc-chargeback-details-caption">
|
||||
#{{ (chargeback$ | async)?.id }}
|
||||
</div>
|
||||
<div class="cc-subheading-1 cc-chargeback-details-caption">
|
||||
{{ (chargeback$ | async)?.stage | ccMapUnion: mapStage }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mat-card *ngIf="chargeback$ | async as chargeback">
|
||||
<mat-card-content fxLayout="column" fxLayoutGap="24px">
|
||||
<h2 class="cc-subheading-1">Chargeback data</h2>
|
||||
<div gdColumns="1fr 1fr 1fr" gdGap="16px">
|
||||
<cc-details-item gdColumn="1 / span 3" title="Created At">
|
||||
{{ chargeback.created_at | date: 'dd.MM.yy HH:mm:ss' }}
|
||||
</cc-details-item>
|
||||
<cc-details-item *ngIf="chargeback.levy" title="Levy Amount">
|
||||
{{ chargeback.levy.amount | ccFormatAmount }}
|
||||
{{ chargeback.levy.currency.symbolic_code | ccCurrency }}
|
||||
</cc-details-item>
|
||||
<cc-details-item *ngIf="chargeback.body" title="Body Amount">
|
||||
{{ chargeback.body.amount | ccFormatAmount }}
|
||||
{{ chargeback.body.currency.symbolic_code | ccCurrency }}
|
||||
</cc-details-item>
|
||||
</div>
|
||||
|
||||
<mat-divider inset></mat-divider>
|
||||
<h2 class="cc-title">Reason</h2>
|
||||
<div gdColumns="1fr 1fr 1fr" gdGap="16px">
|
||||
<cc-details-item title="Category">
|
||||
{{ chargeback.reason.category | ccUnionKey }}
|
||||
</cc-details-item>
|
||||
<cc-details-item title="Code">
|
||||
{{ chargeback.reason.code }}
|
||||
</cc-details-item>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="(shop$ | async) && (payment$ | async)">
|
||||
<mat-divider inset></mat-divider>
|
||||
<div fxLayout fxLayoutAlign="space-between center">
|
||||
<h2 class="cc-title">Payment</h2>
|
||||
<button mat-icon-button (click)="navigateToPayment()">
|
||||
<mat-icon>open_in_new</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<cc-payment-main-info
|
||||
[shop]="shop$ | async"
|
||||
[payment]="payment$ | async"
|
||||
></cc-payment-main-info>
|
||||
</ng-container>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card>
|
||||
<mat-card-content fxLayout fxLayoutAlign="space-between">
|
||||
<button mat-button (click)="changeStatus()">CHANGE STATUS</button>
|
||||
<button mat-button (click)="reopen()">REOPEN</button>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
@ -0,0 +1,99 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Router } from '@angular/router';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { first, switchMap } from 'rxjs/operators';
|
||||
import {
|
||||
InvoicePaymentChargebackStage,
|
||||
InvoicePaymentChargebackStatus,
|
||||
} from 'src/app/thrift-services/damsel/gen-model/domain';
|
||||
|
||||
import { ChangeChargebackStatusDialogComponent } from './change-chargeback-status-dialog';
|
||||
import { ChargebackDetailsService } from './chargeback-details.service';
|
||||
import { ReopenChargebackDialogComponent } from './reopen-chargeback-dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-chargeback-details',
|
||||
templateUrl: 'chargeback-details.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [ChargebackDetailsService],
|
||||
})
|
||||
export class ChargebackDetailsComponent {
|
||||
mapStatus: { [N in keyof InvoicePaymentChargebackStatus] } = {
|
||||
accepted: 'Accepted',
|
||||
cancelled: 'Cancelled',
|
||||
pending: 'Pending',
|
||||
rejected: 'Rejected',
|
||||
};
|
||||
|
||||
mapStage: { [N in keyof InvoicePaymentChargebackStage] } = {
|
||||
arbitration: 'Arbitration',
|
||||
chargeback: 'Chargeback',
|
||||
pre_arbitration: 'Pre-arbitration',
|
||||
};
|
||||
|
||||
chargeback$ = this.chargebackDetailsService.chargeback$;
|
||||
shop$ = this.chargebackDetailsService.shop$;
|
||||
payment$ = this.chargebackDetailsService.payment$;
|
||||
|
||||
constructor(
|
||||
private chargebackDetailsService: ChargebackDetailsService,
|
||||
private dialog: MatDialog,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
changeStatus() {
|
||||
combineLatest([this.chargeback$, this.payment$])
|
||||
.pipe(
|
||||
first(),
|
||||
switchMap(([{ id: chargebackID }, { id: paymentID, invoice_id: invoiceID }]) =>
|
||||
this.dialog
|
||||
.open(ChangeChargebackStatusDialogComponent, {
|
||||
...ChangeChargebackStatusDialogComponent.defaultConfig,
|
||||
data: {
|
||||
invoiceID,
|
||||
paymentID,
|
||||
chargebackID,
|
||||
},
|
||||
})
|
||||
.afterClosed()
|
||||
)
|
||||
)
|
||||
.subscribe(() => this.chargebackDetailsService.loadChargeback());
|
||||
}
|
||||
|
||||
reopen() {
|
||||
combineLatest([this.chargeback$, this.payment$])
|
||||
.pipe(
|
||||
first(),
|
||||
switchMap(([{ id: chargebackID }, { id: paymentID, invoice_id: invoiceID }]) =>
|
||||
this.dialog
|
||||
.open(ReopenChargebackDialogComponent, {
|
||||
...ReopenChargebackDialogComponent.defaultConfig,
|
||||
data: {
|
||||
invoiceID,
|
||||
paymentID,
|
||||
chargebackID,
|
||||
},
|
||||
})
|
||||
.afterClosed()
|
||||
)
|
||||
)
|
||||
.subscribe(() => this.chargebackDetailsService.loadChargeback());
|
||||
}
|
||||
|
||||
navigateToPayment() {
|
||||
this.payment$
|
||||
.pipe(first())
|
||||
.subscribe((p) =>
|
||||
this.router.navigate([
|
||||
'party',
|
||||
p.owner_id,
|
||||
'invoice',
|
||||
p.invoice_id,
|
||||
'payment',
|
||||
p.id,
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
|
||||
import { DetailsItemModule } from '@cc/components/details-item';
|
||||
import { HeadlineModule } from '@cc/components/headline';
|
||||
import { CommonPipesModule } from '@cc/pipes/common-pipes.module';
|
||||
|
||||
import { PaymentMainInfoModule } from '../payment-details/payment-main-info';
|
||||
import { ChangeChargebackStatusDialogComponent } from './change-chargeback-status-dialog';
|
||||
import { ChargebackDetailsRoutingModule } from './chargeback-details-routing.module';
|
||||
import { ChargebackDetailsComponent } from './chargeback-details.component';
|
||||
import { ReopenChargebackDialogComponent } from './reopen-chargeback-dialog';
|
||||
|
||||
const EXPORTED_DECLARATIONS = [
|
||||
ChargebackDetailsComponent,
|
||||
ChangeChargebackStatusDialogComponent,
|
||||
ReopenChargebackDialogComponent,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
ChargebackDetailsRoutingModule,
|
||||
CommonModule,
|
||||
HeadlineModule,
|
||||
MatCardModule,
|
||||
MatDividerModule,
|
||||
DetailsItemModule,
|
||||
CommonPipesModule,
|
||||
FlexLayoutModule,
|
||||
PaymentMainInfoModule,
|
||||
MatButtonModule,
|
||||
ReactiveFormsModule,
|
||||
MatSnackBarModule,
|
||||
MatDialogModule,
|
||||
MatDatepickerModule,
|
||||
MatFormFieldModule,
|
||||
MatSelectModule,
|
||||
MatInputModule,
|
||||
MatIconModule,
|
||||
],
|
||||
declarations: EXPORTED_DECLARATIONS,
|
||||
exports: EXPORTED_DECLARATIONS,
|
||||
})
|
||||
export class ChargebackDetailsModule {}
|
@ -0,0 +1,59 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { combineLatest, merge, Subject } from 'rxjs';
|
||||
import { map, pluck, shareReplay, switchMap, withLatestFrom } from 'rxjs/operators';
|
||||
import { PartyService } from 'src/app/party/party.service';
|
||||
import { createDSL } from 'src/app/query-dsl';
|
||||
import { MerchantStatisticsService } from 'src/app/thrift-services/damsel/merchant-statistics.service';
|
||||
import { PaymentProcessingService } from 'src/app/thrift-services/damsel/payment-processing.service';
|
||||
|
||||
@Injectable()
|
||||
export class ChargebackDetailsService {
|
||||
private loadChargeback$ = new Subject<void>();
|
||||
|
||||
payment$ = this.route.params.pipe(
|
||||
switchMap(({ partyID, invoiceID, paymentID }) => {
|
||||
return this.merchantStatisticsService
|
||||
.getPayments({
|
||||
dsl: createDSL({
|
||||
payments: {
|
||||
...(paymentID ? { payment_id: paymentID } : {}),
|
||||
...(partyID ? { merchant_id: partyID } : {}),
|
||||
...(invoiceID ? { invoice_id: invoiceID } : {}),
|
||||
},
|
||||
}),
|
||||
})
|
||||
.pipe(map(({ data }) => data.payments[0]));
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
shop$ = combineLatest([
|
||||
this.route.params.pipe(pluck('partyID')),
|
||||
this.payment$.pipe(pluck('shop_id')),
|
||||
]).pipe(
|
||||
switchMap(([partyID, shopID]) => this.partyService.getShop(partyID, shopID)),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
chargeback$ = merge(
|
||||
this.route.params,
|
||||
this.loadChargeback$.pipe(withLatestFrom(this.route.params, (_, p) => p))
|
||||
).pipe(
|
||||
switchMap((p) =>
|
||||
this.paymentProcessingService.getChargeback(p.invoiceID, p.paymentID, p.chargebackID)
|
||||
),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
constructor(
|
||||
private partyService: PartyService,
|
||||
private merchantStatisticsService: MerchantStatisticsService,
|
||||
private route: ActivatedRoute,
|
||||
private paymentProcessingService: PaymentProcessingService
|
||||
) {}
|
||||
|
||||
loadChargeback() {
|
||||
this.loadChargeback$.next();
|
||||
}
|
||||
}
|
3
src/app/sections/chargeback-details/index.ts
Normal file
3
src/app/sections/chargeback-details/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './chargeback-details.component';
|
||||
export * from './chargeback-details.module';
|
||||
export * from './chargeback-details.service';
|
@ -0,0 +1 @@
|
||||
export * from './reopen-chargeback-dialog.component';
|
@ -0,0 +1,29 @@
|
||||
<div fxLayout="column" fxLayoutGap="32px">
|
||||
<h1 class="mat-headline">Reopen chargeback</h1>
|
||||
<mat-dialog-content [formGroup]="form" fxLayout="column" fxLayoutGap="24px">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>Reopen stage (optional)</mat-label>
|
||||
<mat-select formControlName="stage">
|
||||
<mat-option *ngFor="let c of stages" [value]="c">
|
||||
{{ c }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<div fxLayout fxLayoutGap="24px">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>Leavy amount (optional)</mat-label>
|
||||
<input matInput type="number" formControlName="leavyAmount" />
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>Body amount (optional)</mat-label>
|
||||
<input matInput type="number" formControlName="bodyAmount" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions fxLayout fxLayoutAlign="space-between">
|
||||
<button mat-button (click)="cancel()">CANCEL</button>
|
||||
<button mat-button color="primary" [disabled]="form.invalid" (click)="changeStatus()">
|
||||
REOPEN
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
</div>
|
@ -0,0 +1,76 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { InvoicePaymentChargebackStage } from 'src/app/thrift-services/damsel/gen-model/domain';
|
||||
import { PaymentProcessingService } from 'src/app/thrift-services/damsel/payment-processing.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-reopen-chargeback-dialog',
|
||||
templateUrl: 'reopen-chargeback-dialog.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ReopenChargebackDialogComponent {
|
||||
static defaultConfig: MatDialogConfig = {
|
||||
disableClose: true,
|
||||
width: '552px',
|
||||
};
|
||||
|
||||
form = this.fb.group({
|
||||
stage: '',
|
||||
leavyAmount: '',
|
||||
bodyAmount: '',
|
||||
date: '',
|
||||
});
|
||||
stages: (keyof InvoicePaymentChargebackStage)[] = [
|
||||
'arbitration',
|
||||
'chargeback',
|
||||
'pre_arbitration',
|
||||
];
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
public params: {
|
||||
invoiceID: string;
|
||||
paymentID: string;
|
||||
chargebackID: string;
|
||||
},
|
||||
private dialogRef: MatDialogRef<ReopenChargebackDialogComponent>,
|
||||
private snackBar: MatSnackBar,
|
||||
private paymentProcessingService: PaymentProcessingService
|
||||
) {}
|
||||
|
||||
changeStatus() {
|
||||
const { stage, leavyAmount, bodyAmount } = this.form.value;
|
||||
this.paymentProcessingService
|
||||
.reopenChargeback(
|
||||
this.params.invoiceID,
|
||||
this.params.paymentID,
|
||||
this.params.chargebackID,
|
||||
Object.assign(
|
||||
{},
|
||||
!!stage && { move_to_stage: { [stage]: {} } },
|
||||
!!bodyAmount && {
|
||||
amount: bodyAmount,
|
||||
currency: { symbolic_code: 'RUB' },
|
||||
},
|
||||
!!leavyAmount && {
|
||||
amount: leavyAmount,
|
||||
currency: { symbolic_code: 'RUB' },
|
||||
}
|
||||
)
|
||||
)
|
||||
.subscribe(
|
||||
() => this.dialogRef.close(),
|
||||
(error) => {
|
||||
console.error(error);
|
||||
this.snackBar.open('An error occurred while reopen chargeback', 'OK');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import pickBy from 'lodash-es/pickBy';
|
||||
|
||||
import { QueryParamsStore } from '@cc/app/shared/services';
|
||||
import { wrapValuesToArray } from '@cc/utils/index';
|
||||
|
||||
import { FormValue } from './chargebacks-search-filters';
|
||||
|
||||
const ARRAY_PARAMS: (keyof FormValue)[] = [
|
||||
'shop_ids',
|
||||
'chargeback_statuses',
|
||||
'chargeback_stages',
|
||||
'chargeback_categories',
|
||||
];
|
||||
|
||||
@Injectable()
|
||||
export class ChargebacksSearchFiltersStore extends QueryParamsStore<FormValue> {
|
||||
constructor(protected router: Router, protected route: ActivatedRoute) {
|
||||
super(router, route);
|
||||
}
|
||||
|
||||
mapToData(queryParams: Params = {}) {
|
||||
return {
|
||||
...queryParams,
|
||||
...wrapValuesToArray(
|
||||
pickBy(
|
||||
queryParams,
|
||||
(v, k: keyof FormValue) => typeof v === 'string' && ARRAY_PARAMS.includes(k)
|
||||
)
|
||||
),
|
||||
} as FormValue;
|
||||
}
|
||||
|
||||
mapToParams(data: Partial<FormValue> = {}): Params {
|
||||
return data;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<form [formGroup]="form" fxLayout fxLayoutGap="16px">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>Date Range</mat-label>
|
||||
<mat-date-range-input [rangePicker]="picker">
|
||||
<input matStartDate matInput formControlName="from_time" placeholder="Start Date" />
|
||||
<input matEndDate matInput formControlName="to_time" placeholder="End Date" />
|
||||
</mat-date-range-input>
|
||||
<mat-datepicker-toggle matSuffix [for]="picker">
|
||||
<mat-icon matDatepickerToggleIcon>keyboard_arrow_down</mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
<mat-date-range-picker #picker></mat-date-range-picker>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<input
|
||||
matInput
|
||||
placeholder="Invoice ID"
|
||||
formControlName="invoice_id"
|
||||
type="string"
|
||||
autocomplete="false"
|
||||
/>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>Shops</mat-label>
|
||||
<mat-select multiple formControlName="shop_ids" class="select">
|
||||
<mat-option *ngFor="let shop of shops$ | async" [value]="shop.id">
|
||||
{{ shop.details.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</form>
|
@ -0,0 +1,3 @@
|
||||
.mat-form-field {
|
||||
min-width: 0;
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnInit,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import moment from 'moment';
|
||||
|
||||
import { ChargebacksParams } from '../../../../query-dsl';
|
||||
import { ChargebacksMainSearchFiltersService } from './chargebacks-main-search-filters.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-chargebacks-main-search-filters',
|
||||
templateUrl: 'chargebacks-main-search-filters.component.html',
|
||||
styleUrls: ['chargebacks-main-search-filters.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [ChargebacksMainSearchFiltersService],
|
||||
})
|
||||
export class ChargebacksMainSearchFiltersComponent implements OnInit {
|
||||
@Input() partyID: string;
|
||||
@Input() initParams: ChargebacksParams;
|
||||
@Output() valueChanges = new EventEmitter<ChargebacksParams>();
|
||||
|
||||
shops$ = this.chargebacksMainSearchFiltersService.shops$;
|
||||
|
||||
form = this.chargebacksMainSearchFiltersService.form;
|
||||
|
||||
constructor(private chargebacksMainSearchFiltersService: ChargebacksMainSearchFiltersService) {
|
||||
this.chargebacksMainSearchFiltersService.searchParamsChanges$.subscribe((params) =>
|
||||
this.valueChanges.emit(params)
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const { from_time, to_time, ...params } = this.initParams || {};
|
||||
this.chargebacksMainSearchFiltersService.init(
|
||||
Object.assign(
|
||||
params,
|
||||
!!from_time && { from_time: moment(from_time) },
|
||||
!!to_time && { to_time: moment(to_time) }
|
||||
)
|
||||
);
|
||||
this.chargebacksMainSearchFiltersService.getShops(this.partyID);
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatOptionModule, MAT_DATE_FORMATS } from '@angular/material/core';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
import { ChargebacksMainSearchFiltersComponent } from './chargebacks-main-search-filters.component';
|
||||
|
||||
export const RU_DATE_FORMATS = {
|
||||
parse: {
|
||||
dateInput: ['l', 'LL'],
|
||||
},
|
||||
display: {
|
||||
dateInput: 'DD.MM.YYYY',
|
||||
monthYearLabel: 'DD.MM.YYYY',
|
||||
dateA11yLabel: 'DD.MM.YYYY',
|
||||
monthYearA11yLabel: 'DD.MM.YYYY',
|
||||
},
|
||||
};
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
MatFormFieldModule,
|
||||
MatDatepickerModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatButtonModule,
|
||||
MatBadgeModule,
|
||||
MatDialogModule,
|
||||
MatDividerModule,
|
||||
FlexLayoutModule,
|
||||
MatSelectModule,
|
||||
MatDialogModule,
|
||||
FormsModule,
|
||||
MatOptionModule,
|
||||
],
|
||||
declarations: [ChargebacksMainSearchFiltersComponent],
|
||||
exports: [ChargebacksMainSearchFiltersComponent],
|
||||
providers: [{ provide: MAT_DATE_FORMATS, useValue: RU_DATE_FORMATS }],
|
||||
})
|
||||
export class ChargebacksMainSearchFiltersModule {}
|
@ -0,0 +1,41 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FormBuilder, Validators } from '@angular/forms';
|
||||
import * as moment from 'moment';
|
||||
import { ReplaySubject } from 'rxjs';
|
||||
import { debounceTime, filter, shareReplay, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { PartyService } from '../../../../party/party.service';
|
||||
import { FormValue } from '../form-value';
|
||||
|
||||
@Injectable()
|
||||
export class ChargebacksMainSearchFiltersService {
|
||||
private getShops$ = new ReplaySubject<string>();
|
||||
|
||||
form = this.fb.group({
|
||||
from_time: [moment().subtract(1, 'month').startOf('d'), Validators.required],
|
||||
to_time: [moment().endOf('d'), Validators.required],
|
||||
invoice_id: '',
|
||||
shop_ids: '',
|
||||
});
|
||||
|
||||
searchParamsChanges$ = this.form.valueChanges.pipe(
|
||||
debounceTime(600),
|
||||
filter(() => this.form.valid),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
shops$ = this.getShops$.pipe(
|
||||
switchMap((partyID) => this.partyService.getShops(partyID)),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
constructor(private partyService: PartyService, private fb: FormBuilder) {}
|
||||
|
||||
getShops(id: string) {
|
||||
this.getShops$.next(id);
|
||||
}
|
||||
|
||||
init(params: FormValue) {
|
||||
this.form.patchValue(params);
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export * from './chargebacks-main-search-filters.component';
|
||||
export * from './chargebacks-main-search-filters.module';
|
@ -0,0 +1,10 @@
|
||||
<button
|
||||
class="other-filters-button"
|
||||
mat-button
|
||||
[matBadge]="count$ | async"
|
||||
matBadgePosition="after"
|
||||
matBadgeColor="accent"
|
||||
(click)="openOtherFiltersDialog()"
|
||||
>
|
||||
OTHER FILTERS
|
||||
</button>
|
@ -0,0 +1,40 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnInit,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import { ChargebacksParams } from 'src/app/query-dsl';
|
||||
|
||||
import { ChargebacksOtherSearchFiltersService } from './chargebacks-other-search-filters.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-chargebacks-other-search-filters',
|
||||
templateUrl: 'chargebacks-other-search-filters.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [ChargebacksOtherSearchFiltersService],
|
||||
})
|
||||
export class ChargebacksOtherSearchFiltersComponent implements OnInit {
|
||||
@Input() initParams: ChargebacksParams;
|
||||
@Output() valueChanges = new EventEmitter<ChargebacksParams>();
|
||||
|
||||
count$ = this.chargebacksOtherSearchFiltersService.filtersCount$;
|
||||
|
||||
constructor(
|
||||
private chargebacksOtherSearchFiltersService: ChargebacksOtherSearchFiltersService
|
||||
) {
|
||||
this.chargebacksOtherSearchFiltersService.formParams$.subscribe((params) => {
|
||||
this.valueChanges.emit(params);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.chargebacksOtherSearchFiltersService.init(this.initParams);
|
||||
}
|
||||
|
||||
openOtherFiltersDialog() {
|
||||
this.chargebacksOtherSearchFiltersService.openOtherFiltersDialog();
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatOptionModule } from '@angular/material/core';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
import { ChargebacksOtherSearchFiltersComponent } from './chargebacks-other-search-filters.component';
|
||||
import { OtherFiltersDialogModule } from './other-filters-dialog';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
MatFormFieldModule,
|
||||
MatDatepickerModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatButtonModule,
|
||||
MatBadgeModule,
|
||||
MatDialogModule,
|
||||
MatDividerModule,
|
||||
FlexLayoutModule,
|
||||
MatSelectModule,
|
||||
MatDialogModule,
|
||||
FormsModule,
|
||||
MatOptionModule,
|
||||
OtherFiltersDialogModule,
|
||||
],
|
||||
declarations: [ChargebacksOtherSearchFiltersComponent],
|
||||
exports: [ChargebacksOtherSearchFiltersComponent],
|
||||
})
|
||||
export class ChargebacksOtherSearchFiltersModule {}
|
@ -0,0 +1,47 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import pick from 'lodash-es/pick';
|
||||
import { ReplaySubject } from 'rxjs';
|
||||
import { filter, map, shareReplay, switchMap, take } from 'rxjs/operators';
|
||||
import { ChargebacksParams } from 'src/app/query-dsl';
|
||||
|
||||
import { OtherFiltersDialogComponent } from './other-filters-dialog';
|
||||
|
||||
@Injectable()
|
||||
export class ChargebacksOtherSearchFiltersService {
|
||||
formParams$ = new ReplaySubject<ChargebacksParams>(1);
|
||||
filtersCount$ = this.formParams$.pipe(
|
||||
map((p) => this.getActiveParamsCount(p) || null),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
constructor(private dialog: MatDialog) {}
|
||||
|
||||
init(params: ChargebacksParams) {
|
||||
this.formParams$.next(
|
||||
pick(params, ['chargeback_statuses', 'chargeback_categories', 'chargeback_stages'])
|
||||
);
|
||||
}
|
||||
|
||||
openOtherFiltersDialog() {
|
||||
this.formParams$
|
||||
.pipe(
|
||||
take(1),
|
||||
switchMap((data) =>
|
||||
this.dialog
|
||||
.open(OtherFiltersDialogComponent, {
|
||||
disableClose: true,
|
||||
width: '552px',
|
||||
data,
|
||||
})
|
||||
.afterClosed()
|
||||
),
|
||||
filter((v) => v)
|
||||
)
|
||||
.subscribe((p) => this.formParams$.next(p));
|
||||
}
|
||||
|
||||
private getActiveParamsCount(params: any) {
|
||||
return Object.values(params).filter((p) => (Array.isArray(p) ? p?.length : p)).length;
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
export * from './chargebacks-other-search-filters.component';
|
||||
export * from './chargebacks-other-search-filters.module';
|
||||
export * from './other-filters-dialog';
|
@ -0,0 +1,2 @@
|
||||
export * from './other-filters-dialog.component';
|
||||
export * from './other-filters-dialog.module';
|
@ -0,0 +1,35 @@
|
||||
<div fxLayout="column" fxLayoutGap="32px">
|
||||
<h1 class="mat-headline">Other filters</h1>
|
||||
<mat-dialog-content [formGroup]="form" fxLayout="column" fxLayoutGap="24px">
|
||||
<mat-form-field>
|
||||
<mat-label>Chargeback Statuses</mat-label>
|
||||
<mat-select multiple formControlName="chargeback_statuses" class="select">
|
||||
<mat-option *ngFor="let s of chargebackStatuses" [value]="s">
|
||||
{{ s }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>Chargeback Categories</mat-label>
|
||||
<mat-select multiple formControlName="chargeback_categories" class="select">
|
||||
<mat-option *ngFor="let c of chargebackCategories" [value]="c">
|
||||
{{ c }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>Chargeback Stages</mat-label>
|
||||
<mat-select multiple formControlName="chargeback_stages" class="select">
|
||||
<mat-option *ngFor="let s of chargebackStages" [value]="s">
|
||||
{{ s }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions fxLayout fxLayoutAlign="space-between">
|
||||
<button mat-button (click)="cancel()">CANCEL</button>
|
||||
<button mat-button color="primary" [disabled]="form.invalid" (click)="save()">SAVE</button>
|
||||
</mat-dialog-actions>
|
||||
</div>
|
@ -0,0 +1,42 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
|
||||
import {
|
||||
chargebackCategories,
|
||||
ChargebacksParams,
|
||||
chargebackStages,
|
||||
chargebackStatuses,
|
||||
} from '../../../../../query-dsl';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'other-filters-dialog.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class OtherFiltersDialogComponent {
|
||||
chargebackStatuses = chargebackStatuses;
|
||||
chargebackCategories = chargebackCategories;
|
||||
chargebackStages = chargebackStages;
|
||||
|
||||
form = this.fb.group({
|
||||
chargeback_statuses: '',
|
||||
chargeback_categories: '',
|
||||
chargeback_stages: '',
|
||||
});
|
||||
|
||||
constructor(
|
||||
private dialogRef: MatDialogRef<OtherFiltersDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public initParams: ChargebacksParams,
|
||||
private fb: FormBuilder
|
||||
) {
|
||||
this.form.patchValue(this.initParams);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
save() {
|
||||
this.dialogRef.close(this.form.value);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
import { OtherFiltersDialogComponent } from './other-filters-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
MatFormFieldModule,
|
||||
MatDatepickerModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatButtonModule,
|
||||
MatBadgeModule,
|
||||
MatDialogModule,
|
||||
MatDividerModule,
|
||||
FlexLayoutModule,
|
||||
MatSelectModule,
|
||||
],
|
||||
declarations: [OtherFiltersDialogComponent],
|
||||
exports: [OtherFiltersDialogComponent],
|
||||
entryComponents: [OtherFiltersDialogComponent],
|
||||
})
|
||||
export class OtherFiltersDialogModule {}
|
@ -0,0 +1,8 @@
|
||||
import { Moment } from 'moment';
|
||||
|
||||
import { ChargebacksParams } from '../../../query-dsl';
|
||||
|
||||
export type FormValue = Omit<ChargebacksParams, 'from_time' | 'to_time'> & {
|
||||
from_time: Moment;
|
||||
to_time: Moment;
|
||||
};
|
@ -0,0 +1,3 @@
|
||||
export * from './chargebacks-other-search-filters';
|
||||
export * from './chargebacks-main-search-filters';
|
||||
export * from './form-value';
|
1
src/app/sections/party-chargebacks/index.ts
Normal file
1
src/app/sections/party-chargebacks/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './party-chargebacks.module';
|
@ -0,0 +1,18 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { AppAuthGuardService } from '../../app-auth-guard.service';
|
||||
import { PartyChargebacksComponent } from './party-chargebacks.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: '',
|
||||
component: PartyChargebacksComponent,
|
||||
canActivate: [AppAuthGuardService],
|
||||
},
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class PartyChargebacksRoutingModule {}
|
@ -0,0 +1,28 @@
|
||||
<div fxLayout="column" fxLayoutGap="24px">
|
||||
<div class="cc-headline">Merchant’s chargebacks</div>
|
||||
<div fxLayout="column" fxLayoutGap="24px">
|
||||
<mat-card>
|
||||
<mat-card-content fxLayout fxLayoutAlign=" center">
|
||||
<cc-chargebacks-main-search-filters
|
||||
fxFlex="75"
|
||||
[partyID]="partyID$ | async"
|
||||
(valueChanges)="searchParamsChanges$.next($event)"
|
||||
[initParams]="initSearchParams$ | async"
|
||||
></cc-chargebacks-main-search-filters>
|
||||
<cc-chargebacks-other-search-filters
|
||||
class="other-filters-button"
|
||||
fxFlex
|
||||
fxLayoutAlign="center start"
|
||||
(valueChanges)="otherSearchParamsChanges$.next($event)"
|
||||
[initParams]="initSearchParams$ | async"
|
||||
></cc-chargebacks-other-search-filters>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card>
|
||||
<cc-chargebacks-table
|
||||
[partyID]="partyID$ | async"
|
||||
[searchParams]="searchParams$ | async"
|
||||
></cc-chargebacks-table>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,32 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { combineLatest, ReplaySubject } from 'rxjs';
|
||||
import { map, pluck, shareReplay } from 'rxjs/operators';
|
||||
|
||||
import { FormValue } from './chargebacks-search-filters';
|
||||
import { ChargebacksSearchFiltersStore } from './chargebacks-search-filters-store.service';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'party-chargebacks.component.html',
|
||||
providers: [ChargebacksSearchFiltersStore],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class PartyChargebacksComponent {
|
||||
partyID$ = this.route.params.pipe(pluck('partyID'), shareReplay(1));
|
||||
initSearchParams$ = this.chargebacksSearchFiltersStore.data$;
|
||||
searchParamsChanges$ = new ReplaySubject<FormValue>();
|
||||
otherSearchParamsChanges$ = new ReplaySubject<FormValue>();
|
||||
searchParams$ = combineLatest([this.searchParamsChanges$, this.otherSearchParamsChanges$]).pipe(
|
||||
map(([a, b]) => ({ ...a, ...b })),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private chargebacksSearchFiltersStore: ChargebacksSearchFiltersStore
|
||||
) {
|
||||
this.searchParams$.subscribe((params) =>
|
||||
this.chargebacksSearchFiltersStore.preserve(params)
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexModule } from '@angular/flex-layout';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
|
||||
import { ChargebacksTableModule } from '@cc/app/shared/components/chargebacks-table';
|
||||
import { StatusModule } from '@cc/components/status';
|
||||
|
||||
import {
|
||||
ChargebacksMainSearchFiltersModule,
|
||||
ChargebacksOtherSearchFiltersModule,
|
||||
} from './chargebacks-search-filters';
|
||||
import { PartyChargebacksRoutingModule } from './party-chargebacks-routing.module';
|
||||
import { PartyChargebacksComponent } from './party-chargebacks.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
FlexModule,
|
||||
MatCardModule,
|
||||
MatProgressBarModule,
|
||||
CommonModule,
|
||||
MatButtonModule,
|
||||
ReactiveFormsModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatTableModule,
|
||||
MatMenuModule,
|
||||
MatIconModule,
|
||||
PartyChargebacksRoutingModule,
|
||||
StatusModule,
|
||||
ChargebacksTableModule,
|
||||
MatBadgeModule,
|
||||
ChargebacksMainSearchFiltersModule,
|
||||
ChargebacksOtherSearchFiltersModule,
|
||||
],
|
||||
declarations: [PartyChargebacksComponent],
|
||||
})
|
||||
export class PartyChargebacksModule {}
|
@ -3,10 +3,9 @@ import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import pickBy from 'lodash-es/pickBy';
|
||||
|
||||
import { SearchFiltersParams } from '@cc/app/shared/components/payments-search-filters/search-filters-params';
|
||||
import { QueryParamsStore } from '@cc/app/shared/services';
|
||||
import { wrapValuesToArray } from '@cc/utils/index';
|
||||
|
||||
import { QueryParamsStore } from './query-params-store';
|
||||
|
||||
const shopIDsAndPrimitives = (v, k) => typeof v === 'string' && k === 'shopIDs';
|
||||
|
||||
@Injectable()
|
||||
|
@ -64,9 +64,18 @@ import { PartyComponent } from './party.component';
|
||||
(m) => m.PaymentRoutingRulesModule
|
||||
),
|
||||
canActivate: [AppAuthGuardService],
|
||||
data: {
|
||||
roles: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'chargebacks',
|
||||
loadChildren: () =>
|
||||
import('../party-chargebacks').then((m) => m.PartyChargebacksModule),
|
||||
canActivate: [AppAuthGuardService],
|
||||
},
|
||||
{
|
||||
path: 'invoice/:invoiceID/payment/:paymentID/chargeback/:chargebackID',
|
||||
loadChildren: () =>
|
||||
import('../chargeback-details').then((m) => m.ChargebackDetailsModule),
|
||||
canActivate: [AppAuthGuardService],
|
||||
},
|
||||
{ path: '', redirectTo: 'payments', pathMatch: 'full' },
|
||||
],
|
||||
|
@ -9,7 +9,7 @@
|
||||
[routerLink]="link.url"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
[active]="rla.isActive || hasActiveFragments(link.otherActiveUrlFragments)"
|
||||
[active]="rla.isActive || (activeLinkByFragment$ | async) === link"
|
||||
>
|
||||
{{ link.name }}
|
||||
</a>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { pluck, shareReplay } from 'rxjs/operators';
|
||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||
import { filter, map, pluck, shareReplay, startWith } from 'rxjs/operators';
|
||||
|
||||
import { hasActiveFragments, SHARE_REPLAY_CONF } from '@cc/utils/index';
|
||||
import { SHARE_REPLAY_CONF } from '@cc/utils/index';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'party.component.html',
|
||||
@ -14,14 +14,41 @@ export class PartyComponent {
|
||||
{ name: 'Claims', url: 'claims', otherActiveUrlFragments: ['claim'] },
|
||||
{ name: 'Shops', url: 'shops' },
|
||||
{ name: 'Payment Routing Rules', url: 'payment-routing-rules' },
|
||||
{
|
||||
name: 'Chargebacks',
|
||||
url: 'chargebacks',
|
||||
otherActiveUrlFragments: ['payment', 'invoice', 'chargeback'],
|
||||
},
|
||||
];
|
||||
|
||||
partyID$ = this.route.params.pipe(pluck('partyID'), shareReplay(SHARE_REPLAY_CONF));
|
||||
activeLinkByFragment$ = this.router.events.pipe(
|
||||
filter((e) => e instanceof NavigationEnd),
|
||||
startWith(undefined),
|
||||
map(() => this.findLinkWithMaxActiveFragments()),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
constructor(private route: ActivatedRoute, private router: Router) {}
|
||||
|
||||
hasActiveFragments(fragments: string[]): boolean {
|
||||
const ulrFragments = this.router.url.split('/');
|
||||
return hasActiveFragments(fragments, ulrFragments);
|
||||
private activeFragments(fragments: string[]): number {
|
||||
if (fragments?.length) {
|
||||
const ulrFragments = this.router.url.split('/');
|
||||
if (
|
||||
ulrFragments.filter((fragment) => fragments.includes(fragment)).length ===
|
||||
fragments.length
|
||||
) {
|
||||
return fragments.length;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private findLinkWithMaxActiveFragments() {
|
||||
return this.links.reduce(([maxLink, maxActiveFragments], link) => {
|
||||
const activeFragments = this.activeFragments(link.otherActiveUrlFragments);
|
||||
return maxActiveFragments > activeFragments
|
||||
? [maxLink, maxActiveFragments]
|
||||
: [link, activeFragments];
|
||||
}, [])?.[0];
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,28 @@
|
||||
<div fxLayout="column" fxLayoutGap="24px">
|
||||
<cc-headline>Payment details</cc-headline>
|
||||
<mat-card *ngIf="payment$ | async as payment">
|
||||
<mat-card-content>
|
||||
<cc-payment-main-info [payment]="payment" [shop]="shop$ | async"></cc-payment-main-info>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<ng-container *ngIf="payment$ | async as payment">
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<cc-payment-main-info
|
||||
[payment]="payment"
|
||||
[shop]="shop$ | async"
|
||||
></cc-payment-main-info>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card *ngIf="payment$ | async as payment" fxLayout="column" fxLayoutGap="32px">
|
||||
<div fxLayout fxLayoutAlign="space-between">
|
||||
<h2 class="cc-headline">Chargebacks</h2>
|
||||
<button mat-raised-button color="primary" (click)="createChargeback()">
|
||||
CREATE CHARGEBACK
|
||||
</button>
|
||||
</div>
|
||||
<cc-chargebacks-table
|
||||
[partyID]="partyID$ | async"
|
||||
[searchParams]="chargebackSerchParams$ | async"
|
||||
[displayedColumns]="['createdAt', 'status', 'stage', 'levyAmount', 'actions']"
|
||||
></cc-chargebacks-table>
|
||||
</mat-card>
|
||||
</ng-container>
|
||||
<div *ngIf="isLoading$ | async" fxLayout fxLayoutAlign="center center">
|
||||
<mat-spinner diameter="64"></mat-spinner>
|
||||
</div>
|
||||
|
@ -1,5 +1,12 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { merge, Subject } from 'rxjs';
|
||||
import { map, pluck, shareReplay, switchMap, take, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
import { CreateChargebackDialogComponent } from '@cc/app/shared/components/create-chargeback-dialog';
|
||||
|
||||
import { ChargebacksParams } from '../../query-dsl';
|
||||
import { PaymentDetailsService } from './payment-details.service';
|
||||
|
||||
@Component({
|
||||
@ -8,11 +15,38 @@ import { PaymentDetailsService } from './payment-details.service';
|
||||
providers: [PaymentDetailsService],
|
||||
})
|
||||
export class PaymentDetailsComponent {
|
||||
partyID$ = this.route.params.pipe(pluck('partyID'), shareReplay(1));
|
||||
payment$ = this.paymentDetailsService.payment$;
|
||||
|
||||
isLoading$ = this.paymentDetailsService.isLoading$;
|
||||
|
||||
shop$ = this.paymentDetailsService.shop$;
|
||||
updateSearchParams$ = new Subject();
|
||||
chargebackSerchParams$ = merge(
|
||||
this.payment$,
|
||||
this.updateSearchParams$.pipe(withLatestFrom(this.payment$, (_, p) => p))
|
||||
).pipe(
|
||||
map(({ id: payment_id, invoice_id }) => ({ invoice_id, payment_id } as ChargebacksParams)),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
constructor(private paymentDetailsService: PaymentDetailsService) {}
|
||||
constructor(
|
||||
private paymentDetailsService: PaymentDetailsService,
|
||||
private route: ActivatedRoute,
|
||||
private dialog: MatDialog
|
||||
) {}
|
||||
|
||||
createChargeback() {
|
||||
this.payment$
|
||||
.pipe(
|
||||
take(1),
|
||||
switchMap(({ id: paymentID, invoice_id: invoiceID }) =>
|
||||
this.dialog
|
||||
.open(CreateChargebackDialogComponent, {
|
||||
...CreateChargebackDialogComponent.defaultConfig,
|
||||
data: { invoiceID, paymentID },
|
||||
})
|
||||
.afterClosed()
|
||||
)
|
||||
)
|
||||
.subscribe(() => this.updateSearchParams$.next());
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,13 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
|
||||
import { ChargebacksTableModule } from '@cc/app/shared/components/chargebacks-table';
|
||||
import { CreateChargebackDialogModule } from '@cc/app/shared/components/create-chargeback-dialog';
|
||||
import { SharedPipesModule } from '@cc/app/shared/pipes';
|
||||
import { DetailsItemModule } from '@cc/components/details-item';
|
||||
import { HeadlineModule } from '@cc/components/headline';
|
||||
@ -27,6 +31,10 @@ import { PaymentToolModule } from './payment-main-info/payment-tool';
|
||||
PaymentToolModule,
|
||||
MatProgressSpinnerModule,
|
||||
PaymentMainInfoModule,
|
||||
ChargebacksTableModule,
|
||||
MatButtonModule,
|
||||
MatDialogModule,
|
||||
CreateChargebackDialogModule,
|
||||
],
|
||||
declarations: [PaymentDetailsComponent],
|
||||
})
|
||||
|
@ -0,0 +1,73 @@
|
||||
<div *ngIf="(chargebacks$ | async)?.length; else empty" fxLayout="column" fxLayoutGap="24px">
|
||||
<table mat-table [dataSource]="chargebacks$ | async">
|
||||
<ng-container matColumnDef="createdAt">
|
||||
<th mat-header-cell *matHeaderCellDef>Created At</th>
|
||||
<td mat-cell *matCellDef="let chargeback">
|
||||
{{ chargeback.created_at | date: 'dd.MM.yyyy HH:mm:ss' }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="status">
|
||||
<th mat-header-cell *matHeaderCellDef>Status</th>
|
||||
<td mat-cell *matCellDef="let chargeback">
|
||||
<cc-status>{{ chargeback.chargeback_status | ccMapUnion: mapStatus }}</cc-status>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="stage">
|
||||
<th mat-header-cell *matHeaderCellDef>Stage</th>
|
||||
<td mat-cell *matCellDef="let chargeback">
|
||||
{{ chargeback.stage | ccMapUnion: mapStage }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="levyAmount">
|
||||
<th mat-header-cell *matHeaderCellDef>Levy Amount</th>
|
||||
<td mat-cell *matCellDef="let chargeback">
|
||||
{{ chargeback.levy_amount | ccFormatAmount }}
|
||||
{{ chargeback.levy_currency_code.symbolic_code | ccCurrency }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="shop">
|
||||
<th mat-header-cell *matHeaderCellDef>Shop</th>
|
||||
<td mat-cell *matCellDef="let chargeback">
|
||||
{{ chargeback.shop_id | shopName: partyID }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef class="action-cell"></th>
|
||||
<td mat-cell *matCellDef="let chargeback" class="action-cell">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item (click)="navigateToChargeback(chargeback)">
|
||||
Chargeback Details
|
||||
</button>
|
||||
</mat-menu>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||
</table>
|
||||
<button
|
||||
fxFlex="100"
|
||||
mat-button
|
||||
*ngIf="hasMore$ | async"
|
||||
(click)="fetchMore()"
|
||||
[disabled]="doAction$ | async"
|
||||
>
|
||||
{{ (doAction$ | async) ? 'LOADING...' : 'SHOW MORE' }}
|
||||
</button>
|
||||
</div>
|
||||
<ng-template #empty>
|
||||
<div *ngIf="isLoading$ | async; else emptyResult" fxLayout fxLayoutAlign="center center">
|
||||
<mat-spinner diameter="64"></mat-spinner>
|
||||
</div>
|
||||
<ng-template #emptyResult>
|
||||
<cc-empty-search-result unwrapped></cc-empty-search-result>
|
||||
</ng-template>
|
||||
</ng-template>
|
@ -0,0 +1,7 @@
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.action-cell {
|
||||
width: 8px;
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Router } from '@angular/router';
|
||||
import { StatChargeback } from 'src/app/thrift-services/damsel/gen-model/merch_stat';
|
||||
|
||||
import { ChargebacksParams } from '../../../query-dsl';
|
||||
import { ComponentChanges } from '../../../shared/utils';
|
||||
import {
|
||||
InvoicePaymentChargebackStage,
|
||||
InvoicePaymentChargebackStatus,
|
||||
} from '../../../thrift-services/damsel/gen-model/domain';
|
||||
import { FetchChargebacksService } from './fetch-chargebacks.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-chargebacks-table',
|
||||
templateUrl: 'chargebacks-table.component.html',
|
||||
styleUrls: ['chargebacks-table.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ChargebacksTableComponent implements OnInit, OnChanges {
|
||||
@Input() partyID: string;
|
||||
@Input() searchParams: ChargebacksParams;
|
||||
@Input() displayedColumns = ['createdAt', 'status', 'stage', 'levyAmount', 'shop', 'actions'];
|
||||
|
||||
chargebacks$ = this.fetchChargebacksService.searchResult$;
|
||||
doAction$ = this.fetchChargebacksService.doAction$;
|
||||
isLoading$ = this.fetchChargebacksService.isLoading$;
|
||||
hasMore$ = this.fetchChargebacksService.hasMore$;
|
||||
|
||||
mapStatus: { [N in keyof InvoicePaymentChargebackStatus] } = {
|
||||
accepted: 'Accepted',
|
||||
cancelled: 'Cancelled',
|
||||
pending: 'Pending',
|
||||
rejected: 'Rejected',
|
||||
};
|
||||
|
||||
mapStage: { [N in keyof InvoicePaymentChargebackStage] } = {
|
||||
arbitration: 'Arbitration',
|
||||
chargeback: 'Chargeback',
|
||||
pre_arbitration: 'Pre-arbitration',
|
||||
};
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private fetchChargebacksService: FetchChargebacksService,
|
||||
private snackBar: MatSnackBar
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.fetchChargebacksService.errors$.subscribe((e) =>
|
||||
this.snackBar.open(`An error occurred while search chargebacks (${e})`, 'OK')
|
||||
);
|
||||
}
|
||||
|
||||
ngOnChanges({ searchParams }: ComponentChanges<ChargebacksTableComponent>) {
|
||||
if (searchParams) {
|
||||
this.fetchChargebacksService.search({
|
||||
...searchParams.currentValue,
|
||||
merchant_id: this.partyID,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
navigateToChargeback({ chargeback_id, invoice_id, payment_id }: StatChargeback) {
|
||||
this.router.navigate([
|
||||
`/party/${this.partyID}/invoice/${invoice_id}/payment/${payment_id}/chargeback/${chargeback_id}`,
|
||||
]);
|
||||
}
|
||||
|
||||
fetchMore() {
|
||||
this.fetchChargebacksService.fetchMore();
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexModule } from '@angular/flex-layout';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
|
||||
import { EmptySearchResultModule } from '@cc/components/empty-search-result';
|
||||
import { StatusModule } from '@cc/components/status';
|
||||
import { CommonPipesModule } from '@cc/pipes/common-pipes.module';
|
||||
|
||||
import { SharedPipesModule } from '../../../shared';
|
||||
import { ChargebacksTableComponent } from './chargebacks-table.component';
|
||||
import { FetchChargebacksService } from './fetch-chargebacks.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatTableModule,
|
||||
FlexModule,
|
||||
StatusModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
MatProgressSpinnerModule,
|
||||
EmptySearchResultModule,
|
||||
CommonPipesModule,
|
||||
SharedPipesModule,
|
||||
MatSnackBarModule,
|
||||
],
|
||||
declarations: [ChargebacksTableComponent],
|
||||
exports: [ChargebacksTableComponent],
|
||||
providers: [FetchChargebacksService],
|
||||
})
|
||||
export class ChargebacksTableModule {}
|
@ -0,0 +1,49 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FetchResult, PartialFetcher } from '@rbkmoney/partial-fetcher';
|
||||
import pickBy from 'lodash-es/pickBy';
|
||||
import moment from 'moment';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
|
||||
import { booleanDelay } from '@cc/operators/index';
|
||||
|
||||
import { ChargebacksParams, createDSL } from '../../../query-dsl';
|
||||
import { StatChargeback } from '../../../thrift-services/damsel/gen-model/merch_stat';
|
||||
import { MerchantStatisticsService } from '../../../thrift-services/damsel/merchant-statistics.service';
|
||||
|
||||
const SEARCH_LIMIT = 10;
|
||||
|
||||
@Injectable()
|
||||
export class FetchChargebacksService extends PartialFetcher<StatChargeback, ChargebacksParams> {
|
||||
isLoading$ = this.doAction$.pipe(booleanDelay(), shareReplay(1));
|
||||
|
||||
constructor(private merchantStatisticsService: MerchantStatisticsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected fetch(
|
||||
{ from_time, to_time, ...params }: ChargebacksParams,
|
||||
continuationToken: string
|
||||
): Observable<FetchResult<StatChargeback>> {
|
||||
return this.merchantStatisticsService
|
||||
.getChargebacks({
|
||||
dsl: createDSL({
|
||||
chargebacks: Object.assign(
|
||||
pickBy(params, (v) => (Array.isArray(v) ? v.length : v)),
|
||||
!!from_time && { from_time: moment(from_time).utc().format() },
|
||||
!!to_time && { to_time: moment(to_time).utc().format() },
|
||||
{
|
||||
size: SEARCH_LIMIT,
|
||||
}
|
||||
),
|
||||
}),
|
||||
...(!!continuationToken && { continuation_token: continuationToken }),
|
||||
})
|
||||
.pipe(
|
||||
map(({ data, continuation_token }) => ({
|
||||
result: data.chargebacks,
|
||||
continuationToken: continuation_token,
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
2
src/app/shared/components/chargebacks-table/index.ts
Normal file
2
src/app/shared/components/chargebacks-table/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './chargebacks-table.component';
|
||||
export * from './chargebacks-table.module';
|
@ -0,0 +1,45 @@
|
||||
<div fxLayout="column" fxLayoutGap="32px">
|
||||
<h1 class="mat-headline">Create chargeback params</h1>
|
||||
<mat-dialog-content [formGroup]="form" fxLayout="column" fxLayoutGap="24px">
|
||||
<div fxLayout fxLayoutGap="16px">
|
||||
<mat-form-field fxFlex>
|
||||
<input
|
||||
matInput
|
||||
formControlName="date"
|
||||
[matDatepicker]="datepicker"
|
||||
placeholder="Pretension date"
|
||||
required
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="datepicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #datepicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>Leavy amount</mat-label>
|
||||
<input matInput type="number" formControlName="leavyAmount" required />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
<h2 class="cc-title">Reason (optional)</h2>
|
||||
<div fxLayout fxLayoutGap="16px">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>Category</mat-label>
|
||||
<mat-select formControlName="category" required>
|
||||
<mat-option *ngFor="let c of categories" [value]="c">
|
||||
{{ c }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>Code (optional)</mat-label>
|
||||
<input matInput formControlName="code" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions fxLayout fxLayoutAlign="space-between">
|
||||
<button mat-button (click)="cancel()">CANCEL</button>
|
||||
<button mat-button color="primary" [disabled]="form.invalid" (click)="create()">
|
||||
CREATE
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
</div>
|
@ -0,0 +1,74 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Moment } from 'moment';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import { InvoicePaymentChargebackCategory } from '../../../thrift-services/damsel/gen-model/domain';
|
||||
import { PaymentProcessingService } from '../../../thrift-services/damsel/payment-processing.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-create-chargeback-dialog',
|
||||
templateUrl: 'create-chargeback-dialog.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CreateChargebackDialogComponent {
|
||||
static defaultConfig: MatDialogConfig = {
|
||||
disableClose: true,
|
||||
width: '552px',
|
||||
};
|
||||
|
||||
form = this.fb.group({
|
||||
date: '',
|
||||
leavyAmount: '',
|
||||
category: '',
|
||||
code: '',
|
||||
});
|
||||
categories: (keyof InvoicePaymentChargebackCategory)[] = [
|
||||
'authorisation',
|
||||
'dispute',
|
||||
'fraud',
|
||||
'processing_error',
|
||||
];
|
||||
|
||||
constructor(
|
||||
private paymentProcessingService: PaymentProcessingService,
|
||||
private fb: FormBuilder,
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
public params: {
|
||||
invoiceID: string;
|
||||
paymentID: string;
|
||||
},
|
||||
private dialogRef: MatDialogRef<CreateChargebackDialogComponent>,
|
||||
private snackBar: MatSnackBar
|
||||
) {}
|
||||
|
||||
create() {
|
||||
const { leavyAmount, code, category, date } = this.form.value;
|
||||
this.paymentProcessingService
|
||||
.createChargeback(this.params.invoiceID, this.params.paymentID, {
|
||||
id: uuid(),
|
||||
occurred_at: (date as Moment).utc().format(),
|
||||
reason: {
|
||||
code,
|
||||
category: { [category]: {} },
|
||||
},
|
||||
levy: {
|
||||
amount: (leavyAmount * 100) as any,
|
||||
currency: { symbolic_code: 'RUB' },
|
||||
},
|
||||
})
|
||||
.subscribe(
|
||||
() => this.dialogRef.close(),
|
||||
(error) => {
|
||||
console.error(error);
|
||||
this.snackBar.open('An error occurred while chargeback creating', 'OK');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexModule } from '@angular/flex-layout';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
|
||||
import { DamselModule } from '../../../thrift-services/damsel';
|
||||
import { CreateChargebackDialogComponent } from './create-chargeback-dialog.component';
|
||||
|
||||
const EXPORTED_DECLARATIONS = [CreateChargebackDialogComponent];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FlexModule,
|
||||
ReactiveFormsModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatDialogModule,
|
||||
DamselModule,
|
||||
MatDividerModule,
|
||||
MatSelectModule,
|
||||
MatDatepickerModule,
|
||||
MatButtonModule,
|
||||
MatSnackBarModule,
|
||||
],
|
||||
declarations: EXPORTED_DECLARATIONS,
|
||||
exports: EXPORTED_DECLARATIONS,
|
||||
})
|
||||
export class CreateChargebackDialogModule {}
|
@ -0,0 +1,2 @@
|
||||
export * from './create-chargeback-dialog.component';
|
||||
export * from './create-chargeback-dialog.module';
|
1
src/app/shared/services/index.ts
Normal file
1
src/app/shared/services/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './query-params-store';
|
@ -3,8 +3,10 @@ import isEqual from 'lodash-es/isEqual';
|
||||
import { Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, map, shareReplay } from 'rxjs/operators';
|
||||
|
||||
import { removeEmptyProperties } from '@cc/utils/index';
|
||||
|
||||
export abstract class QueryParamsStore<D> {
|
||||
data$: Observable<D> = this.route.queryParams.pipe(
|
||||
data$: Observable<Partial<D>> = this.route.queryParams.pipe(
|
||||
distinctUntilChanged(isEqual),
|
||||
map((p) => this.mapToData(p)),
|
||||
shareReplay(1)
|
||||
@ -12,11 +14,12 @@ export abstract class QueryParamsStore<D> {
|
||||
|
||||
constructor(protected router: Router, protected route: ActivatedRoute) {}
|
||||
|
||||
abstract mapToData(queryParams: Params): D;
|
||||
abstract mapToData(queryParams: Params): Partial<D>;
|
||||
|
||||
abstract mapToParams(data: D): Params;
|
||||
|
||||
preserve(data: D) {
|
||||
this.router.navigate([], { queryParams: this.mapToParams(data), preserveFragment: true });
|
||||
const queryParams = removeEmptyProperties(this.mapToParams(data));
|
||||
this.router.navigate([], { queryParams, preserveFragment: true });
|
||||
}
|
||||
}
|
11
src/app/shared/utils/component-changes.ts
Normal file
11
src/app/shared/utils/component-changes.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { SimpleChange } from '@angular/core';
|
||||
|
||||
export interface ComponentChange<T, P extends keyof T>
|
||||
extends Omit<SimpleChange, 'previousValue' | 'currentValue'> {
|
||||
previousValue: T[P];
|
||||
currentValue: T[P];
|
||||
}
|
||||
|
||||
export type ComponentChanges<T> = {
|
||||
[P in keyof T]?: ComponentChange<T, P>;
|
||||
};
|
@ -1 +1,2 @@
|
||||
export * from './extract-claim-status';
|
||||
export * from './component-changes';
|
||||
|
@ -3,6 +3,7 @@
|
||||
@import '../../../components/components-themes';
|
||||
|
||||
@import '../../sections/party/party-theme';
|
||||
@import '../../sections/chargeback-details/chargeback-details-theme';
|
||||
@import '../../sections/payment-routing-rules/payment-routing-rules-theme';
|
||||
@import '../../sections/party-claim/party-claim-theme';
|
||||
|
||||
@ -12,6 +13,7 @@
|
||||
body.#{map-get($theme, name)} {
|
||||
@include angular-material-theme($theme);
|
||||
@include cc-party-theme($theme);
|
||||
@include cc-chargeback-details-theme($theme);
|
||||
@include cc-components-themes($theme);
|
||||
@include cc-payment-routing-rules-theme($theme);
|
||||
@include cc-party-claim-theme($theme);
|
||||
|
@ -5,13 +5,17 @@ import * as claim_management from './gen-nodejs/claim_management_types';
|
||||
import * as domain_config from './gen-nodejs/domain_config_types';
|
||||
import * as domain from './gen-nodejs/domain_types';
|
||||
import * as geo_ip from './gen-nodejs/geo_ip_types';
|
||||
import * as merch_stat from './gen-nodejs/merch_stat_types';
|
||||
import * as payment_processing from './gen-nodejs/payment_processing_types';
|
||||
|
||||
export const namespaces = {
|
||||
const namespaces = {
|
||||
base,
|
||||
domain,
|
||||
domain_config,
|
||||
claim_management,
|
||||
geo_ip,
|
||||
merch_stat,
|
||||
payment_processing,
|
||||
};
|
||||
|
||||
export const {
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { Injectable, NgZone } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
|
||||
import { ThriftService } from '../thrift-service';
|
||||
import { createDamselInstance, damselInstanceToObject } from './create-damsel-instance';
|
||||
import { StatRequest, StatResponse } from './gen-model/merch_stat';
|
||||
import * as MerchantStatistics from './gen-nodejs/MerchantStatistics';
|
||||
import { StatRequest as ThriftStatRequest } from './gen-nodejs/merch_stat_types';
|
||||
@ -15,4 +17,9 @@ export class MerchantStatisticsService extends ThriftService {
|
||||
|
||||
getPayments = (req: StatRequest): Observable<StatResponse> =>
|
||||
this.toObservableAction('GetPayments')(new ThriftStatRequest(req));
|
||||
|
||||
getChargebacks = (req: StatRequest): Observable<StatResponse> =>
|
||||
this.toObservableAction('GetChargebacks')(
|
||||
createDamselInstance('merch_stat', 'StatRequest', req)
|
||||
).pipe(map((r) => damselInstanceToObject('merch_stat', 'StatResponse', r)));
|
||||
}
|
||||
|
@ -1,13 +1,25 @@
|
||||
import { Injectable, NgZone } from '@angular/core';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import { Observable, timer } from 'rxjs';
|
||||
import { first, share, switchMap } from 'rxjs/operators';
|
||||
import { first, map, share, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
|
||||
import { ThriftService } from '../thrift-service';
|
||||
import { InvoiceID } from './gen-model/domain';
|
||||
import { createDamselInstance, damselInstanceToObject } from './create-damsel-instance';
|
||||
import {
|
||||
InvoiceID,
|
||||
InvoicePaymentChargeback,
|
||||
InvoicePaymentChargebackID,
|
||||
InvoicePaymentID,
|
||||
} from './gen-model/domain';
|
||||
import {
|
||||
InvoicePaymentAdjustment,
|
||||
InvoicePaymentAdjustmentParams,
|
||||
InvoicePaymentChargebackAcceptParams,
|
||||
InvoicePaymentChargebackCancelParams,
|
||||
InvoicePaymentChargebackParams,
|
||||
InvoicePaymentChargebackRejectParams,
|
||||
InvoicePaymentChargebackReopenParams,
|
||||
InvoiceRepairScenario,
|
||||
UserInfo,
|
||||
} from './gen-model/payment_processing';
|
||||
@ -20,7 +32,11 @@ import {
|
||||
|
||||
@Injectable()
|
||||
export class PaymentProcessingService extends ThriftService {
|
||||
constructor(zone: NgZone, keycloakTokenInfoService: KeycloakTokenInfoService) {
|
||||
constructor(
|
||||
zone: NgZone,
|
||||
keycloakTokenInfoService: KeycloakTokenInfoService,
|
||||
private keycloakService: KeycloakService
|
||||
) {
|
||||
super(zone, keycloakTokenInfoService, '/v1/processing/invoicing', Invoicing);
|
||||
}
|
||||
|
||||
@ -96,4 +112,107 @@ export class PaymentProcessingService extends ThriftService {
|
||||
id,
|
||||
new InvoiceRepairScenarioObject(scenario)
|
||||
);
|
||||
|
||||
createChargeback = (
|
||||
invoiceID: InvoiceID,
|
||||
paymentID: InvoicePaymentID,
|
||||
params: InvoicePaymentChargebackParams
|
||||
): Observable<InvoicePaymentChargeback> =>
|
||||
this.toObservableAction('CreateChargeback')(
|
||||
this.getUser(),
|
||||
createDamselInstance('domain', 'InvoiceID', invoiceID),
|
||||
createDamselInstance('domain', 'InvoicePaymentID', paymentID),
|
||||
createDamselInstance('payment_processing', 'InvoicePaymentChargebackParams', params)
|
||||
).pipe(map((r) => damselInstanceToObject('domain', 'InvoicePaymentChargeback', r)));
|
||||
|
||||
acceptChargeback = (
|
||||
invoiceID: InvoiceID,
|
||||
paymentID: InvoicePaymentID,
|
||||
chargebackID: InvoicePaymentChargebackID,
|
||||
params: InvoicePaymentChargebackAcceptParams = {}
|
||||
): Observable<void> =>
|
||||
this.toObservableAction('AcceptChargeback')(
|
||||
this.getUser(),
|
||||
createDamselInstance('domain', 'InvoiceID', invoiceID),
|
||||
createDamselInstance('domain', 'InvoicePaymentID', paymentID),
|
||||
createDamselInstance('domain', 'InvoicePaymentChargebackID', chargebackID),
|
||||
createDamselInstance(
|
||||
'payment_processing',
|
||||
'InvoicePaymentChargebackAcceptParams',
|
||||
params
|
||||
)
|
||||
);
|
||||
|
||||
rejectChargeback = (
|
||||
invoiceID: InvoiceID,
|
||||
paymentID: InvoicePaymentID,
|
||||
chargebackID: InvoicePaymentChargebackID,
|
||||
params: InvoicePaymentChargebackRejectParams = {}
|
||||
): Observable<void> =>
|
||||
this.toObservableAction('RejectChargeback')(
|
||||
this.getUser(),
|
||||
createDamselInstance('domain', 'InvoiceID', invoiceID),
|
||||
createDamselInstance('domain', 'InvoicePaymentID', paymentID),
|
||||
createDamselInstance('domain', 'InvoicePaymentChargebackID', chargebackID),
|
||||
createDamselInstance(
|
||||
'payment_processing',
|
||||
'InvoicePaymentChargebackRejectParams',
|
||||
params
|
||||
)
|
||||
);
|
||||
|
||||
reopenChargeback = (
|
||||
invoiceID: InvoiceID,
|
||||
paymentID: InvoicePaymentID,
|
||||
chargebackID: InvoicePaymentChargebackID,
|
||||
params: InvoicePaymentChargebackReopenParams = {}
|
||||
): Observable<void> =>
|
||||
this.toObservableAction('ReopenChargeback')(
|
||||
this.getUser(),
|
||||
createDamselInstance('domain', 'InvoiceID', invoiceID),
|
||||
createDamselInstance('domain', 'InvoicePaymentID', paymentID),
|
||||
createDamselInstance('domain', 'InvoicePaymentChargebackID', chargebackID),
|
||||
createDamselInstance(
|
||||
'payment_processing',
|
||||
'InvoicePaymentChargebackReopenParams',
|
||||
params
|
||||
)
|
||||
);
|
||||
|
||||
cancelChargeback = (
|
||||
invoiceID: InvoiceID,
|
||||
paymentID: InvoicePaymentID,
|
||||
chargebackID: InvoicePaymentChargebackID,
|
||||
params: InvoicePaymentChargebackCancelParams = {}
|
||||
): Observable<void> =>
|
||||
this.toObservableAction('CancelChargeback')(
|
||||
this.getUser(),
|
||||
createDamselInstance('domain', 'InvoiceID', invoiceID),
|
||||
createDamselInstance('domain', 'InvoicePaymentID', paymentID),
|
||||
createDamselInstance('domain', 'InvoicePaymentChargebackID', chargebackID),
|
||||
createDamselInstance(
|
||||
'payment_processing',
|
||||
'InvoicePaymentChargebackCancelParams',
|
||||
params
|
||||
)
|
||||
);
|
||||
|
||||
getChargeback = (
|
||||
invoiceID: InvoiceID,
|
||||
paymentID: InvoicePaymentID,
|
||||
chargebackID: InvoicePaymentChargebackID
|
||||
): Observable<InvoicePaymentChargeback> =>
|
||||
this.toObservableAction('GetPaymentChargeback')(
|
||||
this.getUser(),
|
||||
createDamselInstance('domain', 'InvoiceID', invoiceID),
|
||||
createDamselInstance('domain', 'InvoicePaymentID', paymentID),
|
||||
createDamselInstance('domain', 'InvoicePaymentChargebackID', chargebackID)
|
||||
).pipe(map((r) => damselInstanceToObject('domain', 'InvoicePaymentChargeback', r)));
|
||||
|
||||
private getUser(): UserInfo {
|
||||
return createDamselInstance('payment_processing', 'UserInfo', {
|
||||
id: this.keycloakService.getUsername(),
|
||||
type: { internal_user: {} },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -5,3 +5,4 @@ export * from './ank';
|
||||
export * from './file-storage';
|
||||
export * from './messages';
|
||||
export * from './damsel';
|
||||
export * from './skipper';
|
||||
|
38
src/app/thrift-services/skipper/chargebacks.service.ts
Normal file
38
src/app/thrift-services/skipper/chargebacks.service.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Injectable, NgZone } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { KeycloakTokenInfoService } from '../../keycloak-token-info.service';
|
||||
import { ThriftService } from '../thrift-service';
|
||||
import { ID } from './gen-model/base';
|
||||
import { ChargebackData, ChargebackEvent, ChargebackFilter } from './gen-model/skipper';
|
||||
import * as Skipper from './gen-nodejs/Skipper';
|
||||
import { createSkipperInstance, skipperInstanceToObject } from './skipper-instance-utils';
|
||||
|
||||
@Injectable()
|
||||
export class ChargebacksService extends ThriftService {
|
||||
constructor(zone: NgZone, keycloakTokenInfoService: KeycloakTokenInfoService) {
|
||||
super(zone, keycloakTokenInfoService, '/v1/skipper', Skipper);
|
||||
}
|
||||
|
||||
getChargebacks = (filter: ChargebackFilter): Observable<ChargebackData> =>
|
||||
this.toObservableAction(
|
||||
'getChargebacks',
|
||||
false
|
||||
)(createSkipperInstance('skipper', 'ChargebackFilter', filter)).pipe(
|
||||
map((d) => skipperInstanceToObject('skipper', 'ChargebackData', d))
|
||||
);
|
||||
|
||||
getChargeback = (invoiceID: ID, paymentID: ID, chargebackID: ID): Observable<ChargebackData> =>
|
||||
this.toObservableAction('getChargeback', false)(
|
||||
createSkipperInstance('base', 'ID', invoiceID),
|
||||
createSkipperInstance('base', 'ID', paymentID),
|
||||
createSkipperInstance('base', 'ID', chargebackID)
|
||||
).pipe(map((d) => skipperInstanceToObject('skipper', 'ChargebackData', d)));
|
||||
|
||||
processChargebackData = (event: ChargebackEvent): Observable<void> =>
|
||||
this.toObservableAction(
|
||||
'processChargebackData',
|
||||
false
|
||||
)(createSkipperInstance('skipper', 'ChargebackEvent', event));
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
export * from './skipper.module';
|
||||
export * from './chargebacks.service';
|
||||
export * from './skipper-instance-utils';
|
16
src/app/thrift-services/skipper/skipper-instance-utils.ts
Normal file
16
src/app/thrift-services/skipper/skipper-instance-utils.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import metadata from '../../../assets/meta-skipper.json';
|
||||
import { createThriftInstanceUtils } from '../thrift-instance';
|
||||
import * as base from './gen-nodejs/base_types';
|
||||
import * as chargeback from './gen-nodejs/chargeback_types';
|
||||
import * as skipper from './gen-nodejs/skipper_types';
|
||||
|
||||
const namespaces = {
|
||||
base,
|
||||
chargeback,
|
||||
skipper,
|
||||
};
|
||||
|
||||
export const {
|
||||
createThriftInstance: createSkipperInstance,
|
||||
thriftInstanceToObject: skipperInstanceToObject,
|
||||
} = createThriftInstanceUtils(metadata, namespaces);
|
8
src/app/thrift-services/skipper/skipper.module.ts
Normal file
8
src/app/thrift-services/skipper/skipper.module.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { ChargebacksService } from './chargebacks.service';
|
||||
|
||||
@NgModule({
|
||||
providers: [ChargebacksService],
|
||||
})
|
||||
export class SkipperModule {}
|
@ -59,15 +59,25 @@ export function createThriftInstance<T extends { [N in string]: any }, V extends
|
||||
case 'enum':
|
||||
return value;
|
||||
default:
|
||||
const typeMeta = namespaceMeta.ast[structureType][type];
|
||||
if (structureType === 'typedef') {
|
||||
return internalCreateThriftInstance(typeMeta.type, value);
|
||||
try {
|
||||
const typeMeta = namespaceMeta.ast[structureType][type];
|
||||
if (structureType === 'typedef') {
|
||||
return internalCreateThriftInstance(typeMeta.type, value);
|
||||
}
|
||||
const instance = new namespaces[namespace][type]();
|
||||
for (const [k, v] of Object.entries(value)) {
|
||||
const fieldTypeMeta = typeMeta.find((m) => m.name === k);
|
||||
instance[k] = internalCreateThriftInstance(fieldTypeMeta.type, v);
|
||||
}
|
||||
return instance;
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`Thrift ${namespace}`,
|
||||
type,
|
||||
`instance creation error, value:`,
|
||||
value
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
const instance = new namespaces[namespace][type]();
|
||||
for (const [k, v] of Object.entries(value)) {
|
||||
const fieldTypeMeta = typeMeta.find((m) => m.name === k);
|
||||
instance[k] = internalCreateThriftInstance(fieldTypeMeta.type, v);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,16 @@
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<div fxLayout="row" fxLayoutAlign="center center">
|
||||
<h2 class="mat-headline empty-result">Search result is empty</h2>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<ng-template #content>
|
||||
<div fxLayout="row" fxLayoutAlign="center center">
|
||||
<h2 class="mat-headline empty-result">Search result is empty</h2>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #wrapped>
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<ng-container *ngTemplateOutlet="content"></ng-container>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</ng-template>
|
||||
<ng-container *ngIf="unwrapped; else wrapped">
|
||||
<ng-container *ngTemplateOutlet="content"></ng-container>
|
||||
</ng-container>
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { coerceBoolean } from 'coerce-property';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-empty-search-result',
|
||||
templateUrl: 'empty-search-result.component.html',
|
||||
styleUrls: ['empty-search-result.component.scss'],
|
||||
})
|
||||
export class EmptySearchResultComponent {}
|
||||
export class EmptySearchResultComponent {
|
||||
@Input() @coerceBoolean unwrapped = false;
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexModule } from '@angular/flex-layout';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
@ -7,6 +8,6 @@ import { EmptySearchResultComponent } from './empty-search-result.component';
|
||||
@NgModule({
|
||||
declarations: [EmptySearchResultComponent],
|
||||
exports: [EmptySearchResultComponent],
|
||||
imports: [MatCardModule, FlexModule],
|
||||
imports: [MatCardModule, FlexModule, CommonModule],
|
||||
})
|
||||
export class EmptySearchResultModule {}
|
||||
|
@ -2,10 +2,19 @@ import { NgModule } from '@angular/core';
|
||||
|
||||
import { CurrencyPipe } from './currency.pipe';
|
||||
import { FormatAmountPipe } from './format-amount.pipe';
|
||||
import { MapUnionPipe } from './map-union.pipe';
|
||||
import { ThriftInt64Pipe } from './thrift-int64.pipe';
|
||||
import { ThriftViewPipe } from './thrift-view.pipe';
|
||||
import { UnionKeyPipe } from './union-key';
|
||||
|
||||
const declarations = [FormatAmountPipe, CurrencyPipe, ThriftInt64Pipe, ThriftViewPipe];
|
||||
const declarations = [
|
||||
FormatAmountPipe,
|
||||
CurrencyPipe,
|
||||
ThriftInt64Pipe,
|
||||
ThriftViewPipe,
|
||||
MapUnionPipe,
|
||||
UnionKeyPipe,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations,
|
||||
|
12
src/pipes/map-union.pipe.ts
Normal file
12
src/pipes/map-union.pipe.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
import { getUnionKey } from '../utils';
|
||||
|
||||
@Pipe({
|
||||
name: 'ccMapUnion',
|
||||
})
|
||||
export class MapUnionPipe<T extends object> implements PipeTransform {
|
||||
public transform(union: T, mapObject: { [N in keyof T]: string | number }) {
|
||||
return mapObject[getUnionKey(union)];
|
||||
}
|
||||
}
|
12
src/pipes/union-key.ts
Normal file
12
src/pipes/union-key.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
import { getUnionKey } from '../utils';
|
||||
|
||||
@Pipe({
|
||||
name: 'ccUnionKey',
|
||||
})
|
||||
export class UnionKeyPipe<T extends object> implements PipeTransform {
|
||||
public transform(union: T) {
|
||||
return getUnionKey(union);
|
||||
}
|
||||
}
|
@ -6,6 +6,6 @@
|
||||
"messages": "./node_modules/messages-proto/proto",
|
||||
"file-storage": "./node_modules/file-storage-proto/proto",
|
||||
"ank": "./node_modules/ank-proto/proto",
|
||||
"skipper": "./node_modules/skipper-proto/proto"
|
||||
"skipper": { "path": "./node_modules/skipper-proto/proto", "meta": true }
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user