Skip to content
Snippets Groups Projects
Commit 5eb12f46 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

adding group overlay added

parent 5c28533c
No related branches found
No related tags found
1 merge request!380Resolve "[MIN-194] Display user grouped overlays"
Showing with 167 additions and 2 deletions
......@@ -4,6 +4,7 @@ import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { twMerge } from 'tailwind-merge';
import { CommentDrawer } from '@/components/Map/Drawer/CommentDrawer';
import { LayersDrawer } from '@/components/Map/Drawer/LayersDrawer/LayersDrawer.component';
import { OverlayGroupDrawer } from '@/components/Map/Drawer/OverlayGroupDrawer';
import { AvailablePluginsDrawer } from './AvailablePluginsDrawer';
import { BioEntityDrawer } from './BioEntityDrawer/BioEntityDrawer.component';
import { ExportDrawer } from './ExportDrawer';
......@@ -29,6 +30,7 @@ export const Drawer = (): JSX.Element => {
{isOpen && drawerName === 'reaction' && <ReactionDrawer />}
{isOpen && drawerName === 'overlays' && <OverlaysDrawer />}
{isOpen && drawerName === 'bio-entity' && <BioEntityDrawer />}
{isOpen && drawerName === 'overlay-group' && <OverlayGroupDrawer />}
{isOpen && drawerName === 'project-info' && <ProjectInfoDrawer />}
{isOpen && drawerName === 'export' && <ExportDrawer />}
{isOpen && drawerName === 'available-plugins' && <AvailablePluginsDrawer />}
......
import { DrawerHeadingBackwardButton } from '@/shared/DrawerHeadingBackwardButton';
import { Input } from '@/shared/Input';
import { Button } from '@/shared/Button';
import { openOverlaysDrawer } from '@/redux/drawer/drawer.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { useOverlayGroupForm } from '@/components/Map/Drawer/OverlayGroupDrawer/hooks/useOverlayGroupForm';
export const OverlayGroupDrawer = (): JSX.Element => {
const dispatch = useAppDispatch();
const navigateToOverlays = (): void => {
dispatch(openOverlaysDrawer());
};
const { name, handleChangeName, handleSubmit } = useOverlayGroupForm();
return (
<>
<DrawerHeadingBackwardButton backwardFunction={navigateToOverlays}>
Add overlay group
</DrawerHeadingBackwardButton>
<form className="flex h-[calc(100%-93px)] max-h-[calc(100%-93px)] flex-col overflow-y-auto p-6">
<label className="mb-2.5 text-sm" htmlFor="name">
Name
<Input
type="text"
name="name"
id="name"
data-testid="overlay-name"
value={name}
onChange={handleChangeName}
placeholder="Fancy group name"
sizeVariant="medium"
className="mt-2.5 text-xs"
/>
</label>
<Button
className="mt-2.5 items-center justify-center self-start"
onClick={handleSubmit}
aria-label="add overlay"
>
Add
</Button>
</form>
</>
);
};
import { useState, ChangeEvent } from 'react';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { overlayGroupsSelector } from '@/redux/overlayGroup/overlayGroup.selectors';
import { addOverlayGroup } from '@/redux/overlayGroup/overlayGroup.thunks';
type ReturnType = {
name: string;
handleChangeName: (e: ChangeEvent<HTMLInputElement>) => void;
handleSubmit: () => Promise<void>;
};
export const useOverlayGroupForm = (): ReturnType => {
const dispatch = useAppDispatch();
const overlayGroups = useAppSelector(overlayGroupsSelector);
const [name, setName] = useState('');
const handleChangeName = (e: ChangeEvent<HTMLInputElement>): void => {
setName(e.target.value);
};
const handleSubmit = async (): Promise<void> => {
if (!name) return;
dispatch(
addOverlayGroup({
name,
order: overlayGroups.length,
}),
);
setName('');
};
return {
name,
handleChangeName,
handleSubmit,
};
};
export { OverlayGroupDrawer } from './OverlaysGroupDrawer.component';
import { displayAddOverlaysDrawer } from '@/redux/drawer/drawer.slice';
import {
displayAddOverlayGroupDrawer,
displayAddOverlaysDrawer,
} from '@/redux/drawer/drawer.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { authenticatedUserSelector, loadingUserSelector } from '@/redux/user/user.selectors';
......@@ -17,6 +20,9 @@ export const UserOverlays = (): JSX.Element => {
const handleAddOverlay = (): void => {
dispatch(displayAddOverlaysDrawer());
};
const handleAddOverlayGroup = (): void => {
dispatch(displayAddOverlayGroupDrawer());
};
return (
<div className="py-6">
......@@ -35,6 +41,9 @@ export const UserOverlays = (): JSX.Element => {
<>
<div className="flex items-center justify-between px-6">
<p className="font-semibold">User provided overlays:</p>
<Button onClick={handleAddOverlayGroup} aria-label="add overlay group button">
Add group
</Button>
<Button onClick={handleAddOverlay} aria-label="add overlay button">
Add overlay
</Button>
......
......@@ -135,4 +135,5 @@ export const apiPath = {
getDrugAutocomplete: (): string => `projects/${PROJECT_ID}/drugs/suggestedQueryList`,
getChemicalAutocomplete: (): string => `projects/${PROJECT_ID}/chemicals/suggestedQueryList`,
getOverlayGroups: (): string => `projects/${PROJECT_ID}/overlay_groups/`,
addOverlayGroup: (): string => `projects/${PROJECT_ID}/overlay_groups/`,
};
......@@ -45,6 +45,12 @@ export const displayAddOverlaysDrawerReducer = (state: DrawerState): void => {
state.overlayDrawerState.currentStep = STEP.SECOND;
};
export const displayAddOverlayGroupDrawerReducer = (state: DrawerState): void => {
state.isOpen = true;
state.drawerName = 'overlay-group';
state.overlayDrawerState.currentStep = STEP.FIRST;
};
export const selectTabReducer = (
state: DrawerState,
action: OpenSearchDrawerWithSelectedTabReducerAction,
......
......@@ -2,6 +2,7 @@ import { createSlice } from '@reduxjs/toolkit';
import { DRAWER_INITIAL_STATE } from './drawer.constants';
import {
closeDrawerReducer,
displayAddOverlayGroupDrawerReducer,
displayAddOverlaysDrawerReducer,
displayBioEntitiesListReducer,
displayChemicalsListReducer,
......@@ -29,6 +30,7 @@ const drawerSlice = createSlice({
openSubmapsDrawer: openSubmapsDrawerReducer,
openOverlaysDrawer: openOverlaysDrawerReducer,
displayAddOverlaysDrawer: displayAddOverlaysDrawerReducer,
displayAddOverlayGroupDrawer: displayAddOverlayGroupDrawerReducer,
selectTab: selectTabReducer,
closeDrawer: closeDrawerReducer,
displayDrugsList: displayDrugsListReducer,
......@@ -52,6 +54,7 @@ export const {
openSubmapsDrawer,
openOverlaysDrawer,
displayAddOverlaysDrawer,
displayAddOverlayGroupDrawer,
selectTab,
closeDrawer,
displayDrugsList,
......
......@@ -10,5 +10,7 @@ export const overlayGroupsSelector = createSelector(overlayGroupSelector, overla
if (overlayGroup?.data) {
result = result.concat(overlayGroup?.data);
}
// eslint-disable-next-line no-console
console.log(result);
return result;
});
import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
import { OverlayGroup, PageOf } from '@/types/models';
import { MapOverlay, OverlayGroup, PageOf } from '@/types/models';
import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
import { createAsyncThunk } from '@reduxjs/toolkit';
import { ThunkConfig } from '@/types/store';
import { getError } from '@/utils/error-report/getError';
import { pageableSchema } from '@/models/pageableSchema';
import { overlayGroupSchema } from '@/models/overlayGroupSchema';
import { showToast } from '@/utils/showToast';
import axios from 'axios';
import { apiPath } from '../apiPath';
export const getOverlayGroups = createAsyncThunk<OverlayGroup[], void, ThunkConfig>(
......@@ -14,6 +16,9 @@ export const getOverlayGroups = createAsyncThunk<OverlayGroup[], void, ThunkConf
try {
const response = await axiosInstanceNewAPI.get<PageOf<OverlayGroup>>(
apiPath.getOverlayGroups(),
{
withCredentials: true,
},
);
const isDataValid = validateDataUsingZodSchema(
......@@ -27,3 +32,49 @@ export const getOverlayGroups = createAsyncThunk<OverlayGroup[], void, ThunkConf
}
},
);
type AddOverlayGroupArgs = {
name: string;
order: number;
};
export const addOverlayGroup = createAsyncThunk<undefined, AddOverlayGroupArgs, ThunkConfig>(
'overlays/addOverlayGroup',
async (
{ name, order },
{ dispatch },
// eslint-disable-next-line consistent-return
) => {
try {
const response = await axiosInstanceNewAPI.post<MapOverlay>(
apiPath.addOverlayGroup(),
{
name,
order,
},
{
withCredentials: true,
},
);
const isDataValid = validateDataUsingZodSchema(response.data, overlayGroupSchema);
if (!isDataValid) {
showToast({
type: 'error',
message: 'Problem with adding group encountered',
duration: 120000,
});
} else {
showToast({ type: 'success', message: 'Overlay group added successfully' });
}
await dispatch(getOverlayGroups());
} catch (error) {
if (axios.isAxiosError(error) && error.code === 'ERR_BAD_REQUEST') {
const data = error.response?.data;
showToast({ type: 'error', message: data.reason, duration: 120000 });
} else {
return Promise.reject(getError({ error, prefix: 'Failed to add overlay group' }));
}
}
},
);
......@@ -23,6 +23,7 @@ import {
} from '@/components/FunctionalArea/CookieBanner/CookieBanner.constants';
import { injectMatomoTracking } from '@/utils/injectMatomoTracking';
import { getGlyphs } from '@/redux/glyphs/glyphs.thunks';
import { getOverlayGroups } from '@/redux/overlayGroup/overlayGroup.thunks';
import { getConfiguration, getConfigurationOptions } from '../configuration/configuration.thunks';
import {
initMapBackground,
......@@ -145,6 +146,7 @@ export const fetchInitialAppData = createAsyncThunk<
}
await dispatch(getAllUserOverlaysByCreator());
await dispatch(getOverlayGroups());
/** fetch overlays */
if (queryData.overlaysId) {
dispatch(getInitOverlays({ overlaysId: queryData.overlaysId }));
......
......@@ -8,6 +8,7 @@ export type DrawerName =
| 'submaps'
| 'reaction'
| 'overlays'
| 'overlay-group'
| 'bio-entity'
| 'comment'
| 'available-plugins'
......
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