diff --git a/.github/workflows/unit-test_frontend.yml b/.github/workflows/unit-test_frontend.yml index 2de7cc5be5..339467f39e 100644 --- a/.github/workflows/unit-test_frontend.yml +++ b/.github/workflows/unit-test_frontend.yml @@ -25,7 +25,6 @@ on: pull_request: workflow_dispatch: - jobs: build: diff --git a/CHANGELOG.md b/CHANGELOG.md index 25989ee76e..5764508853 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ _**For better traceability add the corresponding GitHub issue number in each cha - #844 Prefilled bpn on investigation creation - #843 Refactored e2e tests, added edit notification e2e test case - #828 fix duplicates in traction_battery_code_subcomponent table +- #617 redesigned inbox table - #XXX Updated spring boot from 3.2.4 to 3.2.5 - #XXX Bumped logback-core & logback-classic from 1.5.4 to 1.5.5 ### Removed diff --git a/docs/src/docs/user/user-manual.adoc b/docs/src/docs/user/user-manual.adoc index f18a6943ea..bb66c3cfb7 100644 --- a/docs/src/docs/user/user-manual.adoc +++ b/docs/src/docs/user/user-manual.adoc @@ -422,7 +422,7 @@ Inbox for received/sent quality notifications. image:https://raw.githubusercontent.com/eclipse-tractusx/traceability-foss/main/docs/src/images/arc42/user-guide/investigations-list-view.png[] -The tables can be sorted, filtered and searched. The global search bar at the top returns other part results from both tables. +The tables can be sorted, filtered and searched. Choosing the filter input field for any column and typing in any character will show filter suggestions. @@ -441,6 +441,29 @@ Those notifications specify a defect or request to investigate on a specific par * Queued status: Quality investigation is created but not yet released. * Requested status: Quality investigation is sent to the supplier. +=== Create a new quality notification + +By clicking the announcement icon in the upper left corner of the table, you will be navigated to the notification creation view, in where you can start a quality notification from blank. + +=== Table Actions + +Similar to the parts table, the inbox provide a variety on actions you can apply to the listed notifications. +Some actions are related to a single notification, while other can be executed on multiple notifications at a time. + +==== Actions on a single notification + +Through a click on the three dots on the right of a notification list row you open the menu actions, which you can choose from. +Generally, there are the actions to edit the notification (if it's not sent yet) or view details about it. +Additionally, there are the options to apply action alongside the lifecycle of a quality notification. + +==== Actions on multiple notifications + +Notifications can be selected with the checkboxes on the left of the table. +With the selection, there is a context menu for actions on mulitple (selected) notifications. +The "more" menu is opened by clicking on the horizontally aligned three dots icon. + +image:https://raw.githubusercontent.com/eclipse-tractusx/traceability-foss/main/docs/src/images/arc42/user-guide/inbox-multiselect-actions.png[] + === Quality notification create/edit view A quality notification can be started by the following options: diff --git a/docs/src/images/arc42/user-guide/inbox-multiselect-actions.png b/docs/src/images/arc42/user-guide/inbox-multiselect-actions.png new file mode 100644 index 0000000000..a51fed4b32 Binary files /dev/null and b/docs/src/images/arc42/user-guide/inbox-multiselect-actions.png differ diff --git a/docs/src/images/arc42/user-guide/investigations-autosuggestion-filtering.png b/docs/src/images/arc42/user-guide/investigations-autosuggestion-filtering.png index f8de83383a..f64b39e86e 100644 Binary files a/docs/src/images/arc42/user-guide/investigations-autosuggestion-filtering.png and b/docs/src/images/arc42/user-guide/investigations-autosuggestion-filtering.png differ diff --git a/docs/src/images/arc42/user-guide/investigations-list-view.png b/docs/src/images/arc42/user-guide/investigations-list-view.png index 9f28342a65..d75852622e 100644 Binary files a/docs/src/images/arc42/user-guide/investigations-list-view.png and b/docs/src/images/arc42/user-guide/investigations-list-view.png differ diff --git a/frontend/src/app/modules/core/user/table-settings.service.spec.ts b/frontend/src/app/modules/core/user/table-settings.service.spec.ts index d1f01ada7f..c8b8fcb8d5 100644 --- a/frontend/src/app/modules/core/user/table-settings.service.spec.ts +++ b/frontend/src/app/modules/core/user/table-settings.service.spec.ts @@ -19,6 +19,8 @@ import { TestBed } from '@angular/core/testing'; import { TableSettingsService } from '@core/user/table-settings.service'; +import { TableType } from '@shared/components/multi-select-autocomplete/table-type.model'; +import { TableViewConfig } from '@shared/components/parts-table/table-view-config.model'; describe('TableSettingsService', () => { let service: TableSettingsService; @@ -35,4 +37,127 @@ describe('TableSettingsService', () => { it('should be created', () => { expect(service).toBeTruthy(); }); + + describe('should correctly initialize the table view settings', () => { + it('should return PartsAsPlannedCustomerConfigurationModel for AS_PLANNED_CUSTOMER', () => { + const result: TableViewConfig = service.initializeTableViewSettings(TableType.AS_PLANNED_CUSTOMER); + expect(result.displayedColumns.length).toBe(8); + }); + + it('should return PartsAsPlannedConfigurationModel for AS_PLANNED_OWN', () => { + const result: TableViewConfig = service.initializeTableViewSettings(TableType.AS_PLANNED_OWN); + expect(result.displayedColumns.length).toBe(19); + }); + + it('should return PartsAsPlannedSupplierConfigurationModel for AS_PLANNED_SUPPLIER', () => { + const result: TableViewConfig = service.initializeTableViewSettings(TableType.AS_PLANNED_SUPPLIER); + expect(result.displayedColumns.length).toBe(8); + }); + + it('should return PartsAsBuiltConfigurationModel for AS_BUILT_OWN', () => { + const result: TableViewConfig = service.initializeTableViewSettings(TableType.AS_BUILT_OWN); + expect(result.displayedColumns.length).toBe(21); + }); + + it('should return PartsAsBuiltCustomerConfigurationModel for AS_BUILT_CUSTOMER', () => { + const result: TableViewConfig = service.initializeTableViewSettings(TableType.AS_BUILT_CUSTOMER); + expect(result.displayedColumns.length).toBe(13); + }); + + it('should return PartsAsBuiltSupplierConfigurationModel for AS_BUILT_SUPPLIER', () => { + const result: TableViewConfig = service.initializeTableViewSettings(TableType.AS_BUILT_SUPPLIER); + expect(result.displayedColumns.length).toBe(13); + }); + + it('should return NotificationsSentConfigurationModel for SENT_NOTIFICATION', () => { + const result: TableViewConfig = service.initializeTableViewSettings(TableType.SENT_NOTIFICATION); + expect(result.displayedColumns.length).toBe(10); + }); + + it('should return NotificationsReceivedConfigurationModel for RECEIVED_NOTIFICATION', () => { + const result: TableViewConfig = service.initializeTableViewSettings(TableType.RECEIVED_NOTIFICATION); + expect(result.displayedColumns.length).toBe(10); + }); + }); + + describe('YourService', () => { + + it('should return false if storage is empty', () => { + spyOn(service, 'getStoredTableSettings').and.returnValue(null); + const tableViewConfig: TableViewConfig = { + displayedColumns: [], + filterFormGroup: null, + sortableColumns: {}, + displayFilterColumnMappings: [], + filterColumns: [], + }; + const result = service.storedTableSettingsInvalid(tableViewConfig, TableType.AS_PLANNED_CUSTOMER); + expect(result).toBe(false); + }); + + it('should return false if stored columns match displayed columns', () => { + const storage = { + [TableType.AS_PLANNED_CUSTOMER]: { + columnsForDialog: [ 'col1', 'col2', 'menu' ], // Sample stored columns + }, + }; + spyOn(service, 'getStoredTableSettings').and.returnValue(storage); + const tableViewConfig: TableViewConfig = { + displayedColumns: [], + filterFormGroup: null, + sortableColumns: {}, + displayFilterColumnMappings: [], + filterColumns: [], + }; + tableViewConfig.displayedColumns = [ 'col1', 'col2', 'menu' ]; // Sample displayed columns + const result = service.storedTableSettingsInvalid(tableViewConfig, TableType.AS_PLANNED_CUSTOMER); + expect(result).toBe(false); + }); + + it('should return true if stored columns do not match displayed columns', () => { + const storage = { + [TableType.AS_PLANNED_CUSTOMER]: { + columnsForDialog: [ 'col1', 'col2', 'menu' ], // Sample stored columns + }, + }; + spyOn(service, 'getStoredTableSettings').and.returnValue(storage); + const tableViewConfig: TableViewConfig = { + displayedColumns: [], + filterFormGroup: null, + sortableColumns: {}, + displayFilterColumnMappings: [], + filterColumns: [], + }; + tableViewConfig.displayedColumns = [ 'col1', 'col3', 'menu' ]; // Different displayed columns + const result = service.storedTableSettingsInvalid(tableViewConfig, TableType.AS_PLANNED_CUSTOMER); + expect(result).toBe(true); + }); + + it('should show warning toast and remove storage if settings are invalid', () => { + const storage = { + [TableType.AS_PLANNED_CUSTOMER]: { + columnsForDialog: [ 'col1', 'col2', 'menu' ], // Sample stored columns + }, + }; + spyOn(service, 'getStoredTableSettings').and.returnValue(storage); + spyOn(service['toastService'], 'warning'); + spyOn(localStorage, 'removeItem'); + + const tableViewConfig: TableViewConfig = { + displayedColumns: [], + filterFormGroup: null, + sortableColumns: {}, + displayFilterColumnMappings: [], + filterColumns: [], + }; + tableViewConfig.displayedColumns = [ 'col1', 'col3', 'menu' ]; // Different displayed columns + service.storedTableSettingsInvalid(tableViewConfig, TableType.AS_PLANNED_CUSTOMER); + + expect(service['toastService'].warning).toHaveBeenCalledWith('table.tableSettings.invalid', 10000); + expect(localStorage.removeItem).toHaveBeenCalled(); + }); + }); + + + }); diff --git a/frontend/src/app/modules/core/user/table-settings.service.ts b/frontend/src/app/modules/core/user/table-settings.service.ts index 847646dd4d..0e22ae71cf 100644 --- a/frontend/src/app/modules/core/user/table-settings.service.ts +++ b/frontend/src/app/modules/core/user/table-settings.service.ts @@ -19,7 +19,16 @@ import { Injectable } from '@angular/core'; import { TableType } from '@shared/components/multi-select-autocomplete/table-type.model'; +import { NotificationsReceivedConfigurationModel } from '@shared/components/parts-table/notifications-received-configuration.model'; +import { NotificationsSentConfigurationModel } from '@shared/components/parts-table/notifications-sent-configuration.model'; +import { PartsAsBuiltConfigurationModel } from '@shared/components/parts-table/parts-as-built-configuration.model'; +import { PartsAsBuiltCustomerConfigurationModel } from '@shared/components/parts-table/parts-as-built-customer-configuration.model'; +import { PartsAsBuiltSupplierConfigurationModel } from '@shared/components/parts-table/parts-as-built-supplier-configuration.model'; +import { PartsAsPlannedConfigurationModel } from '@shared/components/parts-table/parts-as-planned-configuration.model'; +import { PartsAsPlannedCustomerConfigurationModel } from '@shared/components/parts-table/parts-as-planned-customer-configuration.model'; +import { PartsAsPlannedSupplierConfigurationModel } from '@shared/components/parts-table/parts-as-planned-supplier-configuration.model'; import { TableViewConfig } from '@shared/components/parts-table/table-view-config.model'; +import { ToastService } from '@shared/components/toasts/toast.service'; import { Subject } from 'rxjs'; @Injectable({ @@ -29,6 +38,9 @@ export class TableSettingsService { private settingsKey = 'TableViewSettings'; private changeEvent = new Subject(); + constructor(private readonly toastService: ToastService) { + } + storeTableSettings(tableSettingsList: any): void { // before setting anything, all maps in new tableSettingList should be stringified Object.keys(tableSettingsList).forEach(tableSetting => { @@ -80,6 +92,7 @@ export class TableSettingsService { } } if (isInvalid) { + this.toastService.warning('table.tableSettings.invalid', 10000); localStorage.removeItem(this.settingsKey); } return isInvalid; @@ -92,4 +105,26 @@ export class TableSettingsService { getEvent() { return this.changeEvent.asObservable(); } + + initializeTableViewSettings(tableType: TableType): TableViewConfig { + switch (tableType) { + case TableType.AS_PLANNED_CUSTOMER: + return new PartsAsPlannedCustomerConfigurationModel().filterConfiguration(); + case TableType.AS_PLANNED_OWN: + return new PartsAsPlannedConfigurationModel().filterConfiguration(); + case TableType.AS_PLANNED_SUPPLIER: + return new PartsAsPlannedSupplierConfigurationModel().filterConfiguration(); + case TableType.AS_BUILT_OWN: + return new PartsAsBuiltConfigurationModel().filterConfiguration(); + case TableType.AS_BUILT_CUSTOMER: + return new PartsAsBuiltCustomerConfigurationModel().filterConfiguration(); + case TableType.AS_BUILT_SUPPLIER: + return new PartsAsBuiltSupplierConfigurationModel().filterConfiguration(); + case TableType.SENT_NOTIFICATION: + return new NotificationsSentConfigurationModel().filterConfiguration(); + case TableType.RECEIVED_NOTIFICATION: + return new NotificationsReceivedConfigurationModel().filterConfiguration(); + } + } + } diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html index 522bc44916..64a257552e 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html @@ -116,10 +116,12 @@

@@ -155,6 +157,7 @@

value.id) || [], createdBy: '', - type: this.route.snapshot.queryParams['initialType'], + type: this.route.snapshot.queryParams['initialType'] ?? null, createdByName: '', createdDate: undefined, description: '', @@ -158,6 +158,13 @@ export class NotificationEditComponent implements OnDestroy { } public notificationFormGroupChange(notificationFormGroup: FormGroup) { + // if user switches type of notification in creation mode, reset affected parts and reload new available parts + if (this.selectedNotification.type !== notificationFormGroup.value['type']) { + this.selectedNotification.type = notificationFormGroup.value['type']; + // TODO: comment back in if todos inside the function were handled + // this.switchSelectedNotificationTypeAndResetParts(); + } + this.notificationFormGroup = notificationFormGroup; this.isSaveButtonDisabled = (notificationFormGroup.invalid || this.affectedPartIds.length < 1) || !this.notificationFormGroup.dirty; if (this.notificationFormGroup && this.notificationFormGroup.get('type').value === NotificationType.INVESTIGATION.valueOf() && !this.notificationFormGroup.get('bpn').value && this.sharedPartService.affectedParts && this.sharedPartService.affectedParts.length > 0) { @@ -340,6 +347,17 @@ export class NotificationEditComponent implements OnDestroy { }; } + private switchSelectedNotificationTypeAndResetParts() { + this.selectedNotification.assetIds = []; + this.affectedPartIds = []; + // TODO: to switch notifications we need to build a proper request to make them empty + //this.affectedPartsAsBuilt$ = this.partsFacade ... + // TODO: comment back in if the upper todo was handled + //this.availablePartsAsBuilt$ = this.selectedNotification.type === NotificationType.INVESTIGATION ? this.partsFacade.supplierPartsAsBuilt$ : this.ownPartsFacade.partsAsBuilt$; + this.setAffectedPartsBasedOnNotificationType(this.selectedNotification); + this.setAvailablePartsBasedOnNotificationType(this.selectedNotification); + } + protected readonly TableType = TableType; protected readonly MainAspectType = MainAspectType; } diff --git a/frontend/src/app/modules/page/notifications/detail/notification-detail.component.html b/frontend/src/app/modules/page/notifications/detail/notification-detail.component.html index 3741697654..afe1b4661a 100644 --- a/frontend/src/app/modules/page/notifications/detail/notification-detail.component.html +++ b/frontend/src/app/modules/page/notifications/detail/notification-detail.component.html @@ -40,7 +40,7 @@ > diff --git a/frontend/src/app/modules/page/notifications/detail/notification-detail.component.ts b/frontend/src/app/modules/page/notifications/detail/notification-detail.component.ts index c6542e3103..53548623e9 100644 --- a/frontend/src/app/modules/page/notifications/detail/notification-detail.component.ts +++ b/frontend/src/app/modules/page/notifications/detail/notification-detail.component.ts @@ -45,8 +45,8 @@ export class NotificationDetailComponent implements AfterViewInit, OnDestroy { @ViewChild('semanticModelIdTmp') semanticModelIdTmp: TemplateRef; - public readonly notificationPartsInformation$: Observable>; - public readonly supplierPartsDetailInformation$: Observable>; + public notificationPartsInformation$: Observable>; + public supplierPartsDetailInformation$: Observable>; public readonly selected$: Observable>; public readonly selectedItems$ = new BehaviorSubject([]); @@ -113,7 +113,7 @@ export class NotificationDetailComponent implements AfterViewInit, OnDestroy { this.paramSubscription?.unsubscribe(); } - public navigateToDetailView(){ + public navigateToEditView() { this.router.navigate([ `/inbox/${ this.selectedNotification.id }/edit` ], { queryParams: { diff --git a/frontend/src/app/modules/shared/components/parts-table/notifications-received-configuration.model.ts b/frontend/src/app/modules/shared/components/parts-table/notifications-received-configuration.model.ts new file mode 100644 index 0000000000..2688f76db6 --- /dev/null +++ b/frontend/src/app/modules/shared/components/parts-table/notifications-received-configuration.model.ts @@ -0,0 +1,22 @@ +import { TableFilterConfiguration } from '@shared/components/parts-table/parts-config.model'; + +export class NotificationsReceivedConfigurationModel extends TableFilterConfiguration { + constructor() { + const sortableColumns = { + select: false, + createdBy: true, + createdByName: true, + createdDate: true, + description: true, + severity: true, + status: true, + title: true, + type: true, + menu: false, + }; + + const dateFields = [ 'createdDate' ]; + const singleSearchFields = []; + super(sortableColumns, dateFields, singleSearchFields, true); + } +} diff --git a/frontend/src/app/modules/shared/components/parts-table/notifications-sent-configuration.model.ts b/frontend/src/app/modules/shared/components/parts-table/notifications-sent-configuration.model.ts new file mode 100644 index 0000000000..87e08fbc90 --- /dev/null +++ b/frontend/src/app/modules/shared/components/parts-table/notifications-sent-configuration.model.ts @@ -0,0 +1,22 @@ +import { TableFilterConfiguration } from '@shared/components/parts-table/parts-config.model'; + +export class NotificationsSentConfigurationModel extends TableFilterConfiguration { + constructor() { + const sortableColumns = { + select: false, + description: true, + title: true, + status: true, + createdDate: true, + severity: true, + sendTo: true, + sendToName: true, + type: true, + menu: false, + }; + + const dateFields = [ 'createdDate' ]; + const singleSearchFields = []; + super(sortableColumns, dateFields, singleSearchFields, true); + } +} diff --git a/frontend/src/app/modules/shared/components/parts-table/parts-config.model.ts b/frontend/src/app/modules/shared/components/parts-table/parts-config.model.ts index 43f7953437..de4c11b4c1 100644 --- a/frontend/src/app/modules/shared/components/parts-table/parts-config.model.ts +++ b/frontend/src/app/modules/shared/components/parts-table/parts-config.model.ts @@ -18,10 +18,8 @@ ********************************************************************************/ -import { TableViewConfig } from '@shared/components/parts-table/table-view-config.model'; - - import { PartsTableConfigUtils } from '@shared/components/parts-table/parts-table-config.utils'; +import { TableViewConfig } from '@shared/components/parts-table/table-view-config.model'; export class TableFilterConfiguration implements TableViewConfig { @@ -31,12 +29,12 @@ export class TableFilterConfiguration implements TableViewConfig { filterFormGroup: any; sortableColumns: any; - constructor(sortableColumns: any, dateFields?: any, singleSearchFields?: any) { + constructor(sortableColumns: any, dateFields?: any, singleSearchFields?: any, hasFilterColumn?: boolean) { this.displayedColumns = Object.keys(sortableColumns); this.filterFormGroup = PartsTableConfigUtils.createFormGroup(this.displayedColumns); - this.filterColumns = PartsTableConfigUtils.createFilterColumns(this.displayedColumns); + this.filterColumns = PartsTableConfigUtils.createFilterColumns(this.displayedColumns, hasFilterColumn); this.sortableColumns = sortableColumns; - this.displayFilterColumnMappings = PartsTableConfigUtils.generateFilterColumnsMapping(sortableColumns, dateFields, singleSearchFields); + this.displayFilterColumnMappings = PartsTableConfigUtils.generateFilterColumnsMapping(sortableColumns, dateFields, singleSearchFields, hasFilterColumn); } diff --git a/frontend/src/app/modules/shared/components/parts-table/parts-table-config.utils.ts b/frontend/src/app/modules/shared/components/parts-table/parts-table-config.utils.ts index fe851bb07a..a5cfbd6e2c 100644 --- a/frontend/src/app/modules/shared/components/parts-table/parts-table-config.utils.ts +++ b/frontend/src/app/modules/shared/components/parts-table/parts-table-config.utils.ts @@ -1,4 +1,5 @@ import { FormControl } from '@angular/forms'; +import { CreateHeaderFromColumns } from '@shared/components/table/table.model'; export class PartsTableConfigUtils { @@ -66,4 +67,29 @@ export class PartsTableConfigUtils { return [ first, ...filterColumnsMapping, last ].filter(value => value !== null); } + + public static getDefaultColumnVisibilityMap(displayedColumns: string[]): Map { + const initialColumnMap = new Map(); + for (const column of displayedColumns) { + initialColumnMap.set(column, true); + } + return initialColumnMap; + } + + static setupTableConfigurations(displayedColumnsForTable: string[], displayedColumns: string[], sortableColumns: Record, filterConfiguration: any[], filterFormGroup: any): any { + const headerKey = 'table.column'; + const tableConfig = { + displayedColumns: displayedColumnsForTable, + header: CreateHeaderFromColumns(displayedColumnsForTable, headerKey), + sortableColumns: sortableColumns, + }; + const newFilterFormGroup = { ...filterFormGroup }; + for (const controlName in filterFormGroup) { + if (filterFormGroup.hasOwnProperty(controlName)) { + newFilterFormGroup[controlName] = filterFormGroup[controlName]; + } + } + return { tableConfig, newFilterFormGroup }; + } + } diff --git a/frontend/src/app/modules/shared/components/parts-table/parts-table.component.ts b/frontend/src/app/modules/shared/components/parts-table/parts-table.component.ts index ffcca4f9ce..b1e4e10d6d 100644 --- a/frontend/src/app/modules/shared/components/parts-table/parts-table.component.ts +++ b/frontend/src/app/modules/shared/components/parts-table/parts-table.component.ts @@ -41,12 +41,7 @@ import { TableSettingsService } from '@core/user/table-settings.service'; import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { MultiSelectAutocompleteComponent } from '@shared/components/multi-select-autocomplete/multi-select-autocomplete.component'; import { TableType } from '@shared/components/multi-select-autocomplete/table-type.model'; -import { PartsAsBuiltConfigurationModel } from '@shared/components/parts-table/parts-as-built-configuration.model'; -import { PartsAsBuiltCustomerConfigurationModel } from '@shared/components/parts-table/parts-as-built-customer-configuration.model'; -import { PartsAsBuiltSupplierConfigurationModel } from '@shared/components/parts-table/parts-as-built-supplier-configuration.model'; -import { PartsAsPlannedConfigurationModel } from '@shared/components/parts-table/parts-as-planned-configuration.model'; -import { PartsAsPlannedCustomerConfigurationModel } from '@shared/components/parts-table/parts-as-planned-customer-configuration.model'; -import { PartsAsPlannedSupplierConfigurationModel } from '@shared/components/parts-table/parts-as-planned-supplier-configuration.model'; +import { PartsTableConfigUtils } from '@shared/components/parts-table/parts-table-config.utils'; import { TableViewConfig } from '@shared/components/parts-table/table-view-config.model'; import { TableSettingsComponent } from '@shared/components/table-settings/table-settings.component'; import { @@ -176,34 +171,11 @@ export class PartsTableComponent implements OnInit { return isDateFilter(key); } - private initializeTableViewSettings(): void { - switch (this.tableType) { - case TableType.AS_PLANNED_CUSTOMER: - this.tableViewConfig = new PartsAsPlannedCustomerConfigurationModel().filterConfiguration(); - break; - case TableType.AS_PLANNED_OWN: - this.tableViewConfig = new PartsAsPlannedConfigurationModel().filterConfiguration(); - break; - case TableType.AS_PLANNED_SUPPLIER: - this.tableViewConfig = new PartsAsPlannedSupplierConfigurationModel().filterConfiguration(); - break; - case TableType.AS_BUILT_OWN: - this.tableViewConfig = new PartsAsBuiltConfigurationModel().filterConfiguration(); - break; - case TableType.AS_BUILT_CUSTOMER: - this.tableViewConfig = new PartsAsBuiltCustomerConfigurationModel().filterConfiguration(); - break; - case TableType.AS_BUILT_SUPPLIER: - this.tableViewConfig = new PartsAsBuiltSupplierConfigurationModel().filterConfiguration(); - break; - } - } - private pageSize: number; private sorting: TableHeaderSort; ngOnInit() { - this.initializeTableViewSettings(); + this.tableViewConfig = this.tableSettingsService.initializeTableViewSettings(this.tableType); this.tableSettingsService.getEvent().subscribe(() => { this.setupTableViewSettings(); }); @@ -216,9 +188,8 @@ export class PartsTableComponent implements OnInit { private setupTableViewSettings() { - if (this.tableSettingsService.storedTableSettingsInvalid(this.tableViewConfig, this.tableType)) { - this.toastService.warning('table.tableSettings.invalid', 10000); - } + this.tableSettingsService.storedTableSettingsInvalid(this.tableViewConfig, this.tableType); + const tableSettingsList = this.tableSettingsService.getStoredTableSettings(); // check if there are table settings list if (tableSettingsList) { @@ -230,7 +201,7 @@ export class PartsTableComponent implements OnInit { // if no, create new a table setting for this.tabletype and put it into the list. Additionally, intitialize default table configuration tableSettingsList[this.tableType] = { columnsForDialog: this.tableViewConfig.displayedColumns, - columnSettingsOptions: this.getDefaultColumnVisibilityMap(), + columnSettingsOptions: PartsTableConfigUtils.getDefaultColumnVisibilityMap(this.tableViewConfig.displayedColumns), columnsForTable: this.tableViewConfig.displayedColumns, filterColumnsForTable: this.tableViewConfig.filterColumns, }; @@ -242,7 +213,7 @@ export class PartsTableComponent implements OnInit { const newTableSettingsList = { [this.tableType]: { columnsForDialog: this.tableViewConfig.displayedColumns, - columnSettingsOptions: this.getDefaultColumnVisibilityMap(), + columnSettingsOptions: PartsTableConfigUtils.getDefaultColumnVisibilityMap(this.tableViewConfig.displayedColumns), columnsForTable: this.tableViewConfig.displayedColumns, filterColumnsForTable: this.tableViewConfig.filterColumns, }, @@ -252,14 +223,6 @@ export class PartsTableComponent implements OnInit { } } - private getDefaultColumnVisibilityMap(): Map { - const initialColumnMap = new Map(); - for (const column of this.tableViewConfig.displayedColumns) { - initialColumnMap.set(column, true); - } - return initialColumnMap; - } - private setupTableConfigurations(displayedColumnsForTable: string[], displayedColumns: string[], sortableColumns: Record, filterConfiguration: any[], filterFormGroup: any): any { const headerKey = 'table.column'; diff --git a/frontend/src/app/modules/shared/components/request-notification-new/notification-new-request.component.ts b/frontend/src/app/modules/shared/components/request-notification-new/notification-new-request.component.ts index 5bf49bcf21..e36cc80c32 100644 --- a/frontend/src/app/modules/shared/components/request-notification-new/notification-new-request.component.ts +++ b/frontend/src/app/modules/shared/components/request-notification-new/notification-new-request.component.ts @@ -72,9 +72,16 @@ export class RequestNotificationNewComponent implements OnDestroy, OnInit { this.formGroupChanged.emit(this.formGroup); + } else { + // when clicking new notification without part context enable switching + if (!this.notification.type) { + this.formGroup.get('type').setValue(NotificationType.INVESTIGATION); + this.formGroup.get('type').enable(); + } else { + this.formGroup.get('type').setValue(this.notification.type); + } } - this.formGroup.get('type').setValue(this.notification.type); if (this.notification.type === NotificationType.INVESTIGATION) { this.formGroup.get('bpn').disable(); } @@ -84,7 +91,7 @@ export class RequestNotificationNewComponent implements OnDestroy, OnInit { } this.formGroupChanged.emit(this.formGroup); - this.formGroup.valueChanges.subscribe(value => { + this.formGroup.valueChanges.subscribe(() => { //TODO: For Create, check here or in parent if the part tables should update (depending on passed partId, investigation or alert type) this.formGroupChanged.emit(this.formGroup); }); diff --git a/frontend/src/app/modules/shared/components/table-settings/table-settings.component.spec.ts b/frontend/src/app/modules/shared/components/table-settings/table-settings.component.spec.ts index 05756fb5ee..37ef774f00 100644 --- a/frontend/src/app/modules/shared/components/table-settings/table-settings.component.spec.ts +++ b/frontend/src/app/modules/shared/components/table-settings/table-settings.component.spec.ts @@ -113,7 +113,6 @@ describe('TableSettingsComponent', () => { expect(component.tableType).toEqual(TableType.AS_BUILT_OWN); expect(component.defaultColumns).toEqual([ 'column1', 'column2' ]); expect(component.defaultFilterColumns).toEqual([ 'filtercolumn1', 'filtercolumn2' ]); - expect(component.isCustomerTable).toEqual(false); }); it('should call save method and update tableSettingsService', () => { diff --git a/frontend/src/app/modules/shared/components/table-settings/table-settings.component.ts b/frontend/src/app/modules/shared/components/table-settings/table-settings.component.ts index a65d238002..dad0de45dd 100644 --- a/frontend/src/app/modules/shared/components/table-settings/table-settings.component.ts +++ b/frontend/src/app/modules/shared/components/table-settings/table-settings.component.ts @@ -47,14 +47,10 @@ export class TableSettingsComponent { selectAllSelected: boolean; selectedColumn: string = null; - isCustomerTable: boolean; - - constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any, public readonly tableSettingsService: TableSettingsService) { // Layout this.title = data.title; this.panelClass = data.panelClass; - this.isCustomerTable = data.tableType === TableType.AS_BUILT_CUSTOMER || data.tableType === TableType.AS_PLANNED_CUSTOMER; // Passed Data this.tableType = data.tableType; this.defaultColumns = data.defaultColumns; @@ -125,11 +121,13 @@ export class TableSettingsComponent { return; } - let oldPosition = this.dialogColumns.indexOf(this.selectedColumn); - // in non customer table we have the select Column as first and why - let upperLimit = this.isCustomerTable ? 0 : 1; + const oldPosition = this.dialogColumns.indexOf(this.selectedColumn); + // for tables where we have a select column at first + const upperLimit = this.dialogColumns.includes('select') ? 1 : 0; + // for tables where we have a menu column at last + const bottomLimit = this.dialogColumns.includes('menu') ? this.dialogColumns.length - 2 : this.dialogColumns.length - 1; let step = direction === 'up' ? -1 : 1; - if ((oldPosition == upperLimit && direction === 'up') || (oldPosition === this.dialogColumns.length - 1 && direction === 'down')) { + if ((oldPosition == upperLimit && direction === 'up') || (oldPosition === bottomLimit && direction === 'down')) { return; } let temp = this.dialogColumns[oldPosition + step]; diff --git a/frontend/src/app/modules/shared/components/table/table.component.html b/frontend/src/app/modules/shared/components/table/table.component.html index c1eca19b68..0f1dbe847b 100644 --- a/frontend/src/app/modules/shared/components/table/table.component.html +++ b/frontend/src/app/modules/shared/components/table/table.component.html @@ -21,12 +21,106 @@
-
{{tableHeader | i18n}}
+
{{ tableHeader | i18n }} + + settings + +
+
+
+ +
+ +
+ + +
+ +
+ + + + +
+
+
-
+

{{tableHeader | i18n }}

+ class="large-text table--header">{{ tableHeader | i18n }}

{{ selectedPartsInfoLabel | i18n : {count: selection?.selected?.length || 0} }}

@@ -57,8 +151,8 @@ > - + @@ -67,7 +161,7 @@ data-testid="table-component--head-row" class="table--header--row"> - + @@ -82,7 +176,9 @@ [placeholderMultiple]="('multiSelect.multipleResults' | i18n)" [tableType]="tableType" ngDefaultControl - [formControl]="filterFormGroup.controls[filter.filterKey]"> + [formControl]="filterFormGroup.controls[filter.filterKey]"> + + @@ -209,7 +305,8 @@

{{ 'table.noResultFound' | i18n }}

-
+

+ *matCellDef="let element" mat-cell class="table--cell" data-testid="table-component--cell-data" + [ngClass]="{'max-width-column': labelId === 'contracts' && column === 'contractId' }"> ; @Input() additionalTableHeader = false; + @Input() tableHeaderMenuEnabled = false; @Input() set tableConfig(tableConfig: TableConfig) { @@ -62,23 +69,9 @@ export class TableComponent { const { menuActionsConfig: menuActions, displayedColumns: dc, columnRoles, hasPagination = true } = tableConfig; const displayedColumns = dc.filter(column => this.roleService.hasAccess(columnRoles?.[column] ?? 'user') || this.roleService.hasAccess('admin')); - - const viewDetailsMenuAction: MenuActionConfig = { - label: 'actions.viewDetails', - icon: 'remove_red_eye', - action: (data: Record) => this.selected.emit(data), - }; - - const editDetailsMenuAction: MenuActionConfig = { - label: 'actions.edit', - icon: 'edit', - action: (data: Record) => this.editClicked.emit(data), - condition: data => this.isEditable(data), - isAuthorized: this.roleService.isSupervisor(), - }; - - const menuActionsConfig = menuActions ? [ viewDetailsMenuAction, editDetailsMenuAction, ...menuActions ] : null; + const menuActionsConfig = this.menuActionsWithAddedDefaultActions(menuActions); this._tableConfig = { ...tableConfig, displayedColumns, hasPagination, menuActionsConfig }; + } isEditable(data: any): boolean { @@ -135,7 +128,7 @@ export class TableComponent { } this.removeSelectedValues(deselectItem); - this.emitMultiSelect(); + this.handleSelectionChange(); } @Input() set addTrigger(newItem: unknown) { @@ -144,7 +137,7 @@ export class TableComponent { } this.selection.select(newItem); - this.emitMultiSelect(); + this.handleSelectionChange(); } @Output() selected = new EventEmitter>(); @@ -155,6 +148,7 @@ export class TableComponent { @Output() filterActivated = new EventEmitter(); @Input() public autocompleteEnabled = false; + @Input() tableSettingsEnabled: boolean = false; public readonly dataSource = new MatTableDataSource(); public readonly selection = new SelectionModel(true, []); @@ -165,6 +159,8 @@ export class TableComponent { public selectedRow: Record; public isMenuOpen: boolean; + public notificationsSelectedOnlyInStatusCreated: boolean; + private pageSize: number; private sorting: TableHeaderSort; @@ -175,41 +171,116 @@ export class TableComponent { filterFormGroup = new FormGroup({}); // input notification type map to parttable type, - @Input() - tableType: TableType = TableType.AS_BUILT_OWN; + @Input() tableType: TableType = TableType.AS_BUILT_OWN; + + public displayedColumns: string[]; + public defaultColumns: string[]; - constructor(private readonly roleService: RoleService) { + constructor( + public readonly roleService: RoleService, + private dialog: MatDialog, + private tableSettingsService: TableSettingsService, + public toastService: ToastService, + private readonly router: Router, + ) { } ngOnInit(): void { - const displayFilterColumnMappings = this.tableType === TableType.CONTRACTS ? - PartsTableConfigUtils.generateFilterColumnsMapping(this.tableConfig?.sortableColumns, [ 'creationDate', 'endDate' ], [], true, false) - : PartsTableConfigUtils.generateFilterColumnsMapping(this.tableConfig?.sortableColumns, [ 'createdDate', 'targetDate' ], [], false, true); - - const filterColumns = this.tableType === TableType.CONTRACTS ? - PartsTableConfigUtils.createFilterColumns(this.tableConfig?.displayedColumns, true, false) - : PartsTableConfigUtils.createFilterColumns(this.tableConfig?.displayedColumns, false, true); - - this.tableViewConfig = { - displayedColumns: this.tableConfig?.sortableColumns ? Object.keys(this.tableConfig?.sortableColumns) : [], - filterFormGroup: PartsTableConfigUtils.createFormGroup(this.tableConfig?.displayedColumns), - filterColumns: filterColumns, - sortableColumns: this.tableConfig?.sortableColumns, - displayFilterColumnMappings: displayFilterColumnMappings, - }; - for (const controlName in this.tableViewConfig.filterFormGroup) { - if (this.tableViewConfig.filterFormGroup.hasOwnProperty(controlName)) { - this.filterFormGroup.addControl(controlName, this.tableViewConfig.filterFormGroup[controlName]); + if (this.tableSettingsEnabled) { + this.tableViewConfig = this.tableSettingsService.initializeTableViewSettings(this.tableType); + this.tableSettingsService.getEvent().subscribe(() => { + this.setupTableViewSettings(); + }); + this.setupTableViewSettings(); + } else { + const displayFilterColumnMappings = this.tableType === TableType.CONTRACTS ? + PartsTableConfigUtils.generateFilterColumnsMapping(this.tableConfig?.sortableColumns, [ 'creationDate', 'endDate' ], [], true, false) + : PartsTableConfigUtils.generateFilterColumnsMapping(this.tableConfig?.sortableColumns, [ 'createdDate', 'targetDate' ], [], false, true); + + const filterColumns = this.tableType === TableType.CONTRACTS ? + PartsTableConfigUtils.createFilterColumns(this.tableConfig?.displayedColumns, true, false) + : PartsTableConfigUtils.createFilterColumns(this.tableConfig?.displayedColumns, false, true); + + this.tableViewConfig = { + displayedColumns: this.tableConfig?.sortableColumns ? Object.keys(this.tableConfig?.sortableColumns) : [], + filterFormGroup: PartsTableConfigUtils.createFormGroup(this.tableConfig?.displayedColumns), + filterColumns: filterColumns, + sortableColumns: this.tableConfig?.sortableColumns, + displayFilterColumnMappings: displayFilterColumnMappings, + }; + for (const controlName in this.tableViewConfig.filterFormGroup) { + if (this.tableViewConfig.filterFormGroup.hasOwnProperty(controlName)) { + this.filterFormGroup.addControl(controlName, this.tableViewConfig.filterFormGroup[controlName]); + } } } this.filterFormGroup.valueChanges.subscribe((formValues) => { this.filterActivated.emit(formValues); }); + + } + + private setupTableViewSettings() { + + this.tableSettingsService.storedTableSettingsInvalid(this.tableViewConfig, this.tableType); + + const tableSettingsList = this.tableSettingsService.getStoredTableSettings(); + // check if there are table settings list + if (tableSettingsList) { + // if yes, check if there is a table-setting for this table type + if (tableSettingsList[this.tableType]) { + // if yes, get the effective displayedcolumns from the settings and set the tableconfig after it. + this.setupTableConfigurations(tableSettingsList[this.tableType].columnsForTable, tableSettingsList[this.tableType].filterColumnsForTable, this.tableViewConfig.sortableColumns, this.tableViewConfig.displayFilterColumnMappings, this.tableViewConfig.filterFormGroup); + } else { + // if no, create new a table setting for this.tabletype and put it into the list. Additionally, intitialize default table configuration + tableSettingsList[this.tableType] = { + columnsForDialog: this.tableViewConfig.displayedColumns, + columnSettingsOptions: PartsTableConfigUtils.getDefaultColumnVisibilityMap(this.tableViewConfig.displayedColumns), + columnsForTable: this.tableViewConfig.displayedColumns, + filterColumnsForTable: this.tableViewConfig.filterColumns, + }; + this.tableSettingsService.storeTableSettings(tableSettingsList); + this.setupTableConfigurations(this.tableViewConfig.displayedColumns, this.tableViewConfig.filterColumns, this.tableViewConfig.sortableColumns, this.tableViewConfig.displayFilterColumnMappings, this.tableViewConfig.filterFormGroup); + } + } else { + // if no, create new list and a settings entry for this.tabletype with default values and set correspondingly the tableconfig + const newTableSettingsList = { + [this.tableType]: { + columnsForDialog: this.tableViewConfig.displayedColumns, + columnSettingsOptions: PartsTableConfigUtils.getDefaultColumnVisibilityMap(this.tableViewConfig.displayedColumns), + columnsForTable: this.tableViewConfig.displayedColumns, + filterColumnsForTable: this.tableViewConfig.filterColumns, + }, + }; + this.tableSettingsService.storeTableSettings(newTableSettingsList); + this.setupTableConfigurations(this.tableViewConfig.displayedColumns, this.tableViewConfig.filterColumns, this.tableViewConfig.sortableColumns, this.tableViewConfig.displayFilterColumnMappings, this.tableViewConfig.filterFormGroup); + } + + } + + + private setupTableConfigurations(displayedColumnsForTable: string[], displayedColumns: string[], sortableColumns: Record, filterConfiguration: any[], filterFormGroup: any): any { + const headerKey = 'table.column'; + this.tableConfig = { + ...this.tableConfig, + displayedColumns: displayedColumnsForTable, + header: CreateHeaderFromColumns(displayedColumnsForTable, headerKey), + sortableColumns: sortableColumns, + }; + this.displayedColumns = displayedColumns; + + for (const controlName in filterFormGroup) { + if (filterFormGroup.hasOwnProperty(controlName)) { + this.filterFormGroup.addControl(controlName, filterFormGroup[controlName]); + } + } + } + public areAllRowsSelected(): boolean { return this.dataSource.data.every(data => this.isSelected(data)); } @@ -227,7 +298,7 @@ export class TableComponent { ? this.removeSelectedValues(this.dataSource.data) : this.addSelectedValues(this.dataSource.data); - this.emitMultiSelect(); + this.handleSelectionChange(); } public onPaginationChange({ pageIndex, pageSize }: PageEvent): void { @@ -238,7 +309,7 @@ export class TableComponent { public updateSortingOfData({ active, direction }: Sort): void { this.selection.clear(); - this.emitMultiSelect(); + this.handleSelectionChange(); this.sorting = !direction ? null : ([ active, direction ] as TableHeaderSort); this.isDataLoading = true; if (this.pageSize === 0) { @@ -249,7 +320,7 @@ export class TableComponent { public toggleSelection(row: unknown): void { this.isSelected(row) ? this.removeSelectedValues([ row ]) : this.addSelectedValues([ row ]); - this.emitMultiSelect(); + this.handleSelectionChange(); } public selectElement(row: Record) { @@ -260,7 +331,8 @@ export class TableComponent { } } - private emitMultiSelect(): void { + private handleSelectionChange(): void { + this.notificationsSelectedOnlyInStatusCreated = this.selection.selected.every(notification => notification?.['status'] === NotificationStatus.CREATED); this.multiSelect.emit(this.selection.selected); } @@ -292,5 +364,42 @@ export class TableComponent { removeSelectedValues(this.selection, itemsToRemove); } + openDialog() { + const config = new MatDialogConfig(); + config.data = { + title: 'table.tableSettings.title', + panelClass: 'custom', + tableType: this.tableType, + defaultColumns: this.tableViewConfig.displayedColumns, + defaultFilterColumns: this.tableViewConfig.filterColumns, + }; + this.dialog.open(TableSettingsComponent, config); + } + + navigateToNotificationCreationView() { + this.router.navigate([ 'inbox/create' ]); + } + + private menuActionsWithAddedDefaultActions(menuActionsConfig: MenuActionConfig[] = []): MenuActionConfig[] { + const viewDetailsMenuAction: MenuActionConfig = { + label: 'actions.viewDetails', + icon: 'remove_red_eye', + action: (data: Record) => this.selected.emit(data), + }; + + const editDetailsMenuAction: MenuActionConfig = { + label: 'actions.edit', + icon: 'edit', + action: (data: Record) => this.editClicked.emit(data), + condition: data => this.isEditable(data), + isAuthorized: this.roleService.isSupervisor(), + }; + const defaultActionsToAdd: MenuActionConfig[] = [ viewDetailsMenuAction, editDetailsMenuAction ] + .filter(action => !menuActionsConfig.some(a => a.label === action.label)); + + return [ ...defaultActionsToAdd, ...menuActionsConfig ]; + }; + protected readonly MainAspectType = MainAspectType; + } diff --git a/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.html b/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.html index d7fe00837b..7d80387836 100644 --- a/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.html +++ b/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.html @@ -26,18 +26,21 @@ diff --git a/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.ts b/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.ts index 0dfdddd748..a48fcbbe8b 100644 --- a/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.ts +++ b/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.ts @@ -49,11 +49,15 @@ export class NotificationTabComponent implements AfterViewInit { @Input() multiSortList: TableHeaderSort[] = []; @Input() tableType: TableType; @Input() autocompleteEnabled = false; + @Input() tableSettingsEnabled = false; + @Input() tableHeader = ''; + @Input() tableHeaderMenuEnabled = false; @Output() tableConfigChanged = new EventEmitter(); @Output() notificationsFilterChanged = new EventEmitter(); @Output() selected = new EventEmitter(); @Output() editNotificationClicked = new EventEmitter(); + @Output() multiSelect = new EventEmitter(); @ViewChild('titleTmp') titleTemplate: TemplateRef; @ViewChild('statusTmp') statusTemplate: TemplateRef; @ViewChild('severityTmp') severityTemplate: TemplateRef; @@ -122,5 +126,9 @@ export class NotificationTabComponent implements AfterViewInit { this.tableConfigChanged.emit(tableEventConfig); } + public emitMultiSelect(selected: Notification[]) { + this.multiSelect.emit(selected); + } + protected readonly NotificationType = NotificationType; } diff --git a/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.html b/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.html index 7d66572056..5bd3ec0f7e 100644 --- a/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.html +++ b/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.html @@ -35,6 +35,8 @@ @@ -58,19 +62,22 @@ {{ translationContext + '.tabs.received' | i18n }} diff --git a/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.spec.ts b/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.spec.ts index 8c1447e82a..aa0b010b26 100644 --- a/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.spec.ts +++ b/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.spec.ts @@ -88,7 +88,7 @@ describe('NotificationsInboxComponent', () => { [queuedAndRequestedNotifications$]='queuedAndRequestedNotifications$' [receivedNotifications$]='receivedNotifications$' [translationContext]="'commonAlert'" - [menuActionsConfig]="'menuActionsConfig'" + [menuActionsConfig]="[]" [receivedOptionalColumns]="['severity', 'createdBy', 'createdByName', 'targetDate']" [receivedSortableColumns]="{description: true, title: true, status: true, createdDate: true, severity: true, createdBy: true, createdByName: true, targetDate: true, menu: false}" [queuedAndRequestedOptionalColumns]="['severity', 'sendTo', 'sendToName', 'targetDate']" diff --git a/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.ts b/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.ts index 429c10a308..6692b30f1f 100644 --- a/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.ts +++ b/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.ts @@ -26,7 +26,7 @@ import { MenuActionConfig, TableEventConfig, TableHeaderSort } from '@shared/com import { Notification, Notifications } from '@shared/model/notification.model'; import { View } from '@shared/model/view.model'; import { StaticIdService } from '@shared/service/staticId.service'; -import { Observable } from 'rxjs'; +import { BehaviorSubject, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @Component({ @@ -54,6 +54,7 @@ export class NotificationComponent { public readonly receivedTabLabelId = this.staticIdService.generateId('Notification.receivedTab'); public readonly queuedAndRequestedTabLabelId = this.staticIdService.generateId('Notification.queuedAndRequestedTab'); + public readonly currentSelectedItems$ = new BehaviorSubject([]); constructor( private readonly router: Router, diff --git a/frontend/src/assets/locales/de/common.json b/frontend/src/assets/locales/de/common.json index 600332996e..f081e0a44a 100644 --- a/frontend/src/assets/locales/de/common.json +++ b/frontend/src/assets/locales/de/common.json @@ -32,7 +32,9 @@ "about": "Über Catena-X Open-Source Rückverfolgbarkeit", "relations": "Beziehungen", "admin": "Verwaltung Catena-X", - "alerts": "Qualitätsthemen" + "alerts" : "Qualitätsthemen", + "receivedQualityTopics" : "Empfangene Qualitätsthemen", + "sentQualityTopics" : "Versendete Qualitätsthemen" }, "layout": { "nav": { @@ -90,6 +92,17 @@ "alert": "Qualitätswarnung", "investigation": "Qualitätsuntersuchung" }, + "createNotification" : "Qualitätsthema erstellen", + "sendNotification" : "Qualitätsthemen senden", + "cancelNotification" : "Qualitätsthemen abbrechen", + "addParts" : "Teile hinzufügen", + "viewDetails" : "Details anzeigen", + "more" : "Mehr Aktionen", + "editNotification" : "Qualitätsthemen bearbeiten", + "selectAtLeastOne" : "Aktionen erfordern mindestens eine Selektion in der Tabelle", + "unauthorized" : "Die Funktion ist aufgrund einer fehlenden Rolle deaktiviert. Bitten Sie Ihren Administrator, die erforderliche Rolle für die Funktion bereitzustellen.", + "noFunctionality" : "Funktionalität wurde noch nicht implementiert.", + "onlyNotificationInStatusCreatedAllowed" : "Aktion erfordert eine Selektion an Qualitätsthemen im Status \"Erstellt\".", "column": { "id": "ID", "idShort": "Kurz-ID", diff --git a/frontend/src/assets/locales/en/common.json b/frontend/src/assets/locales/en/common.json index 0216be0494..0527d0030b 100644 --- a/frontend/src/assets/locales/en/common.json +++ b/frontend/src/assets/locales/en/common.json @@ -32,7 +32,9 @@ "about": "About Catena-X Open-Source Traceability", "relations": "Part relations", "admin": "Administration Catena-X", - "alerts": "Quality topics" + "alerts" : "Quality topics", + "receivedQualityTopics" : "Received quality topics", + "sentQualityTopics" : "Sent quality topics" }, "layout": { "nav": { @@ -89,6 +91,17 @@ "saveAction": "Save", "alert": "Quality Alert" }, + "createNotification" : "Create quality topic", + "sendNotification" : "Send quality topics", + "cancelNotification" : "Cancel quality topics", + "addParts" : "Add parts to quality topics", + "viewDetails" : "View details", + "more" : "More actions", + "editNotification" : "Edit quality topics", + "selectAtLeastOne" : "Actions require atleast one selection in the table", + "unauthorized" : "Functionality is disabled because of missing role. Ask your administrator to provide the required role for the functionality.", + "noFunctionality" : "Functionality is not implemented yet", + "onlyNotificationInStatusCreatedAllowed" : "Action only allowed with a selection of notifications in status \"Queued\".", "column": { "id": "ID", "idShort": "ID Short", @@ -299,8 +312,8 @@ "commonAlert": { "viewAll": "View all", "tabs": { - "received": "received", - "queuedAndRequested": "sent" + "received" : "Received", + "queuedAndRequested" : "Sent" }, "status": { "SENT": "Requested",