From af80ecb58ea8e15179b174c67bb35c07c59b523f Mon Sep 17 00:00:00 2001 From: Henry Taeschner Date: Sat, 27 Jan 2024 19:39:11 +0100 Subject: [PATCH] feat: added app search --- .../app-search/app-search.component.html | 111 ++++++++++++++ .../app-search/app-search.component.scss | 13 ++ .../app-search/app-search.component.ts | 143 ++++++++++++++++++ .../product-search.component.html | 4 +- .../product-search.component.ts | 36 +++-- src/app/product-store/product-store.module.ts | 14 ++ src/assets/i18n/de.json | 31 +++- src/assets/i18n/en.json | 29 +++- 8 files changed, 359 insertions(+), 22 deletions(-) create mode 100644 src/app/product-store/app-search/app-search.component.html create mode 100644 src/app/product-store/app-search/app-search.component.scss create mode 100644 src/app/product-store/app-search/app-search.component.ts diff --git a/src/app/product-store/app-search/app-search.component.html b/src/app/product-store/app-search/app-search.component.html new file mode 100644 index 0000000..7033600 --- /dev/null +++ b/src/app/product-store/app-search/app-search.component.html @@ -0,0 +1,111 @@ + + +
+
+ + + + + + + + + + + + +
+
+
+ + +
+ +
+ + + + + + +
+
+
+
{{ limitText(app.appId, 25) }}
+
{{ limitText(app.appName, 30) }}
+
{{ limitText(app.productName, 25) }}
+
+
+
+
+
+
+
+
diff --git a/src/app/product-store/app-search/app-search.component.scss b/src/app/product-store/app-search/app-search.component.scss new file mode 100644 index 0000000..fdfe64a --- /dev/null +++ b/src/app/product-store/app-search/app-search.component.scss @@ -0,0 +1,13 @@ +:host ::ng-deep { + .p-dataview-content .p-grid > div { + min-height: 60px; + &.listview-row { + &:nth-child(odd) { + background-color: #f8f9fa; + } + } + } + .h-05rem { + height: 0.5rem; + } +} diff --git a/src/app/product-store/app-search/app-search.component.ts b/src/app/product-store/app-search/app-search.component.ts new file mode 100644 index 0000000..cc8789b --- /dev/null +++ b/src/app/product-store/app-search/app-search.component.ts @@ -0,0 +1,143 @@ +import { Component, OnInit, ViewChild } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { Observable, finalize } from 'rxjs' +import { DataView } from 'primeng/dataview' +import { TranslateService } from '@ngx-translate/core' +import { Action, DataViewControlTranslations } from '@onecx/portal-integration-angular' +import { MicrofrontendPageResult, MicrofrontendsAPIService } from '../../generated' +import { limitText } from '../../shared/utils' +import { FormControl, FormGroup } from '@angular/forms' + +export interface MicrofrontendSearchCriteria { + appId: FormControl + appName: FormControl + productName: FormControl +} + +@Component({ + templateUrl: './app-search.component.html', + styleUrls: ['./app-search.component.scss'] +}) +export class AppSearchComponent implements OnInit { + public apps$!: Observable + public appSearchCriteriaGroup!: FormGroup + public actions: Action[] = [] + public viewMode = 'grid' + public filter: string | undefined + public sortField = 'appName' + public sortOrder = 1 + public searchInProgress = false + public limitText = limitText + + public dataViewControlsTranslations: DataViewControlTranslations = {} + @ViewChild(DataView) dv: DataView | undefined + + constructor( + private route: ActivatedRoute, + private router: Router, + private appApi: MicrofrontendsAPIService, + private translate: TranslateService + ) { + this.appSearchCriteriaGroup = new FormGroup({ + appId: new FormControl(null), + appName: new FormControl(null), + productName: new FormControl(null) + }) + } + + ngOnInit(): void { + this.prepareTranslations() + this.loadProducts() + } + + public loadProducts(): void { + this.searchInProgress = true + this.apps$ = this.appApi + .searchMicrofrontends({ + microfrontendSearchCriteria: { + appId: this.appSearchCriteriaGroup.controls['appId'].value, + appName: this.appSearchCriteriaGroup.controls['appName'].value, + productName: this.appSearchCriteriaGroup.controls['productName'].value, + pageSize: 1000 + } + }) + .pipe(finalize(() => (this.searchInProgress = false))) + } + + private prepareTranslations(): void { + this.translate + .get([ + 'MICROFRONTEND.APP_ID', + 'MICROFRONTEND.APP_NAME', + 'MICROFRONTEND.PRODUCT_NAME', + 'ACTIONS.NAVIGATION.BACK', + 'ACTIONS.NAVIGATION.BACK.TOOLTIP', + 'ACTIONS.CREATE.LABEL', + 'ACTIONS.CREATE.PRODUCT.TOOLTIP', + 'ACTIONS.DATAVIEW.VIEW_MODE_LIST', + 'ACTIONS.DATAVIEW.VIEW_MODE_TABLE', + 'ACTIONS.DATAVIEW.SORT_BY', + 'ACTIONS.DATAVIEW.FILTER', + 'ACTIONS.DATAVIEW.FILTER_OF', + 'ACTIONS.DATAVIEW.SORT_DIRECTION_ASC', + 'ACTIONS.DATAVIEW.SORT_DIRECTION_DESC' + ]) + .subscribe((data) => { + this.dataViewControlsTranslations = { + sortDropdownPlaceholder: data['ACTIONS.DATAVIEW.SORT_BY'], + filterInputPlaceholder: data['ACTIONS.DATAVIEW.FILTER'], + filterInputTooltip: + data['ACTIONS.DATAVIEW.FILTER_OF'] + + data['MICROFRONTEND.APP_ID'] + + ', ' + + data['MICROFRONTEND.APP_NAME'] + + ', ' + + data['MICROFRONTEND.PRODUCT_NAME'], + viewModeToggleTooltips: { + grid: data['ACTIONS.DATAVIEW.VIEW_MODE_GRID'], + table: data['ACTIONS.DATAVIEW.VIEW_MODE_TABLE'] + }, + sortOrderTooltips: { + ascending: data['ACTIONS.DATAVIEW.SORT_DIRECTION_ASC'], + descending: data['ACTIONS.DATAVIEW.SORT_DIRECTION_DESC'] + }, + sortDropdownTooltip: data['ACTIONS.DATAVIEW.SORT_BY'] + } + this.prepareActionButtons(data) + }) + } + + private prepareActionButtons(data: any): void { + this.actions = [] // provoke change event + this.actions.push({ + label: data['ACTIONS.NAVIGATION.BACK'], + title: data['ACTIONS.NAVIGATION.BACK.TOOLTIP'], + actionCallback: () => this.onBack(), + icon: 'pi pi-arrow-left', + show: 'always' + }) + } + + public onLayoutChange(viewMode: string): void { + this.viewMode = viewMode + } + public onFilterChange(filter: string): void { + this.filter = filter + this.dv?.filter(filter, 'contains') + } + public onSortChange(field: string): void { + this.sortField = field + } + public onSortDirChange(asc: boolean): void { + this.sortOrder = asc ? -1 : 1 + } + public onSearch() { + this.loadProducts() + } + public onSearchReset() { + this.appSearchCriteriaGroup.reset() + } + public onBack() { + this.router.navigate(['../'], { relativeTo: this.route }) + } +} diff --git a/src/app/product-store/product-search/product-search.component.html b/src/app/product-store/product-search/product-search.component.html index 6d870bb..2222917 100644 --- a/src/app/product-store/product-search/product-search.component.html +++ b/src/app/product-store/product-search/product-search.component.html @@ -19,10 +19,10 @@ -
+
- + + public products$!: Observable public productSearchCriteriaGroup!: FormGroup public actions: Action[] = [] public viewMode = 'grid' @@ -48,7 +48,7 @@ export class ProductSearchComponent implements OnInit { public loadProducts(): void { this.searchInProgress = true - this.product$ = this.productApi + this.products$ = this.productApi .searchProducts({ productSearchCriteria: { name: this.productSearchCriteriaGroup.controls['productName'].value, pageSize: 1000 } }) @@ -60,6 +60,8 @@ export class ProductSearchComponent implements OnInit { .get([ 'PRODUCT.NAME', 'PRODUCT.DISPLAY_NAME', + 'DIALOG.SEARCH.APPS.LABEL', + 'DIALOG.SEARCH.APPS.TOOLTIP', 'ACTIONS.CREATE.LABEL', 'ACTIONS.CREATE.PRODUCT.TOOLTIP', 'ACTIONS.DATAVIEW.VIEW_MODE_GRID', @@ -80,7 +82,6 @@ export class ProductSearchComponent implements OnInit { viewModeToggleTooltips: { grid: data['ACTIONS.DATAVIEW.VIEW_MODE_GRID'], list: data['ACTIONS.DATAVIEW.VIEW_MODE_LIST'] - // table: data['ACTIONS.DATAVIEW.VIEW_MODE_TABLE'], }, sortOrderTooltips: { ascending: data['ACTIONS.DATAVIEW.SORT_DIRECTION_ASC'], @@ -94,14 +95,24 @@ export class ProductSearchComponent implements OnInit { private prepareActionButtons(data: any): void { this.actions = [] // provoke change event - this.actions.push({ - label: data['ACTIONS.CREATE.LABEL'], - title: data['ACTIONS.CREATE.PRODUCT.TOOLTIP'], - actionCallback: () => this.onNewProduct(), - permission: 'PRODUCT#EDIT', - icon: 'pi pi-plus', - show: 'always' - }) + this.actions.push( + { + label: data['DIALOG.SEARCH.APPS.LABEL'], + title: data['DIALOG.SEARCH.APPS.TOOLTIP'], + actionCallback: () => this.onAppSearch(), + permission: 'MICROFRONTEND#SEARCH', + icon: 'pi pi-cog', + show: 'always' + }, + { + label: data['ACTIONS.CREATE.LABEL'], + title: data['ACTIONS.CREATE.PRODUCT.TOOLTIP'], + actionCallback: () => this.onNewProduct(), + permission: 'PRODUCT#CREATE', + icon: 'pi pi-plus', + show: 'always' + } + ) } public onLayoutChange(viewMode: string): void { @@ -126,4 +137,7 @@ export class ProductSearchComponent implements OnInit { public onNewProduct() { this.router.navigate(['./new'], { relativeTo: this.route }) } + public onAppSearch() { + this.router.navigate(['./apps'], { relativeTo: this.route }) + } } diff --git a/src/app/product-store/product-store.module.ts b/src/app/product-store/product-store.module.ts index ba2fb81..442186a 100644 --- a/src/app/product-store/product-store.module.ts +++ b/src/app/product-store/product-store.module.ts @@ -11,6 +11,7 @@ import { MFE_INFO, PortalCoreModule, MyMissingTranslationHandler } from '@onecx/ import { CanActivateGuard } from '../shared/can-active-guard.service' import { LabelResolver } from '../shared/label.resolver' import { HttpLoaderFactory, SharedModule } from '../shared/shared.module' +import { AppSearchComponent } from './app-search/app-search.component' import { ProductSearchComponent } from './product-search/product-search.component' import { ProductDetailComponent } from './product-detail/product-detail.component' import { ProductPropertyComponent } from './product-detail/product-props/product-props.component' @@ -24,6 +25,18 @@ const routes: Routes = [ canActivate: [CanActivateGuard], pathMatch: 'full' }, + { + path: 'apps', + component: AppSearchComponent, + canActivate: [CanActivateGuard], + data: { + breadcrumb: 'BREADCRUMBS.APPS', + breadcrumbFn: (data: any) => `${data.labeli18n}` + }, + resolve: { + labeli18n: LabelResolver + } + }, { path: 'new', canActivate: [CanActivateGuard], @@ -51,6 +64,7 @@ const routes: Routes = [ ] @NgModule({ declarations: [ + AppSearchComponent, ProductSearchComponent, ProductDetailComponent, ProductPropertyComponent, diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 8d539b9..4142ccf 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -99,6 +99,7 @@ } }, "BREADCRUMBS": { + "APPS": "Apps", "CREATE": "Neu", "DETAIL": "Details", "EDIT": "Ă„ndern" @@ -128,15 +129,19 @@ "SEARCH.HEADER": "Product Store", "SEARCH.SUBHEADER": "Produkte und deren Apps verwalten", "DETAIL.SUBHEADER": "Produkt Details", + "SEARCH.APPS.LABEL": "Microfrontends", + "SEARCH.APPS.TOOLTIP": "Microfrontends suchen und anzeigen", + "SEARCH.APPS.HEADER": "Microfrontends", + "SEARCH.APPS.SUBHEADER": "Suchen und verwalten", "TABS": { "APPS": "Apps", "INTERN": "Intern", "PROPERTIES": "Eigenschaften" } }, - "APP": { - "APP_ID": "ID", - "APP_NAME": "Name", + "MICROFRONTEND": { + "APP_ID": "App ID", + "APP_NAME": "App-Name", "APP_VERSION": "Version", "DESCRIPTION": "Beschreibung", "PRODUCT_NAME": "Produkt", @@ -145,10 +150,26 @@ "REMOTE_ENTRY": "Remote Entry", "CLASSIFICATIONS": "Klassifizierungen", "CONTACT": "Kontakt", - "ICON_NAME": "Iconname", + "ICON_NAME": "Icon-Name", "NOTE": "Notiz", "EXPOSED_MODULE": "Exposed Module", - "ENDPOINTS": "UI Endpunkte" + "ENDPOINTS": "UI Endpunkte", + "TOOLTIPS": { + "APP_ID": "Eindeutige ID - Basis Bezeichner des Microfrontends", + "APP_NAME": "Name des Microfrontends", + "APP_VERSION": "Version", + "DESCRIPTION": "Beschreibung", + "PRODUCT_NAME": "Name des Produkts dem das Microfrontend zugeordnet ist", + "TECHNOLOGY": "Technologie", + "REMOTE_BASE_URL": "Remote Base URL", + "REMOTE_ENTRY": "Remote Entry", + "CLASSIFICATIONS": "Klassifizierungen", + "CONTACT": "Kontakt", + "ICON_NAME": "Iconname", + "NOTE": "Notiz", + "EXPOSED_MODULE": "Exposed Module", + "ENDPOINTS": "UI Endpunkte" + } }, "PRODUCT": { "ID": "ID", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 8443ee6..ef8a3dd 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -103,6 +103,7 @@ } }, "BREADCRUMBS": { + "APPS": "Apps", "CREATE": "New", "DETAIL": "Details", "EDIT": "Edit" @@ -132,15 +133,19 @@ "SEARCH.HEADER": "Product Store", "SEARCH.SUBHEADER": "Viewing and Managing Products and their Apps", "DETAIL.SUBHEADER": "Product Details", + "SEARCH.APPS.LABEL": "Microfrontends", + "SEARCH.APPS.TOOLTIP": "Search and Display Microfrontends", + "SEARCH.APPS.HEADER": "Microfrontends", + "SEARCH.APPS.SUBHEADER": "Searching and Viewing Microfrontends", "TABS": { "APPS": "Apps", "INTERN": "Internal", "PROPERTIES": "Properties" } }, - "APP": { - "APP_ID": "ID", - "APP_NAME": "Name", + "MICROFRONTEND": { + "APP_ID": "App ID", + "APP_NAME": "App Name", "APP_VERSION": "Version", "DESCRIPTION": "Description", "PRODUCT_NAME": "Product", @@ -152,7 +157,23 @@ "ICON_NAME": "Icon Name", "NOTE": "Note", "EXPOSED_MODULE": "Exposed Module", - "ENDPOINTS": "UI Endpoints" + "ENDPOINTS": "UI Endpoints", + "TOOLTIPS": { + "APP_ID": "Unique ID - base identifier for this Microfrontend", + "APP_NAME": "Business name of the Microfrontend", + "APP_VERSION": "Version", + "DESCRIPTION": "Description", + "PRODUCT_NAME": "Name of the Produkt the Microfrontend is assigned to", + "TECHNOLOGY": "Technology", + "REMOTE_BASE_URL": "Remote Base URL", + "REMOTE_ENTRY": "Remote Entry", + "CLASSIFICATIONS": "Classifications", + "CONTACT": "Contact", + "ICON_NAME": "Icon Name", + "NOTE": "Note", + "EXPOSED_MODULE": "Exposed Module", + "ENDPOINTS": "UI Endpoints" + } }, "PRODUCT": { "ID": "ID",