From d3f51ec88fa5f040b0d7390b3fbd20fefb411670 Mon Sep 17 00:00:00 2001 From: Piotr Gawron <p.gawron@atcomp.pl> Date: Fri, 17 May 2024 11:48:01 +0200 Subject: [PATCH] show error dialog in error handling middleware --- .../EditOverlayModal.component.test.tsx | 7 ++ .../hooks/useEditOverlay.test.ts | 5 ++ .../ErroReportModal.component.tsx | 64 +++++++++++++++++++ .../Modal/ErrorReportModal/index.ts | 1 + .../FunctionalArea/Modal/Modal.component.tsx | 6 ++ .../utils/getBasePublications.test.ts | 12 ++-- .../utils/getBasePublications.ts | 14 ++-- .../utils/fetchElementData.ts | 18 ++++-- .../LoadPluginFromUrl.component.test.tsx | 14 ++-- .../hooks/useLoadPluginFromUrl.ts | 11 ++-- .../Map/MapViewer/utils/useOlMap.ts | 6 ++ .../middlewares/error.middleware.test.ts | 63 +++++++++++------- src/redux/middlewares/error.middleware.ts | 14 ++-- src/redux/modal/modal.constants.ts | 1 + src/redux/modal/modal.mock.ts | 1 + src/redux/modal/modal.reducers.ts | 13 ++++ src/redux/modal/modal.slice.ts | 3 + src/redux/modal/modal.types.ts | 6 ++ src/types/modal.ts | 1 + src/utils/error-report/errorReporting.ts | 17 ++--- .../getErrorMessage.constants.ts | 1 + 21 files changed, 214 insertions(+), 64 deletions(-) create mode 100644 src/components/FunctionalArea/Modal/ErrorReportModal/ErroReportModal.component.tsx create mode 100644 src/components/FunctionalArea/Modal/ErrorReportModal/index.ts diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx index 6bda59af..b8015a4b 100644 --- a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx +++ b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx @@ -46,6 +46,7 @@ describe('EditOverlayModal - component', () => { editOverlayState: overlayFixture, molArtState: {}, overviewImagesState: {}, + errorReportState: {}, }, }); @@ -63,6 +64,7 @@ describe('EditOverlayModal - component', () => { editOverlayState: overlayFixture, molArtState: {}, overviewImagesState: {}, + errorReportState: {}, }, }); @@ -92,6 +94,7 @@ describe('EditOverlayModal - component', () => { editOverlayState: overlayFixture, molArtState: {}, overviewImagesState: {}, + errorReportState: {}, }, overlays: OVERLAYS_INITIAL_STATE_MOCK, }); @@ -126,6 +129,7 @@ describe('EditOverlayModal - component', () => { editOverlayState: overlayFixture, molArtState: {}, overviewImagesState: {}, + errorReportState: {}, }, overlays: OVERLAYS_INITIAL_STATE_MOCK, }); @@ -161,6 +165,7 @@ describe('EditOverlayModal - component', () => { editOverlayState: overlayFixture, molArtState: {}, overviewImagesState: {}, + errorReportState: {}, }, overlays: OVERLAYS_INITIAL_STATE_MOCK, }); @@ -195,6 +200,7 @@ describe('EditOverlayModal - component', () => { editOverlayState: overlayFixture, molArtState: {}, overviewImagesState: {}, + errorReportState: {}, }, overlays: OVERLAYS_INITIAL_STATE_MOCK, }); @@ -223,6 +229,7 @@ describe('EditOverlayModal - component', () => { editOverlayState: overlayFixture, molArtState: {}, overviewImagesState: {}, + errorReportState: {}, }, }); diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts index 3d4c31f1..172a1026 100644 --- a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts +++ b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts @@ -23,6 +23,7 @@ describe('useEditOverlay', () => { editOverlayState: overlayFixture, molArtState: {}, overviewImagesState: {}, + errorReportState: {}, }, }); @@ -58,6 +59,7 @@ describe('useEditOverlay', () => { editOverlayState: overlayFixture, molArtState: {}, overviewImagesState: {}, + errorReportState: {}, }, }); @@ -96,6 +98,7 @@ describe('useEditOverlay', () => { editOverlayState: overlayFixture, molArtState: {}, overviewImagesState: {}, + errorReportState: {}, }, }); @@ -130,6 +133,7 @@ describe('useEditOverlay', () => { editOverlayState: overlayFixture, molArtState: {}, overviewImagesState: {}, + errorReportState: {}, }, }); @@ -165,6 +169,7 @@ describe('useEditOverlay', () => { editOverlayState: overlayFixture, molArtState: {}, overviewImagesState: {}, + errorReportState: {}, }, }); diff --git a/src/components/FunctionalArea/Modal/ErrorReportModal/ErroReportModal.component.tsx b/src/components/FunctionalArea/Modal/ErrorReportModal/ErroReportModal.component.tsx new file mode 100644 index 00000000..819ac4b3 --- /dev/null +++ b/src/components/FunctionalArea/Modal/ErrorReportModal/ErroReportModal.component.tsx @@ -0,0 +1,64 @@ +import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; +import { useAppSelector } from '@/redux/hooks/useAppSelector'; +import { getAllUserOverlaysByCreator } from '@/redux/overlays/overlays.thunks'; +import { loadingUserSelector } from '@/redux/user/user.selectors'; +import { login } from '@/redux/user/user.thunks'; +import { Button } from '@/shared/Button'; +import { Input } from '@/shared/Input'; +import React from 'react'; + +export const ErrorReportModal: React.FC = () => { + const dispatch = useAppDispatch(); + const loadingUser = useAppSelector(loadingUserSelector); + const isPending = loadingUser === 'pending'; + const [credentials, setCredentials] = React.useState({ login: '', password: '' }); + + const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => { + const { name, value } = e.target; + setCredentials(prevCredentials => ({ ...prevCredentials, [name]: value })); + }; + + const handleSubmit = async (e: React.FormEvent<HTMLFormElement>): Promise<void> => { + e.preventDefault(); + await dispatch(login(credentials)); + dispatch(getAllUserOverlaysByCreator()); + }; + + return ( + <div className="w-[400px] border border-t-[#E1E0E6] bg-white p-[24px]"> + <form onSubmit={handleSubmit}> + <label className="mb-5 block text-sm font-semibold" htmlFor="login"> + Hi there: + <Input + type="text" + name="login" + id="login" + placeholder="Your login here.." + value={credentials.login} + onChange={handleChange} + className="mt-2.5 text-sm font-medium text-font-400" + /> + </label> + <label className="text-sm font-semibold" htmlFor="password"> + Hmm: + <Input + type="password" + name="password" + id="password" + placeholder="Your password here.." + value={credentials.password} + onChange={handleChange} + className="mt-2.5 text-sm font-medium text-font-400" + /> + </label> + <Button + type="submit" + className="w-full justify-center text-base font-medium" + disabled={isPending} + > + Submit + </Button> + </form> + </div> + ); +}; diff --git a/src/components/FunctionalArea/Modal/ErrorReportModal/index.ts b/src/components/FunctionalArea/Modal/ErrorReportModal/index.ts new file mode 100644 index 00000000..016741c0 --- /dev/null +++ b/src/components/FunctionalArea/Modal/ErrorReportModal/index.ts @@ -0,0 +1 @@ +export { ErrorReportModal } from './ErroReportModal.component'; diff --git a/src/components/FunctionalArea/Modal/Modal.component.tsx b/src/components/FunctionalArea/Modal/Modal.component.tsx index 3fb3fb37..d913abb8 100644 --- a/src/components/FunctionalArea/Modal/Modal.component.tsx +++ b/src/components/FunctionalArea/Modal/Modal.component.tsx @@ -3,6 +3,7 @@ import { modalSelector } from '@/redux/modal/modal.selector'; import dynamic from 'next/dynamic'; import { EditOverlayModal } from './EditOverlayModal'; import { LoginModal } from './LoginModal'; +import { ErrorReportModal } from './ErrorReportModal'; import { ModalLayout } from './ModalLayout'; import { OverviewImagesModal } from './OverviewImagesModal'; import { PublicationsModal } from './PublicationsModal'; @@ -33,6 +34,11 @@ export const Modal = (): React.ReactNode => { <LoginModal /> </ModalLayout> )} + {isOpen && modalName === 'error-report' && ( + <ModalLayout> + <ErrorReportModal /> + </ModalLayout> + )} {isOpen && modalName === 'publications' && <PublicationsModal />} {isOpen && modalName === 'edit-overlay' && ( <ModalLayout> diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.test.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.test.ts index e29e23ec..ffa8191a 100644 --- a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.test.ts +++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.test.ts @@ -1,13 +1,13 @@ import { apiPath } from '@/redux/apiPath'; import { mockNetworkResponse } from '@/utils/mockNetworkResponse'; import { HttpStatusCode } from 'axios'; -import { handleError } from '@/utils/error-report/errorReporting'; +import { showToast } from '@/utils/showToast'; import { PUBLICATIONS_DEFAULT_SEARCH_FIRST_10_MOCK } from '../../../../../../models/mocks/publicationsResponseMock'; import { getBasePublications } from './getBasePublications'; const mockedAxiosClient = mockNetworkResponse(); -jest.mock('./../../../../../../utils/error-report/errorReporting'); +jest.mock('./../../../../../../utils/showToast'); describe('getBasePublications - util', () => { const length = 10; @@ -32,7 +32,7 @@ describe('getBasePublications - util', () => { expect(result).toStrictEqual([]); }); - it('should return empty array and handle error if http error', async () => { + it('should return empty array and show toast error if http error', async () => { mockedAxiosClient .onGet(apiPath.getPublications({ params: { length } })) .reply(HttpStatusCode.BadRequest); @@ -40,6 +40,10 @@ describe('getBasePublications - util', () => { const result = await getBasePublications({ length }); expect(result).toStrictEqual([]); - expect(handleError).toHaveBeenCalled(); + expect(showToast).toHaveBeenCalledWith({ + message: + "Problem with fetching publications: The server couldn't understand your request. Please check your input and try again.", + type: 'error', + }); }); }); diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.ts b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.ts index a927bb14..d149d031 100644 --- a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.ts +++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsModalLayout/utils/getBasePublications.ts @@ -4,9 +4,8 @@ import { PUBLICATIONS_FETCHING_ERROR_PREFIX } from '@/redux/publications/publica import { axiosInstance } from '@/services/api/utils/axiosInstance'; import { Publication, PublicationsResponse } from '@/types/models'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; -import { getError } from '@/utils/error-report/getError'; -import { handleError } from '@/utils/error-report/errorReporting'; -import { store } from '@/redux/store'; +import { getErrorMessage } from '@/utils/getErrorMessage'; +import { showToast } from '@/utils/showToast'; interface Args { length: number; @@ -22,10 +21,11 @@ export const getBasePublications = async ({ length }: Args): Promise<Publication return isDataValid ? response.data.data : []; } catch (error) { - await handleError( - getError({ error, prefix: PUBLICATIONS_FETCHING_ERROR_PREFIX }), - store.getState(), - ); + const errorMessage = getErrorMessage({ error, prefix: PUBLICATIONS_FETCHING_ERROR_PREFIX }); + showToast({ + type: 'error', + message: errorMessage, + }); return []; } }; diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/utils/fetchElementData.ts b/src/components/FunctionalArea/Modal/PublicationsModal/utils/fetchElementData.ts index 3df91509..d7b14ea9 100644 --- a/src/components/FunctionalArea/Modal/PublicationsModal/utils/fetchElementData.ts +++ b/src/components/FunctionalArea/Modal/PublicationsModal/utils/fetchElementData.ts @@ -5,9 +5,8 @@ import { BIO_ENTITY_FETCHING_ERROR_PREFIX } from '@/redux/bioEntity/bioEntity.co import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance'; import { BioEntityContent, BioEntityResponse } from '@/types/models'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; -import { getError } from '@/utils/error-report/getError'; -import { handleError } from '@/utils/error-report/errorReporting'; -import { store } from '@/redux/store'; +import { showToast } from '@/utils/showToast'; +import { getErrorMessage } from '@/utils/getErrorMessage'; export const fetchElementData = async ( searchQuery: string, @@ -26,10 +25,15 @@ export const fetchElementData = async ( return response.data.content[FIRST_ARRAY_ELEMENT]; } } catch (error) { - await handleError( - getError({ error, prefix: BIO_ENTITY_FETCHING_ERROR_PREFIX }), - store.getState(), - ); + const errorMessage = getErrorMessage({ + error, + prefix: BIO_ENTITY_FETCHING_ERROR_PREFIX, + }); + + showToast({ + type: 'error', + message: errorMessage, + }); } return undefined; diff --git a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx index c5c47d94..162b4545 100644 --- a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx +++ b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx @@ -9,13 +9,13 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import axios, { HttpStatusCode } from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { act } from 'react-dom/test-utils'; -import { handleError } from '@/utils/error-report/errorReporting'; +import { showToast } from '@/utils/showToast'; import { LoadPluginFromUrl } from './LoadPluginFromUrl.component'; const mockedAxiosApiClient = mockNetworkResponse(); const mockedAxiosClient = new MockAdapter(axios); -jest.mock('../../../../../utils/error-report/errorReporting'); +jest.mock('../../../../../utils/showToast'); const renderComponent = (initialStore?: InitialStoreState): { store: StoreType } => { const { Wrapper, store } = getReduxWrapperWithStore(initialStore); @@ -116,13 +116,14 @@ describe('LoadPluginFromUrl - component', () => { const button = screen.getByTestId('load-plugin-button'); expect(button).toBeDisabled(); }); - it('should handle error if plugin failed to load', async () => { + it('should show toast if plugin failed to load', async () => { const pluginUrl = 'http://example.com/plugin.js'; mockedAxiosClient.onGet(pluginUrl).reply(HttpStatusCode.Unauthorized, null); global.URL.canParse = jest.fn().mockReturnValue(true); renderComponent(); + const input = screen.getByTestId('load-plugin-input-url'); expect(input).toBeVisible(); @@ -137,7 +138,12 @@ describe('LoadPluginFromUrl - component', () => { }); await waitFor(() => { - expect(handleError).toHaveBeenCalled(); + expect(showToast).toHaveBeenCalled(); + expect(showToast).toHaveBeenCalledWith({ + message: + "Failed to load plugin: You're not authorized to access this resource. Please log in or check your credentials.", + type: 'error', + }); }); }); diff --git a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts index 23f6c19e..5616dca8 100644 --- a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts +++ b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts @@ -2,9 +2,8 @@ import { PluginsManager } from '@/services/pluginsManager'; import axios from 'axios'; import { ChangeEvent, useMemo, useState, KeyboardEvent } from 'react'; import { ENTER_KEY_CODE } from '@/constants/common'; -import { getError } from '@/utils/error-report/getError'; -import { handleError } from '@/utils/error-report/errorReporting'; -import { store } from '@/redux/store'; +import { showToast } from '@/utils/showToast'; +import { getErrorMessage } from '@/utils/getErrorMessage'; import { PLUGIN_LOADING_ERROR_PREFIX } from '../../AvailablePluginsDrawer.constants'; type UseLoadPluginReturnType = { @@ -44,7 +43,11 @@ export const useLoadPluginFromUrl = (): UseLoadPluginReturnType => { setPluginUrl(''); } catch (error) { - await handleError(getError({ error, prefix: PLUGIN_LOADING_ERROR_PREFIX }), store.getState()); + const errorMessage = getErrorMessage({ error, prefix: PLUGIN_LOADING_ERROR_PREFIX }); + showToast({ + type: 'error', + message: errorMessage, + }); } finally { setIsLoading(false); } diff --git a/src/components/Map/MapViewer/utils/useOlMap.ts b/src/components/Map/MapViewer/utils/useOlMap.ts index 49ec3002..4214f1fd 100644 --- a/src/components/Map/MapViewer/utils/useOlMap.ts +++ b/src/components/Map/MapViewer/utils/useOlMap.ts @@ -20,7 +20,13 @@ type UseOlMap = (input?: UseOlMapInput) => UseOlMapOutput; export const useOlMap: UseOlMap = ({ target } = {}) => { const mapRef = React.useRef<null | HTMLDivElement>(null); const { mapInstance, handleSetMapInstance } = useMapInstance(); + // eslint-disable-next-line no-console + console.log('!!!!!!!!!!'); + // eslint-disable-next-line no-console + console.log(mapInstance); const view = useOlMapView({ mapInstance }); + // eslint-disable-next-line no-console + console.log(view); useOlMapLayers({ mapInstance }); useOlMapListeners({ view, mapInstance }); diff --git a/src/redux/middlewares/error.middleware.test.ts b/src/redux/middlewares/error.middleware.test.ts index 76362550..21bc72d7 100644 --- a/src/redux/middlewares/error.middleware.test.ts +++ b/src/redux/middlewares/error.middleware.test.ts @@ -1,13 +1,8 @@ -import { handleError } from '@/utils/error-report/errorReporting'; import { store } from '@/redux/store'; import { errorMiddlewareListener } from './error.middleware'; -jest.mock('../../utils/error-report/errorReporting', () => ({ - handleError: jest.fn(), -})); - describe('errorMiddlewareListener', () => { - // eslint-disable-next-line no-console + const dispatchSpy = jest.spyOn(store, 'dispatch'); beforeEach(() => { jest.clearAllMocks(); @@ -26,14 +21,19 @@ describe('errorMiddlewareListener', () => { code: 'Error 2', }, }; - const { getState } = store; + const { getState, dispatch } = store; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - await errorMiddlewareListener(action, { getState }); - expect(handleError).toHaveBeenCalledWith({ code: 'Error 2' }, getState()); + await errorMiddlewareListener(action, { getState, dispatch }); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'modal/openErrorReportModal', + payload: expect.anything(), + }), + ); }); - it('should handle error without message when action is rejected without value', async () => { + it('should handle error without message when action is rejected', async () => { const action = { type: 'action/rejected', payload: null, @@ -46,11 +46,16 @@ describe('errorMiddlewareListener', () => { code: 'Error 3', }, }; - const { getState } = store; + const { getState, dispatch } = store; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - await errorMiddlewareListener(action, { getState }); - expect(handleError).toHaveBeenCalledWith({ code: 'Error 3' }, getState()); + await errorMiddlewareListener(action, { getState, dispatch }); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'modal/openErrorReportModal', + payload: expect.anything(), + }), + ); }); it('should not handle error when action is not rejected', async () => { @@ -62,11 +67,11 @@ describe('errorMiddlewareListener', () => { requestStatus: 'fulfilled', }, }; - const { getState } = store; + const { getState, dispatch } = store; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - await errorMiddlewareListener(action, { getState }); - expect(handleError).not.toHaveBeenCalled(); + await errorMiddlewareListener(action, { getState, dispatch }); + expect(dispatchSpy).not.toHaveBeenCalled(); }); it('should handle error with unknown error message when action payload is not a string', async () => { @@ -83,13 +88,15 @@ describe('errorMiddlewareListener', () => { message: 'Error message', }, }; - const { getState } = store; + const { getState, dispatch } = store; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - await errorMiddlewareListener(action, { getState }); - expect(handleError).toHaveBeenCalledWith( - { code: 'ERROR', message: 'Error message' }, - getState(), + await errorMiddlewareListener(action, { getState, dispatch }); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'modal/openErrorReportModal', + payload: expect.anything(), + }), ); }); @@ -105,12 +112,20 @@ describe('errorMiddlewareListener', () => { error: { code: 'ERROR', message: 'xyz', + stack: 'stack', }, }; - const { getState } = store; + const { getState, dispatch } = store; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - await errorMiddlewareListener(action, { getState }); - expect(handleError).toHaveBeenCalledWith({ code: 'ERROR', message: 'xyz' }, getState()); + await errorMiddlewareListener(action, { getState, dispatch }); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'modal/openErrorReportModal', + payload: expect.objectContaining({ + stacktrace: 'stack', + }), + }), + ); }); }); diff --git a/src/redux/middlewares/error.middleware.ts b/src/redux/middlewares/error.middleware.ts index 3466c17c..e9f21c3b 100644 --- a/src/redux/middlewares/error.middleware.ts +++ b/src/redux/middlewares/error.middleware.ts @@ -1,6 +1,7 @@ import type { AppListenerEffectAPI, AppStartListening } from '@/redux/store'; import { Action, createListenerMiddleware, isRejected } from '@reduxjs/toolkit'; -import { handleError } from '@/utils/error-report/errorReporting'; +import { createErrorData } from '@/utils/error-report/errorReporting'; +import { openErrorReportModal } from '@/redux/modal/modal.slice'; export const errorListenerMiddleware = createListenerMiddleware(); @@ -8,10 +9,15 @@ const startListening = errorListenerMiddleware.startListening as AppStartListeni export const errorMiddlewareListener = async ( action: Action, - { getState }: AppListenerEffectAPI, + { getState, dispatch }: AppListenerEffectAPI, ): Promise<void> => { - if (isRejected(action)) { - await handleError(action.error, getState()); + if (isRejected(action) && action.type !== 'user/getSessionValid/rejected') { + // eslint-disable-next-line no-console + console.log(action); + const errorData = await createErrorData(action.error, getState()); + // eslint-disable-next-line no-console + console.log(errorData); + dispatch(openErrorReportModal(errorData)); } }; diff --git a/src/redux/modal/modal.constants.ts b/src/redux/modal/modal.constants.ts index f0df9964..d7dea45c 100644 --- a/src/redux/modal/modal.constants.ts +++ b/src/redux/modal/modal.constants.ts @@ -12,4 +12,5 @@ export const MODAL_INITIAL_STATE: ModalState = { uniprotId: MOL_ART_UNIPROT_ID_DEFAULT, }, editOverlayState: null, + errorReportState: {}, }; diff --git a/src/redux/modal/modal.mock.ts b/src/redux/modal/modal.mock.ts index 22b83303..cde5fab5 100644 --- a/src/redux/modal/modal.mock.ts +++ b/src/redux/modal/modal.mock.ts @@ -12,4 +12,5 @@ export const MODAL_INITIAL_STATE_MOCK: ModalState = { uniprotId: MOL_ART_UNIPROT_ID_DEFAULT, }, editOverlayState: null, + errorReportState: {}, }; diff --git a/src/redux/modal/modal.reducers.ts b/src/redux/modal/modal.reducers.ts index 4871a245..b32a6c4d 100644 --- a/src/redux/modal/modal.reducers.ts +++ b/src/redux/modal/modal.reducers.ts @@ -1,5 +1,6 @@ import { ModalName } from '@/types/modal'; import { PayloadAction } from '@reduxjs/toolkit'; +import { ErrorData } from '@/utils/error-report/ErrorData'; import { ModalState, OpenEditOverlayModalAction } from './modal.types'; export const openModalReducer = (state: ModalState, action: PayloadAction<ModalName>): void => { @@ -48,6 +49,18 @@ export const openLoggedInMenuModalReducer = (state: ModalState): void => { state.modalTitle = 'Select'; }; +export const openErrorReportModalReducer = ( + state: ModalState, + action: PayloadAction<ErrorData | undefined>, +): void => { + state.isOpen = true; + state.modalName = 'error-report'; + state.modalTitle = 'Error Report'; + state.errorReportState = { + errorData: action.payload, + }; +}; + export const setOverviewImageIdReducer = ( state: ModalState, action: PayloadAction<number>, diff --git a/src/redux/modal/modal.slice.ts b/src/redux/modal/modal.slice.ts index c1fe7b36..40f7ed65 100644 --- a/src/redux/modal/modal.slice.ts +++ b/src/redux/modal/modal.slice.ts @@ -10,6 +10,7 @@ import { openPublicationsModalReducer, openEditOverlayModalReducer, openLoggedInMenuModalReducer, + openErrorReportModalReducer, } from './modal.reducers'; const modalSlice = createSlice({ @@ -25,6 +26,7 @@ const modalSlice = createSlice({ openPublicationsModal: openPublicationsModalReducer, openEditOverlayModal: openEditOverlayModalReducer, openLoggedInMenuModal: openLoggedInMenuModalReducer, + openErrorReportModal: openErrorReportModalReducer, }, }); @@ -38,6 +40,7 @@ export const { openPublicationsModal, openEditOverlayModal, openLoggedInMenuModal, + openErrorReportModal, } = modalSlice.actions; export default modalSlice.reducer; diff --git a/src/redux/modal/modal.types.ts b/src/redux/modal/modal.types.ts index dfb5ca5d..ea772096 100644 --- a/src/redux/modal/modal.types.ts +++ b/src/redux/modal/modal.types.ts @@ -1,6 +1,7 @@ import { ModalName } from '@/types/modal'; import { MapOverlay } from '@/types/models'; import { PayloadAction } from '@reduxjs/toolkit'; +import { ErrorData } from '@/utils/error-report/ErrorData'; export type OverviewImagesModalState = { imageId?: number; @@ -10,6 +11,10 @@ export type MolArtModalState = { uniprotId?: string | undefined; }; +export type ErrorRepostState = { + errorData?: ErrorData | undefined; +}; + export type EditOverlayState = MapOverlay | null; export interface ModalState { @@ -18,6 +23,7 @@ export interface ModalState { modalTitle: string; overviewImagesState: OverviewImagesModalState; molArtState: MolArtModalState; + errorReportState: ErrorRepostState; editOverlayState: EditOverlayState; } diff --git a/src/types/modal.ts b/src/types/modal.ts index b268bf17..865adda9 100644 --- a/src/types/modal.ts +++ b/src/types/modal.ts @@ -5,4 +5,5 @@ export type ModalName = | 'login' | 'publications' | 'edit-overlay' + | 'error-report' | 'logged-in-menu'; diff --git a/src/utils/error-report/errorReporting.ts b/src/utils/error-report/errorReporting.ts index 40488af9..e457bc5c 100644 --- a/src/utils/error-report/errorReporting.ts +++ b/src/utils/error-report/errorReporting.ts @@ -2,6 +2,7 @@ import { ErrorData } from '@/utils/error-report/ErrorData'; import { SerializedError } from '@reduxjs/toolkit'; import { ONE_THOUSAND } from '@/constants/common'; import { + GENERIC_AXIOS_ERROR_CODE, UNKNOWN_AXIOS_ERROR_CODE, UNKNOWN_ERROR, } from '@/utils/getErrorMessage/getErrorMessage.constants'; @@ -41,7 +42,12 @@ export const createErrorData = async ( let javaStacktrace = null; if (error !== undefined && 'code' in error) { const { code } = error; - if (code && code !== UNKNOWN_ERROR && code !== UNKNOWN_AXIOS_ERROR_CODE) { + if ( + code && + code !== UNKNOWN_ERROR && + code !== UNKNOWN_AXIOS_ERROR_CODE && + code !== GENERIC_AXIOS_ERROR_CODE + ) { try { javaStacktrace = (await axiosInstance.get<JavaStacktrace>(apiPath.getStacktrace(code))).data .content; @@ -64,12 +70,3 @@ export const createErrorData = async ( version, }; }; - -export const handleError = async ( - error: Error | SerializedError | undefined, - state: RootState, -): Promise<void> => { - const errorData = await createErrorData(error, state); - // eslint-disable-next-line no-console - console.log(errorData); -}; diff --git a/src/utils/getErrorMessage/getErrorMessage.constants.ts b/src/utils/getErrorMessage/getErrorMessage.constants.ts index d3cba2ab..406df75f 100644 --- a/src/utils/getErrorMessage/getErrorMessage.constants.ts +++ b/src/utils/getErrorMessage/getErrorMessage.constants.ts @@ -1,5 +1,6 @@ export const UNKNOWN_ERROR = 'An unknown error occurred. Please try again later.'; export const UNKNOWN_AXIOS_ERROR_CODE = 'UNKNOWN_AXIOS_ERROR'; +export const GENERIC_AXIOS_ERROR_CODE = 'ERR_BAD_REQUEST'; export const HTTP_ERROR_MESSAGES = { 400: "The server couldn't understand your request. Please check your input and try again.", -- GitLab