diff --git a/src/components/FunctionalArea/Modal/AccessDeniedModal/AccessDeniedModal.component.tsx b/src/components/FunctionalArea/Modal/AccessDeniedModal/AccessDeniedModal.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3b0b9333593e3549154be0e9d155ca2a1f84ee7b --- /dev/null +++ b/src/components/FunctionalArea/Modal/AccessDeniedModal/AccessDeniedModal.component.tsx @@ -0,0 +1,14 @@ +import Link from 'next/link'; +import React from 'react'; + +export const AccessDeniedModal: React.FC = () => { + return ( + <div className="w-[400px] border border-t-[#E1E0E6] bg-white p-[24px]"> + <div className="mb-10 text-right"> + <Link href="/" className="ml-auto text-xs"> + Contact admin + </Link> + </div> + </div> + ); +}; diff --git a/src/components/FunctionalArea/Modal/Modal.component.tsx b/src/components/FunctionalArea/Modal/Modal.component.tsx index d913abb8ec51661208b50417a1fa090f1f3fcded..44005fcd0cd9fafe6b29b870f0137613faa35e21 100644 --- a/src/components/FunctionalArea/Modal/Modal.component.tsx +++ b/src/components/FunctionalArea/Modal/Modal.component.tsx @@ -1,6 +1,7 @@ import { useAppSelector } from '@/redux/hooks/useAppSelector'; import { modalSelector } from '@/redux/modal/modal.selector'; import dynamic from 'next/dynamic'; +import { AccessDeniedModal } from '@/components/FunctionalArea/Modal/AccessDeniedModal/AccessDeniedModal.component'; import { EditOverlayModal } from './EditOverlayModal'; import { LoginModal } from './LoginModal'; import { ErrorReportModal } from './ErrorReportModal'; @@ -50,6 +51,11 @@ export const Modal = (): React.ReactNode => { <LoggedInMenuModal /> </ModalLayout> )} + {isOpen && modalName === 'access-denied' && ( + <ModalLayout> + <AccessDeniedModal /> + </ModalLayout> + )} </> ); }; diff --git a/src/components/FunctionalArea/Modal/ModalLayout/ModalLayout.component.tsx b/src/components/FunctionalArea/Modal/ModalLayout/ModalLayout.component.tsx index b834d91c802d23bc93587e5c91f93cff6f481832..044fc845bb65d2e80db95ab31efda249ec881750 100644 --- a/src/components/FunctionalArea/Modal/ModalLayout/ModalLayout.component.tsx +++ b/src/components/FunctionalArea/Modal/ModalLayout/ModalLayout.component.tsx @@ -28,6 +28,7 @@ export const ModalLayout = ({ children }: ModalLayoutProps): JSX.Element => { className={twMerge( 'flex h-5/6 w-10/12 flex-col overflow-hidden rounded-lg', modalName === 'login' && 'h-auto w-[400px]', + modalName === 'access-denied' && 'h-auto w-[400px]', modalName === 'error-report' && 'h-auto w-[800px]', ['edit-overlay', 'logged-in-menu'].includes(modalName) && 'h-auto w-[432px]', )} diff --git a/src/redux/middlewares/error.middleware.test.ts b/src/redux/middlewares/error.middleware.test.ts index 6c34711b000f25d6642c79fdf7068a4b16f47913..f8f7de9c01c87bcb615f05192f22ee758845377e 100644 --- a/src/redux/middlewares/error.middleware.test.ts +++ b/src/redux/middlewares/error.middleware.test.ts @@ -1,5 +1,5 @@ import { store } from '@/redux/store'; -import { showToast } from '@/utils/showToast'; + import { errorMiddlewareListener } from './error.middleware'; jest.mock('../../utils/showToast'); @@ -132,7 +132,7 @@ describe('errorMiddlewareListener', () => { ); }); - it('should toast on access denied', async () => { + it('should show modal on access denied', async () => { const action = { type: 'action/rejected', payload: null, @@ -149,9 +149,10 @@ describe('errorMiddlewareListener', () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error await errorMiddlewareListener(action, { getState, dispatch }); - expect(showToast).toHaveBeenCalledWith({ - message: 'Access denied.', - type: 'error', - }); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'modal/openAccessDeniedModal', + }), + ); }); }); diff --git a/src/redux/middlewares/error.middleware.ts b/src/redux/middlewares/error.middleware.ts index 8c6bd8160be1d71fd632d1ec542b66c48c9017fc..0dd789dc975780854f161336e7e901ee08b7ee65 100644 --- a/src/redux/middlewares/error.middleware.ts +++ b/src/redux/middlewares/error.middleware.ts @@ -1,8 +1,7 @@ import type { AppListenerEffectAPI, AppStartListening } from '@/redux/store'; import { Action, createListenerMiddleware, isRejected } from '@reduxjs/toolkit'; import { createErrorData } from '@/utils/error-report/errorReporting'; -import { openErrorReportModal } from '@/redux/modal/modal.slice'; -import { showToast } from '@/utils/showToast'; +import { openAccessDeniedModal, openErrorReportModal } from '@/redux/modal/modal.slice'; export const errorListenerMiddleware = createListenerMiddleware(); @@ -14,10 +13,7 @@ export const errorMiddlewareListener = async ( ): Promise<void> => { if (isRejected(action) && action.type !== 'user/getSessionValid/rejected') { if (action.error.code === '403') { - showToast({ - type: 'error', - message: 'Access denied.', - }); + dispatch(openAccessDeniedModal()); } else { const errorData = await createErrorData(action.error, getState()); dispatch(openErrorReportModal(errorData)); diff --git a/src/redux/modal/modal.reducers.ts b/src/redux/modal/modal.reducers.ts index 24ae505605ac7a2d4ed377f2069d4aa918e6baf9..a93e5fd119e12aac58667c6dc61dca3f442176ad 100644 --- a/src/redux/modal/modal.reducers.ts +++ b/src/redux/modal/modal.reducers.ts @@ -61,6 +61,12 @@ export const openErrorReportModalReducer = ( }; }; +export const openAccessDeniedModalReducer = (state: ModalState): void => { + state.isOpen = true; + state.modalName = 'access-denied'; + state.modalTitle = 'Access denied!'; +}; + 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 40f7ed6566b9b89c6127e67effb65b5daff38b9d..b0b76cf56c66de2674305fb57b1f864de18d7f48 100644 --- a/src/redux/modal/modal.slice.ts +++ b/src/redux/modal/modal.slice.ts @@ -11,6 +11,7 @@ import { openEditOverlayModalReducer, openLoggedInMenuModalReducer, openErrorReportModalReducer, + openAccessDeniedModalReducer, } from './modal.reducers'; const modalSlice = createSlice({ @@ -27,6 +28,7 @@ const modalSlice = createSlice({ openEditOverlayModal: openEditOverlayModalReducer, openLoggedInMenuModal: openLoggedInMenuModalReducer, openErrorReportModal: openErrorReportModalReducer, + openAccessDeniedModal: openAccessDeniedModalReducer, }, }); @@ -41,6 +43,7 @@ export const { openEditOverlayModal, openLoggedInMenuModal, openErrorReportModal, + openAccessDeniedModal, } = modalSlice.actions; export default modalSlice.reducer; diff --git a/src/types/modal.ts b/src/types/modal.ts index 865adda9bd489d0d216fdc7f93abc51b1bf36970..499406998946f45b8b045ee95fae952b05594387 100644 --- a/src/types/modal.ts +++ b/src/types/modal.ts @@ -6,4 +6,5 @@ export type ModalName = | 'publications' | 'edit-overlay' | 'error-report' + | 'access-denied' | 'logged-in-menu';