diff --git a/package-lock.json b/package-lock.json index a27c37cf..152f765d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,7 @@ "@s-libs/ng-core": "14.0.0", "@s-libs/rxjs-core": "14.0.0", "@vality/deanonimus-proto": "1.0.1-c9a6cae.0", - "@vality/domain-proto": "1.0.1-09e7a75.0", + "@vality/domain-proto": "1.0.1-d59017c.0", "@vality/dominant-cache-proto": "1.0.1-5b29d81.0", "@vality/file-storage-proto": "1.0.1-447212b.0", "@vality/fistful-proto": "1.0.1-9c78e89.0", @@ -61,6 +61,7 @@ "tslib": "2.3.1", "utility-types": "3.10.0", "uuid": "3.3.3", + "yaml": "2.1.3", "zone.js": "0.11.4" }, "devDependencies": { @@ -5606,9 +5607,9 @@ "integrity": "sha512-GjD4N6ZXyuYaGXv4od+lcCGKfIJFi640I6wJ3+O1eA9VrUI4Ro+Ljpu+TLjckMQ6nS1DkXuu+06Dk7Hr5nK1og==" }, "node_modules/@vality/domain-proto": { - "version": "1.0.1-09e7a75.0", - "resolved": "https://registry.npmjs.org/@vality/domain-proto/-/domain-proto-1.0.1-09e7a75.0.tgz", - "integrity": "sha512-y7J2P98eehI7upxTPgg2vmxoBMV7QcgKstYiJBjJmt+Ra1XKhCeoGbRVOINmJqhHAbqGBpuqOAOLI1f7UZ9jLA==" + "version": "1.0.1-d59017c.0", + "resolved": "https://registry.npmjs.org/@vality/domain-proto/-/domain-proto-1.0.1-d59017c.0.tgz", + "integrity": "sha512-6hJYySG2O4guJ9NZdSzKtHLMBmef17fpfBxyCuVpnj+i56p3QEr7E+57UOAqwFaxV2Muw9lO7KkmVNWrtJ4eHQ==" }, "node_modules/@vality/dominant-cache-proto": { "version": "1.0.1-5b29d81.0", @@ -9559,6 +9560,15 @@ "node": ">=10" } }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -22496,12 +22506,11 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", + "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/yargs": { @@ -26585,9 +26594,9 @@ "integrity": "sha512-GjD4N6ZXyuYaGXv4od+lcCGKfIJFi640I6wJ3+O1eA9VrUI4Ro+Ljpu+TLjckMQ6nS1DkXuu+06Dk7Hr5nK1og==" }, "@vality/domain-proto": { - "version": "1.0.1-09e7a75.0", - "resolved": "https://registry.npmjs.org/@vality/domain-proto/-/domain-proto-1.0.1-09e7a75.0.tgz", - "integrity": "sha512-y7J2P98eehI7upxTPgg2vmxoBMV7QcgKstYiJBjJmt+Ra1XKhCeoGbRVOINmJqhHAbqGBpuqOAOLI1f7UZ9jLA==" + "version": "1.0.1-d59017c.0", + "resolved": "https://registry.npmjs.org/@vality/domain-proto/-/domain-proto-1.0.1-d59017c.0.tgz", + "integrity": "sha512-6hJYySG2O4guJ9NZdSzKtHLMBmef17fpfBxyCuVpnj+i56p3QEr7E+57UOAqwFaxV2Muw9lO7KkmVNWrtJ4eHQ==" }, "@vality/dominant-cache-proto": { "version": "1.0.1-5b29d81.0", @@ -29841,6 +29850,14 @@ "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" + }, + "dependencies": { + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + } } }, "create-ecdh": { @@ -39557,10 +39574,9 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", + "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==" }, "yargs": { "version": "17.4.1", diff --git a/package.json b/package.json index f43db42a..4db63900 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@s-libs/ng-core": "14.0.0", "@s-libs/rxjs-core": "14.0.0", "@vality/deanonimus-proto": "1.0.1-c9a6cae.0", - "@vality/domain-proto": "1.0.1-09e7a75.0", + "@vality/domain-proto": "1.0.1-d59017c.0", "@vality/dominant-cache-proto": "1.0.1-5b29d81.0", "@vality/file-storage-proto": "1.0.1-447212b.0", "@vality/fistful-proto": "1.0.1-9c78e89.0", @@ -75,6 +75,7 @@ "tslib": "2.3.1", "utility-types": "3.10.0", "uuid": "3.3.3", + "yaml": "2.1.3", "zone.js": "0.11.4" }, "devDependencies": { diff --git a/projects/ng-core/src/lib/utils/compare-different-types.ts b/projects/ng-core/src/lib/utils/compare-different-types.ts new file mode 100644 index 00000000..3c0cc7d8 --- /dev/null +++ b/projects/ng-core/src/lib/utils/compare-different-types.ts @@ -0,0 +1,8 @@ +/** + * Use with sorting (arr.sort(fn)) + */ +export function compareDifferentTypes(a: T, b: T): number { + return typeof a === 'number' && typeof b === 'number' + ? a - b + : String(a).localeCompare(String(b)); +} diff --git a/projects/ng-core/src/lib/utils/index.ts b/projects/ng-core/src/lib/utils/index.ts index b4c3a89a..8cbf5ff8 100644 --- a/projects/ng-core/src/lib/utils/index.ts +++ b/projects/ng-core/src/lib/utils/index.ts @@ -1,3 +1,5 @@ export * from './clean'; export * from './inline-json'; +export * from './compare-different-types'; export * from './split-ids'; +export * from './is-empty'; diff --git a/projects/ng-core/src/lib/utils/is-empty.ts b/projects/ng-core/src/lib/utils/is-empty.ts new file mode 100644 index 00000000..8c0f1cb0 --- /dev/null +++ b/projects/ng-core/src/lib/utils/is-empty.ts @@ -0,0 +1,7 @@ +import _isEmpty from 'lodash-es/isEmpty'; +import isNil from 'lodash-es/isNil'; +import isObject from 'lodash-es/isObject'; + +export function isEmpty(value: unknown): boolean { + return isObject(value) ? _isEmpty(value) : isNil(value) || value === ''; +} diff --git a/src/app/api/utils/thrift-instance/namespace-type.ts b/src/app/api/utils/thrift-instance/namespace-type.ts index c62b070a..00554b39 100644 --- a/src/app/api/utils/thrift-instance/namespace-type.ts +++ b/src/app/api/utils/thrift-instance/namespace-type.ts @@ -27,8 +27,8 @@ export function isPrimitiveType(type: ValueType): type is ThriftType { return PRIMITIVE_TYPES.includes(type as never); } -export const STRUCTURE_TYPES = ['typedef', 'struct', 'union', 'exception', 'enum'] as const; -export type StructureType = typeof STRUCTURE_TYPES[number]; +export type StructureType = keyof JsonAST; +export const STRUCTURE_TYPES: StructureType[] = ['typedef', 'struct', 'union', 'exception', 'enum']; export interface NamespaceObjectType { namespaceMetadata: ThriftAstMetadata; @@ -48,9 +48,9 @@ export function parseNamespaceObjectType( namespaceMetadata = metadata.reverse().find((m) => m.path === include[namespace].path); if (!namespaceMetadata) namespaceMetadata = metadata.reverse().find((m) => m.name === namespace); - const objectType = (Object.keys(namespaceMetadata.ast) as StructureType[]).find( + const objectType = Object.keys(namespaceMetadata.ast).find( (t) => namespaceMetadata.ast[t][type] - ); + ) as StructureType; if (!objectType || !STRUCTURE_TYPES.includes(objectType)) { throw new Error(`Unknown thrift structure type: ${objectType}`); } diff --git a/src/app/domain/domain-info/domain-info.component.html b/src/app/domain/domain-info/domain-info.component.html index 80188266..22596bed 100644 --- a/src/app/domain/domain-info/domain-info.component.html +++ b/src/app/domain/domain-info/domain-info.component.html @@ -9,9 +9,13 @@ >
diff --git a/src/app/domain/domain-info/domain-info.component.ts b/src/app/domain/domain-info/domain-info.component.ts index 64e8109a..df04ef3e 100644 --- a/src/app/domain/domain-info/domain-info.component.ts +++ b/src/app/domain/domain-info/domain-info.component.ts @@ -4,8 +4,11 @@ import { Router } from '@angular/router'; import { untilDestroyed, UntilDestroy } from '@ngneat/until-destroy'; import { DomainObject, Reference } from '@vality/domain-proto/lib/domain'; import { BaseDialogService, BaseDialogResponseStatus } from '@vality/ng-core'; +import { from } from 'rxjs'; import { filter, switchMap } from 'rxjs/operators'; +import { DomainMetadataViewExtensionsService } from '@cc/app/shared/services/domain-metadata-view-extensions'; + import { ConfirmActionDialogComponent } from '../../../components/confirm-action-dialog'; import { enumHasValue } from '../../../utils'; import { ViewerKind } from '../../shared/components/thrift-viewer'; @@ -26,6 +29,8 @@ export class DomainInfoComponent { version$ = this.domainStoreService.version$; progress$ = this.domainStoreService.isLoading$; objWithRef: { obj: DomainObject; ref: Reference } = null; + metadata$ = from(import('@vality/domain-proto/lib/metadata.json').then((m) => m.default)); + extensions$ = this.domainMetadataViewExtensionsService.extensions$; get kind() { const kind = localStorage.getItem(VIEWER_KIND); @@ -44,7 +49,8 @@ export class DomainInfoComponent { private domainStoreService: DomainStoreService, private baseDialogService: BaseDialogService, private notificationService: NotificationService, - private errorService: ErrorService + private errorService: ErrorService, + private domainMetadataViewExtensionsService: DomainMetadataViewExtensionsService ) {} edit() { diff --git a/src/app/domain/domain-obj-creation/domain-obj-creation.component.html b/src/app/domain/domain-obj-creation/domain-obj-creation.component.html index 820b872b..6435cd3d 100644 --- a/src/app/domain/domain-obj-creation/domain-obj-creation.component.html +++ b/src/app/domain/domain-obj-creation/domain-obj-creation.component.html @@ -16,9 +16,13 @@ > diff --git a/src/app/domain/domain-obj-creation/domain-obj-creation.component.ts b/src/app/domain/domain-obj-creation/domain-obj-creation.component.ts index 00868578..fea55b08 100644 --- a/src/app/domain/domain-obj-creation/domain-obj-creation.component.ts +++ b/src/app/domain/domain-obj-creation/domain-obj-creation.component.ts @@ -5,6 +5,8 @@ import { DomainObject } from '@vality/domain-proto/lib/domain'; import { BehaviorSubject } from 'rxjs'; import { withLatestFrom } from 'rxjs/operators'; +import { DomainMetadataViewExtensionsService } from '@cc/app/shared/services/domain-metadata-view-extensions'; + import { progressTo, getUnionKey, enumHasValue } from '../../../utils'; import { EditorKind } from '../../shared/components/thrift-editor'; import { ViewerKind } from '../../shared/components/thrift-viewer'; @@ -29,6 +31,7 @@ export class DomainObjCreationComponent { metadata$ = this.metadataService.metadata; extensions$ = this.domainMetadataFormExtensionsService.extensions$; + viewerExtensions$ = this.domainMetadataViewExtensionsService.extensions$; progress$ = new BehaviorSubject(0); get kind() { @@ -57,6 +60,7 @@ export class DomainObjCreationComponent { constructor( private domainMetadataFormExtensionsService: DomainMetadataFormExtensionsService, + private domainMetadataViewExtensionsService: DomainMetadataViewExtensionsService, private domainStoreService: DomainStoreService, private notificationService: NotificationService, private errorService: ErrorService, diff --git a/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.html b/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.html index 80432207..9b381ddf 100644 --- a/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.html +++ b/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.html @@ -36,7 +36,13 @@ {{ name | keyTitle | titlecase }} - + diff --git a/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.ts b/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.ts index 8ca1fefb..214353d4 100644 --- a/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.ts +++ b/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.ts @@ -4,13 +4,13 @@ import { Claim, ModificationUnit } from '@vality/domain-proto/lib/claim_manageme import { BaseDialogResponseStatus, BaseDialogService } from '@vality/ng-core'; import { coerceBoolean } from 'coerce-property'; import isEmpty from 'lodash-es/isEmpty'; -import { BehaviorSubject, switchMap } from 'rxjs'; +import { BehaviorSubject, switchMap, from } from 'rxjs'; import { filter, first } from 'rxjs/operators'; import { ClaimManagementService } from '@cc/app/api/claim-management'; import { PartyManagementService } from '@cc/app/api/payment-processing'; import { getModificationName } from '@cc/app/sections/claim/utils/get-modification-name'; -import { Patch } from '@cc/app/shared/components/json-viewer'; +import { DomainMetadataViewExtensionsService } from '@cc/app/shared/services/domain-metadata-view-extensions'; import { NotificationService } from '@cc/app/shared/services/notification'; import { Color, StatusColor } from '@cc/app/styles'; import { ConfirmActionDialogComponent } from '@cc/components/confirm-action-dialog'; @@ -33,11 +33,12 @@ export class ModificationUnitTimelineItemComponent { @Input() title?: string; @Input() icon?: string; @Input() color?: StatusColor | Color; - @Input() patches?: Patch[]; @Output() claimChanged = new EventEmitter(); isLoading$ = inProgressFrom(() => this.progress$); + metadata$ = from(import('@vality/domain-proto/lib/metadata.json').then((m) => m.default)); + extensions$ = this.domainMetadataViewExtensionsService.extensions$; private progress$ = new BehaviorSubject(0); @@ -45,19 +46,16 @@ export class ModificationUnitTimelineItemComponent { private partyManagementService: PartyManagementService, private baseDialogService: BaseDialogService, private claimManagementService: ClaimManagementService, - private notificationService: NotificationService + private notificationService: NotificationService, + private domainMetadataViewExtensionsService: DomainMetadataViewExtensionsService ) {} get name() { return getModificationName(this.modificationUnit.modification); } - get modification() { - return getUnionValue(getUnionValue(this.modificationUnit?.modification)); - } - get hasModificationContent() { - return !isEmpty(this.modification); + return !isEmpty(getUnionValue(getUnionValue(this.modificationUnit?.modification))); } update() { diff --git a/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.html b/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.html index 2634bc32..09d28eed 100644 --- a/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.html +++ b/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.html @@ -1,7 +1,6 @@ diff --git a/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.ts b/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.ts index 9a53f7c4..eef70633 100644 --- a/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.ts +++ b/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.ts @@ -1,13 +1,8 @@ import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; import { Claim, ModificationUnit } from '@vality/domain-proto/lib/claim_management'; -import { Category } from '@vality/dominant-cache-proto'; -import { combineLatest, defer, of, ReplaySubject } from 'rxjs'; -import { catchError, map } from 'rxjs/operators'; +import { ReplaySubject } from 'rxjs'; -import { DominantCacheService } from '@cc/app/api/dominant-cache'; import { ComponentChanges } from '@cc/app/shared'; -import { NotificationService } from '@cc/app/shared/services/notification'; -import { getUnionKey } from '@cc/utils'; @Component({ selector: 'cc-shop-modification-timeline-item', @@ -18,39 +13,8 @@ export class ShopModificationTimelineItemComponent implements OnChanges { @Input() claim: Claim; @Output() claimChanged = new EventEmitter(); - extended$ = combineLatest([ - defer(() => this.modificationUnit$), - this.dominantCacheService.GetCategories().pipe( - catchError((err) => { - this.notificationService.error('Categories were not loaded'); - console.error(err); - return of([] as Category[]); - }) - ), - ]).pipe( - map(([modificationUnit, categories]) => { - const modification = - modificationUnit.modification.party_modification.shop_modification.modification; - switch (getUnionKey(modification)) { - case 'creation': { - const category = categories.find( - (c) => c.ref === String(modification.creation?.category?.id) - ); - return [{ path: ['category', 'id'], value: category?.name }]; - } - default: - return []; - } - }) - ); - private modificationUnit$ = new ReplaySubject(1); - constructor( - private dominantCacheService: DominantCacheService, - private notificationService: NotificationService - ) {} - ngOnChanges({ modificationUnit }: ComponentChanges) { if (modificationUnit) { this.modificationUnit$.next(modificationUnit.currentValue); diff --git a/src/app/sections/shop-details/shop-details.component.html b/src/app/sections/shop-details/shop-details.component.html index 2788e065..067d35be 100644 --- a/src/app/sections/shop-details/shop-details.component.html +++ b/src/app/sections/shop-details/shop-details.component.html @@ -2,7 +2,14 @@ Shop details - +
@@ -13,7 +20,11 @@
diff --git a/src/app/sections/shop-details/shop-details.component.ts b/src/app/sections/shop-details/shop-details.component.ts index 743dffad..c3293995 100644 --- a/src/app/sections/shop-details/shop-details.component.ts +++ b/src/app/sections/shop-details/shop-details.component.ts @@ -1,10 +1,12 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Component } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { UntilDestroy } from '@ngneat/until-destroy'; import { BaseDialogService, BaseDialogResponseStatus } from '@vality/ng-core'; -import { combineLatest, switchMap } from 'rxjs'; +import { combineLatest, switchMap, from } from 'rxjs'; import { pluck, filter, withLatestFrom, first, map } from 'rxjs/operators'; +import { DomainMetadataViewExtensionsService } from '@cc/app/shared/services/domain-metadata-view-extensions'; + import { ConfirmActionDialogComponent } from '../../../components/confirm-action-dialog'; import { getUnionKey } from '../../../utils'; import { PartyManagementService } from '../../api/payment-processing'; @@ -16,7 +18,6 @@ import { FetchShopService } from './services/fetch-shop.service'; @Component({ templateUrl: 'shop-details.component.html', providers: [FetchShopService], - changeDetection: ChangeDetectionStrategy.OnPush, }) export class ShopDetailsComponent { partyID$ = this.route.params.pipe(pluck('partyID')); @@ -25,6 +26,8 @@ export class ShopDetailsComponent { shop$ = this.fetchShopService.shop$; contract$ = this.fetchShopService.contract$.pipe(map((c) => c?.contract)); inProgress$ = this.fetchShopService.inProgress$; + metadata$ = from(import('@vality/domain-proto/lib/metadata.json').then((m) => m.default)); + extensions$ = this.domainMetadataViewExtensionsService.extensions$; constructor( private fetchShopService: FetchShopService, @@ -32,7 +35,8 @@ export class ShopDetailsComponent { private partyManagementService: PartyManagementService, private baseDialogService: BaseDialogService, private errorService: ErrorService, - private notificationService: NotificationService + private notificationService: NotificationService, + private domainMetadataViewExtensionsService: DomainMetadataViewExtensionsService ) { combineLatest([this.partyID$, this.shopID$]).subscribe(([partyID, shopID]) => { this.fetchShopService.getShop(partyID, shopID); diff --git a/src/app/sections/shop-details/shop-details.module.ts b/src/app/sections/shop-details/shop-details.module.ts index e74fc0e3..e13cac99 100644 --- a/src/app/sections/shop-details/shop-details.module.ts +++ b/src/app/sections/shop-details/shop-details.module.ts @@ -12,12 +12,10 @@ import { HeadlineModule } from '@cc/components/headline'; import { ThriftPipesModule } from '../../shared'; import { ShopDetailsRoutingModule } from './shop-details-routing.module'; import { ShopDetailsComponent } from './shop-details.component'; -import { ShopMainInfoModule } from './shop-main-info'; @NgModule({ imports: [ ShopDetailsRoutingModule, - ShopMainInfoModule, HeadlineModule, FlexModule, MatCardModule, diff --git a/src/app/sections/shop-details/shop-main-info/components/category/category.component.html b/src/app/sections/shop-details/shop-main-info/components/category/category.component.html deleted file mode 100644 index 12f6efa6..00000000 --- a/src/app/sections/shop-details/shop-main-info/components/category/category.component.html +++ /dev/null @@ -1,4 +0,0 @@ - - {{ category?.name }} (ID: {{ categoryID }}) - - ID: {{ categoryID }} diff --git a/src/app/sections/shop-details/shop-main-info/components/category/category.component.ts b/src/app/sections/shop-details/shop-main-info/components/category/category.component.ts deleted file mode 100644 index 66449d6f..00000000 --- a/src/app/sections/shop-details/shop-main-info/components/category/category.component.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { Category } from '@vality/domain-proto/lib/domain'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; - -import { DominantCacheService } from '@cc/app/api/dominant-cache'; - -@Component({ - templateUrl: 'category.component.html', - selector: 'cc-category', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class CategoryComponent { - @Input() set category(categoryID: number) { - this.categoryID = categoryID; - this.category$ = this.dominantCacheService - .GetCategories() - .pipe( - map((categories) => - categories.find((category) => category.ref === String(categoryID)) - ) - ); - } - - category$: Observable; - categoryID: number; - - constructor(private dominantCacheService: DominantCacheService) {} -} diff --git a/src/app/sections/shop-details/shop-main-info/index.ts b/src/app/sections/shop-details/shop-main-info/index.ts deleted file mode 100644 index 36000cd9..00000000 --- a/src/app/sections/shop-details/shop-main-info/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './shop-main-info.module'; diff --git a/src/app/sections/shop-details/shop-main-info/shop-blocking.pipe.ts b/src/app/sections/shop-details/shop-main-info/shop-blocking.pipe.ts deleted file mode 100644 index a08e9a79..00000000 --- a/src/app/sections/shop-details/shop-main-info/shop-blocking.pipe.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core'; -import { Blocking } from '@vality/domain-proto/lib/domain'; - -import { getUnionKey } from '@cc/utils/get-union-key'; - -@Pipe({ - name: 'ccBlockingPipe', -}) -export class ShopBlockingPipe implements PipeTransform { - public transform(input: Blocking): string { - switch (getUnionKey(input)) { - case 'blocked': - return 'Blocked'; - case 'unblocked': - return 'Unblocked'; - default: - return ''; - } - } -} diff --git a/src/app/sections/shop-details/shop-main-info/shop-main-info.component.html b/src/app/sections/shop-details/shop-main-info/shop-main-info.component.html deleted file mode 100644 index 496b8c36..00000000 --- a/src/app/sections/shop-details/shop-main-info/shop-main-info.component.html +++ /dev/null @@ -1,62 +0,0 @@ -
-
- {{ shop.details.name }} - {{ - shop.details.description ? shop.details.description : '-' - }} - - - -
-
- {{ - shop.created_at | date: 'dd.MM.yyyy HH:mm:ss' - }} - {{ shop.location.url }} - {{ - shop.account?.currency?.symbolic_code ? shop.account.currency.symbolic_code : '-' - }} -
-
- {{ - shop.blocking | ccBlockingPipe - }} - - {{ shop.suspension | ccSuspensionPipe }} - -
-
-
- {{ - shop.payout_tool_id ? shop.payout_tool_id : '-' - }} - {{ shop.id }} - {{ shop.contract_id }} -
-
diff --git a/src/app/sections/shop-details/shop-main-info/shop-main-info.component.ts b/src/app/sections/shop-details/shop-main-info/shop-main-info.component.ts deleted file mode 100644 index bb2c4ce6..00000000 --- a/src/app/sections/shop-details/shop-main-info/shop-main-info.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { Shop } from '@vality/domain-proto'; - -@Component({ - selector: 'cc-shop-main-info', - templateUrl: 'shop-main-info.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class ShopMainInfoComponent { - @Input() shop: Shop; -} diff --git a/src/app/sections/shop-details/shop-main-info/shop-main-info.module.ts b/src/app/sections/shop-details/shop-main-info/shop-main-info.module.ts deleted file mode 100644 index c9777f28..00000000 --- a/src/app/sections/shop-details/shop-main-info/shop-main-info.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { FlexModule } from '@angular/flex-layout'; - -import { StatusModule } from '@cc/app/shared/components'; -import { DetailsItemModule } from '@cc/components/details-item'; - -import { CategoryComponent } from './components/category/category.component'; -import { ShopBlockingPipe } from './shop-blocking.pipe'; -import { ShopMainInfoComponent } from './shop-main-info.component'; -import { ShopSuspensionPipe } from './shop-suspension.pipe'; - -@NgModule({ - imports: [FlexModule, DetailsItemModule, StatusModule, CommonModule], - declarations: [ShopMainInfoComponent, CategoryComponent, ShopBlockingPipe, ShopSuspensionPipe], - exports: [ShopMainInfoComponent], -}) -export class ShopMainInfoModule {} diff --git a/src/app/sections/shop-details/shop-main-info/shop-suspension.pipe.ts b/src/app/sections/shop-details/shop-main-info/shop-suspension.pipe.ts deleted file mode 100644 index 60f1a3a2..00000000 --- a/src/app/sections/shop-details/shop-main-info/shop-suspension.pipe.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core'; -import { Suspension } from '@vality/domain-proto/lib/domain'; - -import { getUnionKey } from '@cc/utils/get-union-key'; - -@Pipe({ - name: 'ccSuspensionPipe', -}) -export class ShopSuspensionPipe implements PipeTransform { - public transform(input: Suspension): string { - switch (getUnionKey(input)) { - case 'active': - return 'Active'; - case 'suspended': - return 'Suspended'; - default: - return ''; - } - } -} diff --git a/src/app/shared/components/details-dialog/details-dialog.component.html b/src/app/shared/components/details-dialog/details-dialog.component.html index e5773146..18b59019 100644 --- a/src/app/shared/components/details-dialog/details-dialog.component.html +++ b/src/app/shared/components/details-dialog/details-dialog.component.html @@ -1,3 +1,3 @@ - + diff --git a/src/app/shared/components/json-viewer/components/key/key.component.html b/src/app/shared/components/json-viewer/components/key/key.component.html new file mode 100644 index 00000000..1754aff5 --- /dev/null +++ b/src/app/shared/components/json-viewer/components/key/key.component.html @@ -0,0 +1,15 @@ +
+ {{ numberKey }}. + + + {{ (pathItem.key$ | async)?.renderValue$ | async | keyTitle | titlecase + }}{{ + idx !== keys.length - 1 ? ((isUnion(pathItem) | async) ? ': ' : ' / ') : '' + }} + + +
diff --git a/src/app/shared/components/json-viewer/components/key/key.component.scss b/src/app/shared/components/json-viewer/components/key/key.component.scss new file mode 100644 index 00000000..23755f8f --- /dev/null +++ b/src/app/shared/components/json-viewer/components/key/key.component.scss @@ -0,0 +1,3 @@ +.bold { + font-weight: bold; +} diff --git a/src/app/shared/components/json-viewer/components/key/key.component.ts b/src/app/shared/components/json-viewer/components/key/key.component.ts new file mode 100644 index 00000000..e81da8cf --- /dev/null +++ b/src/app/shared/components/json-viewer/components/key/key.component.ts @@ -0,0 +1,43 @@ +import { Component, Input, OnChanges } from '@angular/core'; +import { of, switchMap, ReplaySubject } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { ComponentChanges } from '@cc/app/shared'; + +import { MetadataViewItem } from '../../utils/metadata-view'; + +@Component({ + selector: 'cc-key', + templateUrl: './key.component.html', + styleUrls: ['./key.component.scss'], +}) +export class KeyComponent implements OnChanges { + @Input() keys?: MetadataViewItem[]; + keys$ = new ReplaySubject(1); + numberKey$ = this.keys$.pipe( + switchMap((keys) => { + if (keys.length !== 1) return of(null); + return this.keys[0].key$.pipe( + switchMap((key) => key.renderValue$), + map((value) => { + if (typeof value === 'number') return `${value + 1}`; + return null; + }) + ); + }) + ); + + ngOnChanges(changes: ComponentChanges) { + if (changes.keys) this.keys$.next(this.keys); + } + + parentIsUnion(pathItem: MetadataViewItem) { + if (!pathItem?.data$) return of(false); + return pathItem.data$.pipe(map((data) => data?.trueParent?.objectType === 'union')); + } + + isUnion(pathItem: MetadataViewItem) { + if (!pathItem?.data$) return of(false); + return pathItem.data$.pipe(map((data) => data?.trueTypeNode?.data?.objectType === 'union')); + } +} diff --git a/src/app/shared/components/json-viewer/index.ts b/src/app/shared/components/json-viewer/index.ts index 5ed65ce5..5b14c032 100644 --- a/src/app/shared/components/json-viewer/index.ts +++ b/src/app/shared/components/json-viewer/index.ts @@ -1,2 +1,2 @@ export * from './json-viewer.module'; -export * from './types/patch'; +export * from './utils/metadata-view-extension'; diff --git a/src/app/shared/components/json-viewer/json-viewer.component.html b/src/app/shared/components/json-viewer/json-viewer.component.html index c8121a7f..f2657fd3 100644 --- a/src/app/shared/components/json-viewer/json-viewer.component.html +++ b/src/app/shared/components/json-viewer/json-viewer.component.html @@ -1,30 +1,80 @@ -
-
- +
+
- - {{ item.value }} - info - - + + +
+ + + hide_source + +
+
+
+ + + +
+ + + + + +
+
- - - - {{ item.key | keyTitle | titlecase }} - - -
+ + + + {{ view.renderValue$ | async }} + + + {{ view.renderValue$ | async }} + + + + diff --git a/src/app/shared/components/json-viewer/json-viewer.component.ts b/src/app/shared/components/json-viewer/json-viewer.component.ts index 291904b9..1451ed7c 100644 --- a/src/app/shared/components/json-viewer/json-viewer.component.ts +++ b/src/app/shared/components/json-viewer/json-viewer.component.ts @@ -1,50 +1,59 @@ -import { Component, Input } from '@angular/core'; -import isEqual from 'lodash-es/isEqual'; -import isObject from 'lodash-es/isObject'; +import { Component, Input, OnChanges } from '@angular/core'; +import { ValueType, Field } from '@vality/thrift-ts'; +import yaml from 'yaml'; -import { InlineItem } from './types/inline-item'; -import { Patch } from './types/patch'; -import { getInline } from './utils/get-inline'; +import { ThriftAstMetadata } from '@cc/app/api/utils'; + +import { MetadataFormData } from '../metadata-form'; +import { MetadataViewItem } from './utils/metadata-view'; +import { + MetadataViewExtension, + MetadataViewExtensionResult, +} from './utils/metadata-view-extension'; @Component({ selector: 'cc-json-viewer', templateUrl: './json-viewer.component.html', + styleUrls: ['./json-viewer.scss'], }) -export class JsonViewerComponent { - @Input() json: unknown; - @Input() path: string[] = []; +export class JsonViewerComponent implements OnChanges { + @Input() value: unknown; + @Input() level = 0; + @Input() extension?: MetadataViewExtensionResult; - @Input() patches: Patch[] = []; + @Input() metadata: ThriftAstMetadata[]; + @Input() namespace: string; + @Input() type: ValueType; + @Input() field?: Field; + @Input() parent?: MetadataFormData; - get inline(): InlineItem[] { - try { - return Object.entries(this.json) - .map(([k, v]) => getInline([k], v)) - .filter(Boolean) - .map( - ([path, value]): InlineItem => - new InlineItem( - path, - value, - this.patches?.find((p) => isEqual(p.path, path)) - ) - ) - .sort(({ key: a }, { key: b }) => a.localeCompare(b)); - } catch (err) { - return []; + @Input() data: MetadataFormData; + @Input() extensions: MetadataViewExtension[]; + + view: MetadataViewItem; + className = this.getClassName(); + + ngOnChanges() { + if (this.metadata && this.namespace && this.type) { + try { + this.data = new MetadataFormData( + this.metadata, + this.namespace, + this.type, + this.field, + this.parent + ); + } catch (err) { + this.data = undefined; + console.warn(err); + } } + this.view = new MetadataViewItem(this.value, undefined, this.data, this.extensions); + this.className = this.getClassName(); } - get objects() { - return this.inline.filter(({ value }) => isObject(value)); - } - - get items() { - return this.inline.filter(({ value }) => !isObject(value)); - } - - get className() { - switch (this.path.length) { + getClassName() { + switch (this.level) { case 0: return 'cc-title'; case 1: @@ -56,7 +65,7 @@ export class JsonViewerComponent { } } - trackByFn(idx: number, item: InlineItem) { - return item.path.join(';'); + getTooltip(tooltip: any) { + return yaml.stringify(tooltip); } } diff --git a/src/app/shared/components/json-viewer/json-viewer.module.ts b/src/app/shared/components/json-viewer/json-viewer.module.ts index 23c3633d..0bc3bc96 100644 --- a/src/app/shared/components/json-viewer/json-viewer.module.ts +++ b/src/app/shared/components/json-viewer/json-viewer.module.ts @@ -1,6 +1,7 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FlexModule, GridModule } from '@angular/flex-layout'; +import { MatBadgeModule } from '@angular/material/badge'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatDividerModule } from '@angular/material/divider'; @@ -10,10 +11,11 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { ThriftPipesModule } from '@cc/app/shared'; import { DetailsItemModule } from '@cc/components/details-item'; +import { KeyComponent } from './components/key/key.component'; import { JsonViewerComponent } from './json-viewer.component'; @NgModule({ - declarations: [JsonViewerComponent], + declarations: [JsonViewerComponent, KeyComponent], exports: [JsonViewerComponent], imports: [ CommonModule, @@ -26,6 +28,7 @@ import { JsonViewerComponent } from './json-viewer.component'; MatButtonModule, MatTooltipModule, FlexModule, + MatBadgeModule, ], }) export class JsonViewerModule {} diff --git a/src/app/shared/components/json-viewer/json-viewer.scss b/src/app/shared/components/json-viewer/json-viewer.scss new file mode 100644 index 00000000..eb406fd8 --- /dev/null +++ b/src/app/shared/components/json-viewer/json-viewer.scss @@ -0,0 +1,5 @@ +::ng-deep .tooltip { + display: block; + unicode-bidi: embed; + white-space: pre; +} diff --git a/src/app/shared/components/json-viewer/types/inline-item.ts b/src/app/shared/components/json-viewer/types/inline-item.ts deleted file mode 100644 index f97b7451..00000000 --- a/src/app/shared/components/json-viewer/types/inline-item.ts +++ /dev/null @@ -1,31 +0,0 @@ -import isEmpty from 'lodash-es/isEmpty'; -import isNil from 'lodash-es/isNil'; -import isObject from 'lodash-es/isObject'; - -import { Patch } from '../types/patch'; - -export class InlineItem { - get key() { - return this.patch?.key ?? this.path.join(' / '); - } - - get value() { - return this.patch?.value ?? this.sourceValue; - } - - get tooltip() { - return this.isPatched ? JSON.stringify(this.sourceValue, null, 2) : undefined; - } - - get isPatched() { - return !!this.patch; - } - - get isEmpty() { - return isObject(this.sourceValue) ? isEmpty(this.sourceValue) : isNil(this.sourceValue); - } - - constructor(public path: string[], public sourceValue: unknown, private patch?: Patch) { - this.path = this.patch?.path ?? this.path; - } -} diff --git a/src/app/shared/components/json-viewer/types/patch.ts b/src/app/shared/components/json-viewer/types/patch.ts deleted file mode 100644 index efda534c..00000000 --- a/src/app/shared/components/json-viewer/types/patch.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface Patch { - path: string[]; - key?: string; - value?: unknown; - tooltip?: string; -} diff --git a/src/app/shared/components/json-viewer/utils/get-children-types.ts b/src/app/shared/components/json-viewer/utils/get-children-types.ts new file mode 100644 index 00000000..65687313 --- /dev/null +++ b/src/app/shared/components/json-viewer/utils/get-children-types.ts @@ -0,0 +1,33 @@ +import { ValueType, Field, SetType, MapType, ListType } from '@vality/thrift-ts'; + +import { MetadataFormData, TypeGroup } from '../../metadata-form'; + +export function getChildrenTypes(sourceData: MetadataFormData): { + keyType?: ValueType; + valueType?: ValueType; + fields?: Field[]; +} { + const data = sourceData.trueTypeNode.data; + switch (data.typeGroup) { + case TypeGroup.Object: { + switch (data.objectType) { + case 'struct': + return { fields: (data as MetadataFormData).ast }; + case 'union': + return { fields: (data as MetadataFormData).ast }; + } + return; + } + case TypeGroup.Complex: { + if ((data as MetadataFormData).type.name === 'map') { + return { + keyType: (data as MetadataFormData).type.keyType, + valueType: (data as MetadataFormData).type.valueType, + }; + } + return { + valueType: (data as MetadataFormData).type.valueType, + }; + } + } +} diff --git a/src/app/shared/components/json-viewer/utils/get-entries.ts b/src/app/shared/components/json-viewer/utils/get-entries.ts new file mode 100644 index 00000000..ddef6bef --- /dev/null +++ b/src/app/shared/components/json-viewer/utils/get-entries.ts @@ -0,0 +1,8 @@ +export function getEntries(obj: any): [number | string, any][] { + if (!obj) return []; + return Array.isArray(obj) || obj instanceof Set + ? Array.from(obj).map((v, idx) => [idx, v]) + : obj instanceof Map + ? Array.from(obj) + : Object.entries(obj); +} diff --git a/src/app/shared/components/json-viewer/utils/get-inline.ts b/src/app/shared/components/json-viewer/utils/get-inline.ts deleted file mode 100644 index 7c265a12..00000000 --- a/src/app/shared/components/json-viewer/utils/get-inline.ts +++ /dev/null @@ -1,16 +0,0 @@ -import isNil from 'lodash-es/isNil'; -import isObject from 'lodash-es/isObject'; - -export function getInline(path: string[], value: unknown): [string[], unknown] { - if (isNil(value)) { - return null; - } - if (isObject(value)) { - const entries: [string, unknown][] = Object.entries(value).filter(([, v]) => !isNil(v)); - if (entries.length === 1) { - const [childKey, childValue] = entries[0]; - return getInline([...path, childKey], childValue); - } - } - return [path, value]; -} diff --git a/src/app/shared/components/json-viewer/utils/metadata-view-extension.ts b/src/app/shared/components/json-viewer/utils/metadata-view-extension.ts new file mode 100644 index 00000000..37e47977 --- /dev/null +++ b/src/app/shared/components/json-viewer/utils/metadata-view-extension.ts @@ -0,0 +1,29 @@ +import { Observable, combineLatest, switchMap, of } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { MetadataFormData } from '../../metadata-form'; + +export interface MetadataViewExtensionResult { + key?: string; + value: string; + tooltip?: any; + // link?: string; +} + +export type MetadataViewExtension = { + determinant: (data: MetadataFormData, value: any) => Observable; + extension: (data: MetadataFormData, value: any) => Observable; +}; + +export function getFirstDeterminedExtensionsResult( + sourceExtensions: MetadataViewExtension[], + data: MetadataFormData, + value: any +): Observable { + return sourceExtensions?.length + ? combineLatest(sourceExtensions.map(({ determinant }) => determinant(data, value))).pipe( + map((determined) => sourceExtensions.find((_, idx) => determined[idx])), + switchMap((extension) => extension?.extension(data, value) ?? of(null)) + ) + : of(null); +} diff --git a/src/app/shared/components/json-viewer/utils/metadata-view.ts b/src/app/shared/components/json-viewer/utils/metadata-view.ts new file mode 100644 index 00000000..a371cfee --- /dev/null +++ b/src/app/shared/components/json-viewer/utils/metadata-view.ts @@ -0,0 +1,190 @@ +import { isEmpty } from '@vality/ng-core'; +import { SetType, ListType, MapType, ValueType } from '@vality/thrift-ts'; +import isNil from 'lodash-es/isNil'; +import isObject from 'lodash-es/isObject'; +import { Observable, of, switchMap, combineLatest } from 'rxjs'; +import { map, shareReplay } from 'rxjs/operators'; + +import { MetadataFormData } from '../../metadata-form'; +import { getChildrenTypes } from './get-children-types'; +import { getEntries } from './get-entries'; +import { + MetadataViewExtension, + getFirstDeterminedExtensionsResult, +} from './metadata-view-extension'; + +export class MetadataViewItem { + extension$ = getFirstDeterminedExtensionsResult(this.extensions, this.data, this.value).pipe( + shareReplay({ refCount: true, bufferSize: 1 }) + ); + data$ = this.extension$.pipe(map((ext) => (ext ? null : this.data))); + key$ = this.extension$.pipe( + map((ext) => (isNil(ext?.key) ? this.key : new MetadataViewItem(ext.key))), + shareReplay({ refCount: true, bufferSize: 1 }) + ); + value$ = this.extension$.pipe( + map((ext) => { + const value = ext?.value ?? this.value; + return isEmpty(value) ? null : value; + }) + ); + renderValue$ = combineLatest([this.value$, this.data$]).pipe( + map(([value, data]) => { + if (data?.trueTypeNode?.data?.objectType === 'enum') + return ( + (data.trueTypeNode.data as MetadataFormData).ast.items.find( + (i) => i.value === value + ).name ?? value + ); + if (data?.objectType === 'union' && isEmpty(getEntries(value)?.[0]?.[1])) + return getEntries(value)?.[0]?.[0]; + return value; + }) + ); + + items$: Observable = this.createItems().pipe( + shareReplay({ refCount: true, bufferSize: 1 }) + ); + inline$: Observable = combineLatest([ + this.items$, + this.key$, + this.data$, + this.key$.pipe(switchMap((key) => key?.value$ || of(null))), + ]).pipe( + switchMap(([items, key, data, keyValue]) => { + if ( + !items.length || + items.length > 1 || + isObject(keyValue) || + key?.data || + (data?.trueTypeNode?.data as MetadataFormData)?.type + ?.name + ) + return of([]); + const [item] = items; + return combineLatest([ + item.key$.pipe(switchMap((key) => key.value$)), + item.value$, + ]).pipe( + switchMap(([childKey, childValue]) => { + if ( + typeof childKey === 'number' || + (data?.objectType === 'union' && isEmpty(childValue)) + ) + return of([]); + return item.data$.pipe( + switchMap((itemData) => { + if (data?.objectType === 'union' && itemData?.objectType !== 'union') + return of([item]); + return item.inline$.pipe(map((childInline) => [item, ...childInline])); + }) + ); + }) + ); + }), + shareReplay({ refCount: true, bufferSize: 1 }) + ); + path$: Observable = this.inline$.pipe( + map((inline) => { + return [this, ...inline]; + }), + shareReplay({ refCount: true, bufferSize: 1 }) + ); + current$ = this.path$.pipe(map((keys) => keys.at(-1))); + + isLeaf$ = combineLatest([ + this.current$.pipe(switchMap((c) => c.items$)), + this.data$, + this.value$, + ]).pipe( + map(([items, data, value]) => { + return ( + !items.length || + (data?.objectType === 'union' && isEmpty(getEntries(value)?.[0]?.[1])) + ); + }), + shareReplay({ refCount: true, bufferSize: 1 }) + ); + + isValue$ = combineLatest([ + this.current$.pipe(switchMap((c) => c.items$)), + this.data$, + this.value$, + this.current$.pipe(map((c) => c.key)), + ]).pipe( + map(([items, data, value, key]) => { + return ( + (!items.length && !key) || + (data?.objectType === 'union' && isEmpty(getEntries(value)?.[0]?.[1])) + ); + }) + ); + + leaves$ = this.items$.pipe( + switchMap((items) => + combineLatest( + items.map((item) => item.isLeaf$.pipe(map((isLeaf) => (isLeaf ? item : null)))) + ) + ), + map((items) => items.filter(Boolean)), + shareReplay({ refCount: true, bufferSize: 1 }) + ); + nodes$ = this.items$.pipe( + switchMap((items) => + combineLatest( + items.map((item) => item.isLeaf$.pipe(map((isLeaf) => (isLeaf ? null : item)))) + ) + ), + map((items) => items.filter(Boolean)), + shareReplay({ refCount: true, bufferSize: 1 }) + ); + + isNumberKey$ = this.key$.pipe(map(({ value }) => typeof value === 'number')); + + constructor( + private value: any, + private key?: MetadataViewItem, + private data?: MetadataFormData, + private extensions?: MetadataViewExtension[] + ) {} + + private createItems(): Observable { + return combineLatest([this.data$, this.value$]).pipe( + map(([data, value]) => { + if (data) { + const trueData = this.data.trueTypeNode.data; + if ( + trueData.objectType === 'struct' || + trueData.objectType === 'union' || + (trueData as MetadataFormData).type?.name + ) { + const types = getChildrenTypes(trueData); + return getEntries(value).map(([itemKey, itemValue]) => { + return new MetadataViewItem( + itemValue, + types.keyType + ? new MetadataViewItem( + itemKey, + undefined, + trueData.create({ type: types.keyType }), + this.extensions + ) + : new MetadataViewItem(itemKey), + trueData.create({ + field: types.fields?.find((f) => f.name === itemKey), + type: types.valueType, + }), + this.extensions + ); + }); + } + } + return isObject(value) + ? getEntries(value).map( + ([k, v]) => new MetadataViewItem(v, new MetadataViewItem(k)) + ) + : []; + }) + ); + } +} diff --git a/src/app/shared/components/metadata-form/components/complex-form/complex-form.component.html b/src/app/shared/components/metadata-form/components/complex-form/complex-form.component.html index 035a912f..8c76255d 100644 --- a/src/app/shared/components/metadata-form/components/complex-form/complex-form.component.html +++ b/src/app/shared/components/metadata-form/components/complex-form/complex-form.component.html @@ -48,7 +48,7 @@ Key Value | implements OnInit, Validator { @Input() data: MetadataFormData; + @Input() extensions: MetadataFormExtension[]; valueControls = new FormArray([]); keyControls = new FormArray([]); diff --git a/src/app/shared/components/metadata-form/components/enum-field/enum-field.component.ts b/src/app/shared/components/metadata-form/components/enum-field/enum-field.component.ts index 05530dd2..e505f466 100644 --- a/src/app/shared/components/metadata-form/components/enum-field/enum-field.component.ts +++ b/src/app/shared/components/metadata-form/components/enum-field/enum-field.component.ts @@ -1,5 +1,4 @@ import { Component, Input } from '@angular/core'; -import { Enums } from '@vality/thrift-ts/src/thrift-parser'; import { createControlProviders, ValidatedFormControlSuperclass } from '@cc/utils'; @@ -11,5 +10,5 @@ import { MetadataFormData } from '../../types/metadata-form-data'; providers: createControlProviders(EnumFieldComponent), }) export class EnumFieldComponent extends ValidatedFormControlSuperclass { - @Input() data: MetadataFormData; + @Input() data: MetadataFormData; } diff --git a/src/app/shared/components/metadata-form/components/extension-field/extension-field.component.ts b/src/app/shared/components/metadata-form/components/extension-field/extension-field.component.ts index b89bcc9c..2b944100 100644 --- a/src/app/shared/components/metadata-form/components/extension-field/extension-field.component.ts +++ b/src/app/shared/components/metadata-form/components/extension-field/extension-field.component.ts @@ -3,14 +3,19 @@ import { Validator, ValidationErrors, FormControl, Validators } from '@angular/f import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { FormComponentSuperclass } from '@s-libs/ng-core'; import { ThriftType } from '@vality/thrift-ts'; -import { defer, switchMap, ReplaySubject, Observable } from 'rxjs'; +import { defer, switchMap, ReplaySubject, Observable, combineLatest } from 'rxjs'; import { shareReplay, first, map } from 'rxjs/operators'; import { createControlProviders } from '@cc/utils'; import { ComponentChanges } from '../../../../utils'; import { MetadataFormData } from '../../types/metadata-form-data'; -import { Converter } from '../../types/metadata-form-extension'; +import { + Converter, + MetadataFormExtension, + MetadataFormExtensionResult, + getFirstDeterminedExtensionsResult, +} from '../../types/metadata-form-extension'; @UntilDestroy() @Component({ @@ -23,15 +28,20 @@ export class ExtensionFieldComponent implements Validator, OnChanges, OnInit { @Input() data: MetadataFormData; + @Input() extensions: MetadataFormExtension[]; control = new FormControl(null); - extensionResult$ = defer(() => this.data$).pipe( - switchMap((data) => data.extensionResult$), + extensionResult$: Observable = combineLatest([ + defer(() => this.data$), + defer(() => this.extensions$), + ]).pipe( + switchMap(([data, extensions]) => getFirstDeterminedExtensionsResult(extensions, data)), shareReplay({ refCount: true, bufferSize: 1 }) ); private data$ = new ReplaySubject(1); + private extensions$ = new ReplaySubject(1); private converter$: Observable = this.extensionResult$.pipe( map( ({ converter }) => @@ -69,5 +79,6 @@ export class ExtensionFieldComponent this.data$.next(this.data); this.control.setValidators(this.data.isRequired ? Validators.required : []); } + if (changes.extensions) this.extensions$.next(this.extensions); } } diff --git a/src/app/shared/components/metadata-form/components/primitive-field/primitive-field.component.html b/src/app/shared/components/metadata-form/components/primitive-field/primitive-field.component.html index 9a9c72d4..f3b55a2d 100644 --- a/src/app/shared/components/metadata-form/components/primitive-field/primitive-field.component.html +++ b/src/app/shared/components/metadata-form/components/primitive-field/primitive-field.component.html @@ -24,13 +24,12 @@
- + @@ -49,11 +48,11 @@ {{ data.type | fieldLabel: data.field }} {{ - (data.extensionResult$ | async).label + (extensionResult$ | async).label }} {{ aliases }} @@ -61,7 +60,7 @@ #trigger="matAutocompleteTrigger" [formControl]="control" [matAutocomplete]="auto" - [ngClass]="{ 'cc-code': (data.extensionResult$ | async)?.isIdentifier }" + [ngClass]="{ 'cc-code': (extensionResult$ | async)?.isIdentifier }" [required]="data.isRequired" [type]="inputType" matInput @@ -86,7 +85,7 @@
- + - + diff --git a/src/app/shared/components/metadata-form/components/primitive-field/primitive-field.component.ts b/src/app/shared/components/metadata-form/components/primitive-field/primitive-field.component.ts index 3b650dfd..cdeadb03 100644 --- a/src/app/shared/components/metadata-form/components/primitive-field/primitive-field.component.ts +++ b/src/app/shared/components/metadata-form/components/primitive-field/primitive-field.component.ts @@ -1,10 +1,15 @@ import { Component, Input, OnChanges } from '@angular/core'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { ThriftType } from '@vality/thrift-ts'; -import { combineLatest, defer, ReplaySubject, switchMap } from 'rxjs'; +import { combineLatest, defer, ReplaySubject, switchMap, Observable } from 'rxjs'; import { map, pluck, shareReplay, startWith } from 'rxjs/operators'; import { ComponentChanges, getValueTypeTitle } from '@cc/app/shared'; +import { + MetadataFormExtensionResult, + MetadataFormExtension, +} from '@cc/app/shared/components/metadata-form'; +import { getFirstDeterminedExtensionsResult } from '@cc/app/shared/components/metadata-form/types/metadata-form-extension'; import { createControlProviders, ValidatedFormControlSuperclass } from '@cc/utils'; import { MetadataFormData, getAliases } from '../../types/metadata-form-data'; @@ -20,9 +25,13 @@ export class PrimitiveFieldComponent implements OnChanges { @Input() data: MetadataFormData; + @Input() extensions: MetadataFormExtension[]; - extensionResult$ = defer(() => this.data$).pipe( - switchMap((data) => data.extensionResult$), + extensionResult$: Observable = combineLatest([ + defer(() => this.data$), + defer(() => this.extensions$), + ]).pipe( + switchMap(([data, extensions]) => getFirstDeterminedExtensionsResult(extensions, data)), shareReplay({ refCount: true, bufferSize: 1 }) ); generate$ = this.extensionResult$.pipe(pluck('generate')); @@ -68,10 +77,12 @@ export class PrimitiveFieldComponent } private data$ = new ReplaySubject>(1); + private extensions$ = new ReplaySubject(1); ngOnChanges(changes: ComponentChanges>) { super.ngOnChanges(changes); if (changes.data) this.data$.next(this.data); + if (changes.extensions) this.extensions$.next(this.extensions); } generate(event: MouseEvent) { diff --git a/src/app/shared/components/metadata-form/components/struct-form/struct-form.component.html b/src/app/shared/components/metadata-form/components/struct-form/struct-form.component.html index bd1a4c97..b8db8b0d 100644 --- a/src/app/shared/components/metadata-form/components/struct-form/struct-form.component.html +++ b/src/app/shared/components/metadata-form/components/struct-form/struct-form.component.html @@ -10,7 +10,7 @@ extends ValidatedControlSuperclass implements OnChanges, OnInit { - @Input() data: MetadataFormData; + @Input() data: MetadataFormData; + @Input() extensions: MetadataFormExtension[]; - control = this.fb.group({} as any); + control = this.fb.group({} as never); labelControl = this.fb.control(false); get hasLabel() { diff --git a/src/app/shared/components/metadata-form/components/typedef-form/typedef-form.component.html b/src/app/shared/components/metadata-form/components/typedef-form/typedef-form.component.html index 147d676e..8d99ffda 100644 --- a/src/app/shared/components/metadata-form/components/typedef-form/typedef-form.component.html +++ b/src/app/shared/components/metadata-form/components/typedef-form/typedef-form.component.html @@ -1,5 +1,5 @@ extends ValidatedFormControlSuperclass { - @Input() data: MetadataFormData; + @Input() data: MetadataFormData; + @Input() extensions: MetadataFormExtension[]; } diff --git a/src/app/shared/components/metadata-form/components/union-field/union-field.component.html b/src/app/shared/components/metadata-form/components/union-field/union-field.component.html index 402abfcb..5184aa59 100644 --- a/src/app/shared/components/metadata-form/components/union-field/union-field.component.html +++ b/src/app/shared/components/metadata-form/components/union-field/union-field.component.html @@ -21,7 +21,7 @@ extends FormComponentSuperclass implements OnInit, Validator { - @Input() data: MetadataFormData; + @Input() data: MetadataFormData; + @Input() extensions: MetadataFormExtension[]; fieldControl = new FormControl(); internalControl = new FormControl(); diff --git a/src/app/shared/components/metadata-form/metadata-form.component.html b/src/app/shared/components/metadata-form/metadata-form.component.html index a02c77ea..d3ffef2d 100644 --- a/src/app/shared/components/metadata-form/metadata-form.component.html +++ b/src/app/shared/components/metadata-form/metadata-form.component.html @@ -1,33 +1,37 @@
diff --git a/src/app/shared/components/metadata-form/metadata-form.component.ts b/src/app/shared/components/metadata-form/metadata-form.component.ts index 0ce70ff9..52ded875 100644 --- a/src/app/shared/components/metadata-form/metadata-form.component.ts +++ b/src/app/shared/components/metadata-form/metadata-form.component.ts @@ -1,12 +1,17 @@ import { Component, Input, OnChanges } from '@angular/core'; import { Validator } from '@angular/forms'; import { Field, ValueType } from '@vality/thrift-ts'; +import { Observable } from 'rxjs'; import { ThriftAstMetadata } from '@cc/app/api/utils'; import { createControlProviders, ValidatedFormControlSuperclass } from '@cc/utils'; import { MetadataFormData } from './types/metadata-form-data'; -import { MetadataFormExtension } from './types/metadata-form-extension'; +import { + MetadataFormExtension, + MetadataFormExtensionResult, + getFirstDeterminedExtensionsResult, +} from './types/metadata-form-extension'; @Component({ selector: 'cc-metadata-form', @@ -25,6 +30,7 @@ export class MetadataFormComponent @Input() extensions?: MetadataFormExtension[]; data: MetadataFormData; + extensionResult$: Observable; ngOnChanges() { if (this.metadata && this.namespace && this.type) { @@ -34,8 +40,11 @@ export class MetadataFormComponent this.namespace, this.type, this.field, - this.parent, - this.extensions + this.parent + ); + this.extensionResult$ = getFirstDeterminedExtensionsResult( + this.extensions, + this.data ); } catch (err) { this.data = undefined; diff --git a/src/app/shared/components/metadata-form/types/metadata-form-data.ts b/src/app/shared/components/metadata-form/types/metadata-form-data.ts index c12676fe..fb834316 100644 --- a/src/app/shared/components/metadata-form/types/metadata-form-data.ts +++ b/src/app/shared/components/metadata-form/types/metadata-form-data.ts @@ -1,7 +1,5 @@ import { Field, ValueType } from '@vality/thrift-ts'; import { JsonAST } from '@vality/thrift-ts/src/thrift-parser'; -import { combineLatest, Observable, switchMap } from 'rxjs'; -import { map, pluck, shareReplay } from 'rxjs/operators'; import { ValuesType } from 'utility-types'; import { @@ -13,8 +11,6 @@ import { ThriftAstMetadata, } from '@cc/app/api/utils'; -import { MetadataFormExtension, MetadataFormExtensionResult } from './metadata-form-extension'; - export enum TypeGroup { Complex = 'complex', Primitive = 'primitive', @@ -49,24 +45,20 @@ export function isTypeWithAliases( return Boolean(getByType(data, type, namespace)); } -type ObjectAst = ValuesType>>; - -export class MetadataFormData { +export class MetadataFormData< + T extends ValueType = ValueType, + S extends StructureType = StructureType +> { typeGroup: TypeGroup; namespace: string; type: T; - objectType?: StructureType; - ast?: M; + objectType?: S; + ast?: ValuesType; include?: JsonAST['include']; - /** - * The first one identified is used - */ - extensionResult$: Observable; - /** * Parent who is not typedef */ @@ -78,6 +70,21 @@ export class MetadataFormData[] = []; + let currentData: MetadataFormData = this as never; + while (currentData.objectType === 'typedef') { + typedefs.push(currentData as never); + currentData = currentData.create({ + type: (currentData as MetadataFormData).ast.type, + }); + } + return { data: currentData, typedefs }; + } + get isRequired() { return this.field?.option === 'required' || this.trueParent?.objectType === 'union'; } @@ -87,26 +94,27 @@ export class MetadataFormData(type, namespace); + const namespaceType = parseNamespaceType(type, namespace); this.namespace = namespaceType.namespace; this.type = namespaceType.type; - this.extensionResult$ = combineLatest( - (this.extensions || []).map(({ determinant }) => determinant(this)) - ).pipe( - map((determined) => this.extensions.filter((_, idx) => determined[idx])), - switchMap((extensions) => combineLatest(extensions.map((e) => e.extension(this)))), - pluck(0), - shareReplay({ refCount: true, bufferSize: 1 }) - ); } private setTypeGroup(type: ValueType = this.type) { @@ -124,8 +132,8 @@ export class MetadataFormData { + return sourceExtensions?.length + ? combineLatest(sourceExtensions.map(({ determinant }) => determinant(data))).pipe( + map((determined) => sourceExtensions.find((_, idx) => determined[idx])), + switchMap((extension) => extension?.extension(data) ?? of(null)) + ) + : of(null); +} diff --git a/src/app/shared/components/thrift-viewer/thrift-viewer.component.html b/src/app/shared/components/thrift-viewer/thrift-viewer.component.html index 0744d10a..4dc9a457 100644 --- a/src/app/shared/components/thrift-viewer/thrift-viewer.component.html +++ b/src/app/shared/components/thrift-viewer/thrift-viewer.component.html @@ -13,7 +13,14 @@ view_comfy_alt
- + implements OnChanges { @Input() value: T; @Input() compared?: T; + @Input() metadata: ThriftAstMetadata[]; + @Input() namespace: string; + @Input() type: ValueType; + @Input() extensions?: MetadataViewExtension[]; + @Output() changeKind = new EventEmitter(); valueFile$ = new ReplaySubject(1); @@ -31,10 +39,6 @@ export class ThriftViewerComponent implements OnChanges { return !!this.compared; } - get json() { - return objectToJSON(this.value); - } - ngOnChanges(changes: ComponentChanges>) { if (changes.value) { this.valueFile$.next(toMonacoFile(JSON.stringify(objectToJSON(this.value), null, 2))); diff --git a/src/app/shared/services/domain-metadata-form-extensions/domain-metadata-form-extensions.service.ts b/src/app/shared/services/domain-metadata-form-extensions/domain-metadata-form-extensions.service.ts index 9da671fd..1a135771 100644 --- a/src/app/shared/services/domain-metadata-form-extensions/domain-metadata-form-extensions.service.ts +++ b/src/app/shared/services/domain-metadata-form-extensions/domain-metadata-form-extensions.service.ts @@ -1,6 +1,5 @@ import { Injectable } from '@angular/core'; import { DomainObject, Cash } from '@vality/domain-proto/lib/domain'; -import { Field } from '@vality/thrift-ts'; import moment from 'moment'; import { from, Observable, of } from 'rxjs'; import { map, shareReplay } from 'rxjs/operators'; @@ -118,7 +117,7 @@ export class DomainMetadataFormExtensionsService { constructor(private domainStoreService: DomainStoreService) {} private createDomainObjectsOptions(metadata: ThriftAstMetadata[]): MetadataFormExtension[] { - const domainFields = new MetadataFormData( + const domainFields = new MetadataFormData( metadata, 'domain', 'DomainObject' @@ -137,7 +136,7 @@ export class DomainMetadataFormExtensionsService { objectType: string, objectKey: keyof DomainObject ): MetadataFormExtension { - const objectFields = new MetadataFormData(metadata, 'domain', objectType) + const objectFields = new MetadataFormData(metadata, 'domain', objectType) .ast; const refType = objectFields.find((n) => n.name === 'ref').type as string; return createDomainObjectExtension(refType, () => diff --git a/src/app/shared/services/domain-metadata-view-extensions/domain-metadata-view-extensions.service.ts b/src/app/shared/services/domain-metadata-view-extensions/domain-metadata-view-extensions.service.ts new file mode 100644 index 00000000..8d5e7f85 --- /dev/null +++ b/src/app/shared/services/domain-metadata-view-extensions/domain-metadata-view-extensions.service.ts @@ -0,0 +1,33 @@ +import { formatDate } from '@angular/common'; +import { Injectable } from '@angular/core'; +import { CategoryRef } from '@vality/domain-proto'; +import { Timestamp } from '@vality/domain-proto/lib/base'; +import { of, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { DominantCacheService } from '@cc/app/api/dominant-cache'; +import { MetadataViewExtension } from '@cc/app/shared/components/json-viewer'; +import { isTypeWithAliases } from '@cc/app/shared/components/metadata-form'; + +@Injectable({ + providedIn: 'root', +}) +export class DomainMetadataViewExtensionsService { + extensions$: Observable = of([ + { + determinant: (data) => of(isTypeWithAliases(data, 'CategoryRef', 'domain')), + extension: (data, value: CategoryRef) => + this.dominantCacheService.GetCategories().pipe( + map((categories) => categories.find((c) => c.ref === String(value.id))), + map((category) => ({ value: category.name, tooltip: category })) + ), + }, + { + determinant: (data) => of(isTypeWithAliases(data, 'Timestamp', 'base')), + extension: (data, value: Timestamp) => + of({ value: formatDate(value, 'dd.MM.yyyy HH:mm:ss', 'en') }), + }, + ]); + + constructor(private dominantCacheService: DominantCacheService) {} +} diff --git a/src/app/shared/services/domain-metadata-view-extensions/index.ts b/src/app/shared/services/domain-metadata-view-extensions/index.ts new file mode 100644 index 00000000..f09d7fe7 --- /dev/null +++ b/src/app/shared/services/domain-metadata-view-extensions/index.ts @@ -0,0 +1 @@ +export * from './domain-metadata-view-extensions.service'; diff --git a/src/components/details-item/details-item.component.html b/src/components/details-item/details-item.component.html index 7f4b351f..d05c7e17 100644 --- a/src/components/details-item/details-item.component.html +++ b/src/components/details-item/details-item.component.html @@ -1,11 +1,11 @@
{{ title }}
-
- -
-
+
hide_source
+ + +
diff --git a/src/components/details-item/details-item.component.ts b/src/components/details-item/details-item.component.ts index 1fdab3dd..6ec254df 100644 --- a/src/components/details-item/details-item.component.ts +++ b/src/components/details-item/details-item.component.ts @@ -1,41 +1,12 @@ -import { ContentObserver } from '@angular/cdk/observers'; -import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; -import { defer, ReplaySubject, switchMap } from 'rxjs'; -import { delay, distinctUntilChanged, map, share, startWith } from 'rxjs/operators'; +import { Component, Input } from '@angular/core'; +import { coerceBoolean } from 'coerce-property'; @Component({ selector: 'cc-details-item', templateUrl: 'details-item.component.html', styleUrls: ['details-item.component.scss'], }) -export class DetailsItemComponent implements AfterViewInit { +export class DetailsItemComponent { @Input() title: string; - - @ViewChild('content') contentElementRef: ElementRef; - - isEmpty$ = defer(() => this.viewInit$).pipe( - switchMap(() => - this.contentObserver.observe(this.contentElementRef.nativeElement).pipe(startWith(null)) - ), - map(() => this.getIsEmpty()), - distinctUntilChanged(), - delay(0), - share() - ); - - private viewInit$ = new ReplaySubject(1); - - constructor(private contentObserver: ContentObserver) {} - - ngAfterViewInit() { - this.viewInit$.next(); - } - - private getIsEmpty() { - return !Array.from(this.contentElementRef.nativeElement.childNodes).find( - (n) => - n.nodeType !== Node.COMMENT_NODE || - (n.nodeType === Node.TEXT_NODE && n.nodeValue.trim()) - ); - } + @Input() @coerceBoolean empty: boolean; }