From 4ee4e8f5439781c4ac39fc18ae5091478531cf42 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Tue, 4 Feb 2025 15:58:36 +0100
Subject: [PATCH 01/17] use specific formatting only for a href

---
 CHANGELOG                                                   | 6 +++++-
 .../Drawer/BioEntityDrawer/BioEntityDrawer.component.tsx    | 2 +-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 810c9a49..308d1ccf 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,8 @@
+minerva-front (18.1.1) stable; urgency=medium
+  * Bug fix: styling of notes reset only for a href (#334)
+
+-- Piotr Gawron <piotr.gawron@uni.lu>  Tue, 04 Feb 2025 16:00:00 +0200
+
 minerva-front (18.1.0) stable; urgency=medium
   * Small improvement: support for links that should be opened immediately
     (#342)
@@ -6,7 +11,6 @@ minerva-front (18.1.0) stable; urgency=medium
   * Bug fix: submap download did not download selected map (#337)
   * Bug fix: styling of notes contains original styling for links (#344)
 
-
 -- Piotr Gawron <piotr.gawron@uni.lu>  Thu, 30 Jan 2025 15:00:00 +0200
 
 minerva-front (18.0.7) stable; urgency=medium
diff --git a/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.tsx b/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.tsx
index 4f714969..811e3527 100644
--- a/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.tsx
+++ b/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.tsx
@@ -75,7 +75,7 @@ export const BioEntityDrawer = (): React.ReactNode => {
           </div>
         )}
         {bioEntityData.notes && (
-          <span className="visited:text-purple-600 text-blue-600 underline hover:text-blue-800">
+          <span className="[&_a]:visited:text-purple-600 [&_a]:text-blue-600 [&_a]:underline [&_a]:hover:text-blue-800">
             <hr className="border-b border-b-divide" />
             <div
               className="mt-2 text-sm font-normal"
-- 
GitLab


From 59544e6fddfc73fe943fd7ffd026591ce93b6cb8 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Wed, 5 Feb 2025 09:46:08 +0100
Subject: [PATCH 02/17] don't allow to seaerch for chemicals without defined
 disease

---
 CHANGELOG                                     |  1 +
 .../BioEntityDrawer.component.test.tsx        |  6 ++++++
 .../BioEntityDrawer.component.tsx             | 15 +++++++++++--
 src/models/disease.ts                         |  9 --------
 src/models/generators/diseaseGenerator.ts     |  4 ++--
 src/models/projectSchema.ts                   |  4 ++--
 src/shared/Toast/Toast.component.tsx          | 21 +++++++++++++------
 src/types/models.ts                           |  2 --
 src/utils/showToast.tsx                       |  2 +-
 9 files changed, 40 insertions(+), 24 deletions(-)
 delete mode 100644 src/models/disease.ts

diff --git a/CHANGELOG b/CHANGELOG
index 308d1ccf..4ecb023a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,6 @@
 minerva-front (18.1.1) stable; urgency=medium
   * Bug fix: styling of notes reset only for a href (#334)
+  * Bug fix: disable searching for chemicals in projects without disease (#347)
 
 -- Piotr Gawron <piotr.gawron@uni.lu>  Tue, 04 Feb 2025 16:00:00 +0200
 
diff --git a/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.test.tsx b/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.test.tsx
index b2fad199..6262c046 100644
--- a/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.test.tsx
+++ b/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.test.tsx
@@ -17,6 +17,8 @@ import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreA
 import { InitialStoreState } from '@/utils/testing/getReduxWrapperWithStore';
 import { act, render, screen } from '@testing-library/react';
 import { MockStoreEnhanced } from 'redux-mock-store';
+import { projectFixture } from '@/models/fixtures/projectFixture';
+import { PROJECT_STATE_INITIAL_MOCK } from '@/redux/project/project.mock';
 import { BioEntityDrawer } from './BioEntityDrawer.component';
 
 const renderComponent = (
@@ -360,6 +362,10 @@ describe('BioEntityDrawer - component', () => {
             chemicals: {},
           },
         },
+        project: {
+          ...PROJECT_STATE_INITIAL_MOCK,
+          data: projectFixture,
+        },
       });
 
       const button = screen.getByText('Chemicals for target', { exact: false });
diff --git a/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.tsx b/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.tsx
index 811e3527..19146f7d 100644
--- a/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.tsx
+++ b/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.tsx
@@ -15,6 +15,8 @@ import { ElementSearchResultType } from '@/types/models';
 import { CommentItem } from '@/components/Map/Drawer/BioEntityDrawer/Comments/CommentItem.component';
 import { ModificationResidueItem } from '@/components/Map/Drawer/BioEntityDrawer/ModificationResidueItem';
 import React from 'react';
+import { projectDataSelector } from '@/redux/project/project.selectors';
+import { showToast } from '@/utils/showToast';
 import { CollapsibleSection } from '../ExportDrawer/CollapsibleSection';
 import { AnnotationItem } from './AnnotationItem';
 import { AssociatedSubmap } from './AssociatedSubmap';
@@ -31,8 +33,17 @@ export const BioEntityDrawer = (): React.ReactNode => {
   const relatedSubmap = useAppSelector(currentDrawerBioEntityRelatedSubmapSelector);
   const currentTargetId = bioEntityData?.id ? `${TARGET_PREFIX}:${bioEntityData.id}` : '';
 
+  const project = useAppSelector(projectDataSelector);
+
   const fetchChemicalsForTarget = (): void => {
-    dispatch(getChemicalsForBioEntityDrawerTarget(currentTargetId));
+    if (project === undefined || project.disease === null || project.disease === undefined) {
+      showToast({
+        type: 'info',
+        message: `Project disease not defined. Only projects with defined disease have chemical search available`,
+      });
+    } else {
+      dispatch(getChemicalsForBioEntityDrawerTarget(currentTargetId));
+    }
   };
   const fetchDrugsForTarget = (): void => {
     dispatch(getDrugsForBioEntityDrawerTarget(currentTargetId));
@@ -75,7 +86,7 @@ export const BioEntityDrawer = (): React.ReactNode => {
           </div>
         )}
         {bioEntityData.notes && (
-          <span className="[&_a]:visited:text-purple-600 [&_a]:text-blue-600 [&_a]:underline [&_a]:hover:text-blue-800">
+          <span className="[&_a]:text-blue-600 [&_a]:underline [&_a]:hover:text-blue-800">
             <hr className="border-b border-b-divide" />
             <div
               className="mt-2 text-sm font-normal"
diff --git a/src/models/disease.ts b/src/models/disease.ts
deleted file mode 100644
index b83325f7..00000000
--- a/src/models/disease.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { z } from 'zod';
-
-export const disease = z.object({
-  id: z.number().int().positive(),
-  link: z.string().nullable(),
-  type: z.string(),
-  resource: z.string(),
-  annotatorClassName: z.string(),
-});
diff --git a/src/models/generators/diseaseGenerator.ts b/src/models/generators/diseaseGenerator.ts
index 1e8ae525..83dabef6 100644
--- a/src/models/generators/diseaseGenerator.ts
+++ b/src/models/generators/diseaseGenerator.ts
@@ -1,8 +1,8 @@
 // eslint-disable-next-line import/no-extraneous-dependencies
 import { createFixture, Generator } from 'zod-fixture';
-import { disease } from '@/models/disease';
 import { ZOD_SEED } from '@/constants';
 import { ZodNullable } from 'zod';
+import { referenceSchema } from '@/models/referenceSchema';
 
 export const diseaseGenerator = Generator({
   schema: ZodNullable,
@@ -11,7 +11,7 @@ export const diseaseGenerator = Generator({
     return context.path.at(-1) === 'disease';
   },
   output: () =>
-    createFixture(disease, {
+    createFixture(referenceSchema, {
       seed: ZOD_SEED,
       array: { min: 2, max: 2 },
     }),
diff --git a/src/models/projectSchema.ts b/src/models/projectSchema.ts
index 39862947..070f7264 100644
--- a/src/models/projectSchema.ts
+++ b/src/models/projectSchema.ts
@@ -1,12 +1,12 @@
 import { z } from 'zod';
 import { licenseSchema } from '@/models/licenseSchema';
-import { disease } from './disease';
+import { referenceSchema } from '@/models/referenceSchema';
 import { organism } from './organism';
 import { overviewImageView } from './overviewImageView';
 
 export const projectSchema = z.object({
   version: z.string(),
-  disease: disease.nullable(),
+  disease: referenceSchema.nullable(),
   diseaseName: z.string().nullable(),
   organism: organism.nullable(),
   organismName: z.string().nullable(),
diff --git a/src/shared/Toast/Toast.component.tsx b/src/shared/Toast/Toast.component.tsx
index c8d7804c..6706cb84 100644
--- a/src/shared/Toast/Toast.component.tsx
+++ b/src/shared/Toast/Toast.component.tsx
@@ -2,23 +2,32 @@ import { twMerge } from 'tailwind-merge';
 import { Icon } from '../Icon';
 
 type ToastArgs = {
-  type: 'success' | 'error';
+  type: 'success' | 'error' | 'info';
   message: string;
   onDismiss: () => void;
 };
 
+const textColors = {
+  error: 'text-red-500',
+  success: 'text-green-500',
+  info: 'text-blue-500',
+};
+
+const bgColors = {
+  error: 'before:bg-red-500',
+  success: 'before:bg-green-500',
+  info: 'before:bg-blue-500',
+};
+
 export const Toast = ({ type, message, onDismiss }: ToastArgs): React.ReactNode => (
   <div
     className={twMerge(
       'flex h-[76px] w-[700px] items-center rounded-l rounded-r-lg bg-white p-4 drop-shadow before:absolute before:inset-y-0 before:left-0 before:block before:w-1 before:rounded-l-lg before:content-[""]',
-      type === 'error' ? 'before:bg-red-500' : 'before:bg-green-500',
+      bgColors[type],
     )}
   >
     <p
-      className={twMerge(
-        'h-full overflow-y-auto text-base font-bold',
-        type === 'error' ? 'text-red-500' : 'text-green-500',
-      )}
+      className={twMerge('h-full overflow-y-auto text-base font-bold', textColors[type])}
       dangerouslySetInnerHTML={{ __html: message }}
     />
 
diff --git a/src/types/models.ts b/src/types/models.ts
index a43b77bc..4148c3f7 100644
--- a/src/types/models.ts
+++ b/src/types/models.ts
@@ -9,7 +9,6 @@ import {
 } from '@/models/compartmentPathwaySchema';
 import { configurationOptionSchema } from '@/models/configurationOptionSchema';
 import { configurationSchema, formatSchema, miriamTypesSchema } from '@/models/configurationSchema';
-import { disease } from '@/models/disease';
 import { drugSchema } from '@/models/drugSchema';
 import { elementSearchResult, elementSearchResultType } from '@/models/elementSearchResult';
 import { exportElementsSchema, exportNetworkchema } from '@/models/exportSchema';
@@ -78,7 +77,6 @@ export type MapModel = z.infer<typeof mapModelSchema>;
 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>;
 export type PinDetailsItem = z.infer<typeof targetSchema>;
 export type BioEntity = z.infer<typeof bioEntitySchema>;
diff --git a/src/utils/showToast.tsx b/src/utils/showToast.tsx
index 19cf088e..101491ab 100644
--- a/src/utils/showToast.tsx
+++ b/src/utils/showToast.tsx
@@ -4,7 +4,7 @@ import { Toast } from '@/shared/Toast';
 const DEFAULT_DURATION_MS = 5000;
 
 type ShowToastArgs = {
-  type: 'success' | 'error';
+  type: 'success' | 'error' | 'info';
   message: string;
   duration?: number;
 };
-- 
GitLab


From 48ed731a99d2838e8b6c1ea3e4a6f7e15a858440 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Wed, 5 Feb 2025 11:35:16 +0100
Subject: [PATCH 03/17] overlay structures merged

---
 public/config.js                              |  2 ++
 .../UserOverlayForm.component.test.tsx        |  8 ++---
 src/models/fixtures/overlaysFixture.ts        | 14 +++-----
 .../{mapOverlay.ts => mapOverlaySchema.ts}    | 14 +-------
 src/redux/overlays/overlays.reducers.test.ts  |  9 ++---
 src/redux/overlays/overlays.thunks.ts         | 33 ++++++++++---------
 .../addDataOverlay/addDataOverlay.test.ts     |  4 +--
 .../pluginsEventBus/pluginsEventBus.test.ts   |  6 ++--
 .../pluginsEventBus/pluginsEventBus.ts        |  4 +--
 .../pluginsEventBus/pluginsEventBus.types.ts  | 10 +-----
 src/types/models.ts                           |  5 ++-
 11 files changed, 41 insertions(+), 68 deletions(-)
 rename src/models/{mapOverlay.ts => mapOverlaySchema.ts} (64%)

diff --git a/public/config.js b/public/config.js
index 5a90943d..dfa92d75 100644
--- a/public/config.js
+++ b/public/config.js
@@ -1,5 +1,7 @@
 // const root = 'https://minerva-dev.lcsb.uni.lu';
 // const root = 'https://scimap.lcsb.uni.lu';
+// const root = 'https://imsavar.elixir-luxembourg.org';
+// const root = 'https://pdmap.uni.lu';
 
 const root = 'https://lux1.atcomp.pl';
 // const root = 'http://localhost:8080';
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.test.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.test.tsx
index 35943150..be08de75 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.test.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.test.tsx
@@ -15,7 +15,7 @@ import { HttpStatusCode } from 'axios';
 import { apiPath } from '@/redux/apiPath';
 import {
   createdOverlayFileFixture,
-  createdOverlayFixture,
+  overlayFixture,
   overlaysPageFixture,
   uploadedOverlayFileContentFixture,
 } from '@/models/fixtures/overlaysFixture';
@@ -92,7 +92,7 @@ describe('UserOverlayForm - Component', () => {
 
     mockedAxiosClient
       .onPost(apiPath.createOverlay(projectFixture.projectId))
-      .reply(HttpStatusCode.Ok, createdOverlayFixture);
+      .reply(HttpStatusCode.Ok, overlayFixture);
 
     renderComponent({
       project: {
@@ -222,7 +222,7 @@ describe('UserOverlayForm - Component', () => {
 
     mockedAxiosClient
       .onPost(apiPath.createOverlay(projectFixture.projectId))
-      .reply(HttpStatusCode.Ok, createdOverlayFixture);
+      .reply(HttpStatusCode.Ok, overlayFixture);
 
     mockedAxiosNewClient
       .onGet(apiPath.getAllUserOverlaysByCreatorQuery({ creator: 'test', publicOverlay: false }))
@@ -288,7 +288,7 @@ describe('UserOverlayForm - Component', () => {
 
     mockedAxiosClient
       .onPost(apiPath.createOverlay(projectFixture.projectId))
-      .reply(HttpStatusCode.Ok, createdOverlayFixture);
+      .reply(HttpStatusCode.Ok, overlayFixture);
 
     mockedAxiosNewClient
       .onGet(apiPath.getAllUserOverlaysByCreatorQuery({ creator: 'test', publicOverlay: false }))
diff --git a/src/models/fixtures/overlaysFixture.ts b/src/models/fixtures/overlaysFixture.ts
index efb684ca..88ec2e6b 100644
--- a/src/models/fixtures/overlaysFixture.ts
+++ b/src/models/fixtures/overlaysFixture.ts
@@ -4,17 +4,16 @@ import { createFixture } from 'zod-fixture';
 import { pageableSchema } from '@/models/pageableSchema';
 import {
   createdOverlayFileSchema,
-  createdOverlaySchema,
-  mapOverlay,
+  mapOverlaySchema,
   uploadedOverlayFileContentSchema,
-} from '../mapOverlay';
+} from '../mapOverlaySchema';
 
-export const overlaysPageFixture = createFixture(pageableSchema(mapOverlay), {
+export const overlaysPageFixture = createFixture(pageableSchema(mapOverlaySchema), {
   seed: ZOD_SEED,
   array: { min: 2, max: 2 },
 });
 
-export const overlayFixture = createFixture(mapOverlay, {
+export const overlayFixture = createFixture(mapOverlaySchema, {
   seed: ZOD_SEED,
   array: { min: 1, max: 1 },
 });
@@ -26,11 +25,8 @@ export const createdOverlayFileFixture = createFixture(createdOverlayFileSchema,
 export const uploadedOverlayFileContentFixture = createFixture(uploadedOverlayFileContentSchema, {
   seed: ZOD_SEED,
 });
-export const createdOverlayFixture = createFixture(createdOverlaySchema, {
-  seed: ZOD_SEED,
-});
 
-export const emptyPageFixture = createFixture(pageableSchema(mapOverlay), {
+export const emptyPageFixture = createFixture(pageableSchema(mapOverlaySchema), {
   seed: ZOD_SEED,
   array: { min: 0, max: 0 },
 });
diff --git a/src/models/mapOverlay.ts b/src/models/mapOverlaySchema.ts
similarity index 64%
rename from src/models/mapOverlay.ts
rename to src/models/mapOverlaySchema.ts
index d9b645eb..d6460bfc 100644
--- a/src/models/mapOverlay.ts
+++ b/src/models/mapOverlaySchema.ts
@@ -1,7 +1,7 @@
 import { z } from 'zod';
 import { ZERO } from '@/constants/common';
 
-export const mapOverlay = z.object({
+export const mapOverlaySchema = z.object({
   idObject: z.number(),
   name: z.string(),
   order: z.number().int().gte(ZERO),
@@ -22,15 +22,3 @@ export const createdOverlayFileSchema = z.object({
 });
 
 export const uploadedOverlayFileContentSchema = createdOverlayFileSchema.extend({});
-
-export const createdOverlaySchema = z.object({
-  name: z.string(),
-  creator: z.string(),
-  description: z.string(),
-  genomeType: z.string().nullable(),
-  genomeVersion: z.string().nullable(),
-  idObject: z.number(),
-  publicOverlay: z.boolean(),
-  type: z.string(),
-  order: z.number(),
-});
diff --git a/src/redux/overlays/overlays.reducers.test.ts b/src/redux/overlays/overlays.reducers.test.ts
index fda71acd..f4945820 100644
--- a/src/redux/overlays/overlays.reducers.test.ts
+++ b/src/redux/overlays/overlays.reducers.test.ts
@@ -2,7 +2,6 @@
 import { PROJECT_ID } from '@/constants';
 import {
   createdOverlayFileFixture,
-  createdOverlayFixture,
   overlayFixture,
   overlaysPageFixture,
   uploadedOverlayFileContentFixture,
@@ -121,9 +120,7 @@ describe('overlays reducer', () => {
       .onPost(apiPath.uploadOverlayFileContent(123))
       .reply(HttpStatusCode.Ok, uploadedOverlayFileContentFixture);
 
-    mockedAxiosClient
-      .onPost(apiPath.createOverlay('pd'))
-      .reply(HttpStatusCode.Ok, createdOverlayFixture);
+    mockedAxiosClient.onPost(apiPath.createOverlay('pd')).reply(HttpStatusCode.Ok, overlayFixture);
 
     await store.dispatch(addOverlay(ADD_OVERLAY_MOCK));
     const { loading } = store.getState().overlays.addOverlay;
@@ -142,9 +139,7 @@ describe('overlays reducer', () => {
       .onPost(apiPath.uploadOverlayFileContent(123))
       .reply(HttpStatusCode.Ok, uploadedOverlayFileContentFixture);
 
-    mockedAxiosClient
-      .onPost(apiPath.createOverlay('pd'))
-      .reply(HttpStatusCode.Ok, createdOverlayFixture);
+    mockedAxiosClient.onPost(apiPath.createOverlay('pd')).reply(HttpStatusCode.Ok, overlayFixture);
 
     await store.dispatch(addOverlay(ADD_OVERLAY_MOCK));
     const { loading, error } = store.getState().overlays.addOverlay;
diff --git a/src/redux/overlays/overlays.thunks.ts b/src/redux/overlays/overlays.thunks.ts
index 57698bf4..5c87fa7f 100644
--- a/src/redux/overlays/overlays.thunks.ts
+++ b/src/redux/overlays/overlays.thunks.ts
@@ -1,12 +1,11 @@
 /* eslint-disable no-magic-numbers */
 import {
   createdOverlayFileSchema,
-  createdOverlaySchema,
-  mapOverlay,
+  mapOverlaySchema,
   uploadedOverlayFileContentSchema,
-} from '@/models/mapOverlay';
+} from '@/models/mapOverlaySchema';
 import { axiosInstance, axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
-import { CreatedOverlay, CreatedOverlayFile, MapOverlay, PageOf } from '@/types/models';
+import { CreatedOverlayFile, MapOverlay, PageOf } from '@/types/models';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { createAsyncThunk } from '@reduxjs/toolkit';
 import { z } from 'zod';
@@ -40,7 +39,10 @@ export const getAllPublicOverlaysByProjectId = createAsyncThunk<MapOverlay[], st
         apiPath.getAllOverlaysByProjectIdQuery(projectId, { publicOverlay: true }),
       );
 
-      const isDataValid = validateDataUsingZodSchema(response.data, pageableSchema(mapOverlay));
+      const isDataValid = validateDataUsingZodSchema(
+        response.data,
+        pageableSchema(mapOverlaySchema),
+      );
 
       return isDataValid ? response.data.content : [];
     } catch (error) {
@@ -67,7 +69,10 @@ export const getAllUserOverlaysByCreator = createAsyncThunk<MapOverlay[], void,
         },
       );
 
-      const isDataValid = validateDataUsingZodSchema(response.data, pageableSchema(mapOverlay));
+      const isDataValid = validateDataUsingZodSchema(
+        response.data,
+        pageableSchema(mapOverlaySchema),
+      );
 
       const sortByOrder = (userOverlayA: MapOverlay, userOverlayB: MapOverlay): number => {
         if (userOverlayA.order > userOverlayB.order) return 1;
@@ -158,7 +163,7 @@ const creteOverlay = async ({
   type,
   name,
   projectId,
-}: CreatedOverlayArgs): Promise<CreatedOverlay | undefined> => {
+}: CreatedOverlayArgs): Promise<MapOverlay | undefined> => {
   const data = {
     name,
     description,
@@ -169,17 +174,13 @@ const creteOverlay = async ({
 
   const overlay = new URLSearchParams(data);
 
-  const response = await axiosInstance.post<CreatedOverlay>(
-    apiPath.createOverlay(projectId),
-    overlay,
-    {
-      withCredentials: true,
-    },
-  );
+  const response = await axiosInstance.post<MapOverlay>(apiPath.createOverlay(projectId), overlay, {
+    withCredentials: true,
+  });
 
   PluginsEventBus.dispatchEvent('onAddDataOverlay', response.data);
 
-  const isDataValid = validateDataUsingZodSchema(response.data, createdOverlaySchema);
+  const isDataValid = validateDataUsingZodSchema(response.data, mapOverlaySchema);
 
   return isDataValid ? response.data : undefined;
 };
@@ -256,7 +257,7 @@ export const updateOverlays = createAsyncThunk<undefined, MapOverlay[], ThunkCon
         updatedUserOverlay => updatedUserOverlay.data,
       );
 
-      validateDataUsingZodSchema(updatedUserOverlays, z.array(mapOverlay));
+      validateDataUsingZodSchema(updatedUserOverlays, z.array(mapOverlaySchema));
 
       showToast({ type: 'success', message: USER_OVERLAY_UPDATE_SUCCESS_MESSAGE });
     } catch (error) {
diff --git a/src/services/pluginsManager/map/overlays/addDataOverlay/addDataOverlay.test.ts b/src/services/pluginsManager/map/overlays/addDataOverlay/addDataOverlay.test.ts
index 729d53cb..25e36dc9 100644
--- a/src/services/pluginsManager/map/overlays/addDataOverlay/addDataOverlay.test.ts
+++ b/src/services/pluginsManager/map/overlays/addDataOverlay/addDataOverlay.test.ts
@@ -1,6 +1,6 @@
 import {
   createdOverlayFileFixture,
-  createdOverlayFixture,
+  overlayFixture,
   uploadedOverlayFileContentFixture,
 } from '@/models/fixtures/overlaysFixture';
 import { projectFixture } from '@/models/fixtures/projectFixture';
@@ -56,7 +56,7 @@ describe('addDataOverlay', () => {
 
     mockedAxiosClient
       .onPost(apiPath.createOverlay(projectFixture.projectId))
-      .reply(HttpStatusCode.Ok, createdOverlayFixture);
+      .reply(HttpStatusCode.Ok, overlayFixture);
 
     getStateSpy.mockImplementation(() => MOCK_STATE as RootState);
 
diff --git a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.test.ts b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.test.ts
index c90919b4..af3b56bf 100644
--- a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.test.ts
+++ b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.test.ts
@@ -1,5 +1,4 @@
 /* eslint-disable no-magic-numbers */
-import { createdOverlayFixture } from '@/models/fixtures/overlaysFixture';
 import { RootState, store } from '@/redux/store';
 
 import { PLUGINS_MOCK } from '@/models/mocks/pluginsMock';
@@ -9,6 +8,7 @@ import {
   PLUGINS_INITIAL_STATE_MOCK,
 } from '@/redux/plugins/plugins.mock';
 import { showToast } from '@/utils/showToast';
+import { overlayFixture } from '@/models/fixtures/overlaysFixture';
 import { PluginsEventBus } from './pluginsEventBus';
 import { ERROR_INVALID_EVENT_TYPE, ERROR_PLUGIN_CRASH } from '../errorMessages';
 
@@ -39,14 +39,14 @@ describe('PluginsEventBus', () => {
   it('should dispatch event correctly', () => {
     const callback = jest.fn();
     PluginsEventBus.addListener(plugin.hash, plugin.name, 'onAddDataOverlay', callback);
-    PluginsEventBus.dispatchEvent('onAddDataOverlay', createdOverlayFixture);
+    PluginsEventBus.dispatchEvent('onAddDataOverlay', overlayFixture);
 
     expect(callback).toHaveBeenCalled();
   });
 
   it('should throw error if event type is incorrect', () => {
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    expect(() => PluginsEventBus.dispatchEvent('onData' as any, createdOverlayFixture)).toThrow(
+    expect(() => PluginsEventBus.dispatchEvent('onData' as any, overlayFixture)).toThrow(
       ERROR_INVALID_EVENT_TYPE('onData'),
     );
   });
diff --git a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts
index afd61150..6e9d3def 100644
--- a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts
+++ b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts
@@ -1,5 +1,5 @@
 /* eslint-disable no-magic-numbers */
-import { CreatedOverlay, MapOverlay } from '@/types/models';
+import { MapOverlay } from '@/types/models';
 import { showToast } from '@/utils/showToast';
 import { ERROR_INVALID_EVENT_TYPE, ERROR_PLUGIN_CRASH } from '../errorMessages';
 import {
@@ -22,7 +22,7 @@ import type {
 } from './pluginsEventBus.types';
 
 export function dispatchEvent(type: 'onPluginUnload', data: PluginUnloaded): void;
-export function dispatchEvent(type: 'onAddDataOverlay', createdOverlay: CreatedOverlay): void;
+export function dispatchEvent(type: 'onAddDataOverlay', createdOverlay: MapOverlay): void;
 export function dispatchEvent(type: 'onRemoveDataOverlay', overlayId: number): void;
 export function dispatchEvent(type: 'onShowOverlay', overlay: MapOverlay): void;
 export function dispatchEvent(type: 'onHideOverlay', overlay: MapOverlay): void;
diff --git a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts
index 935487c6..d7bc2d51 100644
--- a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts
+++ b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts
@@ -1,11 +1,4 @@
-import {
-  BioEntityContent,
-  Chemical,
-  CreatedOverlay,
-  Drug,
-  ElementSearchResult,
-  MapOverlay,
-} from '@/types/models';
+import { BioEntityContent, Chemical, Drug, ElementSearchResult, MapOverlay } from '@/types/models';
 import { dispatchEvent } from './pluginsEventBus';
 
 export type BackgroundEvents = 'onBackgroundOverlayChange';
@@ -88,7 +81,6 @@ export type SearchData =
   | SearchDataReaction;
 
 export type EventsData =
-  | CreatedOverlay
   | number
   | MapOverlay
   | ZoomChanged
diff --git a/src/types/models.ts b/src/types/models.ts
index 756f345a..b432fbfa 100644
--- a/src/types/models.ts
+++ b/src/types/models.ts
@@ -11,7 +11,7 @@ import { exportElementsSchema, exportNetworkchema } from '@/models/exportSchema'
 import { geneVariant } from '@/models/geneVariant';
 import { lineSchema } from '@/models/lineSchema';
 import { loginSchema } from '@/models/loginSchema';
-import { createdOverlayFileSchema, createdOverlaySchema, mapOverlay } from '@/models/mapOverlay';
+import { createdOverlayFileSchema, mapOverlaySchema } from '@/models/mapOverlaySchema';
 import {
   markerLineSchema,
   markerPinSchema,
@@ -100,7 +100,7 @@ export type Shape = z.infer<typeof shapeSchema>;
 export type ShapeRelAbs = z.infer<typeof shapeRelAbsSchema>;
 export type ShapeRelAbsBezierPoint = z.infer<typeof shapeRelAbsBezierPointSchema>;
 export type Modification = z.infer<typeof modelElementModificationSchema>;
-export type MapOverlay = z.infer<typeof mapOverlay>;
+export type MapOverlay = z.infer<typeof mapOverlaySchema>;
 export type Drug = z.infer<typeof drugSchema>;
 export type PinDetailsItem = z.infer<typeof targetSchema>;
 export type BioEntity = z.infer<typeof bioEntitySchema>;
@@ -129,7 +129,6 @@ export type OverlayLeftBioEntity = z.infer<typeof overlayLeftBioEntitySchema>;
 export type OverlayLeftReaction = z.infer<typeof overlayLeftReactionSchema>;
 export type Line = z.infer<typeof lineSchema>;
 export type CreatedOverlayFile = z.infer<typeof createdOverlayFileSchema>;
-export type CreatedOverlay = z.infer<typeof createdOverlaySchema>;
 export type Color = z.infer<typeof colorSchema>;
 export type Statistics = z.infer<typeof statisticsSchema>;
 export type Publication = z.infer<typeof publicationSchema>;
-- 
GitLab


From 86dc6ad206a880faf7a5757a96abaebe04cb9f25 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Wed, 5 Feb 2025 12:24:21 +0100
Subject: [PATCH 04/17] data overlay contains id (not idObject)

---
 .../EditOverlayModal.component.test.tsx       |  8 +--
 .../hooks/useEditOverlay.test.ts              |  2 +-
 .../EditOverlayModal/hooks/useEditOverlay.ts  |  2 +-
 .../GroupedOverlayAxes.components.test.tsx    |  2 +-
 .../GroupedOverlayAxes.components.tsx         |  4 +-
 .../OverlayData.component.test.tsx            |  6 +-
 .../OverlayData/OverlayData.component.tsx     |  2 +-
 .../OverlayData/utils/useOverlaysAxes.ts      |  2 +-
 .../GeneralOverlays.component.tsx             |  6 +-
 .../OverlaySingleLegend.component.test.tsx    |  2 +-
 .../OverlaySingleLegend.component.tsx         |  4 +-
 .../OverlaysLegends.component.test.tsx        |  4 +-
 .../OverlaysLegends.component.tsx             |  2 +-
 .../hooks/useUserOverlayActions.test.ts       |  2 +-
 .../hooks/useUserOverlayActions.ts            |  2 +-
 .../UserOverlayListItem.component.tsx         |  2 +-
 ...serOverlaysWithoutGroup.component.test.tsx |  4 +-
 .../UserOverlaysWithoutGroup.component.tsx    |  2 +-
 .../UserOverlaysWithoutGroup.utils.test.ts    | 45 +++++++++-----
 .../hooks/useUserOverlays.ts                  |  2 +-
 .../useBioEntitiesWithSubmapLinks.test.ts     |  2 +-
 src/models/mapOverlaySchema.ts                |  3 +-
 .../overlayBioEntity.selector.ts              |  6 +-
 .../overlayBioEntity.types.ts                 |  2 +-
 .../overlayBioEntity.utils.test.ts            | 60 +++++++++----------
 .../overlayBioEntity.utils.ts                 | 16 ++---
 src/redux/overlays/overlays.mock.ts           | 21 ++++---
 src/redux/overlays/overlays.reducers.test.ts  | 18 +++---
 src/redux/overlays/overlays.selectors.ts      |  8 +--
 src/redux/overlays/overlays.thunks.ts         |  2 +-
 .../overlays/getVisibleDataOverlays.test.ts   |  2 +-
 .../map/overlays/hideDataOverlay.test.ts      |  2 +-
 .../map/overlays/removeDataOverlay.test.ts    |  2 +-
 .../showDataOverlay/showDataOverlay.test.ts   |  2 +-
 .../map/overlays/types/DataOverlay.ts         |  5 +-
 35 files changed, 136 insertions(+), 120 deletions(-)

diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
index adacf77d..8140cfe6 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
@@ -110,7 +110,7 @@ describe('EditOverlayModal - component', () => {
       overlays: OVERLAYS_INITIAL_STATE_MOCK,
     });
     mockedAxiosClient
-      .onDelete(apiPath.removeOverlay(overlayFixture.idObject))
+      .onDelete(apiPath.removeOverlay(overlayFixture.id))
       .reply(HttpStatusCode.Ok, {});
 
     const removeButton = screen.getByTestId('remove-button');
@@ -148,7 +148,7 @@ describe('EditOverlayModal - component', () => {
       overlays: OVERLAYS_INITIAL_STATE_MOCK,
     });
     mockedAxiosClient
-      .onDelete(apiPath.removeOverlay(overlayFixture.idObject))
+      .onDelete(apiPath.removeOverlay(overlayFixture.id))
       .reply(HttpStatusCode.Ok, {});
 
     const removeButton = screen.getByTestId('remove-button');
@@ -187,7 +187,7 @@ describe('EditOverlayModal - component', () => {
       overlays: OVERLAYS_INITIAL_STATE_MOCK,
     });
     mockedAxiosClient
-      .onPatch(apiPath.updateOverlay(overlayFixture.idObject))
+      .onPatch(apiPath.updateOverlay(overlayFixture.id))
       .reply(HttpStatusCode.Ok, overlayFixture);
 
     const page = {
@@ -234,7 +234,7 @@ describe('EditOverlayModal - component', () => {
       overlays: OVERLAYS_INITIAL_STATE_MOCK,
     });
     mockedAxiosClient
-      .onPatch(apiPath.updateOverlay(overlayFixture.idObject))
+      .onPatch(apiPath.updateOverlay(overlayFixture.id))
       .reply(HttpStatusCode.Ok, overlayFixture);
 
     const saveButton = screen.getByTestId('save-button');
diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts
index a474e15f..bb85187c 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts
@@ -85,7 +85,7 @@ describe('useEditOverlay', () => {
 
     const { overlayId } = actions[0].meta.arg;
 
-    expect(overlayId).toBe(overlayFixture.idObject);
+    expect(overlayId).toBe(overlayFixture.id);
   });
   it('should not handle handleRemoveOverlay if proper data is not provided', () => {
     const { Wrapper, store } = getReduxStoreWithActionsListener({
diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts
index a8d02776..6012da5d 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts
@@ -48,7 +48,7 @@ export const useEditOverlay = (): UseEditOverlayReturn => {
 
   const handleRemoveOverlay = (): void => {
     if (!login || !currentEditedOverlay) return;
-    dispatch(removeOverlay({ overlayId: currentEditedOverlay.idObject }));
+    dispatch(removeOverlay({ overlayId: currentEditedOverlay.id }));
   };
 
   const handleUpdateOverlay = async ({
diff --git a/src/components/Map/Drawer/BioEntityDrawer/OverlayData/GroupedOverlayAxes/GroupedOverlayAxes.components.test.tsx b/src/components/Map/Drawer/BioEntityDrawer/OverlayData/GroupedOverlayAxes/GroupedOverlayAxes.components.test.tsx
index 8f073a57..c6f35311 100644
--- a/src/components/Map/Drawer/BioEntityDrawer/OverlayData/GroupedOverlayAxes/GroupedOverlayAxes.components.test.tsx
+++ b/src/components/Map/Drawer/BioEntityDrawer/OverlayData/GroupedOverlayAxes/GroupedOverlayAxes.components.test.tsx
@@ -11,7 +11,7 @@ const BASE_AXIS: OverlayDataAxis = {
   value: 2137,
   color: '#FFFFFF',
   geneVariants: undefined,
-  overlayId: overlayFixture.idObject,
+  overlayId: overlayFixture.id,
 };
 
 const renderComponent = (axes: OverlayDataAxis[], overlay: MapOverlay): void => {
diff --git a/src/components/Map/Drawer/BioEntityDrawer/OverlayData/GroupedOverlayAxes/GroupedOverlayAxes.components.tsx b/src/components/Map/Drawer/BioEntityDrawer/OverlayData/GroupedOverlayAxes/GroupedOverlayAxes.components.tsx
index de4e9208..4a8fac47 100644
--- a/src/components/Map/Drawer/BioEntityDrawer/OverlayData/GroupedOverlayAxes/GroupedOverlayAxes.components.tsx
+++ b/src/components/Map/Drawer/BioEntityDrawer/OverlayData/GroupedOverlayAxes/GroupedOverlayAxes.components.tsx
@@ -11,8 +11,8 @@ interface Props {
 }
 
 export const GroupedOverlayAxes = ({ overlay, axes }: Props): JSX.Element | null => {
-  const { idObject, name } = overlay;
-  const overlayAxes = axes.filter(axis => axis.overlayId === idObject);
+  const { id, name } = overlay;
+  const overlayAxes = axes.filter(axis => axis.overlayId === id);
   const sortedAxes = useMemo(() => getAxesSortedByValue(overlayAxes), [overlayAxes]);
 
   if (overlayAxes.length === SIZE_OF_EMPTY_ARRAY) {
diff --git a/src/components/Map/Drawer/BioEntityDrawer/OverlayData/OverlayData.component.test.tsx b/src/components/Map/Drawer/BioEntityDrawer/OverlayData/OverlayData.component.test.tsx
index c03ea142..d57a9db9 100644
--- a/src/components/Map/Drawer/BioEntityDrawer/OverlayData/OverlayData.component.test.tsx
+++ b/src/components/Map/Drawer/BioEntityDrawer/OverlayData/OverlayData.component.test.tsx
@@ -48,7 +48,7 @@ describe('OverlayData - component', () => {
 
   describe('when axes list is present', () => {
     beforeEach(() => {
-      const OVERLAY_ID = overlayFixture.idObject;
+      const OVERLAY_ID = overlayFixture.id;
       const BIO_ENTITY = MOCKED_OVERLAY_BIO_ENTITY_RENDER[0];
 
       renderComponent({
@@ -119,7 +119,7 @@ describe('OverlayData - component', () => {
 
   describe('when axes list is present and isShowGroupedOverlays=true', () => {
     beforeEach(() => {
-      const OVERLAY_ID = overlayFixture.idObject;
+      const OVERLAY_ID = overlayFixture.id;
       const BIO_ENTITY = MOCKED_OVERLAY_BIO_ENTITY_RENDER[0];
 
       renderComponent(
@@ -195,7 +195,7 @@ describe('OverlayData - component', () => {
 
   describe('when axes list is present and isShowOverlayBioEntityName=true', () => {
     beforeEach(() => {
-      const OVERLAY_ID = overlayFixture.idObject;
+      const OVERLAY_ID = overlayFixture.id;
       const BIO_ENTITY = MOCKED_OVERLAY_BIO_ENTITY_RENDER[0];
 
       renderComponent(
diff --git a/src/components/Map/Drawer/BioEntityDrawer/OverlayData/OverlayData.component.tsx b/src/components/Map/Drawer/BioEntityDrawer/OverlayData/OverlayData.component.tsx
index bc52903a..4076b7b1 100644
--- a/src/components/Map/Drawer/BioEntityDrawer/OverlayData/OverlayData.component.tsx
+++ b/src/components/Map/Drawer/BioEntityDrawer/OverlayData/OverlayData.component.tsx
@@ -37,7 +37,7 @@ export const OverlayData = ({
   const groupedOverlayAxesContent = (
     <>
       {openedOverlays.map(overlay => (
-        <GroupedOverlayAxes key={overlay.idObject} overlay={overlay} axes={uniqueAxes} />
+        <GroupedOverlayAxes key={overlay.id} overlay={overlay} axes={uniqueAxes} />
       ))}
     </>
   );
diff --git a/src/components/Map/Drawer/BioEntityDrawer/OverlayData/utils/useOverlaysAxes.ts b/src/components/Map/Drawer/BioEntityDrawer/OverlayData/utils/useOverlaysAxes.ts
index 69bbcbe9..3e6e57b0 100644
--- a/src/components/Map/Drawer/BioEntityDrawer/OverlayData/utils/useOverlaysAxes.ts
+++ b/src/components/Map/Drawer/BioEntityDrawer/OverlayData/utils/useOverlaysAxes.ts
@@ -25,7 +25,7 @@ export const useOverlaysAxes = ({
   });
 
   return currentBioEntityOverlaysForCurrentBioEntity.map((overlayBioEntity): OverlayDataAxis => {
-    const overlay = openedOverlays.find(o => o.idObject === overlayBioEntity.overlayId);
+    const overlay = openedOverlays.find(o => o.id === overlayBioEntity.overlayId);
 
     return {
       id: overlayBioEntity.id,
diff --git a/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/GeneralOverlays.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/GeneralOverlays.component.tsx
index 9831bce5..e8442c37 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/GeneralOverlays.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/GeneralOverlays.component.tsx
@@ -10,11 +10,7 @@ export const GeneralOverlays = (): JSX.Element => {
       <p className="mb-5 text-sm font-semibold">Shared Overlays:</p>
       <ul>
         {generalPublicOverlays.map(overlay => (
-          <OverlayListItem
-            key={overlay.idObject}
-            name={overlay.name}
-            overlayId={overlay.idObject}
-          />
+          <OverlayListItem key={overlay.id} name={overlay.name} overlayId={overlay.id} />
         ))}
       </ul>
     </div>
diff --git a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.test.tsx b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.test.tsx
index 5d881550..059d4d2c 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.test.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.test.tsx
@@ -14,7 +14,7 @@ describe('OverlaySingleLegend - component', () => {
       overlay: {
         ...overlayFixture,
         name: 'overlay name',
-        idObject: 1234,
+        id: 1234,
       },
     });
   });
diff --git a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.tsx
index 77169eea..623fc31e 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.tsx
@@ -3,12 +3,12 @@ import { BASE_API_URL, PROJECT_ID } from '@/constants';
 import { MapOverlay } from '@/types/models';
 
 interface Props {
-  overlay: Pick<MapOverlay, 'name' | 'idObject'>;
+  overlay: Pick<MapOverlay, 'name' | 'id'>;
 }
 
 export const OverlaySingleLegend = ({ overlay }: Props): JSX.Element => {
   const overlayName = overlay.name;
-  const overlayImageSrc = `${BASE_API_URL}/projects/${PROJECT_ID}/overlays/${overlay.idObject}:downloadLegend`;
+  const overlayImageSrc = `${BASE_API_URL}/projects/${PROJECT_ID}/overlays/${overlay.id}:downloadLegend`;
 
   return (
     <div>
diff --git a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.test.tsx b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.test.tsx
index 94449da6..0cd04073 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.test.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.test.tsx
@@ -49,7 +49,7 @@ describe('OverlaysLegends - component', () => {
         overlays: OVERLAYS_PUBLIC_FETCHED_STATE_MOCK,
         overlayBioEntity: {
           ...OVERLAY_BIO_ENTITY_INITIAL_STATE_MOCK,
-          overlaysId: PUBLIC_OVERLAYS_MOCK.map(o => o.idObject),
+          overlaysId: PUBLIC_OVERLAYS_MOCK.map(o => o.id),
         },
       });
     });
@@ -60,7 +60,7 @@ describe('OverlaysLegends - component', () => {
       expect(screen.getByText(overlay.name)).toBeInTheDocument();
       expect(image).toBeInTheDocument();
       expect(image.getAttribute('src')).toBe(
-        `${BASE_API_URL}/projects/${PROJECT_ID}/overlays/${overlay.idObject}:downloadLegend`,
+        `${BASE_API_URL}/projects/${PROJECT_ID}/overlays/${overlay.id}:downloadLegend`,
       );
     });
   });
diff --git a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.tsx
index b9f55675..8e183970 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.tsx
@@ -8,7 +8,7 @@ export const OverlaysLegends = (): JSX.Element => {
   return (
     <div className="border-t border-t-divide p-6" data-testid="overlays-legends">
       {overlays.map(overlay => (
-        <OverlaySingleLegend key={overlay.idObject} overlay={overlay} />
+        <OverlaySingleLegend key={overlay.id} overlay={overlay} />
       ))}
     </div>
   );
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.test.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.test.ts
index b7e7135b..7a85f041 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.test.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.test.ts
@@ -48,7 +48,7 @@ describe('useUserOverlayActions', () => {
     });
 
     expect(windowOpenMock).toHaveBeenCalledWith(
-      `${BASE_API_URL}/${apiPath.downloadOverlay(overlayFixture.idObject)}`,
+      `${BASE_API_URL}/${apiPath.downloadOverlay(overlayFixture.id)}`,
       '_blank',
     );
   });
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.ts
index 1b10cb09..64688f76 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.ts
@@ -13,7 +13,7 @@ export const useUserOverlayActions = (overlay: MapOverlay): UseUserOverlayAction
   const dispatch = useAppDispatch();
 
   const handleDownloadOverlay = (): void => {
-    window.open(`${BASE_API_URL}/${apiPath.downloadOverlay(overlay.idObject)}`, '_blank');
+    window.open(`${BASE_API_URL}/${apiPath.downloadOverlay(overlay.id)}`, '_blank');
   };
 
   const handleEditOverlay = (): void => {
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayListItem.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayListItem.component.tsx
index 0eb20df6..d67e3b47 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayListItem.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayListItem.component.tsx
@@ -21,7 +21,7 @@ export const UserOverlayListItem = ({
   userOverlay,
   updateUserOverlaysOrder,
 }: OverlayListItemProps): JSX.Element => {
-  const { toggleOverlay, isOverlayActive, isOverlayLoading } = useOverlay(userOverlay.idObject);
+  const { toggleOverlay, isOverlayActive, isOverlayLoading } = useOverlay(userOverlay.id);
   const { dragRef, dropRef, isDragging } = useDragAndDrop({
     onDrop: updateUserOverlaysOrder,
     onHover: moveUserOverlay,
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.test.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.test.tsx
index 6cc8c8ea..f4145e11 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.test.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.test.tsx
@@ -327,12 +327,12 @@ describe('UserOverlaysWithoutGroup - component', () => {
     });
 
     expect(windowOpenMock).toHaveBeenCalledWith(
-      `${BASE_API_URL}/${apiPath.downloadOverlay(overlayFixture.idObject)}`,
+      `${BASE_API_URL}/${apiPath.downloadOverlay(overlayFixture.id)}`,
       '_blank',
     );
   });
   it('should display spinner icon if user overlay is loading', async () => {
-    const OVERLAY_ID = overlayFixture.idObject;
+    const OVERLAY_ID = overlayFixture.id;
     renderComponent({
       modal: MODAL_INITIAL_STATE_MOCK,
       user: {
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.tsx
index ad61cc51..ef571ad6 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.tsx
@@ -32,7 +32,7 @@ export const UserOverlaysWithoutGroup = (): React.ReactNode => {
                   {userOverlaysList?.map((userOverlay, index) => (
                     <UserOverlayListItem
                       moveUserOverlay={moveUserOverlayListItem}
-                      key={userOverlay.idObject}
+                      key={userOverlay.id}
                       index={index}
                       userOverlay={userOverlay}
                       updateUserOverlaysOrder={updateUserOverlaysOrder}
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.utils.test.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.utils.test.ts
index fd5bf60f..d454fd22 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.utils.test.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.utils.test.ts
@@ -4,35 +4,38 @@ import { moveArrayElement } from './UserOverlaysWithoutGroup.utils';
 
 const INPUT_ARRAY: MapOverlay[] = [
   {
+    id: 1,
+    group: null,
     name: 'Overlay1',
     description: 'Description1',
     type: 'Type1',
     creator: 'Creator1',
     genomeType: 'GenomeType1',
     genomeVersion: 'GenomeVersion1',
-    idObject: 1,
     publicOverlay: false,
     order: 1,
   },
   {
+    id: 2,
+    group: null,
     name: 'Overlay2',
     description: 'Description2',
     type: 'Type2',
     creator: 'Creator2',
     genomeType: 'GenomeType2',
     genomeVersion: 'GenomeVersion2',
-    idObject: 2,
     publicOverlay: true,
     order: 2,
   },
   {
+    id: 3,
+    group: null,
     name: 'Overlay3',
     description: 'Description3',
     type: 'Type3',
     creator: 'Creator3',
     genomeType: 'GenomeType3',
     genomeVersion: 'GenomeVersion3',
-    idObject: 3,
     publicOverlay: false,
     order: 3,
   },
@@ -42,35 +45,38 @@ describe('moveArrayElement', () => {
   it('should move an element down in the array', () => {
     const expectedResult: MapOverlay[] = [
       {
+        id: 1,
+        group: null,
         name: 'Overlay1',
         description: 'Description1',
         type: 'Type1',
         creator: 'Creator1',
         genomeType: 'GenomeType1',
         genomeVersion: 'GenomeVersion1',
-        idObject: 1,
         publicOverlay: false,
         order: 1,
       },
       {
+        id: 3,
+        group: null,
         name: 'Overlay3',
         description: 'Description3',
         type: 'Type3',
         creator: 'Creator3',
         genomeType: 'GenomeType3',
         genomeVersion: 'GenomeVersion3',
-        idObject: 3,
         publicOverlay: false,
         order: 3,
       },
       {
+        id: 2,
+        group: null,
         name: 'Overlay2',
         description: 'Description2',
         type: 'Type2',
         creator: 'Creator2',
         genomeType: 'GenomeType2',
         genomeVersion: 'GenomeVersion2',
-        idObject: 2,
         publicOverlay: true,
         order: 2,
       },
@@ -84,35 +90,38 @@ describe('moveArrayElement', () => {
   it('should move an element up in the array', () => {
     const expectedResult: MapOverlay[] = [
       {
+        id: 1,
+        group: null,
         name: 'Overlay1',
         description: 'Description1',
         type: 'Type1',
         creator: 'Creator1',
         genomeType: 'GenomeType1',
         genomeVersion: 'GenomeVersion1',
-        idObject: 1,
         publicOverlay: false,
         order: 1,
       },
       {
+        id: 3,
+        group: null,
         name: 'Overlay3',
         description: 'Description3',
         type: 'Type3',
         creator: 'Creator3',
         genomeType: 'GenomeType3',
         genomeVersion: 'GenomeVersion3',
-        idObject: 3,
         publicOverlay: false,
         order: 3,
       },
       {
+        id: 2,
+        group: null,
         name: 'Overlay2',
         description: 'Description2',
         type: 'Type2',
         creator: 'Creator2',
         genomeType: 'GenomeType2',
         genomeVersion: 'GenomeVersion2',
-        idObject: 2,
         publicOverlay: true,
         order: 2,
       },
@@ -126,35 +135,38 @@ describe('moveArrayElement', () => {
   it('should handle moving an element to the beginning of the array', () => {
     const expectedResult: MapOverlay[] = [
       {
+        id: 3,
+        group: null,
         name: 'Overlay3',
         description: 'Description3',
         type: 'Type3',
         creator: 'Creator3',
         genomeType: 'GenomeType3',
         genomeVersion: 'GenomeVersion3',
-        idObject: 3,
         publicOverlay: false,
         order: 3,
       },
       {
+        id: 1,
+        group: null,
         name: 'Overlay1',
         description: 'Description1',
         type: 'Type1',
         creator: 'Creator1',
         genomeType: 'GenomeType1',
         genomeVersion: 'GenomeVersion1',
-        idObject: 1,
         publicOverlay: false,
         order: 1,
       },
       {
+        id: 2,
+        group: null,
         name: 'Overlay2',
         description: 'Description2',
         type: 'Type2',
         creator: 'Creator2',
         genomeType: 'GenomeType2',
         genomeVersion: 'GenomeVersion2',
-        idObject: 2,
         publicOverlay: true,
         order: 2,
       },
@@ -168,35 +180,38 @@ describe('moveArrayElement', () => {
   it('should handle moving an element to the end of the array', () => {
     const expectedResult: MapOverlay[] = [
       {
+        id: 2,
+        group: null,
         name: 'Overlay2',
         description: 'Description2',
         type: 'Type2',
         creator: 'Creator2',
         genomeType: 'GenomeType2',
         genomeVersion: 'GenomeVersion2',
-        idObject: 2,
         publicOverlay: true,
         order: 2,
       },
       {
+        id: 3,
+        group: null,
         name: 'Overlay3',
         description: 'Description3',
         type: 'Type3',
         creator: 'Creator3',
         genomeType: 'GenomeType3',
         genomeVersion: 'GenomeVersion3',
-        idObject: 3,
         publicOverlay: false,
         order: 3,
       },
       {
+        id: 1,
+        group: null,
         name: 'Overlay1',
         description: 'Description1',
         type: 'Type1',
         creator: 'Creator1',
         genomeType: 'GenomeType1',
         genomeVersion: 'GenomeVersion1',
-        idObject: 1,
         publicOverlay: false,
         order: 1,
       },
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/hooks/useUserOverlays.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/hooks/useUserOverlays.ts
index 7d0114eb..722ea7de 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/hooks/useUserOverlays.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/hooks/useUserOverlays.ts
@@ -46,7 +46,7 @@ export const useUserOverlays = (): UseUserOverlaysReturn => {
         order: index + 1,
       };
 
-      if (userOverlay.idObject !== newOrderedUserOverlay.idObject) {
+      if (userOverlay.id !== newOrderedUserOverlay.id) {
         reorderedUserOverlays.push(newOrderedUserOverlay);
       }
     }
diff --git a/src/components/Map/MapViewer/utils/config/overlaysLayer/useBioEntitiesWithSubmapLinks.test.ts b/src/components/Map/MapViewer/utils/config/overlaysLayer/useBioEntitiesWithSubmapLinks.test.ts
index 7a0643c5..9dc07fc4 100644
--- a/src/components/Map/MapViewer/utils/config/overlaysLayer/useBioEntitiesWithSubmapLinks.test.ts
+++ b/src/components/Map/MapViewer/utils/config/overlaysLayer/useBioEntitiesWithSubmapLinks.test.ts
@@ -180,7 +180,7 @@ describe('useBioEntitiesWithSubmapsLinks', () => {
     const { Wrapper } = getReduxStoreWithActionsListener({
       overlayBioEntity: {
         ...OVERLAY_BIO_ENTITY_INITIAL_STATE_MOCK,
-        overlaysId: PUBLIC_OVERLAYS_MOCK.map(o => o.idObject),
+        overlaysId: PUBLIC_OVERLAYS_MOCK.map(o => o.id),
       },
       configuration: CONFIGURATION_INITIAL_STORE_MOCKS,
       modelElements: {
diff --git a/src/models/mapOverlaySchema.ts b/src/models/mapOverlaySchema.ts
index d6460bfc..d122a0bb 100644
--- a/src/models/mapOverlaySchema.ts
+++ b/src/models/mapOverlaySchema.ts
@@ -2,7 +2,8 @@ import { z } from 'zod';
 import { ZERO } from '@/constants/common';
 
 export const mapOverlaySchema = z.object({
-  idObject: z.number(),
+  id: z.number().int().positive(),
+  group: z.number().int().positive().nullable(),
   name: z.string(),
   order: z.number().int().gte(ZERO),
   creator: z.string(),
diff --git a/src/redux/overlayBioEntity/overlayBioEntity.selector.ts b/src/redux/overlayBioEntity/overlayBioEntity.selector.ts
index a7c7e93b..764b4cf8 100644
--- a/src/redux/overlayBioEntity/overlayBioEntity.selector.ts
+++ b/src/redux/overlayBioEntity/overlayBioEntity.selector.ts
@@ -112,10 +112,10 @@ export const activeOverlaysSelector = createSelector(
   userOverlaysDataSelector,
   (state, overlaysData, userOverlaysData) => {
     const activeOverlays = overlaysData.filter(overlay =>
-      isOverlayActiveSelector(state, overlay.idObject),
+      isOverlayActiveSelector(state, overlay.id),
     );
     const activeUserOverlays =
-      userOverlaysData?.filter(overlay => isOverlayActiveSelector(state, overlay.idObject)) || [];
+      userOverlaysData?.filter(overlay => isOverlayActiveSelector(state, overlay.id)) || [];
 
     return [...activeOverlays, ...activeUserOverlays];
   },
@@ -149,7 +149,7 @@ export const overlaysOpenedIdsSelector = createSelector(
 export const overlaysOpenedSelector = createSelector(
   overlaysDataSelector,
   overlaysOpenedIdsSelector,
-  (data, ids) => data.filter(entity => ids.includes(entity.idObject)),
+  (data, ids) => data.filter(entity => ids.includes(entity.id)),
 );
 
 export const overlaysBioEntityForCurrentBioEntityAndCurrentModelSelector = createSelector(
diff --git a/src/redux/overlayBioEntity/overlayBioEntity.types.ts b/src/redux/overlayBioEntity/overlayBioEntity.types.ts
index 2733514a..cd06daa1 100644
--- a/src/redux/overlayBioEntity/overlayBioEntity.types.ts
+++ b/src/redux/overlayBioEntity/overlayBioEntity.types.ts
@@ -15,6 +15,6 @@ export type RemoveOverlayBioEntityForGivenOverlayAction =
   PayloadAction<RemoveOverlayBioEntityForGivenOverlayPayload>;
 
 export type OverlaysIdsAndOrder = {
-  idObject: number;
+  id: number;
   order: number;
 }[];
diff --git a/src/redux/overlayBioEntity/overlayBioEntity.utils.test.ts b/src/redux/overlayBioEntity/overlayBioEntity.utils.test.ts
index d4248b65..81487dd6 100644
--- a/src/redux/overlayBioEntity/overlayBioEntity.utils.test.ts
+++ b/src/redux/overlayBioEntity/overlayBioEntity.utils.test.ts
@@ -12,9 +12,9 @@ describe('calculateOverlaysOrder', () => {
   const cases = [
     {
       data: [
-        { idObject: 1, order: 11 },
-        { idObject: 2, order: 12 },
-        { idObject: 3, order: 13 },
+        { id: 1, order: 11 },
+        { id: 2, order: 12 },
+        { id: 3, order: 13 },
       ],
       expected: [
         { id: 1, order: 11, calculatedOrder: 1, index: 0 },
@@ -25,9 +25,9 @@ describe('calculateOverlaysOrder', () => {
     // different order
     {
       data: [
-        { idObject: 2, order: 12 },
-        { idObject: 3, order: 13 },
-        { idObject: 1, order: 11 },
+        { id: 2, order: 12 },
+        { id: 3, order: 13 },
+        { id: 1, order: 11 },
       ],
       expected: [
         { id: 1, order: 11, calculatedOrder: 1, index: 0 },
@@ -37,9 +37,9 @@ describe('calculateOverlaysOrder', () => {
     },
     {
       data: [
-        { idObject: 1, order: 11 },
-        { idObject: 2, order: 11 },
-        { idObject: 3, order: 11 },
+        { id: 1, order: 11 },
+        { id: 2, order: 11 },
+        { id: 3, order: 11 },
       ],
       expected: [
         { id: 1, order: 11, calculatedOrder: 1, index: 0 },
@@ -50,9 +50,9 @@ describe('calculateOverlaysOrder', () => {
     // different order
     {
       data: [
-        { idObject: 2, order: 11 },
-        { idObject: 3, order: 11 },
-        { idObject: 1, order: 11 },
+        { id: 2, order: 11 },
+        { id: 3, order: 11 },
+        { id: 1, order: 11 },
       ],
       expected: [
         { id: 1, order: 11, calculatedOrder: 1, index: 0 },
@@ -107,10 +107,10 @@ describe('getValidOverlayBioEntities', () => {
 
 describe('getActiveUserOverlaysIdsAndOrder', () => {
   const userOverlaysIdsAndOrder = [
-    { idObject: 1, order: 1 },
-    { idObject: 2, order: 2 },
-    { idObject: 3, order: 3 },
-    { idObject: 4, order: 4 },
+    { id: 1, order: 1 },
+    { id: 2, order: 2 },
+    { id: 3, order: 3 },
+    { id: 4, order: 4 },
   ];
 
   const activeOverlaysIds = [2, 4];
@@ -118,8 +118,8 @@ describe('getActiveUserOverlaysIdsAndOrder', () => {
 
   it('should return active user overlays with updated order values', () => {
     const expectedOutput = [
-      { idObject: 2, order: 12 },
-      { idObject: 4, order: 14 },
+      { id: 2, order: 12 },
+      { id: 4, order: 14 },
     ];
     expect(
       getActiveUserOverlaysIdsAndOrder(userOverlaysIdsAndOrder, activeOverlaysIds, maxOrderValue),
@@ -135,10 +135,10 @@ describe('getActiveUserOverlaysIdsAndOrder', () => {
   it('should return all user overlays with updated order values when all overlays are active', () => {
     const allOverlaysActive = [1, 2, 3, 4];
     const expectedOutput = [
-      { idObject: 1, order: 11 },
-      { idObject: 2, order: 12 },
-      { idObject: 3, order: 13 },
-      { idObject: 4, order: 14 },
+      { id: 1, order: 11 },
+      { id: 2, order: 12 },
+      { id: 3, order: 13 },
+      { id: 4, order: 14 },
     ];
     expect(
       getActiveUserOverlaysIdsAndOrder(userOverlaysIdsAndOrder, allOverlaysActive, maxOrderValue),
@@ -148,8 +148,8 @@ describe('getActiveUserOverlaysIdsAndOrder', () => {
   it('should return active user overlays with order values starting from 1 when maxOrderValue is 0', () => {
     const maxOrderZero = 0;
     const expectedOutput = [
-      { idObject: 2, order: 2 },
-      { idObject: 4, order: 4 },
+      { id: 2, order: 2 },
+      { id: 4, order: 4 },
     ];
     expect(
       getActiveUserOverlaysIdsAndOrder(userOverlaysIdsAndOrder, activeOverlaysIds, maxOrderZero),
@@ -159,10 +159,10 @@ describe('getActiveUserOverlaysIdsAndOrder', () => {
 
 describe('getActiveOverlaysIdsAndOrder', () => {
   const overlaysIdsAndOrder = [
-    { idObject: 1, order: 1 },
-    { idObject: 2, order: 2 },
-    { idObject: 3, order: 3 },
-    { idObject: 4, order: 4 },
+    { id: 1, order: 1 },
+    { id: 2, order: 2 },
+    { id: 3, order: 3 },
+    { id: 4, order: 4 },
   ];
 
   const activeOverlaysIds = [2, 4];
@@ -171,8 +171,8 @@ describe('getActiveOverlaysIdsAndOrder', () => {
     const expectedOutput = {
       maxOrderValue: 4,
       activeOverlaysIdsAndOrder: [
-        { idObject: 2, order: 2 },
-        { idObject: 4, order: 4 },
+        { id: 2, order: 2 },
+        { id: 4, order: 4 },
       ],
     };
     expect(getActiveOverlaysIdsAndOrder(overlaysIdsAndOrder, activeOverlaysIds)).toEqual(
diff --git a/src/redux/overlayBioEntity/overlayBioEntity.utils.ts b/src/redux/overlayBioEntity/overlayBioEntity.utils.ts
index f1686c8e..fa40d307 100644
--- a/src/redux/overlayBioEntity/overlayBioEntity.utils.ts
+++ b/src/redux/overlayBioEntity/overlayBioEntity.utils.ts
@@ -86,7 +86,7 @@ export const parseOverlayBioEntityToOlRenderingFormat = (
   }, []);
 
 export type OverlayIdAndOrder = {
-  idObject: number;
+  id: number;
   order: number;
 };
 
@@ -108,8 +108,8 @@ const byOrderOrId = (a: OverlayOrder, b: OverlayOrder): number => {
 export const calculateOvarlaysOrder = (
   overlaysIdsAndOrder: OverlayIdAndOrder[],
 ): OverlayOrder[] => {
-  const overlaysOrder = overlaysIdsAndOrder.map(({ idObject, order }, index) => ({
-    id: idObject,
+  const overlaysOrder = overlaysIdsAndOrder.map(({ id, order }, index) => ({
+    id,
     order,
     calculatedOrder: 0,
     index,
@@ -157,12 +157,12 @@ export const getActiveOverlaysIdsAndOrder = (
   activeOverlaysIds: number[],
 ): GetActiveOverlaysIdsAndOrderReturnType => {
   let maxOrderValue = -Infinity;
-  const activeOverlaysIdsAndOrder = overlaysIdsAndOrder.filter(({ idObject }) =>
-    activeOverlaysIds.includes(idObject),
+  const activeOverlaysIdsAndOrder = overlaysIdsAndOrder.filter(({ id }) =>
+    activeOverlaysIds.includes(id),
   );
 
-  overlaysIdsAndOrder.forEach(({ idObject, order }) => {
-    const isActive = activeOverlaysIds.includes(idObject);
+  overlaysIdsAndOrder.forEach(({ id, order }) => {
+    const isActive = activeOverlaysIds.includes(id);
     if (isActive && order > maxOrderValue) maxOrderValue = order;
   });
 
@@ -186,7 +186,7 @@ export const getActiveUserOverlaysIdsAndOrder = (
   );
 
   const activeUserOverlaysIdsAndOrder = clonedUserOverlaysIdsAndOrder.filter(userOverlay => {
-    const isActive = activeOverlaysIds.includes(userOverlay.idObject);
+    const isActive = activeOverlaysIds.includes(userOverlay.id);
     if (isActive) {
       /* eslint-disable-next-line no-param-reassign */
       userOverlay.order = maxOrderValue + userOverlay.order; // user overlays appear after shared overlays, we need to get max order value of shared overlays and add to it user overlay order to be ensured that user overlay will appear after shared overlays
diff --git a/src/redux/overlays/overlays.mock.ts b/src/redux/overlays/overlays.mock.ts
index d3acf0dc..7a22bee0 100644
--- a/src/redux/overlays/overlays.mock.ts
+++ b/src/redux/overlays/overlays.mock.ts
@@ -27,58 +27,63 @@ export const OVERLAYS_INITIAL_STATE_MOCK: OverlaysState = {
 
 export const PUBLIC_OVERLAYS_MOCK: MapOverlay[] = [
   {
+    id: 11,
+    group: null,
     name: 'PD substantia nigra',
     creator: 'appu-admin',
     description:
       'Differential transcriptome expression from post mortem tissue. Meta-analysis from 8 published datasets, FDR = 0.05, see PMIDs 23832570 and 25447234.',
     genomeType: null,
     genomeVersion: null,
-    idObject: 11,
     publicOverlay: true,
     type: 'GENERIC',
     order: 1,
   },
   {
+    id: 12,
+    group: null,
     name: 'Ageing brain',
     creator: 'appu-admin',
     description:
       'Differential transcriptome expression from post mortem tissue. Source: Allen Brain Atlas datasets, see PMID 25447234.',
     genomeType: null,
     genomeVersion: null,
-    idObject: 12,
     publicOverlay: true,
     type: 'GENERIC',
     order: 2,
   },
   {
+    id: 17,
+    group: null,
     name: 'PRKN variants example',
     creator: 'appu-admin',
     description: 'PRKN variants',
     genomeType: 'UCSC',
     genomeVersion: 'hg19',
-    idObject: 17,
     publicOverlay: true,
     type: 'GENETIC_VARIANT',
     order: 3,
   },
   {
+    id: 18,
+    group: null,
     name: 'PRKN variants doubled',
     creator: 'appu-admin',
     description: 'PRKN variants',
     genomeType: 'UCSC',
     genomeVersion: 'hg19',
-    idObject: 18,
     publicOverlay: true,
     type: 'GENETIC_VARIANT',
     order: 4,
   },
   {
+    id: 20,
+    group: null,
     name: 'Generic advanced format overlay',
     creator: 'appu-admin',
     description: 'Data set provided by a user',
     genomeType: null,
     genomeVersion: null,
-    idObject: 20,
     publicOverlay: true,
     type: 'GENERIC',
     order: 5,
@@ -119,25 +124,27 @@ export const ADD_OVERLAY_MOCK = {
 
 export const USER_OVERLAYS_MOCK: MapOverlay[] = [
   {
+    id: 99,
+    group: null,
     name: 'PD substantia nigra',
     creator: 'appu-admin',
     description:
       'Differential transcriptome expression from post mortem tissue. Meta-analysis from 8 published datasets, FDR = 0.05, see PMIDs 23832570 and 25447234.',
     genomeType: null,
     genomeVersion: null,
-    idObject: 99,
     publicOverlay: true,
     type: 'GENERIC',
     order: 1,
   },
   {
+    id: 123,
+    group: null,
     name: 'Ageing brain',
     creator: 'appu-admin',
     description:
       'Differential transcriptome expression from post mortem tissue. Source: Allen Brain Atlas datasets, see PMID 25447234.',
     genomeType: null,
     genomeVersion: null,
-    idObject: 123,
     publicOverlay: true,
     type: 'GENERIC',
     order: 2,
diff --git a/src/redux/overlays/overlays.reducers.test.ts b/src/redux/overlays/overlays.reducers.test.ts
index f4945820..f61c7766 100644
--- a/src/redux/overlays/overlays.reducers.test.ts
+++ b/src/redux/overlays/overlays.reducers.test.ts
@@ -164,7 +164,7 @@ describe('overlays reducer', () => {
 
   it('should update store when updateOverlay is pending', async () => {
     mockedAxiosClient
-      .onPatch(apiPath.updateOverlay(overlayFixture.idObject))
+      .onPatch(apiPath.updateOverlay(overlayFixture.id))
       .reply(HttpStatusCode.Ok, overlayFixture);
 
     store.dispatch(updateOverlays([overlayFixture]));
@@ -174,7 +174,7 @@ describe('overlays reducer', () => {
 
   it('should update store after successful updateOverlay', async () => {
     mockedAxiosClient
-      .onPatch(apiPath.updateOverlay(overlayFixture.idObject))
+      .onPatch(apiPath.updateOverlay(overlayFixture.id))
       .reply(HttpStatusCode.Ok, overlayFixture);
 
     const updateUserOverlaysPromise = store.dispatch(updateOverlays([overlayFixture]));
@@ -189,7 +189,7 @@ describe('overlays reducer', () => {
   });
   it('should update store after failed updateOverlay', async () => {
     mockedAxiosClient
-      .onPatch(apiPath.updateOverlay(overlayFixture.idObject))
+      .onPatch(apiPath.updateOverlay(overlayFixture.id))
       .reply(HttpStatusCode.NotFound, {});
 
     await store.dispatch(updateOverlays([overlayFixture]));
@@ -200,12 +200,12 @@ describe('overlays reducer', () => {
 
   it('should update store when removeOverlay is pending', async () => {
     mockedAxiosClient
-      .onDelete(apiPath.removeOverlay(overlayFixture.idObject))
+      .onDelete(apiPath.removeOverlay(overlayFixture.id))
       .reply(HttpStatusCode.Ok, {});
 
     store.dispatch(
       removeOverlay({
-        overlayId: overlayFixture.idObject,
+        overlayId: overlayFixture.id,
       }),
     );
     const { loading } = store.getState().overlays.removeOverlay;
@@ -214,12 +214,12 @@ describe('overlays reducer', () => {
 
   it('should update store after successful removeOverlay', async () => {
     mockedAxiosClient
-      .onDelete(apiPath.removeOverlay(overlayFixture.idObject))
+      .onDelete(apiPath.removeOverlay(overlayFixture.id))
       .reply(HttpStatusCode.Ok, {});
 
     const removeUserOverlaysPromise = store.dispatch(
       removeOverlay({
-        overlayId: overlayFixture.idObject,
+        overlayId: overlayFixture.id,
       }),
     );
     const { loading } = store.getState().overlays.removeOverlay;
@@ -233,12 +233,12 @@ describe('overlays reducer', () => {
   });
   it('should update store after failed removeOverlay', async () => {
     mockedAxiosClient
-      .onDelete(apiPath.removeOverlay(overlayFixture.idObject))
+      .onDelete(apiPath.removeOverlay(overlayFixture.id))
       .reply(HttpStatusCode.NotFound, {});
 
     const removeUserOverlaysPromise = store.dispatch(
       removeOverlay({
-        overlayId: overlayFixture.idObject,
+        overlayId: overlayFixture.id,
       }),
     );
     const { loading } = store.getState().overlays.removeOverlay;
diff --git a/src/redux/overlays/overlays.selectors.ts b/src/redux/overlays/overlays.selectors.ts
index fc988894..35862e6e 100644
--- a/src/redux/overlays/overlays.selectors.ts
+++ b/src/redux/overlays/overlays.selectors.ts
@@ -9,12 +9,12 @@ export const overlaysDataSelector = createSelector(
 );
 
 export const overlaysIdsAndOrderSelector = createSelector(overlaysDataSelector, overlays =>
-  overlays.map(({ idObject, order }) => ({ idObject, order })),
+  overlays.map(({ id, order }) => ({ id, order })),
 );
 
 export const overlaySelector = createSelector(
   [overlaysDataSelector, (_, overlayId: number): number => overlayId],
-  (overlays, overlayId) => overlays.find(overlay => overlay.idObject === overlayId),
+  (overlays, overlayId) => overlays.find(overlay => overlay.id === overlayId),
 );
 
 export const loadingAddOverlay = createSelector(
@@ -36,11 +36,11 @@ export const userOverlaysDataSelector = createSelector(
 
 export const userOverlaysIdsAndOrderSelector = createSelector(
   userOverlaysDataSelector,
-  userOverlays => userOverlays?.map(({ idObject, order }) => ({ idObject, order })) || [],
+  userOverlays => userOverlays?.map(({ id, order }) => ({ id, order })) || [],
 );
 
 export const userOverlaySelector = createSelector(
   [userOverlaysDataSelector, (_, userOverlayId: number): number => userOverlayId],
   (userOverlays, userOverlayId) =>
-    userOverlays?.find(userOverlay => userOverlay.idObject === userOverlayId),
+    userOverlays?.find(userOverlay => userOverlay.id === userOverlayId),
 );
diff --git a/src/redux/overlays/overlays.thunks.ts b/src/redux/overlays/overlays.thunks.ts
index 5c87fa7f..b87588f5 100644
--- a/src/redux/overlays/overlays.thunks.ts
+++ b/src/redux/overlays/overlays.thunks.ts
@@ -241,7 +241,7 @@ export const updateOverlays = createAsyncThunk<undefined, MapOverlay[], ThunkCon
     try {
       const userOverlaysPromises = userOverlays.map(userOverlay =>
         axiosInstance.patch<MapOverlay>(
-          apiPath.updateOverlay(userOverlay.idObject),
+          apiPath.updateOverlay(userOverlay.id),
           {
             overlay: userOverlay,
           },
diff --git a/src/services/pluginsManager/map/overlays/getVisibleDataOverlays.test.ts b/src/services/pluginsManager/map/overlays/getVisibleDataOverlays.test.ts
index efaf233e..72f224fa 100644
--- a/src/services/pluginsManager/map/overlays/getVisibleDataOverlays.test.ts
+++ b/src/services/pluginsManager/map/overlays/getVisibleDataOverlays.test.ts
@@ -6,7 +6,7 @@ import { OVERLAY_BIO_ENTITY_INITIAL_STATE_MOCK } from '@/redux/overlayBioEntity/
 import { DataOverlay } from '@/services/pluginsManager/map/overlays/types/DataOverlay';
 import { getVisibleDataOverlays } from './getVisibleDataOverlays';
 
-const ACTIVE_OVERLAYS_IDS = overlaysPageFixture.content.map(overlay => overlay.idObject);
+const ACTIVE_OVERLAYS_IDS = overlaysPageFixture.content.map(overlay => overlay.id);
 
 describe('getVisibleDataOverlays', () => {
   afterEach(() => {
diff --git a/src/services/pluginsManager/map/overlays/hideDataOverlay.test.ts b/src/services/pluginsManager/map/overlays/hideDataOverlay.test.ts
index 3d17efab..8a927307 100644
--- a/src/services/pluginsManager/map/overlays/hideDataOverlay.test.ts
+++ b/src/services/pluginsManager/map/overlays/hideDataOverlay.test.ts
@@ -8,7 +8,7 @@ import { hideDataOverlay } from './hideDataOverlay';
 import { PluginsEventBus } from '../../pluginsEventBus';
 import { ERROR_OVERLAY_ID_NOT_ACTIVE, ERROR_OVERLAY_ID_NOT_FOUND } from '../../errorMessages';
 
-const OVERLAY_ID = overlaysPageFixture.content[0].idObject;
+const OVERLAY_ID = overlaysPageFixture.content[0].id;
 
 describe('hideDataOverlay', () => {
   afterEach(() => {
diff --git a/src/services/pluginsManager/map/overlays/removeDataOverlay.test.ts b/src/services/pluginsManager/map/overlays/removeDataOverlay.test.ts
index f2e8feff..e6917094 100644
--- a/src/services/pluginsManager/map/overlays/removeDataOverlay.test.ts
+++ b/src/services/pluginsManager/map/overlays/removeDataOverlay.test.ts
@@ -35,7 +35,7 @@ describe('removeDataOverlay', () => {
 
   it('should dispatch removeOverlay with correct overlayId if matching overlay is found', () => {
     getStateSpy.mockImplementation(() => MOCK_STATE as RootState);
-    const overlayId = overlaysPageFixture.content[0].idObject;
+    const overlayId = overlaysPageFixture.content[0].id;
 
     removeDataOverlay(overlayId);
 
diff --git a/src/services/pluginsManager/map/overlays/showDataOverlay/showDataOverlay.test.ts b/src/services/pluginsManager/map/overlays/showDataOverlay/showDataOverlay.test.ts
index 7709f9ec..c5115124 100644
--- a/src/services/pluginsManager/map/overlays/showDataOverlay/showDataOverlay.test.ts
+++ b/src/services/pluginsManager/map/overlays/showDataOverlay/showDataOverlay.test.ts
@@ -15,7 +15,7 @@ import { showDataOverlay } from './showDataOverlay';
 jest.mock('../../../../../redux/overlayBioEntity/overlayBioEntity.thunk');
 jest.mock('../../../../../redux/store');
 
-const OVERLAY_ID = overlaysPageFixture.content[0].idObject;
+const OVERLAY_ID = overlaysPageFixture.content[0].id;
 
 describe('showDataOverlay function', () => {
   afterEach(() => {
diff --git a/src/services/pluginsManager/map/overlays/types/DataOverlay.ts b/src/services/pluginsManager/map/overlays/types/DataOverlay.ts
index e79d88ba..f8b4eaa1 100644
--- a/src/services/pluginsManager/map/overlays/types/DataOverlay.ts
+++ b/src/services/pluginsManager/map/overlays/types/DataOverlay.ts
@@ -4,8 +4,6 @@ import { DataOverlayEntry } from '@/services/pluginsManager/map/overlays/types/D
 export class DataOverlay {
   id: number;
 
-  idObject: number;
-
   name: string;
 
   order: number;
@@ -25,8 +23,7 @@ export class DataOverlay {
   entries: DataOverlayEntry[];
 
   constructor(mapOverlay: MapOverlay) {
-    this.id = mapOverlay.idObject;
-    this.idObject = mapOverlay.idObject;
+    this.id = mapOverlay.id;
     this.name = mapOverlay.name;
     this.order = mapOverlay.order;
     this.creator = mapOverlay.creator;
-- 
GitLab


From 8472c524b3404fed8da2445c232f27103932c910 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Wed, 5 Feb 2025 13:18:15 +0100
Subject: [PATCH 05/17] Component for grouped overlays extracted

---
 .../UserOverlays/UserOverlays.component.tsx   | 11 +++++++--
 .../UserOverlayActions.component.tsx          |  0
 .../UserOverlayActions.constants.ts           |  0
 .../hooks/useUserOverlayActions.test.ts       |  0
 .../hooks/useUserOverlayActions.ts            |  0
 .../UserOverlayActions/index.ts               |  0
 .../UserOverlayInfo.component.tsx             |  0
 .../UserOverlayListItem.component.tsx         |  0
 .../hooks/useDragAndDrop.ts                   |  0
 .../UserOverlayListItem/index.ts              |  0
 .../UserOverlaysGroup.component.test.tsx}     |  7 +++---
 .../UserOverlaysGroup.component.tsx}          | 17 ++++++++++---
 .../UserOverlaysGroup.utils.test.ts}          |  2 +-
 .../UserOverlaysGroup.utils.ts}               |  0
 .../hooks/useUserOverlays.test.ts             | 19 ++++++++-------
 .../hooks/useUserOverlays.ts                  | 24 +++++++------------
 .../UserOverlays/UserOverlaysGroup/index.ts   |  1 +
 .../UserOverlaysWithoutGroup/index.ts         |  1 -
 18 files changed, 49 insertions(+), 33 deletions(-)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup => UserOverlaysGroup}/UserOverlayListItem/UserOverlayActions/UserOverlayActions.component.tsx (100%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup => UserOverlaysGroup}/UserOverlayListItem/UserOverlayActions/UserOverlayActions.constants.ts (100%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup => UserOverlaysGroup}/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.test.ts (100%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup => UserOverlaysGroup}/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.ts (100%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup => UserOverlaysGroup}/UserOverlayListItem/UserOverlayActions/index.ts (100%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup => UserOverlaysGroup}/UserOverlayListItem/UserOverlayInfo/UserOverlayInfo.component.tsx (100%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup => UserOverlaysGroup}/UserOverlayListItem/UserOverlayListItem.component.tsx (100%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup => UserOverlaysGroup}/UserOverlayListItem/hooks/useDragAndDrop.ts (100%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup => UserOverlaysGroup}/UserOverlayListItem/index.ts (100%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.test.tsx => UserOverlaysGroup/UserOverlaysGroup.component.test.tsx} (97%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.tsx => UserOverlaysGroup/UserOverlaysGroup.component.tsx} (82%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.utils.test.ts => UserOverlaysGroup/UserOverlaysGroup.utils.test.ts} (98%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.utils.ts => UserOverlaysGroup/UserOverlaysGroup.utils.ts} (100%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup => UserOverlaysGroup}/hooks/useUserOverlays.test.ts (91%)
 rename src/components/Map/Drawer/OverlaysDrawer/UserOverlays/{UserOverlaysWithoutGroup => UserOverlaysGroup}/hooks/useUserOverlays.ts (76%)
 create mode 100644 src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/index.ts
 delete mode 100644 src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/index.ts

diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx
index cf4052b5..60d7cb01 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx
@@ -3,9 +3,12 @@ import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
 import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { authenticatedUserSelector, loadingUserSelector } from '@/redux/user/user.selectors';
 import { Button } from '@/shared/Button';
-import { UserOverlaysWithoutGroup } from './UserOverlaysWithoutGroup';
+import { userOverlaysDataSelector } from '@/redux/overlays/overlays.selectors';
+import { UserOverlaysGroup } from './UserOverlaysGroup';
 
 export const UserOverlays = (): JSX.Element => {
+  const userOverlays = useAppSelector(userOverlaysDataSelector);
+
   const dispatch = useAppDispatch();
   const loadingUser = useAppSelector(loadingUserSelector);
   const authenticatedUser = useAppSelector(authenticatedUserSelector);
@@ -36,7 +39,11 @@ export const UserOverlays = (): JSX.Element => {
               Add overlay
             </Button>
           </div>
-          <UserOverlaysWithoutGroup />
+          <UserOverlaysGroup
+            groupName="Without group"
+            groupId={null}
+            overlays={userOverlays || []}
+          />
         </>
       )}
     </div>
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/UserOverlayActions.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayActions/UserOverlayActions.component.tsx
similarity index 100%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/UserOverlayActions.component.tsx
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayActions/UserOverlayActions.component.tsx
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/UserOverlayActions.constants.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayActions/UserOverlayActions.constants.ts
similarity index 100%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/UserOverlayActions.constants.ts
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayActions/UserOverlayActions.constants.ts
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.test.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.test.ts
similarity index 100%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.test.ts
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.test.ts
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.ts
similarity index 100%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.ts
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayActions/hooks/useUserOverlayActions.ts
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/index.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayActions/index.ts
similarity index 100%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayActions/index.ts
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayActions/index.ts
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayInfo/UserOverlayInfo.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayInfo/UserOverlayInfo.component.tsx
similarity index 100%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayInfo/UserOverlayInfo.component.tsx
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayInfo/UserOverlayInfo.component.tsx
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayListItem.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayListItem.component.tsx
similarity index 100%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/UserOverlayListItem.component.tsx
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayListItem.component.tsx
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/hooks/useDragAndDrop.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/hooks/useDragAndDrop.ts
similarity index 100%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/hooks/useDragAndDrop.ts
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/hooks/useDragAndDrop.ts
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/index.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/index.ts
similarity index 100%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlayListItem/index.ts
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/index.ts
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.test.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.test.tsx
similarity index 97%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.test.tsx
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.test.tsx
index f4145e11..68208c80 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.test.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.test.tsx
@@ -14,17 +14,18 @@ import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
 import { HttpStatusCode } from 'axios';
 import { OVERLAYS_INITIAL_STATE_MOCK } from '@/redux/overlays/overlays.mock';
 import { MODAL_INITIAL_STATE_MOCK } from '@/redux/modal/modal.mock';
-import { UserOverlaysWithoutGroup } from './UserOverlaysWithoutGroup.component';
+import { UserOverlaysGroup } from './UserOverlaysGroup.component';
 
 const mockedAxiosNewClient = mockNetworkNewAPIResponse();
 
 const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
   const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
 
+  const overlays = initialStoreState.overlays?.userOverlays.data || [];
   return (
     render(
       <Wrapper>
-        <UserOverlaysWithoutGroup />
+        <UserOverlaysGroup overlays={overlays} groupId={null} groupName="Without group" />
       </Wrapper>,
     ),
     {
@@ -33,7 +34,7 @@ const renderComponent = (initialStoreState: InitialStoreState = {}): { store: St
   );
 };
 
-describe('UserOverlaysWithoutGroup - component', () => {
+describe('UserOverlaysGroup - component', () => {
   it('should render list of overlays', async () => {
     mockedAxiosNewClient
       .onGet(apiPath.getAllUserOverlaysByCreatorQuery({ creator: 'test', publicOverlay: false }))
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
similarity index 82%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.tsx
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
index ef571ad6..6bcf4790 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
@@ -7,12 +7,23 @@ import {
 } from '@/shared/Accordion';
 import { DndProvider } from 'react-dnd';
 import { HTML5Backend } from 'react-dnd-html5-backend';
+import { MapOverlay } from '@/types/models';
 import { UserOverlayListItem } from './UserOverlayListItem';
 import { useUserOverlays } from './hooks/useUserOverlays';
 
-export const UserOverlaysWithoutGroup = (): React.ReactNode => {
+type OverlayGroupProps = {
+  groupId: number | null;
+  groupName: string;
+  overlays: MapOverlay[];
+};
+
+export const UserOverlaysGroup = ({
+  overlays,
+  groupName,
+  groupId,
+}: OverlayGroupProps): React.ReactNode => {
   const { moveUserOverlayListItem, updateUserOverlaysOrder, isPending, userOverlaysList } =
-    useUserOverlays();
+    useUserOverlays(overlays, groupId);
 
   return (
     <DndProvider backend={HTML5Backend}>
@@ -21,7 +32,7 @@ export const UserOverlaysWithoutGroup = (): React.ReactNode => {
           <AccordionItem className="border-b-0">
             <AccordionItemHeading>
               <AccordionItemButton className="px-6 text-sm font-semibold">
-                Without group
+                {groupName}
               </AccordionItemButton>
             </AccordionItemHeading>
             <AccordionItemPanel>
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.utils.test.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.utils.test.ts
similarity index 98%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.utils.test.ts
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.utils.test.ts
index d454fd22..d1b7d78a 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.utils.test.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.utils.test.ts
@@ -1,6 +1,6 @@
 /* eslint-disable no-magic-numbers */
 import { MapOverlay } from '@/types/models';
-import { moveArrayElement } from './UserOverlaysWithoutGroup.utils';
+import { moveArrayElement } from './UserOverlaysGroup.utils';
 
 const INPUT_ARRAY: MapOverlay[] = [
   {
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.utils.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.utils.ts
similarity index 100%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/UserOverlaysWithoutGroup.utils.ts
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.utils.ts
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/hooks/useUserOverlays.test.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.test.ts
similarity index 91%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/hooks/useUserOverlays.test.ts
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.test.ts
index f8de514a..c9777f59 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/hooks/useUserOverlays.test.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.test.ts
@@ -30,9 +30,12 @@ describe('useUserOverlays', () => {
       result: {
         current: { userOverlaysList },
       },
-    } = renderHook(() => useUserOverlays(), {
-      wrapper: Wrapper,
-    });
+    } = renderHook(
+      () => useUserOverlays(OVERLAYS_INITIAL_STATE_MOCK.userOverlays.data || [], null),
+      {
+        wrapper: Wrapper,
+      },
+    );
 
     const actions = store.getActions();
     const firstAction = actions[0];
@@ -73,7 +76,7 @@ describe('useUserOverlays', () => {
       result: {
         current: { userOverlaysList },
       },
-    } = renderHook(() => useUserOverlays(), {
+    } = renderHook(() => useUserOverlays(overlaysPageFixture.content, null), {
       wrapper: Wrapper,
     });
 
@@ -119,7 +122,7 @@ describe('useUserOverlays', () => {
       result: {
         current: { moveUserOverlayListItem, userOverlaysList },
       },
-    } = renderHook(() => useUserOverlays(), {
+    } = renderHook(() => useUserOverlays([FIRST_USER_OVERLAY, SECOND_USER_OVERLAY], null), {
       wrapper: Wrapper,
     });
 
@@ -130,8 +133,8 @@ describe('useUserOverlays', () => {
     expect(userOverlaysList).toEqual([SECOND_USER_OVERLAY, FIRST_USER_OVERLAY]);
   });
   it('calls updateOverlays on calling updateUserOverlaysOrder', async () => {
-    const FIRST_USER_OVERLAY = { ...overlayFixture, order: 1, idObject: 12 };
-    const SECOND_USER_OVERLAY = { ...overlayFixture, order: 2, idObject: 92 };
+    const FIRST_USER_OVERLAY = { ...overlayFixture, order: 1, id: 12 };
+    const SECOND_USER_OVERLAY = { ...overlayFixture, order: 2, id: 92 };
 
     const page = {
       ...overlaysPageFixture,
@@ -170,7 +173,7 @@ describe('useUserOverlays', () => {
       result: {
         current: { moveUserOverlayListItem, updateUserOverlaysOrder },
       },
-    } = renderHook(() => useUserOverlays(), {
+    } = renderHook(() => useUserOverlays([FIRST_USER_OVERLAY, SECOND_USER_OVERLAY], null), {
       wrapper: Wrapper,
     });
 
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/hooks/useUserOverlays.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
similarity index 76%
rename from src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/hooks/useUserOverlays.ts
rename to src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
index 722ea7de..5ec4bb93 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/hooks/useUserOverlays.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
@@ -1,14 +1,11 @@
 /* eslint-disable no-magic-numbers */
 import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
 import { useAppSelector } from '@/redux/hooks/useAppSelector';
-import {
-  loadingUserOverlaysSelector,
-  userOverlaysDataSelector,
-} from '@/redux/overlays/overlays.selectors';
+import { loadingUserOverlaysSelector } from '@/redux/overlays/overlays.selectors';
 import { updateOverlays } from '@/redux/overlays/overlays.thunks';
 import { MapOverlay } from '@/types/models';
-import { useEffect, useState } from 'react';
-import { moveArrayElement } from '../UserOverlaysWithoutGroup.utils';
+import { useState } from 'react';
+import { moveArrayElement } from '../UserOverlaysGroup.utils';
 
 type UseUserOverlaysReturn = {
   isPending: boolean;
@@ -17,19 +14,15 @@ type UseUserOverlaysReturn = {
   updateUserOverlaysOrder: () => void;
 };
 
-export const useUserOverlays = (): UseUserOverlaysReturn => {
+export const useUserOverlays = (
+  userOverlays: MapOverlay[],
+  groupId: number | null,
+): UseUserOverlaysReturn => {
   const dispatch = useAppDispatch();
-  const [userOverlaysList, setUserOverlaysList] = useState<MapOverlay[]>([]);
-  const userOverlays = useAppSelector(userOverlaysDataSelector);
+  const [userOverlaysList, setUserOverlaysList] = useState<MapOverlay[]>(userOverlays);
   const loadingUserOverlays = useAppSelector(loadingUserOverlaysSelector);
   const isPending = loadingUserOverlays === 'pending';
 
-  useEffect(() => {
-    if (userOverlays) {
-      setUserOverlaysList(userOverlays);
-    }
-  }, [userOverlays]);
-
   const moveUserOverlayListItem = (dragIndex: number, hoverIndex: number): void => {
     const updatedUserOverlays = moveArrayElement(userOverlaysList, dragIndex, hoverIndex);
     setUserOverlaysList(updatedUserOverlays);
@@ -44,6 +37,7 @@ export const useUserOverlays = (): UseUserOverlaysReturn => {
       const newOrderedUserOverlay = {
         ...userOverlaysList[index],
         order: index + 1,
+        group: groupId,
       };
 
       if (userOverlay.id !== newOrderedUserOverlay.id) {
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/index.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/index.ts
new file mode 100644
index 00000000..be4477f2
--- /dev/null
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/index.ts
@@ -0,0 +1 @@
+export { UserOverlaysGroup } from './UserOverlaysGroup.component';
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/index.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/index.ts
deleted file mode 100644
index f8de206d..00000000
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysWithoutGroup/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { UserOverlaysWithoutGroup } from './UserOverlaysWithoutGroup.component';
-- 
GitLab


From 5c28533cf9a1e085bafea7485a579a3a75b74e0a Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Wed, 5 Feb 2025 14:18:49 +0100
Subject: [PATCH 06/17] fetch info about overlay groups from backend

---
 .../OverlayGroupSelector.component.test.tsx   | 52 ++++++++++++
 .../OverlayGroupSelector.component.tsx        | 84 +++++++++++++++++++
 .../UserOverlayForm.component.tsx             |  8 +-
 .../UserOverlayForm.constants.ts              | 16 ++--
 .../hooks/useUserOverlayForm.test.ts          |  5 +-
 .../hooks/useUserOverlayForm.ts               | 13 ++-
 src/models/fixtures/overlayGroupsFixture.ts   |  9 ++
 src/models/overlayGroupSchema.ts              |  8 ++
 src/redux/apiPath.ts                          |  1 +
 src/redux/overlayGroup/ovelayGroup.mock.ts    |  8 ++
 .../overlayGroup/overlayGroup.reducers.ts     | 19 +++++
 .../overlayGroup/overlayGroup.selectors.ts    | 14 ++++
 src/redux/overlayGroup/overlayGroup.slice.ts  | 21 +++++
 src/redux/overlayGroup/overlayGroup.thunks.ts | 29 +++++++
 src/redux/overlayGroup/overlayGroup.types.ts  |  4 +
 src/redux/root/root.fixtures.ts               |  2 +
 src/redux/store.ts                            |  2 +
 src/types/models.ts                           |  2 +
 18 files changed, 280 insertions(+), 17 deletions(-)
 create mode 100644 src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/OverlaySelector/OverlayGroupSelector.component.test.tsx
 create mode 100644 src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/OverlaySelector/OverlayGroupSelector.component.tsx
 create mode 100644 src/models/fixtures/overlayGroupsFixture.ts
 create mode 100644 src/models/overlayGroupSchema.ts
 create mode 100644 src/redux/overlayGroup/ovelayGroup.mock.ts
 create mode 100644 src/redux/overlayGroup/overlayGroup.reducers.ts
 create mode 100644 src/redux/overlayGroup/overlayGroup.selectors.ts
 create mode 100644 src/redux/overlayGroup/overlayGroup.slice.ts
 create mode 100644 src/redux/overlayGroup/overlayGroup.thunks.ts
 create mode 100644 src/redux/overlayGroup/overlayGroup.types.ts

diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/OverlaySelector/OverlayGroupSelector.component.test.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/OverlaySelector/OverlayGroupSelector.component.test.tsx
new file mode 100644
index 00000000..d68ce93d
--- /dev/null
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/OverlaySelector/OverlayGroupSelector.component.test.tsx
@@ -0,0 +1,52 @@
+/* eslint-disable no-magic-numbers */
+import React from 'react';
+import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import { OverlayGroup } from '@/types/models';
+import { OverlayGroupSelector } from './OverlayGroupSelector.component';
+
+const items: OverlayGroup[] = [
+  { id: 1, name: 'Item 1', order: 1 },
+  { id: 2, name: 'Item 2', order: 2 },
+  { id: 3, name: 'Item 3', order: 3 },
+];
+
+const onChangeMock = jest.fn();
+
+describe('OverlayGroupSelector component', () => {
+  it('renders the component with initial values', () => {
+    const label = 'Select an item';
+    const value = items[0];
+
+    render(
+      <OverlayGroupSelector items={items} value={value} onChange={onChangeMock} label={label} />,
+    );
+
+    expect(screen.getByText(label)).toBeInTheDocument();
+
+    expect(screen.getByTestId('selector-dropdown-button-name')).toHaveTextContent(value.name);
+  });
+
+  it('opens the dropdown and selects an item', () => {
+    const label = 'Select an item';
+    const value = items[0];
+
+    render(
+      <OverlayGroupSelector items={items} value={value} onChange={onChangeMock} label={label} />,
+    );
+
+    fireEvent.click(screen.getByTestId('selector-dropdown-button-name'));
+
+    expect(screen.getByRole('listbox')).toBeInTheDocument();
+
+    const selectedItem = items[1];
+    const firstItem = screen.getByText(selectedItem.name);
+
+    fireEvent.click(firstItem);
+
+    waitFor(() => {
+      expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
+    });
+
+    expect(onChangeMock).toHaveBeenCalledWith(selectedItem);
+  });
+});
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/OverlaySelector/OverlayGroupSelector.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/OverlaySelector/OverlayGroupSelector.component.tsx
new file mode 100644
index 00000000..13711f65
--- /dev/null
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/OverlaySelector/OverlayGroupSelector.component.tsx
@@ -0,0 +1,84 @@
+/* eslint-disable no-magic-numbers */
+import { useSelect } from 'downshift';
+
+import { twMerge } from 'tailwind-merge';
+import { Icon } from '@/shared/Icon';
+import { OverlayGroup } from '@/types/models';
+
+type OverlayGroupSelectorProps = {
+  items: OverlayGroup[];
+  value: OverlayGroup;
+  onChange: (item: OverlayGroup) => void;
+  label: string;
+};
+
+export const OverlayGroupSelector = ({
+  items,
+  value,
+  onChange,
+  label,
+}: OverlayGroupSelectorProps): JSX.Element => {
+  const onItemSelect = (item: OverlayGroup | undefined | null): void => {
+    if (item) {
+      onChange(item);
+    }
+  };
+
+  const {
+    isOpen,
+    getToggleButtonProps,
+    getMenuProps,
+    highlightedIndex,
+    getItemProps,
+    selectedItem,
+  } = useSelect({
+    items,
+    defaultSelectedItem: items[0],
+    selectedItem: value,
+    onSelectedItemChange: ({ selectedItem: newSelectedItem }) => onItemSelect(newSelectedItem),
+  });
+
+  return (
+    <div className="mb-2.5">
+      <p className="my-2.5 text-sm">{label}</p>
+
+      <div className={twMerge('relative rounded-t bg-cultured text-xs', !isOpen && 'rounded-b')}>
+        <div className={twMerge('flex w-full flex-col rounded-t py-2 pl-4 pr-3')}>
+          <div
+            {...getToggleButtonProps()}
+            className="flex cursor-pointer flex-row items-center justify-between bg-cultured"
+          >
+            <span data-testid="selector-dropdown-button-name" className="font-medium">
+              {selectedItem?.name}
+            </span>
+            <Icon
+              name="chevron-down"
+              className={twMerge('arrow-button h-6 w-6 fill-primary-500', isOpen && 'rotate-180')}
+            />
+          </div>
+        </div>
+        <ul
+          {...getMenuProps()}
+          className={`absolute inset-x-0 z-10 max-h-80 w-full overflow-scroll rounded-b bg-cultured p-0 ${
+            !isOpen && 'hidden'
+          }`}
+        >
+          {isOpen &&
+            items.map((item, index) => (
+              <li
+                className={twMerge(
+                  'border-t',
+                  highlightedIndex === index && 'text-primary-500',
+                  'flex flex-col px-4 py-2',
+                )}
+                key={item.id}
+                {...getItemProps({ item, index })}
+              >
+                <span>{item.name}</span>
+              </li>
+            ))}
+        </ul>
+      </div>
+    </div>
+  );
+};
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.tsx
index ed8f04b7..8efa3bbe 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.tsx
@@ -5,8 +5,9 @@ import { openOverlaysDrawer } from '@/redux/drawer/drawer.slice';
 import { Button } from '@/shared/Button';
 import { Input } from '@/shared/Input';
 import { Textarea } from '@/shared/Textarea';
+import { OverlayGroupSelector } from '@/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/OverlaySelector/OverlayGroupSelector.component';
 import { OverlaySelector } from './OverlaySelector';
-import { OVERLAY_GROUPS, OVERLAY_TYPES } from './UserOverlayForm.constants';
+import { OVERLAY_TYPES } from './UserOverlayForm.constants';
 import { FileUpload } from './FileUpload';
 import { useUserOverlayForm } from './hooks/useUserOverlayForm';
 
@@ -16,6 +17,7 @@ export const UserOverlayForm = (): React.ReactNode => {
     name,
     type,
     group,
+    overlayGroups,
     description,
     uploadedFile,
     elementsList,
@@ -87,10 +89,10 @@ export const UserOverlayForm = (): React.ReactNode => {
           label="Type"
         />
 
-        <OverlaySelector
+        <OverlayGroupSelector
           value={group}
           onChange={handleChangeGroup}
-          items={OVERLAY_GROUPS}
+          items={overlayGroups}
           label="Select group"
         />
 
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.constants.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.constants.ts
index 3b4622db..ee831535 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.constants.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.constants.ts
@@ -1,4 +1,7 @@
 /* eslint-disable no-magic-numbers */
+import { OverlayGroup } from '@/types/models';
+import { ZERO } from '@/constants/common';
+
 export const OVERLAY_TYPES = [
   {
     id: 'GENERIC',
@@ -10,13 +13,10 @@ export const OVERLAY_TYPES = [
   },
 ];
 
-export const OVERLAY_GROUPS = [
-  {
-    id: 'WITHOUT_GROUP',
-    label: 'Without group',
-  },
-];
-
-export const DEFAULT_GROUP = OVERLAY_GROUPS[0];
+export const DEFAULT_GROUP: OverlayGroup = {
+  id: null,
+  name: 'Without group',
+  order: ZERO,
+};
 
 export const DEFAULT_TYPE = OVERLAY_TYPES[0];
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/hooks/useUserOverlayForm.test.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/hooks/useUserOverlayForm.test.ts
index 988e9ea0..679a86c5 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/hooks/useUserOverlayForm.test.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/hooks/useUserOverlayForm.test.ts
@@ -2,6 +2,7 @@ import { renderHook } from '@testing-library/react';
 import { act } from 'react-dom/test-utils';
 import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
 import { ChangeEvent } from 'react';
+import { overlayGroupFixture } from '@/models/fixtures/overlayGroupsFixture';
 import { useUserOverlayForm } from './useUserOverlayForm';
 
 describe('useUserOverlayForm', () => {
@@ -11,11 +12,11 @@ describe('useUserOverlayForm', () => {
 
     act(() => {
       result.current.handleChangeType({ id: '1', label: 'Test Type' });
-      result.current.handleChangeGroup({ id: '1', label: 'Test Group' });
+      result.current.handleChangeGroup(overlayGroupFixture);
     });
 
     expect(result.current.type).toEqual({ id: '1', label: 'Test Type' });
-    expect(result.current.group).toEqual({ id: '1', label: 'Test Group' });
+    expect(result.current.group).toEqual(overlayGroupFixture);
   });
 
   it('should update overlayContent when handleChangeOverlayContent is called', () => {
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/hooks/useUserOverlayForm.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/hooks/useUserOverlayForm.ts
index 5229c9fa..079266ef 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/hooks/useUserOverlayForm.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/hooks/useUserOverlayForm.ts
@@ -4,6 +4,8 @@ import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
 import { projectIdSelector } from '@/redux/project/project.selectors';
 import { addOverlay } from '@/redux/overlays/overlays.thunks';
 import { loadingAddOverlay } from '@/redux/overlays/overlays.selectors';
+import { OverlayGroup } from '@/types/models';
+import { overlayGroupsSelector } from '@/redux/overlayGroup/overlayGroup.selectors';
 import { DEFAULT_GROUP, DEFAULT_TYPE, OVERLAY_TYPES } from '../UserOverlayForm.constants';
 import { SelectorItem } from '../UserOverlayForm.types';
 import { processOverlayContentChange } from '../UserOverlayForm.utils';
@@ -11,7 +13,8 @@ import { processOverlayContentChange } from '../UserOverlayForm.utils';
 type ReturnType = {
   name: string;
   type: SelectorItem;
-  group: SelectorItem;
+  group: OverlayGroup;
+  overlayGroups: OverlayGroup[];
   description: string;
   uploadedFile: File | null;
   elementsList: string;
@@ -21,7 +24,7 @@ type ReturnType = {
   handleChangeName: (e: ChangeEvent<HTMLInputElement>) => void;
   handleChangeDescription: (e: ChangeEvent<HTMLTextAreaElement>) => void;
   handleChangeType: (value: SelectorItem) => void;
-  handleChangeGroup: (value: SelectorItem) => void;
+  handleChangeGroup: (value: OverlayGroup) => void;
   handleChangeUploadedFile: (value: File) => void;
   handleChangeOverlayContent: (value: string) => void;
   handleChangeElementsList: (e: ChangeEvent<HTMLTextAreaElement>) => void;
@@ -31,13 +34,14 @@ type ReturnType = {
 
 export const useUserOverlayForm = (): ReturnType => {
   const dispatch = useAppDispatch();
+  const overlayGroups = useAppSelector(overlayGroupsSelector);
   const projectId = useAppSelector(projectIdSelector);
   const loadingAddOverlayStatus = useAppSelector(loadingAddOverlay);
   const isPending = loadingAddOverlayStatus === 'pending';
 
   const [name, setName] = useState('');
   const [type, setType] = useState<SelectorItem>(DEFAULT_TYPE);
-  const [group, setGroup] = useState<SelectorItem>(DEFAULT_GROUP);
+  const [group, setGroup] = useState<OverlayGroup>(DEFAULT_GROUP);
   const [description, setDescription] = useState('');
   const [uploadedFile, setUploadedFile] = useState<File | null>(null);
   const [elementsList, setElementsList] = useState('');
@@ -55,7 +59,7 @@ export const useUserOverlayForm = (): ReturnType => {
     setType(value);
   };
 
-  const handleChangeGroup = (value: SelectorItem): void => {
+  const handleChangeGroup = (value: OverlayGroup): void => {
     setGroup(value);
   };
 
@@ -123,6 +127,7 @@ export const useUserOverlayForm = (): ReturnType => {
   return {
     name,
     type,
+    overlayGroups,
     group,
     description,
     uploadedFile,
diff --git a/src/models/fixtures/overlayGroupsFixture.ts b/src/models/fixtures/overlayGroupsFixture.ts
new file mode 100644
index 00000000..7c22b1f2
--- /dev/null
+++ b/src/models/fixtures/overlayGroupsFixture.ts
@@ -0,0 +1,9 @@
+import { ZOD_SEED } from '@/constants';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { createFixture } from 'zod-fixture';
+import { overlayGroupSchema } from '@/models/overlayGroupSchema';
+
+export const overlayGroupFixture = createFixture(overlayGroupSchema, {
+  seed: ZOD_SEED,
+  array: { min: 1, max: 1 },
+});
diff --git a/src/models/overlayGroupSchema.ts b/src/models/overlayGroupSchema.ts
new file mode 100644
index 00000000..c044c7d1
--- /dev/null
+++ b/src/models/overlayGroupSchema.ts
@@ -0,0 +1,8 @@
+import { z } from 'zod';
+import { ZERO } from '@/constants/common';
+
+export const overlayGroupSchema = z.object({
+  id: z.number().int().positive().nullable(),
+  name: z.string(),
+  order: z.number().int().gte(ZERO),
+});
diff --git a/src/redux/apiPath.ts b/src/redux/apiPath.ts
index 93d49308..4457eee6 100644
--- a/src/redux/apiPath.ts
+++ b/src/redux/apiPath.ts
@@ -134,4 +134,5 @@ export const apiPath = {
     `projects/${PROJECT_ID}/models/*/bioEntities/suggestedQueryList`,
   getDrugAutocomplete: (): string => `projects/${PROJECT_ID}/drugs/suggestedQueryList`,
   getChemicalAutocomplete: (): string => `projects/${PROJECT_ID}/chemicals/suggestedQueryList`,
+  getOverlayGroups: (): string => `projects/${PROJECT_ID}/overlay_groups/`,
 };
diff --git a/src/redux/overlayGroup/ovelayGroup.mock.ts b/src/redux/overlayGroup/ovelayGroup.mock.ts
new file mode 100644
index 00000000..cfc2261b
--- /dev/null
+++ b/src/redux/overlayGroup/ovelayGroup.mock.ts
@@ -0,0 +1,8 @@
+import { DEFAULT_ERROR } from '@/constants/errors';
+import { OverlayGroupsState } from '@/redux/overlayGroup/overlayGroup.types';
+
+export const OVERLAY_GROUPS_STATE_INITIAL_MOCK: OverlayGroupsState = {
+  data: [],
+  loading: 'idle',
+  error: DEFAULT_ERROR,
+};
diff --git a/src/redux/overlayGroup/overlayGroup.reducers.ts b/src/redux/overlayGroup/overlayGroup.reducers.ts
new file mode 100644
index 00000000..16548227
--- /dev/null
+++ b/src/redux/overlayGroup/overlayGroup.reducers.ts
@@ -0,0 +1,19 @@
+import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
+import { OverlayGroupsState } from '@/redux/overlayGroup/overlayGroup.types';
+import { getOverlayGroups } from '@/redux/overlayGroup/overlayGroup.thunks';
+
+export const getOverlayGroupsReducer = (
+  builder: ActionReducerMapBuilder<OverlayGroupsState>,
+): void => {
+  builder.addCase(getOverlayGroups.pending, state => {
+    state.loading = 'pending';
+  });
+  builder.addCase(getOverlayGroups.fulfilled, (state, action) => {
+    state.data = action.payload || undefined;
+    state.loading = 'succeeded';
+  });
+  builder.addCase(getOverlayGroups.rejected, state => {
+    state.loading = 'failed';
+    // TODO to discuss manage state of failure
+  });
+};
diff --git a/src/redux/overlayGroup/overlayGroup.selectors.ts b/src/redux/overlayGroup/overlayGroup.selectors.ts
new file mode 100644
index 00000000..bd26e08f
--- /dev/null
+++ b/src/redux/overlayGroup/overlayGroup.selectors.ts
@@ -0,0 +1,14 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { DEFAULT_GROUP } from '@/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.constants';
+import { OverlayGroup } from '@/types/models';
+import { rootSelector } from '../root/root.selectors';
+
+const overlayGroupSelector = createSelector(rootSelector, state => state.overlayGroups);
+
+export const overlayGroupsSelector = createSelector(overlayGroupSelector, overlayGroup => {
+  let result: OverlayGroup[] = [DEFAULT_GROUP];
+  if (overlayGroup?.data) {
+    result = result.concat(overlayGroup?.data);
+  }
+  return result;
+});
diff --git a/src/redux/overlayGroup/overlayGroup.slice.ts b/src/redux/overlayGroup/overlayGroup.slice.ts
new file mode 100644
index 00000000..2e90994a
--- /dev/null
+++ b/src/redux/overlayGroup/overlayGroup.slice.ts
@@ -0,0 +1,21 @@
+import { createSlice } from '@reduxjs/toolkit';
+
+import { OverlayGroupsState } from '@/redux/overlayGroup/overlayGroup.types';
+import { getOverlayGroupsReducer } from '@/redux/overlayGroup/overlayGroup.reducers';
+
+const initialState: OverlayGroupsState = {
+  data: [],
+  loading: 'idle',
+  error: { name: '', message: '' },
+};
+
+const overlayGroupsSlice = createSlice({
+  name: 'overlayGroup',
+  initialState,
+  reducers: {},
+  extraReducers: builder => {
+    getOverlayGroupsReducer(builder);
+  },
+});
+
+export default overlayGroupsSlice.reducer;
diff --git a/src/redux/overlayGroup/overlayGroup.thunks.ts b/src/redux/overlayGroup/overlayGroup.thunks.ts
new file mode 100644
index 00000000..aed7d0a0
--- /dev/null
+++ b/src/redux/overlayGroup/overlayGroup.thunks.ts
@@ -0,0 +1,29 @@
+import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
+import { 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 { apiPath } from '../apiPath';
+
+export const getOverlayGroups = createAsyncThunk<OverlayGroup[], void, ThunkConfig>(
+  'overlayGroup/getOverlayGroups',
+  async () => {
+    try {
+      const response = await axiosInstanceNewAPI.get<PageOf<OverlayGroup>>(
+        apiPath.getOverlayGroups(),
+      );
+
+      const isDataValid = validateDataUsingZodSchema(
+        response.data,
+        pageableSchema(overlayGroupSchema),
+      );
+
+      return isDataValid ? response.data.content : [];
+    } catch (error) {
+      return Promise.reject(getError({ error, prefix: 'Failed to fetch overlay groups' }));
+    }
+  },
+);
diff --git a/src/redux/overlayGroup/overlayGroup.types.ts b/src/redux/overlayGroup/overlayGroup.types.ts
new file mode 100644
index 00000000..125020ed
--- /dev/null
+++ b/src/redux/overlayGroup/overlayGroup.types.ts
@@ -0,0 +1,4 @@
+import { OverlayGroup } from '@/types/models';
+import { FetchDataState } from '@/types/fetchDataState';
+
+export type OverlayGroupsState = FetchDataState<OverlayGroup[], []>;
diff --git a/src/redux/root/root.fixtures.ts b/src/redux/root/root.fixtures.ts
index e21a9a9a..534f7ee0 100644
--- a/src/redux/root/root.fixtures.ts
+++ b/src/redux/root/root.fixtures.ts
@@ -9,6 +9,7 @@ import { LAYERS_STATE_INITIAL_MOCK } from '@/redux/layers/layers.mock';
 import { NEW_REACTIONS_INITIAL_STATE_MOCK } from '@/redux/newReactions/newReactions.mock';
 import { GLYPHS_STATE_INITIAL_MOCK } from '@/redux/glyphs/glyphs.mock';
 import { MAP_EDIT_TOOLS_STATE_INITIAL_MOCK } from '@/redux/mapEditTools/mapEditTools.mock';
+import { OVERLAY_GROUPS_STATE_INITIAL_MOCK } from '@/redux/overlayGroup/ovelayGroup.mock';
 import { BIOENTITY_INITIAL_STATE_MOCK } from '../bioEntity/bioEntity.mock';
 import { CHEMICALS_INITIAL_STATE_MOCK } from '../chemicals/chemicals.mock';
 import { CONFIGURATION_INITIAL_STATE } from '../configuration/configuration.adapter';
@@ -43,6 +44,7 @@ export const INITIAL_STORE_STATE_MOCK: RootState = {
   shapes: SHAPES_STATE_INITIAL_MOCK,
   glyphs: GLYPHS_STATE_INITIAL_MOCK,
   projects: PROJECTS_STATE_INITIAL_MOCK,
+  overlayGroups: OVERLAY_GROUPS_STATE_INITIAL_MOCK,
   drugs: DRUGS_INITIAL_STATE_MOCK,
   chemicals: CHEMICALS_INITIAL_STATE_MOCK,
   models: MODELS_INITIAL_STATE_MOCK,
diff --git a/src/redux/store.ts b/src/redux/store.ts
index 3d2cc350..deaa7268 100644
--- a/src/redux/store.ts
+++ b/src/redux/store.ts
@@ -18,6 +18,7 @@ import overlayBioEntityReducer from '@/redux/overlayBioEntity/overlayBioEntity.s
 import overlaysReducer from '@/redux/overlays/overlays.slice';
 import projectReducer from '@/redux/project/project.slice';
 import projectsReducer from '@/redux/projects/projects.slice';
+import overlayGroupsReducer from '@/redux/overlayGroup/overlayGroup.slice';
 import reactionsReducer from '@/redux/reactions/reactions.slice';
 import newReactionsReducer from '@/redux/newReactions/newReactions.slice';
 import searchReducer from '@/redux/search/search.slice';
@@ -53,6 +54,7 @@ export const reducers = {
   search: searchReducer,
   project: projectReducer,
   projects: projectsReducer,
+  overlayGroups: overlayGroupsReducer,
   drugs: drugsReducer,
   chemicals: chemicalsReducer,
   bioEntity: bioEntityReducer,
diff --git a/src/types/models.ts b/src/types/models.ts
index b432fbfa..03578fac 100644
--- a/src/types/models.ts
+++ b/src/types/models.ts
@@ -70,6 +70,7 @@ import { modificationResiduesSchema } from '@/models/modificationResiduesSchema'
 import { segmentSchema } from '@/models/segmentSchema';
 import { layerImageSchema } from '@/models/layerImageSchema';
 import { glyphSchema } from '@/models/glyphSchema';
+import { overlayGroupSchema } from '@/models/overlayGroupSchema';
 
 export type Project = z.infer<typeof projectSchema>;
 export type OverviewImageView = z.infer<typeof overviewImageView>;
@@ -127,6 +128,7 @@ export type OverlayElementWithReaction = z.infer<typeof overlayElementWithReacti
 export type OverlayElementWithBioEntity = z.infer<typeof overlayElementWithBioEntitySchema>;
 export type OverlayLeftBioEntity = z.infer<typeof overlayLeftBioEntitySchema>;
 export type OverlayLeftReaction = z.infer<typeof overlayLeftReactionSchema>;
+export type OverlayGroup = z.infer<typeof overlayGroupSchema>;
 export type Line = z.infer<typeof lineSchema>;
 export type CreatedOverlayFile = z.infer<typeof createdOverlayFileSchema>;
 export type Color = z.infer<typeof colorSchema>;
-- 
GitLab


From 5eb12f4634dd6de80c35d04d84c8aec1d1510739 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Wed, 5 Feb 2025 16:47:14 +0100
Subject: [PATCH 07/17] adding group overlay added

---
 .../Map/Drawer/Drawer.component.tsx           |  2 +
 .../OverlaysGroupDrawer.component.tsx         | 47 ++++++++++++++++
 .../hooks/useOverlayGroupForm.ts              | 40 ++++++++++++++
 .../Map/Drawer/OverlayGroupDrawer/index.ts    |  1 +
 .../UserOverlays/UserOverlays.component.tsx   | 11 +++-
 src/redux/apiPath.ts                          |  1 +
 src/redux/drawer/drawer.reducers.ts           |  6 +++
 src/redux/drawer/drawer.slice.ts              |  3 ++
 .../overlayGroup/overlayGroup.selectors.ts    |  2 +
 src/redux/overlayGroup/overlayGroup.thunks.ts | 53 ++++++++++++++++++-
 src/redux/root/init.thunks.ts                 |  2 +
 src/types/drawerName.ts                       |  1 +
 12 files changed, 167 insertions(+), 2 deletions(-)
 create mode 100644 src/components/Map/Drawer/OverlayGroupDrawer/OverlaysGroupDrawer.component.tsx
 create mode 100644 src/components/Map/Drawer/OverlayGroupDrawer/hooks/useOverlayGroupForm.ts
 create mode 100644 src/components/Map/Drawer/OverlayGroupDrawer/index.ts

diff --git a/src/components/Map/Drawer/Drawer.component.tsx b/src/components/Map/Drawer/Drawer.component.tsx
index b18e949f..de0f9d35 100644
--- a/src/components/Map/Drawer/Drawer.component.tsx
+++ b/src/components/Map/Drawer/Drawer.component.tsx
@@ -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 />}
diff --git a/src/components/Map/Drawer/OverlayGroupDrawer/OverlaysGroupDrawer.component.tsx b/src/components/Map/Drawer/OverlayGroupDrawer/OverlaysGroupDrawer.component.tsx
new file mode 100644
index 00000000..43188a92
--- /dev/null
+++ b/src/components/Map/Drawer/OverlayGroupDrawer/OverlaysGroupDrawer.component.tsx
@@ -0,0 +1,47 @@
+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>
+    </>
+  );
+};
diff --git a/src/components/Map/Drawer/OverlayGroupDrawer/hooks/useOverlayGroupForm.ts b/src/components/Map/Drawer/OverlayGroupDrawer/hooks/useOverlayGroupForm.ts
new file mode 100644
index 00000000..28b86724
--- /dev/null
+++ b/src/components/Map/Drawer/OverlayGroupDrawer/hooks/useOverlayGroupForm.ts
@@ -0,0 +1,40 @@
+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,
+  };
+};
diff --git a/src/components/Map/Drawer/OverlayGroupDrawer/index.ts b/src/components/Map/Drawer/OverlayGroupDrawer/index.ts
new file mode 100644
index 00000000..5859600f
--- /dev/null
+++ b/src/components/Map/Drawer/OverlayGroupDrawer/index.ts
@@ -0,0 +1 @@
+export { OverlayGroupDrawer } from './OverlaysGroupDrawer.component';
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx
index 60d7cb01..99b57046 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx
@@ -1,4 +1,7 @@
-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>
diff --git a/src/redux/apiPath.ts b/src/redux/apiPath.ts
index 4457eee6..d14f7fc5 100644
--- a/src/redux/apiPath.ts
+++ b/src/redux/apiPath.ts
@@ -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/`,
 };
diff --git a/src/redux/drawer/drawer.reducers.ts b/src/redux/drawer/drawer.reducers.ts
index 88f1096c..156866b2 100644
--- a/src/redux/drawer/drawer.reducers.ts
+++ b/src/redux/drawer/drawer.reducers.ts
@@ -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,
diff --git a/src/redux/drawer/drawer.slice.ts b/src/redux/drawer/drawer.slice.ts
index ddb7c23e..fe752832 100644
--- a/src/redux/drawer/drawer.slice.ts
+++ b/src/redux/drawer/drawer.slice.ts
@@ -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,
diff --git a/src/redux/overlayGroup/overlayGroup.selectors.ts b/src/redux/overlayGroup/overlayGroup.selectors.ts
index bd26e08f..8c2831ee 100644
--- a/src/redux/overlayGroup/overlayGroup.selectors.ts
+++ b/src/redux/overlayGroup/overlayGroup.selectors.ts
@@ -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;
 });
diff --git a/src/redux/overlayGroup/overlayGroup.thunks.ts b/src/redux/overlayGroup/overlayGroup.thunks.ts
index aed7d0a0..711bb8ef 100644
--- a/src/redux/overlayGroup/overlayGroup.thunks.ts
+++ b/src/redux/overlayGroup/overlayGroup.thunks.ts
@@ -1,11 +1,13 @@
 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' }));
+      }
+    }
+  },
+);
diff --git a/src/redux/root/init.thunks.ts b/src/redux/root/init.thunks.ts
index 4514bd87..bc082c8e 100644
--- a/src/redux/root/init.thunks.ts
+++ b/src/redux/root/init.thunks.ts
@@ -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 }));
diff --git a/src/types/drawerName.ts b/src/types/drawerName.ts
index 4a7f5114..36e1055b 100644
--- a/src/types/drawerName.ts
+++ b/src/types/drawerName.ts
@@ -8,6 +8,7 @@ export type DrawerName =
   | 'submaps'
   | 'reaction'
   | 'overlays'
+  | 'overlay-group'
   | 'bio-entity'
   | 'comment'
   | 'available-plugins'
-- 
GitLab


From 0bc669921bd66374c559216c85f75005d680b538 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Thu, 6 Feb 2025 16:36:25 +0100
Subject: [PATCH 08/17] when adding overlay allow to select group

---
 .../UserOverlayForm.component.test.tsx        |  4 +--
 .../hooks/useUserOverlayForm.ts               |  1 +
 .../UserOverlays/UserOverlays.component.tsx   | 16 ++++++++----
 src/redux/overlays/overlays.mock.ts           |  2 ++
 src/redux/overlays/overlays.reducers.test.ts  |  4 ++-
 src/redux/overlays/overlays.thunks.ts         | 25 +++++++++++++------
 .../addDataOverlay/addDataOverlay.test.ts     |  3 +++
 .../overlays/addDataOverlay/addDataOverlay.ts |  5 ++++
 8 files changed, 44 insertions(+), 16 deletions(-)

diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.test.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.test.tsx
index be08de75..6c34319e 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.test.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.test.tsx
@@ -220,7 +220,7 @@ describe('UserOverlayForm - Component', () => {
       .onPost(apiPath.uploadOverlayFileContent(createdOverlayFileFixture.id))
       .reply(HttpStatusCode.Ok, uploadedOverlayFileContentFixture);
 
-    mockedAxiosClient
+    mockedAxiosNewClient
       .onPost(apiPath.createOverlay(projectFixture.projectId))
       .reply(HttpStatusCode.Ok, overlayFixture);
 
@@ -286,7 +286,7 @@ describe('UserOverlayForm - Component', () => {
       .onPost(apiPath.uploadOverlayFileContent(createdOverlayFileFixture.id))
       .reply(HttpStatusCode.Ok, uploadedOverlayFileContentFixture);
 
-    mockedAxiosClient
+    mockedAxiosNewClient
       .onPost(apiPath.createOverlay(projectFixture.projectId))
       .reply(HttpStatusCode.Ok, overlayFixture);
 
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/hooks/useUserOverlayForm.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/hooks/useUserOverlayForm.ts
index 079266ef..7f477bc9 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/hooks/useUserOverlayForm.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/hooks/useUserOverlayForm.ts
@@ -114,6 +114,7 @@ export const useUserOverlayForm = (): ReturnType => {
         name,
         projectId,
         type: type.id,
+        group,
       }),
     );
 
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx
index 99b57046..06fad4e2 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx
@@ -7,10 +7,13 @@ import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { authenticatedUserSelector, loadingUserSelector } from '@/redux/user/user.selectors';
 import { Button } from '@/shared/Button';
 import { userOverlaysDataSelector } from '@/redux/overlays/overlays.selectors';
+import { overlayGroupsSelector } from '@/redux/overlayGroup/overlayGroup.selectors';
+import React from 'react';
 import { UserOverlaysGroup } from './UserOverlaysGroup';
 
 export const UserOverlays = (): JSX.Element => {
   const userOverlays = useAppSelector(userOverlaysDataSelector);
+  const overlayGroups = useAppSelector(overlayGroupsSelector);
 
   const dispatch = useAppDispatch();
   const loadingUser = useAppSelector(loadingUserSelector);
@@ -48,11 +51,14 @@ export const UserOverlays = (): JSX.Element => {
               Add overlay
             </Button>
           </div>
-          <UserOverlaysGroup
-            groupName="Without group"
-            groupId={null}
-            overlays={userOverlays || []}
-          />
+          {overlayGroups.map(group => (
+            <UserOverlaysGroup
+              key={group.id}
+              groupName={group.name}
+              groupId={group.id}
+              overlays={(userOverlays || []).filter(overlay => overlay.group === group.id)}
+            />
+          ))}
         </>
       )}
     </div>
diff --git a/src/redux/overlays/overlays.mock.ts b/src/redux/overlays/overlays.mock.ts
index 7a22bee0..cc43a9ac 100644
--- a/src/redux/overlays/overlays.mock.ts
+++ b/src/redux/overlays/overlays.mock.ts
@@ -1,5 +1,6 @@
 import { DEFAULT_ERROR } from '@/constants/errors';
 import { MapOverlay } from '@/types/models';
+import { DEFAULT_GROUP } from '@/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.constants';
 import { OverlaysState } from './overlays.types';
 
 export const OVERLAYS_INITIAL_STATE_MOCK: OverlaysState = {
@@ -120,6 +121,7 @@ export const ADD_OVERLAY_MOCK = {
   name: 'test',
   projectId: 'pd',
   type: 'GENERIC',
+  group: DEFAULT_GROUP,
 };
 
 export const USER_OVERLAYS_MOCK: MapOverlay[] = [
diff --git a/src/redux/overlays/overlays.reducers.test.ts b/src/redux/overlays/overlays.reducers.test.ts
index f61c7766..d1ac1b6f 100644
--- a/src/redux/overlays/overlays.reducers.test.ts
+++ b/src/redux/overlays/overlays.reducers.test.ts
@@ -139,7 +139,9 @@ describe('overlays reducer', () => {
       .onPost(apiPath.uploadOverlayFileContent(123))
       .reply(HttpStatusCode.Ok, uploadedOverlayFileContentFixture);
 
-    mockedAxiosClient.onPost(apiPath.createOverlay('pd')).reply(HttpStatusCode.Ok, overlayFixture);
+    mockedAxiosNewClient
+      .onPost(apiPath.createOverlay('pd'))
+      .reply(HttpStatusCode.Ok, overlayFixture);
 
     await store.dispatch(addOverlay(ADD_OVERLAY_MOCK));
     const { loading, error } = store.getState().overlays.addOverlay;
diff --git a/src/redux/overlays/overlays.thunks.ts b/src/redux/overlays/overlays.thunks.ts
index b87588f5..94f292ae 100644
--- a/src/redux/overlays/overlays.thunks.ts
+++ b/src/redux/overlays/overlays.thunks.ts
@@ -5,7 +5,7 @@ import {
   uploadedOverlayFileContentSchema,
 } from '@/models/mapOverlaySchema';
 import { axiosInstance, axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
-import { CreatedOverlayFile, MapOverlay, PageOf } from '@/types/models';
+import { CreatedOverlayFile, MapOverlay, OverlayGroup, PageOf } from '@/types/models';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { createAsyncThunk } from '@reduxjs/toolkit';
 import { z } from 'zod';
@@ -155,6 +155,7 @@ type CreatedOverlayArgs = {
   name: string;
   type: string;
   projectId: string;
+  group: OverlayGroup;
 };
 
 const creteOverlay = async ({
@@ -163,20 +164,26 @@ const creteOverlay = async ({
   type,
   name,
   projectId,
+  group,
 }: CreatedOverlayArgs): Promise<MapOverlay | undefined> => {
   const data = {
     name,
     description,
     filename: createdFile.filename,
-    type,
+    colorSchemaType: type,
     fileId: createdFile.id.toString(),
+    group: group.id,
+    public: false,
+    orderIndex: 1,
   };
 
-  const overlay = new URLSearchParams(data);
-
-  const response = await axiosInstance.post<MapOverlay>(apiPath.createOverlay(projectId), overlay, {
-    withCredentials: true,
-  });
+  const response = await axiosInstanceNewAPI.post<MapOverlay>(
+    apiPath.createOverlay(projectId),
+    data,
+    {
+      withCredentials: true,
+    },
+  );
 
   PluginsEventBus.dispatchEvent('onAddDataOverlay', response.data);
 
@@ -192,12 +199,13 @@ type AddOverlayArgs = {
   type: string;
   name: string;
   projectId: string;
+  group: OverlayGroup;
 };
 
 export const addOverlay = createAsyncThunk<undefined, AddOverlayArgs, ThunkConfig>(
   'overlays/addOverlay',
   async (
-    { filename, content, description, name, type, projectId },
+    { filename, content, description, name, type, projectId, group },
     { dispatch },
     // eslint-disable-next-line consistent-return
   ) => {
@@ -218,6 +226,7 @@ export const addOverlay = createAsyncThunk<undefined, AddOverlayArgs, ThunkConfi
         name,
         type,
         projectId,
+        group,
       });
 
       await dispatch(getAllUserOverlaysByCreator());
diff --git a/src/services/pluginsManager/map/overlays/addDataOverlay/addDataOverlay.test.ts b/src/services/pluginsManager/map/overlays/addDataOverlay/addDataOverlay.test.ts
index 25e36dc9..e1bbdd74 100644
--- a/src/services/pluginsManager/map/overlays/addDataOverlay/addDataOverlay.test.ts
+++ b/src/services/pluginsManager/map/overlays/addDataOverlay/addDataOverlay.test.ts
@@ -13,6 +13,7 @@ import {
   ERROR_OVERLAY_NAME_NOT_PROVIDED,
   ERROR_PROJECT_ID_NOT_FOUND,
 } from '@/services/pluginsManager/errorMessages';
+import { DEFAULT_GROUP } from '@/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.constants';
 import { addDataOverlay } from './addDataOverlay';
 import { DEFAULT_FILE_NAME, DEFAULT_TYPE } from './addDataOverlay.constants';
 
@@ -69,6 +70,7 @@ describe('addDataOverlay', () => {
       name: overlay.name,
       projectId: projectFixture.projectId,
       type: overlay.type,
+      group: DEFAULT_GROUP,
     });
   });
 
@@ -119,6 +121,7 @@ describe('addDataOverlay', () => {
       name: overlay.name,
       projectId: projectFixture.projectId,
       type: DEFAULT_TYPE,
+      group: DEFAULT_GROUP,
     });
   });
 });
diff --git a/src/services/pluginsManager/map/overlays/addDataOverlay/addDataOverlay.ts b/src/services/pluginsManager/map/overlays/addDataOverlay/addDataOverlay.ts
index d52b3518..c74771d9 100644
--- a/src/services/pluginsManager/map/overlays/addDataOverlay/addDataOverlay.ts
+++ b/src/services/pluginsManager/map/overlays/addDataOverlay/addDataOverlay.ts
@@ -5,6 +5,8 @@ import {
   ERROR_OVERLAY_NAME_NOT_PROVIDED,
   ERROR_PROJECT_ID_NOT_FOUND,
 } from '@/services/pluginsManager/errorMessages';
+import { OverlayGroup } from '@/types/models';
+import { DEFAULT_GROUP } from '@/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.constants';
 import { DEFAULT_FILE_NAME, DEFAULT_TYPE } from './addDataOverlay.constants';
 import { getOverlayContent } from './addDataOverlay.utils';
 
@@ -14,6 +16,7 @@ type AddDataOverlayArgs = {
   filename?: string;
   fileContent: string;
   type?: string;
+  group?: OverlayGroup;
 };
 
 export const addDataOverlay = async ({
@@ -22,6 +25,7 @@ export const addDataOverlay = async ({
   filename,
   fileContent,
   type,
+  group,
 }: AddDataOverlayArgs): Promise<void> => {
   const { dispatch, getState } = store;
   const projectId = projectIdSelector(getState());
@@ -40,6 +44,7 @@ export const addDataOverlay = async ({
       name,
       projectId,
       type: type || DEFAULT_TYPE,
+      group: group || DEFAULT_GROUP,
     }),
   );
 };
-- 
GitLab


From da5424c73afd315861085f5469a29855b8d05524 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Fri, 7 Feb 2025 10:41:49 +0100
Subject: [PATCH 09/17] after overlay is edited refresh the list

---
 .../UserOverlays/UserOverlays.component.tsx       |  7 ++++---
 .../hooks/useUserOverlays.test.ts                 | 15 ++++++++++-----
 .../UserOverlaysGroup/hooks/useUserOverlays.ts    | 10 ++++++++--
 src/redux/overlays/overlays.selectors.ts          |  2 +-
 4 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx
index 06fad4e2..9b5a609a 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx
@@ -7,8 +7,9 @@ import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { authenticatedUserSelector, loadingUserSelector } from '@/redux/user/user.selectors';
 import { Button } from '@/shared/Button';
 import { userOverlaysDataSelector } from '@/redux/overlays/overlays.selectors';
-import { overlayGroupsSelector } from '@/redux/overlayGroup/overlayGroup.selectors';
 import React from 'react';
+import { overlayGroupsSelector } from '@/redux/overlayGroup/overlayGroup.selectors';
+import { OverlayGroup } from '@/types/models';
 import { UserOverlaysGroup } from './UserOverlaysGroup';
 
 export const UserOverlays = (): JSX.Element => {
@@ -51,12 +52,12 @@ export const UserOverlays = (): JSX.Element => {
               Add overlay
             </Button>
           </div>
-          {overlayGroups.map(group => (
+          {overlayGroups.map((group: OverlayGroup) => (
             <UserOverlaysGroup
               key={group.id}
               groupName={group.name}
               groupId={group.id}
-              overlays={(userOverlays || []).filter(overlay => overlay.group === group.id)}
+              overlays={userOverlays.filter(overlay => overlay.group === group.id)}
             />
           ))}
         </>
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.test.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.test.ts
index c9777f59..64a7c1cb 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.test.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.test.ts
@@ -86,9 +86,12 @@ describe('useUserOverlays', () => {
     const FIRST_USER_OVERLAY = overlayFixture;
     const SECOND_USER_OVERLAY = overlayFixture;
 
+    const overlays = [FIRST_USER_OVERLAY, SECOND_USER_OVERLAY];
+    const reversedOverlays = [SECOND_USER_OVERLAY, FIRST_USER_OVERLAY];
+
     const page = {
       ...overlaysPageFixture,
-      data: [FIRST_USER_OVERLAY, SECOND_USER_OVERLAY],
+      data: overlays,
     };
     mockedAxiosNewClient
       .onGet(
@@ -111,7 +114,7 @@ describe('useUserOverlays', () => {
       overlays: {
         ...OVERLAYS_INITIAL_STATE_MOCK,
         userOverlays: {
-          data: [FIRST_USER_OVERLAY, SECOND_USER_OVERLAY],
+          data: overlays,
           loading: 'idle',
           error: DEFAULT_ERROR,
         },
@@ -122,7 +125,7 @@ describe('useUserOverlays', () => {
       result: {
         current: { moveUserOverlayListItem, userOverlaysList },
       },
-    } = renderHook(() => useUserOverlays([FIRST_USER_OVERLAY, SECOND_USER_OVERLAY], null), {
+    } = renderHook(() => useUserOverlays(overlays, null), {
       wrapper: Wrapper,
     });
 
@@ -130,7 +133,7 @@ describe('useUserOverlays', () => {
       moveUserOverlayListItem(0, 1);
     });
 
-    expect(userOverlaysList).toEqual([SECOND_USER_OVERLAY, FIRST_USER_OVERLAY]);
+    expect(userOverlaysList).toEqual(reversedOverlays);
   });
   it('calls updateOverlays on calling updateUserOverlaysOrder', async () => {
     const FIRST_USER_OVERLAY = { ...overlayFixture, order: 1, id: 12 };
@@ -169,11 +172,13 @@ describe('useUserOverlays', () => {
       },
     });
 
+    const originalOverlays = [FIRST_USER_OVERLAY, SECOND_USER_OVERLAY];
+
     const {
       result: {
         current: { moveUserOverlayListItem, updateUserOverlaysOrder },
       },
-    } = renderHook(() => useUserOverlays([FIRST_USER_OVERLAY, SECOND_USER_OVERLAY], null), {
+    } = renderHook(() => useUserOverlays(originalOverlays, null), {
       wrapper: Wrapper,
     });
 
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
index 5ec4bb93..a9e66fb0 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
@@ -4,7 +4,7 @@ import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { loadingUserOverlaysSelector } from '@/redux/overlays/overlays.selectors';
 import { updateOverlays } from '@/redux/overlays/overlays.thunks';
 import { MapOverlay } from '@/types/models';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
 import { moveArrayElement } from '../UserOverlaysGroup.utils';
 
 type UseUserOverlaysReturn = {
@@ -19,10 +19,16 @@ export const useUserOverlays = (
   groupId: number | null,
 ): UseUserOverlaysReturn => {
   const dispatch = useAppDispatch();
-  const [userOverlaysList, setUserOverlaysList] = useState<MapOverlay[]>(userOverlays);
+  const [userOverlaysList, setUserOverlaysList] = useState<MapOverlay[]>([]);
   const loadingUserOverlays = useAppSelector(loadingUserOverlaysSelector);
   const isPending = loadingUserOverlays === 'pending';
 
+  useEffect(() => {
+    if (userOverlays) {
+      setUserOverlaysList(userOverlays);
+    }
+  }, [userOverlays]);
+
   const moveUserOverlayListItem = (dragIndex: number, hoverIndex: number): void => {
     const updatedUserOverlays = moveArrayElement(userOverlaysList, dragIndex, hoverIndex);
     setUserOverlaysList(updatedUserOverlays);
diff --git a/src/redux/overlays/overlays.selectors.ts b/src/redux/overlays/overlays.selectors.ts
index 35862e6e..7086ad31 100644
--- a/src/redux/overlays/overlays.selectors.ts
+++ b/src/redux/overlays/overlays.selectors.ts
@@ -31,7 +31,7 @@ export const loadingUserOverlaysSelector = createSelector(
 
 export const userOverlaysDataSelector = createSelector(
   userOverlaysSelector,
-  overlays => overlays.data,
+  overlays => overlays.data || [],
 );
 
 export const userOverlaysIdsAndOrderSelector = createSelector(
-- 
GitLab


From 0a2209aaf653177910de819b9bfef8bd059cab7d Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Fri, 7 Feb 2025 17:51:32 +0100
Subject: [PATCH 10/17] update group on drag and drop

---
 .../UserOverlayListItem.component.tsx            |  4 +++-
 .../UserOverlayListItem/hooks/useDragAndDrop.ts  | 15 ++++++++++-----
 .../UserOverlaysGroup.component.tsx              |  1 +
 .../hooks/useUserOverlays.test.ts                |  2 +-
 .../UserOverlaysGroup/hooks/useUserOverlays.ts   | 16 ++++++++++++++--
 5 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayListItem.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayListItem.component.tsx
index d67e3b47..c99f9ec9 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayListItem.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayListItem.component.tsx
@@ -12,7 +12,7 @@ type OverlayListItemProps = {
   index: number;
   moveUserOverlay: (dragIndex: number, hoverIndex: number) => void;
   userOverlay: MapOverlay;
-  updateUserOverlaysOrder: () => void;
+  updateUserOverlaysOrder: (overlay: MapOverlay, targetGroupId: number | null) => void;
 };
 
 export const UserOverlayListItem = ({
@@ -26,6 +26,8 @@ export const UserOverlayListItem = ({
     onDrop: updateUserOverlaysOrder,
     onHover: moveUserOverlay,
     index,
+    groupId: userOverlay.group,
+    overlay: userOverlay,
   });
 
   return (
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/hooks/useDragAndDrop.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/hooks/useDragAndDrop.ts
index 6e3eebc9..d899295a 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/hooks/useDragAndDrop.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/hooks/useDragAndDrop.ts
@@ -1,12 +1,15 @@
 /* eslint-disable no-param-reassign */
 import { ConnectDragSource, ConnectDropTarget, useDrag, useDrop } from 'react-dnd';
+import { MapOverlay } from '@/types/models';
 
 const ITEM_TYPE = 'card';
 
 type UseDragAndDropProps = {
   index: number;
+  overlay: MapOverlay;
+  groupId: number | null;
   onHover: (dragIndex: number, hoverIndex: number) => void;
-  onDrop: () => void;
+  onDrop: (overlay: MapOverlay, targetGroupId: number | null) => void;
 };
 
 type UseDragAndDropReturn = {
@@ -19,10 +22,12 @@ export const useDragAndDrop = ({
   index,
   onDrop,
   onHover,
+  groupId,
+  overlay,
 }: UseDragAndDropProps): UseDragAndDropReturn => {
   const [{ isDragging }, dragRef] = useDrag({
     type: ITEM_TYPE,
-    item: { index },
+    item: { index, overlay, groupId },
     collect: monitor => ({
       isDragging: monitor.isDragging(),
     }),
@@ -30,7 +35,7 @@ export const useDragAndDrop = ({
 
   const [, dropRef] = useDrop({
     accept: ITEM_TYPE,
-    hover: (item: { index: number }) => {
+    hover: (item: { index: number; groupId: number | null; overlay: MapOverlay }) => {
       const dragIndex = item.index;
       const hoverIndex = index;
 
@@ -38,8 +43,8 @@ export const useDragAndDrop = ({
 
       item.index = hoverIndex;
     },
-    drop() {
-      onDrop();
+    drop(item: { index: number; groupId: number | null; overlay: MapOverlay }) {
+      onDrop(item.overlay, groupId);
     },
   });
 
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
index 6bcf4790..e3652092 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
@@ -8,6 +8,7 @@ import {
 import { DndProvider } from 'react-dnd';
 import { HTML5Backend } from 'react-dnd-html5-backend';
 import { MapOverlay } from '@/types/models';
+import React from 'react';
 import { UserOverlayListItem } from './UserOverlayListItem';
 import { useUserOverlays } from './hooks/useUserOverlays';
 
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.test.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.test.ts
index 64a7c1cb..3f34c159 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.test.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.test.ts
@@ -186,7 +186,7 @@ describe('useUserOverlays', () => {
       moveUserOverlayListItem(0, 1);
     });
 
-    updateUserOverlaysOrder();
+    updateUserOverlaysOrder(FIRST_USER_OVERLAY, null);
 
     const actions = store.getActions();
 
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
index a9e66fb0..23166dd7 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
@@ -11,7 +11,7 @@ type UseUserOverlaysReturn = {
   isPending: boolean;
   userOverlaysList: MapOverlay[];
   moveUserOverlayListItem: (dragIndex: number, hoverIndex: number) => void;
-  updateUserOverlaysOrder: () => void;
+  updateUserOverlaysOrder: (overlay: MapOverlay, targetGroupId: number | null) => void;
 };
 
 export const useUserOverlays = (
@@ -30,14 +30,24 @@ export const useUserOverlays = (
   }, [userOverlays]);
 
   const moveUserOverlayListItem = (dragIndex: number, hoverIndex: number): void => {
+    // eslint-disable-next-line no-console
     const updatedUserOverlays = moveArrayElement(userOverlaysList, dragIndex, hoverIndex);
     setUserOverlaysList(updatedUserOverlays);
   };
 
-  const updateUserOverlaysOrder = (): void => {
+  const updateUserOverlaysOrder = (overlay: MapOverlay, targetGroupId: number | null): void => {
     const reorderedUserOverlays = [];
     if (!userOverlays) return;
 
+    // eslint-disable-next-line no-console
+    console.log('update', overlay, targetGroupId);
+    if (targetGroupId !== overlay.group) {
+      const newOverlay = {
+        ...overlay,
+        group: targetGroupId,
+      };
+      reorderedUserOverlays.push(newOverlay);
+    }
     for (let index = 0; index < userOverlays.length; index += 1) {
       const userOverlay = userOverlays[index];
       const newOrderedUserOverlay = {
@@ -50,6 +60,8 @@ export const useUserOverlays = (
         reorderedUserOverlays.push(newOrderedUserOverlay);
       }
     }
+    // eslint-disable-next-line no-console
+    console.log(reorderedUserOverlays);
 
     dispatch(updateOverlays(reorderedUserOverlays));
   };
-- 
GitLab


From 557450dd1aff4de3072bffcac31451c0138b4606 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Fri, 7 Feb 2025 18:58:00 +0100
Subject: [PATCH 11/17] allow to edit overlay group

---
 .../EditOverlayModal.component.test.tsx       | 22 +++++++++---------
 .../EditOverlayModal.component.tsx            | 10 ++++++++
 .../hooks/useEditOverlay.test.ts              |  7 +++---
 .../EditOverlayModal/hooks/useEditOverlay.ts  | 23 +++++++++++++++++--
 .../hooks/useUserOverlays.ts                  |  2 --
 .../overlayGroup/overlayGroup.selectors.ts    |  2 --
 src/redux/overlays/overlays.reducers.test.ts  |  8 +++----
 src/redux/overlays/overlays.thunks.ts         |  7 ++++--
 8 files changed, 55 insertions(+), 26 deletions(-)

diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
index 8140cfe6..fe837db8 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
@@ -35,6 +35,10 @@ const renderComponent = (initialStoreState: InitialStoreState = {}): { store: St
   );
 };
 
+const overlay = {
+  ...overlayFixture,
+  group: null,
+};
 describe('EditOverlayModal - component', () => {
   beforeEach(() => {
     jest.clearAllMocks();
@@ -174,9 +178,9 @@ describe('EditOverlayModal - component', () => {
       },
       modal: {
         isOpen: true,
-        modalTitle: overlayFixture.name,
+        modalTitle: overlay.name,
         modalName: 'edit-overlay',
-        editOverlayState: overlayFixture,
+        editOverlayState: overlay,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
@@ -186,13 +190,11 @@ describe('EditOverlayModal - component', () => {
       },
       overlays: OVERLAYS_INITIAL_STATE_MOCK,
     });
-    mockedAxiosClient
-      .onPatch(apiPath.updateOverlay(overlayFixture.id))
-      .reply(HttpStatusCode.Ok, overlayFixture);
+    mockedAxiosNewClient.onPut(apiPath.updateOverlay(overlay.id)).reply(HttpStatusCode.Ok, overlay);
 
     const page = {
       ...overlaysPageFixture,
-      data: [overlayFixture],
+      data: [overlay],
     };
 
     mockedAxiosNewClient
@@ -221,9 +223,9 @@ describe('EditOverlayModal - component', () => {
       },
       modal: {
         isOpen: true,
-        modalTitle: overlayFixture.name,
+        modalTitle: overlay.name,
         modalName: 'edit-overlay',
-        editOverlayState: overlayFixture,
+        editOverlayState: overlay,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
@@ -233,9 +235,7 @@ describe('EditOverlayModal - component', () => {
       },
       overlays: OVERLAYS_INITIAL_STATE_MOCK,
     });
-    mockedAxiosClient
-      .onPatch(apiPath.updateOverlay(overlayFixture.id))
-      .reply(HttpStatusCode.Ok, overlayFixture);
+    mockedAxiosNewClient.onPut(apiPath.updateOverlay(overlay.id)).reply(HttpStatusCode.Ok, overlay);
 
     const saveButton = screen.getByTestId('save-button');
     expect(saveButton).toBeVisible();
diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.tsx b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.tsx
index 46091536..55d70e7f 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.tsx
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.tsx
@@ -2,15 +2,19 @@ import { Button } from '@/shared/Button';
 import { Input } from '@/shared/Input';
 import { Textarea } from '@/shared/Textarea';
 import React from 'react';
+import { OverlayGroupSelector } from '@/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/OverlaySelector/OverlayGroupSelector.component';
 import { useEditOverlay } from './hooks/useEditOverlay';
 
 export const EditOverlayModal = (): React.ReactNode => {
   const {
+    overlayGroups,
     description,
     name,
+    group,
     handleCancelEdit,
     handleDescriptionChange,
     handleNameChange,
+    handleGroupChange,
     handleRemoveOverlay,
     handleSaveEditedOverlay,
   } = useEditOverlay();
@@ -42,6 +46,12 @@ export const EditOverlayModal = (): React.ReactNode => {
             data-testid="overlay-description"
           />
         </label>
+        <OverlayGroupSelector
+          value={group}
+          onChange={handleGroupChange}
+          items={overlayGroups}
+          label="Select group"
+        />
         <div className="mt-10 flex items-center justify-between gap-5 text-center">
           <Button
             type="button"
diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts
index bb85187c..b2be1b21 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts
@@ -126,6 +126,7 @@ describe('useEditOverlay', () => {
     expect(actions.length).toBe(0);
   });
   it('should handle handleSaveEditedOverlay if proper data is provided', () => {
+    const overlay = { ...overlayFixture, group: null };
     const { Wrapper, store } = getReduxStoreWithActionsListener({
       user: {
         authenticated: true,
@@ -137,9 +138,9 @@ describe('useEditOverlay', () => {
       },
       modal: {
         isOpen: true,
-        modalTitle: overlayFixture.name,
+        modalTitle: overlay.name,
         modalName: 'edit-overlay',
-        editOverlayState: overlayFixture,
+        editOverlayState: overlay,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
@@ -162,7 +163,7 @@ describe('useEditOverlay', () => {
     const actions = store.getActions();
 
     expect(actions[0].type).toBe('overlays/updateOverlays/pending');
-    expect(actions[0].meta.arg).toEqual([overlayFixture]);
+    expect(actions[0].meta.arg).toEqual([overlay]);
   });
   it('should not handle handleSaveEditedOverlay if proper data is not provided', () => {
     const { Wrapper, store } = getReduxStoreWithActionsListener({
diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts
index 6012da5d..6b4e2fab 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts
@@ -8,16 +8,21 @@ import {
   updateOverlays,
 } from '@/redux/overlays/overlays.thunks';
 import { loginUserSelector } from '@/redux/user/user.selectors';
-import { MapOverlay } from '@/types/models';
-import { useState } from 'react';
+import { MapOverlay, OverlayGroup } from '@/types/models';
+import React, { useState } from 'react';
+import { overlayGroupsSelector } from '@/redux/overlayGroup/overlayGroup.selectors';
+import { ZERO } from '@/constants/common';
 
 type UseEditOverlayReturn = {
   name: string | undefined;
   description: string | undefined;
+  group: OverlayGroup;
+  overlayGroups: OverlayGroup[];
   handleCancelEdit: () => void;
   handleRemoveOverlay: () => void;
   handleSaveEditedOverlay: () => Promise<void>;
   handleNameChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
+  handleGroupChange: (value: OverlayGroup) => void;
   handleDescriptionChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
 };
 
@@ -25,14 +30,19 @@ type UpdatedOverlay = {
   editedOverlay: MapOverlay;
   overlayName: string;
   overlayDescription: string;
+  overlayGroup: OverlayGroup;
 };
 
 export const useEditOverlay = (): UseEditOverlayReturn => {
+  const overlayGroups = useAppSelector(overlayGroupsSelector);
   const currentEditedOverlay = useAppSelector(currentEditedOverlaySelector);
   const login = useAppSelector(loginUserSelector);
   const dispatch = useAppDispatch();
   const [name, setName] = useState(currentEditedOverlay?.name);
   const [description, setDescription] = useState(currentEditedOverlay?.description);
+  const [group, setGroup] = useState<OverlayGroup>(
+    overlayGroups.filter(overlayGroup => overlayGroup.id === currentEditedOverlay?.group)[ZERO],
+  );
 
   const handleCancelEdit = (): void => {
     dispatch(closeModal());
@@ -45,6 +55,9 @@ export const useEditOverlay = (): UseEditOverlayReturn => {
   const handleDescriptionChange = (e: React.ChangeEvent<HTMLTextAreaElement>): void => {
     setDescription(e.target.value);
   };
+  const handleGroupChange = (value: OverlayGroup): void => {
+    setGroup(value);
+  };
 
   const handleRemoveOverlay = (): void => {
     if (!login || !currentEditedOverlay) return;
@@ -55,6 +68,7 @@ export const useEditOverlay = (): UseEditOverlayReturn => {
     editedOverlay,
     overlayDescription,
     overlayName,
+    overlayGroup,
   }: UpdatedOverlay): Promise<void> => {
     await dispatch(
       updateOverlays([
@@ -62,6 +76,7 @@ export const useEditOverlay = (): UseEditOverlayReturn => {
           ...editedOverlay,
           name: overlayName,
           description: overlayDescription,
+          group: overlayGroup.id,
         },
       ]),
     );
@@ -81,6 +96,7 @@ export const useEditOverlay = (): UseEditOverlayReturn => {
       editedOverlay: currentEditedOverlay,
       overlayDescription: description || '',
       overlayName: name,
+      overlayGroup: group,
     });
 
     await getUserOverlaysByCreator();
@@ -94,7 +110,10 @@ export const useEditOverlay = (): UseEditOverlayReturn => {
     handleSaveEditedOverlay,
     handleNameChange,
     handleDescriptionChange,
+    handleGroupChange,
     name,
     description,
+    group,
+    overlayGroups,
   };
 };
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
index 23166dd7..b03b2535 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
@@ -39,8 +39,6 @@ export const useUserOverlays = (
     const reorderedUserOverlays = [];
     if (!userOverlays) return;
 
-    // eslint-disable-next-line no-console
-    console.log('update', overlay, targetGroupId);
     if (targetGroupId !== overlay.group) {
       const newOverlay = {
         ...overlay,
diff --git a/src/redux/overlayGroup/overlayGroup.selectors.ts b/src/redux/overlayGroup/overlayGroup.selectors.ts
index 8c2831ee..bd26e08f 100644
--- a/src/redux/overlayGroup/overlayGroup.selectors.ts
+++ b/src/redux/overlayGroup/overlayGroup.selectors.ts
@@ -10,7 +10,5 @@ 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;
 });
diff --git a/src/redux/overlays/overlays.reducers.test.ts b/src/redux/overlays/overlays.reducers.test.ts
index d1ac1b6f..0cc3e232 100644
--- a/src/redux/overlays/overlays.reducers.test.ts
+++ b/src/redux/overlays/overlays.reducers.test.ts
@@ -175,8 +175,8 @@ describe('overlays reducer', () => {
   });
 
   it('should update store after successful updateOverlay', async () => {
-    mockedAxiosClient
-      .onPatch(apiPath.updateOverlay(overlayFixture.id))
+    mockedAxiosNewClient
+      .onPut(apiPath.updateOverlay(overlayFixture.id))
       .reply(HttpStatusCode.Ok, overlayFixture);
 
     const updateUserOverlaysPromise = store.dispatch(updateOverlays([overlayFixture]));
@@ -190,8 +190,8 @@ describe('overlays reducer', () => {
     expect(error).toEqual({ message: '', name: '' });
   });
   it('should update store after failed updateOverlay', async () => {
-    mockedAxiosClient
-      .onPatch(apiPath.updateOverlay(overlayFixture.id))
+    mockedAxiosNewClient
+      .onPut(apiPath.updateOverlay(overlayFixture.id))
       .reply(HttpStatusCode.NotFound, {});
 
     await store.dispatch(updateOverlays([overlayFixture]));
diff --git a/src/redux/overlays/overlays.thunks.ts b/src/redux/overlays/overlays.thunks.ts
index 94f292ae..11352f9f 100644
--- a/src/redux/overlays/overlays.thunks.ts
+++ b/src/redux/overlays/overlays.thunks.ts
@@ -249,10 +249,13 @@ export const updateOverlays = createAsyncThunk<undefined, MapOverlay[], ThunkCon
   async userOverlays => {
     try {
       const userOverlaysPromises = userOverlays.map(userOverlay =>
-        axiosInstance.patch<MapOverlay>(
+        axiosInstanceNewAPI.put<MapOverlay>(
           apiPath.updateOverlay(userOverlay.id),
           {
-            overlay: userOverlay,
+            ...userOverlay,
+            orderIndex: userOverlay.order,
+            colorSchemaType: userOverlay.type,
+            public: userOverlay.publicOverlay,
           },
           {
             withCredentials: true,
-- 
GitLab


From f5fa4c15f2f15b13a88c3a7e5f3f3a0bc4b34098 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Fri, 7 Feb 2025 19:09:07 +0100
Subject: [PATCH 12/17] refresh list of overlays after changing group by drag
 and drop

---
 .../UserOverlaysGroup/hooks/useUserOverlays.ts      | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
index b03b2535..0ec79ece 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/hooks/useUserOverlays.ts
@@ -2,7 +2,7 @@
 import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
 import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { loadingUserOverlaysSelector } from '@/redux/overlays/overlays.selectors';
-import { updateOverlays } from '@/redux/overlays/overlays.thunks';
+import { getAllUserOverlaysByCreator, updateOverlays } from '@/redux/overlays/overlays.thunks';
 import { MapOverlay } from '@/types/models';
 import { useEffect, useState } from 'react';
 import { moveArrayElement } from '../UserOverlaysGroup.utils';
@@ -35,7 +35,10 @@ export const useUserOverlays = (
     setUserOverlaysList(updatedUserOverlays);
   };
 
-  const updateUserOverlaysOrder = (overlay: MapOverlay, targetGroupId: number | null): void => {
+  const updateUserOverlaysOrder = async (
+    overlay: MapOverlay,
+    targetGroupId: number | null,
+  ): Promise<void> => {
     const reorderedUserOverlays = [];
     if (!userOverlays) return;
 
@@ -58,10 +61,8 @@ export const useUserOverlays = (
         reorderedUserOverlays.push(newOrderedUserOverlay);
       }
     }
-    // eslint-disable-next-line no-console
-    console.log(reorderedUserOverlays);
-
-    dispatch(updateOverlays(reorderedUserOverlays));
+    await dispatch(updateOverlays(reorderedUserOverlays));
+    dispatch(getAllUserOverlaysByCreator());
   };
 
   return {
-- 
GitLab


From d34a0af0c697fb0590c576b5e6ace4aa5e4e937d Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Fri, 14 Feb 2025 07:56:58 +0100
Subject: [PATCH 13/17] sort overlays

---
 CHANGELOG                             | 1 +
 src/redux/overlays/overlays.thunks.ts | 4 +++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG b/CHANGELOG
index 4ecb023a..bb23454f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 minerva-front (18.1.1) stable; urgency=medium
   * Bug fix: styling of notes reset only for a href (#334)
   * Bug fix: disable searching for chemicals in projects without disease (#347)
+  * Bug fix: public overlays were not sorted (#349)
 
 -- Piotr Gawron <piotr.gawron@uni.lu>  Tue, 04 Feb 2025 16:00:00 +0200
 
diff --git a/src/redux/overlays/overlays.thunks.ts b/src/redux/overlays/overlays.thunks.ts
index 389da306..54ea26d8 100644
--- a/src/redux/overlays/overlays.thunks.ts
+++ b/src/redux/overlays/overlays.thunks.ts
@@ -41,7 +41,9 @@ export const getAllPublicOverlaysByProjectId = createAsyncThunk<MapOverlay[], st
 
       const isDataValid = validateDataUsingZodSchema(response.data, z.array(mapOverlay));
 
-      return isDataValid ? response.data : [];
+      return isDataValid
+        ? response.data.sort((overlayA, overlayB) => overlayA.order - overlayB.order)
+        : [];
     } catch (error) {
       return Promise.reject(getError({ error, prefix: OVERLAYS_FETCHING_ERROR_PREFIX }));
     }
-- 
GitLab


From bf124d6e432fc03d6af4adde2775a36c6940ab20 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Fri, 14 Feb 2025 10:41:06 +0100
Subject: [PATCH 14/17] allow to drag and drop overlays between groups

---
 .../UserOverlayInfo.component.tsx             | 22 +++++----
 .../UserOverlayListItem.component.tsx         | 48 +++++++++++--------
 .../UserOverlaysGroup.component.tsx           | 21 ++++++++
 3 files changed, 60 insertions(+), 31 deletions(-)

diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayInfo/UserOverlayInfo.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayInfo/UserOverlayInfo.component.tsx
index 78dff20c..0d0a5ca5 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayInfo/UserOverlayInfo.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayInfo/UserOverlayInfo.component.tsx
@@ -15,18 +15,20 @@ export const UserOverlayInfo = ({ description, name }: UserOverlayInfoProps): Re
     <div className="flex items-center gap-x-2.5">
       <span className="text-sm">{name}</span>
 
-      <div className="group relative" data-testid="info">
-        <Icon name="info" className="h-4 w-4 fill-black" />
+      {description !== '' && (
+        <div className="group relative" data-testid="info">
+          <Icon name="info" className="h-4 w-4 fill-black" />
 
-        <div
-          className={twMerge(
-            'absolute bottom-0 left-0 top-auto z-20 hidden min-w-[200px] max-w-xs rounded-lg bg-white px-3  py-4 drop-shadow-md group-hover:block',
-            isOverflowPossibility && 'min-w-[100px] max-w-[200px]',
-          )}
-        >
-          <p className="text-xs">{description}</p>
+          <div
+            className={twMerge(
+              'absolute bottom-0 left-0 top-auto z-20 hidden min-w-[200px] max-w-xs rounded-lg bg-white px-3  py-4 drop-shadow-md group-hover:block',
+              isOverflowPossibility && 'min-w-[100px] max-w-[200px]',
+            )}
+          >
+            <p className="text-xs">{description}</p>
+          </div>
         </div>
-      </div>
+      )}
     </div>
   );
 };
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayListItem.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayListItem.component.tsx
index c99f9ec9..8bfc188e 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayListItem.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlayListItem/UserOverlayListItem.component.tsx
@@ -3,6 +3,7 @@ import { MapOverlay } from '@/types/models';
 import { twMerge } from 'tailwind-merge';
 import Image from 'next/image';
 import spinnerIcon from '@/assets/vectors/icons/spinner.svg';
+import { ZERO } from '@/constants/common';
 import { useOverlay } from '../../../hooks/useOverlay';
 import { UserOverlayActions } from './UserOverlayActions';
 import { UserOverlayInfo } from './UserOverlayInfo/UserOverlayInfo.component';
@@ -38,27 +39,32 @@ export const UserOverlayListItem = ({
         isDragging ? 'opacity-0' : 'opacity-100',
       )}
     >
-      <UserOverlayInfo description={userOverlay.description} name={userOverlay.name} />
-      <div className="flex flex-row flex-nowrap items-center">
-        <Button
-          variantStyles="ghost"
-          className="mr-4 max-h-8 flex-none gap-1.5"
-          onClick={toggleOverlay}
-          data-testid="toggle-overlay-button"
-        >
-          {isOverlayLoading && (
-            <Image
-              src={spinnerIcon}
-              alt="spinner icon"
-              height={12}
-              width={12}
-              className="animate-spin"
-            />
-          )}
-          {isOverlayActive || isOverlayLoading ? 'Hide' : 'View'}
-        </Button>
-        <UserOverlayActions overlay={userOverlay} />
-      </div>
+      {userOverlay.id > ZERO && (
+        <UserOverlayInfo description={userOverlay.description} name={userOverlay.name} />
+      )}
+
+      {userOverlay.id > ZERO && (
+        <div className="flex flex-row flex-nowrap items-center">
+          <Button
+            variantStyles="ghost"
+            className="mr-4 max-h-8 flex-none gap-1.5"
+            onClick={toggleOverlay}
+            data-testid="toggle-overlay-button"
+          >
+            {isOverlayLoading && (
+              <Image
+                src={spinnerIcon}
+                alt="spinner icon"
+                height={12}
+                width={12}
+                className="animate-spin"
+              />
+            )}
+            {isOverlayActive || isOverlayLoading ? 'Hide' : 'View'}
+          </Button>
+          <UserOverlayActions overlay={userOverlay} />
+        </div>
+      )}
     </li>
   );
 };
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
index e3652092..7e6053a2 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
@@ -9,6 +9,7 @@ import { DndProvider } from 'react-dnd';
 import { HTML5Backend } from 'react-dnd-html5-backend';
 import { MapOverlay } from '@/types/models';
 import React from 'react';
+import { ZERO } from '@/constants/common';
 import { UserOverlayListItem } from './UserOverlayListItem';
 import { useUserOverlays } from './hooks/useUserOverlays';
 
@@ -26,6 +27,19 @@ export const UserOverlaysGroup = ({
   const { moveUserOverlayListItem, updateUserOverlaysOrder, isPending, userOverlaysList } =
     useUserOverlays(overlays, groupId);
 
+  const nullOverlay: MapOverlay = {
+    id: groupId ? -groupId : ZERO,
+    group: groupId,
+    publicOverlay: false,
+    creator: '',
+    type: '',
+    description: '',
+    name: '',
+    genomeType: '',
+    genomeVersion: '',
+    order: 0,
+  };
+
   return (
     <DndProvider backend={HTML5Backend}>
       <div className="mt-2.5">
@@ -50,6 +64,13 @@ export const UserOverlaysGroup = ({
                       updateUserOverlaysOrder={updateUserOverlaysOrder}
                     />
                   ))}
+                  <UserOverlayListItem
+                    moveUserOverlay={moveUserOverlayListItem}
+                    key={nullOverlay.id}
+                    index={0}
+                    userOverlay={nullOverlay}
+                    updateUserOverlaysOrder={updateUserOverlaysOrder}
+                  />
                 </ul>
               )}
             </AccordionItemPanel>
-- 
GitLab


From d4a78ea917ccc6de33eb18018a48882c3c97cb59 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Fri, 14 Feb 2025 14:03:31 +0100
Subject: [PATCH 15/17] allow to edit/remove group overlay

---
 .../EditOverlayGroupModal.component.test.tsx  | 297 ++++++++++++++++++
 .../EditOverlayGroupModal.component.tsx       |  76 +++++
 .../hooks/useEditOverlayGroup.test.ts         | 217 +++++++++++++
 .../hooks/useEditOverlayGroup.ts              | 101 ++++++
 .../Modal/EditOverlayGroupModal/index.ts      |   1 +
 .../EditOverlayModal.component.test.tsx       |   8 +
 .../hooks/useEditOverlay.test.ts              |   6 +
 ...eObjectEditFactoryModal.component.test.tsx |   2 +
 ...ImageObjectFactoryModal.component.test.tsx |   2 +
 .../LayerTextFactoryModal.component.test.tsx  |   2 +
 .../FunctionalArea/Modal/Modal.component.tsx  |   6 +
 .../UserOverlaysGroup.component.tsx           |  30 +-
 src/redux/apiPath.ts                          |   4 +
 src/redux/modal/modal.constants.ts            |   1 +
 src/redux/modal/modal.mock.ts                 |   1 +
 src/redux/modal/modal.reducers.ts             |  16 +-
 src/redux/modal/modal.selector.ts             |   5 +
 src/redux/modal/modal.slice.ts                |   3 +
 src/redux/modal/modal.types.ts                |   8 +-
 .../overlayGroup/overlayGroup.selectors.ts    |   9 +-
 src/redux/overlayGroup/overlayGroup.thunks.ts |  60 +++-
 .../AccordionItemButton.component.tsx         |   3 +
 src/types/modal.ts                            |   1 +
 23 files changed, 852 insertions(+), 7 deletions(-)
 create mode 100644 src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.test.tsx
 create mode 100644 src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.tsx
 create mode 100644 src/components/FunctionalArea/Modal/EditOverlayGroupModal/hooks/useEditOverlayGroup.test.ts
 create mode 100644 src/components/FunctionalArea/Modal/EditOverlayGroupModal/hooks/useEditOverlayGroup.ts
 create mode 100644 src/components/FunctionalArea/Modal/EditOverlayGroupModal/index.ts

diff --git a/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.test.tsx b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.test.tsx
new file mode 100644
index 00000000..e6a7c263
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.test.tsx
@@ -0,0 +1,297 @@
+import { render, screen, fireEvent } from '@testing-library/react';
+import { StoreType } from '@/redux/store';
+import {
+  InitialStoreState,
+  getReduxWrapperWithStore,
+} from '@/utils/testing/getReduxWrapperWithStore';
+import { overlayFixture, overlaysPageFixture } from '@/models/fixtures/overlaysFixture';
+import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
+import { apiPath } from '@/redux/apiPath';
+import { HttpStatusCode } from 'axios';
+import { DEFAULT_ERROR } from '@/constants/errors';
+import { act } from 'react-dom/test-utils';
+import { OVERLAYS_INITIAL_STATE_MOCK } from '@/redux/overlays/overlays.mock';
+import { showToast } from '@/utils/showToast';
+import { overlayGroupFixture } from '@/models/fixtures/overlayGroupsFixture';
+import { Modal } from '../Modal.component';
+
+const mockedAxiosNewClient = mockNetworkNewAPIResponse();
+
+jest.mock('../../../../utils/showToast');
+
+const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
+  const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
+
+  return (
+    render(
+      <Wrapper>
+        <Modal />
+      </Wrapper>,
+    ),
+    {
+      store,
+    }
+  );
+};
+
+const overlayGroup = {
+  ...overlayGroupFixture,
+  id: 1,
+};
+describe('EditOverlayModal - component', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+  it('should render modal with correct data', () => {
+    renderComponent({
+      modal: {
+        isOpen: true,
+        modalTitle: overlayFixture.name,
+        modalName: 'edit-overlay-group',
+        editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroupFixture,
+        molArtState: {},
+        overviewImagesState: {},
+        errorReportState: {},
+        layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
+        layerTextFactoryState: undefined,
+      },
+    });
+
+    expect(screen.getByLabelText('Name')).toBeVisible();
+    expect(screen.getByLabelText('Order')).toBeVisible();
+    expect(screen.getByTestId('overlay-group-name')).toHaveValue(overlayGroupFixture.name);
+    expect(screen.getByTestId('overlay-group-order')).toHaveValue(`${overlayGroupFixture.order}`);
+  });
+  it('should handle input change correctly', () => {
+    renderComponent({
+      modal: {
+        isOpen: true,
+        modalTitle: overlayFixture.name,
+        modalName: 'edit-overlay-group',
+        editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroup,
+        molArtState: {},
+        overviewImagesState: {},
+        errorReportState: {},
+        layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
+        layerTextFactoryState: undefined,
+      },
+    });
+
+    const overlayNameInput: HTMLInputElement = screen.getByTestId('overlay-group-name');
+    const overlayOrderInput: HTMLInputElement = screen.getByTestId('overlay-group-order');
+
+    fireEvent.change(overlayNameInput, { target: { value: 'Test name' } });
+    fireEvent.change(overlayOrderInput, { target: { value: `11` } });
+
+    expect(overlayNameInput.value).toBe('Test name');
+    expect(overlayOrderInput.value).toBe('11');
+  });
+  it('should handle remove user overlay', async () => {
+    const { store } = renderComponent({
+      user: {
+        authenticated: true,
+        loading: 'succeeded',
+        error: DEFAULT_ERROR,
+        login: 'test',
+        role: 'user',
+        userData: null,
+        token: null,
+      },
+      modal: {
+        isOpen: true,
+        modalTitle: overlayFixture.name,
+        modalName: 'edit-overlay-group',
+        editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroup,
+        molArtState: {},
+        overviewImagesState: {},
+        errorReportState: {},
+        layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
+        layerTextFactoryState: undefined,
+      },
+      overlays: OVERLAYS_INITIAL_STATE_MOCK,
+    });
+    mockedAxiosNewClient
+      .onDelete(apiPath.removeOverlayGroup(overlayGroup.id))
+      .reply(HttpStatusCode.Ok, {});
+
+    const page = {
+      ...overlaysPageFixture,
+      data: [],
+    };
+
+    mockedAxiosNewClient.onGet(apiPath.getOverlayGroups()).reply(HttpStatusCode.Ok, page);
+
+    const removeButton = screen.getByTestId('remove-button');
+    expect(removeButton).toBeVisible();
+    await act(() => {
+      removeButton.click();
+    });
+
+    const { loading } = store.getState().overlayGroups;
+    expect(loading).toBe('succeeded');
+    expect(removeButton).not.toBeVisible();
+  });
+  it('should show toast after successful removing user overlay', async () => {
+    renderComponent({
+      user: {
+        authenticated: true,
+        loading: 'succeeded',
+        error: DEFAULT_ERROR,
+        login: 'test',
+        role: 'user',
+        userData: null,
+        token: null,
+      },
+      modal: {
+        isOpen: true,
+        modalTitle: overlayFixture.name,
+        modalName: 'edit-overlay-group',
+        editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroup,
+        molArtState: {},
+        overviewImagesState: {},
+        errorReportState: {},
+        layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
+        layerTextFactoryState: undefined,
+      },
+      overlays: OVERLAYS_INITIAL_STATE_MOCK,
+    });
+    mockedAxiosNewClient
+      .onDelete(apiPath.removeOverlayGroup(overlayGroup.id))
+      .reply(HttpStatusCode.Ok, {});
+
+    const removeButton = screen.getByTestId('remove-button');
+    expect(removeButton).toBeVisible();
+    await act(() => {
+      removeButton.click();
+    });
+
+    expect(showToast).toHaveBeenCalledWith({
+      message: 'User overlay group removed successfully',
+      type: 'success',
+    });
+  });
+  it('should handle save edited user overlay', async () => {
+    const { store } = renderComponent({
+      user: {
+        authenticated: true,
+        loading: 'succeeded',
+        error: DEFAULT_ERROR,
+        login: 'test',
+        role: 'user',
+        userData: null,
+        token: null,
+      },
+      modal: {
+        isOpen: true,
+        modalTitle: overlayGroup.name,
+        modalName: 'edit-overlay-group',
+        editOverlayState: null,
+        editOverlayGroupState: overlayGroup,
+        molArtState: {},
+        overviewImagesState: {},
+        errorReportState: {},
+        layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
+        layerTextFactoryState: undefined,
+      },
+      overlays: OVERLAYS_INITIAL_STATE_MOCK,
+    });
+    mockedAxiosNewClient
+      .onPut(apiPath.updateOverlay(overlayGroup.id))
+      .reply(HttpStatusCode.Ok, overlayGroup);
+
+    const page = {
+      ...overlaysPageFixture,
+      data: [overlayGroup],
+    };
+
+    mockedAxiosNewClient.onGet(apiPath.getOverlayGroups()).reply(HttpStatusCode.Ok, page);
+
+    const saveButton = screen.getByTestId('save-button');
+    expect(saveButton).toBeVisible();
+    await act(() => {
+      saveButton.click();
+    });
+
+    const { loading } = store.getState().overlayGroups;
+    expect(loading).toBe('succeeded');
+    expect(saveButton).not.toBeVisible();
+  });
+  it('should show toast after successful editing user overlay', async () => {
+    renderComponent({
+      user: {
+        authenticated: true,
+        loading: 'succeeded',
+        error: DEFAULT_ERROR,
+        login: 'test',
+        role: 'user',
+        userData: null,
+        token: null,
+      },
+      modal: {
+        isOpen: true,
+        modalTitle: overlayGroup.name,
+        modalName: 'edit-overlay-group',
+        editOverlayState: null,
+        editOverlayGroupState: overlayGroup,
+        molArtState: {},
+        overviewImagesState: {},
+        errorReportState: {},
+        layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
+        layerTextFactoryState: undefined,
+      },
+      overlays: OVERLAYS_INITIAL_STATE_MOCK,
+    });
+    mockedAxiosNewClient
+      .onPut(apiPath.updateOverlayGroup(overlayGroup.id))
+      .reply(HttpStatusCode.Ok, overlayGroup);
+
+    const saveButton = screen.getByTestId('save-button');
+    expect(saveButton).toBeVisible();
+    await act(() => {
+      saveButton.click();
+    });
+
+    expect(showToast).toHaveBeenCalledWith({
+      message: 'User overlay group updated successfully',
+      type: 'success',
+    });
+  });
+
+  it('should handle cancel edit user overlay', async () => {
+    const { store } = renderComponent({
+      modal: {
+        isOpen: true,
+        modalTitle: overlayFixture.name,
+        modalName: 'edit-overlay-group',
+        editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroup,
+        molArtState: {},
+        overviewImagesState: {},
+        errorReportState: {},
+        layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
+        layerTextFactoryState: undefined,
+      },
+    });
+
+    const cancelButton = screen.getByTestId('cancel-button');
+    expect(cancelButton).toBeVisible();
+    await act(() => {
+      cancelButton.click();
+    });
+
+    const { isOpen } = store.getState().modal;
+    expect(isOpen).toBe(false);
+    expect(cancelButton).not.toBeVisible();
+  });
+});
diff --git a/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.tsx b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.tsx
new file mode 100644
index 00000000..f339e89d
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.tsx
@@ -0,0 +1,76 @@
+import { Button } from '@/shared/Button';
+import { Input } from '@/shared/Input';
+import React from 'react';
+import { useEditOverlayGroup } from './hooks/useEditOverlayGroup';
+
+export const EditOverlayGroupModal = (): React.ReactNode => {
+  const {
+    name,
+    order,
+    handleCancelEdit,
+    handleNameChange,
+    handleOrderChange,
+    handleRemoveOverlayGroup,
+    handleSaveEditedOverlayGroup,
+  } = useEditOverlayGroup();
+
+  return (
+    <div className="w-full border border-t-[#E1E0E6] bg-white p-[24px]">
+      <form>
+        <label className="text-sm font-semibold" htmlFor="overlayGroupName">
+          Name
+          <Input
+            type="text"
+            value={name}
+            onChange={handleNameChange}
+            className="mt-2.5 text-sm font-medium"
+            data-testid="overlay-group-name"
+            name="overlayGroupName"
+            id="overlayGroupName"
+          />
+        </label>
+        <label className="mt-5 block text-sm font-semibold" htmlFor="overlayGroupOrder">
+          Order
+          <Input
+            type="numbe"
+            value={order}
+            onChange={handleOrderChange}
+            className="mt-2.5 text-sm font-medium"
+            data-testid="overlay-group-order"
+            name="overlayGroupOrder"
+            id="overlayGroupOrder"
+          />
+        </label>
+        <div className="mt-10 flex items-center justify-between gap-5 text-center">
+          <Button
+            type="button"
+            variantStyles="ghost"
+            className="flex-1 justify-center"
+            onClick={handleCancelEdit}
+            data-testid="cancel-button"
+          >
+            Cancel
+          </Button>
+          <Button
+            type="button"
+            variantStyles="ghost"
+            className="flex-1 justify-center"
+            onClick={handleRemoveOverlayGroup}
+            data-testid="remove-button"
+          >
+            Remove
+          </Button>
+
+          <Button
+            type="button"
+            className="flex-1 justify-center"
+            onClick={handleSaveEditedOverlayGroup}
+            data-testid="save-button"
+          >
+            Save
+          </Button>
+        </div>
+      </form>
+    </div>
+  );
+};
diff --git a/src/components/FunctionalArea/Modal/EditOverlayGroupModal/hooks/useEditOverlayGroup.test.ts b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/hooks/useEditOverlayGroup.test.ts
new file mode 100644
index 00000000..e5a05139
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/hooks/useEditOverlayGroup.test.ts
@@ -0,0 +1,217 @@
+/* eslint-disable no-magic-numbers */
+import { DEFAULT_ERROR } from '@/constants/errors';
+import { overlayFixture } from '@/models/fixtures/overlaysFixture';
+import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener';
+import { renderHook } from '@testing-library/react';
+import { overlayGroupFixture } from '@/models/fixtures/overlayGroupsFixture';
+import { useEditOverlayGroup } from './useEditOverlayGroup';
+
+const overlayGroup = { ...overlayGroupFixture, id: 109 };
+describe('useEditOverlayGroup', () => {
+  it('should handle cancel edit overlay', () => {
+    const { Wrapper, store } = getReduxStoreWithActionsListener({
+      user: {
+        authenticated: true,
+        loading: 'succeeded',
+        error: DEFAULT_ERROR,
+        login: 'test',
+        role: 'user',
+        userData: null,
+        token: null,
+      },
+      modal: {
+        isOpen: true,
+        modalTitle: overlayFixture.name,
+        modalName: 'edit-overlay-group',
+        editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroup,
+        molArtState: {},
+        overviewImagesState: {},
+        errorReportState: {},
+        layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
+        layerTextFactoryState: undefined,
+      },
+    });
+
+    const {
+      result: {
+        current: { handleCancelEdit },
+      },
+    } = renderHook(() => useEditOverlayGroup(), {
+      wrapper: Wrapper,
+    });
+
+    handleCancelEdit();
+
+    const actions = store.getActions();
+
+    expect(actions[0].type).toBe('modal/closeModal');
+  });
+
+  it('should handle handleRemoveOverlay if proper data is provided', () => {
+    const { Wrapper, store } = getReduxStoreWithActionsListener({
+      user: {
+        authenticated: true,
+        loading: 'succeeded',
+        error: DEFAULT_ERROR,
+        login: 'test',
+        role: 'user',
+        userData: null,
+        token: null,
+      },
+      modal: {
+        isOpen: true,
+        modalTitle: overlayFixture.name,
+        modalName: 'edit-overlay-group',
+        editOverlayState: null,
+        editOverlayGroupState: overlayGroup,
+        molArtState: {},
+        overviewImagesState: {},
+        errorReportState: {},
+        layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
+        layerTextFactoryState: undefined,
+      },
+    });
+
+    const {
+      result: {
+        current: { handleRemoveOverlayGroup },
+      },
+    } = renderHook(() => useEditOverlayGroup(), {
+      wrapper: Wrapper,
+    });
+
+    handleRemoveOverlayGroup();
+
+    const actions = store.getActions();
+
+    expect(actions[0].type).toBe('overlayGroups/removeOverlayGroup/pending');
+
+    const { overlayGroupId } = actions[0].meta.arg;
+
+    expect(overlayGroupId).toBe(overlayGroup.id);
+  });
+  it('should not handle handleRemoveOverlay if proper data is not provided', () => {
+    const { Wrapper, store } = getReduxStoreWithActionsListener({
+      user: {
+        authenticated: true,
+        loading: 'failed',
+        error: DEFAULT_ERROR,
+        login: null,
+        role: 'user',
+        userData: null,
+        token: null,
+      },
+      modal: {
+        isOpen: true,
+        modalTitle: overlayFixture.name,
+        modalName: 'edit-overlay-group',
+        editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroupFixture,
+        molArtState: {},
+        overviewImagesState: {},
+        errorReportState: {},
+        layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
+        layerTextFactoryState: undefined,
+      },
+    });
+
+    const {
+      result: {
+        current: { handleRemoveOverlayGroup },
+      },
+    } = renderHook(() => useEditOverlayGroup(), {
+      wrapper: Wrapper,
+    });
+
+    handleRemoveOverlayGroup();
+
+    const actions = store.getActions();
+
+    expect(actions.length).toBe(0);
+  });
+  it('should handle handleSaveEditedOverlay if proper data is provided', () => {
+    const { Wrapper, store } = getReduxStoreWithActionsListener({
+      user: {
+        authenticated: true,
+        loading: 'succeeded',
+        error: DEFAULT_ERROR,
+        login: 'test',
+        role: 'user',
+        userData: null,
+        token: null,
+      },
+      modal: {
+        isOpen: true,
+        modalTitle: overlayGroup.name,
+        modalName: 'edit-overlay-group',
+        editOverlayState: null,
+        editOverlayGroupState: overlayGroup,
+        molArtState: {},
+        overviewImagesState: {},
+        errorReportState: {},
+        layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
+        layerTextFactoryState: undefined,
+      },
+    });
+
+    const {
+      result: {
+        current: { handleSaveEditedOverlayGroup },
+      },
+    } = renderHook(() => useEditOverlayGroup(), {
+      wrapper: Wrapper,
+    });
+
+    handleSaveEditedOverlayGroup();
+
+    const actions = store.getActions();
+
+    expect(actions[0].type).toBe('overlayGroups/updateOverlayGroups/pending');
+    expect(actions[0].meta.arg).toEqual([overlayGroup]);
+  });
+  it('should not handle handleSaveEditedOverlay if proper data is not provided', () => {
+    const { Wrapper, store } = getReduxStoreWithActionsListener({
+      user: {
+        authenticated: true,
+        loading: 'succeeded',
+        error: DEFAULT_ERROR,
+        login: null,
+        role: 'user',
+        userData: null,
+        token: null,
+      },
+      modal: {
+        isOpen: true,
+        modalTitle: overlayFixture.name,
+        modalName: 'edit-overlay-group',
+        editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroupFixture,
+        molArtState: {},
+        overviewImagesState: {},
+        errorReportState: {},
+        layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
+        layerTextFactoryState: undefined,
+      },
+    });
+
+    const {
+      result: {
+        current: { handleSaveEditedOverlayGroup },
+      },
+    } = renderHook(() => useEditOverlayGroup(), {
+      wrapper: Wrapper,
+    });
+
+    handleSaveEditedOverlayGroup();
+
+    const actions = store.getActions();
+
+    expect(actions.length).toBe(0);
+  });
+});
diff --git a/src/components/FunctionalArea/Modal/EditOverlayGroupModal/hooks/useEditOverlayGroup.ts b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/hooks/useEditOverlayGroup.ts
new file mode 100644
index 00000000..3a8147f4
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/hooks/useEditOverlayGroup.ts
@@ -0,0 +1,101 @@
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { currentEditedOverlayGroupSelector } from '@/redux/modal/modal.selector';
+import { closeModal } from '@/redux/modal/modal.slice';
+import { loginUserSelector } from '@/redux/user/user.selectors';
+import { OverlayGroup } from '@/types/models';
+import React, { useState } from 'react';
+import { ONE } from '@/constants/common';
+import {
+  getOverlayGroups,
+  removeOverlayGroup,
+  updateOverlayGroups,
+} from '@/redux/overlayGroup/overlayGroup.thunks';
+
+type UseEditOverlayGroupReturn = {
+  name: string | undefined;
+  order: number | undefined;
+  handleCancelEdit: () => void;
+  handleRemoveOverlayGroup: () => void;
+  handleSaveEditedOverlayGroup: () => Promise<void>;
+  handleNameChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
+  handleOrderChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
+};
+
+type UpdatedOverlayGroup = {
+  editedOverlayGroup: OverlayGroup;
+  overlayName: string;
+  overlayOrder: number;
+};
+
+export const useEditOverlayGroup = (): UseEditOverlayGroupReturn => {
+  const currentEditedOverlayGroup = useAppSelector(currentEditedOverlayGroupSelector);
+  const login = useAppSelector(loginUserSelector);
+  const dispatch = useAppDispatch();
+  const [name, setName] = useState(currentEditedOverlayGroup?.name);
+  const [order, setOrder] = useState(currentEditedOverlayGroup?.order);
+
+  const handleCancelEdit = (): void => {
+    dispatch(closeModal());
+  };
+
+  const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
+    setName(e.target.value);
+  };
+
+  const handleOrderChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
+    setOrder(Number(e.target.value));
+  };
+
+  const handleRemoveOverlayGroup = (): void => {
+    if (!login || !currentEditedOverlayGroup || !currentEditedOverlayGroup.id) return;
+    dispatch(removeOverlayGroup({ overlayGroupId: currentEditedOverlayGroup.id }));
+  };
+
+  const handleUpdateOverlay = async ({
+    overlayName,
+    overlayOrder,
+    editedOverlayGroup,
+  }: UpdatedOverlayGroup): Promise<void> => {
+    await dispatch(
+      updateOverlayGroups([
+        {
+          ...editedOverlayGroup,
+          name: overlayName,
+          order: overlayOrder,
+        },
+      ]),
+    );
+  };
+
+  const refreshOverlayGroups = async (): Promise<void> => {
+    await dispatch(getOverlayGroups());
+  };
+
+  const handleCloseModal = (): void => {
+    dispatch(closeModal());
+  };
+
+  const handleSaveEditedOverlayGroup = async (): Promise<void> => {
+    if (!currentEditedOverlayGroup || !name || !login) return;
+    await handleUpdateOverlay({
+      editedOverlayGroup: currentEditedOverlayGroup,
+      overlayName: name,
+      overlayOrder: order || ONE,
+    });
+
+    await refreshOverlayGroups();
+
+    handleCloseModal();
+  };
+
+  return {
+    handleCancelEdit,
+    handleRemoveOverlayGroup,
+    handleSaveEditedOverlayGroup,
+    handleNameChange,
+    handleOrderChange,
+    name,
+    order,
+  };
+};
diff --git a/src/components/FunctionalArea/Modal/EditOverlayGroupModal/index.ts b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/index.ts
new file mode 100644
index 00000000..78625bea
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/index.ts
@@ -0,0 +1 @@
+export { EditOverlayGroupModal } from './EditOverlayGroupModal.component';
diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
index c7f3f156..824b1503 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
@@ -12,6 +12,7 @@ import { DEFAULT_ERROR } from '@/constants/errors';
 import { act } from 'react-dom/test-utils';
 import { OVERLAYS_INITIAL_STATE_MOCK } from '@/redux/overlays/overlays.mock';
 import { showToast } from '@/utils/showToast';
+import { overlayGroupFixture } from '@/models/fixtures/overlayGroupsFixture';
 import { Modal } from '../Modal.component';
 
 const mockedAxiosClient = mockNetworkResponse();
@@ -50,6 +51,7 @@ describe('EditOverlayModal - component', () => {
         modalTitle: overlayFixture.name,
         modalName: 'edit-overlay',
         editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroupFixture,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
@@ -71,6 +73,7 @@ describe('EditOverlayModal - component', () => {
         modalTitle: overlayFixture.name,
         modalName: 'edit-overlay',
         editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroupFixture,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
@@ -105,6 +108,7 @@ describe('EditOverlayModal - component', () => {
         modalTitle: overlayFixture.name,
         modalName: 'edit-overlay',
         editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroupFixture,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
@@ -144,6 +148,7 @@ describe('EditOverlayModal - component', () => {
         modalTitle: overlayFixture.name,
         modalName: 'edit-overlay',
         editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroupFixture,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
@@ -184,6 +189,7 @@ describe('EditOverlayModal - component', () => {
         modalTitle: overlay.name,
         modalName: 'edit-overlay',
         editOverlayState: overlay,
+        editOverlayGroupState: overlayGroupFixture,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
@@ -230,6 +236,7 @@ describe('EditOverlayModal - component', () => {
         modalTitle: overlay.name,
         modalName: 'edit-overlay',
         editOverlayState: overlay,
+        editOverlayGroupState: overlayGroupFixture,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
@@ -260,6 +267,7 @@ describe('EditOverlayModal - component', () => {
         modalTitle: overlayFixture.name,
         modalName: 'edit-overlay',
         editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroupFixture,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts
index ac20ca6d..5bcb2c10 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts
@@ -3,6 +3,7 @@ import { DEFAULT_ERROR } from '@/constants/errors';
 import { overlayFixture } from '@/models/fixtures/overlaysFixture';
 import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener';
 import { renderHook } from '@testing-library/react';
+import { overlayGroupFixture } from '@/models/fixtures/overlayGroupsFixture';
 import { useEditOverlay } from './useEditOverlay';
 
 describe('useEditOverlay', () => {
@@ -22,6 +23,7 @@ describe('useEditOverlay', () => {
         modalTitle: overlayFixture.name,
         modalName: 'edit-overlay',
         editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroupFixture,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
@@ -62,6 +64,7 @@ describe('useEditOverlay', () => {
         modalTitle: overlayFixture.name,
         modalName: 'edit-overlay',
         editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroupFixture,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
@@ -105,6 +108,7 @@ describe('useEditOverlay', () => {
         modalTitle: overlayFixture.name,
         modalName: 'edit-overlay',
         editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroupFixture,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
@@ -145,6 +149,7 @@ describe('useEditOverlay', () => {
         modalTitle: overlay.name,
         modalName: 'edit-overlay',
         editOverlayState: overlay,
+        editOverlayGroupState: overlayGroupFixture,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
@@ -185,6 +190,7 @@ describe('useEditOverlay', () => {
         modalTitle: overlayFixture.name,
         modalName: 'edit-overlay',
         editOverlayState: overlayFixture,
+        editOverlayGroupState: overlayGroupFixture,
         molArtState: {},
         overviewImagesState: {},
         errorReportState: {},
diff --git a/src/components/FunctionalArea/Modal/LayerImageObjectModal/LayerImageObjectEditFactoryModal.component.test.tsx b/src/components/FunctionalArea/Modal/LayerImageObjectModal/LayerImageObjectEditFactoryModal.component.test.tsx
index 0c0973f2..775e9c5d 100644
--- a/src/components/FunctionalArea/Modal/LayerImageObjectModal/LayerImageObjectEditFactoryModal.component.test.tsx
+++ b/src/components/FunctionalArea/Modal/LayerImageObjectModal/LayerImageObjectEditFactoryModal.component.test.tsx
@@ -21,6 +21,7 @@ import { MAP_EDIT_TOOLS_STATE_INITIAL_MOCK } from '@/redux/mapEditTools/mapEditT
 import { MAP_EDIT_ACTIONS } from '@/redux/mapEditTools/mapEditTools.constants';
 import { Feature } from 'ol';
 import Polygon from 'ol/geom/Polygon';
+import { overlayGroupFixture } from '@/models/fixtures/overlayGroupsFixture';
 import { LayerImageObjectEditFactoryModal } from './LayerImageObjectEditFactoryModal.component';
 
 const mockedAxiosNewClient = mockNetworkNewAPIResponse();
@@ -52,6 +53,7 @@ const renderComponent = (
       modalTitle: overlayFixture.name,
       modalName: 'edit-overlay',
       editOverlayState: overlayFixture,
+      editOverlayGroupState: overlayGroupFixture,
       molArtState: {},
       overviewImagesState: {},
       errorReportState: {},
diff --git a/src/components/FunctionalArea/Modal/LayerImageObjectModal/LayerImageObjectFactoryModal.component.test.tsx b/src/components/FunctionalArea/Modal/LayerImageObjectModal/LayerImageObjectFactoryModal.component.test.tsx
index 0d0de1fe..159cae33 100644
--- a/src/components/FunctionalArea/Modal/LayerImageObjectModal/LayerImageObjectFactoryModal.component.test.tsx
+++ b/src/components/FunctionalArea/Modal/LayerImageObjectModal/LayerImageObjectFactoryModal.component.test.tsx
@@ -16,6 +16,7 @@ import {
 import { MODELS_DATA_MOCK_WITH_MAIN_MAP } from '@/redux/models/models.mock';
 import { overlayFixture } from '@/models/fixtures/overlaysFixture';
 import { showToast } from '@/utils/showToast';
+import { overlayGroupFixture } from '@/models/fixtures/overlayGroupsFixture';
 import { LayerImageObjectFactoryModal } from './LayerImageObjectFactoryModal.component';
 
 const mockedAxiosNewClient = mockNetworkNewAPIResponse();
@@ -46,6 +47,7 @@ const renderComponent = (): { store: StoreType } => {
       modalTitle: overlayFixture.name,
       modalName: 'edit-overlay',
       editOverlayState: overlayFixture,
+      editOverlayGroupState: overlayGroupFixture,
       molArtState: {},
       overviewImagesState: {},
       errorReportState: {},
diff --git a/src/components/FunctionalArea/Modal/LayerTextFactoryModal/LayerTextFactoryModal.component.test.tsx b/src/components/FunctionalArea/Modal/LayerTextFactoryModal/LayerTextFactoryModal.component.test.tsx
index c739fb38..4f57d684 100644
--- a/src/components/FunctionalArea/Modal/LayerTextFactoryModal/LayerTextFactoryModal.component.test.tsx
+++ b/src/components/FunctionalArea/Modal/LayerTextFactoryModal/LayerTextFactoryModal.component.test.tsx
@@ -21,6 +21,7 @@ import {
   TEXT_VERTICAL_ALIGNMENTS,
 } from '@/components/FunctionalArea/Modal/LayerTextFactoryModal/LayerTextFactory.constants';
 import { layerTextFixture } from '@/models/fixtures/layerTextFixture';
+import { overlayGroupFixture } from '@/models/fixtures/overlayGroupsFixture';
 import { LayerTextFactoryModal } from './LayerTextFactoryModal.component';
 
 const mockedAxiosNewClient = mockNetworkNewAPIResponse();
@@ -51,6 +52,7 @@ const renderComponent = (): { store: StoreType } => {
       modalTitle: overlayFixture.name,
       modalName: 'edit-overlay',
       editOverlayState: overlayFixture,
+      editOverlayGroupState: overlayGroupFixture,
       molArtState: {},
       overviewImagesState: {},
       errorReportState: {},
diff --git a/src/components/FunctionalArea/Modal/Modal.component.tsx b/src/components/FunctionalArea/Modal/Modal.component.tsx
index 6cc342ee..43b135cd 100644
--- a/src/components/FunctionalArea/Modal/Modal.component.tsx
+++ b/src/components/FunctionalArea/Modal/Modal.component.tsx
@@ -10,6 +10,7 @@ import {
   LayerImageObjectFactoryModal,
 } from '@/components/FunctionalArea/Modal/LayerImageObjectModal';
 import { LayerTextFactoryModal } from '@/components/FunctionalArea/Modal/LayerTextFactoryModal/LayerTextFactoryModal.component';
+import { EditOverlayGroupModal } from '@/components/FunctionalArea/Modal/EditOverlayGroupModal';
 import { EditOverlayModal } from './EditOverlayModal';
 import { LoginModal } from './LoginModal';
 import { ErrorReportModal } from './ErrorReportModal';
@@ -60,6 +61,11 @@ export const Modal = (): React.ReactNode => {
           <EditOverlayModal />
         </ModalLayout>
       )}
+      {isOpen && modalName === 'edit-overlay-group' && (
+        <ModalLayout>
+          <EditOverlayGroupModal />
+        </ModalLayout>
+      )}
       {isOpen && modalName === 'logged-in-menu' && (
         <ModalLayout>
           <LoggedInMenuModal />
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
index 7e6053a2..872cf584 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
@@ -10,8 +10,12 @@ import { HTML5Backend } from 'react-dnd-html5-backend';
 import { MapOverlay } from '@/types/models';
 import React from 'react';
 import { ZERO } from '@/constants/common';
-import { UserOverlayListItem } from './UserOverlayListItem';
+import { Icon } from '@/shared/Icon';
+import { openEditOverlayGroupModal } from '@/redux/modal/modal.slice';
+import { overlayGroupSelector } from '@/redux/overlayGroup/overlayGroup.selectors';
+import { store } from '@/redux/store';
 import { useUserOverlays } from './hooks/useUserOverlays';
+import { UserOverlayListItem } from './UserOverlayListItem';
 
 type OverlayGroupProps = {
   groupId: number | null;
@@ -27,6 +31,10 @@ export const UserOverlaysGroup = ({
   const { moveUserOverlayListItem, updateUserOverlaysOrder, isPending, userOverlaysList } =
     useUserOverlays(overlays, groupId);
 
+  const { dispatch, getState } = store;
+  const state = getState();
+  const overlayGroup = overlayGroupSelector(state, groupId);
+
   const nullOverlay: MapOverlay = {
     id: groupId ? -groupId : ZERO,
     group: groupId,
@@ -40,16 +48,34 @@ export const UserOverlaysGroup = ({
     order: 0,
   };
 
+  const openEditGroup = (): void => {
+    // eslint-disable-next-line no-console
+    console.log('Open edit group', groupId);
+    if (overlayGroup) {
+      dispatch(openEditOverlayGroupModal(overlayGroup));
+    }
+  };
+
   return (
     <DndProvider backend={HTML5Backend}>
       <div className="mt-2.5">
         <Accordion allowZeroExpanded>
           <AccordionItem className="border-b-0">
             <AccordionItemHeading>
-              <AccordionItemButton className="px-6 text-sm font-semibold">
+              <AccordionItemButton
+                className="px-6 text-sm font-semibold"
+                sideMenu={
+                  groupId && (
+                    <button onClick={openEditGroup} type="button" className="mr-2">
+                      <Icon name="edit-image" />
+                    </button>
+                  )
+                }
+              >
                 {groupName}
               </AccordionItemButton>
             </AccordionItemHeading>
+
             <AccordionItemPanel>
               {isPending ? (
                 <span className="py-4 pl-10 pr-5">Loading...</span>
diff --git a/src/redux/apiPath.ts b/src/redux/apiPath.ts
index d42b790a..0c35f8da 100644
--- a/src/redux/apiPath.ts
+++ b/src/redux/apiPath.ts
@@ -137,4 +137,8 @@ export const apiPath = {
   getChemicalAutocomplete: (): string => `projects/${PROJECT_ID}/chemicals/suggestedQueryList`,
   getOverlayGroups: (): string => `projects/${PROJECT_ID}/overlay_groups/`,
   addOverlayGroup: (): string => `projects/${PROJECT_ID}/overlay_groups/`,
+  removeOverlayGroup: (overlayGroupId: number): string =>
+    `projects/${PROJECT_ID}/overlay_groups/${overlayGroupId}/`,
+  updateOverlayGroup: (overlayGroupId: number): string =>
+    `projects/${PROJECT_ID}/overlay_groups/${overlayGroupId}/`,
 };
diff --git a/src/redux/modal/modal.constants.ts b/src/redux/modal/modal.constants.ts
index f858a91b..5d9f7748 100644
--- a/src/redux/modal/modal.constants.ts
+++ b/src/redux/modal/modal.constants.ts
@@ -12,6 +12,7 @@ export const MODAL_INITIAL_STATE: ModalState = {
     uniprotId: MOL_ART_UNIPROT_ID_DEFAULT,
   },
   editOverlayState: null,
+  editOverlayGroupState: null,
   errorReportState: {},
   layerFactoryState: { id: undefined },
   layerImageObjectFactoryState: undefined,
diff --git a/src/redux/modal/modal.mock.ts b/src/redux/modal/modal.mock.ts
index 85f6e5bd..59ce90ba 100644
--- a/src/redux/modal/modal.mock.ts
+++ b/src/redux/modal/modal.mock.ts
@@ -12,6 +12,7 @@ export const MODAL_INITIAL_STATE_MOCK: ModalState = {
     uniprotId: MOL_ART_UNIPROT_ID_DEFAULT,
   },
   editOverlayState: null,
+  editOverlayGroupState: null,
   errorReportState: {},
   layerFactoryState: { id: undefined },
   layerImageObjectFactoryState: undefined,
diff --git a/src/redux/modal/modal.reducers.ts b/src/redux/modal/modal.reducers.ts
index 1b903069..1667a059 100644
--- a/src/redux/modal/modal.reducers.ts
+++ b/src/redux/modal/modal.reducers.ts
@@ -2,7 +2,11 @@ import { ModalName } from '@/types/modal';
 import { PayloadAction } from '@reduxjs/toolkit';
 import { ErrorData } from '@/utils/error-report/ErrorData';
 import { BoundingBox } from '@/components/Map/MapViewer/MapViewer.types';
-import { ModalState, OpenEditOverlayModalAction } from './modal.types';
+import {
+  ModalState,
+  OpenEditOverlayGroupModalAction,
+  OpenEditOverlayModalAction,
+} from './modal.types';
 
 const getOpenedModel = (state: ModalState): ModalName | null => {
   if (state.isOpen) {
@@ -114,6 +118,16 @@ export const openEditOverlayModalReducer = (
   state.editOverlayState = action.payload;
 };
 
+export const openEditOverlayGroupModalReducer = (
+  state: ModalState,
+  action: OpenEditOverlayGroupModalAction,
+): void => {
+  state.isOpen = true;
+  state.modalName = 'edit-overlay-group';
+  state.modalTitle = action.payload.name;
+  state.editOverlayGroupState = action.payload;
+};
+
 export const openLicenseModalReducer = (state: ModalState, action: PayloadAction<string>): void => {
   state.isOpen = true;
   state.modalName = 'license';
diff --git a/src/redux/modal/modal.selector.ts b/src/redux/modal/modal.selector.ts
index 4f5c8fb0..0576f26a 100644
--- a/src/redux/modal/modal.selector.ts
+++ b/src/redux/modal/modal.selector.ts
@@ -21,6 +21,11 @@ export const currentEditedOverlaySelector = createSelector(
   modal => modal.editOverlayState,
 );
 
+export const currentEditedOverlayGroupSelector = createSelector(
+  modalSelector,
+  modal => modal.editOverlayGroupState,
+);
+
 export const layerFactoryStateSelector = createSelector(
   modalSelector,
   modal => modal.layerFactoryState,
diff --git a/src/redux/modal/modal.slice.ts b/src/redux/modal/modal.slice.ts
index bf01d86a..c046894e 100644
--- a/src/redux/modal/modal.slice.ts
+++ b/src/redux/modal/modal.slice.ts
@@ -20,6 +20,7 @@ import {
   openLayerImageObjectFactoryModalReducer,
   openLayerImageObjectEditFactoryModalReducer,
   openLayerTextFactoryModalReducer,
+  openEditOverlayGroupModalReducer,
 } from './modal.reducers';
 
 const modalSlice = createSlice({
@@ -35,6 +36,7 @@ const modalSlice = createSlice({
     openAddCommentModal: openAddCommentModalReducer,
     openPublicationsModal: openPublicationsModalReducer,
     openEditOverlayModal: openEditOverlayModalReducer,
+    openEditOverlayGroupModal: openEditOverlayGroupModalReducer,
     openLoggedInMenuModal: openLoggedInMenuModalReducer,
     openErrorReportModal: openErrorReportModalReducer,
     openAccessDeniedModal: openAccessDeniedModalReducer,
@@ -58,6 +60,7 @@ export const {
   openLoginModal,
   openPublicationsModal,
   openEditOverlayModal,
+  openEditOverlayGroupModal,
   openLoggedInMenuModal,
   openErrorReportModal,
   openAccessDeniedModal,
diff --git a/src/redux/modal/modal.types.ts b/src/redux/modal/modal.types.ts
index 20f7a998..05b0f40c 100644
--- a/src/redux/modal/modal.types.ts
+++ b/src/redux/modal/modal.types.ts
@@ -1,5 +1,5 @@
 import { ModalName } from '@/types/modal';
-import { MapOverlay } from '@/types/models';
+import { MapOverlay, OverlayGroup } from '@/types/models';
 import { PayloadAction } from '@reduxjs/toolkit';
 import { ErrorData } from '@/utils/error-report/ErrorData';
 import { BoundingBox } from '@/components/Map/MapViewer/MapViewer.types';
@@ -17,6 +17,7 @@ export type ErrorRepostState = {
 };
 
 export type EditOverlayState = MapOverlay | null;
+export type EditOverlayGroupState = OverlayGroup | null;
 
 export type LayerFactoryState = {
   id: number | undefined;
@@ -34,6 +35,7 @@ export interface ModalState {
   molArtState: MolArtModalState;
   errorReportState: ErrorRepostState;
   editOverlayState: EditOverlayState;
+  editOverlayGroupState: EditOverlayGroupState;
   layerFactoryState: LayerFactoryState;
   layerImageObjectFactoryState: LayerImageObjectFactoryState;
   layerTextFactoryState: LayerTextFactoryState;
@@ -42,3 +44,7 @@ export interface ModalState {
 export type OpenEditOverlayModalPayload = MapOverlay;
 
 export type OpenEditOverlayModalAction = PayloadAction<OpenEditOverlayModalPayload>;
+
+export type OpenEditOverlayGroupModalPayload = OverlayGroup;
+
+export type OpenEditOverlayGroupModalAction = PayloadAction<OpenEditOverlayGroupModalPayload>;
diff --git a/src/redux/overlayGroup/overlayGroup.selectors.ts b/src/redux/overlayGroup/overlayGroup.selectors.ts
index bd26e08f..309ecc8a 100644
--- a/src/redux/overlayGroup/overlayGroup.selectors.ts
+++ b/src/redux/overlayGroup/overlayGroup.selectors.ts
@@ -3,12 +3,17 @@ import { DEFAULT_GROUP } from '@/components/Map/Drawer/OverlaysDrawer/UserOverla
 import { OverlayGroup } from '@/types/models';
 import { rootSelector } from '../root/root.selectors';
 
-const overlayGroupSelector = createSelector(rootSelector, state => state.overlayGroups);
+const overlayGroupsDataSelector = createSelector(rootSelector, state => state.overlayGroups);
 
-export const overlayGroupsSelector = createSelector(overlayGroupSelector, overlayGroup => {
+export const overlayGroupsSelector = createSelector(overlayGroupsDataSelector, overlayGroup => {
   let result: OverlayGroup[] = [DEFAULT_GROUP];
   if (overlayGroup?.data) {
     result = result.concat(overlayGroup?.data);
   }
   return result;
 });
+
+export const overlayGroupSelector = createSelector(
+  [overlayGroupsSelector, (_, overlayGroupId: number | null): number | null => overlayGroupId],
+  (groups, overlayGroupId) => groups.find(group => group.id === overlayGroupId),
+);
diff --git a/src/redux/overlayGroup/overlayGroup.thunks.ts b/src/redux/overlayGroup/overlayGroup.thunks.ts
index 711bb8ef..63d8c2a8 100644
--- a/src/redux/overlayGroup/overlayGroup.thunks.ts
+++ b/src/redux/overlayGroup/overlayGroup.thunks.ts
@@ -8,6 +8,9 @@ import { pageableSchema } from '@/models/pageableSchema';
 import { overlayGroupSchema } from '@/models/overlayGroupSchema';
 import { showToast } from '@/utils/showToast';
 import axios from 'axios';
+import { closeModal } from '@/redux/modal/modal.slice';
+import { getAllUserOverlaysByCreator } from '@/redux/overlays/overlays.thunks';
+import { z } from 'zod';
 import { apiPath } from '../apiPath';
 
 export const getOverlayGroups = createAsyncThunk<OverlayGroup[], void, ThunkConfig>(
@@ -39,7 +42,7 @@ type AddOverlayGroupArgs = {
 };
 
 export const addOverlayGroup = createAsyncThunk<undefined, AddOverlayGroupArgs, ThunkConfig>(
-  'overlays/addOverlayGroup',
+  'overlayGroups/addOverlayGroup',
   async (
     { name, order },
     { dispatch },
@@ -78,3 +81,58 @@ export const addOverlayGroup = createAsyncThunk<undefined, AddOverlayGroupArgs,
     }
   },
 );
+
+export const removeOverlayGroup = createAsyncThunk<
+  undefined,
+  { overlayGroupId: number },
+  ThunkConfig
+>(
+  'overlayGroups/removeOverlayGroup',
+  // eslint-disable-next-line consistent-return
+  async ({ overlayGroupId }, { dispatch }) => {
+    try {
+      await axiosInstanceNewAPI.delete(apiPath.removeOverlayGroup(overlayGroupId), {
+        withCredentials: true,
+      });
+      await dispatch(getAllUserOverlaysByCreator());
+      await dispatch(getOverlayGroups());
+      dispatch(closeModal());
+
+      showToast({ type: 'success', message: 'User overlay group removed successfully' });
+    } catch (error) {
+      return Promise.reject(getError({ error, prefix: 'Failed to remove user overlay group' }));
+    }
+  },
+);
+
+export const updateOverlayGroups = createAsyncThunk<undefined, OverlayGroup[], ThunkConfig>(
+  'overlayGroups/updateOverlayGroups',
+  // eslint-disable-next-line consistent-return
+  async userOverlayGroups => {
+    try {
+      const userOverlaysPromises = userOverlayGroups.map(overlayGroup => {
+        if (overlayGroup.id !== null)
+          return axiosInstanceNewAPI.put<OverlayGroup>(
+            apiPath.updateOverlayGroup(overlayGroup.id),
+            overlayGroup,
+            {
+              withCredentials: true,
+            },
+          );
+        return Promise.resolve({ data: overlayGroup });
+      });
+
+      const overlayGroupsResponses = await Promise.all(userOverlaysPromises);
+
+      const updatedUserOverlayGroups = overlayGroupsResponses.map(
+        updatedUserOverlay => updatedUserOverlay.data,
+      );
+
+      validateDataUsingZodSchema(updatedUserOverlayGroups, z.array(overlayGroupSchema));
+
+      showToast({ type: 'success', message: 'User overlay group updated successfully' });
+    } catch (error) {
+      return Promise.reject(getError({ error, prefix: 'Failed to update user overlay group' }));
+    }
+  },
+);
diff --git a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx
index fb22d501..74aff60f 100644
--- a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx
+++ b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx
@@ -10,6 +10,7 @@ type AccordionItemButtonProps = {
   onClick?: () => void;
   disabled?: boolean;
   className?: string;
+  sideMenu?: React.ReactNode;
 };
 
 export const AccordionItemButton = ({
@@ -18,6 +19,7 @@ export const AccordionItemButton = ({
   onClick,
   disabled,
   className,
+  sideMenu,
 }: AccordionItemButtonProps): JSX.Element => {
   const ButtonIcon = getIcon(variant);
 
@@ -33,6 +35,7 @@ export const AccordionItemButton = ({
         {children}
         {ButtonIcon}
       </button>
+      {sideMenu}
     </AIB>
   );
 };
diff --git a/src/types/modal.ts b/src/types/modal.ts
index 8b02e86a..4b07798e 100644
--- a/src/types/modal.ts
+++ b/src/types/modal.ts
@@ -7,6 +7,7 @@ export type ModalName =
   | 'license'
   | 'publications'
   | 'edit-overlay'
+  | 'edit-overlay-group'
   | 'error-report'
   | 'access-denied'
   | 'select-project'
-- 
GitLab


From ff9966ed3c3378b76fd6143fc50a9f25eae7b278 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Fri, 14 Feb 2025 14:26:19 +0100
Subject: [PATCH 16/17] order is a number

---
 .../EditOverlayGroupModal.component.test.tsx                  | 2 +-
 .../EditOverlayGroupModal/EditOverlayGroupModal.component.tsx | 2 +-
 src/redux/overlayGroup/overlayGroup.thunks.ts                 | 4 +++-
 3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.test.tsx b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.test.tsx
index e6a7c263..7eabc46b 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.test.tsx
+++ b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.test.tsx
@@ -62,7 +62,7 @@ describe('EditOverlayModal - component', () => {
     expect(screen.getByLabelText('Name')).toBeVisible();
     expect(screen.getByLabelText('Order')).toBeVisible();
     expect(screen.getByTestId('overlay-group-name')).toHaveValue(overlayGroupFixture.name);
-    expect(screen.getByTestId('overlay-group-order')).toHaveValue(`${overlayGroupFixture.order}`);
+    expect(screen.getByTestId('overlay-group-order')).toHaveValue(overlayGroupFixture.order);
   });
   it('should handle input change correctly', () => {
     renderComponent({
diff --git a/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.tsx b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.tsx
index f339e89d..bb5095e8 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.tsx
+++ b/src/components/FunctionalArea/Modal/EditOverlayGroupModal/EditOverlayGroupModal.component.tsx
@@ -32,7 +32,7 @@ export const EditOverlayGroupModal = (): React.ReactNode => {
         <label className="mt-5 block text-sm font-semibold" htmlFor="overlayGroupOrder">
           Order
           <Input
-            type="numbe"
+            type="number"
             value={order}
             onChange={handleOrderChange}
             className="mt-2.5 text-sm font-medium"
diff --git a/src/redux/overlayGroup/overlayGroup.thunks.ts b/src/redux/overlayGroup/overlayGroup.thunks.ts
index 63d8c2a8..96b501d6 100644
--- a/src/redux/overlayGroup/overlayGroup.thunks.ts
+++ b/src/redux/overlayGroup/overlayGroup.thunks.ts
@@ -29,7 +29,9 @@ export const getOverlayGroups = createAsyncThunk<OverlayGroup[], void, ThunkConf
         pageableSchema(overlayGroupSchema),
       );
 
-      return isDataValid ? response.data.content : [];
+      return isDataValid
+        ? response.data.content.sort((groupA, groupB) => groupA.order - groupB.order)
+        : [];
     } catch (error) {
       return Promise.reject(getError({ error, prefix: 'Failed to fetch overlay groups' }));
     }
-- 
GitLab


From ac45fe20f7e20b447d9f4f583fe85669278fa0ea Mon Sep 17 00:00:00 2001
From: Piotr Gawron <p.gawron@atcomp.pl>
Date: Wed, 19 Feb 2025 12:29:29 +0100
Subject: [PATCH 17/17] allow to edit public overlays

---
 .../EditOverlayModal/hooks/useEditOverlay.ts  |  3 ++
 .../GeneralOverlays.component.tsx             | 34 +++++++++++++++----
 .../UserOverlaysGroup.component.tsx           |  6 ++--
 3 files changed, 33 insertions(+), 10 deletions(-)

diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts
index 6b4e2fab..3e9362e7 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts
@@ -3,6 +3,7 @@ import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { currentEditedOverlaySelector } from '@/redux/modal/modal.selector';
 import { closeModal } from '@/redux/modal/modal.slice';
 import {
+  getAllPublicOverlaysByProjectId,
   getAllUserOverlaysByCreator,
   removeOverlay,
   updateOverlays,
@@ -12,6 +13,7 @@ import { MapOverlay, OverlayGroup } from '@/types/models';
 import React, { useState } from 'react';
 import { overlayGroupsSelector } from '@/redux/overlayGroup/overlayGroup.selectors';
 import { ZERO } from '@/constants/common';
+import { PROJECT_ID } from '@/constants';
 
 type UseEditOverlayReturn = {
   name: string | undefined;
@@ -100,6 +102,7 @@ export const useEditOverlay = (): UseEditOverlayReturn => {
     });
 
     await getUserOverlaysByCreator();
+    dispatch(getAllPublicOverlaysByProjectId(PROJECT_ID));
 
     handleCloseModal();
   };
diff --git a/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/GeneralOverlays.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/GeneralOverlays.component.tsx
index e8442c37..d8a064e0 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/GeneralOverlays.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/GeneralOverlays.component.tsx
@@ -1,18 +1,38 @@
 import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { overlaysDataSelector } from '@/redux/overlays/overlays.selectors';
+import { userRoleSelector } from '@/redux/user/user.selectors';
+import { USER_ROLE } from '@/constants/user';
+import { UserOverlaysGroup } from '@/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup';
+import React from 'react';
 import { OverlayListItem } from './OverlayListItem';
 
 export const GeneralOverlays = (): JSX.Element => {
   const generalPublicOverlays = useAppSelector(overlaysDataSelector);
+  const role = useAppSelector(userRoleSelector);
+
+  const isAdmin = role === USER_ROLE.ADMIN;
 
   return (
-    <div className="border-b border-b-divide p-6">
-      <p className="mb-5 text-sm font-semibold">Shared Overlays:</p>
-      <ul>
-        {generalPublicOverlays.map(overlay => (
-          <OverlayListItem key={overlay.id} name={overlay.name} overlayId={overlay.id} />
-        ))}
-      </ul>
+    <div>
+      {isAdmin && (
+        <UserOverlaysGroup
+          key={0}
+          groupName="Shared overlays"
+          groupId={null}
+          overlays={generalPublicOverlays}
+          dangerouslySetExpanded
+        />
+      )}
+      {!isAdmin && (
+        <div className="border-b border-b-divide p-6">
+          <p className="mb-5 text-sm font-semibold">Shared Overlays:</p>
+          <ul>
+            {generalPublicOverlays.map(overlay => (
+              <OverlayListItem key={overlay.id} name={overlay.name} overlayId={overlay.id} />
+            ))}
+          </ul>
+        </div>
+      )}
     </div>
   );
 };
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
index cc982139..b18be523 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlaysGroup/UserOverlaysGroup.component.tsx
@@ -21,12 +21,14 @@ type OverlayGroupProps = {
   groupId: number | null;
   groupName: string;
   overlays: MapOverlay[];
+  dangerouslySetExpanded?: boolean;
 };
 
 export const UserOverlaysGroup = ({
   overlays,
   groupName,
   groupId,
+  dangerouslySetExpanded,
 }: OverlayGroupProps): React.ReactNode => {
   const { moveUserOverlayListItem, updateUserOverlaysOrder, isPending, userOverlaysList } =
     useUserOverlays(overlays, groupId);
@@ -49,8 +51,6 @@ export const UserOverlaysGroup = ({
   };
 
   const openEditGroup = (): void => {
-    // eslint-disable-next-line no-console
-    console.log('Open edit group', groupId);
     if (overlayGroup) {
       dispatch(openEditOverlayGroupModal(overlayGroup));
     }
@@ -60,7 +60,7 @@ export const UserOverlaysGroup = ({
     <DndProvider backend={HTML5Backend}>
       <div className="mt-2.5">
         <Accordion allowZeroExpanded>
-          <AccordionItem className="border-b-0">
+          <AccordionItem className="border-b-0" dangerouslySetExpanded={dangerouslySetExpanded}>
             <AccordionItemHeading>
               <AccordionItemButton
                 className="px-6 text-sm font-semibold"
-- 
GitLab