Fix new claim modification form (#88)

This commit is contained in:
Rinat Arsaev 2022-05-24 15:01:51 +03:00 committed by GitHub
parent 174ae290b8
commit b83eeced4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 169 additions and 97 deletions

View File

@ -13,38 +13,43 @@ const baseTsRules = {
],
rules: {
...rules.createImportOrderRule({ internalPathsPattern: '@cc/**' }),
'@typescript-eslint/unbound-method': ['error', { ignoreStatic: true }],
...rules.createAngularSelectorRules({ prefix: 'cc' }),
},
};
// TODO: pretenders for error
const lenientTsRules = {
'@typescript-eslint/no-unsafe-call': 'warn',
'@typescript-eslint/no-unsafe-member-access': 'warn',
'@typescript-eslint/no-unsafe-assignment': 'warn',
'@typescript-eslint/no-unsafe-return': 'warn',
'@typescript-eslint/no-misused-promises': 'warn',
'@typescript-eslint/unbound-method': 'warn',
'@typescript-eslint/restrict-plus-operands': 'warn',
'@typescript-eslint/restrict-template-expressions': 'warn',
'@typescript-eslint/no-unsafe-argument': 'warn',
},
};
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
ignorePatterns: ['**/gen-*/**/*'],
overrides: [
{
...baseTsRules,
files: ['*.ts'],
rules: {
...baseTsRules.rules,
...rules.createAngularSelectorRules({ prefix: 'cc' }),
// TODO: pretenders for error
'@typescript-eslint/no-floating-promises': 'warn',
...lenientTsRules,
},
},
{
...baseTsRules,
// TODO: add fixed directories
files: ['**/src/app/core/**/*.ts'],
},
{
...baseTsRules,
files: ['*.spec.ts'],
extends: [...baseTsRules.extends, './tools/eslint-config/jasmine'],
rules: lenientTsRules,
},
{
files: ['*.html'],

1
.npmrc Normal file
View File

@ -0,0 +1 @@
save-exact=true

View File

@ -7,7 +7,7 @@
"start": "ng serve --proxy-config proxy.conf.js --port 4200",
"build": "ng build --extra-webpack-config webpack.extra.js",
"test": "ng test",
"lint-cmd": "eslint \"src/**/*.{ts,js,html}\" --max-warnings 1387",
"lint-cmd": "eslint \"src/**/*.{ts,js,html}\" --max-warnings 1273",
"lint": "npm run lint-cmd -- --cache",
"lint-fix": "npm run lint -- --fix",
"lint-errors": "npm run lint -- --quiet",

View File

@ -1,8 +1,6 @@
import { ThriftService } from '@cc/app/api/utils/thrift-connector/utils';
export interface ThriftApiOptions {
name: string;
service: ThriftService;
service: object;
serviceName: string;
path: string;
hostname?: string;

View File

@ -1,2 +1 @@
export * from './to-connect-options';
export * from './types';

View File

@ -1,5 +0,0 @@
import { Observable } from 'rxjs';
export type ThriftService = any;
export type ThriftServiceConnection = any;
export type ThriftServiceMethod<T> = (...args) => Observable<T>;

View File

@ -27,14 +27,14 @@ export class AppComponent implements OnInit {
) {}
ngOnInit() {
this.keycloakService.loadUserProfile().then(() => {
void this.keycloakService.loadUserProfile().then(() => {
this.username = this.keycloakService.getUsername();
this.menuItems = this.getMenuItems();
});
}
logout() {
this.keycloakService.logout();
void this.keycloakService.logout();
}
private getMenuItems() {

View File

@ -85,7 +85,7 @@ export class MetaEnricher {
case MetaType.enum:
return meta;
}
this.registerError(`Unsupported enrichment MetaType: ${meta.type}`);
this.registerError(`Unsupported enrichment MetaType: ${String(meta.type)}`);
}
private enrichTypedef({ meta }: MetaTypedef): MetaTypedef {

View File

@ -14,10 +14,10 @@ export class DetailsContainerService {
}
open() {
this.detailsContainer.open();
void this.detailsContainer.open();
}
close() {
this.detailsContainer.close();
void this.detailsContainer.close();
}
}

View File

@ -41,7 +41,7 @@ export class DomainInfoComponent implements OnInit {
}
editDomainObj() {
this.router.navigate(['domain', JSON.stringify(this.detailedObjRef)]);
void this.router.navigate(['domain', JSON.stringify(this.detailedObjRef)]);
}
private initialize() {
@ -54,7 +54,7 @@ export class DomainInfoComponent implements OnInit {
(err) => {
this.isLoading = false;
this.snackBar
.open(`An error occurred while initializing: ${err}`, 'RETRY')
.open(`An error occurred while initializing: ${String(err)}`, 'RETRY')
.onAction()
.subscribe(() => this.initialize());
}

View File

@ -57,7 +57,7 @@ export class DomainObjModificationComponent implements OnInit, OnDestroy {
reviewChanges() {
this.domainReviewService.addReviewModel(this.model);
this.router.navigate(['domain', JSON.stringify(this.model.ref), 'review']);
void this.router.navigate(['domain', JSON.stringify(this.model.ref), 'review']);
}
resetChanges() {
@ -91,7 +91,7 @@ export class DomainObjModificationComponent implements OnInit, OnDestroy {
(err) => {
console.error(err);
this.isLoading = false;
this.snackBar.open(`An error occurred while initializing: ${err}`, 'OK');
this.snackBar.open(`An error occurred while initializing: ${String(err)}`, 'OK');
}
);
}

View File

@ -50,7 +50,7 @@ export class DomainObjReviewComponent implements OnInit, OnDestroy {
}
back() {
this.router.navigate(['domain', this.ref]);
void this.router.navigate(['domain', this.ref]);
}
commit() {
@ -61,12 +61,12 @@ export class DomainObjReviewComponent implements OnInit, OnDestroy {
this.snackBar.open('Commit successful', 'OK', {
duration: 2000,
});
this.router.navigate(['domain', this.ref]);
void this.router.navigate(['domain', this.ref]);
},
(ex) => {
this.isLoading = false;
console.error(ex);
this.snackBar.open(`An error occured while commit: ${ex}`, 'OK');
this.snackBar.open(`An error occured while commit: ${String(ex)}`, 'OK');
}
);
}

View File

@ -6,7 +6,7 @@ import { TargetType } from './targe-type';
name: 'ccPartyItemName',
})
export class PartyItemNamePipe implements PipeTransform {
transform(value: TargetType, ...args: any[]): any {
transform(value: TargetType, ...args: string[]): any {
switch (value) {
case TargetType.partyItem: {
const name = args.length > 0 ? args[0] : 'item';

View File

@ -19,10 +19,11 @@ export function createDomainObjectMetadataFormExtension(
options: objects
.sort((a, b) => a.ref.id - b.ref.id)
.map((o) => ({
label: `#${o.ref.id}` + (o.data.name ? ` ${o.data.name}` : ''),
label: o.data.name,
value: o.ref.id,
details: o,
})),
isIdentifier: true,
}))
),
};

View File

@ -4,22 +4,31 @@ import uniqBy from 'lodash-es/uniqBy';
import { of } from 'rxjs';
import uuid from 'uuid';
import { isTypeWithAliases, MetadataFormExtension } from '@cc/app/shared';
import {
isTypeWithAliases,
MetadataFormExtension,
MetadataFormExtensionOption,
} from '@cc/app/shared';
function createPartyOptions(values: IterableIterator<{ id: string }>) {
function createPartyOptions(
values: IterableIterator<{ id: string }>
): MetadataFormExtensionOption[] {
return Array.from(values).map((value) => ({
label: `${value.id} (from party)`,
label: 'from party',
details: value,
value: value.id,
}));
}
function createClaimOptions(modificationUnits: { id: string; modification: unknown }[]) {
function createClaimOptions(
modificationUnits: { id: string; modification: unknown }[]
): MetadataFormExtensionOption[] {
return uniqBy(
modificationUnits.filter(Boolean).map((unit) => ({
label: `${unit.id} (from claim)`,
details: unit.modification,
label: 'from claim',
details: unit.modification as object,
value: unit.id,
color: 'primary',
})),
'value'
);
@ -39,15 +48,16 @@ export function createPartyClaimMetadataFormExtensions(
extension: () =>
of({
options: [
...createPartyOptions(party.contractors.values()),
...createClaimOptions(
claim.changeset.map(
(unit) =>
unit.modification.party_modification?.contractor_modification
)
),
...createPartyOptions(party.contractors.values()),
],
generate,
isIdentifier: true,
}),
},
{
@ -55,15 +65,16 @@ export function createPartyClaimMetadataFormExtensions(
extension: () =>
of({
options: [
...createPartyOptions(party.contracts.values()),
...createClaimOptions(
claim.changeset.map(
(unit) =>
unit.modification.party_modification?.contract_modification
)
),
...createPartyOptions(party.contracts.values()),
],
generate,
isIdentifier: true,
}),
},
{
@ -71,14 +82,15 @@ export function createPartyClaimMetadataFormExtensions(
extension: () =>
of({
options: [
...createPartyOptions(party.shops.values()),
...createClaimOptions(
claim.changeset.map(
(unit) => unit.modification.party_modification?.shop_modification
)
),
...createPartyOptions(party.shops.values()),
],
generate,
isIdentifier: true,
}),
},
{
@ -86,19 +98,20 @@ export function createPartyClaimMetadataFormExtensions(
extension: () =>
of({
options: [
...createPartyOptions(party.wallets.values()),
...createClaimOptions(
claim.changeset.map(
(unit) => unit.modification.party_modification?.wallet_modification
)
),
...createPartyOptions(party.wallets.values()),
],
generate,
isIdentifier: true,
}),
},
{
determinant: (data) => of(isTypeWithAliases(data, 'ID', 'base')),
extension: () => of({ generate }),
extension: () => of({ generate, isIdentifier: true }),
},
];
}

View File

@ -35,7 +35,7 @@ export class DepositsComponent implements OnInit {
ngOnInit() {
this.fetchDepositsService.errors$.subscribe((e) =>
this.snackBar.open(`An error occurred while search deposits (${e})`, 'OK')
this.snackBar.open(`An error occurred while search deposits (${String(e)})`, 'OK')
);
}
@ -68,7 +68,7 @@ export class DepositsComponent implements OnInit {
depositMenuItemSelected(depositMenuItemEvent: DepositMenuItemEvent) {
switch (depositMenuItemEvent.action) {
case DepositActions.NavigateToDeposit:
this.router.navigate([`operations/deposit/${depositMenuItemEvent.depositID}`]);
void this.router.navigate([`operations/deposit/${depositMenuItemEvent.depositID}`]);
break;
}
}

View File

@ -32,7 +32,7 @@ export class CreateClaimService {
private snackBar: MatSnackBar
) {
this.claim$.subscribe(({ id, party_id }) => {
this.router.navigate([`party/${party_id}/claim/${id}`]);
void this.router.navigate([`party/${party_id}/claim/${id}`]);
});
}

View File

@ -29,7 +29,7 @@ export class PartyClaimsComponent implements OnInit {
ngOnInit() {
this.partyClaimsService.errors$.subscribe((e) =>
this.snackBar.open(`An error occurred while search claim (${e})`, 'OK')
this.snackBar.open(`An error occurred while search claim (${String(e)})`, 'OK')
);
}

View File

@ -42,7 +42,7 @@ export class PartyPaymentsComponent {
const { partyID, invoiceID, paymentID } = $event;
switch ($event.action) {
case PaymentActions.NavigateToPayment:
this.router.navigate([
void this.router.navigate([
`/party/${partyID}/invoice/${invoiceID}/payment/${paymentID}`,
]);
}

View File

@ -33,8 +33,8 @@ export class ShopsTableComponent implements OnChanges {
}
navigateToShop(shopID: string) {
this.route.params.pipe(pluck('partyID')).subscribe((partyID) => {
this.router.navigate([`/party/${partyID}/shop/${shopID}`]);
this.route.params.pipe(pluck('partyID')).subscribe((partyID: string) => {
void this.router.navigate([`/party/${partyID}/shop/${shopID}`]);
});
}
}

View File

@ -73,7 +73,7 @@ export class PaymentAdjustmentComponent implements OnInit {
this.isLoading = false;
},
(e) => {
this.snackBar.open(`${e.message || 'Error'}`, 'OK');
this.snackBar.open(`${String(e.message || 'Error')}`, 'OK');
this.isLoading = false;
console.error(e);
}

View File

@ -25,7 +25,7 @@ export class PaymentRefundsComponent implements OnInit {
invoiceID: this.invoiceID,
});
this.fetchRefundsService.errors$.subscribe((e) =>
this.snackBar.open(`An error occurred while search refunds (${e})`, 'OK')
this.snackBar.open(`An error occurred while search refunds (${String(e)})`, 'OK')
);
}

View File

@ -16,6 +16,6 @@ export class PaymentRoutingRulesetHeaderComponent {
constructor(private router: Router) {}
navigateBack() {
this.router.navigate([this.backTo]);
void this.router.navigate([this.backTo]);
}
}

View File

@ -18,7 +18,7 @@ export class SearchClaimsComponent implements OnInit {
ngOnInit(): void {
this.searchClaimService.errors$.subscribe((e) =>
this.snackBar.open(`An error occurred while search claims (${e})`, 'OK')
this.snackBar.open(`An error occurred while search claims (${String(e)})`, 'OK')
);
}

View File

@ -24,14 +24,14 @@ export class SearchTableComponent {
constructor(private router: Router) {}
navigateToPartyClaims(partyId: string) {
this.router.navigate([`/party/${partyId}/claims`]);
void this.router.navigate([`/party/${partyId}/claims`]);
}
navigateToClaim(partyId: string, claimID: number) {
this.router.navigate([`/party/${partyId}/claim/${claimID}`]);
void this.router.navigate([`/party/${partyId}/claim/${claimID}`]);
}
navigateToNewClaim(partyId: string, claimID: number) {
this.router.navigate([`/party/${partyId}/claim/${claimID}/new`]);
void this.router.navigate([`/party/${partyId}/claim/${claimID}/new`]);
}
}

View File

@ -32,7 +32,7 @@ export class SearchPartiesComponent {
partyMenuItemSelected(event: PartyMenuItemEvent) {
switch (event.action) {
case PartyActions.NavigateToParty:
this.router.navigate([`/party/${event.partyID}`]);
void this.router.navigate([`/party/${event.partyID}`]);
}
}
}

View File

@ -33,7 +33,7 @@ export class SearchPaymentsComponent {
const { partyID, invoiceID, paymentID } = $event;
switch ($event.action) {
case PaymentActions.NavigateToPayment:
this.router.navigate([
void this.router.navigate([
`/party/${partyID}/invoice/${invoiceID}/payment/${paymentID}`,
]);
}

View File

@ -0,0 +1,3 @@
<mat-chip-list *ngIf="label">
<mat-chip [color]="color" [selected]="!!color">{{ label }}</mat-chip>
</mat-chip-list>

View File

@ -0,0 +1,3 @@
:host {
pointer-events: none;
}

View File

@ -0,0 +1,12 @@
import { Component, Input } from '@angular/core';
import { ThemePalette } from '@angular/material/core';
@Component({
selector: 'cc-label',
templateUrl: './label.component.html',
styleUrls: ['./label.component.scss'],
})
export class LabelComponent {
@Input() label: string;
@Input() color?: ThemePalette;
}

View File

@ -11,6 +11,7 @@
[formControl]="control"
[required]="data.isRequired"
[type]="inputType"
[ngClass]="{ 'cc-code': (data.extensionResult$ | async)?.isIdentifier }"
/>
<div matSuffix fxLayoutGap="4px">
<button mat-icon-button *ngIf="control.value" (click)="clear($event)">
@ -31,10 +32,15 @@
<mat-autocomplete #auto="matAutocomplete">
<ng-container *ngIf="data.extensionResult$ | async as extensionResult">
<mat-option
*ngFor="let option of extensionResult.options"
*ngFor="let option of filteredOptions$ | async"
[value]="option.value"
>
{{ option.label || option.value }}
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign=" center">
<div [ngClass]="{ 'cc-code': extensionResult.isIdentifier }">
{{ option.value }}
</div>
<cc-label [label]="option.label" [color]="option.color"></cc-label>
</div>
</mat-option>
</ng-container>
</mat-autocomplete>
@ -46,7 +52,10 @@
<ng-container *ngIf="selected$ | async as selected">
<mat-expansion-panel *ngIf="selected.details">
<mat-expansion-panel-header>
<mat-panel-title>{{ selected.label }}</mat-panel-title>
<mat-panel-title fxLayout="row" fxLayoutGap="8px" fxLayoutAlign=" center">
<div>Details</div>
<cc-label [label]="selected.label" [color]="selected.color"></cc-label>
</mat-panel-title>
</mat-expansion-panel-header>
<cc-json-viewer [json]="selected.details"></cc-json-viewer>
</mat-expansion-panel>

View File

@ -1,4 +0,0 @@
::ng-deep .mat-tooltip {
font-family: monospace !important;
white-space: pre-line !important;
}

View File

@ -4,7 +4,7 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { WrappedFormControlSuperclass } from '@s-libs/ng-core';
import { ThriftType } from '@vality/thrift-ts';
import { combineLatest, defer, ReplaySubject, switchMap } from 'rxjs';
import { map, pluck, shareReplay } from 'rxjs/operators';
import { map, pluck, shareReplay, startWith } from 'rxjs/operators';
import { ComponentChanges, getAliases, getValueTypeTitle } from '@cc/app/shared';
import { createValidatedAbstractControlProviders } from '@cc/utils';
@ -15,7 +15,6 @@ import { MetadataFormData } from '../../types/metadata-form-data';
@Component({
selector: 'cc-primitive-field',
templateUrl: './primitive-field.component.html',
styleUrls: ['primitive-field.component.scss'],
providers: createValidatedAbstractControlProviders(PrimitiveFieldComponent),
})
export class PrimitiveFieldComponent
@ -32,6 +31,20 @@ export class PrimitiveFieldComponent
selected$ = combineLatest([this.extensionResult$, this.control.valueChanges]).pipe(
map(([extensionResult, value]) => extensionResult?.options?.find((o) => o.value === value))
);
filteredOptions$ = combineLatest([
this.control.valueChanges.pipe(startWith('')),
this.extensionResult$,
]).pipe(
map(([value, extensionResult]) => {
const filterValue = String(value ?? '').toLowerCase();
return extensionResult.options.filter(
(option) =>
String(option.value).toLowerCase().includes(filterValue) ||
option.label.toLowerCase().includes(filterValue)
);
}),
shareReplay({ refCount: true, bufferSize: 1 })
);
get inputType(): string {
switch (this.data.type) {

View File

@ -1,2 +1,5 @@
export * from './metadata-form.module';
export * from './types/metadata-form-data';
export { MetadataFormExtension } from '@cc/app/shared/components/metadata-form/types/metadata-form-extension';
export { MetadataFormExtensionResult } from '@cc/app/shared/components/metadata-form/types/metadata-form-extension';
export { MetadataFormExtensionOption } from '@cc/app/shared/components/metadata-form/types/metadata-form-extension';

View File

@ -4,9 +4,10 @@ import { WrappedFormControlSuperclass } from '@s-libs/ng-core';
import { Field, ValueType } from '@vality/thrift-ts';
import { ThriftAstMetadata } from '@cc/app/api/utils';
import { MetadataFormExtension } from '@cc/app/shared/components/metadata-form/types/metadata-form-extension';
import { createValidatedAbstractControlProviders } from '@cc/utils';
import { MetadataFormData, MetadataFormExtension } from './types/metadata-form-data';
import { MetadataFormData } from './types/metadata-form-data';
@Component({
selector: 'cc-metadata-form',

View File

@ -7,6 +7,7 @@ import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipsModule } from '@angular/material/chips';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
@ -19,6 +20,7 @@ import { JsonViewerModule } from '@cc/app/shared/components/json-viewer';
import { ComplexFormComponent } from './components/complex-form/complex-form.component';
import { EnumFieldComponent } from './components/enum-field/enum-field.component';
import { FieldLabelComponent } from './components/field-label/field-label.component';
import { LabelComponent } from './components/label/label.component';
import { PrimitiveFieldComponent } from './components/primitive-field/primitive-field.component';
import { StructFormComponent } from './components/struct-form/struct-form.component';
import { TypedefFormComponent } from './components/typedef-form/typedef-form.component';
@ -44,6 +46,7 @@ import { MetadataFormComponent } from './metadata-form.component';
FlexModule,
ValueTypeTitleModule,
MatCheckboxModule,
MatChipsModule,
],
declarations: [
MetadataFormComponent,
@ -54,6 +57,7 @@ import { MetadataFormComponent } from './metadata-form.component';
TypedefFormComponent,
EnumFieldComponent,
FieldLabelComponent,
LabelComponent,
],
exports: [MetadataFormComponent],
})

View File

@ -1,6 +1,6 @@
import { Field, ValueType } from '@vality/thrift-ts';
import { JsonAST } from '@vality/thrift-ts/src/thrift-parser';
import { Observable, combineLatest, switchMap } from 'rxjs';
import { combineLatest, Observable, switchMap } from 'rxjs';
import { map, pluck, shareReplay } from 'rxjs/operators';
import {
@ -12,19 +12,7 @@ import {
ThriftAstMetadata,
} from '@cc/app/api/utils';
export interface MetadataFormExtensionResult {
options?: {
value: unknown;
label?: string;
details?: string | object;
}[];
generate?: () => Observable<unknown>;
}
export type MetadataFormExtension = {
determinant: (data: MetadataFormData) => Observable<boolean>;
extension: (data: MetadataFormData) => Observable<MetadataFormExtensionResult>;
};
import { MetadataFormExtension, MetadataFormExtensionResult } from './metadata-form-extension';
export enum TypeGroup {
Complex = 'complex',

View File

@ -0,0 +1,22 @@
import { ThemePalette } from '@angular/material/core';
import { Observable } from 'rxjs';
import { MetadataFormData } from '@cc/app/shared';
export interface MetadataFormExtensionOption {
value: unknown;
label?: string;
details?: string | object;
color?: ThemePalette;
}
export interface MetadataFormExtensionResult {
options?: MetadataFormExtensionOption[];
generate?: () => Observable<unknown>;
isIdentifier?: boolean;
}
export type MetadataFormExtension = {
determinant: (data: MetadataFormData) => Observable<boolean>;
extension: (data: MetadataFormData) => Observable<MetadataFormExtensionResult>;
};

View File

@ -6,7 +6,7 @@ import { TargetType } from './targe-type';
name: 'ccPartyItemName',
})
export class PartyItemNamePipe implements PipeTransform {
transform(value: TargetType, ...args: any[]): any {
transform(value: TargetType, ...args: string[]): any {
switch (value) {
case TargetType.PartyItem: {
const name = args.length > 0 ? args[0] : 'item';

View File

@ -99,7 +99,7 @@ export class PaymentsSearcherComponent implements OnInit {
ngOnInit() {
this.fetchPaymentsService.errors$.subscribe((e) =>
this.snackBar.open(`An error occurred while search payments (${e})`, 'OK')
this.snackBar.open(`An error occurred while search payments (${String(e)})`, 'OK')
);
}

View File

@ -19,7 +19,7 @@ function format(
decimalDelimiter: string
) {
const exp =
'\\d(?=(\\d{' + (wholeLength || 3) + '})+' + (decimalLength > 0 ? '\\D' : '$') + ')';
'\\d(?=(\\d{' + String(wholeLength || 3) + '})+' + (decimalLength > 0 ? '\\D' : '$') + ')';
// eslint-disable-next-line no-bitwise
const num = value.toFixed(Math.max(0, ~~decimalLength));
return (decimalDelimiter ? num.replace('.', decimalDelimiter) : num).replace(

View File

@ -29,11 +29,11 @@ export class AppAuthGuardService extends KeycloakAuthGuard {
const result = await this.isAccessAllowed(route);
if (!result) {
this.router.navigate(['404']);
void this.router.navigate(['404']);
}
resolve(result);
} catch (error) {
reject('An error happened during access validation. Details:' + error);
reject('An error happened during access validation. Details:' + String(error));
}
});
}

View File

@ -12,7 +12,7 @@ export class ErrorService {
if (!message) {
message = error.name || error.message;
if (message && message !== error.message) {
message += ` (${error.message})`;
message += ` (${String(error.message)})`;
}
}
return this.notificationService.error('Error' + (message ? `: ${message}` : ''));

View File

@ -23,6 +23,6 @@ export abstract class QueryParamsStore<D> {
preserve(data: D) {
const queryParams = removeEmptyProperties(this.mapToParams(data));
this.router.navigate([], { queryParams, preserveFragment: true });
void this.router.navigate([], { queryParams, preserveFragment: true });
}
}

View File

@ -130,4 +130,8 @@
@include mat.typography-level($config, display-1);
margin: 0;
}
.cc-code.cc-code {
font-family: 'Roboto Mono', monospace;
}
}

View File

@ -7,8 +7,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&amp;subset=cyrillic"
href="https://fonts.googleapis.com/css2?family=Roboto+Mono&family=Roboto:wght@300;400;500&display=swap"
rel="stylesheet"
/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />