diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d14d6d98..69f605d5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ the detailed section referring to by linking pull requests or issues. - Removed 404-causing login polling from broker UI +- Migrated transfer history page to api wrapper + #### Removed #### Fixed diff --git a/fake-backend/json/transferHistoryAssetDetails.json b/fake-backend/json/transferHistoryAssetDetails.json new file mode 100644 index 000000000..1ee0d5755 --- /dev/null +++ b/fake-backend/json/transferHistoryAssetDetails.json @@ -0,0 +1,21 @@ +{ + "properties": { + "asset:prop:id": "urn:artifact:test-asset-1", + "asset:prop:name": "Test Asset 1", + "asset:prop:version": "1.1", + "asset:prop:originator": "https://example-connector.de/api/v1/ids/data", + "asset:prop:originatorOrganization": "Organization", + "asset:prop:keywords": "test, example", + "asset:prop:contenttype": "application/json", + "asset:prop:description": "Test asset for checking asset fetch in transfer history page", + "asset:prop:language": "https://w3id.org/idsa/code/EN", + "asset:prop:publisher": "https://my.cool-api.gg/about", + "asset:prop:standardLicense": "https://my.cool-api.gg/license", + "asset:prop:endpointDocumentation": "https://my.cool-api.gg/docs", + "http://w3id.org/mds#dataCategory": "Example", + "http://w3id.org/mds#dataSubcategory": "example-data", + "http://w3id.org/mds#dataModel": "data-model-001", + "http://w3id.org/mds#geoReferenceMethod": "example-reference-method", + "http://w3id.org/mds#transportMode": "Mode" + } +} diff --git a/fake-backend/json/transferHistoryPage.json b/fake-backend/json/transferHistoryPage.json new file mode 100644 index 000000000..c023acf9a --- /dev/null +++ b/fake-backend/json/transferHistoryPage.json @@ -0,0 +1,68 @@ +{ + "transferEntries": [ + { + "transferProcessId": "339b2a27-3b66-49f5-8b43-6a400d5914b5", + "createdDate": "2023-03-20T11:18:59.659Z", + "lastUpdatedDate": "2023-07-25T11:18:59.659Z", + "state": { + "code": 800, + "name": "COMPLETED", + "simplifiedState": "OK" + }, + "contractAgreementId": "test-asset-1-cd:f52a5d30-6356-4a55-a75a-3c45d7a88c3e", + "direction": "CONSUMING", + "counterPartyConnectorEndpoint": "https://sovity-demo4-mds/api/v1/ids/data", + "assetName": "urn:artifact:test-asset-1", + "assetId": "urn:artifact:test-asset-1", + "errorMessage": null + }, + { + "transferProcessId": "1317d0da-cdc6-42ab-b54b-1f90bcfed508", + "createdDate": "2023-01-20T11:18:59.659Z", + "lastUpdatedDate": "2023-03-25T11:18:59.659Z", + "state": { + "code": -1, + "name": "ERROR", + "simplifiedState": "ERROR" + }, + "contractAgreementId": "test-asset-2-cd:5816a60b-86c1-489a-b26a-ed129947f973", + "direction": "CONSUMING", + "counterPartyConnectorEndpoint": "http://edc2:11003/api/v1/ids/data", + "assetName": "urn:artifact:test-asset-2", + "assetId": "urn:artifact:test-asset-2", + "errorMessage": "TransferProcessManager: attempt #8 failed to send transfer. Retry limit exceeded, TransferProcess 1317d0da-cdc6-42ab-b54b-1f90bcfed508 moves to ERROR state. Cause: java.net.SocketException: Connection reset" + }, + { + "transferProcessId": "81cdf4cf-8427-480f-9662-8a29d66ddd3b", + "createdDate": "2022-03-25T11:18:59.659Z", + "lastUpdatedDate": "2022-11-20T11:18:59.659Z", + "state": { + "code": 800, + "name": "COMPLETED", + "simplifiedState": "OK" + }, + "contractAgreementId": "test-asset-3-cd:6ebbc301-9b1e-4cd7-9f17-97b5b7867531", + "direction": "CONSUMING", + "counterPartyConnectorEndpoint": "https://sovity-demo2-edc/api/v1/ids/data", + "assetName": "urn:artifact:test-asset-3", + "assetId": "urn:artifact:test-asset-3", + "errorMessage": null + }, + { + "transferProcessId": "47240a35-d8fc-41d9-b020-07b87f3cc7b6", + "createdDate": "2022-01-29T11:18:59.659Z", + "lastUpdatedDate": "2022-02-24T11:18:59.659Z", + "state": { + "code": 600, + "name": "IN_PROGRESS", + "simplifiedState": "RUNNING" + }, + "contractAgreementId": "test-asset-4-cd:f52a5d30-6356-4a55-a75a-3c45d7a88c3e", + "direction": "PROVIDING", + "counterPartyConnectorEndpoint": "https://sovity-demo2-edc/api/v1/ids/data", + "assetName": "Test Asset 4", + "assetId": "urn:artifact:test-asset-4", + "errorMessage": null + } + ] +} diff --git a/fake-backend/json/transferProcess.json b/fake-backend/json/transferProcess.json deleted file mode 100644 index a8dd205da..000000000 --- a/fake-backend/json/transferProcess.json +++ /dev/null @@ -1,128 +0,0 @@ -[ - { - "createdAt": 1675344948954, - "updatedAt": 1675344960579, - "id": "be0cac12-bb43-420e-aa29-d66bb3d0e0ac", - "type": "CONSUMER", - "state": "ERROR", - "stateTimestamp": 1675344960579, - "errorDetail": "TransferProcessManager: attempt #8 failed to send transfer. Retry limit exceeded, TransferProcess 1317d0da-cdc6-42ab-b54b-1f90bcfed508 moves to ERROR state. Cause: java.net.SocketException: Connection reset", - "dataRequest": { - "id": "c9fec9a2-1243-4738-9576-d70bf4558687", - "assetId": "urn:artifact:example-asset", - "contractId": "my-data-offer:5816a60b-86c1-489a-b26a-ed129947f973", - "connectorId": "consumer" - }, - "dataDestination": { - "properties": { - "baseUrl": "https://webhook.site/ab9ba683-77d3-4d66-9c79-03b9724927eb", - "type": "HttpData" - } - } - }, - { - "createdAt": 1675346050941, - "updatedAt": 1675346062120, - "id": "81cdf4cf-8427-480f-9662-8a29d66ddd3b", - "type": "CONSUMER", - "state": "COMPLETED", - "stateTimestamp": 1675346062120, - "errorDetail": null, - "dataRequest": { - "id": "92658c7a-9485-421f-a4be-ab186e7b190e", - "assetId": "urn:artifact:example-asset", - "contractId": "my-data-offer:5816a60b-86c1-489a-b26a-ed129947f973", - "connectorId": "consumer" - }, - "dataDestination": { - "properties": { - "baseUrl": "asd", - "type": "HttpData" - } - } - }, - { - "createdAt": 1675346295640, - "updatedAt": 1675346304201, - "id": "339b2a27-3b66-49f5-8b43-6a400d5914b5", - "type": "CONSUMER", - "state": "COMPLETED", - "stateTimestamp": 1675346304201, - "errorDetail": null, - "dataRequest": { - "id": "101eb0ff-8a1c-4879-a9d3-02a378a4f7c7", - "assetId": "urn:artifact:example-asset", - "contractId": "my-data-offer:5816a60b-86c1-489a-b26a-ed129947f973", - "connectorId": "consumer" - }, - "dataDestination": { - "properties": { - "baseUrl": "https://webhook.site/ab9ba683-77d3-4d66-9c79-03b9724927eb", - "type": "HttpData" - } - } - }, - { - "createdAt": 1675346968148, - "updatedAt": 1675346969520, - "id": "47240a35-d8fc-41d9-b020-07b87f3cc7b6", - "type": "PROVIDER", - "state": "IN_PROGRESS", - "stateTimestamp": 1675346969520, - "errorDetail": null, - "dataRequest": { - "id": "de881e9a-3ea8-4f05-9318-1aef45601581", - "assetId": "urn:artifact:pallene-asset", - "contractId": "pallene-asset-def:80de309c-0f0a-4eaf-a169-727c0ca6575b", - "connectorId": "urn:connector:edc" - }, - "dataDestination": { - "properties": { - "baseUrl": "https://webhook.site/ab9ba683-77d3-4d66-9c79-03b9724927eb", - "type": "HttpData" - } - } - }, - { - "createdAt": 1675346958929, - "updatedAt": 1675347017414, - "id": "8a31794f-d6be-449a-a037-eed8c63424df", - "type": "CONSUMER", - "state": "ERROR", - "stateTimestamp": 1675347017414, - "errorDetail": "TransferProcessManager: attempt #8 failed to send transfer. Retry limit exceeded, TransferProcess 8a31794f-d6be-449a-a037-eed8c63424df moves to ERROR state. Cause: org.eclipse.edc.spi.EdcException: Received class de.fraunhofer.iais.eis.RejectionMessageImpl but expected [class de.fraunhofer.iais.eis.RequestInProcessMessageImpl].", - "dataRequest": { - "id": "ba2dca23-ed46-496e-b121-1556958a7ec6", - "assetId": "urn:artifact:pallene-asset", - "contractId": "pallene-asset-def:80de309c-0f0a-4eaf-a169-727c0ca6575b", - "connectorId": "consumer" - }, - "dataDestination": { - "properties": { - "baseUrl": "https://webhook.site/ab9ba683-77d3-4d66-9c79-03b9724927eb", - "type": "HttpData" - } - } - }, - { - "createdAt": 1675347001981, - "updatedAt": 1675347005464, - "id": "d25328ce-bbf4-4623-991b-63b2b529f259", - "type": "PROVIDER", - "state": "IN_PROGRESS", - "stateTimestamp": 1675347005464, - "errorDetail": null, - "dataRequest": { - "id": "7cf62113-67e8-4e17-935c-14ba0e2592d6", - "assetId": "urn:artifact:pallene-asset", - "contractId": "pallene-asset-def:80de309c-0f0a-4eaf-a169-727c0ca6575b", - "connectorId": "urn:connector:edc" - }, - "dataDestination": { - "properties": { - "baseUrl": "https://webhook.site/ab9ba683-77d3-4d66-9c79-03b9724927eb", - "type": "HttpData" - } - } - } -] diff --git a/fake-backend/serve.js b/fake-backend/serve.js index 2b36f3441..8e7990dc3 100644 --- a/fake-backend/serve.js +++ b/fake-backend/serve.js @@ -53,11 +53,6 @@ app.get('/api/v1/data/contractdefinitions', (req, res) => { res.json(contractDefinitions); }); -const transferProcess = json('json/transferProcess.json'); -app.get('/api/v1/data/transferprocess', (req, res) => { - res.json(transferProcess); -}); - const contractAgreements = json('json/contractAgreements.json'); app.get('/api/v1/data/contractagreements', (req, res) => { res.json(contractAgreements); @@ -106,6 +101,21 @@ app.get('/api/v1/data/wrapper/ui/pages/contract-agreement-page', (_, res) => { res.json(contractAgreementPage); }); +const transferProcess = json('json/transferHistoryPage.json'); +app.get('/api/v1/data/wrapper/ui/pages/transfer-history-page', (req, res) => { + res.json(transferProcess); +}); + +const transferHistoryAssetDetails = json( + 'json/transferHistoryAssetDetails.json', +); +app.get( + '/api/v1/data/wrapper/ui/pages/transfer-history-page/transfer-processes/47240a35-d8fc-41d9-b020-07b87f3cc7b6/asset', + (req, res) => { + res.json(transferHistoryAssetDetails); + }, +); + // Connector Limits const connectorLimits = json('json/connectorLimits.json'); app.get('/api/v1/data/wrapper/ee/connector-limits', (_, res) => { diff --git a/src/app/app.component.html b/src/app/app.component.html index 0680b43f9..8f67d6fb5 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1 +1,12 @@ + + +
+ +
diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2a7043b76..cd65d149f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,6 +1,7 @@ import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http'; import {NgModule} from '@angular/core'; import {MatButtonModule} from '@angular/material/button'; +import {MatCardModule} from '@angular/material/card'; import {MAT_DATE_LOCALE, MatNativeDateModule} from '@angular/material/core'; import {MatDatepickerModule} from '@angular/material/datepicker'; import {MatDialogModule} from '@angular/material/dialog'; @@ -37,6 +38,7 @@ import { // Angular Material MatButtonModule, + MatCardModule, MatDatepickerModule, MatDialogModule, MatIconModule, diff --git a/src/app/component-library/catalog/asset-detail-dialog/asset-detail-dialog.service.ts b/src/app/component-library/catalog/asset-detail-dialog/asset-detail-dialog.service.ts new file mode 100644 index 000000000..b77e88687 --- /dev/null +++ b/src/app/component-library/catalog/asset-detail-dialog/asset-detail-dialog.service.ts @@ -0,0 +1,29 @@ +import {Injectable} from '@angular/core'; +import {MatDialog} from '@angular/material/dialog'; +import {NEVER, Observable} from 'rxjs'; +import {showDialogUntil} from '../../../core/utils/mat-dialog-utils'; +import {AssetDetailDialogData} from './asset-detail-dialog-data'; +import {AssetDetailDialogResult} from './asset-detail-dialog-result'; +import {AssetDetailDialogComponent} from './asset-detail-dialog.component'; + +@Injectable() +export class AssetDetailDialogService { + constructor(private dialog: MatDialog) {} + + /** + * Shows an Asset Detail Dialog until until$ emits / completes + * @param data Asset Detail Dialog data, or a stream if there's a need to refresh the data + * @param until$ observable that controls the lifetime of the dialog + */ + open( + data: AssetDetailDialogData | Observable, + until$: Observable = NEVER, + ): Observable { + return showDialogUntil( + this.dialog, + AssetDetailDialogComponent, + {data, maxHeight: '90vh'}, + until$, + ); + } +} diff --git a/src/app/component-library/catalog/catalog.module.ts b/src/app/component-library/catalog/catalog.module.ts index 987959124..0a94bc9a5 100644 --- a/src/app/component-library/catalog/catalog.module.ts +++ b/src/app/component-library/catalog/catalog.module.ts @@ -13,6 +13,7 @@ import {PropertyGridModule} from '../property-grid/property-grid.module'; import {UiElementsModule} from '../ui-elements/ui-elements.module'; import {AssetDetailDialogDataService} from './asset-detail-dialog/asset-detail-dialog-data.service'; import {AssetDetailDialogComponent} from './asset-detail-dialog/asset-detail-dialog.component'; +import {AssetDetailDialogService} from './asset-detail-dialog/asset-detail-dialog.service'; import {AssetPropertyGridGroupBuilder} from './asset-detail-dialog/asset-property-grid-group-builder'; import {ContractOfferCardsComponent} from './contract-offer-cards/contract-offer-cards.component'; import {ContractOfferIconComponent} from './contract-offer-icon/contract-offer-icon.component'; @@ -53,6 +54,10 @@ import {TransferHistoryMiniListComponent} from './transfer-history-mini-list/tra TransferHistoryMiniListComponent, IconWithOnlineStatusComponent, ], - providers: [AssetPropertyGridGroupBuilder, AssetDetailDialogDataService], + providers: [ + AssetPropertyGridGroupBuilder, + AssetDetailDialogDataService, + AssetDetailDialogService, + ], }) export class CatalogModule {} diff --git a/src/app/component-library/json-dialog/json-dialog.module.ts b/src/app/component-library/json-dialog/json-dialog.module.ts index ebe434722..d0d7e19f5 100644 --- a/src/app/component-library/json-dialog/json-dialog.module.ts +++ b/src/app/component-library/json-dialog/json-dialog.module.ts @@ -2,6 +2,7 @@ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {MatButtonModule} from '@angular/material/button'; +import {MatCardModule} from '@angular/material/card'; import {MatCheckboxModule} from '@angular/material/checkbox'; import {MatDialogModule} from '@angular/material/dialog'; import {MatIconModule} from '@angular/material/icon'; @@ -10,6 +11,7 @@ import {NgxJsonViewerModule} from 'ngx-json-viewer'; import {ConfirmationDialogModule} from '../confirmation-dialog/confirmation-dialog.module'; import {PipesAndDirectivesModule} from '../pipes-and-directives/pipes-and-directives.module'; import {JsonDialogComponent} from './json-dialog/json-dialog.component'; +import {JsonDialogService} from './json-dialog/json-dialog.service'; @NgModule({ imports: [ @@ -19,6 +21,7 @@ import {JsonDialogComponent} from './json-dialog/json-dialog.component'; // Angular Material MatButtonModule, + MatCardModule, MatCheckboxModule, MatDialogModule, MatIconModule, @@ -32,6 +35,7 @@ import {JsonDialogComponent} from './json-dialog/json-dialog.component'; ConfirmationDialogModule, ], declarations: [JsonDialogComponent], + providers: [JsonDialogService], exports: [JsonDialogComponent], }) export class JsonDialogModule {} diff --git a/src/app/component-library/json-dialog/json-dialog/json-dialog.service.ts b/src/app/component-library/json-dialog/json-dialog/json-dialog.service.ts new file mode 100644 index 000000000..a69767db3 --- /dev/null +++ b/src/app/component-library/json-dialog/json-dialog/json-dialog.service.ts @@ -0,0 +1,23 @@ +import {Injectable} from '@angular/core'; +import {MatDialog} from '@angular/material/dialog'; +import {NEVER, Observable} from 'rxjs'; +import {showDialogUntil} from '../../../core/utils/mat-dialog-utils'; +import {JsonDialogComponent} from './json-dialog.component'; +import {JsonDialogData} from './json-dialog.data'; + +@Injectable() +export class JsonDialogService { + constructor(private dialog: MatDialog) {} + + /** + * Shows JSON Detail Dialog until until$ emits / completes + * @param data json detail dialog data + * @param until$ observable that controls the lifetime of the dialog + */ + showJsonDetailDialog( + data: JsonDialogData, + until$: Observable = NEVER, + ): Observable { + return showDialogUntil(this.dialog, JsonDialogComponent, {data}, until$); + } +} diff --git a/src/app/component-library/ui-elements/ago/date-input.ts b/src/app/component-library/ui-elements/ago/date-input.ts new file mode 100644 index 000000000..83c854788 --- /dev/null +++ b/src/app/component-library/ui-elements/ago/date-input.ts @@ -0,0 +1 @@ +export type DateInput = Date | string diff --git a/src/app/component-library/ui-elements/ago/formatDateAgo.ts b/src/app/component-library/ui-elements/ago/formatDateAgo.ts index cc9de19f5..99209c52c 100644 --- a/src/app/component-library/ui-elements/ago/formatDateAgo.ts +++ b/src/app/component-library/ui-elements/ago/formatDateAgo.ts @@ -1,13 +1,16 @@ import {formatDistanceToNow} from 'date-fns'; +import {DateInput} from './date-input'; /** * Formats date as "{n} {timeUnit} ago" or "in {n} {timeUnit}s". * @param date date */ -export function formatDateAgo(date?: Date | null): string { +export function formatDateAgo(date?: DateInput | null): string { if (!date) { - return ''; + return 'never'; + } + if (typeof date === 'string') { + date = new Date(date); } - return formatDistanceToNow(date, {addSuffix: true}); } diff --git a/src/app/core/services/api/edc-api.service.ts b/src/app/core/services/api/edc-api.service.ts index 38bc9d3c2..63b33b753 100644 --- a/src/app/core/services/api/edc-api.service.ts +++ b/src/app/core/services/api/edc-api.service.ts @@ -1,6 +1,7 @@ import {Inject, Injectable} from '@angular/core'; import {Observable, from} from 'rxjs'; import { + AssetDto, AssetPage, ConnectorLimits, ContractAgreementPage, @@ -8,6 +9,7 @@ import { EdcClient, IdResponse, IdResponseDto, + TransferHistoryPage, buildEdcClient, } from '@sovity.de/edc-client'; import {AssetCreateRequest} from '@sovity.de/edc-client/dist/generated/models/AssetCreateRequest'; @@ -34,9 +36,7 @@ export class EdcApiService { return from(this.edcClient.uiApi.assetPage()); } - deleteAsset( - assetId: string, - ): Observable { + deleteAsset(assetId: string): Observable { return from(this.edcClient.uiApi.deleteAsset({assetId})); } @@ -52,6 +52,16 @@ export class EdcApiService { ); } + getTransferHistoryPage(): Observable { + return from(this.edcClient.uiApi.transferHistoryPageEndpoint()); + } + + getTransferProcessAsset(transferProcessId: string): Observable { + return from( + this.edcClient.uiApi.getTransferProcessAsset({transferProcessId}), + ); + } + getEnterpriseEditionConnectorLimits(): Observable { return from(this.edcClient.enterpriseEditionApi.connectorLimits()); } diff --git a/src/app/core/services/api/legacy-managent-api-client/api/transferProcess.service.ts b/src/app/core/services/api/legacy-managent-api-client/api/transferProcess.service.ts index 250d7e3d6..3a6bc83f4 100644 --- a/src/app/core/services/api/legacy-managent-api-client/api/transferProcess.service.ts +++ b/src/app/core/services/api/legacy-managent-api-client/api/transferProcess.service.ts @@ -24,13 +24,13 @@ import { } from '@angular/common/http'; import {Inject, Injectable, Optional} from '@angular/core'; import {Observable} from 'rxjs'; +// @ts-ignore +import {TransferProcessDto} from '../../../../core/services/models/transfer-history-entry'; import {Configuration} from '../configuration'; import {CustomHttpParameterCodec} from '../encoder'; // @ts-ignore import {TransferId} from '../model/transferId'; // @ts-ignore -import {TransferProcessDto} from '../model/transferProcessDto'; -// @ts-ignore import {TransferRequestDto} from '../model/transferRequestDto'; // @ts-ignore import {TransferState} from '../model/transferState'; diff --git a/src/app/core/utils/mat-dialog-utils.ts b/src/app/core/utils/mat-dialog-utils.ts new file mode 100644 index 000000000..a7d8968c5 --- /dev/null +++ b/src/app/core/utils/mat-dialog-utils.ts @@ -0,0 +1,26 @@ +import {ComponentType} from '@angular/cdk/portal'; +import {MatDialog, MatDialogConfig} from '@angular/material/dialog'; +import {Observable} from 'rxjs'; + +/** + * Method for launching Angular Material Dialogs with the lifetime of the dialog being handled by a until$ observable + * + * @param dialogService MatDialog + * @param dialog ComponentType + * @param config MatDialogConfig + * @param until$ Observable that controls the lifetime of the dialog + * @return afterClosed Observable + */ +export function showDialogUntil( + dialogService: MatDialog, + dialog: ComponentType, + config: MatDialogConfig, + until$: Observable, +): Observable { + let ref = dialogService.open(dialog, config); + until$.subscribe({ + next: () => ref.close(), + complete: () => ref.close(), + }); + return ref.afterClosed(); +} diff --git a/src/app/routes/broker-ui/catalog-page/catalog-page/catalog-page.component.ts b/src/app/routes/broker-ui/catalog-page/catalog-page/catalog-page.component.ts index ddf10861b..4e8426fc3 100644 --- a/src/app/routes/broker-ui/catalog-page/catalog-page/catalog-page.component.ts +++ b/src/app/routes/broker-ui/catalog-page/catalog-page/catalog-page.component.ts @@ -1,14 +1,12 @@ import {Component, HostBinding, OnDestroy, OnInit} from '@angular/core'; import {FormControl} from '@angular/forms'; -import {MatDialog} from '@angular/material/dialog'; import {PageEvent} from '@angular/material/paginator'; import {BehaviorSubject, Subject} from 'rxjs'; -import {map, takeUntil} from 'rxjs/operators'; +import {filter, map, takeUntil} from 'rxjs/operators'; import {Store} from '@ngxs/store'; import {CatalogPageSortingItem} from '@sovity.de/broker-server-client'; import {AssetDetailDialogDataService} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog-data.service'; -import {AssetDetailDialogResult} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog-result'; -import {AssetDetailDialogComponent} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog.component'; +import {AssetDetailDialogService} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog.service'; import {FilterValueSelectItem} from '../filter-value-select/filter-value-select-item'; import {FilterValueSelectVisibleState} from '../filter-value-select/filter-value-select-visible-state'; import {CatalogActiveFilterPill} from '../state/catalog-active-filter-pill'; @@ -35,7 +33,7 @@ export class CatalogPageComponent implements OnInit, OnDestroy { constructor( private assetDetailDialogDataService: AssetDetailDialogDataService, - private matDialog: MatDialog, + private assetDetailDialogService: AssetDetailDialogService, private store: Store, ) {} @@ -84,12 +82,11 @@ export class CatalogPageComponent implements OnInit, OnDestroy { onDataOfferClick(dataOffer: BrokerDataOffer) { const data = this.assetDetailDialogDataService.brokerDataOfferDetails(dataOffer); - const ref = this.matDialog.open(AssetDetailDialogComponent, {data}); - ref.afterClosed().subscribe((result: AssetDetailDialogResult) => { - if (result?.refreshList) { - this.fetch$.next(null); - } - }); + + this.assetDetailDialogService + .open(data, this.ngOnDestroy$) + .pipe(filter((it) => !!it?.refreshList)) + .subscribe(() => this.fetch$.next(null)); } ngOnDestroy$ = new Subject(); diff --git a/src/app/routes/connector-ui/asset-page/asset-page/asset-page.component.ts b/src/app/routes/connector-ui/asset-page/asset-page/asset-page.component.ts index a0d0483b4..6cbecece7 100644 --- a/src/app/routes/connector-ui/asset-page/asset-page/asset-page.component.ts +++ b/src/app/routes/connector-ui/asset-page/asset-page/asset-page.component.ts @@ -1,10 +1,9 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnDestroy, OnInit} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; -import {BehaviorSubject} from 'rxjs'; -import {map, switchMap} from 'rxjs/operators'; +import {BehaviorSubject, Subject} from 'rxjs'; +import {filter, map, switchMap} from 'rxjs/operators'; import {AssetDetailDialogDataService} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog-data.service'; -import {AssetDetailDialogResult} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog-result'; -import {AssetDetailDialogComponent} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog.component'; +import {AssetDetailDialogService} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog.service'; import {EdcApiService} from '../../../../core/services/api/edc-api.service'; import {AssetPropertyMapper} from '../../../../core/services/asset-property-mapper'; import {Asset} from '../../../../core/services/models/asset'; @@ -22,7 +21,7 @@ export interface AssetList { templateUrl: './asset-page.component.html', styleUrls: ['./asset-page.component.scss'], }) -export class AssetPageComponent implements OnInit { +export class AssetPageComponent implements OnInit, OnDestroy { assetList: Fetched = Fetched.empty(); searchText = ''; private fetch$ = new BehaviorSubject(null); @@ -30,6 +29,7 @@ export class AssetPageComponent implements OnInit { constructor( private edcApiService: EdcApiService, private assetDetailDialogDataService: AssetDetailDialogDataService, + private assetDetailDialogService: AssetDetailDialogService, private dialog: MatDialog, private assetPropertyMapper: AssetPropertyMapper, ) {} @@ -75,18 +75,20 @@ export class AssetPageComponent implements OnInit { onAssetClick(asset: Asset) { const data = this.assetDetailDialogDataService.assetDetails(asset, true); - const ref = this.dialog.open(AssetDetailDialogComponent, { - data, - maxHeight: '90vh', - }); - ref.afterClosed().subscribe((result: AssetDetailDialogResult) => { - if (result?.refreshList) { - this.refresh(); - } - }); + this.assetDetailDialogService + .open(data, this.ngOnDestroy$) + .pipe(filter((it) => !!it?.refreshList)) + .subscribe(() => this.refresh()); } private refresh() { this.fetch$.next(null); } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy() { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } } diff --git a/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.component.ts b/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.component.ts index 299f5118e..aff1fe0fd 100644 --- a/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.component.ts +++ b/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.component.ts @@ -8,10 +8,9 @@ import { distinctUntilChanged, sampleTime, } from 'rxjs'; -import {map} from 'rxjs/operators'; +import {filter, map} from 'rxjs/operators'; import {AssetDetailDialogDataService} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog-data.service'; -import {AssetDetailDialogResult} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog-result'; -import {AssetDetailDialogComponent} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog.component'; +import {AssetDetailDialogService} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog.service'; import {CatalogApiUrlService} from '../../../../core/services/api/catalog-api-url.service'; import {ContractOffer} from '../../../../core/services/models/contract-offer'; import {value$} from '../../../../core/utils/form-group-utils'; @@ -35,6 +34,7 @@ export class CatalogBrowserPageComponent implements OnInit, OnDestroy { constructor( private assetDetailDialogDataService: AssetDetailDialogDataService, + private assetDetailDialogService: AssetDetailDialogService, private catalogBrowserPageService: CatalogBrowserPageService, private catalogApiUrlService: CatalogApiUrlService, private matDialog: MatDialog, @@ -56,12 +56,10 @@ export class CatalogBrowserPageComponent implements OnInit, OnDestroy { onContractOfferClick(contractOffer: ContractOffer) { const data = this.assetDetailDialogDataService.contractOfferDetails(contractOffer); - const ref = this.matDialog.open(AssetDetailDialogComponent, {data}); - ref.afterClosed().subscribe((result: AssetDetailDialogResult) => { - if (result?.refreshList) { - this.fetch$.next(null); - } - }); + this.assetDetailDialogService + .open(data, this.ngOnDestroy$) + .pipe(filter((it) => !!it?.refreshList)) + .subscribe(() => this.fetch$.next(null)); } onShowFetchDetails() { diff --git a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.component.ts b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.component.ts index 0074d3756..c12b53356 100644 --- a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.component.ts +++ b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.component.ts @@ -1,6 +1,5 @@ import {Component, OnDestroy, OnInit} from '@angular/core'; import {FormControl} from '@angular/forms'; -import {MatDialog} from '@angular/material/dialog'; import { BehaviorSubject, Observable, @@ -12,8 +11,7 @@ import { } from 'rxjs'; import {filter, map, takeUntil} from 'rxjs/operators'; import {AssetDetailDialogDataService} from 'src/app/component-library/catalog/asset-detail-dialog/asset-detail-dialog-data.service'; -import {AssetDetailDialogData} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog-data'; -import {AssetDetailDialogComponent} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog.component'; +import {AssetDetailDialogService} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog.service'; import {Fetched} from '../../../../core/services/models/fetched'; import {value$} from '../../../../core/utils/form-group-utils'; import {filterNotNull} from '../../../../core/utils/rxjs-utils'; @@ -36,7 +34,7 @@ export class ContractAgreementPageComponent implements OnInit, OnDestroy { constructor( private assetDetailDialogDataService: AssetDetailDialogDataService, - private matDialog: MatDialog, + private assetDetailDialogService: AssetDetailDialogService, private contractAgreementPageService: ContractAgreementPageService, ) {} @@ -50,17 +48,13 @@ export class ContractAgreementPageComponent implements OnInit, OnDestroy { } onContractAgreementClick(card: ContractAgreementCardMapped) { - const data$: Observable = this.card$( - card.contractAgreementId, - ).pipe( - map((it) => - this.assetDetailDialogDataService.contractAgreementDetails(it), + const data$ = this.card$(card.contractAgreementId).pipe( + map((card) => + this.assetDetailDialogDataService.contractAgreementDetails(card), ), ); - this.matDialog.open(AssetDetailDialogComponent, { - data: data$, - maxHeight: '90vh', - }); + + return this.assetDetailDialogService.open(data$, this.ngOnDestroy$); } refresh() { diff --git a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.service.ts b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.service.ts index 2ac9782bc..313f34443 100644 --- a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.service.ts +++ b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.service.ts @@ -13,7 +13,6 @@ import {ContractAgreementCardMapped} from '../contract-agreement-cards/contract- import {ContractAgreementCardMappedService} from '../contract-agreement-cards/contract-agreement-card-mapped.service'; import {ContractAgreementPageData} from './contract-agreement-page.data'; - @Injectable({providedIn: 'root'}) export class ContractAgreementPageService { constructor( diff --git a/src/app/routes/connector-ui/contract-definition-page/asset-select/asset-select.component.ts b/src/app/routes/connector-ui/contract-definition-page/asset-select/asset-select.component.ts index 4f1cb8e9f..66ea5f4e6 100644 --- a/src/app/routes/connector-ui/contract-definition-page/asset-select/asset-select.component.ts +++ b/src/app/routes/connector-ui/contract-definition-page/asset-select/asset-select.component.ts @@ -1,15 +1,15 @@ -import {Component, Input} from '@angular/core'; +import {Component, Input, OnDestroy} from '@angular/core'; import {FormControl} from '@angular/forms'; -import {MatDialog} from '@angular/material/dialog'; +import {Subject} from 'rxjs'; import {AssetDetailDialogDataService} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog-data.service'; -import {AssetDetailDialogComponent} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog.component'; +import {AssetDetailDialogService} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog.service'; import {Asset} from '../../../../core/services/models/asset'; @Component({ selector: 'asset-select', templateUrl: './asset-select.component.html', }) -export class AssetSelectComponent { +export class AssetSelectComponent implements OnDestroy { @Input() label!: string; @@ -21,14 +21,18 @@ export class AssetSelectComponent { constructor( private assetDetailDialogDataService: AssetDetailDialogDataService, - private matDialog: MatDialog, + private assetDetailDialogService: AssetDetailDialogService, ) {} onAssetClick(asset: Asset) { const data = this.assetDetailDialogDataService.assetDetails(asset, false); - this.matDialog.open(AssetDetailDialogComponent, { - data, - maxHeight: '90vh', - }); + this.assetDetailDialogService.open(data, this.ngOnDestroy$); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy() { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); } } diff --git a/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-cards.component.ts b/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-cards.component.ts index 37eb25c0f..11d31fff6 100644 --- a/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-cards.component.ts +++ b/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-cards.component.ts @@ -3,14 +3,14 @@ import { EventEmitter, HostBinding, Input, + OnDestroy, Output, } from '@angular/core'; import {MatDialog, MatDialogRef} from '@angular/material/dialog'; -import {EMPTY} from 'rxjs'; -import {catchError, filter, map, tap} from 'rxjs/operators'; +import {EMPTY, Subject} from 'rxjs'; +import {catchError, filter, tap} from 'rxjs/operators'; import {AssetDetailDialogDataService} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog-data.service'; -import {AssetDetailDialogResult} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog-result'; -import {AssetDetailDialogComponent} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog.component'; +import {AssetDetailDialogService} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog.service'; import {ConfirmDialogModel} from '../../../../component-library/confirmation-dialog/confirmation-dialog/confirmation-dialog.component'; import {JsonDialogComponent} from '../../../../component-library/json-dialog/json-dialog/json-dialog.component'; import {JsonDialogData} from '../../../../component-library/json-dialog/json-dialog/json-dialog.data'; @@ -27,7 +27,7 @@ import {ContractDefinitionCard} from './contract-definition-card'; selector: 'contract-definition-cards', templateUrl: './contract-definition-cards.component.html', }) -export class ContractDefinitionCardsComponent { +export class ContractDefinitionCardsComponent implements OnDestroy { @HostBinding('class.flex') @HostBinding('class.flex-wrap') @HostBinding('class.gap-[10px]') @@ -44,6 +44,7 @@ export class ContractDefinitionCardsComponent { constructor( private assetDetailDialogDataService: AssetDetailDialogDataService, + private assetDetailDialogService: AssetDetailDialogService, private matDialog: MatDialog, private contractDefinitionService: ContractDefinitionService, private notificationService: NotificationService, @@ -61,16 +62,9 @@ export class ContractDefinitionCardsComponent { onAssetClick(asset: Asset) { const data = this.assetDetailDialogDataService.assetDetails(asset, false); - const ref = this.matDialog.open(AssetDetailDialogComponent, { - data, - maxHeight: '90vh', - }); - ref - .afterClosed() - .pipe( - map((it) => it as AssetDetailDialogResult | null), - filter((it) => !!it?.refreshList), - ) + this.assetDetailDialogService + .open(data, this.ngOnDestroy$) + .pipe(filter((it) => !!it?.refreshList)) .subscribe(() => this.deleteDone.emit()); } @@ -107,4 +101,11 @@ export class ContractDefinitionCardsComponent { dialogRef = this.matDialog.open(JsonDialogComponent, {data}); } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy() { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } } diff --git a/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page-data.service.ts b/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page-data.service.ts index 36c569bc3..a1cd01f8d 100644 --- a/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page-data.service.ts +++ b/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page-data.service.ts @@ -1,6 +1,7 @@ import {Injectable} from '@angular/core'; import {Observable, combineLatest, merge, of, scan} from 'rxjs'; import {map} from 'rxjs/operators'; +import {TransferHistoryEntry} from '@sovity.de/edc-client'; import {CatalogApiUrlService} from '../../../../core/services/api/catalog-api-url.service'; import {ContractOfferService} from '../../../../core/services/api/contract-offer.service'; import {EdcApiService} from '../../../../core/services/api/edc-api.service'; @@ -8,19 +9,16 @@ import { ContractAgreementService, ContractDefinitionService, PolicyService, - TransferProcessDto, - TransferProcessService, } from '../../../../core/services/api/legacy-managent-api-client'; import { ConnectorInfoPropertyGridGroupBuilder } from '../../../../core/services/connector-info-property-grid-group-builder'; import {LastCommitInfoService} from '../../../../core/services/last-commit-info.service'; import {Fetched} from '../../../../core/services/models/fetched'; -import {TransferProcessStates} from '../../../../core/services/models/transfer-process-states'; -import {TransferProcessUtils} from '../../../../core/services/transfer-process-utils'; import {DonutChartData} from '../dashboard-donut-chart/donut-chart-data'; import {DashboardPageData, defaultDashboardData} from './dashboard-page-data'; + @Injectable({providedIn: 'root'}) export class DashboardPageDataService { constructor( @@ -30,11 +28,10 @@ export class DashboardPageDataService { private contractAgreementService: ContractAgreementService, private policyService: PolicyService, private catalogApiUrlService: CatalogApiUrlService, - private transferProcessService: TransferProcessService, - private transferProcessUtils: TransferProcessUtils, private lastCommitInfoService: LastCommitInfoService, private connectorInfoPropertyGridGroupBuilder: ConnectorInfoPropertyGridGroupBuilder, - ) {} + ) { + } /** * Fetch {@link DashboardPageData}. @@ -120,37 +117,32 @@ export class DashboardPageDataService { } private transferProcessKpis(): Observable> { - return this.transferProcessService - .getAllTransferProcesses(0, 10_000_000) + return this.edcApiService.getTransferHistoryPage() .pipe( Fetched.wrap({ failureMessage: 'Failed fetching transfer processes.', }), map((transferData) => ({ incomingTransfersChart: transferData.map((it) => - this.buildTransferChart(it, 'incoming'), + this.buildTransferChart(it.transferEntries, 'CONSUMING'), ), outgoingTransfersChart: transferData.map((it) => - this.buildTransferChart(it, 'outgoing'), + this.buildTransferChart(it.transferEntries, 'PROVIDING'), ), })), ); } private buildTransferChart( - transfers: TransferProcessDto[], - direction: 'incoming' | 'outgoing', + transfers: TransferHistoryEntry[], + direction: 'CONSUMING' | 'PROVIDING', ): DonutChartData { const filteredTransfers = - direction === 'incoming' - ? transfers.filter((it) => this.transferProcessUtils.isIncoming(it)) - : transfers.filter((it) => this.transferProcessUtils.isOutgoing(it)); - - // Use the keys of the TransferProcessesStates Enum as order - const order = Object.keys(TransferProcessStates); - const states = [...new Set(filteredTransfers.map((it) => it.state))].sort( - (a, b) => order.indexOf(a) - order.indexOf(b), - ); + direction === 'CONSUMING' + ? transfers.filter((it) => it.direction === 'CONSUMING') + : transfers.filter((it) => it.direction === 'PROVIDING'); + + const states = [...new Set(filteredTransfers.sort((a, b) => a.state.code - b.state.code).map((it) => it.state.name))] const colorsByState = new Map(); colorsByState.set('IN_PROGRESS', '#7eb0d5'); @@ -159,7 +151,7 @@ export class DashboardPageDataService { const defaultColor = '#bd7ebe'; const amountsByState = states.map( - (state) => filteredTransfers.filter((it) => it.state === state).length, + (state) => filteredTransfers.filter((it) => it.state.name === state).length, ); return { diff --git a/src/app/routes/connector-ui/transfer-history-page/transfer-history-page.module.ts b/src/app/routes/connector-ui/transfer-history-page/transfer-history-page.module.ts index 94c9def77..070f0b122 100644 --- a/src/app/routes/connector-ui/transfer-history-page/transfer-history-page.module.ts +++ b/src/app/routes/connector-ui/transfer-history-page/transfer-history-page.module.ts @@ -2,10 +2,16 @@ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {MatButtonModule} from '@angular/material/button'; +import {MatCardModule} from '@angular/material/card'; +import {MatDialogModule} from '@angular/material/dialog'; import {MatIconModule} from '@angular/material/icon'; import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; import {MatTableModule} from '@angular/material/table'; +import {MatTooltipModule} from '@angular/material/tooltip'; import {RouterModule} from '@angular/router'; +import {CatalogModule} from '../../../component-library/catalog/catalog.module'; +import {JsonDialogModule} from '../../../component-library/json-dialog/json-dialog.module'; import {PipesAndDirectivesModule} from '../../../component-library/pipes-and-directives/pipes-and-directives.module'; import {UiElementsModule} from '../../../component-library/ui-elements/ui-elements.module'; import {TransferHistoryPageComponent} from './transfer-history-page/transfer-history-page.component'; @@ -20,11 +26,17 @@ import {TransferHistoryPageComponent} from './transfer-history-page/transfer-his // Angular Material MatButtonModule, - MatTableModule, - MatPaginatorModule, + MatCardModule, + MatDialogModule, MatIconModule, + MatPaginatorModule, + MatProgressSpinnerModule, + MatTableModule, + MatTooltipModule, // EDC UI Modules + CatalogModule, + JsonDialogModule, PipesAndDirectivesModule, UiElementsModule, ], diff --git a/src/app/routes/connector-ui/transfer-history-page/transfer-history-page/transfer-history-page.component.html b/src/app/routes/connector-ui/transfer-history-page/transfer-history-page/transfer-history-page.component.html index ca8f7cf08..173ad5af6 100644 --- a/src/app/routes/connector-ui/transfer-history-page/transfer-history-page/transfer-history-page.component.html +++ b/src/app/routes/connector-ui/transfer-history-page/transfer-history-page/transfer-history-page.component.html @@ -1,16 +1,15 @@
- - +
+
+ +
- - ID - {{ item.id }} - - - - Created - - {{ asDate(item.createdAt) }} + + + Direction + + + + {{ item.direction === 'PROVIDING' ? 'upload' : 'download' }} + - - - State - {{ item.state }} - - - Last updated - - {{ asDate(item.stateTimestamp) }} + + Last updated + + + - - - Connector ID + + Asset - {{ item.dataRequest.connectorId }} + +
+ {{ item.assetName }} +
- - - Asset ID - - {{ item.dataRequest.assetId }} + + + State + + +
+ + {{ item.state.name }} + {{ + item.state.name === 'CUSTOM' ? ' (' + item.state.code + ')' : '' + }} + + + warning + + + + +
- - - Contract ID + + + Counter-party Connector Endpoint + - {{ item.dataRequest.contractId }} + {{ item.counterPartyConnectorEndpoint }} - - Details - + + Details + + - ; + transferProcesses: Array; }> = Fetched.empty(); constructor( - private transferProcessService: TransferProcessService, - private dialog: MatDialog, + private edcApiService: EdcApiService, + private assetDetailDialogDataService: AssetDetailDialogDataService, + private assetDetailDialogService: AssetDetailDialogService, + private assetPropertyMapper: AssetPropertyMapper, + private notificationService: NotificationService, + private jsonDialogService: JsonDialogService, ) {} - onTransferHistoryDetailsClick(item: TransferProcessDto) { - const data: JsonDialogData = { - title: item.id, - subtitle: 'Transfer History Details', - icon: 'assignment', - objectForJson: item, - }; - this.dialog.open(JsonDialogComponent, {data}); + onTransferHistoryDetailsClick(item: TransferHistoryEntry) { + this.jsonDialogService.showJsonDetailDialog( + { + title: item.assetName ?? item.assetId, + subtitle: 'Transfer History Details', + icon: 'assignment', + objectForJson: item, + }, + this.ngOnDestroy$, + ); } - ngOnInit(): void { - this.loadTransferProcesses(); + loadAssetDetails(transferProcessId: string): Observable { + return this.edcApiService + .getTransferProcessAsset(transferProcessId) + .pipe( + map((asset) => + this.assetPropertyMapper.buildAssetFromProperties(asset.properties), + ), + ); } - onDeprovision(transferProcess: TransferProcessDto): void { - const dialogData = new ConfirmDialogModel( - 'Confirm deprovision', - `Deprovisioning resources for transfer [${transferProcess.id}] will take some time and once started, it cannot be stopped.`, - ); - dialogData.confirmColor = 'warn'; - dialogData.confirmText = 'Confirm'; - dialogData.cancelText = 'Abort'; - const ref = this.dialog.open(ConfirmationDialogComponent, { - maxWidth: '20%', - data: dialogData, + onAssetDetailsClick(transferProcessId: string) { + this.loadAssetDetails(transferProcessId).subscribe({ + next: (asset) => { + const data = this.assetDetailDialogDataService.assetDetails( + asset, + false, + ); + this.assetDetailDialogService.open(data, this.ngOnDestroy$); + }, + error: (error) => { + console.error('Failed to fetch asset details!', error); + this.notificationService.showError('Failed to fetch asset details!'); + }, }); + } - ref.afterClosed().subscribe((res) => { - if (res) { - this.transferProcessService - .deprovisionTransferProcess(transferProcess.id) - .subscribe(() => this.loadTransferProcesses()); - } - }); + ngOnInit(): void { + this.loadTransferProcesses(); } loadTransferProcesses() { - this.transferProcessService - .getAllTransferProcesses(0, 10_000_000) - .pipe( - map((transferProcesses) => ({ - transferProcesses: transferProcesses.sort(function (a, b) { - return ( - b.createdTimestamp?.valueOf()! - a.createdTimestamp?.valueOf()! - ); - }), - })), + const initialRequest: Observable> = + this.edcApiService.getTransferHistoryPage().pipe( Fetched.wrap({ failureMessage: 'Failed fetching transfer history.', }), + ); + + const polling: Observable> = interval( + 5_000, + ).pipe( + skip(1), + switchMap(() => + this.edcApiService + .getTransferHistoryPage() + .pipe(catchError(() => EMPTY)), + ), + map((data) => Fetched.ready(data)), + ); + + return concat(initialRequest, polling) + .pipe( + Fetched.map((transferHistoryPage) => ({ + transferProcesses: transferHistoryPage.transferEntries, + })), ) .subscribe( (transferProcessesList) => @@ -95,7 +120,10 @@ export class TransferHistoryPageComponent implements OnInit { ); } - asDate(epochMillis?: number) { - return epochMillis ? new Date(epochMillis).toLocaleDateString() : ''; + ngOnDestroy$ = new Subject(); + + ngOnDestroy() { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); } } diff --git a/src/styles.scss b/src/styles.scss index b8c120796..48c7cf76a 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -103,6 +103,8 @@ body { .link { color: var(--link-color); text-decoration: none; + display: flex; + align-items: center; &:hover, &:focus { @@ -145,6 +147,7 @@ body { } .mat-icon-\[14px\] { + margin-right: 5px; font-size: 14px !important; width: 14px !important; height: 14px !important; @@ -165,7 +168,16 @@ body { line-height: 12px !important; } +.mat-icon-\[18px\] { + margin-top: 5px; + font-size: 18px !important; + width: 18px !important; + height: 18px !important; + line-height: 18px !important; +} + .mat-icon-\[28px\] { + margin-top: 8px; font-size: 28px !important; width: 28px !important; height: 28px !important; @@ -189,6 +201,7 @@ body { padding-bottom: 0 !important; } } + .max-two-lines-list-item .mat-list-text { display: -webkit-box !important; -webkit-line-clamp: 2; diff --git a/src/theme-colors.mds.scss b/src/theme-colors.mds.scss index 94e074215..50c938a39 100644 --- a/src/theme-colors.mds.scss +++ b/src/theme-colors.mds.scss @@ -37,7 +37,7 @@ $theme-colors-primary: ( ), ); -$link-color: darken (map.get($theme-colors-primary, 500), 5%); +$link-color: darken (map.get($theme-colors-primary, 500), 15%); $theme-colors-accent: ( 50: #ffffff, diff --git a/src/theme.scss b/src/theme.scss index 7c00a9bad..c15940f0a 100644 --- a/src/theme.scss +++ b/src/theme.scss @@ -20,6 +20,7 @@ $custom-typography: mat.define-typography-config( sovityColors.$link-color ); } + .theme-mds { @include mat.all-component-themes(mdsColors.$theme); @include themeGeneratedVars.theme-vars(