diff --git a/src/app/pages/assets/asset-viewer/asset-viewer.component.html b/src/app/pages/assets/asset-viewer/asset-viewer.component.html index e3929cd..5ef543f 100644 --- a/src/app/pages/assets/asset-viewer/asset-viewer.component.html +++ b/src/app/pages/assets/asset-viewer/asset-viewer.component.html @@ -34,6 +34,9 @@ + + info View asset + delete_sweep Delete diff --git a/src/app/pages/assets/asset-viewer/asset-viewer.component.scss b/src/app/pages/assets/asset-viewer/asset-viewer.component.scss index bf22795..dc57dfe 100644 --- a/src/app/pages/assets/asset-viewer/asset-viewer.component.scss +++ b/src/app/pages/assets/asset-viewer/asset-viewer.component.scss @@ -30,3 +30,7 @@ mat-paginator { align-items: center; gap: 10px; /* Adjust the gap between elements */ } + +.card-actions button{ + margin-right: 10px; +} diff --git a/src/app/pages/assets/asset-viewer/asset-viewer.component.ts b/src/app/pages/assets/asset-viewer/asset-viewer.component.ts index 11ff131..793da0d 100644 --- a/src/app/pages/assets/asset-viewer/asset-viewer.component.ts +++ b/src/app/pages/assets/asset-viewer/asset-viewer.component.ts @@ -7,9 +7,11 @@ import { AssetService } from "../../../shared/services/asset.service"; import { AssetEditorDialog } from "../asset-editor-dialog/asset-editor-dialog.component"; import { ConfirmationDialogComponent, ConfirmDialogModel } from "../../../shared/components/confirmation-dialog/confirmation-dialog.component"; import { NotificationService } from "../../../shared/services/notification.service"; -import { DATA_ADDRESS_TYPES } from 'src/app/shared/utils/app.constants'; +import { CONTEXTS, DATA_ADDRESS_TYPES } from 'src/app/shared/utils/app.constants'; import { PageEvent } from '@angular/material/paginator'; -import { QuerySpec } from '@think-it-labs/edc-connector-client'; +import { EDC_CONTEXT, QuerySpec, DataAddress } from '@think-it-labs/edc-connector-client'; +import { ContractOffersViewerComponent } from '../../catalog/contract-offers-viewer/contract-offers-viewer.component'; +import { compact } from 'jsonld'; @Component({ selector: 'app-asset-viewer', @@ -28,6 +30,14 @@ export class AssetViewerComponent implements OnInit { currentPage = 0; paginatorLength = 0; + CONTEXT = { + "@vocab": EDC_CONTEXT, + "description": "http://purl.org/dc/terms/description", + "format": "http://purl.org/dc/terms/format", + "byteSize": "http://www.w3.org/ns/dcat#byteSize", + "keywords": "http://www.w3.org/ns/dcat#keyword" + } + constructor(private assetService: AssetService, private notificationService: NotificationService, private readonly dialog: MatDialog) { @@ -68,6 +78,25 @@ export class AssetViewerComponent implements OnInit { }); } + viewAsset(asset: Asset) { + compact(asset, this.CONTEXT).then(compactedAsset => { + this.dialog.open(ContractOffersViewerComponent, { + data: { + assetId: compactedAsset['@id'], + properties: compactedAsset.properties, + privateProperties: compactedAsset.privateProperties, + dataAddress: compactedAsset.dataAddress, + isCatalogView: false + }, + disableClose: true + }); + }) + .catch(error => { + this.notificationService.showError("Error compacting JsonLD"); + }); + + } + onCreate() { const dialogRef = this.dialog.open(AssetEditorDialog, { disableClose: true }); dialogRef.afterClosed().pipe(first()).subscribe((result: { assetInput?: AssetInput }) => { diff --git a/src/app/pages/catalog/catalog-browser/catalog-browser.component.ts b/src/app/pages/catalog/catalog-browser/catalog-browser.component.ts index 7381793..9ebf8c0 100644 --- a/src/app/pages/catalog/catalog-browser/catalog-browser.component.ts +++ b/src/app/pages/catalog/catalog-browser/catalog-browser.component.ts @@ -39,7 +39,8 @@ export class CatalogBrowserComponent implements OnInit { assetId: assetId, contractOffers: contractOffers, endpointUrl: endpointUrl, - properties: properties + properties: properties, + isCatalogView: true }, }); } diff --git a/src/app/pages/catalog/contract-offers-viewer/contract-offers-viewer.component.html b/src/app/pages/catalog/contract-offers-viewer/contract-offers-viewer.component.html index 491a30c..9448cdb 100644 --- a/src/app/pages/catalog/contract-offers-viewer/contract-offers-viewer.component.html +++ b/src/app/pages/catalog/contract-offers-viewer/contract-offers-viewer.component.html @@ -1,51 +1,187 @@ -Contract Offers - {{data.assetId}} + + + + + + + + + Id + {{data.properties.id}} + + + Name + {{data.properties.name}} + + + + + Content-Type + {{data.properties.contentType ? data.properties.contentType : 'Not available'}} + + + Version + {{data.properties.version}} + + + + + Short description + {{data.properties.shortDescription}} + + + + + Description + + + + + + Asset type + {{data.properties.assetType}} + + + Keywords + {{data.properties.keywords}} + + + + + Byte size + {{data.properties.byteSize ? data.properties.byteSize : 'Not available'}} + + + File format + {{data.properties.fileFormat ? data.properties.fileFormat : 'Not available'}} + + + + + + + + + + + + {{item.key}} + + + + + + + {{ subEntry.key }}: {{ + subEntry.value }} + + + + + + + + + {{ item.key }}: + + + + {{ subItem }} + + + + + + + + + + {{item.key}} + + + + {{ subEntry.key }}: {{ + subEntry.value }} + + + + + + + {{ item.key }}: {{ item.value + }} + + + + + + + + + + + + Storage type + {{ dataAddressType }} + + + + + + File + {{ data.dataAddress.keyName }} + + + + + + + + + {{ property.key }} + {{ property.value }} + + + + + + + + + + + + + + + {{ getJsonPolicy(contractOffer) | json }} + + + + drive_file_rename_outline + Negotiate Contract + + + + + + + + + + + - - - - - Properties - - - - numbers - Version - {{data.properties.version}} - - - content_paste - Content-Type - {{data.properties.contentType}} - - - - - - - Contract offer - - - {{ getJsonPolicy(contractOffer) | json }} - - - - drive_file_rename_outline - Negotiate Contract - - - - - - - - - - - - - cancel - close - - + + + cancel + close + + diff --git a/src/app/pages/catalog/contract-offers-viewer/contract-offers-viewer.component.scss b/src/app/pages/catalog/contract-offers-viewer/contract-offers-viewer.component.scss index 3617f98..8db72b0 100644 --- a/src/app/pages/catalog/contract-offers-viewer/contract-offers-viewer.component.scss +++ b/src/app/pages/catalog/contract-offers-viewer/contract-offers-viewer.component.scss @@ -18,6 +18,7 @@ mat-list-item:hover .property-title, .property-value{ font-size: 14px; + color: #adadad; } .property-icon { @@ -35,3 +36,99 @@ mat-list-item:hover .property-title, .dialog-card { width: 750px; } + +:host ::ng-deep .grey .detail-item { + background: #474747; + padding: 1rem 1.25rem; + border-radius: 16px; +} + +.details { + display: flex; + flex-wrap: wrap; + gap: 20px; +} + +.detail-item { + flex: 1 1 calc((100% / 2) - 20px); + min-width: calc((100% / 2) - 20px); + box-sizing: border-box; +} + +@media (max-width: 480px) { + .detail-item { + flex: 1 1 100%; + min-width: 100%; + } +} + +.detail-title { + font-family: "Baloo 2", Helvetica, Arial, sans-serif; + font-weight: bold; + color: #333; + display: block; + + &-inline { + font-family: "Baloo 2", Helvetica, Arial, sans-serif; + font-weight: bold; + color: #333; + display: inline; + } +} + +.detail-value { + font-size: 14px; + color: #555; + display: block; + + &-inline { + font-size: 14px; + color: #555; + display: inline; + } +} + +ul { + margin: 0px; +} + +.property-value.description{ + white-space: normal; +} + +.mdc-card.vocabulary{ + padding: 20px 20px 0px 20px; +} + +.mdc-card:not(.vocabulary){ + padding: 20px; +} + +.property-value{ + overflow-wrap: anywhere; + white-space: pre-wrap; +} + +.grey{ + margin-bottom: 15px; +} + +mat-expansion-panel-header{ + font-size: 16px; +} + +.contract-offer{ + margin-bottom: 20px; +} + +.value-not-available{ + font-style: italic; +} + +:host ::ng-deep td { + border: 1px solid white; +} + +:host ::ng-deep a { + color: white; +} diff --git a/src/app/pages/catalog/contract-offers-viewer/contract-offers-viewer.component.ts b/src/app/pages/catalog/contract-offers-viewer/contract-offers-viewer.component.ts index 98f58f2..7fa7021 100644 --- a/src/app/pages/catalog/contract-offers-viewer/contract-offers-viewer.component.ts +++ b/src/app/pages/catalog/contract-offers-viewer/contract-offers-viewer.component.ts @@ -5,13 +5,16 @@ import { NegotiationResult } from "../../../shared/models/negotiation-result"; import { ContractNegotiation, ContractNegotiationRequest, Policy } from "../../../shared/models/edc-connector-entities"; import { CatalogBrowserService } from 'src/app/shared/services/catalog-browser.service'; import { NotificationService } from 'src/app/shared/services/notification.service'; -import { Router } from '@angular/router'; +import { StorageType } from 'src/app/shared/models/storage-type'; export interface ContractOffersDialogData { assetId: string; - contractOffers: Policy[]; - endpointUrl: string; + contractOffers?: Policy[]; + endpointUrl?: string; properties: any; + privateProperties: any; + dataAddress?: any; + isCatalogView: boolean; } interface RunningTransferProcess { @@ -30,12 +33,74 @@ export class ContractOffersViewerComponent { runningTransferProcesses: RunningTransferProcess[] = []; runningNegotiations: Map = new Map(); finishedNegotiations: Map = new Map(); + assetDataKeys: string[]; + assetDataEntries: { [key: string]: any[] } = {}; + dataAddressType: string; private pollingHandleNegotiation?: any; constructor(@Inject(MAT_DIALOG_DATA) public data: ContractOffersDialogData, - private apiService: CatalogBrowserService, private notificationService: NotificationService, - private router: Router) { } + @Inject('STORAGE_TYPES') public storageTypes: StorageType[], + private apiService: CatalogBrowserService, private notificationService: NotificationService) { + this.assetDataKeys = Object.keys(data.properties.assetData); + this.processAssetData(); + + if(data.dataAddress) { + if(data.privateProperties) { + this.dataAddressType = 'InesDataStore'; + } else { + this.dataAddressType = this.getDataAddressName(data.dataAddress.type); + delete data.dataAddress['@type']; + } + + } + } + + getDataAddressName(dataAddressTypeId: string) { + const foundObject = this.storageTypes.find(item => item.id === dataAddressTypeId); + return foundObject ? foundObject.name : null; +} + + processAssetData() { + this.assetDataKeys = this.assetDataKeys.filter(key => { + const entries = this.getEntries(this.data.properties.assetData[key]); + + if (entries.length === 0) { + return false; + } + + this.assetDataEntries[key] = entries.map(item => ({ + key: item.key, + value: item.value, + isObject: this.isObject(item.value), + isArray: this.isArray(item.value), + entries: this.isObject(item.value) ? this.getEntries(item.value) : null + })); + + return true; + }); + } + + hasDetailedInformation() { + return this.data && this.data.properties && this.data.properties.assetData && + Object.keys(this.data.properties.assetData).length > 0; + } + + getEntries(obj: any): { key: string, value: any }[] { + return Object.entries(obj || {}).map(([key, value]) => ({ key, value })); + } + + isObject(value: any): boolean { + return value && typeof value === 'object' && !Array.isArray(value); + } + + isArray(value: any): boolean { + return Array.isArray(value); + } + + containsOnlyObjects(array: any[]): boolean { + return array.every(item => this.isObject(item)); + } isBusy(contractOffer: Policy) { return this.runningNegotiations.get(contractOffer["@id"]) !== undefined || !!this.runningTransferProcesses.find(tp => tp.assetId === contractOffer.assetId); @@ -83,8 +148,8 @@ export class ContractOffersViewerComponent { } checkActiveNegotiations() { - // there are no active negotiations - this.pollingHandleNegotiation = setInterval(() => { + // there are no active negotiations + this.pollingHandleNegotiation = setInterval(() => { const finishedNegotiationStates = [ "VERIFIED", diff --git a/src/app/shared/services/catalog-browser.service.ts b/src/app/shared/services/catalog-browser.service.ts index 9eb8598..7b7122f 100644 --- a/src/app/shared/services/catalog-browser.service.ts +++ b/src/app/shared/services/catalog-browser.service.ts @@ -138,12 +138,18 @@ export class CatalogBrowserService { for (const dataset of datasets) { const properties: { [key: string]: string; } = { - id: dataset["id"], - name: dataset["name"], - version: dataset["version"], - type: dataset["type"], - contentType: dataset["contenttype"] - } + id: dataset["id"], + name: dataset["name"], + version: dataset["version"], + assetType: dataset["assetType"], + contentType: dataset["contenttype"], + assetData: dataset["assetData"], + description: dataset["http://purl.org/dc/terms/description"], + shortDescription: dataset["shortDescription"], + byteSyze: dataset["http://www.w3.org/ns/dcat#byteSize"], + format: dataset["http://purl.org/dc/terms/format"], + keywords: dataset["http://www.w3.org/ns/dcat#keyword"] + } const assetId = dataset["@id"]; const hasPolicy = dataset["odrl:hasPolicy"];
{{ getJsonPolicy(contractOffer) | json }}