diff --git a/src/components/FunctionalArea/NavBar/NavBar.component.tsx b/src/components/FunctionalArea/NavBar/NavBar.component.tsx index 71785c29a29654461fface67cc8f7ef4e97cf1aa..78b2dffa815a8841c25d28b133cc23c04e404b18 100644 --- a/src/components/FunctionalArea/NavBar/NavBar.component.tsx +++ b/src/components/FunctionalArea/NavBar/NavBar.component.tsx @@ -1,6 +1,6 @@ import logoImg from '@/assets/vectors/branding/logo.svg'; import luxembourgLogoImg from '@/assets/vectors/branding/luxembourg-logo.svg'; -import { API_DOCS_URL, MINERVA_WEBSITE_URL } from '@/constants'; +import { MINERVA_WEBSITE_URL } from '@/constants'; import { openDrawer } from '@/redux/drawer/drawer.slice'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { openLegend } from '@/redux/legend/legend.slice'; @@ -33,12 +33,16 @@ export const NavBar = (): JSX.Element => { const configuration = store.getState().configuration.main.data; const version = configuration ? `(v${configuration.version})` : ''; + const constant = store.getState().constant.main.data; + + const apiDocsUrl = constant ? constant.apiDocsUrl : ''; + return ( <div className="flex min-h-full w-[88px] flex-col items-center justify-between overflow-y-auto bg-cultured py-8"> <div data-testid="nav-buttons"> <div className="mb-8 flex flex-col gap-[10px]"> <IconButton icon="info" onClick={openDrawerInfo} title="Project info" /> - <a href={API_DOCS_URL} target="_blank"> + <a href={apiDocsUrl} target="_blank"> <IconButton icon="page" title="API Doc" /> </a> <IconButton icon="plugin" onClick={openDrawerPlugins} title="Available plugins" /> diff --git a/src/constants/index.ts b/src/constants/index.ts index 45ab179b8518efc24fe3b6e9ff44aa45307f8123..4cfb0b9bcae9f9aee3c0dc0b6e0781c35adc8db9 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -11,6 +11,5 @@ export const ZOD_SEED = parseInt(process.env.ZOD_SEED || '123', 10); export const BIO_ENTITY = 'bioEntity'; export const DRUGS_CHEMICALS = ['drugs', 'chemicals']; export const MINERVA_WEBSITE_URL = 'https://minerva.pages.uni.lu/doc/'; -export const API_DOCS_URL = `${BASE_API_URL}/../docs/`; export const ADMIN_PANEL_URL = getConfigValue('ADMIN_PANEL_URL'); export const CURRENT_PROJECT_ADMIN_PANEL_URL = `${ADMIN_PANEL_URL}?id=${PROJECT_ID}`; diff --git a/src/redux/constant/constant.adapter.ts b/src/redux/constant/constant.adapter.ts new file mode 100644 index 0000000000000000000000000000000000000000..7c754c4a5450d19363c5abf2f71d34450990a981 --- /dev/null +++ b/src/redux/constant/constant.adapter.ts @@ -0,0 +1,19 @@ +import { DEFAULT_ERROR } from '@/constants/errors'; +import { Loading } from '@/types/loadingState'; +import { ConstantMainState } from './constant.types'; + +const REQUEST_INITIAL_STATUS: { loading: Loading; error: Error } = { + loading: 'idle', + error: DEFAULT_ERROR, +}; + +const MAIN_CONSTANT_INITIAL_STATE: ConstantMainState = { + data: undefined, + ...REQUEST_INITIAL_STATUS, +}; + +export const CONSTANT_INITIAL_STATE = { + main: MAIN_CONSTANT_INITIAL_STATE, +}; + +export type ConstantState = typeof CONSTANT_INITIAL_STATE; diff --git a/src/redux/constant/constant.reducers.ts b/src/redux/constant/constant.reducers.ts new file mode 100644 index 0000000000000000000000000000000000000000..2fd30593d3715850bea3d3e9e99c40d0a8722f6b --- /dev/null +++ b/src/redux/constant/constant.reducers.ts @@ -0,0 +1,16 @@ +import { ActionReducerMapBuilder } from '@reduxjs/toolkit'; +import { ConstantState } from './constant.adapter'; +import { getConstant } from './constant.thunks'; + +export const getConstantReducer = (builder: ActionReducerMapBuilder<ConstantState>): void => { + builder.addCase(getConstant.pending, state => { + state.main.loading = 'pending'; + }); + builder.addCase(getConstant.fulfilled, (state, action) => { + state.main.loading = 'succeeded'; + state.main.data = action.payload; + }); + builder.addCase(getConstant.rejected, state => { + state.main.loading = 'failed'; + }); +}; diff --git a/src/redux/constant/constant.slice.ts b/src/redux/constant/constant.slice.ts new file mode 100644 index 0000000000000000000000000000000000000000..7fac32f9d9cd209c2d2e12208e149c1dafe63ed8 --- /dev/null +++ b/src/redux/constant/constant.slice.ts @@ -0,0 +1,14 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { CONSTANT_INITIAL_STATE } from './constant.adapter'; +import { getConstantReducer } from './constant.reducers'; + +export const constantSlice = createSlice({ + name: 'constant', + initialState: CONSTANT_INITIAL_STATE, + reducers: {}, + extraReducers: builder => { + getConstantReducer(builder); + }, +}); + +export default constantSlice.reducer; diff --git a/src/redux/constant/constant.thunks.ts b/src/redux/constant/constant.thunks.ts new file mode 100644 index 0000000000000000000000000000000000000000..c939e0812f6bc36ecc195e51dd4226515b435408 --- /dev/null +++ b/src/redux/constant/constant.thunks.ts @@ -0,0 +1,22 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { getErrorMessage } from '@/utils/getErrorMessage'; +import { ThunkConfig } from '@/types/store'; +import { ConstantType } from '@/redux/constant/constant.types'; +import { getConfigValue } from '@/constants/index.utils'; + +export const getConstant = createAsyncThunk<ConstantType | undefined, void, ThunkConfig>( + 'constant/getConstant', + async (_, { rejectWithValue }) => { + try { + const apiBaseUrl = getConfigValue('BASE_API_URL'); + const result: ConstantType = { + apiBaseUrl, + apiDocsUrl: `${apiBaseUrl}/../docs/`, + }; + return result; + } catch (error) { + const errorMessage = getErrorMessage({ error, prefix: 'Failed to build constants' }); + return rejectWithValue(errorMessage); + } + }, +); diff --git a/src/redux/constant/constant.types.ts b/src/redux/constant/constant.types.ts new file mode 100644 index 0000000000000000000000000000000000000000..3b31843391955944adb5ecc784cfb7389fda2b4c --- /dev/null +++ b/src/redux/constant/constant.types.ts @@ -0,0 +1,8 @@ +import { FetchDataState } from '@/types/fetchDataState'; + +export type ConstantType = { + apiBaseUrl: string; + apiDocsUrl: string; +}; + +export type ConstantMainState = FetchDataState<ConstantType>; diff --git a/src/redux/root/init.thunks.ts b/src/redux/root/init.thunks.ts index 6c0da82dab4a184787865238727c552f772c1193..96d2232686fbbc91de90fb131de01774cfe566d2 100644 --- a/src/redux/root/init.thunks.ts +++ b/src/redux/root/init.thunks.ts @@ -6,6 +6,7 @@ import { getDefaultSearchTab } from '@/components/FunctionalArea/TopBar/SearchBa import { PluginsManager } from '@/services/pluginsManager'; import { createAsyncThunk } from '@reduxjs/toolkit'; import { ZERO } from '@/constants/common'; +import { getConstant } from '@/redux/constant/constant.thunks'; import { getAllBackgroundsByProjectId } from '../backgrounds/backgrounds.thunks'; import { getConfiguration, getConfigurationOptions } from '../configuration/configuration.thunks'; import { @@ -58,6 +59,7 @@ export const fetchInitialAppData = createAsyncThunk< /** Fetch all data required for rendering map */ await Promise.all([ + dispatch(getConstant()), dispatch(getConfiguration()), dispatch(getConfigurationOptions()), dispatch(getProjectById(PROJECT_ID)), diff --git a/src/redux/root/root.fixtures.ts b/src/redux/root/root.fixtures.ts index 5ff21f0b80bfa11ef48b15914aff4304615bb16f..feaaaedffe924b2b295a0e3d2d9590e85e137df0 100644 --- a/src/redux/root/root.fixtures.ts +++ b/src/redux/root/root.fixtures.ts @@ -1,3 +1,4 @@ +import { CONSTANT_INITIAL_STATE } from '@/redux/constant/constant.adapter'; import { BACKGROUND_INITIAL_STATE_MOCK } from '../backgrounds/background.mock'; import { BIOENTITY_INITIAL_STATE_MOCK } from '../bioEntity/bioEntity.mock'; import { CHEMICALS_INITIAL_STATE_MOCK } from '../chemicals/chemicals.mock'; @@ -38,6 +39,7 @@ export const INITIAL_STORE_STATE_MOCK: RootState = { overlays: OVERLAYS_INITIAL_STATE_MOCK, reactions: REACTIONS_STATE_INITIAL_MOCK, configuration: CONFIGURATION_INITIAL_STATE, + constant: CONSTANT_INITIAL_STATE, overlayBioEntity: OVERLAY_BIO_ENTITY_INITIAL_STATE_MOCK, modal: MODAL_INITIAL_STATE_MOCK, contextMenu: CONTEXT_MENU_INITIAL_STATE, diff --git a/src/redux/store.ts b/src/redux/store.ts index 82042d987bf7638f1d8ffe8cc7f33610475c07bf..891c7cfec500361e496e7d2d53b534f4a79f1bfe 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -2,6 +2,7 @@ import backgroundsReducer from '@/redux/backgrounds/backgrounds.slice'; import bioEntityReducer from '@/redux/bioEntity/bioEntity.slice'; import chemicalsReducer from '@/redux/chemicals/chemicals.slice'; import configurationReducer from '@/redux/configuration/configuration.slice'; +import constantReducer from '@/redux/constant/constant.slice'; import contextMenuReducer from '@/redux/contextMenu/contextMenu.slice'; import cookieBannerReducer from '@/redux/cookieBanner/cookieBanner.slice'; import drawerReducer from '@/redux/drawer/drawer.slice'; @@ -50,6 +51,7 @@ export const reducers = { cookieBanner: cookieBannerReducer, user: userReducer, configuration: configurationReducer, + constant: constantReducer, overlayBioEntity: overlayBioEntityReducer, legend: legendReducer, statistics: statisticsReducer,