mirror of
https://github.com/valitydev/dashboard.git
synced 2024-11-06 02:25:23 +00:00
OPS-280: New API Keys Module (#122)
This commit is contained in:
parent
57db0bbe3d
commit
8ee6946797
@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PrettierConfiguration">
|
||||
<option name="myConfigurationMode" value="AUTOMATIC" />
|
||||
<option name="myRunOnSave" value="true" />
|
||||
<option name="myFilesPattern" value="{**/*,*}.{html,js,ts,css,scss,md,json,prettierrc,svg,yaml,yml}" />
|
||||
</component>
|
||||
|
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@ -1,10 +1,4 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.tslint": true
|
||||
},
|
||||
"javascript.preferences.importModuleSpecifier": "relative",
|
||||
"typescript.preferences.importModuleSpecifier": "relative",
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"cSpell.words": [
|
||||
"CAPI",
|
||||
@ -46,6 +40,5 @@
|
||||
"инвойса"
|
||||
],
|
||||
"cSpell.language": "en,ru",
|
||||
"prettier.prettierPath": "node_modules/prettier",
|
||||
"tasksStatusbar.taskLabelFilter": "Start",
|
||||
"prettier.prettierPath": "node_modules/prettier"
|
||||
}
|
||||
|
17
.vscode/tasks.json
vendored
17
.vscode/tasks.json
vendored
@ -1,17 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"problemMatcher": [],
|
||||
"label": "Start dev"
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "stage",
|
||||
"problemMatcher": [],
|
||||
"label": "Start stage"
|
||||
}
|
||||
]
|
||||
}
|
47
package-lock.json
generated
47
package-lock.json
generated
@ -31,7 +31,9 @@
|
||||
"@sentry/angular": "7.7.0",
|
||||
"@sentry/integrations": "7.7.0",
|
||||
"@sentry/tracing": "7.7.0",
|
||||
"@vality/ng-core": "^0.3.0",
|
||||
"@vality/swag-anapi-v2": "2.0.1-38f360b.0",
|
||||
"@vality/swag-api-keys": "^1.0.0",
|
||||
"@vality/swag-claim-management": "0.1.1-bfc2e6c.0",
|
||||
"@vality/swag-organizations": "1.0.1-cd6cc10.0",
|
||||
"@vality/swag-payments": "0.1.1-01da4bb.0",
|
||||
@ -6048,6 +6050,23 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@vality/ng-core": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@vality/ng-core/-/ng-core-0.3.0.tgz",
|
||||
"integrity": "sha512-uezgCzRGWTfe6PDnAqbFQ0nqCv+8auIGTd1tej8hV+h0fTOahAJybTrpYpzvegYdVJ1IwVAUqMAiLKo5RCtKhQ==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/cdk": ">=15.0.0",
|
||||
"@angular/common": ">=15.0.0",
|
||||
"@angular/core": ">=15.0.0",
|
||||
"@angular/material": ">=15.0.0",
|
||||
"coerce-property": ">=0.3.2",
|
||||
"lodash-es": "^4.0.0",
|
||||
"utility-types": ">=3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vality/swag-anapi-v2": {
|
||||
"version": "2.0.1-38f360b.0",
|
||||
"resolved": "https://registry.npmjs.org/@vality/swag-anapi-v2/-/swag-anapi-v2-2.0.1-38f360b.0.tgz",
|
||||
@ -6060,6 +6079,18 @@
|
||||
"@angular/core": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vality/swag-api-keys": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@vality/swag-api-keys/-/swag-api-keys-1.0.0.tgz",
|
||||
"integrity": "sha512-UjPzFVLy3dccgW14MnNlVUfRDIIiZGlYhtgCxBOFoPIyf954BsVeUOm/f4gpZXwzDhU6ge60HAUzTCxlu5/Sew==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">=14.0.0",
|
||||
"@angular/core": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vality/swag-claim-management": {
|
||||
"version": "0.1.1-bfc2e6c.0",
|
||||
"license": "Apache-2.0",
|
||||
@ -22478,6 +22509,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@vality/ng-core": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@vality/ng-core/-/ng-core-0.3.0.tgz",
|
||||
"integrity": "sha512-uezgCzRGWTfe6PDnAqbFQ0nqCv+8auIGTd1tej8hV+h0fTOahAJybTrpYpzvegYdVJ1IwVAUqMAiLKo5RCtKhQ==",
|
||||
"requires": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"@vality/swag-anapi-v2": {
|
||||
"version": "2.0.1-38f360b.0",
|
||||
"resolved": "https://registry.npmjs.org/@vality/swag-anapi-v2/-/swag-anapi-v2-2.0.1-38f360b.0.tgz",
|
||||
@ -22486,6 +22525,14 @@
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"@vality/swag-api-keys": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@vality/swag-api-keys/-/swag-api-keys-1.0.0.tgz",
|
||||
"integrity": "sha512-UjPzFVLy3dccgW14MnNlVUfRDIIiZGlYhtgCxBOFoPIyf954BsVeUOm/f4gpZXwzDhU6ge60HAUzTCxlu5/Sew==",
|
||||
"requires": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"@vality/swag-claim-management": {
|
||||
"version": "0.1.1-bfc2e6c.0",
|
||||
"requires": {
|
||||
|
@ -49,7 +49,9 @@
|
||||
"@sentry/angular": "7.7.0",
|
||||
"@sentry/integrations": "7.7.0",
|
||||
"@sentry/tracing": "7.7.0",
|
||||
"@vality/ng-core": "^0.3.0",
|
||||
"@vality/swag-anapi-v2": "2.0.1-38f360b.0",
|
||||
"@vality/swag-api-keys": "^1.0.0",
|
||||
"@vality/swag-claim-management": "0.1.1-bfc2e6c.0",
|
||||
"@vality/swag-organizations": "1.0.1-cd6cc10.0",
|
||||
"@vality/swag-payments": "0.1.1-01da4bb.0",
|
||||
|
16
src/app/api/api-keys/api-keys.module.ts
Normal file
16
src/app/api/api-keys/api-keys.module.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Configuration } from '@vality/swag-api-keys';
|
||||
|
||||
import { ConfigService } from '../../config';
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
{
|
||||
provide: Configuration,
|
||||
deps: [ConfigService],
|
||||
useFactory: (configService: ConfigService) =>
|
||||
new Configuration({ basePath: `${configService.apiEndpoint}/apikeys/v1` }),
|
||||
},
|
||||
],
|
||||
})
|
||||
export class ApiKeysModule {}
|
10
src/app/api/api-keys/api-keys.service.ts
Normal file
10
src/app/api/api-keys/api-keys.service.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ApiKeysService as ApiService } from '@vality/swag-api-keys';
|
||||
|
||||
import { createApi } from '../utils';
|
||||
import { PartyIdExtension } from '../utils/extensions';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ApiKeysService extends createApi(ApiService, [PartyIdExtension]) {}
|
2
src/app/api/api-keys/index.ts
Normal file
2
src/app/api/api-keys/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './api-keys.service';
|
||||
export * from './api-keys.module';
|
@ -14,7 +14,7 @@ export class PartyIdExtension implements ApiExtension {
|
||||
selector() {
|
||||
return this.contextOrganizationService.organization$.pipe(
|
||||
first(),
|
||||
map(({ party }) => ({ partyID: party }))
|
||||
map(({ party }) => ({ partyID: party, partyId: party }))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import { QUERY_PARAMS_SERIALIZERS } from '@dsh/app/shared/services/query-params/
|
||||
import { createDateRangeWithPresetSerializer } from '@dsh/components/date-range-filter';
|
||||
import { SpinnerModule } from '@dsh/components/indicators';
|
||||
|
||||
import { ApiKeysModule } from './api/api-keys';
|
||||
import { OrganizationsModule } from './api/organizations';
|
||||
import { AppComponent } from './app.component';
|
||||
import { AuthModule, KeycloakAngularModule, KeycloakService } from './auth';
|
||||
@ -66,6 +67,7 @@ import { ENV, environment } from '../environments';
|
||||
QuestionaryAggrProxyModule,
|
||||
WalletModule,
|
||||
SpinnerModule,
|
||||
ApiKeysModule,
|
||||
],
|
||||
providers: [
|
||||
LanguageService,
|
||||
|
@ -0,0 +1,23 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApiKey } from '@vality/swag-api-keys';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { ExpandedIdManager } from '@dsh/app/shared/services';
|
||||
|
||||
import { FetchApiKeysService } from './fetch-api-keys.service';
|
||||
|
||||
@Injectable()
|
||||
export class ApiKeysExpandedIdManager extends ExpandedIdManager<ApiKey> {
|
||||
constructor(
|
||||
protected route: ActivatedRoute,
|
||||
protected router: Router,
|
||||
private fetchApiKeysService: FetchApiKeysService
|
||||
) {
|
||||
super(route, router);
|
||||
}
|
||||
|
||||
protected get dataSet$(): Observable<ApiKey[]> {
|
||||
return this.fetchApiKeysService.apiKeys$;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
<div
|
||||
fxLayout="column"
|
||||
fxLayoutGap="16px"
|
||||
*transloco="let t; scope: 'payment-section'; read: 'paymentSection.apiKeys.list'"
|
||||
>
|
||||
<dsh-last-updated [lastUpdated]="lastUpdated" (update)="refreshData.emit()"></dsh-last-updated>
|
||||
<dsh-row fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="24px">
|
||||
<dsh-row-header-label fxFlex="50" fxFlex.lt-md="100">{{ t('name') | uppercase }}</dsh-row-header-label>
|
||||
<dsh-row-header-label fxFlex="25" fxHide.lt-md>{{ t('createdAt') | uppercase }}</dsh-row-header-label>
|
||||
<dsh-row-header-label fxFlex="25" fxHide.lt-md>{{ t('status') | uppercase }}</dsh-row-header-label>
|
||||
</dsh-row>
|
||||
<dsh-accordion
|
||||
fxLayout="column"
|
||||
fxLayoutGap="16px"
|
||||
(expandedChange)="expandedIdChange.emit($event)"
|
||||
[expanded]="expandedId"
|
||||
>
|
||||
<dsh-accordion-item *ngFor="let apiKey of apiKeys" #accordionItem>
|
||||
<dsh-row fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="24px">
|
||||
<dsh-row-label class="wrap-url" fxFlex="50" fxFlex.lt-md="100">
|
||||
{{ apiKey.name }}
|
||||
</dsh-row-label>
|
||||
<dsh-row-label fxFlex="25" fxHide.lt-md>{{
|
||||
apiKey.createdAt | date: 'dd MMMM yyyy, HH:mm'
|
||||
}}</dsh-row-label>
|
||||
<dsh-row-label fxFlex="25" fxHide.lt-md>{{ apiKey.status }}</dsh-row-label>
|
||||
</dsh-row>
|
||||
<dsh-accordion-item-content>
|
||||
<dsh-card fxLayout="column" fxLayoutGap="32px">
|
||||
<dsh-accordion-item-content-header (collapse)="accordionItem.collapse($event)">
|
||||
<div fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<div>{{ t('details') }} #{{ apiKey.id }}</div>
|
||||
<div>{{ apiKey.createdAt | date: 'dd MMMM yyyy, HH:mm' }}</div>
|
||||
</div>
|
||||
</dsh-accordion-item-content-header>
|
||||
<div fxLayout="column" fxLayoutGap="24px">
|
||||
<div fxLayout="column" gdColumns="1fr 1fr 1fr" gdColumns.lt-md="1fr" gdGap="24px">
|
||||
<dsh-details-item [title]="t('name')">
|
||||
<div class="dsh-body-1">
|
||||
{{ apiKey.name }}
|
||||
</div>
|
||||
</dsh-details-item>
|
||||
<dsh-details-item [title]="t('status')">
|
||||
<div class="dsh-body-1">{{ apiKey.status }}</div>
|
||||
</dsh-details-item>
|
||||
<dsh-details-item [title]="t('createdAt')">
|
||||
<div class="dsh-body-1">{{ apiKey.createdAt | date: 'dd MMMM yyyy, HH:mm' }}</div>
|
||||
</dsh-details-item>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div fxLayout="column" fxLayoutGap="24px">
|
||||
<div>
|
||||
<button dsh-button color="warn" (click)="delete(apiKey)">
|
||||
{{ t('delete') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</dsh-card>
|
||||
</dsh-accordion-item-content>
|
||||
</dsh-accordion-item>
|
||||
</dsh-accordion>
|
||||
</div>
|
@ -0,0 +1,25 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||
import { DialogService } from '@vality/ng-core';
|
||||
import { ApiKey } from '@vality/swag-api-keys';
|
||||
|
||||
import { ApiKeyDeleteDialogComponent } from './components/api-key-delete-dialog/api-key-delete-dialog.component';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'dsh-api-keys-list',
|
||||
templateUrl: 'api-keys-list.component.html',
|
||||
})
|
||||
export class ApiKeysListComponent {
|
||||
@Input() apiKeys: ApiKey[];
|
||||
@Input() expandedId: number;
|
||||
@Input() lastUpdated: string;
|
||||
@Output() expandedIdChange = new EventEmitter<number>();
|
||||
@Output() refreshData = new EventEmitter<void>();
|
||||
|
||||
constructor(private dialogService: DialogService) {}
|
||||
|
||||
delete(apiKey: ApiKey) {
|
||||
this.dialogService.open(ApiKeyDeleteDialogComponent, { apiKeyId: apiKey.id });
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { TranslocoModule } from '@ngneat/transloco';
|
||||
import { DialogModule } from '@vality/ng-core';
|
||||
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
import { IndicatorsModule } from '@dsh/components/indicators';
|
||||
import { LayoutModule } from '@dsh/components/layout';
|
||||
|
||||
import { ApiKeysListComponent } from './api-keys-list.component';
|
||||
import { ApiKeyDeleteDialogComponent } from './components/api-key-delete-dialog/api-key-delete-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
TranslocoModule,
|
||||
MatSnackBarModule,
|
||||
LayoutModule,
|
||||
FlexLayoutModule,
|
||||
CommonModule,
|
||||
IndicatorsModule,
|
||||
MatDividerModule,
|
||||
ButtonModule,
|
||||
ApiKeyDeleteDialogComponent,
|
||||
DialogModule,
|
||||
],
|
||||
declarations: [ApiKeysListComponent],
|
||||
exports: [ApiKeysListComponent],
|
||||
})
|
||||
export class ApiKeysListModule {}
|
@ -0,0 +1,14 @@
|
||||
<dsh-base-dialog
|
||||
*transloco="let t; scope: 'payment-section'; read: 'paymentSection.apiKeys.deleteDialog'"
|
||||
[title]="t('title')"
|
||||
(cancel)="closeWithCancellation()"
|
||||
>
|
||||
<div class="dsh-body-1">
|
||||
{{ t('desc') }}
|
||||
</div>
|
||||
<ng-container dshBaseDialogActions>
|
||||
<button dsh-button (click)="confirm()" color="warn">
|
||||
{{ t('confirm') }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</dsh-base-dialog>
|
@ -0,0 +1,49 @@
|
||||
import { Component, Injector } from '@angular/core';
|
||||
import { FlexModule } from '@angular/flex-layout';
|
||||
import { TranslocoModule } from '@ngneat/transloco';
|
||||
import { untilDestroyed, UntilDestroy } from '@ngneat/until-destroy';
|
||||
import { DialogSuperclass } from '@vality/ng-core';
|
||||
import { RequestRevokeApiKeyRequestParams } from '@vality/swag-api-keys';
|
||||
|
||||
import { ApiKeysService } from '@dsh/api/api-keys';
|
||||
import { BaseDialogModule } from '@dsh/app/shared/components/dialog/base-dialog';
|
||||
import { ErrorService, NotificationService } from '@dsh/app/shared/services';
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
import { SpinnerModule } from '@dsh/components/indicators';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'dsh-api-key-delete-dialog',
|
||||
standalone: true,
|
||||
templateUrl: './api-key-delete-dialog.component.html',
|
||||
styles: [],
|
||||
imports: [BaseDialogModule, SpinnerModule, FlexModule, ButtonModule, TranslocoModule],
|
||||
})
|
||||
export class ApiKeyDeleteDialogComponent extends DialogSuperclass<
|
||||
ApiKeyDeleteDialogComponent,
|
||||
Pick<RequestRevokeApiKeyRequestParams, 'apiKeyId'>
|
||||
> {
|
||||
constructor(
|
||||
injector: Injector,
|
||||
private apiKeysService: ApiKeysService,
|
||||
private errorService: ErrorService,
|
||||
private notificationService: NotificationService
|
||||
) {
|
||||
super(injector);
|
||||
}
|
||||
|
||||
confirm() {
|
||||
this.apiKeysService
|
||||
.requestRevokeApiKey(this.dialogData)
|
||||
.pipe(untilDestroyed(this))
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.notificationService.success();
|
||||
this.closeWithSuccess();
|
||||
},
|
||||
error: (err) => {
|
||||
this.errorService.error(err);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export * from './webhook-list.module';
|
||||
export * from './webhook-list.component';
|
@ -0,0 +1,21 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { ApiKeysComponent } from './api-keys.component';
|
||||
import { ApiKeyRevokeComponent } from './components/api-key-revoke/api-key-revoke.component';
|
||||
|
||||
const ROUTES: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: ApiKeysComponent,
|
||||
},
|
||||
{
|
||||
path: ':apiKeyId/revoke/:apiKeyRevokeToken',
|
||||
component: ApiKeyRevokeComponent,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(ROUTES)],
|
||||
})
|
||||
export class ApiKeysRoutingModule {}
|
@ -0,0 +1,36 @@
|
||||
<div fxLayout="column" fxLayoutGap="32px" *transloco="let t; scope: 'payment-section'; read: 'paymentSection.apiKeys'">
|
||||
<div
|
||||
fxLayout.lt-md="column"
|
||||
fxLayout="row"
|
||||
fxLayoutAlign.lt-md="center stretch"
|
||||
fxLayoutAlign="space-between"
|
||||
fxLayoutGap="24px"
|
||||
>
|
||||
<div fxLayoutAlign=" center" fxLayoutGap="16px" (click)="toggle()" style="cursor: pointer">
|
||||
<div class="dsh-subheading-1">
|
||||
{{ t('showInactive') }}
|
||||
</div>
|
||||
<mat-slide-toggle [checked]="showInactive"></mat-slide-toggle>
|
||||
</div>
|
||||
<button dsh-button color="accent" (click)="create()">{{ t('create') }}</button>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="(isLoading$ | async) || !(apiKeys$ | async); else keysList">
|
||||
<div fxLayout fxFlexAlign="center">
|
||||
<dsh-spinner></dsh-spinner>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #keysList>
|
||||
<dsh-api-keys-list
|
||||
[apiKeys]="apiKeys$ | async"
|
||||
[expandedId]="expandedId$ | async"
|
||||
(expandedIdChange)="expandedIdChange($event)"
|
||||
[lastUpdated]="lastUpdated$ | async"
|
||||
(refreshData)="update()"
|
||||
></dsh-api-keys-list>
|
||||
<dsh-empty-search-result
|
||||
*ngIf="(apiKeys$ | async)?.length === 0"
|
||||
[text]="t('emptyResult')"
|
||||
></dsh-empty-search-result>
|
||||
</ng-template>
|
||||
</div>
|
@ -0,0 +1,3 @@
|
||||
:host {
|
||||
width: 100%;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { DialogService } from '@vality/ng-core';
|
||||
import { ApiKeyStatus, ListApiKeysRequestParams } from '@vality/swag-api-keys';
|
||||
|
||||
import { ApiKeyCreateDialogComponent } from '@dsh/app/sections/payment-section/integrations/api-keys/components/api-key-create-dialog/api-key-create-dialog.component';
|
||||
|
||||
import { ApiKeysExpandedIdManager } from './api-keys-expanded-id-manager.service';
|
||||
import { FetchApiKeysService } from './fetch-api-keys.service';
|
||||
import { QueryParamsService } from '../../../../shared';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
templateUrl: 'api-keys.component.html',
|
||||
styleUrls: ['api-keys.component.scss'],
|
||||
providers: [ApiKeysExpandedIdManager, FetchApiKeysService],
|
||||
})
|
||||
export class ApiKeysComponent {
|
||||
showInactive = this.qp.params.showInactive;
|
||||
apiKeys$ = this.fetchApiKeysService.apiKeys$;
|
||||
isLoading$ = this.fetchApiKeysService.isLoading$;
|
||||
expandedId$ = this.apiKeysExpandedIdManager.expandedId$;
|
||||
lastUpdated$ = this.fetchApiKeysService.lastUpdated$;
|
||||
|
||||
constructor(
|
||||
private qp: QueryParamsService<{ showInactive: boolean }>,
|
||||
private apiKeysExpandedIdManager: ApiKeysExpandedIdManager,
|
||||
private fetchApiKeysService: FetchApiKeysService,
|
||||
private dialogService: DialogService
|
||||
) {}
|
||||
|
||||
update(params: Omit<ListApiKeysRequestParams, 'partyId'> = {}) {
|
||||
this.fetchApiKeysService.update(Object.assign(params, !this.showInactive && { status: ApiKeyStatus.Active }));
|
||||
}
|
||||
|
||||
create() {
|
||||
this.dialogService
|
||||
.open(ApiKeyCreateDialogComponent)
|
||||
.afterClosed()
|
||||
.pipe(untilDestroyed(this))
|
||||
.subscribe(() => {
|
||||
this.update();
|
||||
});
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.showInactive = !this.showInactive;
|
||||
void this.qp.set({ showInactive: this.showInactive });
|
||||
this.update();
|
||||
}
|
||||
|
||||
expandedIdChange(id: number) {
|
||||
this.apiKeysExpandedIdManager.expandedIdChange(id);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import { ClipboardModule } from '@angular/cdk/clipboard';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexModule } from '@angular/flex-layout';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
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 { CardModule } from '@dsh/components/layout';
|
||||
|
||||
import { ApiKeysListModule } from './api-keys-list/api-keys-list.module';
|
||||
import { ApiKeysRoutingModule } from './api-keys-routing.module';
|
||||
import { ApiKeysComponent } from './api-keys.component';
|
||||
import { ApiKeyCreateDialogComponent } from './components/api-key-create-dialog/api-key-create-dialog.component';
|
||||
import { ApiKeyRevokeComponent } from './components/api-key-revoke/api-key-revoke.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ApiKeysComponent, ApiKeyRevokeComponent],
|
||||
imports: [
|
||||
ApiKeysRoutingModule,
|
||||
FlexModule,
|
||||
TranslocoModule,
|
||||
CardModule,
|
||||
MatInputModule,
|
||||
CommonModule,
|
||||
ButtonModule,
|
||||
ClipboardModule,
|
||||
EmptySearchResultModule,
|
||||
SpinnerModule,
|
||||
MatSlideToggleModule,
|
||||
ApiKeysListModule,
|
||||
ApiKeyCreateDialogComponent,
|
||||
],
|
||||
})
|
||||
export class ApiKeysModule {}
|
@ -0,0 +1,33 @@
|
||||
<dsh-base-dialog
|
||||
*transloco="let t; scope: 'payment-section'; read: 'paymentSection.apiKeys.createDialog'"
|
||||
[title]="t('title')"
|
||||
(cancel)="closeWithCancellation()"
|
||||
>
|
||||
<div *ngIf="apiKey; else releaseApiKey">
|
||||
<mat-form-field style="width: 100%">
|
||||
<mat-label>{{ t('apiKey') }}</mat-label>
|
||||
<textarea matInput [value]="apiKey" readonly rows="5"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<ng-template #releaseApiKey>
|
||||
<div fxLayout="column" fxLayoutGap="24px" [formGroup]="form">
|
||||
<mat-form-field style="width: 100%">
|
||||
<mat-label>{{ t('keyName') }}</mat-label>
|
||||
<input matInput required formControlName="name" />
|
||||
</mat-form-field>
|
||||
<div class="dsh-body-1">
|
||||
{{ t('desc') }}
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-container dshBaseDialogActions>
|
||||
<button *ngIf="apiKey; else confirmActions" dsh-button (click)="copy()" color="primary">
|
||||
{{ t('copy') }}
|
||||
</button>
|
||||
<ng-template #confirmActions>
|
||||
<button dsh-button (click)="confirm()" color="primary" [disabled]="form.invalid">
|
||||
{{ t('confirm') }}
|
||||
</button>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</dsh-base-dialog>
|
@ -0,0 +1,76 @@
|
||||
import { ClipboardModule, Clipboard } from '@angular/cdk/clipboard';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, Injector } from '@angular/core';
|
||||
import { FlexModule } from '@angular/flex-layout';
|
||||
import { ReactiveFormsModule, NonNullableFormBuilder } from '@angular/forms';
|
||||
import { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy-input';
|
||||
import { TranslocoModule, TranslocoService } from '@ngneat/transloco';
|
||||
import { untilDestroyed, UntilDestroy } from '@ngneat/until-destroy';
|
||||
import { DialogSuperclass } from '@vality/ng-core';
|
||||
|
||||
import { ApiKeysService } from '@dsh/api/api-keys';
|
||||
import { BaseDialogModule } from '@dsh/app/shared/components/dialog/base-dialog';
|
||||
import { ErrorService, NotificationService } from '@dsh/app/shared/services';
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
import { SpinnerModule } from '@dsh/components/indicators';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'dsh-api-key-create-dialog',
|
||||
standalone: true,
|
||||
templateUrl: './api-key-create-dialog.component.html',
|
||||
styles: [],
|
||||
imports: [
|
||||
BaseDialogModule,
|
||||
SpinnerModule,
|
||||
FlexModule,
|
||||
ButtonModule,
|
||||
TranslocoModule,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule,
|
||||
CommonModule,
|
||||
ClipboardModule,
|
||||
],
|
||||
})
|
||||
export class ApiKeyCreateDialogComponent extends DialogSuperclass<ApiKeyCreateDialogComponent> {
|
||||
form = this.fb.group({ name: '' });
|
||||
apiKey: string;
|
||||
|
||||
constructor(
|
||||
injector: Injector,
|
||||
private apiKeysService: ApiKeysService,
|
||||
private errorService: ErrorService,
|
||||
private notificationService: NotificationService,
|
||||
private fb: NonNullableFormBuilder,
|
||||
private clipboard: Clipboard,
|
||||
private transloco: TranslocoService
|
||||
) {
|
||||
super(injector);
|
||||
}
|
||||
|
||||
confirm() {
|
||||
this.apiKeysService
|
||||
.issueApiKey()
|
||||
.pipe(untilDestroyed(this))
|
||||
.subscribe({
|
||||
next: (res) => {
|
||||
this.apiKey = res.accessToken;
|
||||
},
|
||||
error: (err) => {
|
||||
this.errorService.error(err);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
copy() {
|
||||
if (this.clipboard.copy(this.apiKey)) {
|
||||
this.notificationService.success(
|
||||
this.transloco.selectTranslate('apiKeys.copy.success', null, 'payment-section')
|
||||
);
|
||||
} else {
|
||||
this.notificationService.error(
|
||||
this.transloco.selectTranslate('apiKeys.copy.error', null, 'payment-section')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
<div fxLayoutAlign="center center"><dsh-spinner></dsh-spinner></div>
|
@ -0,0 +1,42 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { TranslocoService } from '@ngneat/transloco';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
|
||||
import { ApiKeysService } from '@dsh/api/api-keys';
|
||||
import { ErrorService, NotificationService } from '@dsh/app/shared/services';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'dsh-api-key-revoke',
|
||||
templateUrl: './api-key-revoke.component.html',
|
||||
})
|
||||
export class ApiKeyRevokeComponent implements OnInit {
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private apiKeysService: ApiKeysService,
|
||||
private router: Router,
|
||||
private notificationService: NotificationService,
|
||||
private errorService: ErrorService,
|
||||
private translocoService: TranslocoService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
const { apiKeyRevokeToken, apiKeyId } = this.route.snapshot.params as Record<string, string>;
|
||||
this.apiKeysService
|
||||
.revokeApiKey({ apiKeyRevokeToken, apiKeyId })
|
||||
.pipe(untilDestroyed(this))
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.notificationService.success(
|
||||
this.translocoService.selectTranslate('apiKeys.revoke.success', null, 'payment-section')
|
||||
);
|
||||
void this.router.navigate(['../../..'], { relativeTo: this.route });
|
||||
},
|
||||
error: (err) => {
|
||||
this.errorService.error(err);
|
||||
void this.router.navigate(['../../..'], { relativeTo: this.route });
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ListApiKeysRequestParams } from '@vality/swag-api-keys';
|
||||
import { BehaviorSubject, Observable, defer, of } from 'rxjs';
|
||||
import { catchError, map, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { ApiKeysService } from '@dsh/api/api-keys';
|
||||
import { ErrorService } from '@dsh/app/shared/services';
|
||||
import { mapToTimestamp, shareReplayRefCount } from '@dsh/operators';
|
||||
import { inProgressFrom, progressTo } from '@dsh/utils';
|
||||
|
||||
@Injectable()
|
||||
export class FetchApiKeysService {
|
||||
apiKeys$ = defer(() => this.fetchApiKeys$).pipe(
|
||||
switchMap((p) =>
|
||||
this.apiKeysService.listApiKeys(p).pipe(
|
||||
map((r) => r.results),
|
||||
progressTo(() => this.progress$),
|
||||
catchError((err) => {
|
||||
this.errorService.error(err);
|
||||
return of([]);
|
||||
})
|
||||
)
|
||||
),
|
||||
shareReplayRefCount()
|
||||
);
|
||||
isLoading$ = inProgressFrom(() => this.progress$, this.apiKeys$);
|
||||
lastUpdated$: Observable<string> = this.apiKeys$.pipe(mapToTimestamp, shareReplayRefCount());
|
||||
|
||||
private progress$ = new BehaviorSubject(0);
|
||||
private fetchApiKeys$ = new BehaviorSubject<Omit<ListApiKeysRequestParams, 'partyId'>>({});
|
||||
|
||||
constructor(private apiKeysService: ApiKeysService, private errorService: ErrorService) {}
|
||||
|
||||
update(params: Omit<ListApiKeysRequestParams, 'partyId'> = {}) {
|
||||
this.fetchApiKeys$.next(params);
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export * from './api-keys.module';
|
@ -4,6 +4,7 @@ import { RouterModule, Routes } from '@angular/router';
|
||||
import { createPrivateRoute, RoleAccessName } from '@dsh/app/auth';
|
||||
|
||||
import { IntegrationsComponent } from './integrations.component';
|
||||
import { environment } from '../../../../environments';
|
||||
|
||||
const ROUTES: Routes = [
|
||||
{
|
||||
@ -26,8 +27,10 @@ const ROUTES: Routes = [
|
||||
),
|
||||
createPrivateRoute(
|
||||
{
|
||||
path: 'api-key',
|
||||
loadChildren: () => import('./api-key').then((m) => m.ApiKeyModule),
|
||||
path: 'api-keys',
|
||||
loadChildren: environment.stage
|
||||
? () => import('./api-keys').then((m) => m.ApiKeysModule)
|
||||
: () => import('./api-key').then((m) => m.ApiKeyModule),
|
||||
},
|
||||
[RoleAccessName.ApiKeys]
|
||||
),
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { TranslocoService } from '@ngneat/transloco';
|
||||
|
||||
import { environment } from '../../../../environments';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'integrations.component.html',
|
||||
})
|
||||
@ -11,8 +13,10 @@ export class IntegrationsComponent {
|
||||
label$: this.transloco.selectTranslate('integrations.tabs.payment-link', null, 'payment-section'),
|
||||
},
|
||||
{
|
||||
path: 'api-key',
|
||||
label$: this.transloco.selectTranslate('integrations.tabs.api-key', null, 'payment-section'),
|
||||
path: 'api-keys',
|
||||
label$: environment.stage
|
||||
? this.transloco.selectTranslate('integrations.tabs.api-keys', null, 'payment-section')
|
||||
: this.transloco.selectTranslate('integrations.tabs.api-key', null, 'payment-section'),
|
||||
},
|
||||
{
|
||||
path: 'webhooks',
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
LegacySimpleSnackBar as SimpleSnackBar,
|
||||
} from '@angular/material/legacy-snack-bar';
|
||||
import { TranslocoService } from '@ngneat/transloco';
|
||||
import { Observable, first, isObservable, timeout } from 'rxjs';
|
||||
|
||||
const DEFAULT_DURATION_MS = 3000;
|
||||
|
||||
@ -14,21 +15,32 @@ export class NotificationService {
|
||||
constructor(private snackBar: MatSnackBar, private transloco: TranslocoService) {}
|
||||
|
||||
success(
|
||||
message: string = this.transloco.translate('notification.success', null, 'services')
|
||||
message: string | Observable<string> = this.transloco.translate('notification.success', null, 'services')
|
||||
): MatSnackBarRef<SimpleSnackBar> {
|
||||
return this.openSnackBar(message);
|
||||
}
|
||||
|
||||
error(
|
||||
message: string = this.transloco.translate('notification.error', null, 'services')
|
||||
message: string | Observable<string> = this.transloco.translate('notification.error', null, 'services')
|
||||
): MatSnackBarRef<SimpleSnackBar> {
|
||||
return this.openSnackBar(message);
|
||||
}
|
||||
|
||||
private openSnackBar(message: string, config: MatSnackBarConfig<unknown> = {}): MatSnackBarRef<SimpleSnackBar> {
|
||||
return this.snackBar.open(message, this.transloco.translate('notification.ok', null, 'services'), {
|
||||
private openSnackBar(
|
||||
message: string | Observable<string>,
|
||||
config: MatSnackBarConfig<unknown> = {}
|
||||
): MatSnackBarRef<SimpleSnackBar> {
|
||||
const okMessage = this.transloco.translate('notification.ok', null, 'services');
|
||||
const resConfig = {
|
||||
duration: DEFAULT_DURATION_MS,
|
||||
...config,
|
||||
});
|
||||
};
|
||||
if (isObservable(message)) {
|
||||
message.pipe(first(), timeout(5000)).subscribe((m) => {
|
||||
this.snackBar.open(m, okMessage, resConfig);
|
||||
});
|
||||
return;
|
||||
}
|
||||
return this.snackBar.open(message, okMessage, resConfig);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,38 @@
|
||||
"description": "Your private key to access the",
|
||||
"title": "API key"
|
||||
},
|
||||
"apiKeys": {
|
||||
"copy": {
|
||||
"error": "An error occurred while copying",
|
||||
"success": "Successfully copied"
|
||||
},
|
||||
"create": "Release key",
|
||||
"createDialog": {
|
||||
"apiKey": "Private API key",
|
||||
"confirm": "Release key",
|
||||
"copy": "Copy",
|
||||
"desc": "Private API key will be available only once, at the moment of its issuance. Therefore, it is necessary to copy the key and save it in a secure location after receiving it.",
|
||||
"keyName": "Key name",
|
||||
"title": "Release a new key"
|
||||
},
|
||||
"deleteDialog": {
|
||||
"confirm": "Request key revocation",
|
||||
"desc": "You will receive an email with a confirmation link. Please follow the link to confirm the operation. After revoking the key, it will no longer be valid, and if you need access to the API in the future, you will have to create a new one.",
|
||||
"title": "Key revocation request"
|
||||
},
|
||||
"emptyResult": "No keys released",
|
||||
"list": {
|
||||
"createdAt": "Created at",
|
||||
"delete": "Revoke key",
|
||||
"details": "Key data",
|
||||
"name": "Name",
|
||||
"status": "Status"
|
||||
},
|
||||
"revoke": {
|
||||
"success": "Key successfully revoked"
|
||||
},
|
||||
"showInactive": "Show inactive"
|
||||
},
|
||||
"balances": {
|
||||
"title": "Available amount"
|
||||
},
|
||||
@ -76,6 +108,7 @@
|
||||
"integrations": {
|
||||
"tabs": {
|
||||
"api-key": "API KEY",
|
||||
"api-keys": "API KEYS",
|
||||
"payment-link": "PAYMENT LINK",
|
||||
"webhooks": "WEBHOOKS"
|
||||
}
|
||||
@ -146,8 +179,8 @@
|
||||
"shops": "Shops"
|
||||
},
|
||||
"table": {
|
||||
"id": "ID",
|
||||
"amount": "Amount",
|
||||
"id": "ID",
|
||||
"shop": "Shop",
|
||||
"status": "Status",
|
||||
"statusChanged": "Last modified"
|
||||
|
@ -18,6 +18,38 @@
|
||||
"description": "Ваш приватный ключ для доступа к",
|
||||
"title": "API ключ"
|
||||
},
|
||||
"apiKeys": {
|
||||
"copy": {
|
||||
"error": "Ошибка копирования",
|
||||
"success": "Успешно скопировано"
|
||||
},
|
||||
"create": "Выпустить ключ",
|
||||
"createDialog": {
|
||||
"apiKey": "Приватный API ключ",
|
||||
"confirm": "Выпустить ключ",
|
||||
"copy": "Скопировать",
|
||||
"desc": "Приватный API ключ будет доступен только один раз, в момент его выпуска. Поэтому необходимо скопировать ключ и сохранить его в надежном месте после получения.",
|
||||
"keyName": "Название ключа",
|
||||
"title": "Выпустить новый ключ"
|
||||
},
|
||||
"deleteDialog": {
|
||||
"confirm": "Запросить отзыв ключа",
|
||||
"desc": "На вашу электронную почту будет отправлено письмо со ссылкой. Пожалуйста, перейдите по этой ссылке, чтобы подтвердить операцию. После отзыва ключ больше не будет действительным, и если вам потребуется доступ к API в будущем, вам придется создать новый ключ.",
|
||||
"title": "Запрос на отзыв ключа"
|
||||
},
|
||||
"emptyResult": "Не выпущено ни одного ключа",
|
||||
"list": {
|
||||
"createdAt": "Создан",
|
||||
"delete": "Удалить",
|
||||
"details": "Данные ключа",
|
||||
"name": "Название",
|
||||
"status": "Статус"
|
||||
},
|
||||
"revoke": {
|
||||
"success": "Ключ успешно отозван"
|
||||
},
|
||||
"showInactive": "Показывать неактивные"
|
||||
},
|
||||
"balances": {
|
||||
"title": "Доступные средства"
|
||||
},
|
||||
@ -76,6 +108,7 @@
|
||||
"integrations": {
|
||||
"tabs": {
|
||||
"api-key": "API КЛЮЧ",
|
||||
"api-keys": "API КЛЮЧИ",
|
||||
"payment-link": "ПЛАТЕЖНАЯ ССЫЛКА",
|
||||
"webhooks": "WEBHOOKS"
|
||||
}
|
||||
@ -146,8 +179,8 @@
|
||||
"shops": "Магазины"
|
||||
},
|
||||
"table": {
|
||||
"id": "ID",
|
||||
"amount": "Сумма списания",
|
||||
"id": "ID",
|
||||
"shop": "Магазин",
|
||||
"status": "Статус",
|
||||
"statusChanged": "Статус изменен"
|
||||
|
9
src/environments/default-environment.ts
Normal file
9
src/environments/default-environment.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Environment } from './types/environment';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
export const DEFAULT_ENVIRONMENT: Environment = {
|
||||
production: false,
|
||||
stage: window.location.host.split('.')[0] === 'stage',
|
||||
appConfigPath: '/appConfig.json',
|
||||
authConfigPath: '/authConfig.json',
|
||||
};
|
@ -1,8 +0,0 @@
|
||||
import { environment as prodEnvironment } from './environment.prod';
|
||||
import { Environment } from './types/environment';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
export const environment: Environment = {
|
||||
...prodEnvironment,
|
||||
production: false,
|
||||
};
|
@ -1,8 +1,8 @@
|
||||
import { DEFAULT_ENVIRONMENT } from './default-environment';
|
||||
import { Environment } from './types/environment';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
export const environment: Environment = {
|
||||
...DEFAULT_ENVIRONMENT,
|
||||
production: true,
|
||||
appConfigPath: '/appConfig.json',
|
||||
authConfigPath: '/authConfig.json',
|
||||
};
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { environment as devEnvironment } from './environment.dev';
|
||||
import { DEFAULT_ENVIRONMENT } from './default-environment';
|
||||
import { Environment } from './types/environment';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
export const environment: Environment = {
|
||||
...devEnvironment,
|
||||
...DEFAULT_ENVIRONMENT,
|
||||
appConfigPath: '/appConfig.stage.json',
|
||||
authConfigPath: '/authConfig.stage.json',
|
||||
stage: true,
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { environment as devEnvironment } from './environment.dev';
|
||||
import { DEFAULT_ENVIRONMENT } from './default-environment';
|
||||
import { Environment } from './types/environment';
|
||||
|
||||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
@ -6,7 +6,7 @@ import { Environment } from './types/environment';
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
export const environment: Environment = devEnvironment;
|
||||
export const environment: Environment = DEFAULT_ENVIRONMENT;
|
||||
|
||||
/*
|
||||
* For easier debugging in development mode, you can import the following file
|
||||
|
@ -2,4 +2,5 @@ export interface Environment {
|
||||
production: boolean;
|
||||
appConfigPath: string;
|
||||
authConfigPath: string;
|
||||
stage: boolean;
|
||||
}
|
||||
|
@ -6,7 +6,8 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.mat-dialog-container {
|
||||
.mat-dialog-container,
|
||||
.mat-mdc-dialog-surface {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
@mixin mat-dialog-override() {
|
||||
.mat-dialog-container {
|
||||
.mat-dialog-container,
|
||||
.mat-mdc-dialog-surface {
|
||||
box-shadow: none !important;
|
||||
padding: 24px !important;
|
||||
border-radius: 8px !important;
|
||||
|
Loading…
Reference in New Issue
Block a user