diff --git a/angular.json b/angular.json index 072886c..67dc702 100644 --- a/angular.json +++ b/angular.json @@ -108,7 +108,12 @@ "assets": ["src/favicon.ico", "src/assets"], "styles": ["src/styles.scss"], "scripts": [], - "codeCoverageExclude": ["src/app/test/**", "src/app/generated/**"] + "codeCoverageExclude": [ + "**/*.module.ts", + "src/app/test/**", + "src/app/environments/**", + "src/app/generated/**" + ] } }, "lint": { diff --git a/proxy.conf.js b/proxy.conf.js index 4b179e3..208fd0a 100644 --- a/proxy.conf.js +++ b/proxy.conf.js @@ -26,16 +26,6 @@ const PROXY_CONFIG = { changeOrigin: true, logLevel: 'debug', bypass: bypassFn - }, - '/ahm-api': { - target: 'http://ahm', - secure: false, - pathRewrite: { - '^.*/ahm-api': '' - }, - changeOrigin: true, - logLevel: 'debug', - bypass: logFn } } diff --git a/sonar-local-project.properties b/sonar-local-project.properties index 5f4e98b..7df0358 100644 --- a/sonar-local-project.properties +++ b/sonar-local-project.properties @@ -18,7 +18,7 @@ sonar.testExecutionReportPaths=reports/sonarqube_report.xml sonar.sourceEncoding=UTF-8 #sonar.sources=src/app #sonar.working.directory=dist/sonar -sonar.coverage.exclusions=*.ts,*.js,src/*.ts,src/**/*.module.ts,src/environments/*,src/assets/**/*,src/app/generated/**/*,src/test/* +sonar.coverage.exclusions=*.ts,*.js,src/*.ts,src/**/*.module.ts,src/environments/*,src/assets/**/*,src/app/generated/**/*,src/app/test/* #sonar.exclusions=src/app/generated/**/* #sonar.cpd.exclusions= #sonar.tests=src/app 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 3cc6580..227954b 100644 --- a/src/app/product-store/product-detail/product-detail.component.html +++ b/src/app/product-store/product-detail/product-detail.component.html @@ -9,16 +9,16 @@ > -
+
- + @@ -32,3 +32,45 @@ + + +
+
+
+
{{('ACTIONS.DELETE.MESSAGE' | translate).replace('{{ITEM}}', product?.name)}}
+
{{ 'ACTIONS.DELETE.MESSAGE_INFO' | translate }}
+
+
+ +
+ + +
+
+
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 2f42df8..9f8d9be 100644 --- a/src/app/product-store/product-detail/product-detail.component.ts +++ b/src/app/product-store/product-detail/product-detail.component.ts @@ -10,7 +10,7 @@ import { Product, ProductsAPIService, MicrofrontendsAPIService, GetProductReques import { environment } from '../../../environments/environment' import { ProductPropertyComponent } from './product-props/product-props.component' -type ChangeMode = 'VIEW' | 'NEW' | 'EDIT' +type ChangeMode = 'VIEW' | 'CREATE' | 'EDIT' @Component({ templateUrl: './product-detail.component.html', @@ -24,7 +24,7 @@ export class ProductDetailComponent implements OnInit { public productName: string public product: Product | undefined // usedInWorkspace: Workspace[] | undefined - public changeMode: ChangeMode = 'NEW' + public changeMode: ChangeMode = 'CREATE' public loading = false public dateFormat = 'medium' public actions: Action[] = [] @@ -75,7 +75,6 @@ export class ProductDetailComponent implements OnInit { this.prepareTranslations() }, error: (err: any) => { - console.log('ERR') this.msgService.error({ summaryKey: 'DIALOG.LOAD_ERROR' // detailKey: err.error.indexOf('was not found') > 1 ? 'DIALOG.NOT_FOUND' : err.error @@ -109,10 +108,10 @@ export class ProductDetailComponent implements OnInit { this.translate .get([ 'ACTIONS.DELETE.LABEL', - 'ACTIONS.DELETE.TOOLTIP', + 'ACTIONS.DELETE.PRODUCT.TOOLTIP', 'ACTIONS.DELETE.MESSAGE', 'ACTIONS.EDIT.LABEL', - 'ACTIONS.EDIT.TOOLTIP', + 'ACTIONS.EDIT.PRODUCT.TOOLTIP', 'ACTIONS.CANCEL', 'ACTIONS.TOOLTIPS.CANCEL', 'ACTIONS.SAVE', @@ -139,7 +138,7 @@ export class ProductDetailComponent implements OnInit { }, { label: data['ACTIONS.EDIT.LABEL'], - title: data['ACTIONS.EDIT.TOOLTIP'], + title: data['ACTIONS.EDIT.PRODUCT.TOOLTIP'], actionCallback: () => this.onEdit(), icon: 'pi pi-pencil', show: 'always', @@ -151,7 +150,7 @@ export class ProductDetailComponent implements OnInit { label: data['ACTIONS.CANCEL'], title: data['ACTIONS.TOOLTIPS.CANCEL'], actionCallback: () => this.onCancel(), - icon: 'pi pi-pencil', + icon: 'pi pi-times', show: 'always', conditional: true, showCondition: this.changeMode !== 'VIEW' @@ -160,7 +159,7 @@ export class ProductDetailComponent implements OnInit { label: data['ACTIONS.SAVE'], title: data['ACTIONS.TOOLTIPS.SAVE'], actionCallback: () => this.onSave(), - icon: 'pi pi-pencil', + icon: 'pi pi-save', show: 'always', conditional: true, showCondition: this.changeMode !== 'VIEW', @@ -168,7 +167,7 @@ export class ProductDetailComponent implements OnInit { }, { label: data['ACTIONS.DELETE.LABEL'], - title: data['ACTIONS.DELETE.TOOLTIP'], + title: data['ACTIONS.DELETE.PRODUCT.TOOLTIP'], actionCallback: () => { this.productDeleteMessage = data['ACTIONS.DELETE.MESSAGE'].replace('{{ITEM}}', this.product?.name) this.productDeleteVisible = true @@ -208,7 +207,7 @@ export class ProductDetailComponent implements OnInit { this.getProduct() this.prepareTranslations() } - if (this.changeMode === 'NEW') { + if (this.changeMode === 'CREATE') { this.close() } } @@ -217,8 +216,35 @@ export class ProductDetailComponent implements OnInit { } public onCreate(data: any) { this.product = data + this.changeMode === 'VIEW' + this.router.navigate(['./../', this.product?.name], { relativeTo: this.route }) + } + public onChange(nameChanged: boolean) { + console.log('detail.onChange ') + if (nameChanged) { + this.close() + } else { + this.getProduct() + this.changeMode === 'VIEW' + this.prepareTranslations() + } } - public onNameChange(change: boolean) { - change ? this.close() : this.getProduct() + public onDelete(ev: MouseEvent, item: Product): void { + ev.stopPropagation() + this.product = item + this.productDeleteVisible = true + } + public onDeleteConfirmation(): void { + if (this.product?.id) { + this.productApi.deleteProduct({ id: this.product?.id }).subscribe({ + next: () => { + this.productDeleteVisible = false + this.product = undefined + this.msgService.success({ summaryKey: 'ACTIONS.DELETE.PRODUCT.OK' }) + this.close() + }, + error: () => this.msgService.error({ summaryKey: 'ACTIONS.DELETE.PRODUCT.NOK' }) + }) + } } } 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 f08f9a1..1c06eeb 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 @@ -6,6 +6,10 @@ + +
+ +
diff --git a/src/app/product-store/product-detail/product-props/product-props.component.spec.ts b/src/app/product-store/product-detail/product-props/product-props.component.spec.ts index 5726b9e..8dc5e7b 100644 --- a/src/app/product-store/product-detail/product-props/product-props.component.spec.ts +++ b/src/app/product-store/product-detail/product-props/product-props.component.spec.ts @@ -100,15 +100,15 @@ describe('ProductPropertyComponent', () => { classifications: new FormControl(null) }) component.formGroup = formGroup as FormGroup - component.changeMode = 'NEW' + component.changeMode = 'CREATE' component.onSubmit() expect(apiServiceSpy.createProduct).toHaveBeenCalled() - expect(msgServiceSpy.success).toHaveBeenCalledWith({ summaryKey: 'ACTIONS.CREATE.MESSAGE.PRODUCT_OK' }) + expect(msgServiceSpy.success).toHaveBeenCalledWith({ summaryKey: 'ACTIONS.CREATE.PRODUCT.OK' }) }) - it('should call updateProduct onSubmit in view mode', () => { + it('should call updateProduct onSubmit in edit mode', () => { apiServiceSpy.updateProduct.and.returnValue(of({})) const formGroup = new FormGroup({ id: new FormControl('id'), @@ -123,15 +123,15 @@ describe('ProductPropertyComponent', () => { classifications: new FormControl(null) }) component.formGroup = formGroup as FormGroup - component.changeMode = 'VIEW' + component.changeMode = 'EDIT' component.onSubmit() expect(apiServiceSpy.updateProduct).toHaveBeenCalled() - expect(msgServiceSpy.success).toHaveBeenCalledWith({ summaryKey: 'ACTIONS.EDIT.MESSAGE.PRODUCT_OK' }) + expect(msgServiceSpy.success).toHaveBeenCalledWith({ summaryKey: 'ACTIONS.EDIT.PRODUCT.OK' }) }) - it('should display error if searchProducts fails', () => { + it('should display error if updateProduct fails', () => { apiServiceSpy.updateProduct.and.returnValue(throwError(() => new Error())) const formGroup = new FormGroup({ id: new FormControl('id'), @@ -146,17 +146,17 @@ describe('ProductPropertyComponent', () => { classifications: new FormControl(null) }) component.formGroup = formGroup as FormGroup - component.changeMode = 'VIEW' + component.changeMode = 'EDIT' component.onSubmit() expect(component.formGroup.valid).toBeTrue() expect(msgServiceSpy.error).toHaveBeenCalledWith({ - summaryKey: 'ACTIONS.EDIT.MESSAGE.PRODUCT_NOK' + summaryKey: 'ACTIONS.EDIT.PRODUCT.NOK' }) }) - it('should display error if searchProducts fails', () => { + it('should display error if createProduct fails', () => { apiServiceSpy.createProduct.and.returnValue(throwError(() => new Error())) const formGroup = new FormGroup({ id: new FormControl('id'), @@ -171,13 +171,13 @@ describe('ProductPropertyComponent', () => { classifications: new FormControl(null) }) component.formGroup = formGroup as FormGroup - component.changeMode = 'NEW' + component.changeMode = 'CREATE' component.onSubmit() expect(component.formGroup.valid).toBeTrue() expect(msgServiceSpy.error).toHaveBeenCalledWith({ - summaryKey: 'ACTIONS.CREATE.MESSAGE.PRODUCT_NOK' + summaryKey: 'ACTIONS.CREATE.PRODUCT.NOK' }) }) 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 bd1bdb5..9f244b9 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 @@ -1,13 +1,11 @@ -import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core' -import { FormControl, FormGroup, Validators } from '@angular/forms' -// import { TranslateService } from '@ngx-translate/core' +import { Component, ElementRef, EventEmitter, Input, OnChanges, Output } from '@angular/core' +import { AbstractControl, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms' import { SelectItem } from 'primeng/api' import { PortalMessageService } from '@onecx/portal-integration-angular' import { CreateProductRequest, Product, ProductsAPIService, UpdateProductRequest } from '../../../generated' import { IconService } from '../../../shared/iconservice' import { dropDownSortItemsByLabel } from 'src/app/shared/utils' -// import { setFetchUrls } from '../../../shared/utils' export interface ProductDetailForm { id: FormControl @@ -22,6 +20,14 @@ export interface ProductDetailForm { classifications: FormControl } +export function productNameValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value = control.value + if (!value) return null + return value === 'new' ? { invalidProductName: true } : null + } +} + @Component({ selector: 'ps-product-props', templateUrl: './product-props.component.html', @@ -32,22 +38,28 @@ export class ProductPropertyComponent implements OnChanges { @Input() dateFormat = 'medium' @Input() changeMode = 'VIEW' @Output() productCreated = new EventEmitter() - @Output() productNameChanged = new EventEmitter() + @Output() productChanged = new EventEmitter() public formGroup: FormGroup public productId: string | undefined public productName: string | null | undefined public fetchingLogoUrl?: string public iconItems: SelectItem[] = [{ label: '', value: null }] + //private productNamePattern = '^(?!new$)(.*)$' // matching for valid product names constructor( private icon: IconService, + private elements: ElementRef, private productApi: ProductsAPIService, - // private translate: TranslateService, private msgService: PortalMessageService ) { this.formGroup = new FormGroup({ id: new FormControl(null), - name: new FormControl(null, [Validators.required, Validators.minLength(2), Validators.maxLength(255)]), + name: new FormControl(null, [ + Validators.required, + Validators.minLength(2), + Validators.maxLength(255), + productNameValidator() + ]), displayName: new FormControl(null, [Validators.required, Validators.minLength(2), Validators.maxLength(255)]), operator: new FormControl(null), version: new FormControl(null, [Validators.required, Validators.maxLength(255)]), @@ -68,15 +80,22 @@ export class ProductPropertyComponent implements OnChanges { }) this.productId = this.product.id this.productName = this.product.name // business key => manage the change! - } else this.formGroup.reset() + } else { + this.formGroup.reset() + } this.changeMode !== 'VIEW' ? this.formGroup.enable() : this.formGroup.disable() } + /** CREATE/UPDATE product + */ public onSubmit() { if (this.formGroup.valid) { - this.changeMode === 'NEW' ? this.createProduct() : this.updateProduct() + this.changeMode === 'CREATE' ? this.createProduct() : this.updateProduct() } else { this.msgService.error({ summaryKey: 'VALIDATION.FORM_INVALID' }) + // set focus to first invalid field + const invalidControl = this.elements.nativeElement.querySelector('input.ng-invalid') + if (invalidControl) invalidControl.focus() } } @@ -96,20 +115,10 @@ export class ProductPropertyComponent implements OnChanges { }) .subscribe({ next: (data) => { - console.log('NEXT') - this.msgService.success({ summaryKey: 'ACTIONS.CREATE.MESSAGE.PRODUCT_OK' }) + this.msgService.success({ summaryKey: 'ACTIONS.CREATE.PRODUCT.OK' }) this.productCreated.emit(data) }, - error: (err) => { - // console.log('ERR', err) - /* err.error.key && err.error.key === 'PERSIST_ENTITY_FAILED' - ? this.msgService.error({ - summaryKey: 'ACTIONS.CREATE.MESSAGE.PRODUCT_NOK', - detailKey: 'VALIDATION.PRODUCT.UNIQUE_CONSTRAINT' - }) - : */ - this.msgService.error({ summaryKey: 'ACTIONS.CREATE.MESSAGE.PRODUCT_NOK' }) - } + error: (err) => this.displaySaveError(err) }) } @@ -130,21 +139,25 @@ export class ProductPropertyComponent implements OnChanges { }) .subscribe({ next: () => { - this.msgService.success({ summaryKey: 'ACTIONS.EDIT.MESSAGE.PRODUCT_OK' }) - this.productNameChanged.emit(this.productName !== this.formGroup.value['name']) + this.changeMode === 'VIEW' + this.msgService.success({ summaryKey: 'ACTIONS.EDIT.PRODUCT.OK' }) + this.productChanged.emit(this.productName !== this.formGroup.value['name']) }, - error: (err) => { - /* err.error.key && err.error.key === 'PERSIST_ENTITY_FAILED' - ? this.msgService.error({ - summaryKey: 'ACTIONS.EDIT.MESSAGE.PRODUCT_NOK', - detailKey: 'VALIDATION.PRODUCT.UNIQUE_CONSTRAINT' - }) - : */ - this.msgService.error({ summaryKey: 'ACTIONS.EDIT.MESSAGE.PRODUCT_NOK' }) - } + error: (err) => this.displaySaveError(err) }) } + private displaySaveError(err: any) { + err.error && err.error.errorCode && err.error.errorCode === 'MERGE_ENTITY_FAILED' // 'PERSIST_ENTITY_FAILED' + ? this.msgService.error({ + summaryKey: 'ACTIONS.' + this.changeMode + '.PRODUCT.NOK', + detailKey: 'VALIDATION.PRODUCT.UNIQUE_CONSTRAINT' + }) + : this.msgService.error({ summaryKey: 'ACTIONS.' + this.changeMode + '.PRODUCT.NOK' }) + } + + /** File Handling + */ public onFileUpload(ev: Event, fieldType: 'logo'): void { if (ev.target && (ev.target as HTMLInputElement).files) { const files = (ev.target as HTMLInputElement).files 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 c2be4f3..89cc3e5 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 @@ -176,7 +176,7 @@ describe('ProductDetailComponent', () => { it('should behave correctly onCancel in new mode', () => { spyOn(component, 'close') - component.changeMode = 'NEW' + component.changeMode = 'CREATE' component.onCancel() @@ -190,27 +190,27 @@ describe('ProductDetailComponent', () => { expect(component.productPropsComponent.onSubmit).toHaveBeenCalled() }) - + /* it('should behave correctly onCreate', () => { - const data: any = { id: 'id ' } + const data: any = { id: 'id ', name: 'name' } component.onCreate(data) expect(component.product).toEqual(data) }) - +*/ it('should behave correctly onNameChange if change true', () => { spyOn(component, 'close') - component.onNameChange(true) + component.onChange(true) expect(component.close).toHaveBeenCalled() }) - it('should behave correctly onNameChange if change false', () => { + it('should behave correctly onChange if change false', () => { spyOn(component, 'getProduct') - component.onNameChange(false) + component.onChange(false) expect(component.getProduct).toHaveBeenCalled() }) 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 bfa3f25..5cc6b4a 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 @@ -60,7 +60,7 @@ describe('ProductSearchComponent', () => { }) it('should prepare action buttons on init', () => { - translateServiceSpy.get.and.returnValue(of({ 'ACTIONS.CREATE.PRODUCT': 'Create' })) + translateServiceSpy.get.and.returnValue(of({ 'ACTIONS.CREATE.LABEL': 'Create' })) spyOn(component, 'onNewProduct') component.ngOnInit() @@ -104,7 +104,7 @@ describe('ProductSearchComponent', () => { }) it('should call loadProducts onSearch', () => { - translateServiceSpy.get.and.returnValue(of({ 'ACTIONS.CREATE.PRODUCT': 'Create' })) + translateServiceSpy.get.and.returnValue(of({ 'ACTIONS.CREATE.LABEL': 'Create' })) spyOn(component, 'loadProducts') component.onSearch() 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 3d1eef1..747ae39 100644 --- a/src/app/product-store/product-search/product-search.component.ts +++ b/src/app/product-store/product-search/product-search.component.ts @@ -60,7 +60,7 @@ export class ProductSearchComponent implements OnInit { .get([ 'PRODUCT.NAME', 'PRODUCT.DISPLAY_NAME', - 'ACTIONS.CREATE.PRODUCT', + 'ACTIONS.CREATE.LABEL', 'ACTIONS.CREATE.PRODUCT.TOOLTIP', 'ACTIONS.DATAVIEW.VIEW_MODE_GRID', 'ACTIONS.DATAVIEW.VIEW_MODE_LIST', @@ -95,7 +95,7 @@ export class ProductSearchComponent implements OnInit { private prepareActionButtons(data: any): void { this.actions = [] // provoke change event this.actions.push({ - label: data['ACTIONS.CREATE.PRODUCT'], + label: data['ACTIONS.CREATE.LABEL'], title: data['ACTIONS.CREATE.PRODUCT.TOOLTIP'], actionCallback: () => this.onNewProduct(), permission: 'PRODUCT#EDIT', diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 15771ce..f0d64be 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -44,18 +44,15 @@ import { CanActivateGuard } from './can-active-guard.service' import { ImageContainerComponent } from './image-container/image-container.component' export const basePathProvider = (mfeInfo: MfeInfo) => { - /* console.log( - 'Base path provider: ' + (mfeInfo ? mfeInfo.remoteBaseUrl + '' + environment.apiPrefix : '' + environment.apiPrefix) - ) */ - return mfeInfo ? mfeInfo.remoteBaseUrl + '' + environment.apiPrefix : '' + environment.apiPrefix + const appBasePath = mfeInfo ? mfeInfo.remoteBaseUrl + '' + environment.apiPrefix : environment.apiPrefix + console.log('Base path: ' + appBasePath) + return appBasePath } +// Always load the app assets directly from the Microfrontend export function HttpLoaderFactory(http: HttpClient, mfeInfo: MfeInfo) { - /* if (mfeInfo) { - console.log(`Configuring translation loader ${mfeInfo?.remoteBaseUrl}`) - } */ - // if running standalone then load the app assets directly from remote base URL const appAssetPrefix = mfeInfo && mfeInfo.remoteBaseUrl ? mfeInfo.remoteBaseUrl : './' + console.log('Path prefix: ' + appAssetPrefix) return new TranslateCombinedLoader( new TranslateHttpLoader(http, appAssetPrefix + 'assets/i18n/', '.json'), new TranslateHttpLoader(http, appAssetPrefix + 'onecx-portal-lib/assets/i18n/', '.json') diff --git a/src/app/shared/utils.spec.ts b/src/app/shared/utils.spec.ts index 2caf2a6..f0a8e79 100644 --- a/src/app/shared/utils.spec.ts +++ b/src/app/shared/utils.spec.ts @@ -1,13 +1,6 @@ import { SelectItem } from 'primeng/api' -import { - limitText, - setFetchUrls, - dropDownSortItemsByLabel, - dropDownGetLabelByValue, - sortByLocale, - filterObject -} from './utils' +import { limitText, setFetchUrls, dropDownSortItemsByLabel, dropDownGetLabelByValue, sortByLocale } from './utils' describe('util functions', () => { describe('limitText', () => { @@ -33,15 +26,15 @@ describe('util functions', () => { describe('setFetchUrls', () => { it('should prepend apiPrefix to a relative URL', () => { - const result = setFetchUrls('ahm-api', '/am') + const result = setFetchUrls('api-prefix', '/url') - expect(result).toEqual('ahm-api/am') + expect(result).toEqual('api-prefix/url') }) it('should return the original URL if it is absolute', () => { - const result = setFetchUrls('ahm-api', 'http://am') + const result = setFetchUrls('api-prefix', 'http://host') - expect(result).toEqual('http://am') + expect(result).toEqual('http://host') }) }) @@ -80,13 +73,4 @@ describe('util functions', () => { expect(sortedStrings[0]).toEqual('str1') }) }) - - describe('filterObject', () => { - it('should exclude specified properties from the object', () => { - const obj = { prop1: 'value1', prop2: 'value2', prop3: 'value3' } - const exProps = ['prop2', 'prop3'] - const result = filterObject(obj, exProps) - expect(result).toEqual({ prop1: 'value1' }) - }) - }) }) diff --git a/src/app/shared/utils.ts b/src/app/shared/utils.ts index a1f23f6..0b60e1e 100644 --- a/src/app/shared/utils.ts +++ b/src/app/shared/utils.ts @@ -30,16 +30,3 @@ export function dropDownGetLabelByValue(ddArray: SelectItem[], val: string): str export function sortByLocale(a: any, b: any): number { return a.toUpperCase().localeCompare(b.toUpperCase()) } - -/** - * Filter objects => exclude given properties - */ -export function filterObject(obj: any, exProps: string[]): any { - const pickedObj: any = {} - for (const prop in obj) { - if (!exProps.includes(prop)) { - pickedObj[prop] = obj[prop] - } - } - return pickedObj -} diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 5f0b2b9..8f98b31 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -7,28 +7,52 @@ "YES.TOOLTIP": "Fortfahren mit der Aktion" }, "CREATE": { - "PRODUCT": "Produkt erstellen", - "PRODUCT.TOOLTIP": "Ein neues Produkt erstellen", - "MESSAGE": { - "PRODUCT_ALREADY_EXISTS": "Ein Produkt mit diesem Namen existert schon", - "PRODUCT_OK": "Das Produkt wurde erfolgreich erstellt", - "PRODUCT_NOK": "Ein Fehler ist aufgetreten. Das Produkt wurde nicht erstellt." + "LABEL": "Erstellen", + "APP": { + "HEADER": "App erstellen", + "TOOLTIP": "Eine neue App erstellen", + "ALREADY_EXISTS": "Eine App mit diesem Namen existert schon für das Produkt", + "OK": "Die App wurde erfolgreich erstellt", + "NOK": "Ein Fehler ist aufgetreten. Die App wurde nicht erstellt." + }, + "PRODUCT": { + "HEADER": "Produkt erstellen", + "TOOLTIP": "Ein neues Produkt erstellen", + "ALREADY_EXISTS": "Ein Produkt mit diesem Namen existert schon", + "OK": "Das Produkt wurde erfolgreich erstellt", + "NOK": "Ein Fehler ist aufgetreten. Das Produkt wurde nicht erstellt." } }, "DELETE": { "LABEL": "Löschen", - "TOOLTIP": "Produkt löschen", - "MESSAGE": "Möchten Sie {{ITEM}} löschen?", - "MESSAGE_INFO": "Diese Aktion kann nicht rückgängig gemacht werden!", - "PRODUCT_NOK": "Produkt konnte nicht gelöscht werden", - "PRODUCT_OK": "Produkt erfolgreich gelöscht" + "APP": { + "TOOLTIP": "App löschen", + "HEADER": "App löschen", + "NOK": "App konnte nicht gelöscht werden", + "OK": "App erfolgreich gelöscht" + }, + "PRODUCT": { + "TOOLTIP": "Produkt löschen", + "HEADER": "Produkt löschen", + "NOK": "Produkt konnte nicht gelöscht werden", + "OK": "Produkt erfolgreich gelöscht" + }, + "MESSAGE": "Möchten Sie \"{{ITEM}}\" löschen?", + "MESSAGE_INFO": "Diese Aktion kann nicht rückgängig gemacht werden!" }, "EDIT": { "LABEL": "Bearbeiten", - "TOOLTIP": "Die Eigenschaften bearbeiten", - "MESSAGE": { - "PRODUCT_OK": "Das Produkt wurde erfolgreich geändert", - "PRODUCT_NOK": "Ein Fehler ist aufgetreten. Das Produkt wurde nicht geändert." + "APP": { + "HEADER": "App bearbeiten", + "TOOLTIP": "Die App-Eigenschaften bearbeiten", + "OK": "Das App wurde erfolgreich geändert", + "NOK": "Ein Fehler ist aufgetreten. Das App wurde nicht geändert." + }, + "PRODUCT": { + "HEADER": "Produkt bearbeiten", + "TOOLTIP": "Die Produkteigenschaften bearbeiten", + "OK": "Das Produkt wurde erfolgreich geändert", + "NOK": "Ein Fehler ist aufgetreten. Das Produkt wurde nicht geändert." } }, "NAVIGATION": { @@ -149,6 +173,7 @@ "FORM_INVALID": "Die Daten sind nicht bereit zum Speichern.", "PRODUCT.UNIQUE_CONSTRAINT": "Ein Produkt mit diesem Namen existiert bereits.", "PRODUCT.NOT_EXISTS": "Ein Produkt mit diesem Namen konnte nicht gefunden werden.", + "PRODUCT.INVALID_NAME": "Dieser Namen kann nicht verwendet werden.", "ERRORS": { "PARSE_ERROR": "Parserfehler: Die enthaltene Struktur entspricht nicht dem erwarteten Format.", "INTERNAL_ERROR": "Interner Fehler", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 2cee51a..9950c72 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -7,28 +7,56 @@ "YES.TOOLTIP": "Proceed with the action" }, "CREATE": { - "PRODUCT": "Create Product", - "PRODUCT.TOOLTIP": "Create a new Product", - "MESSAGE": { - "PRODUCT_ALREADY_EXISTS": "A Product with that name already exists", - "PRODUCT_OK": "The Product was created successfully", - "PRODUCT_NOK": "An error has occurred. The Product was not created." + "LABEL": "Create", + "APP": { + "HEADER": "Create App", + "TOOLTIP": "Create a new App", + "MESSAGE": { + "ALREADY_EXISTS": "App with that name already exists", + "OK": "The App was created successfully", + "NOK": "An error has occurred. The App was not created." + } + }, + "PRODUCT": { + "HEADER": "Create Product", + "TOOLTIP": "Create a new Product", + "MESSAGE": { + "ALREADY_EXISTS": "A Product with that name already exists", + "OK": "The Product was created successfully", + "NOK": "An error has occurred. The Product was not created." + } } }, "DELETE": { "LABEL": "Delete", - "TOOLTIP": "Delete Product", - "MESSAGE": "Do you want to delete {{ITEM}}?", - "MESSAGE_INFO": "This action cannot be undone!", - "PRODUCT_NOK": "Product could not be deleted", - "PRODUCT_OK": "Product deleted successfully" + "APP": { + "TOOLTIP": "Delete App", + "HEADER": "Delete App", + "NOK": "App could not be deleted", + "OK": "App deleted successfully" + }, + "PRODUCT": { + "TOOLTIP": "Delete Product", + "HEADER": "Delete Product", + "NOK": "Product could not be deleted", + "OK": "Product deleted successfully" + }, + "MESSAGE": "Do you want to delete \"{{ITEM}}\"?", + "MESSAGE_INFO": "This action cannot be undone!" }, "EDIT": { "LABEL": "Edit", - "TOOLTIP": "Edit properties", - "MESSAGE": { - "PRODUCT_OK": "The Product was changed successfully", - "PRODUCT_NOK": "An error has occurred. The Product was not changed." + "APP": { + "HEADER": "Edit App", + "TOOLTIP": "Edit App properties", + "OK": "Das App wurde erfolgreich geändert", + "NOK": "Ein Fehler ist aufgetreten. Das App wurde nicht geändert." + }, + "PRODUCT": { + "HEADER": "Edit Product", + "TOOLTIP": "Edit Product properties", + "OK": "The Product was changed successfully", + "NOK": "An error has occurred. The Product was not changed." } }, "NAVIGATION": { @@ -149,6 +177,7 @@ "FORM_INVALID": "The data are not ready to store.", "PRODUCT.UNIQUE_CONSTRAINT": "A Product with this name exists already.", "PRODUCT.NOT_EXISTS": "A product with this name could not be found.", + "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",