diff --git a/.env b/.env index 88c76981d85f1e298c0c03d911a28e8edaace6ee..099bf13e4dc69a665944e4dab0c04a5a76d454fb 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 254ffd6c956bfd4f642b74fe24370e95d5610e5e..1b307ecc05742d534f2bf1906896a393b59b587e 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 43423ae1b1478dfd342c37c5488a44cc09703006..2b1791dc989e6b1798d2cc13dea9efa66687ff29 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 0000000000000000000000000000000000000000..a8a9605280785e7071cd94256022177803ba9808 --- /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 0000000000000000000000000000000000000000..e50dd6c458e988c46be55d2f054d9982369d8478 --- /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 0000000000000000000000000000000000000000..b76cd45abde2b9bf1c4c3314040ab37e4e48a610 --- /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 0000000000000000000000000000000000000000..82587a893d3f6014e7a656b5104bc5d26c492721 --- /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 051e5ca197d0bf239e1f525e8a460dfa499467cb..9f763751b2a179aac4d7cac803d565c3e1256481 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 8469a3d16b0f7eb0e9144339ff69da900564e86c..28512d6af51f765811f99f18f60e7697f10ebc76 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 0000000000000000000000000000000000000000..01b6ed81bf434fb40ee56ec1784e3365ea8c2666 --- /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 0000000000000000000000000000000000000000..6c099f17070cd19fff023efdbfaa8e57ca521c0e --- /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 0000000000000000000000000000000000000000..491e981d8d91db22a34f03dff1a14559b746d642 --- /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 0000000000000000000000000000000000000000..ee0b860f5584dc5865b34fc549f8d7a6fb885024 --- /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 0000000000000000000000000000000000000000..5457757cb6dfddd22da156c6f6ba400ac9480647 --- /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 19bf5fb562d94a93ce85bdfac206caafe498cb96..a6307772f6c81239b02d58d839c0e050112fa761 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 95fafc9ad9a0bdf6b68f7aed6f5eb3fc5eac8002..ac46d776e601f8ae23c18c2e0dd58de9edb8fe75 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 0000000000000000000000000000000000000000..7c8f18498df26e565b70f326626ad24c75809d36 --- /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 0000000000000000000000000000000000000000..ec3bbe2488d921e89eeaaf69df39df69f271e9b3 --- /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 0000000000000000000000000000000000000000..f47b5aa8da79bc969d3ce1e098005e9537c686f2 --- /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 0000000000000000000000000000000000000000..c7cb548e8e353865becc3ed343095836d3fa9ddc --- /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 0000000000000000000000000000000000000000..06bd18926c75f5fc88c8f55cde1e92cd5c102609 --- /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 0000000000000000000000000000000000000000..09863b89a1a6f81fa67e66216a2a5afb21c69449 --- /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 0000000000000000000000000000000000000000..80b0439bc90d4f04f09ac02c9b5a64803dff2fd4 --- /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 0000000000000000000000000000000000000000..8d259288d5d8eb69d15d48a8408d4e83d6342573 --- /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 0000000000000000000000000000000000000000..3dc3c70e8be55ef5502dece731bb090a7758d07d --- /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 0000000000000000000000000000000000000000..ee00e94527ebcf6d68a453c26e0808f94874b1fa --- /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 0000000000000000000000000000000000000000..9ba0ec033ee3257bc54f091e2194b7af739d4b60 --- /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 994cb48464988a1ea7b97d4baf788e55dc623b8e..a6e0d9d0e9990dd201476682f6854a3893c1c0c8 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 944895cfc791a7bcb44f46b7d5709793ad1fd1f1..d99f5d55ca335e5549354ac7d5e649489e1683f5 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 f88c4b6b18bb0ad4ef8541928d2511489dcac317..c92be2c478cdd4f90986974b8a8de59e6c5d1901 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 0000000000000000000000000000000000000000..cda0f3b219e9b7f7fe6e7d9ebd7099b9f0530271 --- /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 0136870f551d8ceb8695c6e54e45087e360f3ca6..556abf07c075702ce0656f8fe3aaabe80ff42fa6 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 abadf02ac342719044fbc94260c65ed3704d8b71..6b2907550af03e0d2cb53ae474d28f40c7c08124 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>;