diff --git a/frontend/cypress/support/step_definitions/menu.ts b/frontend/cypress/support/step_definitions/menu.ts index 4f306543a1..d74591c5df 100644 --- a/frontend/cypress/support/step_definitions/menu.ts +++ b/frontend/cypress/support/step_definitions/menu.ts @@ -38,7 +38,7 @@ When("user navigate to {string}", function(desiredMenu) { break; } case 'Quality alerts': { - cy.get('[href="/alerts"]').click(); + cy.get('[href="/inbox"]').click(); break; } case 'About': { diff --git a/frontend/src/app/mocks/mock.ts b/frontend/src/app/mocks/mock.ts index e5a424d1ac..1195549e40 100644 --- a/frontend/src/app/mocks/mock.ts +++ b/frontend/src/app/mocks/mock.ts @@ -24,7 +24,6 @@ import { adminHandler, dashboardHandler, errorHandler, - investigationsHandlers, otherPartsAsBuiltHandlers, otherPartsAsPlannedHandlers, partsAsBuiltHandlers, @@ -39,10 +38,9 @@ const handlers = [ ...otherPartsAsPlannedHandlers, ...partsAsBuiltHandlers, ...partsAsPlannedHandlers, - ...investigationsHandlers, ...alertsHandlers, ...adminHandler, ...errorHandler, - ...policyHandler + ...policyHandler, ]; export const worker = setupWorker(...handlers); diff --git a/frontend/src/app/mocks/services/alerts-mock/alerts.handler.ts b/frontend/src/app/mocks/services/alerts-mock/alerts.handler.ts index 6feae03141..9ce6942c13 100644 --- a/frontend/src/app/mocks/services/alerts-mock/alerts.handler.ts +++ b/frontend/src/app/mocks/services/alerts-mock/alerts.handler.ts @@ -31,19 +31,19 @@ import { } from './alerts.test.model'; const commonHandler = [ - rest.post(`*${ environment.apiUrl }/notifications/:alertId/close`, (req, res, ctx) => { + rest.post(`*${ environment.apiUrl }/notifications/:notificationId/close`, (req, res, ctx) => { return res(ctx.status(204)); }), - rest.post(`*${ environment.apiUrl }/notifications/:alertId/approve`, (req, res, ctx) => { + rest.post(`*${ environment.apiUrl }/notifications/:notificationId/approve`, (req, res, ctx) => { return res(ctx.status(400), ctx.json({message: "Failed to send alert to EDC"})); }), - rest.post(`*${ environment.apiUrl }/notifications/:alertId/cancel`, (req, res, ctx) => { + rest.post(`*${ environment.apiUrl }/notifications/:notificationId/cancel`, (req, res, ctx) => { return res(ctx.status(204)); }), - rest.post(`${ environment.apiUrl }/notifications/:alertId/update`, (req, res, ctx) => { + rest.post(`${ environment.apiUrl }/notifications/:notificationId/update`, (req, res, ctx) => { return res(ctx.status(204)); }), ]; @@ -79,7 +79,7 @@ export const alertsHandlers = [ return res(ctx.status(200), ctx.json(applyPagination(buildMockAlerts(currentStatus, 'RECEIVER'), pagination))); }), - rest.get(`*${ environment.apiUrl }/notifications/:alertId`, (req, res, ctx) => { + rest.get(`*${ environment.apiUrl }/notifications/:notificationId`, (req, res, ctx) => { const { alertId } = req.params; const indexFromId = parseInt((alertId as string).replace('id-', ''), 10); @@ -110,7 +110,7 @@ export const alertsHandlers = [ //return res(ctx.status(200), ctx.json({ id: AlertIdPrefix + 1 })); }), - rest.put(`*${ environment.apiUrl }/notifications/:alertId/status`, async (req, res, ctx) => { + rest.put(`*${ environment.apiUrl }/notifications/:notificationId/status`, async (req, res, ctx) => { const { alertId } = req.params; const { status } = await req.json(); @@ -142,7 +142,7 @@ export const alertsTestHandlers = [ return res(ctx.status(200), ctx.json(applyPagination(testBuildMockAlerts(currentStatus, 'RECEIVER'), pagination))); }), - rest.get(`*${ environment.apiUrl }/notifications/:alertId`, (req, res, ctx) => { + rest.get(`*${ environment.apiUrl }/notifications/:notificationId`, (req, res, ctx) => { const { alertId } = req.params; const indexFromId = parseInt((alertId as string).replace('id-', ''), 10); @@ -167,7 +167,7 @@ export const alertsTestHandlers = [ return res(ctx.status(200), ctx.json({ id: testAlertIdPrefix + 1 })); }), - rest.put(`*${ environment.apiUrl }/notifications/:alertId/status`, async (req, res, ctx) => { + rest.put(`*${ environment.apiUrl }/notifications/:notificationId/status`, async (req, res, ctx) => { const { alertId } = req.params; const { status } = await req.json(); diff --git a/frontend/src/app/mocks/services/index.ts b/frontend/src/app/mocks/services/index.ts index 4f2350caaf..507b497535 100644 --- a/frontend/src/app/mocks/services/index.ts +++ b/frontend/src/app/mocks/services/index.ts @@ -28,7 +28,6 @@ export { otherPartsAsPlannedHandlers, otherPartsAsPlannedHandlersTest, } from './otherParts-mock/otherParts.handler'; -export { investigationsHandlers, investigationsTestHandlers } from './investigations-mock/investigations.handler'; export { adminHandler } from './admin-mock/admin.handler'; export { errorHandler } from './error-mock/error.handler'; export { policyHandler } from './policy-mock/policy.handler' diff --git a/frontend/src/app/mocks/services/investigations-mock/investigations.handler.ts b/frontend/src/app/mocks/services/investigations-mock/investigations.handler.ts deleted file mode 100644 index 705a15b9e5..0000000000 --- a/frontend/src/app/mocks/services/investigations-mock/investigations.handler.ts +++ /dev/null @@ -1,190 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { environment } from '@env'; -import { NotificationStatus } from '@shared/model/notification.model'; -import { rest } from 'msw'; -import { applyPagination, extractPaginationOfNotifications } from '../pagination.helper'; -import { buildMockInvestigations, getInvestigationById } from './investigations.model'; -import { - buildMockInvestigations as testBuildMockInvestigations, - getInvestigationById as testGetInvestigationById, - InvestigationIdPrefix as testInvestigationIdPrefix, -} from './investigations.test.model'; - -const commonHandler = [ - rest.post(`*${ environment.apiUrl }/investigations/:investigationId/close`, (req, res, ctx) => { - return res(ctx.status(204)); - }), - - rest.post(`*${ environment.apiUrl }/investigations/:investigationId/approve`, (req, res, ctx) => { - return res(ctx.status(400), ctx.json({message: "Failed to send investigation to EDC"})); - }), - - rest.post(`*${ environment.apiUrl }/investigations/:investigationId/cancel`, (req, res, ctx) => { - return res(ctx.status(204)); - }), - - rest.post(`${ environment.apiUrl }/investigations/:investigationId/update`, (req, res, ctx) => { - return res(ctx.status(204)); - }), -]; - -export const investigationsHandlers = [ - rest.post(`*${ environment.apiUrl }/investigations/filter`, (req, res, ctx) => { - const pagination = extractPaginationOfNotifications(req); - - const currentStatus = [ - NotificationStatus.CREATED, - NotificationStatus.SENT, - NotificationStatus.ACKNOWLEDGED, - NotificationStatus.ACCEPTED, - NotificationStatus.DECLINED, - NotificationStatus.CLOSED, - NotificationStatus.CANCELED, - ]; - - return res( - ctx.status(200), - ctx.json(applyPagination(buildMockInvestigations(currentStatus, 'SENDER'), pagination)), - ); - }), - - rest.post(`*${ environment.apiUrl }/investigations/filter`, (req, res, ctx) => { - const pagination = extractPaginationOfNotifications(req); - - const currentStatus = [ - NotificationStatus.RECEIVED, - NotificationStatus.ACKNOWLEDGED, - NotificationStatus.ACCEPTED, - NotificationStatus.DECLINED, - NotificationStatus.CLOSED, - NotificationStatus.CANCELED, - ]; - return res( - ctx.status(200), - ctx.json(applyPagination(buildMockInvestigations(currentStatus, 'RECEIVER'), pagination)), - ); - }), - - rest.get(`*${ environment.apiUrl }/investigations/:investigationId`, (req, res, ctx) => { - const { investigationId } = req.params; - - const indexFromId = parseInt((investigationId as string).replace('id-', ''), 10); - - const statusCollection = [ - NotificationStatus.CREATED, - NotificationStatus.SENT, - NotificationStatus.RECEIVED, - NotificationStatus.CLOSED, - NotificationStatus.CANCELED, - NotificationStatus.ACKNOWLEDGED, - NotificationStatus.ACCEPTED, - NotificationStatus.DECLINED, - - NotificationStatus.ACKNOWLEDGED, - NotificationStatus.ACCEPTED, - NotificationStatus.DECLINED, - NotificationStatus.CLOSED, - NotificationStatus.CANCELED, - ]; - const channel = [ 2, 8, 9, 10, 11, 12 ].includes(indexFromId) ? 'RECEIVER' : 'SENDER'; - const randomNotification = buildMockInvestigations([ statusCollection[indexFromId] ], channel)[0]; - - return res(ctx.status(200), ctx.json({ ...randomNotification, id: investigationId })); - }), - rest.post(`*${ environment.apiUrl }/investigations`, (_, res, ctx) => { - return res(ctx.status(400), ctx.json({message: "Error while sending investigation to EDC"})); - //return res(ctx.status(200), ctx.json({ id: InvestigationIdPrefix + 1 })); - }), - - rest.put(`*${ environment.apiUrl }/investigations/:investigationId/status`, async (req, res, ctx) => { - const { investigationId } = req.params; - const { status } = await req.json(); - - const investigation = getInvestigationById(investigationId as string); - return res(ctx.status(200), ctx.json({ ...investigation, status })); - }), - ...commonHandler, -]; - -export const investigationsTestHandlers = [ - rest.post(`*${ environment.apiUrl }/investigations/filter`, (req, res, ctx) => { - const pagination = extractPaginationOfNotifications(req); - - const currentStatus = [ - NotificationStatus.CREATED, - NotificationStatus.SENT, - NotificationStatus.ACKNOWLEDGED, - NotificationStatus.ACCEPTED, - NotificationStatus.DECLINED, - ]; - - return res( - ctx.status(200), - ctx.json(applyPagination(testBuildMockInvestigations(currentStatus, 'SENDER'), pagination)), - ); - }), - - rest.post(`*${ environment.apiUrl }/investigations/filter`, (req, res, ctx) => { - const pagination = extractPaginationOfNotifications(req); - - const currentStatus = [ NotificationStatus.RECEIVED, NotificationStatus.ACKNOWLEDGED ]; - return res( - ctx.status(200), - ctx.json(applyPagination(testBuildMockInvestigations(currentStatus, 'RECEIVER'), pagination)), - ); - }), - - rest.get(`*${ environment.apiUrl }/investigations/:investigationId`, (req, res, ctx) => { - const { investigationId } = req.params; - - const indexFromId = parseInt((investigationId as string).replace('id-', ''), 10); - - const statusCollection = [ - NotificationStatus.CREATED, - NotificationStatus.SENT, - NotificationStatus.RECEIVED, - NotificationStatus.CLOSED, - NotificationStatus.CANCELED, - NotificationStatus.ACKNOWLEDGED, - NotificationStatus.ACCEPTED, - NotificationStatus.DECLINED, - NotificationStatus.ACKNOWLEDGED, - ]; - const channel = indexFromId === 2 || indexFromId === 8 ? 'RECEIVER' : 'SENDER'; - const randomNotification = testBuildMockInvestigations([ statusCollection[indexFromId] ], channel)[0]; - - return res(ctx.status(200), ctx.json({ ...randomNotification, id: investigationId })); - }), - rest.post(`*${ environment.apiUrl }/investigations`, (_, res, ctx) => { - return res(ctx.status(200), ctx.json({ id: testInvestigationIdPrefix + 1 })); - }), - - rest.put(`*${ environment.apiUrl }/investigations/:investigationId/status`, async (req, res, ctx) => { - const { investigationId } = req.params; - const { status } = await req.json(); - - const investigation = testGetInvestigationById(investigationId as string); - return res(ctx.status(200), ctx.json({ ...investigation, status })); - }), - ...commonHandler, -]; diff --git a/frontend/src/app/mocks/services/investigations-mock/investigations.model.ts b/frontend/src/app/mocks/services/investigations-mock/investigations.model.ts deleted file mode 100644 index 0353212f3f..0000000000 --- a/frontend/src/app/mocks/services/investigations-mock/investigations.model.ts +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import type { NotificationResponse } from '@shared/model/notification.model'; -import { NotificationStatus, NotificationType, NotificationTypeResponse } from '@shared/model/notification.model'; -import { Severity } from '@shared/model/severity.model'; -import { getRandomAsset } from '../parts-mock/partsAsPlanned/partsAsPlanned.model'; -import { MOCK_part_1 } from '../parts-mock/partsAsPlanned/partsAsPlanned.test.model'; -import { getRandomIntFromInterval, getRandomText } from '../text-generator.helper'; - -export const InvestigationIdPrefix = 'id-'; - -// TODO: rethink this approach -const severities = [Severity.MINOR, Severity.MAJOR, Severity.CRITICAL, Severity.LIFE_THREATENING]; - -export const buildMockInvestigations = ( - statuses: NotificationStatus[], - channel: 'SENDER' | 'RECEIVER', -): NotificationResponse[] => - new Array(101).fill(null).map((_, index) => { - const status = statuses[index % statuses.length]; - const severity = severities[index % severities.length]; - // every 10th alert should have an error - const errorInvestigation = (index + 1) % 10 === 0 ? 'The Services returned an Error while processing this Investigation' : undefined; - - const close = status === NotificationStatus.CLOSED ? getRandomText(getRandomIntFromInterval(15, 500)) : ''; - const isDeclined = Math.random() >= 0.5; - - const decline = - status === NotificationStatus.DECLINED || (!!close && isDeclined) - ? getRandomText(getRandomIntFromInterval(15, 500)) - : ''; - - const accept = - status === NotificationStatus.ACCEPTED || (!!close && !isDeclined) - ? getRandomText(getRandomIntFromInterval(15, 500)) - : ''; - - const numberToString = (i: number) => i.toString().padStart(2, '0'); - const month = getRandomIntFromInterval(1, 12); - const day = getRandomIntFromInterval(1, 27); - const title = 'Title'; - return { - id: `${InvestigationIdPrefix}${index + 1}`, - description: `Investigation No ${index + 1} ${getRandomText(getRandomIntFromInterval(15, 500))}`, - status, - severity, - channel, - createdBy: 'BPN10000000OEM0A', - createdByName: 'OEM xxxxxxxxxxxxxxx A', - title, - sendTo: 'BPN20000000OEM0B', - sendToName: 'OEM xxxxxxxxxxxxxxx B', - reason: {close, decline, accept}, - createdDate: `2022-${numberToString(month)}-${numberToString(day)}T12:34:12`, - targetDate: `2022-${numberToString(month)}-${numberToString(day + 1)}T11:34:12Z`, - assetIds: [MOCK_part_1.id, getRandomAsset().id, getRandomAsset().id, getRandomAsset().id], - errorMessage: errorInvestigation, - type: NotificationTypeResponse.INVESTIGATION, - }; - }); - -const MockEmptyInvestigation: NotificationResponse = { - id: `${InvestigationIdPrefix}000`, - description: `Investigation No 000`, - status: NotificationStatus.CREATED, - severity: Severity.MINOR, - createdBy: 'BPN10000000OEM0A', - createdByName: 'OEM xxxxxxxxxxxxxxx A', - sendTo: 'BPN20000000OEM0B', - sendToName: 'OEM xxxxxxxxxxxxxxx B', - title: 'Title', - reason: {close: '', decline: '', accept: ''}, - createdDate: `2022-05-01T12:34:12`, - targetDate: `2022-02-01T12:34:12`, - assetIds: [getRandomAsset().id], - channel: 'SENDER', - type: NotificationTypeResponse.INVESTIGATION -}; - -export interface NotificationFilter { - notificationIds: string[]; -} - -export const getInvestigationById = (id: string) => { - return [].find(investigation => investigation.id === id) || {...MockEmptyInvestigation, id}; -}; diff --git a/frontend/src/app/mocks/services/investigations-mock/investigations.test.model.ts b/frontend/src/app/mocks/services/investigations-mock/investigations.test.model.ts index 2b04d11253..7f61b98f42 100644 --- a/frontend/src/app/mocks/services/investigations-mock/investigations.test.model.ts +++ b/frontend/src/app/mocks/services/investigations-mock/investigations.test.model.ts @@ -61,23 +61,3 @@ export const buildMockInvestigations = ( }; }); -const MockEmptyInvestigation: NotificationResponse = { - id: `${ InvestigationIdPrefix }000`, - title: '', - description: `Investigation No 000`, - status: NotificationStatus.CREATED, - severity: Severity.MINOR, - createdBy: 'BPN10000000OEM0A', - createdByName: 'OEM xxxxxxxxxxxxxxx A', - sendTo: 'BPN20000000OEM0B', - sendToName: 'OEM xxxxxxxxxxxxxxx B', - reason: { close: '', accept: '', decline: '' }, - createdDate: `2022-05-01T12:34:12`, - assetIds: [ getRandomAsset().id ], - channel: 'SENDER', - type: NotificationTypeResponse.INVESTIGATION, -}; - -export const getInvestigationById = (id: string) => { - return [].find(investigation => investigation.id === id) || { ...MockEmptyInvestigation, id }; -}; diff --git a/frontend/src/app/modules/core/api/http-error.interceptor.ts b/frontend/src/app/modules/core/api/http-error.interceptor.ts index 1e43486265..f110860fad 100644 --- a/frontend/src/app/modules/core/api/http-error.interceptor.ts +++ b/frontend/src/app/modules/core/api/http-error.interceptor.ts @@ -19,15 +19,16 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http'; -import {Observable, throwError} from 'rxjs'; -import {catchError, retry} from 'rxjs/operators'; -import {ToastService} from 'src/app/modules/shared/components/toasts/toast.service'; +import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { catchError, retry } from 'rxjs/operators'; +import { ToastService } from 'src/app/modules/shared/components/toasts/toast.service'; export class HttpErrorInterceptor implements HttpInterceptor { // List of request.url that should not automatically display a toast but are handled custom (Can be extended later by METHOD) - private avoidList = ['/api/alerts', '/api/investigations', '/api/alerts/*/approve', '/api/investigations/*/approve'] + private avoidList = [ '/api/notifications', '/api/notifications/*/approve' ]; + constructor(private readonly toastService: ToastService) { } diff --git a/frontend/src/app/modules/core/known-route.ts b/frontend/src/app/modules/core/known-route.ts index 3ba316dc4c..56f968600f 100644 --- a/frontend/src/app/modules/core/known-route.ts +++ b/frontend/src/app/modules/core/known-route.ts @@ -27,7 +27,7 @@ export const OTHER_PARTS_BASE_ROUTE = 'otherParts'; export const DASHBOARD_BASE_ROUTE = 'dashboard'; export const ADMIN_BASE_ROUTE = 'admin'; export const ABOUT_BASE_ROUTE = 'about'; -export const NOTIFICATION_BASE_ROUTE = 'alerts'; +export const NOTIFICATION_BASE_ROUTE = 'inbox'; export const NO_PERMISSION_BASE_ROUTE = 'no-permissions'; export const NavigableUrls = [ diff --git a/frontend/src/app/modules/core/layout/header/header.component.html b/frontend/src/app/modules/core/layout/header/header.component.html index 798b772874..f5c4660cb4 100644 --- a/frontend/src/app/modules/core/layout/header/header.component.html +++ b/frontend/src/app/modules/core/layout/header/header.component.html @@ -27,7 +27,7 @@ - + diff --git a/frontend/src/app/modules/core/layout/header/header.component.ts b/frontend/src/app/modules/core/layout/header/header.component.ts index e83dc5141c..3b97fcc972 100644 --- a/frontend/src/app/modules/core/layout/header/header.component.ts +++ b/frontend/src/app/modules/core/layout/header/header.component.ts @@ -37,7 +37,7 @@ export class HeaderComponent { about: 'info', parts: 'build', otherParts: 'commute', - alerts: 'inbox', + inbox: 'inbox', admin: 'apps', }; diff --git a/frontend/src/app/modules/page/notifications/core/notifications.facade.ts b/frontend/src/app/modules/page/notifications/core/notifications.facade.ts index 64fdecc81f..b59b9d0494 100644 --- a/frontend/src/app/modules/page/notifications/core/notifications.facade.ts +++ b/frontend/src/app/modules/page/notifications/core/notifications.facade.ts @@ -21,11 +21,15 @@ import { Injectable } from '@angular/core'; import { NotificationsState } from '@page/notifications/core/notifications.state'; import { provideDataObject } from '@page/parts/core/parts.helper'; import { TableHeaderSort } from '@shared/components/table/table.model'; -import { Notification, Notifications, NotificationStatus } from '@shared/model/notification.model'; +import { + Notification, + NotificationDeeplinkFilter, + Notifications, + NotificationStatus, +} from '@shared/model/notification.model'; import { View } from '@shared/model/view.model'; import { NotificationService } from '@shared/service/notification.service'; import { Observable, Subscription } from 'rxjs'; -import { NotificationFilter } from '../../../../mocks/services/investigations-mock/investigations.model'; @Injectable() export class NotificationsFacade { @@ -50,7 +54,7 @@ export class NotificationsFacade { return this.notificationService.getNotificationById(id, false); } - public setReceivedNotifications(page = 0, pageSize = 50, sorting: TableHeaderSort[] = [], filter?: NotificationFilter, fullFilter?: any): void { + public setReceivedNotifications(page = 0, pageSize = 50, sorting: TableHeaderSort[] = [], filter?: NotificationDeeplinkFilter, fullFilter?: any): void { this.notificationReceivedSubscription?.unsubscribe(); this.notificationReceivedSubscription = this.notificationService .getReceived(page, pageSize, sorting, filter, fullFilter, false) @@ -60,7 +64,7 @@ export class NotificationsFacade { }); } - public setQueuedAndRequestedNotifications(page = 0, pageSize = 50, sorting: TableHeaderSort[] = [], filter?: NotificationFilter, fullFilter?: any): void { + public setQueuedAndRequestedNotifications(page = 0, pageSize = 50, sorting: TableHeaderSort[] = [], filter?: NotificationDeeplinkFilter, fullFilter?: any): void { this.notificationQueuedAndRequestedSubscription?.unsubscribe(); this.notificationQueuedAndRequestedSubscription = this.notificationService .getCreated(page, pageSize, sorting, filter, fullFilter, false) diff --git a/frontend/src/app/modules/page/notifications/detail/notification-detail.component.spec.ts b/frontend/src/app/modules/page/notifications/detail/notification-detail.component.spec.ts index d25af35cd8..18f3c54819 100644 --- a/frontend/src/app/modules/page/notifications/detail/notification-detail.component.spec.ts +++ b/frontend/src/app/modules/page/notifications/detail/notification-detail.component.spec.ts @@ -18,13 +18,12 @@ ********************************************************************************/ import { ActivatedRoute } from '@angular/router'; -import { NotificationsModule } from '@page/notifications/notifications.module'; import { NotificationDetailComponent } from '@page/notifications/detail/notification-detail.component'; +import { NotificationsModule } from '@page/notifications/notifications.module'; import { NotificationService } from '@shared/service/notification.service'; -import { fireEvent, screen, waitFor } from '@testing-library/angular'; +import { screen, waitFor } from '@testing-library/angular'; import { renderComponent } from '@tests/test-render.utils'; import { of } from 'rxjs'; -import { MOCK_part_1 } from '../../../../mocks/services/parts-mock/partsAsPlanned/partsAsPlanned.test.model'; describe('NotificationDetailComponent', () => { @@ -48,11 +47,6 @@ describe('NotificationDetailComponent', () => { }); }; - it('should render specific text and additional table for received notification', async () => { - await renderNotificationDetail(); - await waitFor(() => expect(screen.getByText('pageAlert.subHeadline.affectedParts')).toBeInTheDocument()); - await waitFor(() => expect(screen.getByText('pageAlert.subHeadline.supplierParts')).toBeInTheDocument()); - }); it('should render specific text for queued or requested notifications', async () => { await renderNotificationDetail('id-1'); @@ -64,13 +58,4 @@ describe('NotificationDetailComponent', () => { await waitFor(() => expect(screen.getByText('actions.goBack')).toBeInTheDocument()); }); - it('should render copy data to clipboard', async () => { - await renderNotificationDetail('id-1'); - await waitFor(() => expect(screen.getByText('pageAlert.subHeadline.supplierParts')).toBeInTheDocument()); - - const spy = spyOn(navigator.clipboard, 'writeText').and.returnValue(new Promise(null)); - fireEvent.click(await waitFor(() => screen.getByTestId('copy-button--' + MOCK_part_1.id))); - - expect(spy).toHaveBeenCalledWith('NO-341449848714937445621543'); - }); }); diff --git a/frontend/src/app/modules/page/notifications/notifications.routing.ts b/frontend/src/app/modules/page/notifications/notifications.routing.ts index cd63f7dfca..ecda44d696 100644 --- a/frontend/src/app/modules/page/notifications/notifications.routing.ts +++ b/frontend/src/app/modules/page/notifications/notifications.routing.ts @@ -33,7 +33,7 @@ const NOTIFICATIONS_ROUTING: Routes = [ resolve: { i18next: I18NEXT_NAMESPACE_RESOLVER }, }, { - path: ':alertId', + path: ':notificationId', pathMatch: 'full', component: NotificationDetailComponent, data: { i18nextNamespaces: [ 'page.alert' ] }, diff --git a/frontend/src/app/modules/page/notifications/presentation/notifications.component.spec.ts b/frontend/src/app/modules/page/notifications/presentation/notifications.component.spec.ts index 739fa1c6c8..b9e1dd7301 100644 --- a/frontend/src/app/modules/page/notifications/presentation/notifications.component.spec.ts +++ b/frontend/src/app/modules/page/notifications/presentation/notifications.component.spec.ts @@ -44,7 +44,7 @@ describe('NotificationsComponent', () => { fireEvent.click(await waitFor(() => screen.getByTestId('table-menu-button--actions.viewDetails'))); const tabInformation: NotificationTabInformation = { tabIndex: null, pageNumber: undefined }; - expect(spy).toHaveBeenCalledWith([ '/alerts/id-84' ], { queryParams: tabInformation }); + expect(spy).toHaveBeenCalledWith([ '/inbox/id-84' ], { queryParams: tabInformation }); }); it('should call change pagination of received notifications', async () => { diff --git a/frontend/src/app/modules/shared/components/notification-common-modal/notification-common-modal.component.ts b/frontend/src/app/modules/shared/components/notification-common-modal/notification-common-modal.component.ts index 4147ff4136..cbbef22108 100644 --- a/frontend/src/app/modules/shared/components/notification-common-modal/notification-common-modal.component.ts +++ b/frontend/src/app/modules/shared/components/notification-common-modal/notification-common-modal.component.ts @@ -48,9 +48,9 @@ export class NotificationCommonModalComponent { @ViewChild(AcknowledgeNotificationModalComponent) acknowledgeModal: AcknowledgeNotificationModalComponent; @ViewChild(DeclineNotificationModalComponent) declineModal: DeclineNotificationModalComponent; - +// TODO do not delete the facade here. This will lead to a nullpointer exception within the modal call. public constructor( - @Optional() private readonly alertsFacade: NotificationsFacade, + @Optional() private readonly notificationsFacade: NotificationsFacade, ) { } diff --git a/frontend/src/app/modules/shared/helper/filter-helper.ts b/frontend/src/app/modules/shared/helper/filter-helper.ts index 7b3e7ca926..37fe599b5e 100644 --- a/frontend/src/app/modules/shared/helper/filter-helper.ts +++ b/frontend/src/app/modules/shared/helper/filter-helper.ts @@ -23,7 +23,8 @@ import { FilterOperator, getFilterOperatorValue, } from '@page/parts/model/parts.model'; -import { NotificationFilter } from '../../../mocks/services/investigations-mock/investigations.model'; +import { NotificationDeeplinkFilter } from '@shared/model/notification.model'; + export const DATE_FILTER_KEYS = [ 'manufacturingDate', 'functionValidFrom', 'functionValidUntil', 'validityPeriodFrom', 'validityPeriodTo', 'createdDate', 'targetDate', 'creationDate', 'endDate' ]; @@ -145,7 +146,7 @@ export function enrichDeeplinkFilterAndGetUpdatedFilter(filter: any): string[] { let filterList: string[] = []; if (filter?.notificationIds) { - if(Array.isArray(filter.notificationIds)) { + if (Array.isArray(filter.notificationIds)) { filter.notificationIds.forEach(notificationId => { filterList.push('id,EQUAL,' + notificationId + ',OR'); }); @@ -181,7 +182,7 @@ export function toGlobalSearchAssetFilter(formValues: string, isAsBuilt: boolean return filter; } -export function provideFilterListForNotifications( filter?: NotificationFilter, fullFilter?: any): string[] { +export function provideFilterListForNotifications(filter?: NotificationDeeplinkFilter, fullFilter?: any): string[] { let filterList: string[] = []; if (filter && !fullFilter) { @@ -192,8 +193,10 @@ export function provideFilterListForNotifications( filter?: NotificationFilter, let params: HttpParams; params = enrichFilterAndGetUpdatedParams(fullFilter, new HttpParams(), 'AND'); let filterParams = params.getAll('filter'); - if(filterParams){ - filterParams.forEach(filter => {filterList.push(filter)}); + if (filterParams) { + filterParams.forEach(filter => { + filterList.push(filter); + }); } } @@ -204,5 +207,5 @@ export function provideFilterListForNotifications( filter?: NotificationFilter, export function containsAtleastOneFilterEntry(filter: AssetAsBuiltFilter | AssetAsPlannedFilter): boolean { return Object.keys(filter) .filter(key => filter[key].length) - .length > 0 + .length > 0; } diff --git a/frontend/src/app/modules/shared/helper/notification-helper.ts b/frontend/src/app/modules/shared/helper/notification-helper.ts index 27cb703aed..85a1385ce5 100644 --- a/frontend/src/app/modules/shared/helper/notification-helper.ts +++ b/frontend/src/app/modules/shared/helper/notification-helper.ts @@ -18,14 +18,11 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +import { NotificationDeeplinkFilter } from '@shared/model/notification.model'; export interface DeeplinkNotificationFilter { - receivedFilter: DeeplinkAssetNotificationIds, - sentFilter: DeeplinkAssetNotificationIds -} - -export interface DeeplinkAssetNotificationIds { - notificationIds: string[]; + receivedFilter: NotificationDeeplinkFilter, + sentFilter: NotificationDeeplinkFilter } export function createDeeplinkNotificationFilter(params: any): DeeplinkNotificationFilter { diff --git a/frontend/src/app/modules/shared/model/notification.model.ts b/frontend/src/app/modules/shared/model/notification.model.ts index 18c0d1d78a..6c1e571f4a 100644 --- a/frontend/src/app/modules/shared/model/notification.model.ts +++ b/frontend/src/app/modules/shared/model/notification.model.ts @@ -71,7 +71,7 @@ export interface NotificationFilter { targetDate?: string; bpn?: string; errorMessage?: string; - title: NotificationTypeResponse; + title: string; } export enum NotificationType { @@ -130,6 +130,8 @@ export enum NotificationColumn { RECEIVED_INVESTIGATION = 'receivedActiveInvestigations', SENT_INVESTIGATION = 'sentActiveInvestigations' } - +export interface NotificationDeeplinkFilter { + notificationIds: string[]; +} export type NotificationsResponse = PaginationResponse; export type Notifications = Pagination; diff --git a/frontend/src/app/modules/shared/service/notification.service.ts b/frontend/src/app/modules/shared/service/notification.service.ts index 9de4b2edc1..6c76a13b60 100644 --- a/frontend/src/app/modules/shared/service/notification.service.ts +++ b/frontend/src/app/modules/shared/service/notification.service.ts @@ -29,10 +29,10 @@ import { provideFilterListForNotifications } from '@shared/helper/filter-helper' import { Severity } from '@shared/model/severity.model'; import type { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { NotificationFilter } from '../../../mocks/services/investigations-mock/investigations.model'; import { Notification, NotificationCreateResponse, + NotificationDeeplinkFilter, NotificationResponse, Notifications, NotificationsResponse, @@ -40,114 +40,114 @@ import { } from '../model/notification.model'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class NotificationService { - private readonly url = environment.apiUrl; - - constructor(private readonly apiService: ApiService) { - } - - - // TODO: merge functions for created and received notifications - public getCreated(page: number, pageSize: number, sorting: TableHeaderSort[], filter?: NotificationFilter, fullFilter?: any, isInvestigation = true): Observable { - const sort = sorting.length ? sorting.map(array => `${array[0]},${array[1]}`) : ['createdDate,desc']; - const requestUrl = this.notificationUrl() + '/filter'; - const additionalFilters = new Set([...provideFilterListForNotifications(filter, fullFilter), 'channel,EQUAL,SENDER,AND']); - - const body = { - pageAble: { - page: page, - size: pageSize, - sort: [...sort], - }, - searchCriteria: { - filter: [...additionalFilters], - }, - }; - - return this.apiService - .post(requestUrl, body) - .pipe(map(data => NotificationAssembler.assembleNotifications(data))); - } - - public getReceived(page: number, pageSize: number, sorting: TableHeaderSort[], filter?: NotificationFilter, fullFilter?: any, isInvestigation = true): Observable { - const sort = sorting.length ? sorting.map(array => `${array[0]},${array[1]}`) : ['createdDate,desc']; - const requestUrl = this.notificationUrl() + '/filter'; - const additionalFilters = new Set([...provideFilterListForNotifications(filter, fullFilter), 'channel,EQUAL,RECEIVER,AND']); - - const body = { - pageAble: { - page: page, - size: pageSize, - sort: sort, - }, - searchCriteria: { - filter: [...additionalFilters], - }, - }; - - return this.apiService - .post(requestUrl, body) - .pipe(map(data => NotificationAssembler.assembleNotifications(data))); - } - - - public getNotificationById(id: string, isInvestigation = true): Observable { - const requestUrl = this.notificationUrl(); - return this.apiService - .get(`${requestUrl}/${id}`) - .pipe(map(notification => NotificationAssembler.assembleNotification(notification))); - } - - public createNotification(partIds: string[], description: string, severity: Severity, bpn: string, isAsBuilt: boolean, type: string): Observable { - const body = {partIds, description, severity, receiverBpn: bpn, isAsBuilt, type}; - - return this.apiService.post(`${this.url}/notifications`, body).pipe(map(({id}) => id)); - } - - - public closeNotification(id: string, reason: string, isInvestigation = true): Observable { - const requestUrl = this.notificationUrl(); - const body = {reason}; - return this.apiService.post(`${requestUrl}/${id}/close`, body); - } - - public approveNotification(id: string, isInvestigation = true): Observable { - const requestUrl = this.notificationUrl(); - return this.apiService.post(`${requestUrl}/${id}/approve`); - } - - public cancelNotification(id: string, isInvestigation = true): Observable { - const requestUrl = this.notificationUrl(); - return this.apiService.post(`${requestUrl}/${id}/cancel`); - } - - public updateNotification( - id: string, - status: NotificationStatus.ACKNOWLEDGED | NotificationStatus.ACCEPTED | NotificationStatus.DECLINED, - reason = '', isInvestigation = true, - ): Observable { - const requestUrl = this.notificationUrl(); - const body = {reason, status}; - return this.apiService.post(`${requestUrl}/${id}/update`, body); - } - - public getDistinctFilterValues(channel: NotificationChannel, fieldNames: string, startsWith: string, isInvestigation = true) { - const mappedFieldName = PartsAssembler.mapFieldNameToApi(fieldNames); - const requestUrl = this.notificationUrl(); - let params = new HttpParams() - .set('fieldName', mappedFieldName) - .set('startWith', startsWith) - .set('size', 200) - .set('channel', channel); - - return this.apiService - .getBy(`${requestUrl}/distinctFilterValues`, params); - - } - - public notificationUrl(): string { - return this.url + "/notifications"; - } + private readonly url = environment.apiUrl; + + constructor(private readonly apiService: ApiService) { + } + + + // TODO: merge functions for created and received notifications + public getCreated(page: number, pageSize: number, sorting: TableHeaderSort[], filter?: NotificationDeeplinkFilter, fullFilter?: any, isInvestigation = true): Observable { + const sort = sorting.length ? sorting.map(array => `${ array[0] },${ array[1] }`) : [ 'createdDate,desc' ]; + const requestUrl = this.notificationUrl() + '/filter'; + const additionalFilters = new Set([ ...provideFilterListForNotifications(filter, fullFilter), 'channel,EQUAL,SENDER,AND' ]); + + const body = { + pageAble: { + page: page, + size: pageSize, + sort: [ ...sort ], + }, + searchCriteria: { + filter: [ ...additionalFilters ], + }, + }; + + return this.apiService + .post(requestUrl, body) + .pipe(map(data => NotificationAssembler.assembleNotifications(data))); + } + + public getReceived(page: number, pageSize: number, sorting: TableHeaderSort[], filter?: NotificationDeeplinkFilter, fullFilter?: any, isInvestigation = true): Observable { + const sort = sorting.length ? sorting.map(array => `${ array[0] },${ array[1] }`) : [ 'createdDate,desc' ]; + const requestUrl = this.notificationUrl() + '/filter'; + const additionalFilters = new Set([ ...provideFilterListForNotifications(filter, fullFilter), 'channel,EQUAL,RECEIVER,AND' ]); + + const body = { + pageAble: { + page: page, + size: pageSize, + sort: sort, + }, + searchCriteria: { + filter: [ ...additionalFilters ], + }, + }; + + return this.apiService + .post(requestUrl, body) + .pipe(map(data => NotificationAssembler.assembleNotifications(data))); + } + + + public getNotificationById(id: string, isInvestigation = true): Observable { + const requestUrl = this.notificationUrl(); + return this.apiService + .get(`${ requestUrl }/${ id }`) + .pipe(map(notification => NotificationAssembler.assembleNotification(notification))); + } + + public createNotification(partIds: string[], description: string, severity: Severity, bpn: string, isAsBuilt: boolean, type: string): Observable { + const body = { partIds, description, severity, receiverBpn: bpn, isAsBuilt, type }; + + return this.apiService.post(`${ this.url }/notifications`, body).pipe(map(({ id }) => id)); + } + + + public closeNotification(id: string, reason: string, isInvestigation = true): Observable { + const requestUrl = this.notificationUrl(); + const body = { reason }; + return this.apiService.post(`${ requestUrl }/${ id }/close`, body); + } + + public approveNotification(id: string, isInvestigation = true): Observable { + const requestUrl = this.notificationUrl(); + return this.apiService.post(`${ requestUrl }/${ id }/approve`); + } + + public cancelNotification(id: string, isInvestigation = true): Observable { + const requestUrl = this.notificationUrl(); + return this.apiService.post(`${ requestUrl }/${ id }/cancel`); + } + + public updateNotification( + id: string, + status: NotificationStatus.ACKNOWLEDGED | NotificationStatus.ACCEPTED | NotificationStatus.DECLINED, + reason = '', isInvestigation = true, + ): Observable { + const requestUrl = this.notificationUrl(); + const body = { reason, status }; + return this.apiService.post(`${ requestUrl }/${ id }/update`, body); + } + + public getDistinctFilterValues(channel: NotificationChannel, fieldNames: string, startsWith: string, isInvestigation = true) { + const mappedFieldName = PartsAssembler.mapFieldNameToApi(fieldNames); + const requestUrl = this.notificationUrl(); + let params = new HttpParams() + .set('fieldName', mappedFieldName) + .set('startWith', startsWith) + .set('size', 200) + .set('channel', channel); + + return this.apiService + .getBy(`${ requestUrl }/distinctFilterValues`, params); + + } + + public notificationUrl(): string { + return this.url + '/notifications'; + } } diff --git a/frontend/src/tests/mock-test-server.ts b/frontend/src/tests/mock-test-server.ts index 0473fdea84..37e1fa4f16 100644 --- a/frontend/src/tests/mock-test-server.ts +++ b/frontend/src/tests/mock-test-server.ts @@ -24,7 +24,6 @@ import { adminHandler, dashboardHandler, errorHandler, - investigationsTestHandlers, otherPartsAsBuiltHandlers, otherPartsAsBuiltHandlersTest, partsHandlersTest, @@ -37,10 +36,9 @@ const handlers = [ ...otherPartsAsBuiltHandlers, ...otherPartsAsBuiltHandlersTest, ...partsHandlersTest, - ...investigationsTestHandlers, ...alertsTestHandlers, ...adminHandler, ...errorHandler, - ...policyHandler + ...policyHandler, ]; export const worker = setupWorker(...handlers); diff --git a/frontend/src/tests/test-render.utils.ts b/frontend/src/tests/test-render.utils.ts index b10297a3e7..72b8d2f99a 100644 --- a/frontend/src/tests/test-render.utils.ts +++ b/frontend/src/tests/test-render.utils.ts @@ -20,10 +20,9 @@ ********************************************************************************/ import { HttpClientModule } from '@angular/common/http'; -import { APP_INITIALIZER, LOCALE_ID, Type, ɵɵComponentDeclaration, ɵɵFactoryDeclaration } from '@angular/core'; +import { APP_INITIALIZER, Type, ɵɵComponentDeclaration, ɵɵFactoryDeclaration } from '@angular/core'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { MockedKeycloakService } from '@core/auth/mocked-keycloak.service'; -import { localeIdFactory } from '@core/i18n/global-i18n.providers'; import { Role } from '@core/user/role.model'; import { SharedModule } from '@shared/shared.module'; import { TemplateModule } from '@shared/template.module'; @@ -31,7 +30,6 @@ import { render, RenderComponentOptions, RenderResult, RenderTemplateOptions, wa import { Screen } from '@testing-library/dom'; import { I18NEXT_SERVICE, I18NextModule, ITranslationService } from 'angular-i18next'; import { KeycloakService } from 'keycloak-angular'; -import { node } from 'webpack'; type RenderFnOptionsExtension = { translations?: string[];