From 58dd027e82844ef529b3598122dc4219052d5cdd Mon Sep 17 00:00:00 2001 From: illfixit <66363651+illfixit@users.noreply.github.com> Date: Mon, 5 Aug 2024 11:10:05 +0200 Subject: [PATCH] feat: leagacy create asset (part I) --- .../connector-fake-impl/asset-fake-service.ts | 1 + .../asset-data-source-mapper-legacy.ts | 103 ++ .../services/asset-request-builder-legacy.ts | 111 ++ .../asset-edit-dialog-data.ts | 5 + .../asset-edit-dialog-mode.ts | 1 + .../asset-edit-dialog-result.ts | 13 + .../asset-edit-dialog.component.html | 795 ++++++++++ .../asset-edit-dialog.component.ts | 125 ++ .../asset-edit-dialog.service.ts | 44 + .../assets-id-validator-builder.ts | 35 + .../form/asset-advanced-form-builder.ts | 55 + .../form/asset-datasource-form-builder.ts | 105 ++ .../form/asset-edit-dialog-form-mapper.ts | 107 ++ .../form/asset-edit-dialog-form.ts | 180 +++ .../form/asset-metadata-form-builder.ts | 94 ++ .../asset-edit-dialog/form/http-methods.ts | 11 + .../form/model/asset-advanced-form-model.ts | 36 + .../asset-datasource-form-enabled-ctrls.ts | 48 + .../form/model/asset-datasource-form-model.ts | 49 + .../model/asset-editor-dialog-form-model.ts | 21 + .../form/model/asset-metadata-form-model.ts | 23 + .../model/http-datasource-auth-header-type.ts | 1 + .../http-datasource-header-form-model.ts | 15 + .../http-datasource-query-param-form-model.ts | 15 + .../model/temporal-coverage-form-model.ts | 15 + .../asset-page/asset-page.module.ts | 22 +- .../asset-page/asset-page.component.html | 12 +- .../asset-page/asset-page.component.ts | 13 + .../data-category-select-data.ts | 48 + .../data-category-select-item.service.ts | 34 + .../data-category-select-item.ts | 4 + .../data-category-select.component.html | 9 + .../data-category-select.component.ts | 22 + .../data-subcategory-items.pipe.ts | 17 + .../data-subcategory-select-data.ts | 189 +++ .../data-subcategory-select-item.service.ts | 44 + .../data-subcategory-select-item.ts | 5 + .../data-subcategory-select.component.html | 11 + .../data-subcategory-select.component.ts | 23 + .../keyword-select.component.html | 22 + .../keyword-select.component.ts | 38 + .../language-select/language-select-data.ts | 1299 +++++++++++++++++ .../language-select-item.service.ts | 70 + .../language-select/language-select-item.ts | 7 + .../language-select.component.html | 16 + .../language-select.component.ts | 22 + .../transport-mode-select-data.ts | 23 + .../transport-mode-select-item.service.ts | 27 + .../transport-mode-select-item.ts | 4 + .../transport-mode-select.component.html | 9 + .../transport-mode-select.component.ts | 22 + 51 files changed, 4012 insertions(+), 8 deletions(-) create mode 100644 src/app/core/services/asset-data-source-mapper-legacy.ts create mode 100644 src/app/core/services/asset-request-builder-legacy.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog-data.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog-mode.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog-result.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog.component.html create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog.component.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog.service.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/assets-id-validator-builder.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-advanced-form-builder.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-datasource-form-builder.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-edit-dialog-form-mapper.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-edit-dialog-form.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-metadata-form-builder.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/http-methods.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-advanced-form-model.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-datasource-form-enabled-ctrls.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-datasource-form-model.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-editor-dialog-form-model.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-metadata-form-model.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/http-datasource-auth-header-type.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/http-datasource-header-form-model.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/http-datasource-query-param-form-model.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/temporal-coverage-form-model.ts create mode 100644 src/app/routes/connector-ui/asset-page/data-category-select/data-category-select-data.ts create mode 100644 src/app/routes/connector-ui/asset-page/data-category-select/data-category-select-item.service.ts create mode 100644 src/app/routes/connector-ui/asset-page/data-category-select/data-category-select-item.ts create mode 100644 src/app/routes/connector-ui/asset-page/data-category-select/data-category-select.component.html create mode 100644 src/app/routes/connector-ui/asset-page/data-category-select/data-category-select.component.ts create mode 100644 src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-items.pipe.ts create mode 100644 src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select-data.ts create mode 100644 src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select-item.service.ts create mode 100644 src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select-item.ts create mode 100644 src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select.component.html create mode 100644 src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select.component.ts create mode 100644 src/app/routes/connector-ui/asset-page/keyword-select/keyword-select.component.html create mode 100644 src/app/routes/connector-ui/asset-page/keyword-select/keyword-select.component.ts create mode 100644 src/app/routes/connector-ui/asset-page/language-select/language-select-data.ts create mode 100644 src/app/routes/connector-ui/asset-page/language-select/language-select-item.service.ts create mode 100644 src/app/routes/connector-ui/asset-page/language-select/language-select-item.ts create mode 100644 src/app/routes/connector-ui/asset-page/language-select/language-select.component.html create mode 100644 src/app/routes/connector-ui/asset-page/language-select/language-select.component.ts create mode 100644 src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select-data.ts create mode 100644 src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select-item.service.ts create mode 100644 src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select-item.ts create mode 100644 src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select.component.html create mode 100644 src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select.component.ts diff --git a/src/app/core/services/api/fake-backend/connector-fake-impl/asset-fake-service.ts b/src/app/core/services/api/fake-backend/connector-fake-impl/asset-fake-service.ts index 8e6e75fd8..367b6291f 100644 --- a/src/app/core/services/api/fake-backend/connector-fake-impl/asset-fake-service.ts +++ b/src/app/core/services/api/fake-backend/connector-fake-impl/asset-fake-service.ts @@ -41,6 +41,7 @@ function patchAsset(assetId: string, patcher: Patcher): UiAsset { } export const createAsset = (asset: UiAssetCreateRequest): IdResponseDto => { + console.log(); const assetId = asset.id; assets.push({ assetId, diff --git a/src/app/core/services/asset-data-source-mapper-legacy.ts b/src/app/core/services/asset-data-source-mapper-legacy.ts new file mode 100644 index 000000000..14b26847d --- /dev/null +++ b/src/app/core/services/asset-data-source-mapper-legacy.ts @@ -0,0 +1,103 @@ +import {Injectable} from '@angular/core'; +import {UiDataSource} from '@sovity.de/edc-client'; +import {AssetDatasourceFormValue} from '../../routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-datasource-form-model'; +import {HttpDatasourceHeaderFormValue} from '../../routes/connector-ui/asset-page/asset-edit-dialog/form/model/http-datasource-header-form-model'; +import {getAuthFields} from '../utils/form-value-utils'; +import {QueryParamsMapper} from './query-params-mapper'; + +@Injectable({providedIn: 'root'}) +export class AssetDataSourceMapperLegacy { + constructor(private queryParamsMapper: QueryParamsMapper) {} + + buildDataSourceOrNullLegacy( + formValue: AssetDatasourceFormValue, + ): UiDataSource | null { + if (formValue.dataAddressType === 'Unchanged') { + return null; + } + return this.buildDataSourceLegacy(formValue); + } + + buildDataSourceLegacy(formValue: AssetDatasourceFormValue): UiDataSource { + switch (formValue?.dataAddressType) { + case 'Custom-Data-Address-Json': + return this.buildCustomDataSourceLegacy(formValue); + case 'On-Request': + return this.buildOnRequestDataSourceLegacy(formValue); + case 'Http': + return this.buildHttpDataSourceLegacy(formValue); + default: + throw new Error( + `Invalid Data Address Type ${formValue?.dataAddressType}`, + ); + } + } + + private buildCustomDataSourceLegacy( + formValue: AssetDatasourceFormValue, + ): UiDataSource { + const json = JSON.parse(formValue.dataDestination?.trim()!!); + return { + type: 'CUSTOM', + customProperties: json, + }; + } + + private buildHttpDataSourceLegacy( + formValue: AssetDatasourceFormValue, + ): UiDataSource { + const baseUrl = this.queryParamsMapper.getBaseUrlWithoutQueryParams( + formValue.httpUrl!, + )!; + const queryString = this.queryParamsMapper.getFullQueryString( + formValue.httpUrl!, + formValue.httpQueryParams ?? [], + ); + + const authFields = getAuthFields(formValue); + + return { + type: 'HTTP_DATA', + httpData: { + method: formValue.httpMethod, + baseUrl, + queryString: queryString ?? undefined, + authHeaderName: authFields.authHeaderName ?? undefined, + authHeaderValue: { + secretName: authFields.authHeaderSecretName ?? undefined, + rawValue: authFields.authHeaderValue ?? undefined, + }, + headers: this.buildHttpHeaders(formValue.httpHeaders ?? []), + enableMethodParameterization: formValue.httpProxyMethod, + enablePathParameterization: formValue.httpProxyPath, + enableQueryParameterization: formValue.httpProxyQueryParams, + enableBodyParameterization: formValue.httpProxyBody, + }, + }; + } + + private buildOnRequestDataSourceLegacy( + formValue: AssetDatasourceFormValue, + ): UiDataSource { + return { + type: 'ON_REQUEST', + onRequest: { + contactEmail: formValue.contactEmail!!, + contactPreferredEmailSubject: formValue.contactPreferredEmailSubject!!, + }, + }; + } + + private buildHttpHeaders( + headers: HttpDatasourceHeaderFormValue[], + ): Record { + return Object.fromEntries( + headers + .map((header) => [ + header.headerName?.trim() || '', + header.headerValue?.trim() || '', + ]) + .filter((a) => a[0] && a[1]), + ); + } +} diff --git a/src/app/core/services/asset-request-builder-legacy.ts b/src/app/core/services/asset-request-builder-legacy.ts new file mode 100644 index 000000000..fb77fe190 --- /dev/null +++ b/src/app/core/services/asset-request-builder-legacy.ts @@ -0,0 +1,111 @@ +import {Injectable} from '@angular/core'; +import {UiAssetCreateRequest, UiAssetEditRequest} from '@sovity.de/edc-client'; +import {AssetEditorDialogFormValue} from '../../routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-editor-dialog-form-model'; +import {toGmtZeroHourDate} from '../utils/date-utils'; +import {AssetDataSourceMapperLegacy} from './asset-data-source-mapper-legacy'; +import {AssetRequestCommonMetadata} from './asset-request-common-metadata'; + +@Injectable() +export class AssetRequestBuilderLegacy { + constructor(private assetDataSourceMapper: AssetDataSourceMapperLegacy) {} + + buildAssetCreateRequestLegacy( + formValue: AssetEditorDialogFormValue, + ): UiAssetCreateRequest { + const id = formValue.metadata?.id!; + const metadata = this.buildAssetRequestCommonMetadataLegacy(formValue); + const dataSource = this.assetDataSourceMapper.buildDataSourceLegacy( + formValue.datasource!, + ); + return { + id, + ...metadata, + dataSource, + }; + } + + buildAssetEditRequestLegacy( + formValue: AssetEditorDialogFormValue, + ): UiAssetEditRequest { + const metadata = this.buildAssetRequestCommonMetadataLegacy(formValue); + const dataSourceOrNull = + this.assetDataSourceMapper.buildDataSourceOrNullLegacy( + formValue.datasource!, + ); + return { + ...metadata, + dataSourceOverrideOrNull: dataSourceOrNull ?? undefined, + }; + } + + buildAssetRequestCommonMetadataLegacy( + formValue: AssetEditorDialogFormValue, + ): AssetRequestCommonMetadata { + const title = formValue.metadata?.title!; + const version = formValue.metadata?.version; + const description = formValue.metadata?.description; + const language = formValue.metadata?.language?.id; + const keywords = formValue.metadata?.keywords; + const licenseUrl = formValue.metadata?.standardLicense; + const publisherHomepage = formValue.metadata?.publisher; + const mediaType = formValue.metadata?.contentType; + const landingPageUrl = formValue.metadata?.endpointDocumentation; + + const dataCategory = formValue.advanced?.dataCategory?.id; + const dataSubcategory = formValue.advanced?.dataSubcategory?.id; + const transportMode = formValue.advanced?.transportMode?.id; + const geoReferenceMethod = formValue.advanced?.geoReferenceMethod; + const dataModel = formValue.advanced?.dataModel; + + const sovereignLegalName = formValue.advanced?.sovereignLegalName; + const geoLocation = formValue.advanced?.geoLocation; + const nutsLocations = formValue.advanced?.nutsLocations; + const dataSampleUrls = formValue.advanced?.dataSampleUrls; + const referenceFileUrls = formValue.advanced?.referenceFileUrls; + const referenceFilesDescription = + formValue.advanced?.referenceFilesDescription; + const conditionsForUse = formValue.advanced?.conditionsForUse; + const dataUpdateFrequency = formValue.advanced?.dataUpdateFrequency; + let temporalCoverageFrom = + formValue.advanced?.temporalCoverage?.from || undefined; + let temporalCoverageToInclusive = + formValue.advanced?.temporalCoverage?.toInclusive || undefined; + temporalCoverageFrom = temporalCoverageFrom + ? toGmtZeroHourDate(temporalCoverageFrom) + : undefined; + temporalCoverageToInclusive = temporalCoverageToInclusive + ? toGmtZeroHourDate(temporalCoverageToInclusive) + : undefined; + + return { + title, + language, + description, + publisherHomepage, + licenseUrl, + version, + keywords, + mediaType, + landingPageUrl, + dataCategory, + dataSubcategory, + dataModel, + geoReferenceMethod, + transportMode, + sovereignLegalName, + geoLocation, + nutsLocations, + dataSampleUrls, + referenceFileUrls, + referenceFilesDescription, + conditionsForUse, + dataUpdateFrequency, + temporalCoverageFrom, + temporalCoverageToInclusive, + customJsonAsString: undefined, + customJsonLdAsString: undefined, + privateCustomJsonAsString: undefined, + privateCustomJsonLdAsString: undefined, + }; + } +} diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog-data.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog-data.ts new file mode 100644 index 000000000..35e7e625d --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog-data.ts @@ -0,0 +1,5 @@ +import {AssetEditorDialogFormValue} from './form/model/asset-editor-dialog-form-model'; + +export interface AssetEditDialogData { + initialFormValue: AssetEditorDialogFormValue; +} diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog-mode.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog-mode.ts new file mode 100644 index 000000000..829ea52c7 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog-mode.ts @@ -0,0 +1 @@ +export type AssetEditDialogMode = 'CREATE' | 'EDIT'; diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog-result.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog-result.ts new file mode 100644 index 000000000..af21e6881 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog-result.ts @@ -0,0 +1,13 @@ +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; + +export interface AssetEditDialogResult { + /** + * Updated asset list for the asset page + */ + refreshedList: UiAssetMapped[]; + + /** + * The updated / created asset + */ + asset: UiAssetMapped; +} diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog.component.html b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog.component.html new file mode 100644 index 000000000..0d720f03b --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog.component.html @@ -0,0 +1,795 @@ +

+ {{ form.mode === 'CREATE' ? 'Create New Asset' : 'Edit Asset' }} +

+ + + + +
+ General Information +
+
+ + + Name + + + + + + Version + + +
+ + + + Asset ID + + + {{ validationMessages.invalidWhitespacesOrColonsMessage }} + + + {{ validationMessages.invalidPrefix('ID', 'urn:artifact') }} + + + {{ validationMessages.idExistsErrorMessage }} + + + + + + + Description + + + The description uses + Markdown syntax + + + +
+ + + + + + + +
+ + + Content Type + + +
+ + + + Endpoint Documentation + + + {{ validationMessages.invalidUrlMessage }} + + + +
+ + + Publisher + + + {{ validationMessages.invalidUrlMessage }} + + + + + + Standard License + + + {{ validationMessages.invalidUrlMessage }} + + +
+
+
+
+ + +
+ Advanced Information +
+
+ + + + + +
+ + + + Data Model + + + +
+ + + + + + Geo reference method + + +
+ + + + Sovereign + + Legal name of the data owner + + + +
+ + Data update frequency + + + + + + Geo location + + +
+ + +
NUTS locations
+
+ + NUTS location + + + + +
+ +
+ +
+ + +
Data samples
+
+ + URL + + + + +
+ +
+ +
+ + +
Reference files
+
+ + URL + + + + +
+ +
+ +
+ + + + Reference files description + + Additional information regarding the reference files + + + + + + Temporal coverage + + + + + DD/MM/YYYY (optional) – DD/MM/YYYY (optional) + + + {{ validationMessages.invalidDateRangeMessage }} + + + + + + Conditions for use + + Additional not legally relevant usage instructions (e.g. how to + cite the dataset) + + +
+
+
+ + +
+ Datasource Information +
Datasource
+
+ + + + + + + Custom Datasource Config (JSON) + + + {{ validationMessages.invalidJsonMessage }} + + + + + + +
Contact E-Mail
+ +
+ This email address will be offered to potential consumers for + contacting you. This is done in place of having an actual data + source connected. +
+ + + Contact E-Mail + + + {{ validationMessages.invalidEmailMessage }} + + + + +
Preferred E-Mail Subject
+ +
+ Will be added to the mailto-link and displayed to potential + consumers to use. +
+ + + Preferred E-Mail Subject + + +
+ + + +
Method
+ +
+ The consuming side must provide a Custom HTTP Method with + method parameterization enabled. +
+ + + + + + {{ form.proxyMethod ? 'Default' : '' }} Method + + + {{ method }} + + + + + + +
+ +
+ +
URL
+ +
+ The consuming side must provide a Custom HTTP Subpath with + method parameterization is enabled. The Custom HTTP Subpath will + be appended to the base path. +
+ + + + URL + Base URL + + + {{ validationMessages.invalidUrlMessage }} + + + + +
+ +
+ +
+ {{ form.proxyQueryParams ? 'Default' : '' }} Query Params +
+ +
+ + + Query Param Name + + {{ header.errors }} + {{ validationMessages.invalidQueryParam }} + + + + + + Value + + {{ validationMessages.invalidQueryParam }} + + + + + +
+ +
+ With query param parameterization enabled, the default query + params and the query params provided by the consumer will be + merged. +
+ +
+ + + + + +
+ +
Request Body
+ +
+ The request body can only be set from the consumer side, if + parameterization is enabled. +
+ + +
+ +
+ +
Authentication
+ + +
+ +
+ + + + Type + + + Header with Vault Secret + + Header with Value + + +
+ + + Auth Header Name + + + + + + Auth Header Value + + + + + + Vault Secret Name + + +
+ + +
+ +
+ +
Additional Headers
+ +
+ + + Header Name + + + + + + Header Value + + + + + +
+ + +
+ +
+
+
+
+
+
+
+ + + + + + diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog.component.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog.component.ts new file mode 100644 index 000000000..b4a73ad6f --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog.component.ts @@ -0,0 +1,125 @@ +import {Component, Inject, OnDestroy} from '@angular/core'; +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {EMPTY, Observable, Subject, switchMap} from 'rxjs'; +import {catchError, finalize, map, takeUntil, tap} from 'rxjs/operators'; +import {IdResponseDto} from '@sovity.de/edc-client'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {AssetRequestBuilderLegacy} from '../../../../core/services/asset-request-builder-legacy'; +import {AssetService} from '../../../../core/services/asset.service'; +import {NotificationService} from '../../../../core/services/notification.service'; +import {ValidationMessages} from '../../../../core/validators/validation-messages'; +import {AssetEditDialogData} from './asset-edit-dialog-data'; +import {AssetEditDialogResult} from './asset-edit-dialog-result'; +import {AssetAdvancedFormBuilder} from './form/asset-advanced-form-builder'; +import {AssetDatasourceFormBuilder} from './form/asset-datasource-form-builder'; +import {AssetEditDialogForm} from './form/asset-edit-dialog-form'; +import {AssetMetadataFormBuilder} from './form/asset-metadata-form-builder'; +import {DATA_SOURCE_HTTP_METHODS} from './form/http-methods'; +import {AssetEditorDialogFormValue} from './form/model/asset-editor-dialog-form-model'; + +@Component({ + selector: 'asset-edit-dialog', + templateUrl: './asset-edit-dialog.component.html', + providers: [ + AssetAdvancedFormBuilder, + AssetDatasourceFormBuilder, + AssetEditDialogForm, + AssetRequestBuilderLegacy, + AssetMetadataFormBuilder, + ], +}) +export class AssetEditDialogComponent implements OnDestroy { + loading = false; + + methods = DATA_SOURCE_HTTP_METHODS; + + constructor( + private edcApiService: EdcApiService, + private assetService: AssetService, + public form: AssetEditDialogForm, + public validationMessages: ValidationMessages, + private assetEntryBuilder: AssetRequestBuilderLegacy, + private notificationService: NotificationService, + private dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) private data: AssetEditDialogData, + ) { + this.form.reset(this.data.initialFormValue); + } + + onSave() { + const formValue = this.form.value; + + // Workaround around disabled controls not being included in the form value + if (formValue.mode !== 'CREATE') { + formValue.metadata!.id = this.form.metadata.controls.id.getRawValue(); + } + + this.form.all.disable(); + this.loading = true; + this._saveRequest(formValue) + .pipe( + // Save Asset + takeUntil(this.ngOnDestroy$), + tap(() => { + this.notificationService.showInfo('Successfully saved asset'); + }), + catchError((error) => { + console.error('Failed saving asset!', error); + this.notificationService.showError('Failed saving asset!'); + this.form.all.enable(); + return EMPTY; + }), + switchMap(() => this.assetService.fetchAssets()), + map( + (assets): AssetEditDialogResult => ({ + refreshedList: assets, + asset: assets?.find( + (it) => it.assetId === this.form.value.metadata?.id, + )!, + }), + ), + finalize(() => { + this.loading = false; + }), + ) + .subscribe({ + next: (result: AssetEditDialogResult) => this.close(result), + error: (error) => { + console.error('Failed refreshing asset list!', error); + this.notificationService.showError('Failed refreshing asset list!'); + }, + }); + } + + private _saveRequest( + formValue: AssetEditorDialogFormValue, + ): Observable { + const mode = formValue.mode; + + if (mode === 'CREATE') { + const createRequest = + this.assetEntryBuilder.buildAssetCreateRequestLegacy(formValue); + return this.edcApiService.createAsset(createRequest); + } + + if (mode === 'EDIT') { + const assetId = formValue.metadata?.id!; + const editRequest = + this.assetEntryBuilder.buildAssetEditRequestLegacy(formValue); + return this.edcApiService.editAsset(assetId, editRequest); + } + + throw new Error(`Unsupported mode: ${mode}`); + } + + private close(params: AssetEditDialogResult) { + this.dialogRef.close(params); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy(): void { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog.service.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog.service.ts new file mode 100644 index 000000000..da1dcf7ff --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/asset-edit-dialog.service.ts @@ -0,0 +1,44 @@ +import {Injectable} from '@angular/core'; +import {MatDialog} from '@angular/material/dialog'; +import {NEVER, Observable} from 'rxjs'; +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; +import {showDialogUntil} from '../../../../core/utils/mat-dialog-utils'; +import {AssetEditDialogData} from './asset-edit-dialog-data'; +import {AssetEditDialogResult} from './asset-edit-dialog-result'; +import {AssetEditDialogComponent} from './asset-edit-dialog.component'; +import {AssetEditDialogFormMapper} from './form/asset-edit-dialog-form-mapper'; + +@Injectable() +export class AssetEditDialogService { + constructor( + private dialog: MatDialog, + private assetEditDialogFormMapper: AssetEditDialogFormMapper, + ) {} + + showCreateDialog( + until$: Observable = NEVER, + ): Observable { + const initialFormValue = this.assetEditDialogFormMapper.forCreate(); + return this._open({initialFormValue}, until$); + } + + showEditDialog( + asset: UiAssetMapped, + until$: Observable = NEVER, + ): Observable { + const initialFormValue = this.assetEditDialogFormMapper.forEdit(asset); + return this._open({initialFormValue}, until$); + } + + private _open( + data: AssetEditDialogData, + until$: Observable, + ): Observable { + return showDialogUntil( + this.dialog, + AssetEditDialogComponent, + {data}, + until$, + ); + } +} diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/assets-id-validator-builder.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/assets-id-validator-builder.ts new file mode 100644 index 000000000..bfef34c99 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/assets-id-validator-builder.ts @@ -0,0 +1,35 @@ +import {Injectable} from '@angular/core'; +import { + AbstractControl, + AsyncValidatorFn, + ValidationErrors, +} from '@angular/forms'; +import {Observable} from 'rxjs'; +import {map} from 'rxjs/operators'; +import {AssetService} from '../../../../core/services/asset.service'; + +@Injectable({ + providedIn: 'root', +}) +export class AssetsIdValidatorBuilder { + constructor(private assetServiceMapped: AssetService) {} + + assetIdDoesNotExistsValidator(): AsyncValidatorFn { + return (control: AbstractControl): Observable => { + return this.fetchAssetIds().pipe( + map((assetIds) => { + if (assetIds.has(control.value)) { + return {idAlreadyExists: true}; + } + return null; + }), + ); + }; + } + + private fetchAssetIds(): Observable> { + return this.assetServiceMapped + .fetchAssets() + .pipe(map((assets) => new Set(assets.map((asset) => asset.assetId)))); + } +} diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-advanced-form-builder.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-advanced-form-builder.ts new file mode 100644 index 000000000..3c5df5bdf --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-advanced-form-builder.ts @@ -0,0 +1,55 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'; +import {urlValidator} from 'src/app/core/validators/url-validator'; +import {validOptionalDateRange} from 'src/app/core/validators/valid-optional-date-range'; +import {AssetAdvancedFormModel} from './model/asset-advanced-form-model'; +import {AssetEditorDialogFormValue} from './model/asset-editor-dialog-form-model'; + +@Injectable() +export class AssetAdvancedFormBuilder { + constructor(private formBuilder: FormBuilder) {} + + buildFormGroup( + initial: AssetEditorDialogFormValue['advanced'], + ): FormGroup { + return this.formBuilder.nonNullable.group({ + dataModel: initial?.dataModel!, + dataCategory: [initial?.dataCategory || null, Validators.required], + dataSubcategory: initial?.dataSubcategory || null, + transportMode: initial?.transportMode || null, + geoReferenceMethod: initial?.geoReferenceMethod!, + sovereignLegalName: initial?.sovereignLegalName!, + geoLocation: initial?.geoLocation!, + nutsLocations: this.formBuilder.nonNullable.array( + initial?.nutsLocations?.map((x) => this.buildRequiredString(x)) ?? [], + ), + dataSampleUrls: this.formBuilder.array( + initial?.dataSampleUrls?.map((x) => this.buildRequiredUrl(x)) ?? [], + ), + referenceFileUrls: this.formBuilder.nonNullable.array( + initial?.referenceFileUrls?.map((x) => this.buildRequiredUrl(x)) ?? [], + ), + referenceFilesDescription: initial?.referenceFilesDescription!, + conditionsForUse: initial?.conditionsForUse!, + dataUpdateFrequency: initial?.dataUpdateFrequency!, + temporalCoverage: this.formBuilder.group( + { + from: initial?.temporalCoverage?.from || null, + toInclusive: initial?.temporalCoverage?.toInclusive || null, + }, + {validators: validOptionalDateRange}, + ), + }); + } + + buildRequiredString(initial: string): FormControl { + return this.formBuilder.nonNullable.control(initial, Validators.required); + } + + buildRequiredUrl(initial: string): FormControl { + return this.formBuilder.nonNullable.control(initial, [ + Validators.required, + urlValidator, + ]); + } +} diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-datasource-form-builder.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-datasource-form-builder.ts new file mode 100644 index 000000000..0fafca5dc --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-datasource-form-builder.ts @@ -0,0 +1,105 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormGroup, Validators} from '@angular/forms'; +import {validQueryParam} from 'src/app/core/validators/valid-query-param'; +import {switchDisabledControls} from '../../../../../core/utils/form-group-utils'; +import {jsonValidator} from '../../../../../core/validators/json-validator'; +import {urlValidator} from '../../../../../core/validators/url-validator'; +import {assetDatasourceFormEnabledCtrls} from './model/asset-datasource-form-enabled-ctrls'; +import { + AssetDatasourceFormModel, + AssetDatasourceFormValue, +} from './model/asset-datasource-form-model'; +import { + HttpDatasourceHeaderFormModel, + HttpDatasourceHeaderFormValue, +} from './model/http-datasource-header-form-model'; +import { + HttpDatasourceQueryParamFormModel, + HttpDatasourceQueryParamFormValue, +} from './model/http-datasource-query-param-form-model'; + +@Injectable() +export class AssetDatasourceFormBuilder { + constructor(private formBuilder: FormBuilder) {} + + buildFormGroup( + initial: AssetDatasourceFormValue, + ): FormGroup { + const datasource: FormGroup = + this.formBuilder.nonNullable.group({ + dataAddressType: initial?.dataAddressType!, + dataDestination: [ + initial?.dataDestination!, + [Validators.required, jsonValidator], + ], + + // On-Request + contactEmail: [ + initial?.contactEmail!, + [Validators.required, Validators.email], + ], + contactPreferredEmailSubject: [ + initial?.contactPreferredEmailSubject!, + Validators.required, + ], + + // Http Datasource Fields + httpUrl: [initial?.httpUrl!, [Validators.required, urlValidator]], + httpMethod: [initial?.httpMethod!, Validators.required], + + httpAuthHeaderType: [initial?.httpAuthHeaderType!], + httpAuthHeaderName: [initial?.httpAuthHeaderName!, Validators.required], + httpAuthHeaderValue: [ + initial?.httpAuthHeaderValue!, + Validators.required, + ], + httpAuthHeaderSecretName: [ + initial?.httpAuthHeaderSecretName!, + Validators.required, + ], + httpQueryParams: this.formBuilder.array( + initial?.httpQueryParams?.map( + (param: HttpDatasourceQueryParamFormValue) => + this.buildQueryParamFormGroup(param), + ) ?? [], + ), + + httpDefaultPath: [initial?.httpDefaultPath!], + httpProxyMethod: [initial?.httpProxyMethod!], + httpProxyPath: [initial?.httpProxyPath!], + httpProxyQueryParams: [initial?.httpProxyQueryParams!], + httpProxyBody: [initial?.httpProxyBody!], + + httpHeaders: this.formBuilder.array( + initial?.httpHeaders?.map((header: HttpDatasourceHeaderFormValue) => + this.buildHeaderFormGroup(header), + ) ?? [], + ), + }); + + switchDisabledControls( + datasource, + assetDatasourceFormEnabledCtrls, + ); + + return datasource; + } + + buildHeaderFormGroup( + initial: HttpDatasourceHeaderFormValue, + ): FormGroup { + return this.formBuilder.nonNullable.group({ + headerName: [initial.headerName!, Validators.required], + headerValue: [initial.headerValue!, Validators.required], + }); + } + + buildQueryParamFormGroup( + initial: HttpDatasourceQueryParamFormValue, + ): FormGroup { + return this.formBuilder.nonNullable.group({ + paramName: [initial.paramName!, [Validators.required, validQueryParam]], + paramValue: [initial.paramValue!, [validQueryParam]], + }); + } +} diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-edit-dialog-form-mapper.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-edit-dialog-form-mapper.ts new file mode 100644 index 000000000..f8cc0d97f --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-edit-dialog-form-mapper.ts @@ -0,0 +1,107 @@ +import {Injectable} from '@angular/core'; +import {UiAssetMapped} from '../../../../../core/services/models/ui-asset-mapped'; +import {LanguageSelectItemService} from '../../language-select/language-select-item.service'; +import {AssetDatasourceFormValue} from './model/asset-datasource-form-model'; +import {AssetEditorDialogFormValue} from './model/asset-editor-dialog-form-model'; + +/** + * Handles AngularForms for AssetEditorDialog + */ +@Injectable() +export class AssetEditDialogFormMapper { + constructor(private languageSelectItemService: LanguageSelectItemService) {} + + forCreate(): AssetEditorDialogFormValue { + return { + mode: 'CREATE', + metadata: { + id: '', + title: '', + version: '', + contentType: '', + description: '', + keywords: [], + language: this.languageSelectItemService.english(), + publisher: '', + standardLicense: '', + endpointDocumentation: '', + }, + advanced: { + dataModel: '', + dataCategory: null, + dataSubcategory: null, + transportMode: null, + geoReferenceMethod: '', + }, + datasource: this.emptyHttpDatasource(), + }; + } + + forEdit(asset: UiAssetMapped): AssetEditorDialogFormValue { + return { + mode: 'EDIT', + metadata: { + id: asset.assetId, + title: asset.title, + version: asset.version, + contentType: asset.mediaType, + description: asset.description, + keywords: asset.keywords, + language: asset.language, + publisher: asset.publisherHomepage, + standardLicense: asset.licenseUrl, + endpointDocumentation: asset.landingPageUrl, + }, + advanced: { + dataModel: asset.dataModel, + dataCategory: asset.dataCategory, + dataSubcategory: asset.dataSubcategory, + transportMode: asset.transportMode, + geoReferenceMethod: asset.geoReferenceMethod, + sovereignLegalName: asset.sovereignLegalName, + geoLocation: asset.geoLocation, + nutsLocations: asset.nutsLocations, + dataSampleUrls: asset.dataSampleUrls, + referenceFileUrls: asset.referenceFileUrls, + referenceFilesDescription: asset.referenceFilesDescription, + conditionsForUse: asset.conditionsForUse, + dataUpdateFrequency: asset.dataUpdateFrequency, + temporalCoverage: { + from: asset.temporalCoverageFrom, + toInclusive: asset.temporalCoverageToInclusive, + }, + }, + datasource: this.emptyEditDatasource(), + }; + } + + private emptyHttpDatasource(): AssetDatasourceFormValue { + return { + dataAddressType: 'Http', + dataDestination: '', + + httpUrl: '', + httpMethod: 'GET', + httpAuthHeaderType: 'None', + httpAuthHeaderName: '', + httpAuthHeaderValue: '', + httpAuthHeaderSecretName: '', + httpQueryParams: [], + + httpDefaultPath: '', + httpProxyMethod: false, + httpProxyPath: false, + httpProxyQueryParams: false, + httpProxyBody: false, + + httpHeaders: [], + }; + } + + private emptyEditDatasource(): AssetDatasourceFormValue { + return { + ...this.emptyHttpDatasource(), + dataAddressType: 'Http', + }; + } +} diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-edit-dialog-form.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-edit-dialog-form.ts new file mode 100644 index 000000000..2ca953ccf --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-edit-dialog-form.ts @@ -0,0 +1,180 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormGroup} from '@angular/forms'; +import {DataAddressType} from '../../../../../component-library/data-address/data-address-type-select/data-address-type'; +import {ActiveFeatureSet} from '../../../../../core/config/active-feature-set'; +import {DataCategorySelectItem} from '../../data-category-select/data-category-select-item'; +import {AssetEditDialogMode} from '../asset-edit-dialog-mode'; +import {AssetAdvancedFormBuilder} from './asset-advanced-form-builder'; +import {AssetDatasourceFormBuilder} from './asset-datasource-form-builder'; +import {AssetMetadataFormBuilder} from './asset-metadata-form-builder'; +import {AssetAdvancedFormModel} from './model/asset-advanced-form-model'; +import {AssetDatasourceFormModel} from './model/asset-datasource-form-model'; +import { + AssetEditorDialogFormModel, + AssetEditorDialogFormValue, +} from './model/asset-editor-dialog-form-model'; +import {AssetMetadataFormModel} from './model/asset-metadata-form-model'; + +/** + * Handles AngularForms for AssetEditorDialog + */ +@Injectable() +export class AssetEditDialogForm { + all!: FormGroup; + + /** + * FormGroup for stepper step "Metadata" + */ + metadata!: AssetEditorDialogFormModel['metadata']; + + /** + * FormGroup for stepper step "Advanced" + */ + advanced!: AssetEditorDialogFormModel['advanced']; + + /** + * FormGroup for stepper step "Data Source" + */ + datasource!: AssetEditorDialogFormModel['datasource']; + + /** + * Quick access to selected data address type + */ + get dataAddressType(): DataAddressType | null { + return this.datasource!.controls.dataAddressType.value; + } + + /** + * Quick access to selected data category + */ + get dataCategory(): DataCategorySelectItem | null { + return this.advanced!.controls.dataCategory.value; + } + + /** + * Quick Access to Dialog Mode + */ + get mode(): AssetEditDialogMode { + return this.all.controls.mode.value; + } + + /** + * Quick access to full value + */ + get value(): AssetEditorDialogFormValue { + return this.all.value; + } + + get proxyMethod(): boolean { + return this.datasource!.controls.httpProxyMethod.value; + } + + get proxyPath(): boolean { + return this.datasource!.controls.httpProxyPath.value; + } + + get proxyQueryParams(): boolean { + return this.datasource!.controls.httpProxyQueryParams.value; + } + + constructor( + private formBuilder: FormBuilder, + private activeFeatureSet: ActiveFeatureSet, + private assetMetadataFormBuilder: AssetMetadataFormBuilder, + private assetAdvancedFormBuilder: AssetAdvancedFormBuilder, + private assetDatasourceFormBuilder: AssetDatasourceFormBuilder, + ) {} + + reset(initial: AssetEditorDialogFormValue) { + this.all = this.buildFormGroup(initial); + this.metadata = this.all.controls.metadata; + this.advanced = this.all.controls.advanced; + this.datasource = this.all.controls.datasource; + } + + buildFormGroup( + initial: AssetEditorDialogFormValue, + ): FormGroup { + const metadata: FormGroup = + this.assetMetadataFormBuilder.buildFormGroup( + initial.metadata!, + initial.mode!, + ); + + const formGroup: FormGroup = + this.formBuilder.nonNullable.group({ + mode: [initial.mode as AssetEditDialogMode], + metadata, + }); + + if (this.activeFeatureSet.hasMdsFields()) { + const advanced: FormGroup = + this.assetAdvancedFormBuilder.buildFormGroup(initial.advanced); + formGroup.addControl('advanced', advanced); + } + + if (initial.mode !== 'EDIT') { + const datasource: FormGroup = + this.assetDatasourceFormBuilder.buildFormGroup(initial.datasource!); + formGroup.addControl('datasource', datasource); + } + + return formGroup; + } + + onHttpHeadersAddClick() { + this.datasource!.controls.httpHeaders.push( + this.assetDatasourceFormBuilder.buildHeaderFormGroup({ + headerName: '', + headerValue: '', + }), + ); + } + + onHttpHeadersRemoveClick(index: number) { + this.datasource!.controls.httpHeaders.removeAt(index); + } + + onHttpQueryParamsAddClick() { + this.datasource!.controls.httpQueryParams.push( + this.assetDatasourceFormBuilder.buildQueryParamFormGroup({ + paramName: '', + paramValue: '', + }), + ); + } + + onHttpQueryParamsRemoveClick(index: number) { + this.datasource!.controls.httpQueryParams.removeAt(index); + } + + onNutsLocationsAddClick() { + this.advanced!.controls.nutsLocations.push( + this.assetAdvancedFormBuilder.buildRequiredString(''), + ); + } + + onNutsLocationsRemoveClick(index: number) { + this.advanced!.controls.nutsLocations.removeAt(index); + } + + onDataSampleUrlsAddClick() { + this.advanced!.controls.dataSampleUrls.push( + this.assetAdvancedFormBuilder.buildRequiredUrl(''), + ); + } + + onDataSampleUrlsRemoveClick(index: number) { + this.advanced!.controls.dataSampleUrls.removeAt(index); + } + + onReferenceFileUrlsAddClick() { + this.advanced!.controls.referenceFileUrls.push( + this.assetAdvancedFormBuilder.buildRequiredUrl(''), + ); + } + + onReferenceFileUrlsRemoveClick(index: number) { + this.advanced!.controls.referenceFileUrls.removeAt(index); + } +} diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-metadata-form-builder.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-metadata-form-builder.ts new file mode 100644 index 000000000..573355703 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/asset-metadata-form-builder.ts @@ -0,0 +1,94 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'; +import {combineLatest, distinctUntilChanged, pairwise} from 'rxjs'; +import {map} from 'rxjs/operators'; +import {value$} from '../../../../../core/utils/form-group-utils'; +import {noWhitespacesOrColonsValidator} from '../../../../../core/validators/no-whitespaces-or-colons-validator'; +import {urlValidator} from '../../../../../core/validators/url-validator'; +import {AssetEditDialogMode} from '../asset-edit-dialog-mode'; +import {AssetsIdValidatorBuilder} from '../assets-id-validator-builder'; +import { + AssetMetadataFormModel, + AssetMetadataFormValue, +} from './model/asset-metadata-form-model'; + +@Injectable() +export class AssetMetadataFormBuilder { + constructor( + private formBuilder: FormBuilder, + private assetsIdValidatorBuilder: AssetsIdValidatorBuilder, + ) {} + + buildFormGroup( + initial: AssetMetadataFormValue, + mode: AssetEditDialogMode, + ): FormGroup { + const metadata: FormGroup = + this.formBuilder.nonNullable.group({ + id: [ + initial?.id!, + [Validators.required, noWhitespacesOrColonsValidator], + [this.assetsIdValidatorBuilder.assetIdDoesNotExistsValidator()], + ], + title: [initial?.title!, Validators.required], + version: [initial?.version!], + contentType: [initial?.contentType!], + description: [initial?.description!], + keywords: [initial?.keywords!], + language: [initial?.language ?? null], + publisher: [initial?.publisher!, urlValidator], + standardLicense: [initial?.standardLicense!, urlValidator], + endpointDocumentation: [initial?.endpointDocumentation!, urlValidator], + }); + + // generate id from name and version(if available) + if (mode === 'CREATE') { + this.initIdGeneration( + metadata.controls.id, + metadata.controls.title, + metadata.controls.version, + ); + } else { + metadata.controls.id.disable(); + } + + return metadata; + } + + private initIdGeneration( + idCtrl: FormControl, + titleCtrl: FormControl, + versionCtrl: FormControl, + ) { + combineLatest([ + value$(titleCtrl).pipe(distinctUntilChanged()), + value$(versionCtrl).pipe(distinctUntilChanged()), + ]) + .pipe( + map(([title, version]) => this.generateId(title, version)), + pairwise(), + ) + .subscribe(([previousId, currentId]) => { + if (!idCtrl.value || idCtrl.value === previousId) { + idCtrl.setValue(currentId); + } + }); + } + + private generateId(title: string | null, version: string | null) { + if (!title) { + return ''; + } + const titleClean = this.cleanIdComponent(title); + const versionClean = this.cleanIdComponent(version); + return version ? `${titleClean}-${versionClean}` : titleClean; + } + + private cleanIdComponent(s: string | null) { + return (s ?? '') + .trim() + .replace(':', '-') + .replaceAll(' ', '-') + .toLowerCase(); + } +} diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/http-methods.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/http-methods.ts new file mode 100644 index 000000000..ac48610ce --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/http-methods.ts @@ -0,0 +1,11 @@ +export const DATA_SOURCE_HTTP_METHODS = [ + 'GET', + 'POST', + 'PUT', + 'PATCH', + 'DELETE', + 'OPTIONS', +]; +export const DATA_SINK_HTTP_METHODS = DATA_SOURCE_HTTP_METHODS.filter( + (it) => it !== 'GET', +); diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-advanced-form-model.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-advanced-form-model.ts new file mode 100644 index 000000000..6dd62daba --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-advanced-form-model.ts @@ -0,0 +1,36 @@ +import { + FormArray, + FormControl, + FormGroup, + ɵFormGroupValue, +} from '@angular/forms'; +import {DataCategorySelectItem} from '../../../data-category-select/data-category-select-item'; +import {DataSubcategorySelectItem} from '../../../data-subcategory-select/data-subcategory-select-item'; +import {TransportModeSelectItem} from '../../../transport-mode-select/transport-mode-select-item'; +import {TemporalCoverageFormModel} from './temporal-coverage-form-model'; + +/** + * Form Model for AssetEditorDialog > Advanced + * (MDS Properties) + */ +export interface AssetAdvancedFormModel { + dataCategory: FormControl; + dataSubcategory: FormControl; + dataModel: FormControl; + geoReferenceMethod: FormControl; + transportMode: FormControl; + sovereignLegalName: FormControl; + geoLocation: FormControl; + nutsLocations: FormArray>; + dataSampleUrls: FormArray>; + referenceFileUrls: FormArray>; + referenceFilesDescription: FormControl; + conditionsForUse: FormControl; + dataUpdateFrequency: FormControl; + temporalCoverage: FormGroup; +} + +/** + * Form Value for AssetEditorDialog > Advanced + */ +export type AssetAdvancedFormValue = ɵFormGroupValue; diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-datasource-form-enabled-ctrls.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-datasource-form-enabled-ctrls.ts new file mode 100644 index 000000000..3767c8248 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-datasource-form-enabled-ctrls.ts @@ -0,0 +1,48 @@ +import { + AssetDatasourceFormModel, + AssetDatasourceFormValue, +} from './asset-datasource-form-model'; + +export const assetDatasourceFormEnabledCtrls = ( + value: AssetDatasourceFormValue, +): Record => { + const customDataAddressJson = + value.dataAddressType === 'Custom-Data-Address-Json'; + + const onRequest = value.dataAddressType === 'On-Request'; + + const http = value.dataAddressType === 'Http'; + const httpAuth = value.httpAuthHeaderType !== 'None'; + const httpAuthByValue = value.httpAuthHeaderType === 'Value'; + const httpAuthByVault = value.httpAuthHeaderType === 'Vault-Secret'; + const proxyPath = !!value.httpProxyPath; + + return { + dataAddressType: true, + + // Custom Datasource JSON + dataDestination: customDataAddressJson, + + // On Request Datasource + contactEmail: onRequest, + contactPreferredEmailSubject: onRequest, + + // Http Datasource Fields + httpUrl: http, + httpMethod: http, + + httpAuthHeaderType: http, + httpAuthHeaderName: http && httpAuth, + httpAuthHeaderValue: http && httpAuthByValue, + httpAuthHeaderSecretName: http && httpAuthByVault, + httpQueryParams: http, + + httpDefaultPath: http && proxyPath, + httpProxyMethod: http, + httpProxyPath: http, + httpProxyQueryParams: http, + httpProxyBody: http, + + httpHeaders: http, + }; +}; diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-datasource-form-model.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-datasource-form-model.ts new file mode 100644 index 000000000..3819dab36 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-datasource-form-model.ts @@ -0,0 +1,49 @@ +import { + FormArray, + FormControl, + FormGroup, + ɵFormGroupValue, +} from '@angular/forms'; +import {UiDataSourceHttpDataMethod} from '@sovity.de/edc-client'; +// import {DataAddress} from 'src/app/component-library/edit-asset-form/edit-asset-form/form/model/data-address'; +import {DataAddressType} from 'src/app/component-library/data-address/data-address-type-select/data-address-type'; +import {HttpDatasourceAuthHeaderType} from './http-datasource-auth-header-type'; +import {HttpDatasourceHeaderFormModel} from './http-datasource-header-form-model'; +import {HttpDatasourceQueryParamFormModel} from './http-datasource-query-param-form-model'; + +/** + * Form Model for AssetEditorDialog > Datasource + */ +export interface AssetDatasourceFormModel { + // dataAddressType: FormControl; + dataAddressType: FormControl; + + // Custom Datasource JSON + dataDestination: FormControl; + + // On-Request Datasource + contactEmail: FormControl; + contactPreferredEmailSubject: FormControl; + + // Http Datasource + httpUrl: FormControl; + httpMethod: FormControl; + + httpAuthHeaderType: FormControl; + httpAuthHeaderName: FormControl; + httpAuthHeaderValue: FormControl; + httpAuthHeaderSecretName: FormControl; + httpHeaders: FormArray>; + httpQueryParams: FormArray>; + httpProxyMethod: FormControl; + httpProxyPath: FormControl; + httpProxyQueryParams: FormControl; + httpProxyBody: FormControl; + httpDefaultPath: FormControl; +} + +/** + * Form Value for AssetEditorDialog > Datasource + */ +export type AssetDatasourceFormValue = + ɵFormGroupValue; diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-editor-dialog-form-model.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-editor-dialog-form-model.ts new file mode 100644 index 000000000..cbc11c372 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-editor-dialog-form-model.ts @@ -0,0 +1,21 @@ +import {FormControl, FormGroup, ɵFormGroupValue} from '@angular/forms'; +import {AssetEditDialogMode} from '../../asset-edit-dialog-mode'; +import {AssetAdvancedFormModel} from './asset-advanced-form-model'; +import {AssetDatasourceFormModel} from './asset-datasource-form-model'; +import {AssetMetadataFormModel} from './asset-metadata-form-model'; + +/** + * Form Model for AssetEditorDialog + */ +export interface AssetEditorDialogFormModel { + mode: FormControl; + metadata: FormGroup; + advanced?: FormGroup; + datasource?: FormGroup; +} + +/** + * Form Value for AssetEditorDialog + */ +export type AssetEditorDialogFormValue = + ɵFormGroupValue; diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-metadata-form-model.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-metadata-form-model.ts new file mode 100644 index 000000000..cef83fbf2 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/asset-metadata-form-model.ts @@ -0,0 +1,23 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; +import {LanguageSelectItem} from '../../../language-select/language-select-item'; + +/** + * Form Model for AssetEditorDialog > Metadata + */ +export interface AssetMetadataFormModel { + id: FormControl; + title: FormControl; + version: FormControl; + contentType: FormControl; + description: FormControl; + keywords: FormControl; + language: FormControl; + publisher: FormControl; + standardLicense: FormControl; + endpointDocumentation: FormControl; +} + +/** + * Form Value for AssetEditorDialog > Metadata + */ +export type AssetMetadataFormValue = ɵFormGroupValue; diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/http-datasource-auth-header-type.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/http-datasource-auth-header-type.ts new file mode 100644 index 000000000..c208d6fd0 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/http-datasource-auth-header-type.ts @@ -0,0 +1 @@ +export type HttpDatasourceAuthHeaderType = 'None' | 'Value' | 'Vault-Secret'; diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/http-datasource-header-form-model.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/http-datasource-header-form-model.ts new file mode 100644 index 000000000..784e9d096 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/http-datasource-header-form-model.ts @@ -0,0 +1,15 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; + +/** + * Form Model for AssetEditorDialog > Datasource > HTTP/REST > Header + */ +export interface HttpDatasourceHeaderFormModel { + headerName: FormControl; + headerValue: FormControl; +} + +/** + * Form Value for AssetEditorDialog > Datasource > HTTP/REST > Header + */ +export type HttpDatasourceHeaderFormValue = + ɵFormGroupValue; diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/http-datasource-query-param-form-model.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/http-datasource-query-param-form-model.ts new file mode 100644 index 000000000..719bf5448 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/http-datasource-query-param-form-model.ts @@ -0,0 +1,15 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; + +/** + * Form Model for AssetEditorDialog > Datasource > HTTP/REST > Header + */ +export interface HttpDatasourceQueryParamFormModel { + paramName: FormControl; + paramValue: FormControl; +} + +/** + * Form Value for AssetEditorDialog > Datasource > HTTP/REST > QueryParam + */ +export type HttpDatasourceQueryParamFormValue = + ɵFormGroupValue; diff --git a/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/temporal-coverage-form-model.ts b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/temporal-coverage-form-model.ts new file mode 100644 index 000000000..f145f9f5e --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-edit-dialog/form/model/temporal-coverage-form-model.ts @@ -0,0 +1,15 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; + +/** + * Form Model for AssetEditorDialog > Advanced > Temporal Coverage + */ +export interface TemporalCoverageFormModel { + from: FormControl; + toInclusive: FormControl; +} + +/** + * Form Value for AssetEditorDialog > Advanced > Temporal Coverage + */ +export type TemporalCoverageFormValue = + ɵFormGroupValue; diff --git a/src/app/routes/connector-ui/asset-page/asset-page.module.ts b/src/app/routes/connector-ui/asset-page/asset-page.module.ts index 0623da6d5..33d61a3a7 100644 --- a/src/app/routes/connector-ui/asset-page/asset-page.module.ts +++ b/src/app/routes/connector-ui/asset-page/asset-page.module.ts @@ -25,7 +25,16 @@ import {DataAddressModule} from '../../../component-library/data-address/data-ad import {PipesAndDirectivesModule} from '../../../component-library/pipes-and-directives/pipes-and-directives.module'; import {UiElementsModule} from '../../../component-library/ui-elements/ui-elements.module'; import {AssetCardsComponent} from './asset-cards/asset-cards.component'; +import {AssetEditDialogComponent} from './asset-edit-dialog/asset-edit-dialog.component'; +import {AssetEditDialogService} from './asset-edit-dialog/asset-edit-dialog.service'; +import {AssetEditDialogFormMapper} from './asset-edit-dialog/form/asset-edit-dialog-form-mapper'; import {AssetPageComponent} from './asset-page/asset-page.component'; +import {DataCategorySelectComponent} from './data-category-select/data-category-select.component'; +import {DataSubcategoryItemsPipe} from './data-subcategory-select/data-subcategory-items.pipe'; +import {DataSubcategorySelectComponent} from './data-subcategory-select/data-subcategory-select.component'; +import {KeywordSelectComponent} from './keyword-select/keyword-select.component'; +import {LanguageSelectComponent} from './language-select/language-select.component'; +import {TransportModeSelectComponent} from './transport-mode-select/transport-mode-select.component'; @NgModule({ imports: [ @@ -61,7 +70,18 @@ import {AssetPageComponent} from './asset-page/asset-page.component'; PipesAndDirectivesModule, UiElementsModule, ], - declarations: [AssetCardsComponent, AssetPageComponent], + declarations: [ + AssetCardsComponent, + AssetPageComponent, + AssetEditDialogComponent, + DataCategorySelectComponent, + DataSubcategorySelectComponent, + DataSubcategoryItemsPipe, + KeywordSelectComponent, + LanguageSelectComponent, + TransportModeSelectComponent, + ], + providers: [AssetEditDialogService, AssetEditDialogFormMapper], exports: [AssetPageComponent], }) export class AssetPageModule {} diff --git a/src/app/routes/connector-ui/asset-page/asset-page/asset-page.component.html b/src/app/routes/connector-ui/asset-page/asset-page/asset-page.component.html index f8177e406..65bfd342a 100644 --- a/src/app/routes/connector-ui/asset-page/asset-page/asset-page.component.html +++ b/src/app/routes/connector-ui/asset-page/asset-page/asset-page.component.html @@ -18,17 +18,15 @@ - -
- add_circle_outline - Create asset -
-
+ (click)="onCreate()"> + add_circle_outline + Create asset +
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 116920020..ecb018f0f 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 @@ -8,6 +8,7 @@ import {AssetDetailDialogService} from '../../../../component-library/catalog/as import {AssetService} from '../../../../core/services/asset.service'; import {Fetched} from '../../../../core/services/models/fetched'; import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; +import {AssetEditDialogService} from '../asset-edit-dialog/asset-edit-dialog.service'; export interface AssetList { filteredAssets: UiAssetMapped[]; @@ -28,6 +29,7 @@ export class AssetPageComponent implements OnInit, OnDestroy { private assetServiceMapped: AssetService, private assetDetailDialogDataService: AssetDetailDialogDataService, private assetDetailDialogService: AssetDetailDialogService, + private assetEditDialogService: AssetEditDialogService, private router: Router, ) {} @@ -73,6 +75,17 @@ export class AssetPageComponent implements OnInit, OnDestroy { .subscribe(() => this.refresh()); } + onCreate() { + this.assetEditDialogService + .showCreateDialog(this.ngOnDestroy$) + .subscribe((result) => { + if (result?.refreshedList) { + // this.assetListUpdater$.next(result.refreshedList); + this.refresh(); + } + }); + } + private refresh() { this.fetch$.next(null); } diff --git a/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select-data.ts b/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select-data.ts new file mode 100644 index 000000000..611e3f020 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select-data.ts @@ -0,0 +1,48 @@ +import {DataCategorySelectItem} from './data-category-select-item'; + +export const DATA_CATEGORY_SELECT_DATA: DataCategorySelectItem[] = [ + { + id: 'Traffic Information', + label: 'Traffic Information', + }, + { + id: 'Roadworks and Road Conditions', + label: 'Roadworks and Road Conditions', + }, + { + id: 'Traffic Flow Information', + label: 'Traffic Flow Information', + }, + { + id: 'Parking Information', + label: 'Parking Information', + }, + { + id: 'Electromobility', + label: 'Electromobility', + }, + { + id: 'Traffic Signs and Speed Information', + label: 'Traffic Signs and Speed Information', + }, + { + id: 'Weather Information', + label: 'Weather Information', + }, + { + id: 'Public Transport Information', + label: 'Public Transport Information', + }, + { + id: 'Shared and On-Demand Mobility', + label: 'Shared and On-Demand Mobility', + }, + { + id: 'Infrastructure and Logistics', + label: 'Infrastructure and Logistics', + }, + { + id: 'Various', + label: 'Various', + }, +]; diff --git a/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select-item.service.ts b/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select-item.service.ts new file mode 100644 index 000000000..2a4d27614 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select-item.service.ts @@ -0,0 +1,34 @@ +import {Injectable} from '@angular/core'; +import {DATA_CATEGORY_SELECT_DATA} from './data-category-select-data'; +import {DataCategorySelectItem} from './data-category-select-item'; + +/** + * Access list of available DataCategorySelectItems + */ +@Injectable({providedIn: 'root'}) +export class DataCategorySelectItemService { + itemsById: Map; + + constructor() { + this.itemsById = this.buildItemsMap(); + } + + /** + * Find DataCategorySelectItem by id + * @param id language select item id + */ + findById(id: string): DataCategorySelectItem { + const item = this.itemsById.get(id); + if (item != null) { + return item; + } + return { + id, + label: id, + }; + } + + private buildItemsMap(): Map { + return new Map(DATA_CATEGORY_SELECT_DATA.map((it) => [it.id, it])); + } +} diff --git a/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select-item.ts b/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select-item.ts new file mode 100644 index 000000000..dbf30de59 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select-item.ts @@ -0,0 +1,4 @@ +export interface DataCategorySelectItem { + id: string; + label: string; +} diff --git a/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select.component.html b/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select.component.html new file mode 100644 index 000000000..9b6585a5a --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select.component.html @@ -0,0 +1,9 @@ + + {{ label }} + + + + {{ item.label }} + + + diff --git a/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select.component.ts b/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select.component.ts new file mode 100644 index 000000000..eff3b4595 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/data-category-select/data-category-select.component.ts @@ -0,0 +1,22 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {DATA_CATEGORY_SELECT_DATA} from './data-category-select-data'; +import {DataCategorySelectItem} from './data-category-select-item'; + +@Component({ + selector: 'data-category-select', + templateUrl: 'data-category-select.component.html', +}) +export class DataCategorySelectComponent { + @Input() + label!: string; + + @Input() + control!: FormControl; + + @HostBinding('class.flex') + @HostBinding('class.flex-row') + cls = true; + + items = DATA_CATEGORY_SELECT_DATA; +} diff --git a/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-items.pipe.ts b/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-items.pipe.ts new file mode 100644 index 000000000..00c8b8c18 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-items.pipe.ts @@ -0,0 +1,17 @@ +import {Pipe, PipeTransform} from '@angular/core'; +import {DataCategorySelectItem} from '../data-category-select/data-category-select-item'; +import {DataSubcategorySelectItem} from './data-subcategory-select-item'; +import {DataSubcategorySelectItemService} from './data-subcategory-select-item.service'; + +@Pipe({ + name: 'dataSubcategoryItems', +}) +export class DataSubcategoryItemsPipe implements PipeTransform { + constructor(private items: DataSubcategorySelectItemService) {} + + transform( + dataCategory: DataCategorySelectItem | null, + ): DataSubcategorySelectItem[] { + return this.items.findByDataCategory(dataCategory); + } +} diff --git a/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select-data.ts b/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select-data.ts new file mode 100644 index 000000000..ebe31cdc8 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select-data.ts @@ -0,0 +1,189 @@ +import {DataSubcategorySelectItem} from './data-subcategory-select-item'; + +export const DATA_SUBCATEGORY_SELECT_DATA: DataSubcategorySelectItem[] = [ + { + id: 'Accidents', + dataCategoryId: 'Traffic Information', + label: 'Accidents', + }, + + { + id: 'Hazard Warnings', + dataCategoryId: 'Traffic Information', + label: 'Hazard Warnings', + }, + + { + id: 'Roadworks', + dataCategoryId: 'Roadworks and Road Conditions', + label: 'Roadworks', + }, + + { + id: 'Road Conditions', + dataCategoryId: 'Roadworks and Road Conditions', + label: 'Road Conditions', + }, + + { + id: 'Realtime Traffic Flow Data', + dataCategoryId: 'Traffic Flow Information', + label: 'Realtime Traffic Flow Data', + }, + + { + id: 'Forecast Traffic Flow Data', + dataCategoryId: 'Traffic Flow Information', + label: 'Forecast Traffic Flow Data', + }, + + { + id: 'Availability and Forecast', + dataCategoryId: 'Parking Information', + label: 'Availability and Forecast', + }, + + { + id: 'Prices', + dataCategoryId: 'Parking Information', + label: 'Prices', + }, + + { + id: 'Location of Charging Station', + dataCategoryId: 'Electromobility', + label: 'Location of Charging Station', + }, + + { + id: 'Prices at Charging Station', + dataCategoryId: 'Electromobility', + label: 'Prices at Charging Station', + }, + + { + id: 'Availability of Charging Station', + dataCategoryId: 'Electromobility', + label: 'Availability of Charging Station', + }, + + { + id: 'Dynamic Speed Information', + dataCategoryId: 'Traffic Signs and Speed Information', + label: 'Dynamic Speed Information', + }, + + { + id: 'Dynamic Traffic Signs', + dataCategoryId: 'Traffic Signs and Speed Information', + label: 'Dynamic Traffic Signs', + }, + + { + id: 'Static Traffic Signs', + dataCategoryId: 'Traffic Signs and Speed Information', + label: 'Static Traffic Signs', + }, + + { + id: 'Current Weather Conditions', + dataCategoryId: 'Weather Information', + label: 'Current weather conditions', + }, + + { + id: 'Weather Forecast ', + dataCategoryId: 'Weather Information', + label: 'Weather Forecast ', + }, + + { + id: 'Special Events or Disruptions', + dataCategoryId: 'Weather Information', + label: 'Special Events or Disruptions', + }, + + { + id: 'Timetables', + dataCategoryId: 'Public Transport Information', + label: 'Timetables', + }, + + { + id: 'Fare', + dataCategoryId: 'Public Transport Information', + label: 'Fare', + }, + + { + id: 'Location Information', + dataCategoryId: 'Public Transport Information', + label: 'Location Information', + }, + + { + id: 'Vehicle Information ', + dataCategoryId: 'Shared and On-Demand Mobility', + label: 'Vehicle information', + }, + + { + id: 'Availability ', + dataCategoryId: 'Shared and On-Demand Mobility', + label: 'Availability', + }, + + { + id: 'Location ', + dataCategoryId: 'Shared and On-Demand Mobility', + label: 'Location', + }, + + { + id: 'Range ', + dataCategoryId: 'Shared and On-Demand Mobility', + label: 'Range', + }, + + { + id: 'General Information About Planning Of Routes', + dataCategoryId: 'Infrastructure and Logistics', + label: 'General Information About Planning Of Routes', + }, + + { + id: 'Pedestrian Networks', + dataCategoryId: 'Infrastructure and Logistics', + label: 'Pedestrian Networks', + }, + + { + id: 'Cycling Networks', + dataCategoryId: 'Infrastructure and Logistics', + label: 'Cycling Networks', + }, + + { + id: 'Road Network', + dataCategoryId: 'Infrastructure and Logistics', + label: 'Road Network', + }, + + { + id: 'Water Routes', + dataCategoryId: 'Infrastructure and Logistics', + label: 'Water Routes', + }, + + { + id: 'Cargo Logistics', + dataCategoryId: 'Infrastructure and Logistics', + label: 'Cargo and Logistics', + }, + + { + id: 'Toll Information', + dataCategoryId: 'Infrastructure and Logistics', + label: 'Toll Information', + }, +]; diff --git a/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select-item.service.ts b/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select-item.service.ts new file mode 100644 index 000000000..bd38df5f2 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select-item.service.ts @@ -0,0 +1,44 @@ +import {Injectable} from '@angular/core'; +import {associateBy, groupedBy} from '../../../../core/utils/map-utils'; +import {DataCategorySelectItem} from '../data-category-select/data-category-select-item'; +import {DATA_SUBCATEGORY_SELECT_DATA} from './data-subcategory-select-data'; +import {DataSubcategorySelectItem} from './data-subcategory-select-item'; + +/** + * Access list of available DataSubcategorySelectItems + */ +@Injectable({providedIn: 'root'}) +export class DataSubcategorySelectItemService { + itemsById = associateBy(DATA_SUBCATEGORY_SELECT_DATA, (it) => it.id); + itemsByDataCategory = groupedBy( + DATA_SUBCATEGORY_SELECT_DATA, + (it) => it.dataCategoryId, + ); + + /** + * Find DataSubcategorySelectItem by id + * @param id language select item id + */ + findById(id: string): DataSubcategorySelectItem { + const item = this.itemsById.get(id); + if (item != null) { + return item; + } + return { + id, + dataCategoryId: '', + label: id, + }; + } + + /** + * Find DataCategorySelectItems by (parent) data category + */ + findByDataCategory( + dataCategory: DataCategorySelectItem | null, + ): DataSubcategorySelectItem[] { + return dataCategory + ? this.itemsByDataCategory.get(dataCategory?.id) ?? [] + : []; + } +} diff --git a/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select-item.ts b/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select-item.ts new file mode 100644 index 000000000..cef088969 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select-item.ts @@ -0,0 +1,5 @@ +export interface DataSubcategorySelectItem { + id: string; + dataCategoryId: string; + label: string; +} diff --git a/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select.component.html b/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select.component.html new file mode 100644 index 000000000..f622010ad --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select.component.html @@ -0,0 +1,11 @@ + + {{ label }} + + - + + {{ item.label }} + + + diff --git a/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select.component.ts b/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select.component.ts new file mode 100644 index 000000000..217b9de41 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/data-subcategory-select/data-subcategory-select.component.ts @@ -0,0 +1,23 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {DataCategorySelectItem} from '../data-category-select/data-category-select-item'; +import {DataSubcategorySelectItem} from './data-subcategory-select-item'; + +@Component({ + selector: 'data-subcategory-select', + templateUrl: 'data-subcategory-select.component.html', +}) +export class DataSubcategorySelectComponent { + @Input() + label!: string; + + @Input() + dataCategory: DataCategorySelectItem | null = null; + + @Input() + control!: FormControl; + + @HostBinding('class.flex') + @HostBinding('class.flex-row') + cls = true; +} diff --git a/src/app/routes/connector-ui/asset-page/keyword-select/keyword-select.component.html b/src/app/routes/connector-ui/asset-page/keyword-select/keyword-select.component.html new file mode 100644 index 000000000..198ba94b7 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/keyword-select/keyword-select.component.html @@ -0,0 +1,22 @@ + + {{ label }} + + + {{ keyword }} + + + + + diff --git a/src/app/routes/connector-ui/asset-page/keyword-select/keyword-select.component.ts b/src/app/routes/connector-ui/asset-page/keyword-select/keyword-select.component.ts new file mode 100644 index 000000000..bdfb24b83 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/keyword-select/keyword-select.component.ts @@ -0,0 +1,38 @@ +import {COMMA, ENTER, SEMICOLON} from '@angular/cdk/keycodes'; +import {Component, HostBinding, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {MatChipInputEvent} from '@angular/material/chips'; +import {removeOnce} from '../../../../core/utils/array-utils'; + +@Component({ + selector: 'keyword-select', + templateUrl: 'keyword-select.component.html', +}) +export class KeywordSelectComponent { + separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON]; + + @Input() + label!: string; + + @Input() + control!: FormControl; + + @HostBinding('class.flex') + @HostBinding('class.flex-row') + cls = true; + + remove(keyword: string) { + this.control.setValue(removeOnce(this.control.value, keyword)); + } + + add(event: MatChipInputEvent): void { + const keywords = (event.value || '') + .split(/[,;]/) + .map((it) => it.trim()) + .filter((it) => it); + if (keywords.length) { + this.control.setValue([...this.control.value, ...keywords]); + } + event.chipInput.clear(); + } +} diff --git a/src/app/routes/connector-ui/asset-page/language-select/language-select-data.ts b/src/app/routes/connector-ui/asset-page/language-select/language-select-data.ts new file mode 100644 index 000000000..79740613f --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/language-select/language-select-data.ts @@ -0,0 +1,1299 @@ +import {LanguageSelectItem} from './language-select-item'; + +export const LANGUAGE_SELECT_DATA: LanguageSelectItem[] = [ + { + id: 'https://w3id.org/idsa/code/MULTI_LINGUAL', + idShort: 'MULTI_LINGUAL', + label: 'Multilingual', + comment: + 'Code indicates that several languages are used or no concrete language can be determined.', + }, + + { + id: 'https://w3id.org/idsa/code/AB', + idShort: 'AB', + label: 'Abkhaz', + sameAs: 'https://dbpedia.org/resource/ISO_639:ab', + }, + + { + id: 'https://w3id.org/idsa/code/AA', + idShort: 'AA', + label: 'Afar', + sameAs: 'https://dbpedia.org/resource/ISO_639:aa', + }, + + { + id: 'https://w3id.org/idsa/code/AF', + idShort: 'AF', + label: 'Afrikaans', + sameAs: 'https://dbpedia.org/resource/ISO_639:af', + }, + + { + id: 'https://w3id.org/idsa/code/AK', + idShort: 'AK', + label: 'Akan', + sameAs: 'https://dbpedia.org/resource/ISO_639:ak', + }, + + { + id: 'https://w3id.org/idsa/code/SQ', + idShort: 'SQ', + label: 'Albanian', + sameAs: 'https://dbpedia.org/resource/ISO_639:sq', + }, + + { + id: 'https://w3id.org/idsa/code/AM', + idShort: 'AM', + label: 'Amharic', + sameAs: 'https://dbpedia.org/resource/ISO_639:am', + }, + + { + id: 'https://w3id.org/idsa/code/AR', + idShort: 'AR', + label: 'Arabic', + sameAs: 'https://dbpedia.org/resource/ISO_639:ar', + }, + + { + id: 'https://w3id.org/idsa/code/AN', + idShort: 'AN', + label: 'Aragonese', + sameAs: 'https://dbpedia.org/resource/ISO_639:an', + }, + + { + id: 'https://w3id.org/idsa/code/HY', + idShort: 'HY', + label: 'Armenian', + sameAs: 'https://dbpedia.org/resource/ISO_639:hy', + }, + + { + id: 'https://w3id.org/idsa/code/AS', + idShort: 'AS', + label: 'Assamese', + sameAs: 'https://dbpedia.org/resource/ISO_639:as', + }, + + { + id: 'https://w3id.org/idsa/code/AV', + idShort: 'AV', + label: 'Avaric', + sameAs: 'https://dbpedia.org/resource/ISO_639:av', + }, + + { + id: 'https://w3id.org/idsa/code/AE', + idShort: 'AE', + label: 'Avestan', + sameAs: 'https://dbpedia.org/resource/ISO_639:ae', + }, + + { + id: 'https://w3id.org/idsa/code/AY', + idShort: 'AY', + label: 'Aymara', + sameAs: 'https://dbpedia.org/resource/ISO_639:ay', + }, + + { + id: 'https://w3id.org/idsa/code/AZ', + idShort: 'AZ', + label: 'Azerbaijani', + sameAs: 'https://dbpedia.org/resource/ISO_639:az', + }, + + { + id: 'https://w3id.org/idsa/code/BM', + idShort: 'BM', + label: 'Bambara', + sameAs: 'https://dbpedia.org/resource/ISO_639:bm', + }, + + { + id: 'https://w3id.org/idsa/code/BA', + idShort: 'BA', + label: 'Bashkir', + sameAs: 'https://dbpedia.org/resource/ISO_639:ba', + }, + + { + id: 'https://w3id.org/idsa/code/EU', + idShort: 'EU', + label: 'Basque', + sameAs: 'https://dbpedia.org/resource/ISO_639:eu', + }, + + { + id: 'https://w3id.org/idsa/code/BE', + idShort: 'BE', + label: 'Belarusian', + sameAs: 'https://dbpedia.org/resource/ISO_639:be', + }, + + { + id: 'https://w3id.org/idsa/code/BN', + idShort: 'BN', + label: 'Bengali, Bangla', + sameAs: 'https://dbpedia.org/resource/ISO_639:bn', + }, + + { + id: 'https://w3id.org/idsa/code/BH', + idShort: 'BH', + label: 'Bihari', + sameAs: 'https://dbpedia.org/resource/ISO_639:bh', + }, + + { + id: 'https://w3id.org/idsa/code/BI', + idShort: 'BI', + label: 'Bislama', + sameAs: 'https://dbpedia.org/resource/ISO_639:bi', + }, + + { + id: 'https://w3id.org/idsa/code/BS', + idShort: 'BS', + label: 'Bosnian', + sameAs: 'https://dbpedia.org/resource/ISO_639:bs', + }, + + { + id: 'https://w3id.org/idsa/code/BR', + idShort: 'BR', + label: 'Breton', + sameAs: 'https://dbpedia.org/resource/ISO_639:br', + }, + + { + id: 'https://w3id.org/idsa/code/BG', + idShort: 'BG', + label: 'Bulgarian', + sameAs: 'https://dbpedia.org/resource/ISO_639:bg', + }, + + { + id: 'https://w3id.org/idsa/code/MY', + idShort: 'MY', + label: 'Burmese', + sameAs: 'https://dbpedia.org/resource/ISO_639:my', + }, + + { + id: 'https://w3id.org/idsa/code/CA', + idShort: 'CA', + label: 'Catalan', + sameAs: 'https://dbpedia.org/resource/ISO_639:ca', + }, + + { + id: 'https://w3id.org/idsa/code/CH', + idShort: 'CH', + label: 'Chamorro', + sameAs: 'https://dbpedia.org/resource/ISO_639:ch', + }, + + { + id: 'https://w3id.org/idsa/code/CE', + idShort: 'CE', + label: 'Chechen', + sameAs: 'https://dbpedia.org/resource/ISO_639:ce', + }, + + { + id: 'https://w3id.org/idsa/code/NY', + idShort: 'NY', + label: 'Chichewa, Chewa, Nyanja', + sameAs: 'https://dbpedia.org/resource/ISO_639:ny', + }, + + { + id: 'https://w3id.org/idsa/code/ZH', + idShort: 'ZH', + label: 'Chinese', + sameAs: 'https://dbpedia.org/resource/ISO_639:zh', + }, + + { + id: 'https://w3id.org/idsa/code/CV', + idShort: 'CV', + label: 'Chuvash', + sameAs: 'https://dbpedia.org/resource/ISO_639:cv', + }, + + { + id: 'https://w3id.org/idsa/code/KW', + idShort: 'KW', + label: 'Cornish', + sameAs: 'https://dbpedia.org/resource/ISO_639:kw', + }, + + { + id: 'https://w3id.org/idsa/code/CO', + idShort: 'CO', + label: 'Corsican', + sameAs: 'https://dbpedia.org/resource/ISO_639:co', + }, + + { + id: 'https://w3id.org/idsa/code/CR', + idShort: 'CR', + label: 'Cree', + sameAs: 'https://dbpedia.org/resource/ISO_639:cr', + }, + + { + id: 'https://w3id.org/idsa/code/HR', + idShort: 'HR', + label: 'Croatian', + sameAs: 'https://dbpedia.org/resource/ISO_639:hr', + }, + + { + id: 'https://w3id.org/idsa/code/CS', + idShort: 'CS', + label: 'Czech', + sameAs: 'https://dbpedia.org/resource/ISO_639:cs', + }, + + { + id: 'https://w3id.org/idsa/code/DA', + idShort: 'DA', + label: 'Danish', + sameAs: 'https://dbpedia.org/resource/ISO_639:da', + }, + + { + id: 'https://w3id.org/idsa/code/DV', + idShort: 'DV', + label: 'Divehi, Dhivehi, Maldivian', + sameAs: 'https://dbpedia.org/resource/ISO_639:dv', + }, + + { + id: 'https://w3id.org/idsa/code/NL', + idShort: 'NL', + label: 'Dutch', + sameAs: 'https://dbpedia.org/resource/ISO_639:nl', + }, + + { + id: 'https://w3id.org/idsa/code/DZ', + idShort: 'DZ', + label: 'Dzongkha', + sameAs: 'https://dbpedia.org/resource/ISO_639:dz', + }, + + { + id: 'https://w3id.org/idsa/code/EN', + idShort: 'EN', + label: 'English', + sameAs: 'https://dbpedia.org/resource/ISO_639:en', + }, + + { + id: 'https://w3id.org/idsa/code/EO', + idShort: 'EO', + label: 'Esperanto', + sameAs: 'https://dbpedia.org/resource/ISO_639:eo', + }, + + { + id: 'https://w3id.org/idsa/code/ET', + idShort: 'ET', + label: 'Estonian', + sameAs: 'https://dbpedia.org/resource/ISO_639:et', + }, + + { + id: 'https://w3id.org/idsa/code/EE', + idShort: 'EE', + label: 'Ewe', + sameAs: 'https://dbpedia.org/resource/ISO_639:ee', + }, + + { + id: 'https://w3id.org/idsa/code/FO', + idShort: 'FO', + label: 'Faroese', + sameAs: 'https://dbpedia.org/resource/ISO_639:fo', + }, + + { + id: 'https://w3id.org/idsa/code/FJ', + idShort: 'FJ', + label: 'Fijian', + sameAs: 'https://dbpedia.org/resource/ISO_639:fj', + }, + + { + id: 'https://w3id.org/idsa/code/FI', + idShort: 'FI', + label: 'Finnish', + sameAs: 'https://dbpedia.org/resource/ISO_639:fi', + }, + + { + id: 'https://w3id.org/idsa/code/FR', + idShort: 'FR', + label: 'French', + sameAs: 'https://dbpedia.org/resource/ISO_639:fr', + }, + + { + id: 'https://w3id.org/idsa/code/FF', + idShort: 'FF', + label: 'Fula, Fulah, Pulaar, Pular', + sameAs: 'https://dbpedia.org/resource/ISO_639:ff', + }, + + { + id: 'https://w3id.org/idsa/code/GL', + idShort: 'GL', + label: 'Galician', + sameAs: 'https://dbpedia.org/resource/ISO_639:gl', + }, + + { + id: 'https://w3id.org/idsa/code/KA', + idShort: 'KA', + label: 'Georgian', + sameAs: 'https://dbpedia.org/resource/ISO_639:ka', + }, + + { + id: 'https://w3id.org/idsa/code/DE', + idShort: 'DE', + label: 'German', + sameAs: 'https://dbpedia.org/resource/ISO_639:de', + }, + + { + id: 'https://w3id.org/idsa/code/EL', + idShort: 'EL', + label: 'Greek (modern)', + sameAs: 'https://dbpedia.org/resource/ISO_639:el', + }, + + { + id: 'https://w3id.org/idsa/code/GN', + idShort: 'GN', + label: 'Guaraní', + sameAs: 'https://dbpedia.org/resource/ISO_639:gn', + }, + + { + id: 'https://w3id.org/idsa/code/GU', + idShort: 'GU', + label: 'Gujarati', + sameAs: 'https://dbpedia.org/resource/ISO_639:gu', + }, + + { + id: 'https://w3id.org/idsa/code/HT', + idShort: 'HT', + label: 'Haitian, Haitian Creole', + sameAs: 'https://dbpedia.org/resource/ISO_639:ht', + }, + + { + id: 'https://w3id.org/idsa/code/HA', + idShort: 'HA', + label: 'Hausa', + sameAs: 'https://dbpedia.org/resource/ISO_639:ha', + }, + + { + id: 'https://w3id.org/idsa/code/HE', + idShort: 'HE', + label: 'Hebrew (modern)', + sameAs: 'https://dbpedia.org/resource/ISO_639:he', + }, + + { + id: 'https://w3id.org/idsa/code/HZ', + idShort: 'HZ', + label: 'Herero', + sameAs: 'https://dbpedia.org/resource/ISO_639:hz', + }, + + { + id: 'https://w3id.org/idsa/code/HI', + idShort: 'HI', + label: 'Hindi', + sameAs: 'https://dbpedia.org/resource/ISO_639:hi', + }, + + { + id: 'https://w3id.org/idsa/code/HO', + idShort: 'HO', + label: 'Hiri Motu', + sameAs: 'https://dbpedia.org/resource/ISO_639:ho', + }, + + { + id: 'https://w3id.org/idsa/code/HU', + idShort: 'HU', + label: 'Hungarian', + sameAs: 'https://dbpedia.org/resource/ISO_639:hu', + }, + + { + id: 'https://w3id.org/idsa/code/IA', + idShort: 'IA', + label: 'Interlingua', + sameAs: 'https://dbpedia.org/resource/ISO_639:ia', + }, + + { + id: 'https://w3id.org/idsa/code/ID', + idShort: 'ID', + label: 'Indonesian', + sameAs: 'https://dbpedia.org/resource/ISO_639:id', + }, + + { + id: 'https://w3id.org/idsa/code/IE', + idShort: 'IE', + label: 'Interlingue', + sameAs: 'https://dbpedia.org/resource/ISO_639:ie', + }, + + { + id: 'https://w3id.org/idsa/code/GA', + idShort: 'GA', + label: 'Irish', + sameAs: 'https://dbpedia.org/resource/ISO_639:ga', + }, + + { + id: 'https://w3id.org/idsa/code/IG', + idShort: 'IG', + label: 'Igbo', + sameAs: 'https://dbpedia.org/resource/ISO_639:ig', + }, + + { + id: 'https://w3id.org/idsa/code/IK', + idShort: 'IK', + label: 'Inupiaq', + sameAs: 'https://dbpedia.org/resource/ISO_639:ik', + }, + + { + id: 'https://w3id.org/idsa/code/IO', + idShort: 'IO', + label: 'Ido', + sameAs: 'https://dbpedia.org/resource/ISO_639:io', + }, + + { + id: 'https://w3id.org/idsa/code/IS', + idShort: 'IS', + label: 'Icelandic', + sameAs: 'https://dbpedia.org/resource/ISO_639:is', + }, + + { + id: 'https://w3id.org/idsa/code/IT', + idShort: 'IT', + label: 'Italian', + sameAs: 'https://dbpedia.org/resource/ISO_639:it', + }, + + { + id: 'https://w3id.org/idsa/code/IU', + idShort: 'IU', + label: 'Inuktitut', + sameAs: 'https://dbpedia.org/resource/ISO_639:iu', + }, + + { + id: 'https://w3id.org/idsa/code/JA', + idShort: 'JA', + label: 'Japanese', + sameAs: 'https://dbpedia.org/resource/ISO_639:ja', + }, + + { + id: 'https://w3id.org/idsa/code/JV', + idShort: 'JV', + label: 'Javanese', + sameAs: 'https://dbpedia.org/resource/ISO_639:jv', + }, + + { + id: 'https://w3id.org/idsa/code/KL', + idShort: 'KL', + label: 'Kalaallisut, Greenlandic', + sameAs: 'https://dbpedia.org/resource/ISO_639:kl', + }, + + { + id: 'https://w3id.org/idsa/code/KN', + idShort: 'KN', + label: 'Kannada', + sameAs: 'https://dbpedia.org/resource/ISO_639:kn', + }, + + { + id: 'https://w3id.org/idsa/code/KR', + idShort: 'KR', + label: 'Kanuri', + sameAs: 'https://dbpedia.org/resource/ISO_639:kr', + }, + + { + id: 'https://w3id.org/idsa/code/KS', + idShort: 'KS', + label: 'Kashmiri', + sameAs: 'https://dbpedia.org/resource/ISO_639:ks', + }, + + { + id: 'https://w3id.org/idsa/code/KK', + idShort: 'KK', + label: 'Kazakh', + sameAs: 'https://dbpedia.org/resource/ISO_639:kk', + }, + + { + id: 'https://w3id.org/idsa/code/KM', + idShort: 'KM', + label: 'Khmer', + sameAs: 'https://dbpedia.org/resource/ISO_639:km', + }, + + { + id: 'https://w3id.org/idsa/code/KI', + idShort: 'KI', + label: 'Kikuyu, Gikuyu', + sameAs: 'https://dbpedia.org/resource/ISO_639:ki', + }, + + { + id: 'https://w3id.org/idsa/code/RW', + idShort: 'RW', + label: 'Kinyarwanda', + sameAs: 'https://dbpedia.org/resource/ISO_639:rw', + }, + + { + id: 'https://w3id.org/idsa/code/KY', + idShort: 'KY', + label: 'Kyrgyz', + sameAs: 'https://dbpedia.org/resource/ISO_639:ky', + }, + + { + id: 'https://w3id.org/idsa/code/KV', + idShort: 'KV', + label: 'Komi', + sameAs: 'https://dbpedia.org/resource/ISO_639:kv', + }, + + { + id: 'https://w3id.org/idsa/code/KG', + idShort: 'KG', + label: 'Kongo', + sameAs: 'https://dbpedia.org/resource/ISO_639:kg', + }, + + { + id: 'https://w3id.org/idsa/code/KO', + idShort: 'KO', + label: 'Korean', + sameAs: 'https://dbpedia.org/resource/ISO_639:ko', + }, + + { + id: 'https://w3id.org/idsa/code/KU', + idShort: 'KU', + label: 'Kurdish', + sameAs: 'https://dbpedia.org/resource/ISO_639:ku', + }, + + { + id: 'https://w3id.org/idsa/code/KJ', + idShort: 'KJ', + label: 'Kwanyama, Kuanyama', + sameAs: 'https://dbpedia.org/resource/ISO_639:kj', + }, + + { + id: 'https://w3id.org/idsa/code/LA', + idShort: 'LA', + label: 'Latin', + sameAs: 'https://dbpedia.org/resource/ISO_639:la', + }, + + { + id: 'https://w3id.org/idsa/code/LB', + idShort: 'LB', + label: 'Luxembourgish, Letzeburgesch', + sameAs: 'https://dbpedia.org/resource/ISO_639:lb', + }, + + { + id: 'https://w3id.org/idsa/code/LG', + idShort: 'LG', + label: 'Ganda', + sameAs: 'https://dbpedia.org/resource/ISO_639:lg', + }, + + { + id: 'https://w3id.org/idsa/code/LI', + idShort: 'LI', + label: 'Limburgish, Limburgan, Limburger', + sameAs: 'https://dbpedia.org/resource/ISO_639:li', + }, + + { + id: 'https://w3id.org/idsa/code/LN', + idShort: 'LN', + label: 'Lingala', + sameAs: 'https://dbpedia.org/resource/ISO_639:ln', + }, + + { + id: 'https://w3id.org/idsa/code/LO', + idShort: 'LO', + label: 'Lao', + sameAs: 'https://dbpedia.org/resource/ISO_639:lo', + }, + + { + id: 'https://w3id.org/idsa/code/LT', + idShort: 'LT', + label: 'Lithuanian', + sameAs: 'https://dbpedia.org/resource/ISO_639:lt', + }, + + { + id: 'https://w3id.org/idsa/code/LU', + idShort: 'LU', + label: 'Luba-Katanga', + sameAs: 'https://dbpedia.org/resource/ISO_639:lu', + }, + + { + id: 'https://w3id.org/idsa/code/LV', + idShort: 'LV', + label: 'Latvian', + sameAs: 'https://dbpedia.org/resource/ISO_639:lv', + }, + + { + id: 'https://w3id.org/idsa/code/GV', + idShort: 'GV', + label: 'Manx', + sameAs: 'https://dbpedia.org/resource/ISO_639:gv', + }, + + { + id: 'https://w3id.org/idsa/code/MK', + idShort: 'MK', + label: 'Macedonian', + sameAs: 'https://dbpedia.org/resource/ISO_639:mk', + }, + + { + id: 'https://w3id.org/idsa/code/MG', + idShort: 'MG', + label: 'Malagasy', + sameAs: 'https://dbpedia.org/resource/ISO_639:mg', + }, + + { + id: 'https://w3id.org/idsa/code/MS', + idShort: 'MS', + label: 'Malay', + sameAs: 'https://dbpedia.org/resource/ISO_639:ms', + }, + + { + id: 'https://w3id.org/idsa/code/ML', + idShort: 'ML', + label: 'Malayalam', + sameAs: 'https://dbpedia.org/resource/ISO_639:ml', + }, + + { + id: 'https://w3id.org/idsa/code/MT', + idShort: 'MT', + label: 'Maltese', + sameAs: 'https://dbpedia.org/resource/ISO_639:mt', + }, + + { + id: 'https://w3id.org/idsa/code/MI', + idShort: 'MI', + label: 'Māori', + sameAs: 'https://dbpedia.org/resource/ISO_639:mi', + }, + + { + id: 'https://w3id.org/idsa/code/MR', + idShort: 'MR', + label: 'Marathi (Marāṭhī)', + sameAs: 'https://dbpedia.org/resource/ISO_639:mr', + }, + + { + id: 'https://w3id.org/idsa/code/MH', + idShort: 'MH', + label: 'Marshallese', + sameAs: 'https://dbpedia.org/resource/ISO_639:mh', + }, + + { + id: 'https://w3id.org/idsa/code/MN', + idShort: 'MN', + label: 'Mongolian', + sameAs: 'https://dbpedia.org/resource/ISO_639:mn', + }, + + { + id: 'https://w3id.org/idsa/code/NA', + idShort: 'NA', + label: 'Nauruan', + sameAs: 'https://dbpedia.org/resource/ISO_639:na', + }, + + { + id: 'https://w3id.org/idsa/code/NV', + idShort: 'NV', + label: 'Navajo, Navaho', + sameAs: 'https://dbpedia.org/resource/ISO_639:nv', + }, + + { + id: 'https://w3id.org/idsa/code/ND', + idShort: 'ND', + label: 'Northern Ndebele', + sameAs: 'https://dbpedia.org/resource/ISO_639:nd', + }, + + { + id: 'https://w3id.org/idsa/code/NE', + idShort: 'NE', + label: 'Nepali', + sameAs: 'https://dbpedia.org/resource/ISO_639:ne', + }, + + { + id: 'https://w3id.org/idsa/code/NG', + idShort: 'NG', + label: 'Ndonga', + sameAs: 'https://dbpedia.org/resource/ISO_639:ng', + }, + + { + id: 'https://w3id.org/idsa/code/NB', + idShort: 'NB', + label: 'Norwegian Bokmål', + sameAs: 'https://dbpedia.org/resource/ISO_639:nb', + }, + + { + id: 'https://w3id.org/idsa/code/NN', + idShort: 'NN', + label: 'Norwegian Nynorsk', + sameAs: 'https://dbpedia.org/resource/ISO_639:nn', + }, + + { + id: 'https://w3id.org/idsa/code/NO', + idShort: 'NO', + label: 'Norwegian', + sameAs: 'https://dbpedia.org/resource/ISO_639:no', + }, + + { + id: 'https://w3id.org/idsa/code/II', + idShort: 'II', + label: 'Nuosu', + sameAs: 'https://dbpedia.org/resource/ISO_639:ii', + }, + + { + id: 'https://w3id.org/idsa/code/NR', + idShort: 'NR', + label: 'Southern Ndebele', + sameAs: 'https://dbpedia.org/resource/ISO_639:nr', + }, + + { + id: 'https://w3id.org/idsa/code/OC', + idShort: 'OC', + label: 'Occitan', + sameAs: 'https://dbpedia.org/resource/ISO_639:oc', + }, + + { + id: 'https://w3id.org/idsa/code/OJ', + idShort: 'OJ', + label: 'Ojibwe, Ojibwa', + sameAs: 'https://dbpedia.org/resource/ISO_639:oj', + }, + + { + id: 'https://w3id.org/idsa/code/CU', + idShort: 'CU', + label: 'Old Church Slavonic, Church Slavonic, Old Bulgarian', + sameAs: 'https://dbpedia.org/resource/ISO_639:cu', + }, + + { + id: 'https://w3id.org/idsa/code/OM', + idShort: 'OM', + label: 'Oromo', + sameAs: 'https://dbpedia.org/resource/ISO_639:om', + }, + + { + id: 'https://w3id.org/idsa/code/OR', + idShort: 'OR', + label: 'Oriya', + sameAs: 'https://dbpedia.org/resource/ISO_639:or', + }, + + { + id: 'https://w3id.org/idsa/code/OS', + idShort: 'OS', + label: 'Ossetian, Ossetic', + sameAs: 'https://dbpedia.org/resource/ISO_639:os', + }, + + { + id: 'https://w3id.org/idsa/code/PA', + idShort: 'PA', + label: '(Eastern) Punjabi', + sameAs: 'https://dbpedia.org/resource/ISO_639:pa', + }, + + { + id: 'https://w3id.org/idsa/code/PI', + idShort: 'PI', + label: 'Pāli', + sameAs: 'https://dbpedia.org/resource/ISO_639:pi', + }, + + { + id: 'https://w3id.org/idsa/code/FA', + idShort: 'FA', + label: 'Persian (Farsi)', + sameAs: 'https://dbpedia.org/resource/ISO_639:fa', + }, + + { + id: 'https://w3id.org/idsa/code/PL', + idShort: 'PL', + label: 'Polish', + sameAs: 'https://dbpedia.org/resource/ISO_639:pl', + }, + + { + id: 'https://w3id.org/idsa/code/PS', + idShort: 'PS', + label: 'Pashto, Pushto', + sameAs: 'https://dbpedia.org/resource/ISO_639:ps', + }, + + { + id: 'https://w3id.org/idsa/code/PT', + idShort: 'PT', + label: 'Portuguese', + sameAs: 'https://dbpedia.org/resource/ISO_639:pt', + }, + + { + id: 'https://w3id.org/idsa/code/QU', + idShort: 'QU', + label: 'Quechua', + sameAs: 'https://dbpedia.org/resource/ISO_639:qu', + }, + + { + id: 'https://w3id.org/idsa/code/RM', + idShort: 'RM', + label: 'Romansh', + sameAs: 'https://dbpedia.org/resource/ISO_639:rm', + }, + + { + id: 'https://w3id.org/idsa/code/RN', + idShort: 'RN', + label: 'Kirundi', + sameAs: 'https://dbpedia.org/resource/ISO_639:rn', + }, + + { + id: 'https://w3id.org/idsa/code/RO', + idShort: 'RO', + label: 'Romanian', + sameAs: 'https://dbpedia.org/resource/ISO_639:ro', + }, + + { + id: 'https://w3id.org/idsa/code/RU', + idShort: 'RU', + label: 'Russian', + sameAs: 'https://dbpedia.org/resource/ISO_639:ru', + }, + + { + id: 'https://w3id.org/idsa/code/SA', + idShort: 'SA', + label: 'Sanskrit (Saṁskṛta)', + sameAs: 'https://dbpedia.org/resource/ISO_639:sa', + }, + + { + id: 'https://w3id.org/idsa/code/SC', + idShort: 'SC', + label: 'Sardinian', + sameAs: 'https://dbpedia.org/resource/ISO_639:sc', + }, + + { + id: 'https://w3id.org/idsa/code/SD', + idShort: 'SD', + label: 'Sindhi', + sameAs: 'https://dbpedia.org/resource/ISO_639:sd', + }, + + { + id: 'https://w3id.org/idsa/code/SE', + idShort: 'SE', + label: 'Northern Sami', + sameAs: 'https://dbpedia.org/resource/ISO_639:se', + }, + + { + id: 'https://w3id.org/idsa/code/SM', + idShort: 'SM', + label: 'Samoan', + sameAs: 'https://dbpedia.org/resource/ISO_639:sm', + }, + + { + id: 'https://w3id.org/idsa/code/SG', + idShort: 'SG', + label: 'Sango', + sameAs: 'https://dbpedia.org/resource/ISO_639:sg', + }, + + { + id: 'https://w3id.org/idsa/code/SR', + idShort: 'SR', + label: 'Serbian', + sameAs: 'https://dbpedia.org/resource/ISO_639:sr', + }, + + { + id: 'https://w3id.org/idsa/code/GD', + idShort: 'GD', + label: 'Scottish Gaelic, Gaelic', + sameAs: 'https://dbpedia.org/resource/ISO_639:gd', + }, + + { + id: 'https://w3id.org/idsa/code/SN', + idShort: 'SN', + label: 'Shona', + sameAs: 'https://dbpedia.org/resource/ISO_639:sn', + }, + + { + id: 'https://w3id.org/idsa/code/SI', + idShort: 'SI', + label: 'Sinhalese, Sinhala', + sameAs: 'https://dbpedia.org/resource/ISO_639:si', + }, + + { + id: 'https://w3id.org/idsa/code/SK', + idShort: 'SK', + label: 'Slovak', + sameAs: 'https://dbpedia.org/resource/ISO_639:sk', + }, + + { + id: 'https://w3id.org/idsa/code/SL', + idShort: 'SL', + label: 'Slovene', + sameAs: 'https://dbpedia.org/resource/ISO_639:sl', + }, + + { + id: 'https://w3id.org/idsa/code/SO', + idShort: 'SO', + label: 'Somali', + sameAs: 'https://dbpedia.org/resource/ISO_639:so', + }, + + { + id: 'https://w3id.org/idsa/code/ST', + idShort: 'ST', + label: 'outhern Sotho', + sameAs: 'https://dbpedia.org/resource/ISO_639:st', + }, + + { + id: 'https://w3id.org/idsa/code/ES', + idShort: 'ES', + label: 'Spanish', + sameAs: 'https://dbpedia.org/resource/ISO_639:es', + }, + + { + id: 'https://w3id.org/idsa/code/SU', + idShort: 'SU', + label: 'Sundanese', + sameAs: 'https://dbpedia.org/resource/ISO_639:su', + }, + + { + id: 'https://w3id.org/idsa/code/SW', + idShort: 'SW', + label: 'Swahili', + sameAs: 'https://dbpedia.org/resource/ISO_639:sw', + }, + + { + id: 'https://w3id.org/idsa/code/SS', + idShort: 'SS', + label: 'Swati', + sameAs: 'https://dbpedia.org/resource/ISO_639:ss', + }, + + { + id: 'https://w3id.org/idsa/code/SV', + idShort: 'SV', + label: 'Swedish', + sameAs: 'https://dbpedia.org/resource/ISO_639:sv', + }, + + { + id: 'https://w3id.org/idsa/code/TA', + idShort: 'TA', + label: 'Tamil', + sameAs: 'https://dbpedia.org/resource/ISO_639:ta', + }, + + { + id: 'https://w3id.org/idsa/code/TE', + idShort: 'TE', + label: 'Telugu', + sameAs: 'https://dbpedia.org/resource/ISO_639:te', + }, + + { + id: 'https://w3id.org/idsa/code/TG', + idShort: 'TG', + label: 'Tajik', + sameAs: 'https://dbpedia.org/resource/ISO_639:tg', + }, + + { + id: 'https://w3id.org/idsa/code/TH', + idShort: 'TH', + label: 'Thai', + sameAs: 'https://dbpedia.org/resource/ISO_639:th', + }, + + { + id: 'https://w3id.org/idsa/code/TI', + idShort: 'TI', + label: 'Tigrinya', + sameAs: 'https://dbpedia.org/resource/ISO_639:ti', + }, + + { + id: 'https://w3id.org/idsa/code/BO', + idShort: 'BO', + label: 'Tibetan Standard, Tibetan, Central', + sameAs: 'https://dbpedia.org/resource/ISO_639:bo', + }, + + { + id: 'https://w3id.org/idsa/code/TK', + idShort: 'TK', + label: 'Turkmen', + sameAs: 'https://dbpedia.org/resource/ISO_639:tk', + }, + + { + id: 'https://w3id.org/idsa/code/TL', + idShort: 'TL', + label: 'Tagalog', + sameAs: 'https://dbpedia.org/resource/ISO_639:tl', + }, + + { + id: 'https://w3id.org/idsa/code/TN', + idShort: 'TN', + label: 'Tswana', + sameAs: 'https://dbpedia.org/resource/ISO_639:tn', + }, + + { + id: 'https://w3id.org/idsa/code/TO', + idShort: 'TO', + label: 'Tonga (Tonga Islands)', + sameAs: 'https://dbpedia.org/resource/ISO_639:to', + }, + + { + id: 'https://w3id.org/idsa/code/TR', + idShort: 'TR', + label: 'Turkish', + sameAs: 'https://dbpedia.org/resource/ISO_639:tr', + }, + + { + id: 'https://w3id.org/idsa/code/TS', + idShort: 'TS', + label: 'Tsonga', + sameAs: 'https://dbpedia.org/resource/ISO_639:ts', + }, + + { + id: 'https://w3id.org/idsa/code/TT', + idShort: 'TT', + label: 'Tatar', + sameAs: 'https://dbpedia.org/resource/ISO_639:tt', + }, + + { + id: 'https://w3id.org/idsa/code/TW', + idShort: 'TW', + label: 'Twi', + sameAs: 'https://dbpedia.org/resource/ISO_639:tw', + }, + + { + id: 'https://w3id.org/idsa/code/TY', + idShort: 'TY', + label: 'Tahitian', + sameAs: 'https://dbpedia.org/resource/ISO_639:ty', + }, + + { + id: 'https://w3id.org/idsa/code/UG', + idShort: 'UG', + label: 'Uyghur', + sameAs: 'https://dbpedia.org/resource/ISO_639:ug', + }, + + { + id: 'https://w3id.org/idsa/code/UK', + idShort: 'UK', + label: 'Ukrainian', + sameAs: 'https://dbpedia.org/resource/ISO_639:uk', + }, + + { + id: 'https://w3id.org/idsa/code/UR', + idShort: 'UR', + label: 'Urdu', + sameAs: 'https://dbpedia.org/resource/ISO_639:ur', + }, + + { + id: 'https://w3id.org/idsa/code/UZ', + idShort: 'UZ', + label: 'Uzbek', + sameAs: 'https://dbpedia.org/resource/ISO_639:uz', + }, + + { + id: 'https://w3id.org/idsa/code/VE', + idShort: 'VE', + label: 'Venda', + sameAs: 'https://dbpedia.org/resource/ISO_639:ve', + }, + + { + id: 'https://w3id.org/idsa/code/VI', + idShort: 'VI', + label: 'Vietnamese', + sameAs: 'https://dbpedia.org/resource/ISO_639:vi', + }, + + { + id: 'https://w3id.org/idsa/code/VO', + idShort: 'VO', + label: 'Volapük', + sameAs: 'https://dbpedia.org/resource/ISO_639:vo', + }, + + { + id: 'https://w3id.org/idsa/code/WA', + idShort: 'WA', + label: 'Walloon', + sameAs: 'https://dbpedia.org/resource/ISO_639:wa', + }, + + { + id: 'https://w3id.org/idsa/code/CY', + idShort: 'CY', + label: 'Welsh', + sameAs: 'https://dbpedia.org/resource/ISO_639:cy', + }, + + { + id: 'https://w3id.org/idsa/code/WO', + idShort: 'WO', + label: 'Wolof', + sameAs: 'https://dbpedia.org/resource/ISO_639:wo', + }, + + { + id: 'https://w3id.org/idsa/code/FY', + idShort: 'FY', + label: 'Western Frisian', + sameAs: 'https://dbpedia.org/resource/ISO_639:fy', + }, + + { + id: 'https://w3id.org/idsa/code/XH', + idShort: 'XH', + label: 'Xhosa', + sameAs: 'https://dbpedia.org/resource/ISO_639:xh', + }, + + { + id: 'https://w3id.org/idsa/code/YI', + idShort: 'YI', + label: 'Yiddish', + sameAs: 'https://dbpedia.org/resource/ISO_639:yi', + }, + + { + id: 'https://w3id.org/idsa/code/YO', + idShort: 'YO', + label: 'Yoruba', + sameAs: 'https://dbpedia.org/resource/ISO_639:yo', + }, + + { + id: 'https://w3id.org/idsa/code/ZA', + idShort: 'ZA', + label: 'Zhuang, Chuang', + sameAs: 'https://dbpedia.org/resource/ISO_639:za', + }, + + { + id: 'https://w3id.org/idsa/code/ZU', + idShort: 'ZU', + label: 'Zulu', + sameAs: 'https://dbpedia.org/resource/ISO_639:zu', + }, +]; diff --git a/src/app/routes/connector-ui/asset-page/language-select/language-select-item.service.ts b/src/app/routes/connector-ui/asset-page/language-select/language-select-item.service.ts new file mode 100644 index 000000000..4c1f8da9a --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/language-select/language-select-item.service.ts @@ -0,0 +1,70 @@ +import {Injectable} from '@angular/core'; +import {LANGUAGE_SELECT_DATA} from './language-select-data'; +import {LanguageSelectItem} from './language-select-item'; + +/** + * Access list of available LanguageSelectItems + */ +@Injectable({providedIn: 'root'}) +export class LanguageSelectItemService { + /** + * Partition LanguageSelectItems into highlighted and other. + * Usability: See important options first and close to each other. + */ + highlightItemIds = [ + 'https://w3id.org/idsa/code/MULTI_LINGUAL', + 'https://w3id.org/idsa/code/DE', + 'https://w3id.org/idsa/code/EN', + ]; + highlightItems: LanguageSelectItem[]; + otherItems: LanguageSelectItem[]; + itemsByKeyword: Map; + + constructor() { + this.highlightItems = this.buildHighlightItems(); + this.otherItems = this.buildOtherItems(); + this.itemsByKeyword = this.buildItemLookupMap(); + } + + /** + * Find LanguageSelectItem by id + * @param id language select item id + */ + findById(id: string): LanguageSelectItem { + let item = this.itemsByKeyword.get(id); + if (!item) { + item = {id, label: id}; + } + return item; + } + + english(): LanguageSelectItem { + return this.findById('https://w3id.org/idsa/code/EN'); + } + + private buildHighlightItems(): LanguageSelectItem[] { + return LANGUAGE_SELECT_DATA.filter((it) => + this.highlightItemIds.includes(it.id), + ); + } + + private buildOtherItems(): LanguageSelectItem[] { + return LANGUAGE_SELECT_DATA.filter( + (it) => !this.highlightItemIds.includes(it.id), + ); + } + + private buildItemLookupMap(): Map { + const map = new Map(); + LANGUAGE_SELECT_DATA.forEach((it) => { + map.set(it.id, it); + if (it.idShort) { + map.set(it.idShort, it); + } + if (it.sameAs) { + map.set(it.sameAs, it); + } + }); + return map; + } +} diff --git a/src/app/routes/connector-ui/asset-page/language-select/language-select-item.ts b/src/app/routes/connector-ui/asset-page/language-select/language-select-item.ts new file mode 100644 index 000000000..175da8983 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/language-select/language-select-item.ts @@ -0,0 +1,7 @@ +export interface LanguageSelectItem { + id: string; + label: string; + comment?: string; + idShort?: string; + sameAs?: string; +} diff --git a/src/app/routes/connector-ui/asset-page/language-select/language-select.component.html b/src/app/routes/connector-ui/asset-page/language-select/language-select.component.html new file mode 100644 index 000000000..76859b135 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/language-select/language-select.component.html @@ -0,0 +1,16 @@ + + {{ label }} + + - + + {{ item.label }} + + + + {{ item.label }} + + + diff --git a/src/app/routes/connector-ui/asset-page/language-select/language-select.component.ts b/src/app/routes/connector-ui/asset-page/language-select/language-select.component.ts new file mode 100644 index 000000000..d90ca3822 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/language-select/language-select.component.ts @@ -0,0 +1,22 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {LanguageSelectItem} from './language-select-item'; +import {LanguageSelectItemService} from './language-select-item.service'; + +@Component({ + selector: 'language-select', + templateUrl: 'language-select.component.html', +}) +export class LanguageSelectComponent { + @Input() + label!: string; + + @Input() + control!: FormControl; + + @HostBinding('class.flex') + @HostBinding('class.flex-row') + cls = true; + + constructor(public items: LanguageSelectItemService) {} +} diff --git a/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select-data.ts b/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select-data.ts new file mode 100644 index 000000000..9444eb723 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select-data.ts @@ -0,0 +1,23 @@ +import {TransportModeSelectItem} from './transport-mode-select-item'; + +export const TRANSPORT_MODE_SELECT_DATA: TransportModeSelectItem[] = [ + { + id: 'Rail', + label: 'Rail', + }, + + { + id: 'Road', + label: 'Road', + }, + + { + id: 'Water', + label: 'Water', + }, + + { + id: 'Air', + label: 'Air', + }, +]; diff --git a/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select-item.service.ts b/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select-item.service.ts new file mode 100644 index 000000000..78b905432 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select-item.service.ts @@ -0,0 +1,27 @@ +import {Injectable} from '@angular/core'; +import {associateBy} from '../../../../core/utils/map-utils'; +import {TRANSPORT_MODE_SELECT_DATA} from './transport-mode-select-data'; +import {TransportModeSelectItem} from './transport-mode-select-item'; + +/** + * Access list of available TransportModeSelectItems + */ +@Injectable({providedIn: 'root'}) +export class TransportModeSelectItemService { + itemsById = associateBy(TRANSPORT_MODE_SELECT_DATA, (it) => it.id); + + /** + * Find TransportModeSelectItem by id + * @param id language select item id + */ + findById(id: string): TransportModeSelectItem { + const item = this.itemsById.get(id); + if (item != null) { + return item; + } + return { + id, + label: id, + }; + } +} diff --git a/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select-item.ts b/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select-item.ts new file mode 100644 index 000000000..f516b6cfa --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select-item.ts @@ -0,0 +1,4 @@ +export interface TransportModeSelectItem { + id: string; + label: string; +} diff --git a/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select.component.html b/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select.component.html new file mode 100644 index 000000000..9b6585a5a --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select.component.html @@ -0,0 +1,9 @@ + + {{ label }} + + + + {{ item.label }} + + + diff --git a/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select.component.ts b/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select.component.ts new file mode 100644 index 000000000..aa0e91685 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/transport-mode-select/transport-mode-select.component.ts @@ -0,0 +1,22 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {TRANSPORT_MODE_SELECT_DATA} from './transport-mode-select-data'; +import {TransportModeSelectItem} from './transport-mode-select-item'; + +@Component({ + selector: 'transport-mode-select', + templateUrl: 'transport-mode-select.component.html', +}) +export class TransportModeSelectComponent { + @Input() + label!: string; + + @Input() + control!: FormControl; + + @HostBinding('class.flex') + @HostBinding('class.flex-row') + cls = true; + + items = TRANSPORT_MODE_SELECT_DATA; +}