From 02c814f1b0d82e695bf166e356ba86418c585890 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Wed, 4 Sep 2024 11:31:22 +0200
Subject: [PATCH] include drug and chemical autocomplete

---
 .../TopBar/SearchBar/SearchBar.component.tsx  | 19 ++++++---
 .../TopBar/TopBar.component.test.tsx          |  4 ++
 src/redux/apiPath.ts                          |  2 +
 .../autocomplete/autocomplete.reducers.ts     | 38 +++++++++++++++++-
 .../autocomplete/autocomplete.selectors.ts    | 10 +++++
 src/redux/autocomplete/autocomplete.slice.ts  | 30 +++++++++++++-
 src/redux/autocomplete/autocomplete.thunks.ts | 40 +++++++++++++++++++
 src/redux/root/init.thunks.ts                 |  8 +++-
 src/redux/root/root.fixtures.ts               |  2 +
 src/redux/store.ts                            |  8 +++-
 10 files changed, 150 insertions(+), 11 deletions(-)

diff --git a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx
index b1dda904..fc28ddc3 100644
--- a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx
+++ b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx
@@ -1,4 +1,8 @@
-import { autocompleteSearchSelector } from '@/redux/autocomplete/autocomplete.selectors';
+import {
+  autocompleteChemicalSelector,
+  autocompleteDrugSelector,
+  autocompleteSearchSelector,
+} from '@/redux/autocomplete/autocomplete.selectors';
 import {
   currentSelectedSearchElement,
   searchDrawerOpenSelector,
@@ -37,6 +41,8 @@ export const SearchBar = (): JSX.Element => {
   const isPerfectMatch = useSelector(perfectMatchSelector);
   const searchValueState = useSelector(searchValueSelector);
   const searchAutocompleteState = useSelector(autocompleteSearchSelector);
+  const drugAutocompleteState = useSelector(autocompleteDrugSelector);
+  const chemicalAutocompleteState = useSelector(autocompleteChemicalSelector);
   const dispatch = useAppDispatch();
   const router = useRouter();
   const currentTab = useSelector(currentSelectedSearchElement);
@@ -87,11 +93,12 @@ export const SearchBar = (): JSX.Element => {
     openSearchDrawerIfClosed(currentTab);
   };
 
-  // eslint-disable-next-line no-console
-  console.log(searchAutocompleteState.searchValues);
-  const suggestions = searchAutocompleteState.searchValues.map(entry => {
-    return { name: entry };
-  });
+  const suggestions = searchAutocompleteState.searchValues
+    .concat(drugAutocompleteState.searchValues, chemicalAutocompleteState.searchValues)
+    .map(entry => {
+      return { name: entry };
+    })
+    .sort((a: Suggestion, b: Suggestion) => a.name.localeCompare(b.name));
 
   const getSuggestions = (value: string): Suggestion[] => {
     const inputValue = value.trim().toLowerCase();
diff --git a/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx b/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx
index 83958538..0fb61179 100644
--- a/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx
+++ b/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx
@@ -119,6 +119,8 @@ describe('TopBar - component', () => {
       map: initialMapStateFixture,
       search: SEARCH_STATE_INITIAL_MOCK,
       autocompleteSearch: AUTOCOMPLETE_INITIAL_STATE,
+      autocompleteDrug: AUTOCOMPLETE_INITIAL_STATE,
+      autocompleteChemical: AUTOCOMPLETE_INITIAL_STATE,
       backgrounds: { ...BACKGROUND_INITIAL_STATE_MOCK, data: BACKGROUNDS_MOCK },
     });
 
@@ -136,6 +138,8 @@ describe('TopBar - component', () => {
       user: USER_INITIAL_STATE_MOCK,
       search: SEARCH_STATE_INITIAL_MOCK,
       autocompleteSearch: AUTOCOMPLETE_INITIAL_STATE,
+      autocompleteDrug: AUTOCOMPLETE_INITIAL_STATE,
+      autocompleteChemical: AUTOCOMPLETE_INITIAL_STATE,
       drawer: initialStateFixture,
       project: {
         ...PROJECT_STATE_INITIAL_MOCK,
diff --git a/src/redux/apiPath.ts b/src/redux/apiPath.ts
index b8002874..423aefbf 100644
--- a/src/redux/apiPath.ts
+++ b/src/redux/apiPath.ts
@@ -112,4 +112,6 @@ export const apiPath = {
 
   getSearchAutocomplete: (): string =>
     `projects/${PROJECT_ID}/models/*/bioEntities/suggestedQueryList`,
+  getDrugAutocomplete: (): string => `projects/${PROJECT_ID}/drugs/suggestedQueryList`,
+  getChemicalAutocomplete: (): string => `projects/${PROJECT_ID}/chemicals/suggestedQueryList`,
 };
diff --git a/src/redux/autocomplete/autocomplete.reducers.ts b/src/redux/autocomplete/autocomplete.reducers.ts
index 8fb6ce0e..a36fa9a3 100644
--- a/src/redux/autocomplete/autocomplete.reducers.ts
+++ b/src/redux/autocomplete/autocomplete.reducers.ts
@@ -1,6 +1,10 @@
 import { AutocompleteState } from '@/redux/autocomplete/autocomplete.types';
 import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
-import { getSearchAutocomplete } from '@/redux/autocomplete/autocomplete.thunks';
+import {
+  getChemicalAutocomplete,
+  getDrugAutocomplete,
+  getSearchAutocomplete,
+} from '@/redux/autocomplete/autocomplete.thunks';
 
 export const getSearchAutocompleteReducer = (
   builder: ActionReducerMapBuilder<AutocompleteState>,
@@ -17,3 +21,35 @@ export const getSearchAutocompleteReducer = (
     // TODO: error management to be discussed in the team
   });
 };
+
+export const getDrugAutocompleteReducer = (
+  builder: ActionReducerMapBuilder<AutocompleteState>,
+): void => {
+  builder.addCase(getDrugAutocomplete.pending, state => {
+    state.loading = 'pending';
+  });
+  builder.addCase(getDrugAutocomplete.fulfilled, (state, action) => {
+    state.searchValues = action.payload ? action.payload : [];
+    state.loading = 'succeeded';
+  });
+  builder.addCase(getDrugAutocomplete.rejected, state => {
+    state.loading = 'failed';
+    // TODO: error management to be discussed in the team
+  });
+};
+
+export const getChemicalAutocompleteReducer = (
+  builder: ActionReducerMapBuilder<AutocompleteState>,
+): void => {
+  builder.addCase(getChemicalAutocomplete.pending, state => {
+    state.loading = 'pending';
+  });
+  builder.addCase(getChemicalAutocomplete.fulfilled, (state, action) => {
+    state.searchValues = action.payload ? action.payload : [];
+    state.loading = 'succeeded';
+  });
+  builder.addCase(getChemicalAutocomplete.rejected, state => {
+    state.loading = 'failed';
+    // TODO: error management to be discussed in the team
+  });
+};
diff --git a/src/redux/autocomplete/autocomplete.selectors.ts b/src/redux/autocomplete/autocomplete.selectors.ts
index c6f1f6ae..d1ee002a 100644
--- a/src/redux/autocomplete/autocomplete.selectors.ts
+++ b/src/redux/autocomplete/autocomplete.selectors.ts
@@ -5,3 +5,13 @@ export const autocompleteSearchSelector = createSelector(
   rootSelector,
   state => state.autocompleteSearch,
 );
+
+export const autocompleteDrugSelector = createSelector(
+  rootSelector,
+  state => state.autocompleteDrug,
+);
+
+export const autocompleteChemicalSelector = createSelector(
+  rootSelector,
+  state => state.autocompleteChemical,
+);
diff --git a/src/redux/autocomplete/autocomplete.slice.ts b/src/redux/autocomplete/autocomplete.slice.ts
index ab28dbc6..6d042065 100644
--- a/src/redux/autocomplete/autocomplete.slice.ts
+++ b/src/redux/autocomplete/autocomplete.slice.ts
@@ -1,9 +1,13 @@
 import { createSlice } from '@reduxjs/toolkit';
 import { AUTOCOMPLETE_INITIAL_STATE } from '@/redux/autocomplete/autocomplete.constants';
-import { getSearchAutocompleteReducer } from '@/redux/autocomplete/autocomplete.reducers';
+import {
+  getChemicalAutocompleteReducer,
+  getDrugAutocompleteReducer,
+  getSearchAutocompleteReducer,
+} from '@/redux/autocomplete/autocomplete.reducers';
 
 export const autocompleteSearchSlice = createSlice({
-  name: 'autocomplete',
+  name: 'autocompleteSearch',
   initialState: AUTOCOMPLETE_INITIAL_STATE,
   reducers: {},
   extraReducers(builder) {
@@ -12,3 +16,25 @@ export const autocompleteSearchSlice = createSlice({
 });
 
 export const autocompleteSearchReducer = autocompleteSearchSlice.reducer;
+
+export const autocompleteDrugSlice = createSlice({
+  name: 'autocompleteDrug',
+  initialState: AUTOCOMPLETE_INITIAL_STATE,
+  reducers: {},
+  extraReducers(builder) {
+    getDrugAutocompleteReducer(builder);
+  },
+});
+
+export const autocompleteDrugReducer = autocompleteDrugSlice.reducer;
+
+export const autocompleteChemicalSlice = createSlice({
+  name: 'autocompleteChemical',
+  initialState: AUTOCOMPLETE_INITIAL_STATE,
+  reducers: {},
+  extraReducers(builder) {
+    getChemicalAutocompleteReducer(builder);
+  },
+});
+
+export const autocompleteChemicalReducer = autocompleteChemicalSlice.reducer;
diff --git a/src/redux/autocomplete/autocomplete.thunks.ts b/src/redux/autocomplete/autocomplete.thunks.ts
index 6397ea42..d9204b9e 100644
--- a/src/redux/autocomplete/autocomplete.thunks.ts
+++ b/src/redux/autocomplete/autocomplete.thunks.ts
@@ -26,3 +26,43 @@ export const getSearchAutocomplete = createAsyncThunk<
     }
   },
 );
+
+export const getDrugAutocomplete = createAsyncThunk<
+  string[] | undefined,
+  void,
+  { state: RootState } & ThunkConfig
+>(
+  'project/getDrugAutocomplete',
+  // eslint-disable-next-line consistent-return
+  async () => {
+    try {
+      const response = await axiosInstance.get<string[]>(apiPath.getDrugAutocomplete());
+
+      const isDataValid = validateDataUsingZodSchema(response.data, autocompleteSchema);
+
+      return isDataValid ? response.data : undefined;
+    } catch (error) {
+      return Promise.reject(getError({ error }));
+    }
+  },
+);
+
+export const getChemicalAutocomplete = createAsyncThunk<
+  string[] | undefined,
+  void,
+  { state: RootState } & ThunkConfig
+>(
+  'project/getChemicalAutocomplete',
+  // eslint-disable-next-line consistent-return
+  async () => {
+    try {
+      const response = await axiosInstance.get<string[]>(apiPath.getChemicalAutocomplete());
+
+      const isDataValid = validateDataUsingZodSchema(response.data, autocompleteSchema);
+
+      return isDataValid ? response.data : undefined;
+    } catch (error) {
+      return Promise.reject(getError({ error }));
+    }
+  },
+);
diff --git a/src/redux/root/init.thunks.ts b/src/redux/root/init.thunks.ts
index 476e5b40..9387f79c 100644
--- a/src/redux/root/init.thunks.ts
+++ b/src/redux/root/init.thunks.ts
@@ -7,7 +7,11 @@ import { PluginsManager } from '@/services/pluginsManager';
 import { createAsyncThunk } from '@reduxjs/toolkit';
 import { ZERO } from '@/constants/common';
 import { getConstant } from '@/redux/constant/constant.thunks';
-import { getSearchAutocomplete } from '@/redux/autocomplete/autocomplete.thunks';
+import {
+  getChemicalAutocomplete,
+  getDrugAutocomplete,
+  getSearchAutocomplete,
+} from '@/redux/autocomplete/autocomplete.thunks';
 import { getAllBackgroundsByProjectId } from '../backgrounds/backgrounds.thunks';
 import { getConfiguration, getConfigurationOptions } from '../configuration/configuration.thunks';
 import {
@@ -89,6 +93,8 @@ export const fetchInitialAppData = createAsyncThunk<
 
   // autocomplete
   dispatch(getSearchAutocomplete());
+  dispatch(getDrugAutocomplete());
+  dispatch(getChemicalAutocomplete());
 
   /** Trigger search */
   if (queryData.searchValue) {
diff --git a/src/redux/root/root.fixtures.ts b/src/redux/root/root.fixtures.ts
index 5f116bc5..e260f230 100644
--- a/src/redux/root/root.fixtures.ts
+++ b/src/redux/root/root.fixtures.ts
@@ -32,6 +32,8 @@ import { USER_INITIAL_STATE_MOCK } from '../user/user.mock';
 
 export const INITIAL_STORE_STATE_MOCK: RootState = {
   autocompleteSearch: AUTOCOMPLETE_INITIAL_STATE,
+  autocompleteDrug: AUTOCOMPLETE_INITIAL_STATE,
+  autocompleteChemical: AUTOCOMPLETE_INITIAL_STATE,
   search: SEARCH_STATE_INITIAL_MOCK,
   project: PROJECT_STATE_INITIAL_MOCK,
   projects: PROJECTS_STATE_INITIAL_MOCK,
diff --git a/src/redux/store.ts b/src/redux/store.ts
index a5c2daea..f11f6e57 100644
--- a/src/redux/store.ts
+++ b/src/redux/store.ts
@@ -18,7 +18,11 @@ import projectsReducer from '@/redux/projects/projects.slice';
 import reactionsReducer from '@/redux/reactions/reactions.slice';
 import searchReducer from '@/redux/search/search.slice';
 import userReducer from '@/redux/user/user.slice';
-import { autocompleteSearchReducer } from '@/redux/autocomplete/autocomplete.slice';
+import {
+  autocompleteChemicalReducer,
+  autocompleteDrugReducer,
+  autocompleteSearchReducer,
+} from '@/redux/autocomplete/autocomplete.slice';
 import {
   AnyAction,
   ListenerEffectAPI,
@@ -40,6 +44,8 @@ import statisticsReducer from './statistics/statistics.slice';
 
 export const reducers = {
   autocompleteSearch: autocompleteSearchReducer,
+  autocompleteDrug: autocompleteDrugReducer,
+  autocompleteChemical: autocompleteChemicalReducer,
   search: searchReducer,
   project: projectReducer,
   projects: projectsReducer,
-- 
GitLab