mirror of
https://github.com/valitydev/dashboard.git
synced 2024-11-06 02:25:23 +00:00
Epic FE-1031: webhooks (#190)
* FE-1032: integrations module added. (#183) * FE-1032: integrations module added. * FE-1033: webhooks search (#185) * webhooks receiver added * FE-1034: webhooks panels (#188) * FE-1035: webhook creation (#192) * FE-1037: empty search n loading fix (#195) * FE-1037: empty-result n loading. also webhooks sort added
This commit is contained in:
parent
885fb09097
commit
1f11f9f9e7
14
package-lock.json
generated
14
package-lock.json
generated
@ -3994,6 +3994,15 @@
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"@types/lodash.sortby": {
|
||||
"version": "4.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash.sortby/-/lodash.sortby-4.7.6.tgz",
|
||||
"integrity": "sha512-EnvAOmKvEg7gdYpYrS6+fVFPw5dL9rBnJi3vcKI7wqWQcLJVF/KRXK9dH29HjGNVvFUj0s9prRP3J8jEGnGKDw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"@types/lodash.template": {
|
||||
"version": "4.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash.template/-/lodash.template-4.4.6.tgz",
|
||||
@ -10397,6 +10406,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.round/-/lodash.round-4.0.4.tgz",
|
||||
"integrity": "sha1-EBtrpcbOzJmPKrvoC2Apr/0l0mI="
|
||||
},
|
||||
"lodash.sortby": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
|
||||
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg="
|
||||
},
|
||||
"lodash.template": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz",
|
||||
|
@ -55,6 +55,7 @@
|
||||
"lodash.negate": "^3.0.2",
|
||||
"lodash.random": "^3.2.0",
|
||||
"lodash.round": "^4.0.4",
|
||||
"lodash.sortby": "^4.7.0",
|
||||
"lodash.template": "^4.4.0",
|
||||
"moment": "^2.24.0",
|
||||
"ng-yandex-metrika": "^3.0.2",
|
||||
@ -84,6 +85,7 @@
|
||||
"@types/lodash.negate": "^3.0.6",
|
||||
"@types/lodash.random": "^3.2.6",
|
||||
"@types/lodash.round": "^4.0.6",
|
||||
"@types/lodash.sortby": "^4.7.6",
|
||||
"@types/lodash.template": "^4.4.6",
|
||||
"@types/moment": "^2.13.0",
|
||||
"@types/node": "^12.12.32",
|
||||
|
@ -9,3 +9,4 @@ export * from './files';
|
||||
export * from './kontur-focus';
|
||||
export * from './messages';
|
||||
export * from './capi';
|
||||
export * from './webhooks';
|
||||
|
2
src/app/api/webhooks/index.ts
Normal file
2
src/app/api/webhooks/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './webhooks.module';
|
||||
export * from './webhooks.service';
|
10
src/app/api/webhooks/webhooks.module.ts
Normal file
10
src/app/api/webhooks/webhooks.module.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { CAPIModule } from '../capi';
|
||||
import { WebhooksService } from './webhooks.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [CAPIModule],
|
||||
providers: [WebhooksService]
|
||||
})
|
||||
export class WebhooksModule {}
|
26
src/app/api/webhooks/webhooks.service.ts
Normal file
26
src/app/api/webhooks/webhooks.service.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { Webhook, WebhooksService as ApiWebhooksService } from '../../api-codegen/capi';
|
||||
import { genXRequestID } from '../utils';
|
||||
|
||||
@Injectable()
|
||||
export class WebhooksService {
|
||||
constructor(private apiWebhooksService: ApiWebhooksService) {}
|
||||
|
||||
createWebhook(params: Webhook): Observable<Webhook> {
|
||||
return this.apiWebhooksService.createWebhook(genXRequestID(), params);
|
||||
}
|
||||
|
||||
getWebhooks(): Observable<Webhook[]> {
|
||||
return this.apiWebhooksService.getWebhooks(genXRequestID());
|
||||
}
|
||||
|
||||
getWebhookByID(weebhookID: string): Observable<Webhook> {
|
||||
return this.apiWebhooksService.getWebhookByID(genXRequestID(), weebhookID);
|
||||
}
|
||||
|
||||
deleteWebhookByID(weebhookID: string): Observable<any> {
|
||||
return this.apiWebhooksService.deleteWebhookByID(genXRequestID(), weebhookID);
|
||||
}
|
||||
}
|
1
src/app/sections/payment-section/integrations/index.ts
Normal file
1
src/app/sections/payment-section/integrations/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './integrations.module';
|
@ -0,0 +1,23 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { IntegrationsComponent } from './integrations.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: IntegrationsComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'webhooks',
|
||||
loadChildren: () => import('./webhooks/webhooks.module').then(m => m.WebhooksModule)
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class IntegrationsRoutingModule {}
|
@ -0,0 +1,24 @@
|
||||
<dsh-scroll-up></dsh-scroll-up>
|
||||
<div
|
||||
class="dsh-integrations"
|
||||
*transloco="let t; scope: 'payment-section'; read: 'paymentSection.integrations'"
|
||||
fxLayout="column"
|
||||
fxLayoutGap="20px"
|
||||
>
|
||||
<h1 class="mat-display-1">{{ t.headline }}</h1>
|
||||
<nav dsh-tab-nav-bar>
|
||||
<a
|
||||
dsh-tab-link
|
||||
*ngFor="let link of links"
|
||||
[routerLink]="link.path"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
[active]="rla.isActive"
|
||||
>
|
||||
{{ t.tabs[link.path] }}
|
||||
</a>
|
||||
</nav>
|
||||
<div>
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,4 @@
|
||||
.dsh-integrations {
|
||||
max-width: 720px;
|
||||
margin: auto;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router, RouterEvent } from '@angular/router';
|
||||
import { filter } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'integrations.component.html',
|
||||
styleUrls: ['integrations.component.scss']
|
||||
})
|
||||
export class IntegrationsComponent {
|
||||
links = [
|
||||
{
|
||||
path: 'webhooks'
|
||||
}
|
||||
];
|
||||
|
||||
constructor(private router: Router) {
|
||||
this.router.events
|
||||
.pipe(filter((e: RouterEvent) => e.url && e.url.endsWith('integrations')))
|
||||
.subscribe(e => this.router.navigate([e.url, 'webhooks']));
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexModule } from '@angular/flex-layout';
|
||||
import { TranslocoModule } from '@ngneat/transloco';
|
||||
|
||||
import { LayoutModule } from '@dsh/components/layout';
|
||||
import { ScrollUpModule } from '@dsh/components/navigation';
|
||||
|
||||
import { IntegrationsRoutingModule } from './integrations-routing.module';
|
||||
import { IntegrationsComponent } from './integrations.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [IntegrationsRoutingModule, CommonModule, FlexModule, LayoutModule, TranslocoModule, ScrollUpModule],
|
||||
declarations: [IntegrationsComponent]
|
||||
})
|
||||
export class IntegrationsModule {}
|
@ -0,0 +1,29 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Subject } from 'rxjs';
|
||||
import { filter, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { CreateWebhookComponent } from './create-webhook/create-webhook.component';
|
||||
import { ReceiveWebhooksService } from './receive-webhooks.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateWebhookService {
|
||||
private createWebhook$ = new Subject();
|
||||
|
||||
constructor(private dialog: MatDialog, private receiveWebhooksService: ReceiveWebhooksService) {
|
||||
this.createWebhook$.pipe(switchMap(() => this.openCreateClaimDialog())).subscribe(() => {
|
||||
this.receiveWebhooksService.receiveWebhooks();
|
||||
});
|
||||
}
|
||||
|
||||
createWebhook() {
|
||||
this.createWebhook$.next();
|
||||
}
|
||||
|
||||
private openCreateClaimDialog() {
|
||||
return this.dialog
|
||||
.open(CreateWebhookComponent, { width: '560px', disableClose: true })
|
||||
.afterClosed()
|
||||
.pipe(filter(r => r === 'created'));
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<div fxLayout="column" [fxLayoutGap]="layoutGap" *transloco="let t; scope: 'webhook'; read: 'webhook'">
|
||||
<div class="mat-title">{{ t.create.title }}</div>
|
||||
<form [formGroup]="form" fxLayout="column">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ t.details.shop }}</mat-label>
|
||||
<mat-select formControlName="shop" required>
|
||||
<mat-option *ngFor="let shop of shops$ | async" [value]="shop.id">
|
||||
{{ shop.details.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ t.details.url }}</mat-label>
|
||||
<input formControlName="url" matInput type="text" autocomplete="off" required />
|
||||
</mat-form-field>
|
||||
<section class="checkbox-section" fxLayout="column" fxLayoutGap="10px">
|
||||
<label class="mat-body-2">{{ t.create.types }}:</label>
|
||||
<div formArrayName="eventTypes" fxLayout="column" fxLayoutGap="10px">
|
||||
<div *ngFor="let eventType of eventTypes.controls; let i = index" [formGroupName]="i">
|
||||
<mat-checkbox formControlName="selected" class="mat-body-1">{{
|
||||
t.events.types[eventType.controls.eventName.value]
|
||||
}}</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
<div fxLayout="row" fxLayoutAlign="space-between center" *transloco="let m">
|
||||
<button dsh-button (click)="cancel()">{{ m.cancel }}</button>
|
||||
<button dsh-button color="accent" [disabled]="(isLoading$ | async) || !form.valid" (click)="save()">
|
||||
{{ t.create.attach }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,42 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { FormArray } from '@angular/forms';
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { filter } from 'rxjs/operators';
|
||||
|
||||
import { ShopService } from '../../../../../api/shop';
|
||||
import { LAYOUT_GAP } from '../../../../constants';
|
||||
import { CreateWebhookService } from './create-webhook.service';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'create-webhook.component.html',
|
||||
providers: [CreateWebhookService]
|
||||
})
|
||||
export class CreateWebhookComponent {
|
||||
form = this.createWebhookService.form;
|
||||
types = this.createWebhookService.invoiceTypes;
|
||||
shops$ = this.shopService.shops$;
|
||||
isLoading$ = this.createWebhookService.isLoading$;
|
||||
|
||||
constructor(
|
||||
private dialogRef: MatDialogRef<CreateWebhookComponent>,
|
||||
private createWebhookService: CreateWebhookService,
|
||||
@Inject(LAYOUT_GAP) public layoutGap: string,
|
||||
private shopService: ShopService
|
||||
) {
|
||||
this.createWebhookService.webhookCreated$.pipe(filter(r => !!r)).subscribe(r => {
|
||||
this.dialogRef.close(r);
|
||||
});
|
||||
}
|
||||
|
||||
get eventTypes() {
|
||||
return this.form.get('eventTypes') as FormArray;
|
||||
}
|
||||
|
||||
save() {
|
||||
this.createWebhookService.save();
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { TranslocoService } from '@ngneat/transloco';
|
||||
import { Subject } from 'rxjs';
|
||||
import { catchError, map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { InvoicesTopic } from '../../../../../api-codegen/capi/swagger-codegen';
|
||||
import { WebhooksService } from '../../../../../api/webhooks';
|
||||
import { booleanDebounceTime, progress, SHARE_REPLAY_CONF } from '../../../../../custom-operators';
|
||||
import { FormParams } from './form-params';
|
||||
import { formValuesToWebhook } from './form-values-to-webhook';
|
||||
|
||||
const oneMustBeSelected: ValidatorFn = (control: FormGroup): ValidationErrors | null =>
|
||||
control.value.map(c => c.selected).includes(true) ? null : { Error: 'At least one of checkboxes select needed' };
|
||||
|
||||
@Injectable()
|
||||
export class CreateWebhookService {
|
||||
invoiceTypes = Object.values(InvoicesTopic.EventTypesEnum);
|
||||
form = this.initForm();
|
||||
|
||||
private createWebhook$: Subject<FormParams> = new Subject();
|
||||
|
||||
webhookCreated$: Subject<'created' | null> = new Subject();
|
||||
isLoading$ = progress(this.createWebhook$, this.webhookCreated$).pipe(
|
||||
booleanDebounceTime(),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private webhooksService: WebhooksService,
|
||||
private snackBar: MatSnackBar,
|
||||
private transloco: TranslocoService
|
||||
) {
|
||||
this.createWebhook$
|
||||
.pipe(
|
||||
map(formValuesToWebhook),
|
||||
switchMap(v =>
|
||||
this.webhooksService.createWebhook(v).pipe(
|
||||
catchError(err => {
|
||||
console.error(err);
|
||||
this.snackBar.open(this.transloco.translate('httpError'), 'OK');
|
||||
this.webhookCreated$.next(null);
|
||||
return [];
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
.subscribe(() => this.webhookCreated$.next('created'));
|
||||
}
|
||||
|
||||
save() {
|
||||
this.createWebhook$.next(this.form.value);
|
||||
}
|
||||
|
||||
private initForm(): FormGroup {
|
||||
return this.fb.group({
|
||||
shop: ['', Validators.required],
|
||||
url: ['', Validators.required],
|
||||
eventTypes: this.fb.array(
|
||||
this.invoiceTypes.map(t =>
|
||||
this.fb.group({
|
||||
eventName: t,
|
||||
selected: false
|
||||
})
|
||||
),
|
||||
[oneMustBeSelected]
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
import { InvoicesTopic } from '../../../../../api-codegen/capi/swagger-codegen';
|
||||
|
||||
interface EventType {
|
||||
eventName: InvoicesTopic.EventTypesEnum;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
export interface FormParams {
|
||||
shop: string;
|
||||
url: string;
|
||||
eventTypes: EventType[];
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
import { Webhook } from '../../../../../api-codegen/capi/swagger-codegen';
|
||||
import { FormParams } from './form-params';
|
||||
|
||||
export const formValuesToWebhook = (v: FormParams): Webhook =>
|
||||
({
|
||||
url: v.url,
|
||||
scope: {
|
||||
shopID: v.shop,
|
||||
eventTypes: v.eventTypes.filter(e => e.selected).map(e => e.eventName),
|
||||
topic: 'InvoicesTopic'
|
||||
}
|
||||
} as Webhook);
|
@ -0,0 +1 @@
|
||||
export * from './webhooks.module';
|
@ -0,0 +1,56 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { TranslocoService } from '@ngneat/transloco';
|
||||
import sortBy from 'lodash.sortby';
|
||||
import { BehaviorSubject, Observable, of, Subject } from 'rxjs';
|
||||
import { catchError, filter, map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { Webhook } from '../../../../api-codegen/capi/swagger-codegen';
|
||||
import { WebhooksService } from '../../../../api/webhooks';
|
||||
import { booleanDebounceTime, progress, SHARE_REPLAY_CONF } from '../../../../custom-operators';
|
||||
|
||||
@Injectable()
|
||||
export class ReceiveWebhooksService {
|
||||
private webhooksState$ = new BehaviorSubject(null);
|
||||
private receiveWebhooks$ = new Subject();
|
||||
|
||||
webhooks$: Observable<Webhook[]> = this.webhooksState$.pipe(
|
||||
filter(s => !!s),
|
||||
map(w => sortBy(w, i => !i.active)),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
|
||||
webhooksReceived$ = this.webhooks$.pipe(
|
||||
map(s => !!s),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
|
||||
isLoading$ = progress(this.receiveWebhooks$, this.webhooksState$).pipe(
|
||||
booleanDebounceTime(),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
|
||||
constructor(
|
||||
private webhooksService: WebhooksService,
|
||||
private snackBar: MatSnackBar,
|
||||
private transloco: TranslocoService
|
||||
) {
|
||||
this.receiveWebhooks$
|
||||
.pipe(
|
||||
switchMap(_ =>
|
||||
this.webhooksService.getWebhooks().pipe(
|
||||
catchError(err => {
|
||||
console.error(err);
|
||||
this.snackBar.open(this.transloco.translate('httpError'), 'OK');
|
||||
return of([]);
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
.subscribe(webhooks => this.webhooksState$.next(webhooks));
|
||||
}
|
||||
|
||||
receiveWebhooks() {
|
||||
this.receiveWebhooks$.next();
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
@import '../../../../../../../node_modules/@angular/material/theming';
|
||||
|
||||
@mixin dsh-webhook-panel-theme($theme) {
|
||||
$gray: map-get($theme, gray);
|
||||
|
||||
.dsh-webhook-header {
|
||||
color: map-get($gray, 500);
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<ng-container *transloco="let t; scope: 'webhook'; read: 'webhook.actions'">
|
||||
<div class="mat-title">{{ t.title }}</div>
|
||||
<button dsh-stroked-button color="warn" (click)="delete()">{{ t.buttons.delete }}</button>
|
||||
</ng-container>
|
@ -0,0 +1,19 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
import { RemoveWebhookService } from './remove-webhook.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-actions',
|
||||
templateUrl: 'actions.component.html',
|
||||
providers: [RemoveWebhookService]
|
||||
})
|
||||
export class ActionsComponent {
|
||||
@Input()
|
||||
webhookID: string;
|
||||
|
||||
constructor(private removeWebhookService: RemoveWebhookService) {}
|
||||
|
||||
delete() {
|
||||
this.removeWebhookService.removeWebhook(this.webhookID);
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { TranslocoService } from '@ngneat/transloco';
|
||||
import { forkJoin, of, Subject } from 'rxjs';
|
||||
import { catchError, filter, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { ConfirmActionDialogComponent } from '@dsh/components/popups';
|
||||
|
||||
import { WebhooksService } from '../../../../../../api/webhooks';
|
||||
import { ReceiveWebhooksService } from '../../receive-webhooks.service';
|
||||
|
||||
@Injectable()
|
||||
export class RemoveWebhookService {
|
||||
private removeWebhook$: Subject<string> = new Subject();
|
||||
|
||||
constructor(
|
||||
private dialog: MatDialog,
|
||||
private receiveWebhooksService: ReceiveWebhooksService,
|
||||
private webhooksService: WebhooksService,
|
||||
private snackBar: MatSnackBar,
|
||||
private transloco: TranslocoService
|
||||
) {
|
||||
this.removeWebhook$
|
||||
.pipe(
|
||||
switchMap(webhookID => forkJoin([of(webhookID), this.openConfirmDialog()])),
|
||||
switchMap(([webhookID]) =>
|
||||
this.webhooksService.deleteWebhookByID(webhookID).pipe(
|
||||
catchError(err => {
|
||||
console.error(err);
|
||||
this.snackBar.open(this.transloco.translate('httpError'), 'OK');
|
||||
return 'error';
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
.subscribe(() => {
|
||||
this.receiveWebhooks();
|
||||
});
|
||||
}
|
||||
|
||||
removeWebhook(webhookID: string) {
|
||||
this.removeWebhook$.next(webhookID);
|
||||
}
|
||||
|
||||
private receiveWebhooks() {
|
||||
this.receiveWebhooksService.receiveWebhooks();
|
||||
}
|
||||
|
||||
private openConfirmDialog() {
|
||||
return this.dialog
|
||||
.open(ConfirmActionDialogComponent)
|
||||
.afterClosed()
|
||||
.pipe(filter(r => r === 'confirm'));
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<div
|
||||
class="dsh-webhook-panel-details-section"
|
||||
fxLayout="column"
|
||||
[fxLayoutGap]="layoutGap"
|
||||
*transloco="let t; scope: 'webhook'; read: 'webhook.details'"
|
||||
>
|
||||
<dsh-details-item [title]="t.url">
|
||||
<div class="mat-body-1">
|
||||
{{ webhook.url }}
|
||||
</div>
|
||||
</dsh-details-item>
|
||||
<dsh-details-item [title]="t.shop">
|
||||
<div class="mat-body-1">
|
||||
{{ shopName }}
|
||||
</div>
|
||||
</dsh-details-item>
|
||||
</div>
|
@ -0,0 +1,18 @@
|
||||
import { Component, Inject, Input } from '@angular/core';
|
||||
|
||||
import { Webhook } from '../../../../../../api-codegen/capi/swagger-codegen';
|
||||
import { LAYOUT_GAP } from '../../../../../constants';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-details',
|
||||
templateUrl: 'details.component.html'
|
||||
})
|
||||
export class DetailsComponent {
|
||||
@Input()
|
||||
webhook: Webhook;
|
||||
|
||||
@Input()
|
||||
shopName: string;
|
||||
|
||||
constructor(@Inject(LAYOUT_GAP) public layoutGap: string) {}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<div class="dsh-webhook-panel-events-section" *transloco="let t; scope: 'webhook'; read: 'webhook.events'">
|
||||
<div class="mat-title">{{ t.title }}</div>
|
||||
<ul fxLayout="column" [fxLayoutGap]="layoutGap" class="dsh-webhooks-event-list">
|
||||
<li class="mat-body-1" *ngFor="let event of events">{{ t.types[event] }}</li>
|
||||
</ul>
|
||||
</div>
|
@ -0,0 +1,5 @@
|
||||
.dsh-webhooks-event-list {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin: 0;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import { Component, Inject, Input } from '@angular/core';
|
||||
|
||||
import { InvoicesTopic } from '../../../../../../api-codegen/capi/swagger-codegen';
|
||||
import { LAYOUT_GAP } from '../../../../../constants';
|
||||
type InvoicesEventTypesEnum = InvoicesTopic.EventTypesEnum;
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-events',
|
||||
templateUrl: 'events.component.html',
|
||||
styleUrls: ['events.component.scss']
|
||||
})
|
||||
export class EventsComponent {
|
||||
@Input()
|
||||
events: InvoicesEventTypesEnum[];
|
||||
|
||||
constructor(@Inject(LAYOUT_GAP) public layoutGap: string) {}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<div class="dsh-webhook-panel-key-section" *transloco="let t; scope: 'webhook'; read: 'webhook.key'">
|
||||
<div class="mat-title">{{ t.title }}</div>
|
||||
<div fxLayout="column" [fxLayoutGap]="layoutGap">
|
||||
<div class="mat-caption">
|
||||
{{ t.publicKey }}
|
||||
</div>
|
||||
<div class="mat-body-1 pre-wrap">
|
||||
{{ key }}
|
||||
</div>
|
||||
<div>
|
||||
<button dsh-button [cdkCopyToClipboard]="key" (cdkCopyToClipboardCopied)="copied($event)">
|
||||
{{ t.buttons.copy }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,3 @@
|
||||
.pre-wrap {
|
||||
white-space: pre-wrap;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { Component, Inject, Input } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { TranslocoService } from '@ngneat/transloco';
|
||||
|
||||
import { LAYOUT_GAP } from '../../../../../constants';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-key',
|
||||
templateUrl: 'key.component.html',
|
||||
styleUrls: ['key.component.scss']
|
||||
})
|
||||
export class KeyComponent {
|
||||
@Input()
|
||||
key: string;
|
||||
|
||||
constructor(
|
||||
@Inject(LAYOUT_GAP) public layoutGap: string,
|
||||
private snackBar: MatSnackBar,
|
||||
private transloco: TranslocoService
|
||||
) {}
|
||||
|
||||
copied(isCopied: boolean) {
|
||||
this.snackBar.open(this.transloco.translate(isCopied ? 'copied' : 'copyFailed'), 'OK', { duration: 1000 });
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<dsh-expand-panel #expandPanel *transloco="let t; scope: 'webhook'; read: 'webhook.details'">
|
||||
<div fxLayout="column" fxLayoutGap="10px">
|
||||
<div class="mat-body-1">{{ webhook.url }}</div>
|
||||
<div fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<span class="mat-caption">{{ shopName$ | async }}</span>
|
||||
<span class="mat-caption">
|
||||
{{ webhook.active ? t.status.active : t.status.inactive }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<dsh-expand-panel-more>
|
||||
<div fxLayout="column" [fxLayoutGap]="layoutGap" fxFlex="100">
|
||||
<div fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<div class="mat-caption dsh-webhook-header" *transloco="let d; scope: 'webhook'; read: 'webhook'">
|
||||
{{ d.webhook }} #{{ webhook.id }}
|
||||
</div>
|
||||
<div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="10px">
|
||||
<div class="mat-caption dsh-webhook-header">
|
||||
{{ webhook.active ? t.status.active : t.status.inactive }}
|
||||
</div>
|
||||
<button dsh-icon-button>
|
||||
<mat-icon svgIcon="keyboard_arrow_up" (click)="expandPanel.collapse($event)"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<dsh-details [webhook]="webhook" [shopName]="shopName$ | async"></dsh-details>
|
||||
<mat-divider></mat-divider>
|
||||
<dsh-events [events]="events"></dsh-events>
|
||||
<mat-divider></mat-divider>
|
||||
<dsh-key [key]="webhook.publicKey"></dsh-key>
|
||||
<mat-divider *ngIf="webhook.active"></mat-divider>
|
||||
<dsh-actions [webhookID]="webhook.id" *ngIf="webhook.active"></dsh-actions>
|
||||
</div>
|
||||
</dsh-expand-panel-more>
|
||||
</dsh-expand-panel>
|
@ -0,0 +1,35 @@
|
||||
import { Component, Inject, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import isEqual from 'lodash.isequal';
|
||||
import { pluck } from 'rxjs/operators';
|
||||
|
||||
import { InvoicesTopic, Webhook } from '../../../../../api-codegen/capi/swagger-codegen';
|
||||
import { LAYOUT_GAP } from '../../../../constants';
|
||||
import { WebhookPanelService } from './webhook-panel.service';
|
||||
|
||||
type InvoicesEventTypesEnum = InvoicesTopic.EventTypesEnum;
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-webhook-panel',
|
||||
templateUrl: 'webhook-panel.component.html',
|
||||
providers: [WebhookPanelService]
|
||||
})
|
||||
export class WebhookPanelComponent implements OnChanges {
|
||||
@Input()
|
||||
webhook: Webhook;
|
||||
|
||||
events: InvoicesEventTypesEnum[] = [];
|
||||
|
||||
shopName$ = this.webhookPanelService.shopInfo$.pipe(pluck('name'));
|
||||
|
||||
constructor(@Inject(LAYOUT_GAP) public layoutGap: string, private webhookPanelService: WebhookPanelService) {}
|
||||
|
||||
ngOnChanges({ webhook }: SimpleChanges) {
|
||||
if (!isEqual(webhook.previousValue, webhook.currentValue)) {
|
||||
const {
|
||||
scope: { eventTypes, shopID }
|
||||
} = webhook.currentValue;
|
||||
this.events = eventTypes;
|
||||
this.webhookPanelService.getShopInfo(shopID);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import { ClipboardModule } from '@angular/cdk/clipboard';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexModule } from '@angular/flex-layout';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { TranslocoModule } from '@ngneat/transloco';
|
||||
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
import { LayoutModule } from '@dsh/components/layout';
|
||||
import { ConfirmActionDialogModule } from '@dsh/components/popups';
|
||||
|
||||
import { ActionsComponent } from './actions/actions.component';
|
||||
import { DetailsComponent } from './details/details.component';
|
||||
import { EventsComponent } from './events/events.component';
|
||||
import { KeyComponent } from './key/key.component';
|
||||
import { WebhookPanelComponent } from './webhook-panel.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
ClipboardModule,
|
||||
LayoutModule,
|
||||
CommonModule,
|
||||
ButtonModule,
|
||||
FlexModule,
|
||||
MatIconModule,
|
||||
TranslocoModule,
|
||||
MatDialogModule,
|
||||
ConfirmActionDialogModule,
|
||||
MatDividerModule
|
||||
],
|
||||
declarations: [WebhookPanelComponent, ActionsComponent, DetailsComponent, EventsComponent, KeyComponent],
|
||||
exports: [WebhookPanelComponent]
|
||||
})
|
||||
export class WebhookPanelModule {}
|
@ -0,0 +1,32 @@
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
import { combineLatest, Observable, of, Subject, Subscription } from 'rxjs';
|
||||
import { distinctUntilChanged, map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { ShopService } from '../../../../../api/shop';
|
||||
import { SHARE_REPLAY_CONF } from '../../../../../custom-operators';
|
||||
import { mapToShopInfo, ShopInfo } from '../../../operations/operators';
|
||||
|
||||
@Injectable()
|
||||
export class WebhookPanelService implements OnDestroy {
|
||||
private getShopInfo$ = new Subject<string>();
|
||||
private subscription: Subscription = Subscription.EMPTY;
|
||||
|
||||
shopInfo$: Observable<ShopInfo> = this.getShopInfo$.pipe(
|
||||
distinctUntilChanged(),
|
||||
switchMap(shopID => combineLatest([of(shopID), this.shopService.shops$.pipe(mapToShopInfo)])),
|
||||
map(([shopID, shops]) => shops.find(shop => shop.shopID === shopID)),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
|
||||
constructor(private shopService: ShopService) {
|
||||
this.subscription = this.shopInfo$.subscribe();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
getShopInfo(shopID: string) {
|
||||
this.getShopInfo$.next(shopID);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { WebhooksComponent } from './webhooks.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: WebhooksComponent
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)]
|
||||
})
|
||||
export class WebhooksRoutingModule {}
|
@ -0,0 +1,27 @@
|
||||
<div fxLayout="column" [fxLayoutGap]="layoutGap" *transloco="let t; scope: 'webhook'; read: 'webhook'">
|
||||
<div
|
||||
fxLayout.lt-md="column"
|
||||
fxLayout="row"
|
||||
fxLayoutAlign.lt-md="center stretch"
|
||||
fxLayoutAlign="space-between center"
|
||||
[fxLayoutGap]="layoutGap"
|
||||
>
|
||||
<div class="mat-caption">
|
||||
{{ t.description }}
|
||||
</div>
|
||||
<button dsh-stroked-button color="accent" (click)="createWebhook()" fxFlex.gt-sm="25">{{ t.attach }}</button>
|
||||
</div>
|
||||
|
||||
<div *ngIf="isLoading$ | async" fxLayout fxLayoutAlign="center center">
|
||||
<dsh-spinner></dsh-spinner>
|
||||
</div>
|
||||
|
||||
<dsh-empty-search-result
|
||||
*ngIf="!(webhooks$ | async)?.length && (webhooksReceived$ | async)"
|
||||
[text]="t.emptyResult"
|
||||
></dsh-empty-search-result>
|
||||
|
||||
<div fxLayout="column" [fxLayoutGap]="layoutGap" *ngIf="!(isLoading$ | async)">
|
||||
<dsh-webhook-panel *ngFor="let webhook of webhooks$ | async" [webhook]="webhook"></dsh-webhook-panel>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,28 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
|
||||
import { LAYOUT_GAP } from '../../../constants';
|
||||
import { CreateWebhookService } from './create-webhook.service';
|
||||
import { ReceiveWebhooksService } from './receive-webhooks.service';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'webhooks.component.html'
|
||||
})
|
||||
export class WebhooksComponent implements OnInit {
|
||||
webhooks$ = this.receiveWebhooksService.webhooks$;
|
||||
isLoading$ = this.receiveWebhooksService.isLoading$;
|
||||
webhooksReceived$ = this.receiveWebhooksService.webhooksReceived$;
|
||||
|
||||
constructor(
|
||||
private receiveWebhooksService: ReceiveWebhooksService,
|
||||
@Inject(LAYOUT_GAP) public layoutGap: string,
|
||||
private createWebhookService: CreateWebhookService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.receiveWebhooksService.receiveWebhooks();
|
||||
}
|
||||
|
||||
createWebhook() {
|
||||
this.createWebhookService.createWebhook();
|
||||
}
|
||||
}
|
@ -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 { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { TranslocoModule } from '@ngneat/transloco';
|
||||
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
import { EmptySearchResultModule } from '@dsh/components/empty-search-result';
|
||||
import { SpinnerModule } from '@dsh/components/indicators';
|
||||
|
||||
import { ShopService } from '../../../../api/shop';
|
||||
import { WebhooksModule as ApiWebhooksModule } from '../../../../api/webhooks';
|
||||
import { CreateWebhookService } from './create-webhook.service';
|
||||
import { CreateWebhookComponent } from './create-webhook/create-webhook.component';
|
||||
import { ReceiveWebhooksService } from './receive-webhooks.service';
|
||||
import { WebhookPanelModule } from './webhook-panel/webhook-panel.module';
|
||||
import { WebhooksRoutingModule } from './webhooks-routing.module';
|
||||
import { WebhooksComponent } from './webhooks.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
WebhooksRoutingModule,
|
||||
ApiWebhooksModule,
|
||||
ButtonModule,
|
||||
MatDialogModule,
|
||||
CommonModule,
|
||||
FlexModule,
|
||||
ReactiveFormsModule,
|
||||
MatFormFieldModule,
|
||||
MatSelectModule,
|
||||
MatInputModule,
|
||||
MatCheckboxModule,
|
||||
TranslocoModule,
|
||||
WebhookPanelModule,
|
||||
SpinnerModule,
|
||||
EmptySearchResultModule
|
||||
],
|
||||
declarations: [WebhooksComponent, CreateWebhookComponent],
|
||||
entryComponents: [CreateWebhookComponent],
|
||||
providers: [ReceiveWebhooksService, CreateWebhookService, ShopService]
|
||||
})
|
||||
export class WebhooksModule {}
|
@ -29,4 +29,14 @@
|
||||
>
|
||||
{{ t.reports }}
|
||||
</dsh-state-nav-item>
|
||||
<dsh-state-nav-item
|
||||
routerLink="./integrations"
|
||||
routerLinkActive
|
||||
#integrations="routerLinkActive"
|
||||
[selected]="integrations.isActive"
|
||||
withIcon
|
||||
icon="build"
|
||||
>
|
||||
{{ t.integrations }}
|
||||
</dsh-state-nav-item>
|
||||
</dsh-state-nav>
|
||||
|
@ -14,6 +14,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { TranslocoModule, TRANSLOCO_SCOPE } from '@ngneat/transloco';
|
||||
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
import { EmptySearchResultModule } from '@dsh/components/empty-search-result';
|
||||
import { FormControlsModule, RangeDatepickerModule } from '@dsh/components/form-controls';
|
||||
import { IndicatorsModule } from '@dsh/components/indicators';
|
||||
import { LayoutModule } from '@dsh/components/layout';
|
||||
@ -23,7 +24,6 @@ import { TableModule } from '@dsh/components/table';
|
||||
import { InvoiceModule } from '../../../../api';
|
||||
import { FromMinorModule } from '../../../../from-minor';
|
||||
import { LanguageModule } from '../../../../language';
|
||||
import { EmptySearchResultModule } from '../../empty-search-result';
|
||||
import { LastUpdatedModule } from '../last-updated/last-updated.module';
|
||||
import { CreateInvoiceDialogComponent } from './create-invoice-dialog';
|
||||
import { InvoicesRoutingModule } from './invoices-routing.module';
|
||||
|
@ -11,6 +11,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { TranslocoModule, TRANSLOCO_SCOPE } from '@ngneat/transloco';
|
||||
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
import { EmptySearchResultModule } from '@dsh/components/empty-search-result';
|
||||
import { FormControlsModule, RangeDatepickerModule } from '@dsh/components/form-controls';
|
||||
import { IndicatorsModule } from '@dsh/components/indicators';
|
||||
import { LayoutModule } from '@dsh/components/layout';
|
||||
@ -19,7 +20,6 @@ import { TableModule } from '@dsh/components/table';
|
||||
|
||||
import { FromMinorModule } from '../../../../from-minor';
|
||||
import { LanguageModule } from '../../../../language';
|
||||
import { EmptySearchResultModule } from '../../empty-search-result';
|
||||
import { LastUpdatedModule } from '../last-updated/last-updated.module';
|
||||
import { PaymentsRoutingModule } from './payments-routing.module';
|
||||
import { PaymentsComponent } from './payments.component';
|
||||
|
@ -11,6 +11,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { TranslocoModule, TRANSLOCO_SCOPE } from '@ngneat/transloco';
|
||||
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
import { EmptySearchResultModule } from '@dsh/components/empty-search-result';
|
||||
import { FormControlsModule, RangeDatepickerModule } from '@dsh/components/form-controls';
|
||||
import { IndicatorsModule } from '@dsh/components/indicators';
|
||||
import { LayoutModule } from '@dsh/components/layout';
|
||||
@ -18,7 +19,6 @@ import { StateNavModule } from '@dsh/components/navigation';
|
||||
import { TableModule } from '@dsh/components/table';
|
||||
|
||||
import { FromMinorModule } from '../../../../from-minor';
|
||||
import { EmptySearchResultModule } from '../../empty-search-result';
|
||||
import { LastUpdatedModule } from '../last-updated/last-updated.module';
|
||||
import { RefundsRoutingModule } from './refunds-routing.module';
|
||||
import { RefundsComponent } from './refunds.component';
|
||||
|
@ -10,15 +10,19 @@ const paymentSectionRoutes: Routes = [
|
||||
children: [
|
||||
{
|
||||
path: 'operations',
|
||||
loadChildren: () => import('./operations/operations.module').then(mod => mod.OperationsModule)
|
||||
loadChildren: () => import('./operations/operations.module').then(m => m.OperationsModule)
|
||||
},
|
||||
{
|
||||
path: 'reports',
|
||||
loadChildren: () => import('./reports/reports.module').then(mod => mod.ReportsModule)
|
||||
loadChildren: () => import('./reports/reports.module').then(m => m.ReportsModule)
|
||||
},
|
||||
{
|
||||
path: 'payouts',
|
||||
loadChildren: () => import('./payouts/payouts.module').then(mod => mod.PayoutsModule)
|
||||
loadChildren: () => import('./payouts/payouts.module').then(m => m.PayoutsModule)
|
||||
},
|
||||
{
|
||||
path: 'integrations',
|
||||
loadChildren: () => import('./integrations/integrations.module').then(m => m.IntegrationsModule)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { TranslocoModule } from '@ngneat/transloco';
|
||||
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
import { EmptySearchResultModule } from '@dsh/components/empty-search-result';
|
||||
import { FormControlsModule } from '@dsh/components/form-controls';
|
||||
import { IndicatorsModule } from '@dsh/components/indicators';
|
||||
import { LayoutModule } from '@dsh/components/layout';
|
||||
@ -20,7 +21,6 @@ import { StateNavModule } from '@dsh/components/navigation';
|
||||
import { TableModule } from '@dsh/components/table';
|
||||
|
||||
import { ReportsModule as ReportsApiModule } from '../../../api';
|
||||
import { EmptySearchResultModule } from '../empty-search-result';
|
||||
import { LastUpdatedModule } from '../operations/last-updated/last-updated.module';
|
||||
import { CreateReportDialogComponent } from './create-report-dialog';
|
||||
import { ReportsRoutingModule } from './reports-routing.module';
|
||||
|
@ -2,7 +2,8 @@
|
||||
"nav": {
|
||||
"operations": "Операции",
|
||||
"reports": "Отчеты",
|
||||
"payouts": "Возмещения"
|
||||
"payouts": "Возмещения",
|
||||
"integrations": "Интеграция"
|
||||
},
|
||||
"operations": {
|
||||
"headline": "Операции",
|
||||
@ -11,5 +12,11 @@
|
||||
"refunds": "Возвраты",
|
||||
"invoices": "Инвойсы"
|
||||
}
|
||||
},
|
||||
"integrations": {
|
||||
"headline": "Интеграция",
|
||||
"tabs": {
|
||||
"webhooks": "Webhooks"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,8 @@
|
||||
"loading": "Загрузка...",
|
||||
"ok": "OK",
|
||||
"noDocs": "Нет прикрепленных документов",
|
||||
"copied": "Скопировано",
|
||||
"copyFailed": "Ошибка копирования",
|
||||
"period": {
|
||||
"fromTime": "Начало периода",
|
||||
"toTime": "Конец периода"
|
||||
|
56
src/assets/i18n/webhook/ru.json
Normal file
56
src/assets/i18n/webhook/ru.json
Normal file
@ -0,0 +1,56 @@
|
||||
{
|
||||
"webhook": "Webhook",
|
||||
"description": "Webhook – это инструмент для получения асинхронных оповещений при наступлении одного или группы интересующих вас событий. Например: платеж успешно завершен.",
|
||||
"attach": "Установить webhook",
|
||||
"emptyResult": "Webhook’и отсутствуют",
|
||||
"details": {
|
||||
"status": {
|
||||
"active": "Активен",
|
||||
"inactive": "Неактивен"
|
||||
},
|
||||
"url": "URL, на который будут поступать оповещения о произошедших событиях",
|
||||
"shop": "Магазин"
|
||||
},
|
||||
"events": {
|
||||
"title": "Набор событий",
|
||||
"types": {
|
||||
"CustomerCreated": "CustomerCreated (плательщик создан)",
|
||||
"CustomerDeleted": "CustomerDeleted (плательщик удален)",
|
||||
"CustomerReady": "CustomerReady (плательщик готов)",
|
||||
"CustomerBindingStarted": "CustomerBindingStarted (привязка к плательщику запущена)",
|
||||
"CustomerBindingSucceeded": "CustomerBindingSucceeded (привязка к плательщику успешно завершена)",
|
||||
"CustomerBindingFailed": "CustomerBindingFailed (привязка к плательщику завершена с неудачей)",
|
||||
"InvoiceCreated": "InvoiceCreated (создан новый инвойс)",
|
||||
"InvoicePaid": "InvoicePaid (инвойс перешел в состояние \"Оплачен\")",
|
||||
"InvoiceCancelled": "InvoiceCancelled (инвойс отменен по истечению срока давности)",
|
||||
"InvoiceFulfilled": "InvoiceFulfilled (инвойс успешно погашен)",
|
||||
"PaymentStarted": "PaymentStarted (создан платеж)",
|
||||
"PaymentProcessed": "PaymentProcessed (платеж в обработке)",
|
||||
"PaymentCaptured": "PaymentCaptured (платеж успешно завершен)",
|
||||
"PaymentCancelled": "PaymentCancelled (платеж успешно отменен)",
|
||||
"PaymentRefunded": "PaymentRefunded (платеж успешно возвращен)",
|
||||
"PaymentFailed": "PaymentFailed (при проведении платежа возникла ошибка)",
|
||||
"PaymentRefundCreated": "PaymentRefundCreated (возврат платежа успешно создан)",
|
||||
"PaymentRefundSucceeded": "PaymentRefundSucceeded (возврат платежа прошел успешно)",
|
||||
"PaymentRefundFailed": "PaymentRefundFailed (возврат платежа завершился неудачей)"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"title": "Действия",
|
||||
"buttons": {
|
||||
"delete": "Удалить webhook"
|
||||
}
|
||||
},
|
||||
"key": {
|
||||
"title": "Публичный ключ",
|
||||
"publicKey": "Содержимое публичного ключа, служащего для проверки авторитативности приходящих на url оповещений",
|
||||
"buttons": {
|
||||
"copy": "Скопировать ключ"
|
||||
}
|
||||
},
|
||||
"create": {
|
||||
"title": "Установить новый webhook",
|
||||
"types": "Набор типов событий, о которых следует оповещать",
|
||||
"attach": "Установить"
|
||||
}
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
<div *transloco="let t" class="mat-headline dsh-empty-search-result">
|
||||
{{ t.emptySearchResult }}
|
||||
{{ text ? text : t.emptySearchResult }}
|
||||
</div>
|
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-empty-search-result',
|
||||
@ -6,4 +6,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
styleUrls: ['empty-search-result.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class EmptySearchResultComponent {}
|
||||
export class EmptySearchResultComponent {
|
||||
@Input()
|
||||
text?: string;
|
||||
}
|
@ -35,6 +35,7 @@
|
||||
@import '../../app/sections/invoice-details/cart/cart-theme';
|
||||
@import '../../app/sections/invoice-details/payments/payments-theme';
|
||||
@import '../../app/sections/payment-section/payouts/payout-panel/payout-panel-theme';
|
||||
@import '../../app/sections/payment-section/integrations/webhooks/webhook-panel/webhook-panel-theme';
|
||||
|
||||
@mixin dsh-theme($theme) {
|
||||
body.#{map-get($theme, name)} {
|
||||
@ -72,5 +73,6 @@
|
||||
@include dsh-panel-theme($theme);
|
||||
@include dsh-range-datepicker-theme($theme);
|
||||
@include dsh-payout-panel-theme($theme);
|
||||
@include dsh-webhook-panel-theme($theme);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user