diff --git a/.gitignore b/.gitignore index d7f6b2e..0b0fa46 100644 --- a/.gitignore +++ b/.gitignore @@ -17,20 +17,18 @@ speed-measure-plugin*.json .idea .project .classpath -.c9/ .history .settings .vscode *.launch -*.sublime-workspace # misc .angular .eslintcache -.husky/_ .sass-cache -.gitlab* .scannerwork +.husky/_ +.gitlab* connect.lock typings *.log diff --git a/package-lock.json b/package-lock.json index c6c1954..0d66cfe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,9 +41,9 @@ "keycloak-angular": "^16.0.1", "primeflex": "^3.3.1", "primeicons": "^7.0.0", - "primeng": "^17.18.10", + "primeng": "^17.18.11", "rxjs": "7.8.1", - "tslib": "^2.7.0" + "tslib": "^2.8.0" }, "devDependencies": { "@angular-devkit/build-angular": "^18.1.2", @@ -88,7 +88,7 @@ "sonar-scanner": "^3.1.0", "sonarqube-scanner": "^4.2.3", "ts-node": "10.9.2", - "typescript": "5.5", + "typescript": "5.5.4", "url-loader": "^4.1.1", "webpack": "^5.95.0", "webpack-merge": "^6.0.1" diff --git a/package.json b/package.json index ee0d4d1..fd5d3dc 100644 --- a/package.json +++ b/package.json @@ -67,9 +67,9 @@ "keycloak-angular": "^16.0.1", "primeflex": "^3.3.1", "primeicons": "^7.0.0", - "primeng": "^17.18.10", + "primeng": "^17.18.11", "rxjs": "7.8.1", - "tslib": "^2.7.0" + "tslib": "^2.8.0" }, "devDependencies": { "@angular-devkit/build-angular": "^18.1.2", @@ -114,7 +114,7 @@ "sonar-scanner": "^3.1.0", "sonarqube-scanner": "^4.2.3", "ts-node": "10.9.2", - "typescript": "5.5", + "typescript": "5.5.4", "url-loader": "^4.1.1", "webpack": "^5.95.0", "webpack-merge": "^6.0.1" diff --git a/src/app/_ps-mixins.scss b/src/app/_ps-mixins.scss index 66b3462..74737ff 100644 --- a/src/app/_ps-mixins.scss +++ b/src/app/_ps-mixins.scss @@ -180,7 +180,7 @@ .p-autocomplete-panel .p-autocomplete-items .p-autocomplete-item { padding-top: 0.4rem; padding-bottom: 0.4rem; - &:nth-child(odd) { + &:nth-child(even) { background-color: #f8f9fa; } } @@ -377,8 +377,8 @@ @mixin dataview-list-zebra-rows { :host ::ng-deep { .p-dataview-list .p-dataview-content .p-grid > div { - &:nth-child(odd) { - background-color: #f8f9fa; + &:nth-child(even) { + background-color: var(--table-body-row-even-bg); } } } @@ -388,7 +388,7 @@ .p-dataview-list .p-dataview-content .p-grid > div { &:nth-child(4n + 1), &:nth-child(4n + 2) { - background-color: #f8f9fa; + background-color: var(--table-body-row-even-bg); } } } @@ -396,24 +396,24 @@ @mixin dataview-list-flex-zebra-rows { :host ::ng-deep { .p-dataview-list .p-dataview-content > div { - &:nth-child(odd) { - background-color: #f8f9fa; + &:nth-child(even) { + background-color: var(--table-body-row-even-bg); } } } } @mixin dropdown-zebra-rows { :host ::ng-deep { - .p-dropdown-panel .p-dropdown-items *:nth-child(odd) .p-dropdown-item { - background-color: #f8f9fa; + .p-dropdown-panel .p-dropdown-items *:nth-child(even) .p-dropdown-item { + background-color: var(--table-body-row-even-bg); } } } @mixin listbox-zebra-rows { :host ::ng-deep { .p-listbox:not(.p-disabled) .p-listbox-item:not(.p-highlight):not(.p-disabled) { - &:nth-child(odd) { - background-color: #f8f9fa; + &:nth-child(even) { + background-color: var(--table-body-row-even-bg); } } } @@ -421,16 +421,16 @@ @mixin picklist-zebra-rows { :host ::ng-deep { .p-picklist-list li { - &:nth-child(odd) { - background-color: #f8f9fa; + &:nth-child(even) { + background-color: var(--table-body-row-even-bg); } } } } @mixin table-zebra-rows { :host ::ng-deep { - .p-datatable .p-datatable-tbody > tr:nth-child(odd) > td { - background-color: #f8f9fa; + .p-datatable .p-datatable-tbody > tr:nth-child(even) > td { + background-color: var(--table-body-row-even-bg); } } } @@ -490,6 +490,15 @@ } } } +@mixin tabview-fix-color-selected-tab { + :host ::ng-deep { + // correct the background color on inital selected tab + .p-tabview .p-tabview-nav li.p-highlight .p-tabview-nav-link[aria-selected='true'] { + background-color: rgba(var(--primary-color-rgb), 0.12); + } + } +} + /* ********************************************** * TABLE * **********************************************/ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2a2fd88..87c994b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,39 +1,39 @@ -import { APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, NgModule } from '@angular/core' +import { APP_INITIALIZER, NgModule } from '@angular/core' import { CommonModule } from '@angular/common' -import { HttpClient, HttpClientModule } from '@angular/common/http' +import { HttpClient, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { RouterModule, Routes } from '@angular/router' import { BrowserModule } from '@angular/platform-browser' import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core' -import { - APP_CONFIG, - AppStateService, - createTranslateLoader, - translateServiceInitializer, - PortalCoreModule, - UserService -} from '@onecx/portal-integration-angular' import { KeycloakAuthModule } from '@onecx/keycloak-auth' +import { createTranslateLoader } from '@onecx/angular-accelerator' +import { APP_CONFIG, AppStateService, UserService } from '@onecx/angular-integration-interface' +import { translateServiceInitializer, PortalCoreModule } from '@onecx/portal-integration-angular' -import { AppComponent } from './app.component' import { environment } from 'src/environments/environment' +import { AppComponent } from './app.component' + +const routes: Routes = [ + { + path: '', + loadChildren: () => import('./product-store/product-store.module').then((m) => m.ProductStoreModule) + } +] -const routes: Routes = [{ path: '', pathMatch: 'full' }] @NgModule({ bootstrap: [AppComponent], declarations: [AppComponent], imports: [ CommonModule, BrowserModule, - HttpClientModule, - KeycloakAuthModule, BrowserAnimationsModule, + KeycloakAuthModule, + PortalCoreModule.forRoot('onecx-product-store-ui'), RouterModule.forRoot(routes, { initialNavigation: 'enabledBlocking', enableTracing: true }), - PortalCoreModule.forRoot('onecx-product-store-ui'), TranslateModule.forRoot({ isolate: true, loader: { @@ -50,12 +50,12 @@ const routes: Routes = [{ path: '', pathMatch: 'full' }] useFactory: translateServiceInitializer, multi: true, deps: [UserService, TranslateService] - } - ], - schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA] + }, + provideHttpClient(withInterceptorsFromDi()) + ] }) export class AppModule { constructor() { - console.info('App Module constructor') + console.info('OneCX Product Store Module constructor') } } diff --git a/src/app/onecx-product-store-remote.module.ts b/src/app/onecx-product-store-remote.module.ts index f78fa93..3cfd0f8 100644 --- a/src/app/onecx-product-store-remote.module.ts +++ b/src/app/onecx-product-store-remote.module.ts @@ -1,24 +1,23 @@ -import { HttpClient, HttpClientModule } from '@angular/common/http' -import { BrowserModule } from '@angular/platform-browser' import { APP_INITIALIZER, DoBootstrap, Injector, NgModule } from '@angular/core' -import { Router, RouterModule, Routes } from '@angular/router' -import { MissingTranslationHandler, TranslateLoader, TranslateModule } from '@ngx-translate/core' +import { HttpClient, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' +import { RouterModule, Routes, Router } from '@angular/router' +import { BrowserModule } from '@angular/platform-browser' +import { BrowserAnimationsModule } from '@angular/platform-browser/animations' +import { TranslateLoader, TranslateModule, MissingTranslationHandler } from '@ngx-translate/core' +import { AngularAuthModule } from '@onecx/angular-auth' +import { createTranslateLoader } from '@onecx/angular-accelerator' +import { createAppEntrypoint, initializeRouter, startsWith } from '@onecx/angular-webcomponents' +import { addInitializeModuleGuard, AppStateService, ConfigurationService } from '@onecx/angular-integration-interface' import { - AppStateService, - ConfigurationService, - createTranslateLoader, PortalApiConfiguration, PortalCoreModule, PortalMissingTranslationHandler } from '@onecx/portal-integration-angular' -import { addInitializeModuleGuard } from '@onecx/angular-integration-interface' -import { createAppEntrypoint, initializeRouter, startsWith } from '@onecx/angular-webcomponents' -import { AppEntrypointComponent } from './app-entrypoint.component' -import { AngularAuthModule } from '@onecx/angular-auth' -import { BrowserAnimationsModule } from '@angular/platform-browser/animations' -import { environment } from 'src/environments/environment' + import { Configuration } from './shared/generated' +import { environment } from 'src/environments/environment' +import { AppEntrypointComponent } from './app-entrypoint.component' function apiConfigProvider(configService: ConfigurationService, appStateService: AppStateService) { return new PortalApiConfiguration(Configuration, environment.apiPrefix, configService, appStateService) @@ -30,13 +29,13 @@ const routes: Routes = [ loadChildren: () => import('./product-store/product-store.module').then((m) => m.ProductStoreModule) } ] + @NgModule({ declarations: [AppEntrypointComponent], imports: [ AngularAuthModule, - BrowserAnimationsModule, BrowserModule, - HttpClientModule, + BrowserAnimationsModule, PortalCoreModule.forMicroFrontend(), RouterModule.forRoot(addInitializeModuleGuard(routes)), TranslateModule.forRoot({ @@ -49,18 +48,17 @@ const routes: Routes = [ missingTranslationHandler: { provide: MissingTranslationHandler, useClass: PortalMissingTranslationHandler } }) ], - exports: [], providers: [ ConfigurationService, + { provide: Configuration, useFactory: apiConfigProvider, deps: [ConfigurationService, AppStateService] }, { provide: APP_INITIALIZER, useFactory: initializeRouter, multi: true, deps: [Router, AppStateService] }, - { provide: Configuration, useFactory: apiConfigProvider, deps: [ConfigurationService, AppStateService] } - ], - schemas: [] + provideHttpClient(withInterceptorsFromDi()) + ] }) export class OneCXProductStoreModule implements DoBootstrap { constructor(private readonly injector: Injector) { diff --git a/src/app/product-store/app-delete/app-delete.component.html b/src/app/product-store/app-delete/app-delete.component.html index 1c4518b..b686306 100644 --- a/src/app/product-store/app-delete/app-delete.component.html +++ b/src/app/product-store/app-delete/app-delete.component.html @@ -10,11 +10,12 @@
-
- {{ ('ACTIONS.DELETE.MESSAGE' | translate).replace("{{ITEM}}", appAbstract?.appId ?? '') }} +
{{ 'ACTIONS.DELETE.APP.MESSAGE' | translate }}
+
+ {{ appAbstract?.appId }}
-
{{ 'ACTIONS.DELETE.APP.OPERATOR_TEXT' | translate }}
-
{{ 'ACTIONS.DELETE.MESSAGE_INFO' | translate }}
+
{{ 'ACTIONS.DELETE.APP.OPERATOR_TEXT' | translate }}
+
{{ 'ACTIONS.DELETE.MESSAGE_INFO' | translate }}
diff --git a/src/app/product-store/app-detail/app-detail.component.scss b/src/app/product-store/app-detail/app-detail.component.scss index ca0cb4d..bd65084 100644 --- a/src/app/product-store/app-detail/app-detail.component.scss +++ b/src/app/product-store/app-detail/app-detail.component.scss @@ -4,6 +4,8 @@ @include table-zebra-rows; @include table-inline-buttons; @include table-vertical-lines; +@include dialog-footer-buttons; +@include dialog-modal-dialog-content; :host ::ng-deep { /** diff --git a/src/app/product-store/app-search/app-search.component.html b/src/app/product-store/app-search/app-search.component.html index e757d6c..f81f26d 100644 --- a/src/app/product-store/app-search/app-search.component.html +++ b/src/app/product-store/app-search/app-search.component.html @@ -153,6 +153,7 @@
+
@@ -245,13 +246,20 @@
{{ app.exposedModule }}
+
+ {{ app.appVersion }} +
+
diff --git a/src/app/product-store/product-detail/product-detail.component.html b/src/app/product-store/product-detail/product-detail.component.html index 4f459eb..a4918d8 100644 --- a/src/app/product-store/product-detail/product-detail.component.html +++ b/src/app/product-store/product-detail/product-detail.component.html @@ -8,30 +8,41 @@ [figureBackground]="!currentLogoUrl" > - -
- -
+ + + + + @@ -40,12 +51,17 @@ id="ps_product_detail_panel_intern" [disabled]="changeMode !== 'VIEW'" [header]="'DIALOG.TABS.INTERN' | translate" + [attr.aria-label]="'DIALOG.TABS.INTERN' | translate" [tooltip]="'DIALOG.TABS.TOOLTIPS.INTERN' | translate" + tooltipPosition="top" + tooltipEvent="hover" > @@ -68,9 +87,11 @@
-
- {{('ACTIONS.DELETE.MESSAGE' | translate).replace('{{ITEM}}', product?.name)}} +
{{ 'ACTIONS.DELETE.PRODUCT.MESSAGE' | translate }}
+
+ {{ product?.name }}
-
{{ 'ACTIONS.DELETE.PRODUCT.OPERATOR_TEXT' | translate }}
+
{{ 'ACTIONS.DELETE.PRODUCT.OPERATOR_TEXT' | translate }}
{{ 'ACTIONS.DELETE.MESSAGE_INFO' | translate }}
@@ -92,6 +114,7 @@ icon="pi pi-times" (onClick)="productDeleteVisible = false" [label]="'ACTIONS.CONFIRMATION.NO' | translate" + [ariaLabel]="'ACTIONS.CONFIRMATION.NO' | translate" [pTooltip]="'ACTIONS.CONFIRMATION.NO.TOOLTIP' | translate" tooltipPosition="top" tooltipEvent="hover" @@ -101,6 +124,7 @@ icon="pi pi-check" (onClick)="onDeleteConfirmation()" [label]="'ACTIONS.CONFIRMATION.YES' | translate" + [ariaLabel]="'ACTIONS.CONFIRMATION.YES' | translate" [pTooltip]="'ACTIONS.CONFIRMATION.YES.TOOLTIP' | translate" tooltipPosition="top" tooltipEvent="hover" diff --git a/src/app/product-store/product-detail/product-detail.component.scss b/src/app/product-store/product-detail/product-detail.component.scss index 9f747cc..339151f 100644 --- a/src/app/product-store/product-detail/product-detail.component.scss +++ b/src/app/product-store/product-detail/product-detail.component.scss @@ -7,3 +7,4 @@ @include dialog-footer-buttons; @include dialog-non-full-size-modal; @include displaying-text-responsive; +@include tabview-fix-color-selected-tab; diff --git a/src/app/product-store/product-detail/product-detail.component.ts b/src/app/product-store/product-detail/product-detail.component.ts index e769d16..7d40eb5 100644 --- a/src/app/product-store/product-detail/product-detail.component.ts +++ b/src/app/product-store/product-detail/product-detail.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, ViewChild } from '@angular/core' import { Location } from '@angular/common' import { ActivatedRoute, Router } from '@angular/router' -import { Observable, finalize, map } from 'rxjs' +import { catchError, Observable, of, finalize, map } from 'rxjs' import { TranslateService } from '@ngx-translate/core' import { Action, PortalMessageService, UserService } from '@onecx/portal-integration-angular' @@ -22,12 +22,14 @@ export type ChangeMode = 'VIEW' | 'CREATE' | 'EDIT' | 'COPY' styleUrls: ['./product-detail.component.scss'] }) export class ProductDetailComponent implements OnInit { + public exceptionKey: string | undefined + public loading = false public actions$: Observable | undefined - public productName: string + public productName: string | null = null + public product$!: Observable public product: ProductAndWorkspaces | undefined public product_for_apps: ProductAndWorkspaces | undefined - public changeMode: ChangeMode = 'CREATE' - public loading = false + public changeMode: ChangeMode = 'VIEW' public dateFormat = 'medium' public headerImageUrl?: string public productDeleteVisible = false @@ -48,46 +50,45 @@ export class ProductDetailComponent implements OnInit { private readonly translate: TranslateService ) { this.dateFormat = this.user.lang$.getValue() === 'de' ? 'dd.MM.yyyy HH:mm:ss' : 'medium' - this.productName = this.route.snapshot.paramMap.get('name') || '' + this.productName = this.route.snapshot.paramMap.get('name') } ngOnInit(): void { - if (this.productName !== '') { + this.product = undefined + if (this.productName) { this.changeMode = 'VIEW' this.getProduct() } else { - this.product = undefined + this.changeMode = 'CREATE' + this.product$ = of({} as ProductAndWorkspaces) this.prepareActionButtons() } } - public onTabChange($event: any) { + public onTabChange($event: any, product: ProductAndWorkspaces) { this.selectedTabIndex = $event.index - this.prepareActionButtons() - if (this.selectedTabIndex === 2) this.product_for_apps = this.product // lazy load + this.prepareActionButtons(product) + if (this.selectedTabIndex === 3) this.product_for_apps = product // lazy load } - public getProduct() { + public getProduct(): void { this.loading = true - this.productApi - .getProductByName({ name: this.productName }) - .pipe( - finalize(() => { - this.loading = false - }) - ) - .subscribe({ - next: (data: any) => { - if (data) { - this.product = data - this.prepareActionButtons() - this.currentLogoUrl = this.getLogoUrl(this.product!) - } - } - }) - } - - public prepareActionButtons(): void { + this.product$ = this.productApi.getProductByName({ name: this.productName! }).pipe( + map((data: ProductAndWorkspaces) => { + this.prepareActionButtons(data) + this.currentLogoUrl = this.getLogoUrl(data) + return data + }), + catchError((err) => { + this.exceptionKey = 'EXCEPTIONS.HTTP_STATUS_' + err.status + '.PRODUCT' + console.error('getProductByName():', err) + return of({} as ProductAndWorkspaces) + }), + finalize(() => (this.loading = false)) + ) + } + + public prepareActionButtons(product?: ProductAndWorkspaces): void { this.actions$ = this.translate .get([ 'ACTIONS.COPY.LABEL', @@ -114,17 +115,7 @@ export class ProductDetailComponent implements OnInit { icon: 'pi pi-arrow-left', show: 'always', conditional: true, - showCondition: this.changeMode === 'VIEW' - }, - { - label: data['ACTIONS.COPY.LABEL'], - title: data['ACTIONS.COPY.PRODUCT.HEADER'], - actionCallback: () => this.onCopy(), - icon: 'pi pi-copy', - show: 'always', - conditional: true, - showCondition: this.selectedTabIndex === 0 && this.changeMode === 'VIEW' && this.product !== undefined, - permission: 'PRODUCT#CREATE' + showCondition: product && this.changeMode === 'VIEW' }, { label: data['ACTIONS.EDIT.LABEL'], @@ -136,8 +127,8 @@ export class ProductDetailComponent implements OnInit { showCondition: this.selectedTabIndex === 0 && this.changeMode === 'VIEW' && - this.product !== undefined && - !this.product.undeployed, + product !== undefined && + !product.undeployed, permission: 'PRODUCT#EDIT' }, { @@ -159,17 +150,24 @@ export class ProductDetailComponent implements OnInit { showCondition: this.changeMode !== 'VIEW', permission: 'PRODUCT#EDIT' }, + { + label: data['ACTIONS.COPY.LABEL'], + title: data['ACTIONS.COPY.PRODUCT.HEADER'], + actionCallback: () => this.onCopy(product), + icon: 'pi pi-copy', + show: 'asOverflow', + conditional: true, + showCondition: this.selectedTabIndex === 0 && this.changeMode === 'VIEW' && product !== undefined, + permission: 'PRODUCT#CREATE' + }, { label: data['ACTIONS.DELETE.LABEL'], title: data['ACTIONS.DELETE.PRODUCT.TOOLTIP'], - actionCallback: () => { - this.productDeleteMessage = data['ACTIONS.DELETE.MESSAGE'].replace('{{ITEM}}', this.product?.name) - this.productDeleteVisible = true - }, + actionCallback: () => this.onDelete(product), icon: 'pi pi-trash', show: 'asOverflow', conditional: true, - showCondition: this.changeMode === 'VIEW' && this.product !== undefined, + showCondition: this.changeMode === 'VIEW' && product !== undefined, permission: 'PRODUCT#DELETE' } ] @@ -184,9 +182,9 @@ export class ProductDetailComponent implements OnInit { this.close() } - public onCopy(): void { + public onCopy(item: any): void { this.changeMode = 'COPY' - this.prepareActionButtons() + this.prepareActionButtons(item) } public onEdit() { this.changeMode = 'EDIT' @@ -204,20 +202,20 @@ export class ProductDetailComponent implements OnInit { this.productPropsComponent.onSave() } - public onCreate(data: any) { - this.product = data + public onRouteToCreatedProduct(product: any) { this.changeMode = 'VIEW' - this.router.navigate(['./../', this.product?.name], { relativeTo: this.route }) + this.router.navigate(['../', product?.name], { relativeTo: this.route }) } - public onChange() { + public onChange(product?: Product) { this.changeMode = 'VIEW' + // update observable with response data => null + // this.product$ = new Observable((sub) => sub.next(product as ProductAndWorkspaces)) this.getProduct() } - public onDelete(ev: MouseEvent, item: Product): void { - ev.stopPropagation() - this.product = item + public onDelete(product?: Product): void { + this.product = product this.productDeleteVisible = true } public onDeleteConfirmation(): void { diff --git a/src/app/product-store/product-detail/product-intern/product-intern.component.html b/src/app/product-store/product-detail/product-intern/product-intern.component.html index 4f21f7b..a201370 100644 --- a/src/app/product-store/product-detail/product-intern/product-intern.component.html +++ b/src/app/product-store/product-detail/product-intern/product-intern.component.html @@ -1,97 +1,95 @@ -
-
- -
- + +
+ + +
+ + +
+ + - + + + + -
- - -
- - - - - - - - -
+ /> + + +
- -
- - - - - - - - -
+ +
+ + + + + + + +
diff --git a/src/app/product-store/product-detail/product-props/product-props.component.html b/src/app/product-store/product-detail/product-props/product-props.component.html index 2f9d54e..f895872 100644 --- a/src/app/product-store/product-detail/product-props/product-props.component.html +++ b/src/app/product-store/product-detail/product-props/product-props.component.html @@ -1,5 +1,5 @@
-
+
diff --git a/src/app/product-store/product-detail/product-props/product-props.component.ts b/src/app/product-store/product-detail/product-props/product-props.component.ts index be654b4..5da3d13 100644 --- a/src/app/product-store/product-detail/product-props/product-props.component.ts +++ b/src/app/product-store/product-detail/product-props/product-props.component.ts @@ -46,7 +46,7 @@ export class ProductPropertyComponent implements OnChanges, OnInit { @Input() dateFormat = 'medium' @Input() changeMode: ChangeMode = 'VIEW' @Output() productCreated = new EventEmitter() - @Output() productChanged = new EventEmitter() + @Output() productChanged = new EventEmitter() @Output() changeModeChange = new EventEmitter() @Output() currentLogoUrl = new EventEmitter() @@ -129,11 +129,11 @@ export class ProductPropertyComponent implements OnChanges, OnInit { name: this.formGroup.value['name'], version: this.formGroup.value['version'], description: this.formGroup.value['description'], + displayName: this.formGroup.value['displayName'], provider: this.formGroup.value['provider'], - imageUrl: this.formGroup.controls['imageUrl'].value, basePath: this.formGroup.value['basePath'], - displayName: this.formGroup.value['displayName'], iconName: this.formGroup.value['iconName'], + imageUrl: this.formGroup.controls['imageUrl'].value, classifications: this.convertToUniqueStringArray(this.formGroup.value['classifications']?.toString()) } as CreateProductRequest }) @@ -163,9 +163,9 @@ export class ProductPropertyComponent implements OnChanges, OnInit { } as UpdateProductRequest }) .subscribe({ - next: () => { + next: (data) => { this.msgService.success({ summaryKey: 'ACTIONS.EDIT.PRODUCT.OK' }) - this.productChanged.emit() + this.productChanged.emit(data) }, error: (err) => this.displaySaveError(err) }) diff --git a/src/app/product-store/product-detail/product.detail.component.spec.ts b/src/app/product-store/product-detail/product.detail.component.spec.ts index 408a390..3e45656 100644 --- a/src/app/product-store/product-detail/product.detail.component.spec.ts +++ b/src/app/product-store/product-detail/product.detail.component.spec.ts @@ -8,7 +8,7 @@ import { TranslateTestingModule } from 'ngx-translate-testing' import { PortalMessageService, ConfigurationService, UserService } from '@onecx/portal-integration-angular' import { ProductDetailComponent } from './product-detail.component' import { ProductPropertyComponent } from './product-props/product-props.component' -import { ProductsAPIService } from 'src/app/shared/generated' +import { ProductAndWorkspaces, ProductsAPIService } from 'src/app/shared/generated' import { provideHttpClient } from '@angular/common/http' import { provideHttpClientTesting } from '@angular/common/http/testing' @@ -111,22 +111,43 @@ describe('ProductDetailComponent', () => { }) it('should be set up correctly onInit if no product name', () => { - component.productName = '' + component.productName = null component.ngOnInit() expect(component.changeMode).toEqual('CREATE') }) - it('should get product onInit', () => { - const p = { id: 'id', name: 'name', basePath: 'path' } - apiServiceSpy.getProductByName.and.returnValue(of(p)) - + it('should get product onInit - successful found', (done) => { + const product = { id: 'id', name: 'name', basePath: 'path' } + apiServiceSpy.getProductByName.and.returnValue(of(product)) component.productName = 'name' + component.ngOnInit() + + component.product$.subscribe({ + next: (result) => { + expect(result.id).toBe('id') + done() + }, + error: done.fail + }) + }) + + it('should get product onInit - not found', (done) => { + const err = { status: 404 } + apiServiceSpy.getProductByName.and.returnValue(throwError(() => err)) + component.productName = 'unknown' component.ngOnInit() - expect(component.product?.id).toEqual(p.id) + component.product$.subscribe({ + next: (result) => { + expect(result).toEqual({} as ProductAndWorkspaces) + expect(component.exceptionKey).toEqual('EXCEPTIONS.HTTP_STATUS_404.PRODUCT') + done() + }, + error: done.fail + }) }) it('should prepare action buttons on init', () => { @@ -159,15 +180,14 @@ describe('ProductDetailComponent', () => { it('should fulfill all conditions for edit button', () => { spyOn(component, 'onEdit') - component.product = product component.changeMode = 'VIEW' - component.prepareActionButtons() + component.prepareActionButtons(product) let actions: any = [] component.actions$!.subscribe((act) => (actions = act)) - actions[2].actionCallback() + actions[1].actionCallback() expect(component.onEdit).toHaveBeenCalled() }) @@ -187,7 +207,7 @@ describe('ProductDetailComponent', () => { }) it('should behave correctly onCopy', () => { - component.onCopy() + component.onCopy({}) expect(component.changeMode).toEqual('COPY') }) @@ -240,10 +260,9 @@ describe('ProductDetailComponent', () => { it('should behave correctly onCreate', () => { const routerSpy = spyOn(router, 'navigate') - component.onCreate(product) + component.onRouteToCreatedProduct(product) - expect(component.product).toEqual(product) - expect(routerSpy).toHaveBeenCalledWith(['./../', product.name], jasmine.any(Object)) + expect(routerSpy).toHaveBeenCalledWith(['../', product.name], jasmine.any(Object)) }) it('should behave correctly onChange if change false', () => { @@ -255,9 +274,7 @@ describe('ProductDetailComponent', () => { }) it('should behave correctly onDelete', () => { - const event: MouseEvent = new MouseEvent('type') - - component.onDelete(event, product) + component.onDelete(product) expect(component.product).toEqual(product) }) @@ -289,11 +306,10 @@ describe('ProductDetailComponent', () => { expect(component.dateFormat).toEqual('dd.MM.yyyy HH:mm:ss') }) - it('should behave correctly onTabChange: 2', () => { - component.product = product - component.onTabChange({ index: 2 }) + it('should behave correctly onTabChange: 3', () => { + component.onTabChange({ index: 3 }, product) - expect(component.selectedTabIndex).toEqual(2) + expect(component.selectedTabIndex).toEqual(3) expect(component.product_for_apps).toEqual(product) }) 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 43e433d..69ab266 100644 --- a/src/app/product-store/product-search/product-search.component.html +++ b/src/app/product-store/product-search/product-search.component.html @@ -39,23 +39,30 @@
- -
- -
+ + + + -
+
-
-
-
- {{ product.displayName }} -
-
- {{ product.name }} -
+
+
+ {{ product.displayName }} +
+
+ {{ product.name }} +
+
+ {{ product.version }}
@@ -154,6 +150,7 @@ *ngIf="product?.undeployed" [id]="'ps_product_search_data_grid_row_' + i + '_undeployed'" class="card-badge-right badge-1 p-2 pi pi-ban danger-action-text font-bold" + [attr.aria-label]="'PRODUCT.TOOLTIPS.UNDEPLOYED_SEARCH' | translate" [pTooltip]="'PRODUCT.TOOLTIPS.UNDEPLOYED_SEARCH' | translate" tooltipPosition="top" tooltipEvent="hover" @@ -167,11 +164,12 @@ >
-
+
{{ product.name }}
+
+ {{ product.version ?? ('PRODUCT.VERSION.MISSING' | translate) }} +
diff --git a/src/app/product-store/product-search/product-search.component.spec.ts b/src/app/product-store/product-search/product-search.component.spec.ts index f240653..b240612 100644 --- a/src/app/product-store/product-search/product-search.component.spec.ts +++ b/src/app/product-store/product-search/product-search.component.spec.ts @@ -121,12 +121,10 @@ describe('ProductSearchComponent', () => { component.products$.subscribe({ next: (result) => { - if (result.stream) { - expect(result.stream.length).toBe(1) - result.stream.forEach((product) => { - expect(product.id).toEqual('id') - }) - } + expect(result.length).toBe(1) + result.forEach((product) => { + expect(product.id).toEqual('id') + }) done() }, error: done.fail @@ -140,9 +138,21 @@ describe('ProductSearchComponent', () => { component.products$.subscribe({ next: (result) => { - if (result.stream) { - expect(result.stream.length).toBe(0) - } + expect(result.length).toBe(0) + done() + }, + error: done.fail + }) + }) + + it('should search products - no stream', (done) => { + apiProductServiceSpy.searchProducts.and.returnValue(of({} as ProductPageResult)) + + component.onSearch() + + component.products$.subscribe({ + next: (result) => { + expect(result.length).toBe(0) done() }, error: done.fail @@ -157,8 +167,8 @@ describe('ProductSearchComponent', () => { component.products$.subscribe({ next: (result) => { - if (result.stream) { - expect(result.stream.length).toBe(0) + if (result) { + expect(result.length).toBe(0) expect(component.exceptionKey).toEqual('EXCEPTIONS.HTTP_STATUS_403.PRODUCTS') } done() diff --git a/src/app/product-store/product-search/product-search.component.ts b/src/app/product-store/product-search/product-search.component.ts index 9c5a1d1..ea36947 100644 --- a/src/app/product-store/product-search/product-search.component.ts +++ b/src/app/product-store/product-search/product-search.component.ts @@ -28,7 +28,7 @@ export interface ProductSearchCriteria { export class ProductSearchComponent implements OnInit { public exceptionKey: string | undefined public searchInProgress = false - public products$!: Observable + public products$!: Observable public productSearchCriteriaGroup!: FormGroup public actions$: Observable | undefined public viewMode: 'grid' | 'list' = 'grid' @@ -69,10 +69,14 @@ export class ProductSearchComponent implements OnInit { } }) .pipe( + map((data: ProductPageResult) => { + if (data.stream) return data.stream.sort(this.sortProductsByDisplayName) + else return [] + }), catchError((err) => { this.exceptionKey = 'EXCEPTIONS.HTTP_STATUS_' + err.status + '.PRODUCTS' console.error('searchProducts():', err) - return of({ stream: [] } as ProductPageResult) + return of([]) }), finalize(() => (this.searchInProgress = false)) ) diff --git a/src/app/product-store/slot-delete/slot-delete.component.html b/src/app/product-store/slot-delete/slot-delete.component.html index ba27412..a6f75f7 100644 --- a/src/app/product-store/slot-delete/slot-delete.component.html +++ b/src/app/product-store/slot-delete/slot-delete.component.html @@ -1,8 +1,10 @@
-
- {{ ('ACTIONS.DELETE.MESSAGE' | translate).replace("{{ITEM}}", slot?.name ?? '') }} -
-
{{ 'ACTIONS.DELETE.SLOT.OPERATOR_TEXT' | translate }}
-
{{ 'ACTIONS.DELETE.MESSAGE_INFO' | translate }}
+
{{ 'ACTIONS.DELETE.SLOT.MESSAGE' | translate }}
+
{{ slot?.name }}
+
{{ 'ACTIONS.DELETE.SLOT.OPERATOR_TEXT' | translate }}
+
{{ 'ACTIONS.DELETE.MESSAGE_INFO' | translate }}
diff --git a/src/app/shared/image-container/image-container.component.html b/src/app/shared/image-container/image-container.component.html index 001374b..fed16b9 100644 --- a/src/app/shared/image-container/image-container.component.html +++ b/src/app/shared/image-container/image-container.component.html @@ -5,7 +5,7 @@ [ocxSrc]="displayImageUrl" (error)="onImageError()" [alt]="'DIALOG.LOGO.LABEL' | translate" - [pTooltip]="title ? title : ('DIALOG.LOGO.LABEL' | translate)" + [pTooltip]="title" tooltipPosition="top" tooltipEvent="hover" /> @@ -15,7 +15,7 @@ [class]="'image-object image-' + (small ? 'sm' : 'lg') + ' sm:image-lg ' + (styleClass ? styleClass : '')" [src]="defaultImageUrl$ | async" [alt]="'DIALOG.LOGO.PLACEHOLDER' | translate" - [pTooltip]="title ? title : ('DIALOG.LOGO.PLACEHOLDER' | translate)" + [pTooltip]="title" tooltipPosition="top" tooltipEvent="hover" /> diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 08c7249..1d2c559 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -43,6 +43,7 @@ "MS.HEADER": "Microservice löschen", "APP": { "TOOLTIP": "App löschen", + "MESSAGE": "Möchten sie die folgende Komponente löschen?", "NOK": "App konnte nicht gelöscht werden", "OK": "App erfolgreich gelöscht", "OPERATOR_TEXT": "Diese App wurde automatisiert erstellt. Wenn weiterhin benötigt dann wird sie beim nächsten Deployment wiederhergestellt." @@ -50,6 +51,7 @@ "PRODUCT": { "TOOLTIP": "Applikation löschen", "HEADER": "Applikation löschen", + "MESSAGE": "Möchten sie die folgende Applikation löschen?", "NOK": "Applikation konnte nicht gelöscht werden", "OK": "Applikation erfolgreich gelöscht", "OPERATOR_TEXT": "Diese Applikation wurde automatisiert erstellt. Wenn weiterhin benötigt dann wird sie beim nächsten Deployment wiederhergestellt." @@ -57,6 +59,7 @@ "SLOT": { "TOOLTIP": "Slot löschen", "HEADER": "SLot löschen", + "MESSAGE": "Möchten sie den folgenden Slot löschen?", "NOK": "Slot konnte nicht gelöscht werden", "OK": "Slot erfolgreich gelöscht", "OPERATOR_TEXT": "Dieser Slot wurde automatisiert erstellt. Wenn weiterhin benötigt dann wird er beim nächsten Deployment wiederhergestellt." @@ -146,6 +149,7 @@ }, "CANCEL": "Abbrechen", "CHOOSE": "Auswählen", + "LOADING": "...wird geladen", "SAVE": "Speichern", "UPLOAD": "Hochladen", "TOOLTIPS": { @@ -296,6 +300,7 @@ "PRODUCT": { "NAME": "Name", "VERSION": "Version", + "VERSION.MISSING": "Version nicht gesetzt", "DESCRIPTION": "Beschreibung", "DISPLAY_NAME": "Anzeigename", "PROVIDER": "Anbieter", @@ -332,51 +337,56 @@ "UNDEPLOYED_SEARCH": "Dieser Slot ist nicht länger nutzbar. Eine weitere Nutzung führt zu Fehlermeldungen.", "NOT_DEFINED": "Diese Funktionalität ist derzeit nicht vefügbar." }, + "VALIDATION": { + "FORM_INVALID": "Die Daten sind nicht bereit zum Speichern.", + "HINTS": { + "FORMAT_URL": "Format: " + }, + "APP.UNIQUE_CONSTRAINT.REMOTE_MODULE": "Eine App mit dieser Remote Konfiguration existiert bereits für die Applikation.", + "APP.UNIQUE_CONSTRAINT.APP_ID": "Eine App mit dieser ID existiert bereits für die Applikation.", + "PRODUCT.UNIQUE_CONSTRAINT.NAME": "Eine Applikation mit diesem Namen existiert bereits.", + "PRODUCT.UNIQUE_CONSTRAINT.BASEPATH": "Eine Applikation mit diesem Basepath existiert bereits.", + "PRODUCT.INVALID_NAME": "Dieser Name ist reserviert und kann nicht verwendet werden.", + "ERRORS": { + "PARSE_ERROR": "Parserfehler: Die enthaltene Struktur entspricht nicht dem erwarteten Format.", + "INTERNAL_ERROR": "Interner Fehler", + "EMPTY_REQUIRED_FIELD": "Dieses Feld ist ein Pflichtfeld.", + "MAXIMUM_LENGTH": "Es sind nur {{chars}} Zeichen erlaubt.", + "MINIMUM_LENGTH": "Es sind mindestens {{chars}} Zeichen erforderlich.", + "PATTERN_ERROR": "Ihre Eingabe entspricht nicht dem benötigten Format.", + "FILETYPE_PATTERN_ERROR": "Ihre Datei entspricht nicht den erlaubten Formaten: .png, .jpg, .jpeg" + } + }, "EXCEPTIONS": { "HTTP_STATUS_0": { - "PRODUCTS": "Unbekanntes Problem beim Abrufen von Applikationen-Daten - Bitte versuchen sie es noch einmal.", + "PRODUCT": "Unbekanntes Problem beim Abrufen von Applikationsdetails - Bitte versuchen sie es noch einmal.", + "PRODUCTS": "Unbekanntes Problem beim Abrufen von Applikationen - Bitte versuchen sie es noch einmal.", "APPS": "Unbekanntes Problem beim Abrufen von App-Daten - Bitte versuchen sie es noch einmal.", "SLOTS": "Unbekanntes Problem beim Abrufen von Slot-Daten - Bitte versuchen sie es noch einmal." }, "HTTP_STATUS_401": { + "PRODUCT": "Sie sind nicht autorisiert, um Applikationsdetails zu sehen.", "PRODUCTS": "Sie sind nicht autorisiert, um Applikationen zu sehen.", "APPS": "Sie sind nicht autorisiert, um Apps zu sehen.", "SLOTS": "Sie sind nicht autorisiert, um Slots zu sehen." }, "HTTP_STATUS_403": { + "PRODUCT": "Sie haben keine Rechte, um Applikationsdetails zu sehen.", "PRODUCTS": "Sie haben keine Rechte, um Applikationen zu sehen.", "APPS": "Sie haben keine Rechte, um Apps zu sehen.", "SLOTS": "Sie haben keine Rechte, um Slots zu sehen." }, "HTTP_STATUS_404": { + "PRODUCT": "Applikation konnte nicht gefunden werden.", "PRODUCTS": "Es konnten keine Applikationen gefunden werden.", "APPS": "Es konnten keine Apps gefunden werden.", "SLOTS": "Es konnten keine Slots gefunden werden." }, "HTTP_STATUS_500": { + "PRODUCT": "Unbekanntes Server-Problem beim Abrufen von Applikationsdetails - Bitte versuchen sie es noch einmal.", "PRODUCTS": "Unbekanntes Server-Problem beim Abrufen von Applikation-Daten - Bitte versuchen sie es noch einmal.", "APPS": "Unbekanntes Server-Problem beim Abrufen von App-Daten - Bitte versuchen sie es noch einmal.", "SLOTS": "Unbekanntes Server-Problem beim Abrufen von Slot-Daten - Bitte versuchen sie es noch einmal." } - }, - "VALIDATION": { - "FORM_INVALID": "Die Daten sind nicht bereit zum Speichern.", - "HINTS": { - "FORMAT_URL": "Format: " - }, - "APP.UNIQUE_CONSTRAINT.REMOTE_MODULE": "Eine App mit dieser Remote Konfiguration existiert bereits für die Applikation.", - "APP.UNIQUE_CONSTRAINT.APP_ID": "Eine App mit dieser ID existiert bereits für die Applikation.", - "PRODUCT.UNIQUE_CONSTRAINT.NAME": "Eine Applikation mit diesem Namen existiert bereits.", - "PRODUCT.UNIQUE_CONSTRAINT.BASEPATH": "Eine Applikation mit diesem Basepath existiert bereits.", - "PRODUCT.INVALID_NAME": "Dieser Name ist reserviert und kann nicht verwendet werden.", - "ERRORS": { - "PARSE_ERROR": "Parserfehler: Die enthaltene Struktur entspricht nicht dem erwarteten Format.", - "INTERNAL_ERROR": "Interner Fehler", - "EMPTY_REQUIRED_FIELD": "Dieses Feld ist ein Pflichtfeld.", - "MAXIMUM_LENGTH": "Es sind nur {{chars}} Zeichen erlaubt.", - "MINIMUM_LENGTH": "Es sind mindestens {{chars}} Zeichen erforderlich.", - "PATTERN_ERROR": "Ihre Eingabe entspricht nicht dem benötigten Format.", - "FILETYPE_PATTERN_ERROR": "Ihre Datei entspricht nicht den erlaubten Formaten: .png, .jpg, .jpeg" - } } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 69e1e71..52299e3 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -43,6 +43,7 @@ "MS.HEADER": "Delete Microservice", "APP": { "TOOLTIP": "Delete App", + "MESSAGE": "Do you want to delete the following Component?", "NOK": "App could not be deleted", "OK": "App deleted successfully", "OPERATOR_TEXT": "This App was created by automation. If still needed it will be restored during the next deployment." @@ -50,6 +51,7 @@ "PRODUCT": { "TOOLTIP": "Delete Application", "HEADER": "Delete Application", + "MESSAGE": "Do you want to delete the following Application?", "NOK": "Application could not be deleted", "OK": "Application deleted successfully", "OPERATOR_TEXT": "This Application was created by automation. If still needed it will be restored during the next deployment." @@ -57,6 +59,7 @@ "SLOT": { "TOOLTIP": "Delete Slot", "HEADER": "Delete Slot", + "MESSAGE": "Do you want to delete the following Slot?", "NOK": "Slot could not be deleted", "OK": "Slot deleted successfully", "OPERATOR_TEXT": "This Slot was created by automation. If still needed it will be restored during the next deployment." @@ -146,6 +149,7 @@ }, "CANCEL": "Cancel", "CHOOSE": "Choose", + "LOADING": "...is loading", "SAVE": "Save", "UPLOAD": "Upload", "TOOLTIPS": { @@ -296,6 +300,7 @@ "PRODUCT": { "NAME": "Name", "VERSION": "Version", + "VERSION.MISSING": "Version not set", "DESCRIPTION": "Description", "DISPLAY_NAME": "Display Name", "PROVIDER": "Provider", @@ -332,51 +337,56 @@ "UNDEPLOYED_SEARCH": "This Slot is no longer usable. Further use leads to error messages.", "NOT_DEFINED": "This functionality is not available at the moment." }, + "VALIDATION": { + "FORM_INVALID": "The data are not ready to store.", + "HINTS": { + "FORMAT_URL": "Format: " + }, + "APP.UNIQUE_CONSTRAINT.REMOTE_MODULE": "An App with this remote configuration already exists for the Application.", + "APP.UNIQUE_CONSTRAINT.APP_ID": "An App with this id exists already for the Application.", + "PRODUCT.UNIQUE_CONSTRAINT.NAME": "An Application with this name exists already.", + "PRODUCT.UNIQUE_CONSTRAINT.BASEPATH": "An Application with this basepath exists already.", + "PRODUCT.INVALID_NAME": "This name is a reserved term and cannot be used.", + "ERRORS": { + "PARSE_ERROR": "Parse error: The contained structure does not match the expected format.", + "INTERNAL_ERROR": "Internal error", + "EMPTY_REQUIRED_FIELD": "This field is required.", + "MAXIMUM_LENGTH": "Only {{chars}} characters are allowed.", + "MINIMUM_LENGTH": "At least {{chars}} characters are required.", + "PATTERN_ERROR": "Your input does not match the required pattern", + "FILETYPE_PATTERN_ERROR": "Your file does not match the allowed filetypes: .png, .jpg, .jpeg" + } + }, "EXCEPTIONS": { "HTTP_STATUS_0": { + "PRODUCT": "Unknown problem retrieving Application data - please try again.", "PRODUCTS": "Unknown problem retrieving Application data - please try again.", "APPS": "Unknown problem retrieving App data - please try again.", "SLOTS": "Unknown problem retrieving Slot data - please try again." }, "HTTP_STATUS_401": { + "PRODUCT": "You are not authorized to see Application details.", "PRODUCTS": "You are not authorized to see Applications.", "APPS": "You are not authorized to see Apps.", "SLOTS": "You are not authorized to see Slots." }, "HTTP_STATUS_403": { + "PRODUCT": "You have no permission to see Application details.", "PRODUCTS": "You have no permissions to see Applications.", "APPS": "You have no permissions to see Apps.", "SLOTS": "You have no permissions to see Slots." }, "HTTP_STATUS_404": { + "PRODUCT": "Application could be found.", "PRODUCTS": "No Applications could be found.", "APPS": "No Apps could be found.", "SLOTS": "No Slots could be found." }, "HTTP_STATUS_500": { + "PRODUCT": "Unknown server problem retrieving Application data - please try again.", "PRODUCTS": "Unknown server problem retrieving Application data - please try again.", "APPS": "Unknown server problem retrieving App data - please try again.", "SLOTS": "Unknown server problem retrieving Slot data - please try again." } - }, - "VALIDATION": { - "FORM_INVALID": "The data are not ready to store.", - "HINTS": { - "FORMAT_URL": "Format: " - }, - "APP.UNIQUE_CONSTRAINT.REMOTE_MODULE": "An App with this remote configuration already exists for the Application.", - "APP.UNIQUE_CONSTRAINT.APP_ID": "An App with this id exists already for the Application.", - "PRODUCT.UNIQUE_CONSTRAINT.NAME": "An Application with this name exists already.", - "PRODUCT.UNIQUE_CONSTRAINT.BASEPATH": "An Application with this basepath exists already.", - "PRODUCT.INVALID_NAME": "This name is a reserved term and cannot be used.", - "ERRORS": { - "PARSE_ERROR": "Parse error: The contained structure does not match the expected format.", - "INTERNAL_ERROR": "Internal error", - "EMPTY_REQUIRED_FIELD": "This field is required.", - "MAXIMUM_LENGTH": "Only {{chars}} characters are allowed.", - "MINIMUM_LENGTH": "At least {{chars}} characters are required.", - "PATTERN_ERROR": "Your input does not match the required pattern", - "FILETYPE_PATTERN_ERROR": "Your file does not match the allowed filetypes: .png, .jpg, .jpeg" - } } } diff --git a/tsconfig.app.json b/tsconfig.app.json index 022dcfc..c13b8dd 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -8,6 +8,6 @@ "target": "ES2022" }, "files": ["src/main.ts", "src/polyfills.ts", "src/app/onecx-product-store-remote.module.ts"], - "include": ["src/**/*.ts", "src/**/*.d.ts"], + "include": ["src/**/*.ts"], "exclude": ["src/test.ts", "src/**/*.spec.ts"] } diff --git a/webpack.config.js b/webpack.config.js index c05ad20..52bfcda 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,26 +4,27 @@ const { share, withModuleFederationPlugin } = require('@angular-architects/modul const config = withModuleFederationPlugin({ name: 'onecx-product-store-ui', filename: 'remoteEntry.js', - exposes: { './OneCXProductStoreModule': 'src/bootstrap.ts' }, + exposes: { './OneCXProductStoreModule': 'src/main.ts' }, shared: share({ '@angular/core': { requiredVersion: 'auto', includeSecondaries: true }, - '@angular/forms': { requiredVersion: 'auto', includeSecondaries: true, eager: false }, '@angular/common': { requiredVersion: 'auto', includeSecondaries: { skip: ['@angular/common/http/testing'] } }, '@angular/common/http': { requiredVersion: 'auto', includeSecondaries: true }, - '@angular/router': { requiredVersion: 'auto', includeSecondaries: true }, + '@angular/forms': { requiredVersion: 'auto', includeSecondaries: true }, '@angular/platform-browser': { requiredVersion: 'auto', includeSecondaries: true }, - rxjs: { requiredVersion: 'auto', includeSecondaries: true }, + '@angular/router': { requiredVersion: 'auto', includeSecondaries: true }, '@ngx-translate/core': { requiredVersion: 'auto' }, + primeng: { requiredVersion: 'auto', includeSecondaries: true }, + rxjs: { requiredVersion: 'auto', includeSecondaries: true }, '@onecx/accelerator': { requiredVersion: 'auto', includeSecondaries: true }, '@onecx/angular-accelerator': { requiredVersion: 'auto', includeSecondaries: true }, '@onecx/angular-auth': { requiredVersion: 'auto', includeSecondaries: true }, '@onecx/angular-integration-interface': { requiredVersion: 'auto', includeSecondaries: true }, + '@onecx/angular-testing': { requiredVersion: 'auto', includeSecondaries: true }, '@onecx/angular-webcomponents': { requiredVersion: 'auto', includeSecondaries: true }, '@onecx/integration-interface': { requiredVersion: 'auto', includeSecondaries: true }, '@onecx/keycloak-auth': { requiredVersion: 'auto', includeSecondaries: true }, '@onecx/portal-integration-angular': { requiredVersion: 'auto', includeSecondaries: true }, - '@onecx/portal-layout-styles': { requiredVersion: 'auto', includeSecondaries: true }, - primeng: { requiredVersion: 'auto', includeSecondaries: true } + '@onecx/portal-layout-styles': { requiredVersion: 'auto', includeSecondaries: true } }), sharedMappings: ['@onecx/portal-integration-angular'] })