From d0a3bf9d3e4913ff73136f90981e4bd1b37a2bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Or=C5=82=C3=B3w?= <adrian.orlow@fishbrain.com> Date: Mon, 23 Oct 2023 15:51:27 +0200 Subject: [PATCH] feat: add map base data --- .env | 1 + next.config.js | 6 +++ src/constants/index.ts | 1 + src/models/mapBackground.ts | 21 ++++++++++ src/models/mapModel.ts | 19 +++++++++ src/models/mapOverlay.ts | 14 +++++++ src/models/overviewImageView.ts | 26 ++++++++++++ src/models/project.ts | 28 +------------ src/redux/apiPath.ts | 7 ++++ src/redux/backgrounds/background.selectors.ts | 28 +++++++++++++ src/redux/backgrounds/backgrounds.reducers.ts | 12 ++++++ src/redux/backgrounds/backgrounds.slice.ts | 20 ++++++++++ src/redux/backgrounds/backgrounds.thunks.ts | 20 ++++++++++ src/redux/backgrounds/backgrounds.types.ts | 4 ++ src/redux/map/map.constants.ts | 1 + src/redux/map/map.types.ts | 1 + src/redux/models/models.reducers.ts | 12 ++++++ src/redux/models/models.selectors.ts | 13 ++++++ src/redux/models/models.slice.ts | 20 ++++++++++ src/redux/models/models.thunks.ts | 20 ++++++++++ src/redux/models/models.types.ts | 4 ++ src/redux/overlays/overlays.reducers.ts | 12 ++++++ src/redux/overlays/overlays.selectors.ts | 9 +++++ src/redux/overlays/overlays.slice.ts | 20 ++++++++++ src/redux/overlays/overlays.thunks.ts | 20 ++++++++++ src/redux/overlays/overlays.types.ts | 4 ++ src/redux/project/project.selectors.ts | 6 +++ src/redux/project/project.slice.ts | 4 +- src/redux/project/project.thunks.ts | 6 +-- src/redux/project/project.types.ts | 4 +- src/redux/root/mapStages.selectors.ts | 40 +++++++++++++++++++ src/redux/store.ts | 10 ++++- src/types/models.ts | 8 ++++ 33 files changed, 386 insertions(+), 35 deletions(-) create mode 100644 src/models/mapBackground.ts create mode 100644 src/models/mapModel.ts create mode 100644 src/models/mapOverlay.ts create mode 100644 src/models/overviewImageView.ts create mode 100644 src/redux/backgrounds/background.selectors.ts create mode 100644 src/redux/backgrounds/backgrounds.reducers.ts create mode 100644 src/redux/backgrounds/backgrounds.slice.ts create mode 100644 src/redux/backgrounds/backgrounds.thunks.ts create mode 100644 src/redux/backgrounds/backgrounds.types.ts create mode 100644 src/redux/models/models.reducers.ts create mode 100644 src/redux/models/models.selectors.ts create mode 100644 src/redux/models/models.slice.ts create mode 100644 src/redux/models/models.thunks.ts create mode 100644 src/redux/models/models.types.ts create mode 100644 src/redux/overlays/overlays.reducers.ts create mode 100644 src/redux/overlays/overlays.selectors.ts create mode 100644 src/redux/overlays/overlays.slice.ts create mode 100644 src/redux/overlays/overlays.thunks.ts create mode 100644 src/redux/overlays/overlays.types.ts create mode 100644 src/redux/project/project.selectors.ts create mode 100644 src/redux/root/mapStages.selectors.ts diff --git a/.env b/.env index 88c76981..099bf13e 100644 --- a/.env +++ b/.env @@ -1,3 +1,4 @@ NEXT_PUBLIC_BASE_API_URL = 'https://corsproxy.io/?https://pdmap.uni.lu/minerva/api' +BASE_MAP_IMAGES_URL = 'https://pdmap.uni.lu' NEXT_PUBLIC_PROJECT_ID = 'pd_map_winter_23' \ No newline at end of file diff --git a/next.config.js b/next.config.js index 254ffd6c..1b307ecc 100644 --- a/next.config.js +++ b/next.config.js @@ -4,6 +4,12 @@ const nextConfig = { experimental: { fontLoaders: [{ loader: '@next/font/google', options: { subsets: ['latin'] } }], }, + env: { + BASE_API_URL: process.env.NEXT_PUBLIC_BASE_API_URL || '', + BASE_MAP_IMAGES_URL: process.env.BASE_MAP_IMAGES_URL || '', + PROJECT_ID: process.env.NEXT_PUBLIC_PROJECT_ID || '', + ZOD_SEED: 997, + }, }; module.exports = nextConfig; diff --git a/src/constants/index.ts b/src/constants/index.ts index 43423ae1..2b1791dc 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,3 +1,4 @@ export const BASE_API_URL = process.env.NEXT_PUBLIC_BASE_API_URL || ''; +export const BASE_MAP_IMAGES_URL = process.env.BASE_MAP_IMAGES_URL || ''; export const PROJECT_ID = process.env.NEXT_PUBLIC_PROJECT_ID || ''; export const ZOD_SEED = 997; diff --git a/src/models/mapBackground.ts b/src/models/mapBackground.ts new file mode 100644 index 00000000..a8a96052 --- /dev/null +++ b/src/models/mapBackground.ts @@ -0,0 +1,21 @@ +import { z } from 'zod'; + +export const mapBackground = z.object({ + id: z.number(), + name: z.string(), + defaultOverlay: z.boolean(), + project: z.object({ projectId: z.string() }), + creator: z.object({ login: z.string() }), + status: z.string(), + progress: z.number(), + description: z.null(), + order: z.number(), + images: z.array( + z.object({ + id: z.number(), + model: z.object({ id: z.number() }), + projectBackground: z.object({ id: z.number() }), + path: z.string(), + }), + ), +}); diff --git a/src/models/mapModel.ts b/src/models/mapModel.ts new file mode 100644 index 00000000..e50dd6c4 --- /dev/null +++ b/src/models/mapModel.ts @@ -0,0 +1,19 @@ +import { z } from 'zod'; + +export const mapModel = z.object({ + idObject: z.number(), + width: z.number(), + height: z.number(), + defaultCenterX: z.number().nullable(), + defaultCenterY: z.number().nullable(), + description: z.string(), + name: z.string(), + defaultZoomLevel: z.number().nullable(), + tileSize: z.number(), + references: z.array(z.unknown()), + authors: z.array(z.unknown()), + creationDate: z.unknown(), + modificationDates: z.array(z.unknown()), + minZoom: z.number(), + maxZoom: z.number(), +}); diff --git a/src/models/mapOverlay.ts b/src/models/mapOverlay.ts new file mode 100644 index 00000000..b76cd45a --- /dev/null +++ b/src/models/mapOverlay.ts @@ -0,0 +1,14 @@ +import { z } from 'zod'; + +export const mapOverlay = z.object({ + name: z.string(), + googleLicenseConsent: z.boolean(), + creator: z.string(), + description: z.string(), + genomeType: z.null(), + genomeVersion: z.null(), + idObject: z.number(), + publicOverlay: z.boolean(), + type: z.string(), + order: z.number(), +}); diff --git a/src/models/overviewImageView.ts b/src/models/overviewImageView.ts new file mode 100644 index 00000000..82587a89 --- /dev/null +++ b/src/models/overviewImageView.ts @@ -0,0 +1,26 @@ +import { z } from 'zod'; + +export const overviewImageView = z.object({ + idObject: z.number(), + filename: z.string(), + width: z.number(), + height: z.number(), + links: z.array( + z.union([ + z.object({ + idObject: z.number(), + polygon: z.array(z.object({ x: z.number(), y: z.number() })), + imageLinkId: z.number(), + type: z.string(), + }), + z.object({ + idObject: z.number(), + polygon: z.array(z.object({ x: z.number(), y: z.number() })), + zoomLevel: z.number(), + modelPoint: z.object({ x: z.number(), y: z.number() }), + modelLinkId: z.number(), + type: z.string(), + }), + ]), + ), +}); diff --git a/src/models/project.ts b/src/models/project.ts index 051e5ca1..9f763751 100644 --- a/src/models/project.ts +++ b/src/models/project.ts @@ -1,6 +1,7 @@ import { z } from 'zod'; import { disease } from './disease'; import { organism } from './organism'; +import { overviewImageView } from './overviewImageView'; export const projectSchema = z.object({ version: z.string(), @@ -18,30 +19,5 @@ export const projectSchema = z.object({ projectId: z.string(), creationDate: z.string(), mapCanvasType: z.string(), - overviewImageViews: z.array( - z.object({ - idObject: z.number(), - filename: z.string(), - width: z.number(), - height: z.number(), - links: z.array( - z.union([ - z.object({ - idObject: z.number(), - polygon: z.array(z.object({ x: z.number(), y: z.number() })), - imageLinkId: z.number(), - type: z.string(), - }), - z.object({ - idObject: z.number(), - polygon: z.array(z.object({ x: z.number(), y: z.number() })), - zoomLevel: z.number(), - modelPoint: z.object({ x: z.number(), y: z.number() }), - modelLinkId: z.number(), - type: z.string(), - }), - ]), - ), - }), - ), + overviewImageViews: z.array(overviewImageView), }); diff --git a/src/redux/apiPath.ts b/src/redux/apiPath.ts index 8469a3d1..28512d6a 100644 --- a/src/redux/apiPath.ts +++ b/src/redux/apiPath.ts @@ -9,4 +9,11 @@ export const apiPath = { `projects/${PROJECT_ID}/miRnas:search?query=${searchQuery}`, getChemicalsStringWithQuery: (searchQuery: string): string => `projects/${PROJECT_ID}/chemicals:search?query=${searchQuery}`, + getAllModelsByProjectIdQuery: (projectId: string): string => `projects/${projectId}/models/*/`, + getAllOverlaysByProjectIdQuery: ( + projectId: string, + { publicOverlay }: { publicOverlay: boolean }, + ): string => `projects/${projectId}/overlays/?publicOverlay=${String(publicOverlay)}`, + getAllBackgroundsByProjectIdQuery: (projectId: string): string => + `projects/${projectId}/backgrounds/`, }; diff --git a/src/redux/backgrounds/background.selectors.ts b/src/redux/backgrounds/background.selectors.ts new file mode 100644 index 00000000..01b6ed81 --- /dev/null +++ b/src/redux/backgrounds/background.selectors.ts @@ -0,0 +1,28 @@ +import { createSelector } from '@reduxjs/toolkit'; +import { mapDataSelector } from '../map/map.selectors'; +import { rootSelector } from '../root/root.selectors'; + +export const backgroundsSelector = createSelector(rootSelector, state => state.backgrounds); + +export const backgroundsDataSelector = createSelector( + backgroundsSelector, + backgrounds => backgrounds.data || [], +); + +export const currentBackgroundSelector = createSelector( + backgroundsDataSelector, + mapDataSelector, + (backgrounds, mapData) => backgrounds.find(background => background.id === mapData.backgroundId), +); + +export const currentBackgroundImageSelector = createSelector( + mapDataSelector, + currentBackgroundSelector, + (mapData, background) => + background ? background.images.find(image => image.model.id === mapData.modelId) : undefined, +); + +export const currentBackgroundImagePathSelector = createSelector( + currentBackgroundImageSelector, + image => (image ? image.path : ''), +); diff --git a/src/redux/backgrounds/backgrounds.reducers.ts b/src/redux/backgrounds/backgrounds.reducers.ts new file mode 100644 index 00000000..6c099f17 --- /dev/null +++ b/src/redux/backgrounds/backgrounds.reducers.ts @@ -0,0 +1,12 @@ +import { ActionReducerMapBuilder } from '@reduxjs/toolkit'; +import { getAllBackgroundsByProjectId } from './backgrounds.thunks'; +import { BackgroundsState } from './backgrounds.types'; + +export const getAllBackgroundsByProjectIdReducer = ( + builder: ActionReducerMapBuilder<BackgroundsState>, +): void => { + builder.addCase(getAllBackgroundsByProjectId.fulfilled, (state, action) => { + state.data = action.payload || []; + state.loading = 'succeeded'; + }); +}; diff --git a/src/redux/backgrounds/backgrounds.slice.ts b/src/redux/backgrounds/backgrounds.slice.ts new file mode 100644 index 00000000..491e981d --- /dev/null +++ b/src/redux/backgrounds/backgrounds.slice.ts @@ -0,0 +1,20 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { getAllBackgroundsByProjectIdReducer } from './backgrounds.reducers'; +import { BackgroundsState } from './backgrounds.types'; + +const initialState: BackgroundsState = { + data: [], + loading: 'idle', + error: { name: '', message: '' }, +}; + +const backgroundsState = createSlice({ + name: 'backgrounds', + initialState, + reducers: {}, + extraReducers: builder => { + getAllBackgroundsByProjectIdReducer(builder); + }, +}); + +export default backgroundsState.reducer; diff --git a/src/redux/backgrounds/backgrounds.thunks.ts b/src/redux/backgrounds/backgrounds.thunks.ts new file mode 100644 index 00000000..ee0b860f --- /dev/null +++ b/src/redux/backgrounds/backgrounds.thunks.ts @@ -0,0 +1,20 @@ +import { mapBackground } from '@/models/mapBackground'; +import { axiosInstance } from '@/services/api/utils/axiosInstance'; +import { MapBackground } from '@/types/models'; +import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { z } from 'zod'; +import { apiPath } from '../apiPath'; + +export const getAllBackgroundsByProjectId = createAsyncThunk( + 'models/getAllBackgroundsByProjectId', + async (projectId: string): Promise<MapBackground[]> => { + const response = await axiosInstance.get<MapBackground[]>( + apiPath.getAllBackgroundsByProjectIdQuery(projectId), + ); + + const isDataValid = validateDataUsingZodSchema(response.data, z.array(mapBackground)); + + return isDataValid ? response.data : []; + }, +); diff --git a/src/redux/backgrounds/backgrounds.types.ts b/src/redux/backgrounds/backgrounds.types.ts new file mode 100644 index 00000000..5457757c --- /dev/null +++ b/src/redux/backgrounds/backgrounds.types.ts @@ -0,0 +1,4 @@ +import { FetchDataState } from '@/types/fetchDataState'; +import { MapBackground } from '@/types/models'; + +export type BackgroundsState = FetchDataState<MapBackground[] | []>; diff --git a/src/redux/map/map.constants.ts b/src/redux/map/map.constants.ts index 19bf5fb5..a6307772 100644 --- a/src/redux/map/map.constants.ts +++ b/src/redux/map/map.constants.ts @@ -11,6 +11,7 @@ export const MAP_DATA_INITIAL_STATE: MapData = { projectId: PROJECT_ID, meshId: '', modelId: 0, + backgroundId: 0, overlaysIds: [], position: DEFAULT_CENTER_POINT, show: { diff --git a/src/redux/map/map.types.ts b/src/redux/map/map.types.ts index 95fafc9a..ac46d776 100644 --- a/src/redux/map/map.types.ts +++ b/src/redux/map/map.types.ts @@ -13,6 +13,7 @@ export type MapData = { projectId: string; meshId: string; modelId: number; + backgroundId: number; overlaysIds: number[]; size: MapSize; position: Point; diff --git a/src/redux/models/models.reducers.ts b/src/redux/models/models.reducers.ts new file mode 100644 index 00000000..7c8f1849 --- /dev/null +++ b/src/redux/models/models.reducers.ts @@ -0,0 +1,12 @@ +import { getAllModelsByProjectId } from '@/redux/models/models.thunks'; +import { ActionReducerMapBuilder } from '@reduxjs/toolkit'; +import { ModelsState } from './models.types'; + +export const getAllModelsByProjectIdReducer = ( + builder: ActionReducerMapBuilder<ModelsState>, +): void => { + builder.addCase(getAllModelsByProjectId.fulfilled, (state, action) => { + state.data = action.payload || []; + state.loading = 'succeeded'; + }); +}; diff --git a/src/redux/models/models.selectors.ts b/src/redux/models/models.selectors.ts new file mode 100644 index 00000000..ec3bbe24 --- /dev/null +++ b/src/redux/models/models.selectors.ts @@ -0,0 +1,13 @@ +import { rootSelector } from '@/redux/root/root.selectors'; +import { createSelector } from '@reduxjs/toolkit'; +import { mapDataSelector } from '../map/map.selectors'; + +export const modelsSelector = createSelector(rootSelector, state => state.models); + +export const modelsDataSelector = createSelector(modelsSelector, models => models.data || []); + +export const currentModelSelector = createSelector( + modelsSelector, + mapDataSelector, + (models, mapData) => models.find(model => model.idObject === mapData.modelId), +); diff --git a/src/redux/models/models.slice.ts b/src/redux/models/models.slice.ts new file mode 100644 index 00000000..f47b5aa8 --- /dev/null +++ b/src/redux/models/models.slice.ts @@ -0,0 +1,20 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { getAllModelsByProjectIdReducer } from './models.reducers'; +import { ModelsState } from './models.types'; + +const initialState: ModelsState = { + data: [], + loading: 'idle', + error: { name: '', message: '' }, +}; + +const modelsSlice = createSlice({ + name: 'models', + initialState, + reducers: {}, + extraReducers: builder => { + getAllModelsByProjectIdReducer(builder); + }, +}); + +export default modelsSlice.reducer; diff --git a/src/redux/models/models.thunks.ts b/src/redux/models/models.thunks.ts new file mode 100644 index 00000000..c7cb548e --- /dev/null +++ b/src/redux/models/models.thunks.ts @@ -0,0 +1,20 @@ +import { mapModel } from '@/models/mapModel'; +import { axiosInstance } from '@/services/api/utils/axiosInstance'; +import { MapModel } from '@/types/models'; +import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { z } from 'zod'; +import { apiPath } from '../apiPath'; + +export const getAllModelsByProjectId = createAsyncThunk( + 'models/getAllModelsByProjectId', + async (projectId: string): Promise<MapModel[]> => { + const response = await axiosInstance.get<MapModel[]>( + apiPath.getAllModelsByProjectIdQuery(projectId), + ); + + const isDataValid = validateDataUsingZodSchema(response.data, z.array(mapModel)); + + return isDataValid ? response.data : []; + }, +); diff --git a/src/redux/models/models.types.ts b/src/redux/models/models.types.ts new file mode 100644 index 00000000..06bd1892 --- /dev/null +++ b/src/redux/models/models.types.ts @@ -0,0 +1,4 @@ +import { FetchDataState } from '@/types/fetchDataState'; +import { MapModel } from '@/types/models'; + +export type ModelsState = FetchDataState<MapModel[] | []>; diff --git a/src/redux/overlays/overlays.reducers.ts b/src/redux/overlays/overlays.reducers.ts new file mode 100644 index 00000000..09863b89 --- /dev/null +++ b/src/redux/overlays/overlays.reducers.ts @@ -0,0 +1,12 @@ +import { ActionReducerMapBuilder } from '@reduxjs/toolkit'; +import { getAllPublicOverlaysByProjectId } from './overlays.thunks'; +import { OverlaysState } from './overlays.types'; + +export const getAllPublicOverlaysByProjectIdReducer = ( + builder: ActionReducerMapBuilder<OverlaysState>, +): void => { + builder.addCase(getAllPublicOverlaysByProjectId.fulfilled, (state, action) => { + state.data = action.payload || []; + state.loading = 'succeeded'; + }); +}; diff --git a/src/redux/overlays/overlays.selectors.ts b/src/redux/overlays/overlays.selectors.ts new file mode 100644 index 00000000..80b0439b --- /dev/null +++ b/src/redux/overlays/overlays.selectors.ts @@ -0,0 +1,9 @@ +import { createSelector } from '@reduxjs/toolkit'; +import { rootSelector } from '../root/root.selectors'; + +export const overlaysSelector = createSelector(rootSelector, state => state.overlays); + +export const overlaysDataSelector = createSelector( + overlaysSelector, + overlays => overlays.data || [], +); diff --git a/src/redux/overlays/overlays.slice.ts b/src/redux/overlays/overlays.slice.ts new file mode 100644 index 00000000..8d259288 --- /dev/null +++ b/src/redux/overlays/overlays.slice.ts @@ -0,0 +1,20 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { getAllPublicOverlaysByProjectIdReducer } from './overlays.reducers'; +import { OverlaysState } from './overlays.types'; + +const initialState: OverlaysState = { + data: [], + loading: 'idle', + error: { name: '', message: '' }, +}; + +const overlaysState = createSlice({ + name: 'overlays', + initialState, + reducers: {}, + extraReducers: builder => { + getAllPublicOverlaysByProjectIdReducer(builder); + }, +}); + +export default overlaysState.reducer; diff --git a/src/redux/overlays/overlays.thunks.ts b/src/redux/overlays/overlays.thunks.ts new file mode 100644 index 00000000..3dc3c70e --- /dev/null +++ b/src/redux/overlays/overlays.thunks.ts @@ -0,0 +1,20 @@ +import { mapOverlay } from '@/models/mapOverlay'; +import { axiosInstance } from '@/services/api/utils/axiosInstance'; +import { MapOverlay } from '@/types/models'; +import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { z } from 'zod'; +import { apiPath } from '../apiPath'; + +export const getAllPublicOverlaysByProjectId = createAsyncThunk( + 'models/getAllPublicOverlaysByProjectId', + async (projectId: string): Promise<MapOverlay[]> => { + const response = await axiosInstance.get<MapOverlay[]>( + apiPath.getAllOverlaysByProjectIdQuery(projectId, { publicOverlay: true }), + ); + + const isDataValid = validateDataUsingZodSchema(response.data, z.array(mapOverlay)); + + return isDataValid ? response.data : []; + }, +); diff --git a/src/redux/overlays/overlays.types.ts b/src/redux/overlays/overlays.types.ts new file mode 100644 index 00000000..ee00e945 --- /dev/null +++ b/src/redux/overlays/overlays.types.ts @@ -0,0 +1,4 @@ +import { FetchDataState } from '@/types/fetchDataState'; +import { MapOverlay } from '@/types/models'; + +export type OverlaysState = FetchDataState<MapOverlay[] | []>; diff --git a/src/redux/project/project.selectors.ts b/src/redux/project/project.selectors.ts new file mode 100644 index 00000000..9ba0ec03 --- /dev/null +++ b/src/redux/project/project.selectors.ts @@ -0,0 +1,6 @@ +import { createSelector } from '@reduxjs/toolkit'; +import { rootSelector } from '../root/root.selectors'; + +export const projectSelector = createSelector(rootSelector, state => state.project); + +export const projectDataSelector = createSelector(projectSelector, project => project.data); diff --git a/src/redux/project/project.slice.ts b/src/redux/project/project.slice.ts index 994cb484..a6e0d9d0 100644 --- a/src/redux/project/project.slice.ts +++ b/src/redux/project/project.slice.ts @@ -1,9 +1,9 @@ -import { createSlice } from '@reduxjs/toolkit'; import { ProjectState } from '@/redux/project/project.types'; +import { createSlice } from '@reduxjs/toolkit'; import { getProjectByIdReducer } from './project.reducers'; const initialState: ProjectState = { - data: [], + data: undefined, loading: 'idle', error: { name: '', message: '' }, }; diff --git a/src/redux/project/project.thunks.ts b/src/redux/project/project.thunks.ts index 944895cf..d99f5d55 100644 --- a/src/redux/project/project.thunks.ts +++ b/src/redux/project/project.thunks.ts @@ -1,11 +1,11 @@ -import { createAsyncThunk } from '@reduxjs/toolkit'; +import { projectSchema } from '@/models/project'; import { axiosInstance } from '@/services/api/utils/axiosInstance'; import { Project } from '@/types/models'; import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema'; -import { projectSchema } from '@/models/project'; +import { createAsyncThunk } from '@reduxjs/toolkit'; export const getProjectById = createAsyncThunk( - 'project/getUsersByIdStatus', + 'project/getProjectById', async (id: string): Promise<Project | undefined> => { const response = await axiosInstance.get<Project>(`projects/${id}`); diff --git a/src/redux/project/project.types.ts b/src/redux/project/project.types.ts index f88c4b6b..c92be2c4 100644 --- a/src/redux/project/project.types.ts +++ b/src/redux/project/project.types.ts @@ -1,8 +1,8 @@ -import { Project } from '@/types/models'; import { Loading } from '@/types/loadingState'; +import { Project } from '@/types/models'; export type ProjectState = { - data: Project | undefined | []; + data: Project | undefined; loading: Loading; error: Error; }; diff --git a/src/redux/root/mapStages.selectors.ts b/src/redux/root/mapStages.selectors.ts new file mode 100644 index 00000000..cda0f3b2 --- /dev/null +++ b/src/redux/root/mapStages.selectors.ts @@ -0,0 +1,40 @@ +import { createSelector } from '@reduxjs/toolkit'; +import { backgroundsSelector } from '../backgrounds/background.selectors'; +import { modelsSelector } from '../models/models.selectors'; +import { overlaysSelector } from '../overlays/overlays.selectors'; +import { projectSelector } from '../project/project.selectors'; + +export const mapLoadingFirstStageInitializedSelector = createSelector( + projectSelector, + project => project.loading !== 'idle', +); + +export const mapLoadingFirstStageCompletedSelector = createSelector( + projectSelector, + project => project.loading === 'succeeded', +); + +export const mapLoadingSecondStageInitializedSelector = createSelector( + backgroundsSelector, + modelsSelector, + overlaysSelector, + (backgrounds, models, overlays) => + [backgrounds.loading, models.loading, overlays.loading].every(loading => loading !== 'idle'), +); + +export const mapLoadingSecondStageCompletedSelector = createSelector( + backgroundsSelector, + modelsSelector, + overlaysSelector, + (backgrounds, models, overlays) => + [backgrounds.loading, models.loading, overlays.loading].every( + loading => loading === 'succeeded', + ), +); + +export const mapLoadingAllStagesCompletedSelector = createSelector( + mapLoadingFirstStageCompletedSelector, + mapLoadingSecondStageCompletedSelector, + (firstStageCompleted, secondStageCompleted) => + [firstStageCompleted, secondStageCompleted].every(completed => completed === true), +); diff --git a/src/redux/store.ts b/src/redux/store.ts index 0136870f..556abf07 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -1,17 +1,23 @@ import drawerReducer from '@/redux/drawer/drawer.slice'; import drugsReducer from '@/redux/drugs/drugs.slice'; import mapReducer from '@/redux/map/map.slice'; -import projectSlice from '@/redux/project/project.slice'; +import projectReducer from '@/redux/project/project.slice'; import searchReducer from '@/redux/search/search.slice'; import { configureStore } from '@reduxjs/toolkit'; +import backgroundsReducer from './backgrounds/backgrounds.slice'; +import modelsReducer from './models/models.slice'; +import overlaysReducer from './overlays/overlays.slice'; export const store = configureStore({ reducer: { search: searchReducer, - project: projectSlice, + project: projectReducer, drugs: drugsReducer, drawer: drawerReducer, map: mapReducer, + backgrounds: backgroundsReducer, + overlays: overlaysReducer, + models: modelsReducer, }, devTools: true, }); diff --git a/src/types/models.ts b/src/types/models.ts index abadf02a..6b290755 100644 --- a/src/types/models.ts +++ b/src/types/models.ts @@ -2,12 +2,20 @@ import { bioEntityContentSchema } from '@/models/bioEntityContentSchema'; import { chemicalSchema } from '@/models/chemicalSchema'; import { disease } from '@/models/disease'; import { drugSchema } from '@/models/drugSchema'; +import { mapModel } from '@/models/mapModel'; +import { mapOverlay } from '@/models/mapOverlay'; import { mirnaSchema } from '@/models/mirnaSchema'; import { organism } from '@/models/organism'; +import { overviewImageView } from '@/models/overviewImageView'; import { projectSchema } from '@/models/project'; import { z } from 'zod'; +import { mapBackground } from '../models/mapBackground'; export type Project = z.infer<typeof projectSchema>; +export type OverviewImageView = z.infer<typeof overviewImageView>; +export type MapModel = z.infer<typeof mapModel>; +export type MapOverlay = z.infer<typeof mapOverlay>; +export type MapBackground = z.infer<typeof mapBackground>; export type Organism = z.infer<typeof organism>; export type Disease = z.infer<typeof disease>; export type Drug = z.infer<typeof drugSchema>; -- GitLab