This is an automated email from the ASF dual-hosted git repository. riemer pushed a commit to branch add-adapter-preview-card in repository https://gitbox.apache.org/repos/asf/streampipes.git
commit f17e23846ecf1df782f3a5de22d2d79cd53d6dc2 Author: Dominik Riemer <[email protected]> AuthorDate: Mon Feb 23 22:43:28 2026 +0100 feat: Add feature card to adapter page --- ui/deployment/feature-cards.yml | 4 + .../live-preview-table.component.html | 52 ++++---- .../live-preview-table.component.scss | 9 ++ .../live-preview-table.component.ts | 21 ++-- .../pipeline-element-runtime-info.component.html | 1 + .../pipeline-element-runtime-info.component.scss | 17 --- .../pipeline-element-runtime-info.component.ts | 4 +- .../connect-feature-card.component.html | 116 +++++++++++++++++ .../connect-feature-card.component.scss | 99 +++++++++++++++ .../connect-feature-card.component.ts | 138 +++++++++++++++++++++ .../existing-adapters.component.html | 2 + 11 files changed, 410 insertions(+), 53 deletions(-) diff --git a/ui/deployment/feature-cards.yml b/ui/deployment/feature-cards.yml index 27b4dded52..8265f5adff 100644 --- a/ui/deployment/feature-cards.yml +++ b/ui/deployment/feature-cards.yml @@ -29,3 +29,7 @@ componentPath: './dataset/components/dataset-feature-card/dataset-feature-card.component' componentName: DatasetFeatureCardComponent moduleName: spDatasets +- id: adapter + componentPath: './connect/components/connect-feature-card/connect-feature-card.component' + componentName: ConnectFeatureCardComponent + moduleName: spConnect diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/live-preview-table/live-preview-table.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/live-preview-table/live-preview-table.component.html index 8c119527db..0b595ebf3c 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/live-preview-table/live-preview-table.component.html +++ b/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/live-preview-table/live-preview-table.component.html @@ -20,7 +20,7 @@ @if (showTitle) { <p>{{ 'Here is a preview of your data:' | translate }}</p> } - <table mat-table [dataSource]="runtimeInfo"> + <table mat-table [dataSource]="runtimeInfo" [class.compact]="compact"> <ng-container matColumnDef="runtimeName"> <th mat-header-cell *matHeaderCellDef> <strong>{{ 'Runtime Name' | translate }}</strong> @@ -30,32 +30,34 @@ </td> </ng-container> - <ng-container matColumnDef="label"> - <th mat-header-cell *matHeaderCellDef> - <strong>{{ 'Field Name' | translate }}</strong> - </th> - <td mat-cell *matCellDef="let element"> - {{ element.label }} - </td> - </ng-container> + @if (!compact) { + <ng-container matColumnDef="label"> + <th mat-header-cell *matHeaderCellDef> + <strong>{{ 'Field Name' | translate }}</strong> + </th> + <td mat-cell *matCellDef="let element"> + {{ element.label }} + </td> + </ng-container> - <ng-container matColumnDef="description"> - <th mat-header-cell *matHeaderCellDef> - <strong>{{ 'Description' | translate }}</strong> - </th> - <td mat-cell *matCellDef="let element"> - {{ element.description }} - </td> - </ng-container> + <ng-container matColumnDef="description"> + <th mat-header-cell *matHeaderCellDef> + <strong>{{ 'Description' | translate }}</strong> + </th> + <td mat-cell *matCellDef="let element"> + {{ element.description }} + </td> + </ng-container> - <ng-container matColumnDef="runtimeType"> - <th mat-header-cell *matHeaderCellDef> - <strong>{{ 'Type' | translate }}</strong> - </th> - <td mat-cell *matCellDef="let element"> - {{ element.runtimeType }} - </td> - </ng-container> + <ng-container matColumnDef="runtimeType"> + <th mat-header-cell *matHeaderCellDef> + <strong>{{ 'Type' | translate }}</strong> + </th> + <td mat-cell *matCellDef="let element"> + {{ element.runtimeType }} + </td> + </ng-container> + } <ng-container matColumnDef="value"> <th mat-header-cell *matHeaderCellDef> diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/live-preview-table/live-preview-table.component.scss b/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/live-preview-table/live-preview-table.component.scss index b6f18bbb57..c5e5de49e4 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/live-preview-table/live-preview-table.component.scss +++ b/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/live-preview-table/live-preview-table.component.scss @@ -41,3 +41,12 @@ text-align: center; background: #ffe7ad; } + +.mat-mdc-table.compact { + --mat-table-row-item-container-height: 36px; + + .mat-mdc-header-cell, + .mat-mdc-cell { + font-size: var(--font-size-xs); + } +} diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/live-preview-table/live-preview-table.component.ts b/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/live-preview-table/live-preview-table.component.ts index 88d4a8417d..fcafa4fd49 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/live-preview-table/live-preview-table.component.ts +++ b/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/live-preview-table/live-preview-table.component.ts @@ -16,7 +16,7 @@ * */ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { EventSchema } from '@streampipes/platform-services'; import { RuntimeInfo } from '../pipeline-element-runtime-info.model'; import { @@ -53,7 +53,7 @@ import { TranslatePipe } from '@ngx-translate/core'; TranslatePipe, ], }) -export class LivePreviewTableComponent { +export class LivePreviewTableComponent implements OnInit { @Input() eventSchema: EventSchema; @@ -63,13 +63,14 @@ export class LivePreviewTableComponent { @Input() showTitle = true; - displayedColumns: string[] = [ - 'runtimeName', - 'label', - 'description', - 'runtimeType', - 'value', - ]; + @Input() + compact = false; + + displayedColumns: string[] = []; - constructor() {} + ngOnInit() { + this.displayedColumns = this.compact + ? ['runtimeName', 'value'] + : ['runtimeName', 'label', 'description', 'runtimeType', 'value']; + } } diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/pipeline-element-runtime-info.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/pipeline-element-runtime-info.component.html index 8432e1835c..222e7436fb 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/pipeline-element-runtime-info.component.html +++ b/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/pipeline-element-runtime-info.component.html @@ -18,6 +18,7 @@ @if (runtimeInfo) { <sp-live-preview-table + [compact]="compact" [showTitle]="showTitle" [eventSchema]="streamDescription.eventSchema" [runtimeInfo]="runtimeInfo" diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/pipeline-element-runtime-info.component.scss b/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/pipeline-element-runtime-info.component.scss deleted file mode 100644 index 13cbc4aacb..0000000000 --- a/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/pipeline-element-runtime-info.component.scss +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/pipeline-element-runtime-info.component.ts b/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/pipeline-element-runtime-info.component.ts index 59c4d0cbfb..8acab3c382 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/pipeline-element-runtime-info.component.ts +++ b/ui/projects/streampipes/shared-ui/src/lib/components/pipeline-element-runtime-info/pipeline-element-runtime-info.component.ts @@ -38,7 +38,6 @@ import { LivePreviewErrorComponent } from './live-preview-error/live-preview-err @Component({ selector: 'sp-pipeline-element-runtime-info', templateUrl: './pipeline-element-runtime-info.component.html', - styleUrls: ['./pipeline-element-runtime-info.component.scss'], imports: [LivePreviewTableComponent, LivePreviewErrorComponent], }) export class PipelineElementRuntimeInfoComponent implements OnInit, OnDestroy { @@ -48,6 +47,9 @@ export class PipelineElementRuntimeInfoComponent implements OnInit, OnDestroy { @Input() showTitle = true; + @Input() + compact = false; + runtimeData: { runtimeName: string; value: any }[]; runtimeInfo: RuntimeInfo[]; timer: any; diff --git a/ui/src/app/connect/components/connect-feature-card/connect-feature-card.component.html b/ui/src/app/connect/components/connect-feature-card/connect-feature-card.component.html new file mode 100644 index 0000000000..1e6c4ca372 --- /dev/null +++ b/ui/src/app/connect/components/connect-feature-card/connect-feature-card.component.html @@ -0,0 +1,116 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You under the Apache License, Version 2.0 + ~ (the "License"); you may not use this file except in compliance with + ~ the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + --> + +@if (adapter) { + <div fxFlexFill fxLayout="column" class="feature-card-outer"> + <sp-feature-card-header + [title]="adapter.name" + [description]="adapter.description" + [icon]="assetLinkType?.linkIcon" + [iconColor]="assetLinkType?.linkColor" + [detailsLink]="assetLinkType?.navPaths" + (close)="onClose?.()" + (onDetailsClick)="navigateToAdapter()" + > + </sp-feature-card-header> + + <div class="feature-card-meta-card meta-card"> + <sp-feature-card-meta-section [label]="'Status' | translate"> + <div + class="feature-card-meta-box" + fxLayout="row" + fxLayoutAlign="start center" + fxLayoutGap="0.75rem" + > + <mat-icon class="feature-card-meta-icon"> + {{ adapter.running ? 'play_circle' : 'pause_circle' }} + </mat-icon> + <div> + <div class="feature-card-meta-field-label"> + {{ 'Adapter status' | translate }} + </div> + <sp-label + class="mt-xs" + textCase="uppercase" + [tone]="getStatusTone()" + size="small" + variant="soft" + > + {{ getStatusLabel() | translate }} + </sp-label> + </div> + </div> + </sp-feature-card-meta-section> + + <div class="feature-card-meta-section__label"> + {{ 'Preview info' | translate }} + </div> + <div class="adapter-info-table" fxLayout="column"> + <div class="adapter-info-row"> + <span class="adapter-info-key">{{ + 'Created' | translate + }}</span> + <span class="adapter-info-value">{{ + formatDate(adapter.createdAt) + }}</span> + </div> + + <div class="adapter-info-row"> + <span class="adapter-info-key">{{ + 'Output stream' | translate + }}</span> + <span class="adapter-info-value">{{ + getOutputStreamName() + }}</span> + </div> + + <div class="adapter-info-row"> + <span class="adapter-info-key">{{ + 'Schema fields' | translate + }}</span> + <span class="adapter-info-value">{{ + getFieldCount() + }}</span> + </div> + </div> + </div> + + <div fxFlex fxLayout="column" class="feature-preview-wrapper"> + <div class="adapter-live-preview"> + <div class="feature-card-meta-section__label"> + {{ 'Live preview' | translate }} + </div> + + <div class="adapter-live-preview__content"> + @if (streamDescription) { + <sp-pipeline-element-runtime-info + [compact]="true" + [showTitle]="false" + [streamDescription]="streamDescription" + > + </sp-pipeline-element-runtime-info> + } @else { + <span class="adapter-preview-empty">{{ + 'No live preview available.' | translate + }}</span> + } + </div> + </div> + </div> + </div> +} diff --git a/ui/src/app/connect/components/connect-feature-card/connect-feature-card.component.scss b/ui/src/app/connect/components/connect-feature-card/connect-feature-card.component.scss new file mode 100644 index 0000000000..93dec1fe3e --- /dev/null +++ b/ui/src/app/connect/components/connect-feature-card/connect-feature-card.component.scss @@ -0,0 +1,99 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +:host { + width: 100%; + height: 100%; +} + +.meta-card { + display: flex; + flex-direction: column; +} + +.adapter-info-table { + gap: 0.25rem; +} + +.adapter-info-row { + display: grid; + grid-template-columns: minmax(7rem, 38%) minmax(0, 1fr); + gap: 0.75rem; + align-items: start; + padding: 0.5rem 0.625rem; + border-radius: 0.375rem; + border: 1px solid transparent; +} + +.adapter-info-row:nth-child(odd) { + background: var(--color-bg-1); + border-color: color-mix(in srgb, var(--color-bg-3) 45%, transparent); +} + +.adapter-info-row:nth-child(even) { + background: var(--color-bg-0); + border-color: color-mix(in srgb, var(--color-bg-3) 65%, transparent); +} + +.adapter-info-key { + font-size: var(--text-xs, 0.75rem); + font-weight: 600; + opacity: 0.78; + line-height: 1.2; + word-break: break-word; + text-transform: capitalize; + letter-spacing: 0.03em; + padding-top: 0.125rem; +} + +.adapter-info-value { + min-width: 0; + font-size: var(--text-sm, 0.85rem); + font-weight: var(--font-weight-bold); + line-height: 1.3; + overflow-wrap: anywhere; + border-left: 1px solid var(--color-bg-3); + padding-left: 0.75rem; + opacity: 0.95; +} + +.adapter-live-preview { + display: flex; + flex-direction: column; + flex: 1 1 auto; + min-height: 0; + gap: 0.5rem; +} + +.adapter-live-preview__content { + flex: 1 1 auto; + min-height: 0; + overflow: auto; + border: 1px solid var(--color-bg-3); + border-radius: 0.5rem; + padding: 0.25rem; +} + +.adapter-preview-empty { + display: block; + padding: 0.5rem; +} + +.adapter-live-preview__content > sp-pipeline-element-runtime-info { + display: block; +} diff --git a/ui/src/app/connect/components/connect-feature-card/connect-feature-card.component.ts b/ui/src/app/connect/components/connect-feature-card/connect-feature-card.component.ts new file mode 100644 index 0000000000..6caa979090 --- /dev/null +++ b/ui/src/app/connect/components/connect-feature-card/connect-feature-card.component.ts @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Component, inject, Input, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { FlexFillDirective } from '@ngbracket/ngx-layout'; +import { + FlexDirective, + LayoutAlignDirective, + LayoutDirective, + LayoutGapDirective, +} from '@ngbracket/ngx-layout/flex'; +import { MatIcon } from '@angular/material/icon'; +import { TranslatePipe } from '@ngx-translate/core'; +import { forkJoin } from 'rxjs'; +import { + AdapterDescription, + AdapterService, + AssetConstants, + AssetLinkType, + GenericStorageService, + PipelineElementService, + SpDataStream, +} from '@streampipes/platform-services'; +import { + DateFormatService, + FeatureCardHeaderComponent, + FeatureCardMetaSectionComponent, + PipelineElementRuntimeInfoComponent, + SpLabelComponent, +} from '@streampipes/shared-ui'; + +@Component({ + selector: 'sp-connect-feature-card', + templateUrl: './connect-feature-card.component.html', + styleUrls: ['./connect-feature-card.component.scss'], + imports: [ + FlexFillDirective, + FlexDirective, + LayoutDirective, + LayoutAlignDirective, + LayoutGapDirective, + MatIcon, + TranslatePipe, + FeatureCardHeaderComponent, + FeatureCardMetaSectionComponent, + SpLabelComponent, + PipelineElementRuntimeInfoComponent, + ], +}) +export class ConnectFeatureCardComponent implements OnInit { + @Input() + resourceId: string; + + @Input() + onClose?: () => void; + + adapter: AdapterDescription; + assetLinkType: AssetLinkType; + streamDescription: SpDataStream; + + private adapterService = inject(AdapterService); + private genericStorageService = inject(GenericStorageService); + private pipelineElementService = inject(PipelineElementService); + private dateFormatService = inject(DateFormatService); + private router = inject(Router); + + ngOnInit(): void { + forkJoin([ + this.adapterService.getAdapter(this.resourceId), + this.genericStorageService.getAllDocuments( + AssetConstants.ASSET_LINK_TYPES_DOC_NAME, + ), + ]).subscribe(([adapter, assetLinkTypes]) => { + this.adapter = adapter; + this.assetLinkType = assetLinkTypes.find( + link => link.linkType === 'adapter', + ); + + if (adapter?.correspondingDataStreamElementId) { + this.pipelineElementService + .getDataStreamByElementId( + adapter.correspondingDataStreamElementId, + ) + .subscribe(stream => { + this.streamDescription = stream; + }); + } + }); + } + + formatDate(timestamp?: number): string { + return this.dateFormatService.formatDate(timestamp); + } + + getFieldCount(): number { + return ( + this.streamDescription?.eventSchema?.eventProperties?.length ?? 0 + ); + } + + getOutputStreamName(): string { + return ( + this.streamDescription?.name || + this.adapter?.dataStream?.name || + this.adapter?.correspondingDataStreamElementId || + '–' + ); + } + + getStatusLabel(): string { + return this.adapter?.running ? 'Running' : 'Stopped'; + } + + getStatusTone(): 'success' | 'error' { + return this.adapter?.running ? 'success' : 'error'; + } + + navigateToAdapter(): void { + this.onClose?.(); + this.router.navigate(['connect', 'details', this.resourceId, 'data']); + } +} diff --git a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html index d4369bfba1..c389dcb574 100644 --- a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html +++ b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html @@ -95,6 +95,8 @@ <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start"> <sp-table fxFlex="100" + featureCardId="adapter" + resourceIdKey="elementId" [columns]="displayedColumns" [dataSource]="dataSource" [showActionsMenu]="true"
