Skip to content
Snippets Groups Projects
Commit a95af58c authored by Adrian Orłów's avatar Adrian Orłów
Browse files

feat: add PoC pins numbers

parent 6824a72e
No related branches found
No related tags found
2 merge requests!223reset the pin numbers before search results are fetch (so the results will be...,!155feat: Make pins numbers unique for search results (MIN-266)
Pipeline #87417 passed
Showing
with 8160 additions and 36 deletions
......@@ -33,6 +33,8 @@ export const BioEntityDrawer = (): React.ReactNode => {
dispatch(getDrugsForBioEntityDrawerTarget(currentTargetId));
};
console.log({ bioEntityData });
if (!bioEntityData) {
return null;
}
......
import { Icon } from '@/shared/Icon';
import { PinDetailsItem } from '@/types/models';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { modelsDataSelector } from '@/redux/models/models.selectors';
import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { openMapAndSetActive, setActiveMap } from '@/redux/map/map.slice';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { mapModelIdSelector, mapOpenedMapsSelector } from '@/redux/map/map.selectors';
import { openMapAndSetActive, setActiveMap } from '@/redux/map/map.slice';
import { modelsDataSelector } from '@/redux/models/models.selectors';
import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
import { Icon } from '@/shared/Icon';
import { PinDetailsItem } from '@/types/models';
import { useSetBounds } from '@/utils/map/useSetBounds';
import { getListOfAvailableSubmaps, getPinColor } from './PinsListItem.component.utils';
import { AvailableSubmaps, PinTypeWithNone } from '../PinsList.types';
import { getListOfAvailableSubmaps, getPinColor } from './PinsListItem.component.utils';
import { useVisiblePinsPolygonCoordinates } from './hooks/useVisiblePinsPolygonCoordinates';
interface PinsListItemProps {
......
import { ONE } from '@/constants/common';
import { ZERO } from '@/constants/common';
import { EntityNumber } from '@/redux/entityNumber/entityNumber.types';
import { BioEntity } from '@/types/models';
import { PinType } from '@/types/pin';
import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
......@@ -10,17 +11,19 @@ export const getBioEntitiesFeatures = (
{
pointToProjection,
type,
entityNumber,
}: {
pointToProjection: UsePointToProjectionResult;
type: PinType;
entityNumber: EntityNumber;
},
): Feature[] => {
return bioEntites.map((bioEntity, index) =>
return bioEntites.map(bioEntity =>
getBioEntitySingleFeature(bioEntity, {
pointToProjection,
type,
// pin's index number
value: index + ONE,
value: entityNumber?.[bioEntity.fullName || bioEntity.elementId] || ZERO,
}),
);
};
......@@ -2,6 +2,7 @@
import { searchedBioEntitesSelectorOfCurrentMap } from '@/redux/bioEntity/bioEntity.selectors';
import { searchedChemicalsBioEntitesOfCurrentMapSelector } from '@/redux/chemicals/chemicals.selectors';
import { searchedDrugsBioEntitesOfCurrentMapSelector } from '@/redux/drugs/drugs.selectors';
import { entityNumberDataSelector } from '@/redux/entityNumber/entityNumber.selectors';
import { markersPinsOfCurrentMapDataSelector } from '@/redux/markers/markers.selectors';
import { usePointToProjection } from '@/utils/map/usePointToProjection';
import Feature from 'ol/Feature';
......@@ -19,16 +20,36 @@ export const useOlMapPinsLayer = (): VectorLayer<VectorSource<Feature<Geometry>>
const chemicalsBioEntities = useSelector(searchedChemicalsBioEntitesOfCurrentMapSelector);
const drugsBioEntities = useSelector(searchedDrugsBioEntitesOfCurrentMapSelector);
const markersEntities = useSelector(markersPinsOfCurrentMapDataSelector);
const entityNumber = useSelector(entityNumberDataSelector);
const elementsFeatures = useMemo(
() =>
[
getBioEntitiesFeatures(contentBioEntites, { pointToProjection, type: 'bioEntity' }),
getBioEntitiesFeatures(chemicalsBioEntities, { pointToProjection, type: 'chemicals' }),
getBioEntitiesFeatures(drugsBioEntities, { pointToProjection, type: 'drugs' }),
getBioEntitiesFeatures(contentBioEntites, {
pointToProjection,
type: 'bioEntity',
entityNumber,
}),
getBioEntitiesFeatures(chemicalsBioEntities, {
pointToProjection,
type: 'chemicals',
entityNumber,
}),
getBioEntitiesFeatures(drugsBioEntities, {
pointToProjection,
type: 'drugs',
entityNumber,
}),
getMarkersFeatures(markersEntities, { pointToProjection }),
].flat(),
[contentBioEntites, drugsBioEntities, chemicalsBioEntities, pointToProjection, markersEntities],
[
contentBioEntites,
drugsBioEntities,
chemicalsBioEntities,
pointToProjection,
markersEntities,
entityNumber,
],
);
const vectorSource = useMemo(() => {
......
import { PerfectMultiSearchParams, PerfectSearchParams } from '@/types/search';
import { PayloadAction, createAsyncThunk } from '@reduxjs/toolkit';
import { bioEntityResponseSchema } from '@/models/bioEntityResponseSchema';
import { apiPath } from '@/redux/apiPath';
import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
import { BioEntityContent, BioEntityResponse } from '@/types/models';
import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
import { getErrorMessage } from '@/utils/getErrorMessage';
import { PerfectMultiSearchParams, PerfectSearchParams } from '@/types/search';
import { ThunkConfig } from '@/types/store';
import { getErrorMessage } from '@/utils/getErrorMessage';
import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
import { PayloadAction, createAsyncThunk } from '@reduxjs/toolkit';
import { addNumbersToEntityNumberData } from '../entityNumber/entityNumber.slice';
import {
BIO_ENTITY_FETCHING_ERROR_PREFIX,
MULTI_BIO_ENTITY_FETCHING_ERROR_PREFIX,
......@@ -18,23 +19,36 @@ export const getBioEntity = createAsyncThunk<
BioEntityContent[] | undefined,
GetBioEntityProps,
ThunkConfig
>('project/getBioEntityContents', async ({ searchQuery, isPerfectMatch }, { rejectWithValue }) => {
try {
const response = await axiosInstanceNewAPI.get<BioEntityResponse>(
apiPath.getBioEntityContentsStringWithQuery({ searchQuery, isPerfectMatch }),
);
>(
'project/getBioEntityContents',
async (
{ searchQuery, isPerfectMatch, addNumbersToEntityNumber = true },
{ rejectWithValue, dispatch },
) => {
try {
const response = await axiosInstanceNewAPI.get<BioEntityResponse>(
apiPath.getBioEntityContentsStringWithQuery({ searchQuery, isPerfectMatch }),
);
const isDataValid = validateDataUsingZodSchema(response.data, bioEntityResponseSchema);
const isDataValid = validateDataUsingZodSchema(response.data, bioEntityResponseSchema);
return isDataValid ? response.data.content : undefined;
} catch (error) {
const errorMessage = getErrorMessage({
error,
prefix: BIO_ENTITY_FETCHING_ERROR_PREFIX,
});
return rejectWithValue(errorMessage);
}
});
if (addNumbersToEntityNumber) {
const bioEntityIds = response.data.content.map(
b => b.bioEntity.fullName || b.bioEntity.elementId,
);
dispatch(addNumbersToEntityNumberData(bioEntityIds));
}
return isDataValid ? response.data.content : undefined;
} catch (error) {
const errorMessage = getErrorMessage({
error,
prefix: BIO_ENTITY_FETCHING_ERROR_PREFIX,
});
return rejectWithValue(errorMessage);
}
},
);
type GetMultiBioEntityProps = PerfectMultiSearchParams;
type GetMultiBioEntityActions = PayloadAction<BioEntityContent[] | undefined>[];
......@@ -49,7 +63,7 @@ export const getMultiBioEntity = createAsyncThunk<
async ({ searchQueries, isPerfectMatch }, { dispatch, rejectWithValue }) => {
try {
const asyncGetBioEntityFunctions = searchQueries.map(searchQuery =>
dispatch(getBioEntity({ searchQuery, isPerfectMatch })),
dispatch(getBioEntity({ searchQuery, isPerfectMatch, addNumbersToEntityNumber: false })),
);
const bioEntityContentsActions = (await Promise.all(
......@@ -60,6 +74,11 @@ export const getMultiBioEntity = createAsyncThunk<
.map(bioEntityContentsAction => bioEntityContentsAction.payload || [])
.flat();
const bioEntityIds = bioEntityContents.map(
b => b.bioEntity.fullName || b.bioEntity.elementId,
);
dispatch(addNumbersToEntityNumberData(bioEntityIds));
return bioEntityContents;
} catch (error) {
const errorMessage = getErrorMessage({
......
......@@ -2,11 +2,12 @@ import { drugSchema } from '@/models/drugSchema';
import { apiPath } from '@/redux/apiPath';
import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
import { Drug } from '@/types/models';
import { ThunkConfig } from '@/types/store';
import { getErrorMessage } from '@/utils/getErrorMessage';
import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
import { createAsyncThunk } from '@reduxjs/toolkit';
import { z } from 'zod';
import { ThunkConfig } from '@/types/store';
import { addNumbersToEntityNumberData } from '../entityNumber/entityNumber.slice';
import { DRUGS_FETCHING_ERROR_PREFIX, MULTI_DRUGS_FETCHING_ERROR_PREFIX } from './drugs.constants';
export const getDrugs = createAsyncThunk<Drug[] | undefined, string, ThunkConfig>(
......@@ -36,7 +37,20 @@ export const getMultiDrugs = createAsyncThunk<void, string[], ThunkConfig>(
dispatch(getDrugs(searchQuery)),
);
await Promise.all(asyncGetDrugsFunctions);
const drugsDataActions = await Promise.all(asyncGetDrugsFunctions);
const drugsTargetsData = drugsDataActions
.map(drugsDataAction =>
typeof drugsDataAction.payload === 'string' ? [] : drugsDataAction.payload || [],
)
.flat()
.map(drug => drug.targets)
.flat()
.map(target => target.targetElements)
.flat();
const drugsIds = drugsTargetsData.map(d => d.fullName || d.elementId);
dispatch(addNumbersToEntityNumberData(drugsIds));
} catch (error) {
const errorMessage = getErrorMessage({ error, prefix: MULTI_DRUGS_FETCHING_ERROR_PREFIX });
......
import { EntityNumberState } from './entityNumber.types';
export const ENTITY_NUMBER_INITIAL_STATE: EntityNumberState = {
data: {},
};
import { EntityNumberState } from './entityNumber.types';
export const ENTITY_NUMBER_INITIAL_STATE_MOCK: EntityNumberState = {
data: {},
};
import { ONE } from '@/constants/common';
import {
AddNumbersToEntityNumberDataAction,
EntityNumber,
EntityNumberState,
} from './entityNumber.types';
export const clearEntityNumberDataReducer = (state: EntityNumberState): void => {
state.data = {};
};
export const addNumbersToEntityNumberDataReducer = (
state: EntityNumberState,
action: AddNumbersToEntityNumberDataAction,
): void => {
const { payload: ids } = action;
const lastNumber = Object.keys(state.data).length || ONE; // min num = 1
const newEntityNumber: EntityNumber = Object.fromEntries(
ids.map((id, index) => [id, lastNumber + index]),
);
state.data = {
...newEntityNumber,
...state.data,
};
};
import { createSelector } from '@reduxjs/toolkit';
import { rootSelector } from '../root/root.selectors';
export const entityNumberSelector = createSelector(rootSelector, state => state.entityNumber);
export const entityNumberDataSelector = createSelector(
entityNumberSelector,
entityNumber => entityNumber.data,
);
export const numberByEntityNumberIdSelector = createSelector(
[entityNumberDataSelector, (_state, id: string): string => id],
(entityNumber, id) => entityNumber?.[id],
);
import { createSlice } from '@reduxjs/toolkit';
import { ENTITY_NUMBER_INITIAL_STATE } from './entityNumber.constants';
import {
addNumbersToEntityNumberDataReducer,
clearEntityNumberDataReducer,
} from './entityNumber.reducers';
export const entityNumberSlice = createSlice({
name: 'entityNumber',
initialState: ENTITY_NUMBER_INITIAL_STATE,
reducers: {
addNumbersToEntityNumberData: addNumbersToEntityNumberDataReducer,
clearEntityNumberData: clearEntityNumberDataReducer,
},
extraReducers: () => {},
});
export const { addNumbersToEntityNumberData, clearEntityNumberData } = entityNumberSlice.actions;
export default entityNumberSlice.reducer;
import { PayloadAction } from '@reduxjs/toolkit';
export type EntityNumberId = string;
export type EntityNumberValue = number;
export type EntityNumber = Record<EntityNumberId, EntityNumberValue>;
export interface EntityNumberState {
data: EntityNumber;
}
export type AddNumbersToEntityNumberDataPayload = EntityNumberId[];
export type AddNumbersToEntityNumberDataAction = PayloadAction<AddNumbersToEntityNumberDataPayload>;
......@@ -23,13 +23,14 @@ import {
configureStore,
} from '@reduxjs/toolkit';
import compartmentPathwaysReducer from './compartmentPathways/compartmentPathways.slice';
import entityNumberReducer from './entityNumber/entityNumber.slice';
import exportReducer from './export/export.slice';
import legendReducer from './legend/legend.slice';
import { mapListenerMiddleware } from './map/middleware/map.middleware';
import markersReducer from './markers/markers.slice';
import { errorListenerMiddleware } from './middlewares/error.middleware';
import pluginsReducer from './plugins/plugins.slice';
import publicationsReducer from './publications/publications.slice';
import { errorListenerMiddleware } from './middlewares/error.middleware';
import statisticsReducer from './statistics/statistics.slice';
export const reducers = {
......@@ -57,6 +58,7 @@ export const reducers = {
export: exportReducer,
plugins: pluginsReducer,
markers: markersReducer,
entityNumber: entityNumberReducer,
};
export const middlewares = [mapListenerMiddleware.middleware, errorListenerMiddleware.middleware];
......
......@@ -6,4 +6,5 @@ export type PerfectMultiSearchParams = {
export type PerfectSearchParams = {
searchQuery: string;
isPerfectMatch: boolean;
addNumbersToEntityNumber?: boolean;
};
yarn.lock 0 → 100644
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment