diff --git a/package-lock.json b/package-lock.json
index 93aa9055e22ddae368df2d8f59f6dbbc92ef3785..6f42bd0976df8e01c2764dd4c37253b89ed17558 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -37,6 +37,7 @@
         "react-dom": "18.2.0",
         "react-dropzone": "14.2.3",
         "react-redux": "8.1.3",
+        "react-select": "5.9.0",
         "sonner": "1.4.3",
         "tailwind-merge": "1.14.0",
         "tailwindcss": "3.4.13",
@@ -136,7 +137,6 @@
       "version": "7.23.5",
       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
       "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
-      "dev": true,
       "dependencies": {
         "@babel/highlight": "^7.23.4",
         "chalk": "^2.4.2"
@@ -149,7 +149,6 @@
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
       "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
       "dependencies": {
         "color-convert": "^1.9.0"
       },
@@ -161,7 +160,6 @@
       "version": "2.4.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
       "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-      "dev": true,
       "dependencies": {
         "ansi-styles": "^3.2.1",
         "escape-string-regexp": "^1.0.5",
@@ -175,7 +173,6 @@
       "version": "1.9.3",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
       "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
       "dependencies": {
         "color-name": "1.1.3"
       }
@@ -183,14 +180,12 @@
     "node_modules/@babel/code-frame/node_modules/color-name": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-      "dev": true
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
     },
     "node_modules/@babel/code-frame/node_modules/has-flag": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
       "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-      "dev": true,
       "engines": {
         "node": ">=4"
       }
@@ -199,7 +194,6 @@
       "version": "5.5.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
       "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dev": true,
       "dependencies": {
         "has-flag": "^3.0.0"
       },
@@ -360,7 +354,6 @@
       "version": "7.22.15",
       "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
       "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
-      "dev": true,
       "dependencies": {
         "@babel/types": "^7.22.15"
       },
@@ -424,7 +417,6 @@
       "version": "7.23.4",
       "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
       "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
-      "dev": true,
       "engines": {
         "node": ">=6.9.0"
       }
@@ -433,7 +425,6 @@
       "version": "7.22.20",
       "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
       "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
-      "dev": true,
       "engines": {
         "node": ">=6.9.0"
       }
@@ -465,7 +456,6 @@
       "version": "7.23.4",
       "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
       "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
-      "dev": true,
       "dependencies": {
         "@babel/helper-validator-identifier": "^7.22.20",
         "chalk": "^2.4.2",
@@ -479,7 +469,6 @@
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
       "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
       "dependencies": {
         "color-convert": "^1.9.0"
       },
@@ -491,7 +480,6 @@
       "version": "2.4.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
       "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-      "dev": true,
       "dependencies": {
         "ansi-styles": "^3.2.1",
         "escape-string-regexp": "^1.0.5",
@@ -505,7 +493,6 @@
       "version": "1.9.3",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
       "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
       "dependencies": {
         "color-name": "1.1.3"
       }
@@ -513,14 +500,12 @@
     "node_modules/@babel/highlight/node_modules/color-name": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-      "dev": true
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
     },
     "node_modules/@babel/highlight/node_modules/has-flag": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
       "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-      "dev": true,
       "engines": {
         "node": ">=4"
       }
@@ -529,7 +514,6 @@
       "version": "5.5.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
       "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dev": true,
       "dependencies": {
         "has-flag": "^3.0.0"
       },
@@ -785,7 +769,6 @@
       "version": "7.23.6",
       "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz",
       "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==",
-      "dev": true,
       "dependencies": {
         "@babel/helper-string-parser": "^7.23.4",
         "@babel/helper-validator-identifier": "^7.22.20",
@@ -1217,6 +1200,133 @@
         "ms": "^2.1.1"
       }
     },
+    "node_modules/@emotion/babel-plugin": {
+      "version": "11.13.5",
+      "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
+      "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.16.7",
+        "@babel/runtime": "^7.18.3",
+        "@emotion/hash": "^0.9.2",
+        "@emotion/memoize": "^0.9.0",
+        "@emotion/serialize": "^1.3.3",
+        "babel-plugin-macros": "^3.1.0",
+        "convert-source-map": "^1.5.0",
+        "escape-string-regexp": "^4.0.0",
+        "find-root": "^1.1.0",
+        "source-map": "^0.5.7",
+        "stylis": "4.2.0"
+      }
+    },
+    "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+      "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
+    },
+    "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@emotion/babel-plugin/node_modules/source-map": {
+      "version": "0.5.7",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+      "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/@emotion/cache": {
+      "version": "11.14.0",
+      "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
+      "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
+      "dependencies": {
+        "@emotion/memoize": "^0.9.0",
+        "@emotion/sheet": "^1.4.0",
+        "@emotion/utils": "^1.4.2",
+        "@emotion/weak-memoize": "^0.4.0",
+        "stylis": "4.2.0"
+      }
+    },
+    "node_modules/@emotion/hash": {
+      "version": "0.9.2",
+      "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+      "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
+    },
+    "node_modules/@emotion/memoize": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+      "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
+    },
+    "node_modules/@emotion/react": {
+      "version": "11.14.0",
+      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
+      "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
+      "dependencies": {
+        "@babel/runtime": "^7.18.3",
+        "@emotion/babel-plugin": "^11.13.5",
+        "@emotion/cache": "^11.14.0",
+        "@emotion/serialize": "^1.3.3",
+        "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+        "@emotion/utils": "^1.4.2",
+        "@emotion/weak-memoize": "^0.4.0",
+        "hoist-non-react-statics": "^3.3.1"
+      },
+      "peerDependencies": {
+        "react": ">=16.8.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@emotion/serialize": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+      "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+      "dependencies": {
+        "@emotion/hash": "^0.9.2",
+        "@emotion/memoize": "^0.9.0",
+        "@emotion/unitless": "^0.10.0",
+        "@emotion/utils": "^1.4.2",
+        "csstype": "^3.0.2"
+      }
+    },
+    "node_modules/@emotion/sheet": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+      "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="
+    },
+    "node_modules/@emotion/unitless": {
+      "version": "0.10.0",
+      "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+      "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="
+    },
+    "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
+      "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
+      "peerDependencies": {
+        "react": ">=16.8.0"
+      }
+    },
+    "node_modules/@emotion/utils": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+      "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="
+    },
+    "node_modules/@emotion/weak-memoize": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+      "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
+    },
     "node_modules/@eslint-community/eslint-utils": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -1289,6 +1399,28 @@
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       }
     },
+    "node_modules/@floating-ui/core": {
+      "version": "1.6.8",
+      "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz",
+      "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==",
+      "dependencies": {
+        "@floating-ui/utils": "^0.2.8"
+      }
+    },
+    "node_modules/@floating-ui/dom": {
+      "version": "1.6.12",
+      "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz",
+      "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==",
+      "dependencies": {
+        "@floating-ui/core": "^1.6.0",
+        "@floating-ui/utils": "^0.2.8"
+      }
+    },
+    "node_modules/@floating-ui/utils": {
+      "version": "0.2.8",
+      "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz",
+      "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="
+    },
     "node_modules/@humanwhocodes/config-array": {
       "version": "0.11.13",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
@@ -2435,6 +2567,11 @@
       "resolved": "https://registry.npmjs.org/@types/openlayers/-/openlayers-4.6.23.tgz",
       "integrity": "sha512-7jCPIa4D4LV03Rttae1AEqvkIN0+nc6Snz4IgA/IjsJD5O3ONxpscqIOdp1qAGuAsikR/ZC9vrPF9np8JRc6ig=="
     },
+    "node_modules/@types/parse-json": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+      "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
+    },
     "node_modules/@types/prop-types": {
       "version": "15.7.11",
       "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
@@ -2484,6 +2621,14 @@
         "redux": "^4.0.0"
       }
     },
+    "node_modules/@types/react-transition-group": {
+      "version": "4.4.12",
+      "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
+      "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
+      "peerDependencies": {
+        "@types/react": "*"
+      }
+    },
     "node_modules/@types/redux-mock-store": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/@types/redux-mock-store/-/redux-mock-store-1.0.6.tgz",
@@ -3383,6 +3528,43 @@
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
+    "node_modules/babel-plugin-macros": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+      "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+      "dependencies": {
+        "@babel/runtime": "^7.12.5",
+        "cosmiconfig": "^7.0.0",
+        "resolve": "^1.19.0"
+      },
+      "engines": {
+        "node": ">=10",
+        "npm": ">=6"
+      }
+    },
+    "node_modules/babel-plugin-macros/node_modules/cosmiconfig": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+      "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+      "dependencies": {
+        "@types/parse-json": "^4.0.0",
+        "import-fresh": "^3.2.1",
+        "parse-json": "^5.0.0",
+        "path-type": "^4.0.0",
+        "yaml": "^1.10.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/babel-plugin-macros/node_modules/yaml": {
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+      "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/babel-preset-current-node-syntax": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
@@ -5044,6 +5226,15 @@
       "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
       "dev": true
     },
+    "node_modules/dom-helpers": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+      "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+      "dependencies": {
+        "@babel/runtime": "^7.8.7",
+        "csstype": "^3.0.2"
+      }
+    },
     "node_modules/domexception": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
@@ -5211,7 +5402,6 @@
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
       "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
-      "dev": true,
       "dependencies": {
         "is-arrayish": "^0.2.1"
       }
@@ -5363,7 +5553,6 @@
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
       "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
-      "dev": true,
       "engines": {
         "node": ">=0.8.0"
       }
@@ -6569,8 +6758,7 @@
     "node_modules/find-root": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
-      "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
-      "dev": true
+      "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
     },
     "node_modules/find-up": {
       "version": "4.1.0",
@@ -7553,8 +7741,7 @@
     "node_modules/is-arrayish": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
-      "dev": true
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
     },
     "node_modules/is-async-function": {
       "version": "2.0.0",
@@ -9292,8 +9479,7 @@
     "node_modules/json-parse-even-better-errors": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
-      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
-      "dev": true
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
     },
     "node_modules/json-schema": {
       "version": "0.4.0",
@@ -10294,6 +10480,11 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/memoize-one": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
+      "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
+    },
     "node_modules/meow": {
       "version": "8.1.2",
       "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz",
@@ -10984,7 +11175,6 @@
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
       "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
-      "dev": true,
       "dependencies": {
         "@babel/code-frame": "^7.0.0",
         "error-ex": "^1.3.1",
@@ -11811,6 +12001,26 @@
         }
       }
     },
+    "node_modules/react-select": {
+      "version": "5.9.0",
+      "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.9.0.tgz",
+      "integrity": "sha512-nwRKGanVHGjdccsnzhFte/PULziueZxGD8LL2WojON78Mvnq7LdAMEtu2frrwld1fr3geixg3iiMBIc/LLAZpw==",
+      "dependencies": {
+        "@babel/runtime": "^7.12.0",
+        "@emotion/cache": "^11.4.0",
+        "@emotion/react": "^11.8.1",
+        "@floating-ui/dom": "^1.0.1",
+        "@types/react-transition-group": "^4.4.0",
+        "memoize-one": "^6.0.0",
+        "prop-types": "^15.6.0",
+        "react-transition-group": "^4.3.0",
+        "use-isomorphic-layout-effect": "^1.2.0"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+        "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+      }
+    },
     "node_modules/react-themeable": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/react-themeable/-/react-themeable-1.1.0.tgz",
@@ -11827,6 +12037,21 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/react-transition-group": {
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+      "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+      "dependencies": {
+        "@babel/runtime": "^7.5.5",
+        "dom-helpers": "^5.0.1",
+        "loose-envify": "^1.4.0",
+        "prop-types": "^15.6.2"
+      },
+      "peerDependencies": {
+        "react": ">=16.6.0",
+        "react-dom": ">=16.6.0"
+      }
+    },
     "node_modules/read-cache": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -12908,6 +13133,11 @@
         }
       }
     },
+    "node_modules/stylis": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+      "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
+    },
     "node_modules/sucrase": {
       "version": "3.35.0",
       "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
@@ -13173,7 +13403,6 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
       "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
-      "dev": true,
       "engines": {
         "node": ">=4"
       }
@@ -13568,6 +13797,19 @@
         "react": ">=16.8.0"
       }
     },
+    "node_modules/use-isomorphic-layout-effect": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.0.tgz",
+      "integrity": "sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==",
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/use-sync-external-store": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
@@ -14177,7 +14419,6 @@
       "version": "7.23.5",
       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
       "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
-      "dev": true,
       "requires": {
         "@babel/highlight": "^7.23.4",
         "chalk": "^2.4.2"
@@ -14187,7 +14428,6 @@
           "version": "3.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
           "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
           "requires": {
             "color-convert": "^1.9.0"
           }
@@ -14196,7 +14436,6 @@
           "version": "2.4.2",
           "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
           "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-          "dev": true,
           "requires": {
             "ansi-styles": "^3.2.1",
             "escape-string-regexp": "^1.0.5",
@@ -14207,7 +14446,6 @@
           "version": "1.9.3",
           "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
           "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-          "dev": true,
           "requires": {
             "color-name": "1.1.3"
           }
@@ -14215,20 +14453,17 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-          "dev": true
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-          "dev": true
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
         },
         "supports-color": {
           "version": "5.5.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
           "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-          "dev": true,
           "requires": {
             "has-flag": "^3.0.0"
           }
@@ -14355,7 +14590,6 @@
       "version": "7.22.15",
       "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
       "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
-      "dev": true,
       "requires": {
         "@babel/types": "^7.22.15"
       }
@@ -14400,14 +14634,12 @@
     "@babel/helper-string-parser": {
       "version": "7.23.4",
       "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
-      "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
-      "dev": true
+      "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ=="
     },
     "@babel/helper-validator-identifier": {
       "version": "7.22.20",
       "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
-      "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
-      "dev": true
+      "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A=="
     },
     "@babel/helper-validator-option": {
       "version": "7.23.5",
@@ -14430,7 +14662,6 @@
       "version": "7.23.4",
       "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
       "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
-      "dev": true,
       "requires": {
         "@babel/helper-validator-identifier": "^7.22.20",
         "chalk": "^2.4.2",
@@ -14441,7 +14672,6 @@
           "version": "3.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
           "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
           "requires": {
             "color-convert": "^1.9.0"
           }
@@ -14450,7 +14680,6 @@
           "version": "2.4.2",
           "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
           "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-          "dev": true,
           "requires": {
             "ansi-styles": "^3.2.1",
             "escape-string-regexp": "^1.0.5",
@@ -14461,7 +14690,6 @@
           "version": "1.9.3",
           "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
           "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-          "dev": true,
           "requires": {
             "color-name": "1.1.3"
           }
@@ -14469,20 +14697,17 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-          "dev": true
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-          "dev": true
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
         },
         "supports-color": {
           "version": "5.5.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
           "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-          "dev": true,
           "requires": {
             "has-flag": "^3.0.0"
           }
@@ -14670,7 +14895,6 @@
       "version": "7.23.6",
       "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz",
       "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==",
-      "dev": true,
       "requires": {
         "@babel/helper-string-parser": "^7.23.4",
         "@babel/helper-validator-identifier": "^7.22.20",
@@ -15016,6 +15240,116 @@
         }
       }
     },
+    "@emotion/babel-plugin": {
+      "version": "11.13.5",
+      "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
+      "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+      "requires": {
+        "@babel/helper-module-imports": "^7.16.7",
+        "@babel/runtime": "^7.18.3",
+        "@emotion/hash": "^0.9.2",
+        "@emotion/memoize": "^0.9.0",
+        "@emotion/serialize": "^1.3.3",
+        "babel-plugin-macros": "^3.1.0",
+        "convert-source-map": "^1.5.0",
+        "escape-string-regexp": "^4.0.0",
+        "find-root": "^1.1.0",
+        "source-map": "^0.5.7",
+        "stylis": "4.2.0"
+      },
+      "dependencies": {
+        "convert-source-map": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+          "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
+        },
+        "escape-string-regexp": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+          "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
+        }
+      }
+    },
+    "@emotion/cache": {
+      "version": "11.14.0",
+      "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
+      "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
+      "requires": {
+        "@emotion/memoize": "^0.9.0",
+        "@emotion/sheet": "^1.4.0",
+        "@emotion/utils": "^1.4.2",
+        "@emotion/weak-memoize": "^0.4.0",
+        "stylis": "4.2.0"
+      }
+    },
+    "@emotion/hash": {
+      "version": "0.9.2",
+      "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+      "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
+    },
+    "@emotion/memoize": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+      "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
+    },
+    "@emotion/react": {
+      "version": "11.14.0",
+      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
+      "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
+      "requires": {
+        "@babel/runtime": "^7.18.3",
+        "@emotion/babel-plugin": "^11.13.5",
+        "@emotion/cache": "^11.14.0",
+        "@emotion/serialize": "^1.3.3",
+        "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+        "@emotion/utils": "^1.4.2",
+        "@emotion/weak-memoize": "^0.4.0",
+        "hoist-non-react-statics": "^3.3.1"
+      }
+    },
+    "@emotion/serialize": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+      "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+      "requires": {
+        "@emotion/hash": "^0.9.2",
+        "@emotion/memoize": "^0.9.0",
+        "@emotion/unitless": "^0.10.0",
+        "@emotion/utils": "^1.4.2",
+        "csstype": "^3.0.2"
+      }
+    },
+    "@emotion/sheet": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+      "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="
+    },
+    "@emotion/unitless": {
+      "version": "0.10.0",
+      "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+      "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="
+    },
+    "@emotion/use-insertion-effect-with-fallbacks": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
+      "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
+      "requires": {}
+    },
+    "@emotion/utils": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+      "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="
+    },
+    "@emotion/weak-memoize": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+      "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
+    },
     "@eslint-community/eslint-utils": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -15068,6 +15402,28 @@
       "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
       "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A=="
     },
+    "@floating-ui/core": {
+      "version": "1.6.8",
+      "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz",
+      "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==",
+      "requires": {
+        "@floating-ui/utils": "^0.2.8"
+      }
+    },
+    "@floating-ui/dom": {
+      "version": "1.6.12",
+      "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz",
+      "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==",
+      "requires": {
+        "@floating-ui/core": "^1.6.0",
+        "@floating-ui/utils": "^0.2.8"
+      }
+    },
+    "@floating-ui/utils": {
+      "version": "0.2.8",
+      "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz",
+      "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="
+    },
     "@humanwhocodes/config-array": {
       "version": "0.11.13",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
@@ -15925,6 +16281,11 @@
       "resolved": "https://registry.npmjs.org/@types/openlayers/-/openlayers-4.6.23.tgz",
       "integrity": "sha512-7jCPIa4D4LV03Rttae1AEqvkIN0+nc6Snz4IgA/IjsJD5O3ONxpscqIOdp1qAGuAsikR/ZC9vrPF9np8JRc6ig=="
     },
+    "@types/parse-json": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+      "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
+    },
     "@types/prop-types": {
       "version": "15.7.11",
       "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
@@ -15974,6 +16335,12 @@
         "redux": "^4.0.0"
       }
     },
+    "@types/react-transition-group": {
+      "version": "4.4.12",
+      "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
+      "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
+      "requires": {}
+    },
     "@types/redux-mock-store": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/@types/redux-mock-store/-/redux-mock-store-1.0.6.tgz",
@@ -16606,6 +16973,35 @@
         "@types/babel__traverse": "^7.0.6"
       }
     },
+    "babel-plugin-macros": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+      "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+      "requires": {
+        "@babel/runtime": "^7.12.5",
+        "cosmiconfig": "^7.0.0",
+        "resolve": "^1.19.0"
+      },
+      "dependencies": {
+        "cosmiconfig": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+          "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+          "requires": {
+            "@types/parse-json": "^4.0.0",
+            "import-fresh": "^3.2.1",
+            "parse-json": "^5.0.0",
+            "path-type": "^4.0.0",
+            "yaml": "^1.10.0"
+          }
+        },
+        "yaml": {
+          "version": "1.10.2",
+          "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+          "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
+        }
+      }
+    },
     "babel-preset-current-node-syntax": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
@@ -17840,6 +18236,15 @@
       "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
       "dev": true
     },
+    "dom-helpers": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+      "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+      "requires": {
+        "@babel/runtime": "^7.8.7",
+        "csstype": "^3.0.2"
+      }
+    },
     "domexception": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
@@ -17972,7 +18377,6 @@
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
       "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
-      "dev": true,
       "requires": {
         "is-arrayish": "^0.2.1"
       }
@@ -18102,8 +18506,7 @@
     "escape-string-regexp": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
-      "dev": true
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
     },
     "escodegen": {
       "version": "2.1.0",
@@ -18933,8 +19336,7 @@
     "find-root": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
-      "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
-      "dev": true
+      "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
     },
     "find-up": {
       "version": "4.1.0",
@@ -19631,8 +20033,7 @@
     "is-arrayish": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
-      "dev": true
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
     },
     "is-async-function": {
       "version": "2.0.0",
@@ -20872,8 +21273,7 @@
     "json-parse-even-better-errors": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
-      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
-      "dev": true
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
     },
     "json-schema": {
       "version": "0.4.0",
@@ -21615,6 +22015,11 @@
       "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
       "dev": true
     },
+    "memoize-one": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
+      "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
+    },
     "meow": {
       "version": "8.1.2",
       "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz",
@@ -22111,7 +22516,6 @@
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
       "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
-      "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
         "error-ex": "^1.3.1",
@@ -22602,6 +23006,22 @@
         "use-sync-external-store": "^1.0.0"
       }
     },
+    "react-select": {
+      "version": "5.9.0",
+      "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.9.0.tgz",
+      "integrity": "sha512-nwRKGanVHGjdccsnzhFte/PULziueZxGD8LL2WojON78Mvnq7LdAMEtu2frrwld1fr3geixg3iiMBIc/LLAZpw==",
+      "requires": {
+        "@babel/runtime": "^7.12.0",
+        "@emotion/cache": "^11.4.0",
+        "@emotion/react": "^11.8.1",
+        "@floating-ui/dom": "^1.0.1",
+        "@types/react-transition-group": "^4.4.0",
+        "memoize-one": "^6.0.0",
+        "prop-types": "^15.6.0",
+        "react-transition-group": "^4.3.0",
+        "use-isomorphic-layout-effect": "^1.2.0"
+      }
+    },
     "react-themeable": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/react-themeable/-/react-themeable-1.1.0.tgz",
@@ -22617,6 +23037,17 @@
         }
       }
     },
+    "react-transition-group": {
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+      "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+      "requires": {
+        "@babel/runtime": "^7.5.5",
+        "dom-helpers": "^5.0.1",
+        "loose-envify": "^1.4.0",
+        "prop-types": "^15.6.2"
+      }
+    },
     "read-cache": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -23430,6 +23861,11 @@
         "client-only": "0.0.1"
       }
     },
+    "stylis": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+      "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
+    },
     "sucrase": {
       "version": "3.35.0",
       "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
@@ -23629,8 +24065,7 @@
     "to-fast-properties": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
-      "dev": true
+      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="
     },
     "to-regex-range": {
       "version": "5.0.1",
@@ -23903,6 +24338,12 @@
       "integrity": "sha512-6X8H/mikbrt0XE8e+JXRtZ8yYVvKkdYRfmIhWZYsP8rcNs9hk3APV8Ua2mFkKRLcJKVdnX2/Vwrmg2GWKUQEaQ==",
       "requires": {}
     },
+    "use-isomorphic-layout-effect": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.0.tgz",
+      "integrity": "sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==",
+      "requires": {}
+    },
     "use-sync-external-store": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
diff --git a/package.json b/package.json
index 213f3cd7aeefd37073e42e98a791e68064c412c5..52f25ca960a70a3e6c7eb204f2519242adfd512c 100644
--- a/package.json
+++ b/package.json
@@ -51,6 +51,7 @@
     "react-dom": "18.2.0",
     "react-dropzone": "14.2.3",
     "react-redux": "8.1.3",
+    "react-select": "5.9.0",
     "sonner": "1.4.3",
     "tailwind-merge": "1.14.0",
     "tailwindcss": "3.4.13",
diff --git a/src/components/AppWrapper/AppWrapper.component.tsx b/src/components/AppWrapper/AppWrapper.component.tsx
index 2bae3997dd16e426802fd547b3235057ce22aa8c..39a65352f62dfc4a51e0c0a198333ef4f7b8ab17 100644
--- a/src/components/AppWrapper/AppWrapper.component.tsx
+++ b/src/components/AppWrapper/AppWrapper.component.tsx
@@ -3,6 +3,7 @@ import { MapInstanceProvider } from '@/utils/context/mapInstanceContext';
 import { ReactNode } from 'react';
 import { Provider } from 'react-redux';
 import { Toaster } from 'sonner';
+import { Modal } from '@/components/FunctionalArea/Modal';
 
 interface AppWrapperProps {
   children: ReactNode;
@@ -13,6 +14,7 @@ export const AppWrapper = ({ children }: AppWrapperProps): JSX.Element => {
     <MapInstanceProvider>
       <Provider store={store}>
         <>
+          <Modal />
           <Toaster
             position="top-center"
             visibleToasts={1}
diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
index eab74a7e4fd30da041120e5731c0987b8720dc3e..5e0b2ec58fb1783beee5480196e935a6f0ed932a 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
@@ -50,6 +50,7 @@ describe('EditOverlayModal - component', () => {
         overviewImagesState: {},
         errorReportState: {},
         layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
       },
     });
 
@@ -69,6 +70,7 @@ describe('EditOverlayModal - component', () => {
         overviewImagesState: {},
         errorReportState: {},
         layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
       },
     });
 
@@ -100,6 +102,7 @@ describe('EditOverlayModal - component', () => {
         overviewImagesState: {},
         errorReportState: {},
         layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
       },
       overlays: OVERLAYS_INITIAL_STATE_MOCK,
     });
@@ -136,6 +139,7 @@ describe('EditOverlayModal - component', () => {
         overviewImagesState: {},
         errorReportState: {},
         layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
       },
       overlays: OVERLAYS_INITIAL_STATE_MOCK,
     });
@@ -173,6 +177,7 @@ describe('EditOverlayModal - component', () => {
         overviewImagesState: {},
         errorReportState: {},
         layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
       },
       overlays: OVERLAYS_INITIAL_STATE_MOCK,
     });
@@ -218,6 +223,7 @@ describe('EditOverlayModal - component', () => {
         overviewImagesState: {},
         errorReportState: {},
         layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
       },
       overlays: OVERLAYS_INITIAL_STATE_MOCK,
     });
@@ -248,6 +254,7 @@ describe('EditOverlayModal - component', () => {
         overviewImagesState: {},
         errorReportState: {},
         layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
       },
     });
 
diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts
index 16f4307190235d6e03c4175b168b4b68fbc46e50..0079d31f1d11e8720a32c335826d0c86987d4ca3 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.test.ts
@@ -25,6 +25,7 @@ describe('useEditOverlay', () => {
         overviewImagesState: {},
         errorReportState: {},
         layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
       },
     });
 
@@ -62,6 +63,7 @@ describe('useEditOverlay', () => {
         overviewImagesState: {},
         errorReportState: {},
         layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
       },
     });
 
@@ -102,6 +104,7 @@ describe('useEditOverlay', () => {
         overviewImagesState: {},
         errorReportState: {},
         layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
       },
     });
 
@@ -138,6 +141,7 @@ describe('useEditOverlay', () => {
         overviewImagesState: {},
         errorReportState: {},
         layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
       },
     });
 
@@ -175,6 +179,7 @@ describe('useEditOverlay', () => {
         overviewImagesState: {},
         errorReportState: {},
         layerFactoryState: { id: undefined },
+        layerImageObjectFactoryState: undefined,
       },
     });
 
diff --git a/src/components/FunctionalArea/Modal/LayerImageObjectFactoryModal/LayerImageObjectFactoryModal.component.test.tsx b/src/components/FunctionalArea/Modal/LayerImageObjectFactoryModal/LayerImageObjectFactoryModal.component.test.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..5806621f6ee2e401190e84d6516adce5d64c5ea1
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/LayerImageObjectFactoryModal/LayerImageObjectFactoryModal.component.test.tsx
@@ -0,0 +1,141 @@
+/* eslint-disable no-magic-numbers */
+import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import { act } from 'react-dom/test-utils';
+import { StoreType } from '@/redux/store';
+import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
+import { INITIAL_STORE_STATE_MOCK } from '@/redux/root/root.fixtures';
+import { GLYPHS_STATE_INITIAL_MOCK } from '@/redux/glyphs/glyphs.mock';
+import { apiPath } from '@/redux/apiPath';
+import { HttpStatusCode } from 'axios';
+import { layerImageFixture } from '@/models/fixtures/layerImageFixture';
+import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
+import {
+  LAYER_STATE_DEFAULT_DATA,
+  LAYERS_STATE_INITIAL_LAYER_MOCK,
+} from '@/redux/layers/layers.mock';
+import { MODELS_DATA_MOCK_WITH_MAIN_MAP } from '@/redux/models/models.mock';
+import { overlayFixture } from '@/models/fixtures/overlaysFixture';
+import { showToast } from '@/utils/showToast';
+import { LayerImageObjectFactoryModal } from './LayerImageObjectFactoryModal.component';
+
+const mockedAxiosNewClient = mockNetworkNewAPIResponse();
+
+const glyph = { id: 1, file: 23, filename: 'Glyph1.png' };
+
+jest.mock('../../../../utils/showToast');
+
+const renderComponent = (): { store: StoreType } => {
+  const { Wrapper, store } = getReduxWrapperWithStore({
+    ...INITIAL_STORE_STATE_MOCK,
+    glyphs: {
+      ...GLYPHS_STATE_INITIAL_MOCK,
+      data: [glyph],
+    },
+    layers: {
+      0: {
+        ...LAYERS_STATE_INITIAL_LAYER_MOCK,
+        data: {
+          ...LAYER_STATE_DEFAULT_DATA,
+          activeLayer: 1,
+        },
+      },
+    },
+    modal: {
+      isOpen: true,
+      modalTitle: overlayFixture.name,
+      modalName: 'edit-overlay',
+      editOverlayState: overlayFixture,
+      molArtState: {},
+      overviewImagesState: {},
+      errorReportState: {},
+      layerFactoryState: { id: undefined },
+      layerImageObjectFactoryState: {
+        x: 1,
+        y: 1,
+        width: 1,
+        height: 1,
+      },
+    },
+    models: {
+      ...MODELS_DATA_MOCK_WITH_MAIN_MAP,
+    },
+  });
+
+  return {
+    store,
+    ...render(
+      <Wrapper>
+        <LayerImageObjectFactoryModal />
+      </Wrapper>,
+    ),
+  };
+};
+
+describe('LayerImageObjectFactoryModal - component', () => {
+  it('should render LayerImageObjectFactoryModal component with initial state', () => {
+    renderComponent();
+
+    expect(screen.getByText(/Glyph:/i)).toBeInTheDocument();
+    expect(screen.getByText(/File:/i)).toBeInTheDocument();
+    expect(screen.getByText(/Submit/i)).toBeInTheDocument();
+    expect(screen.getByText(/No Image/i)).toBeInTheDocument();
+  });
+
+  it('should display a list of glyphs in the dropdown', async () => {
+    renderComponent();
+
+    const dropdown = screen.getByTestId('autocomplete');
+    if (!dropdown.firstChild) {
+      throw new Error('Dropdown does not have a firstChild');
+    }
+    fireEvent.keyDown(dropdown.firstChild, { key: 'ArrowDown' });
+    await waitFor(() => expect(screen.getByText(glyph.filename)).toBeInTheDocument());
+    fireEvent.click(screen.getByText(glyph.filename));
+  });
+
+  it('should update the selected glyph on dropdown change', async () => {
+    renderComponent();
+
+    const dropdown = screen.getByTestId('autocomplete');
+    if (!dropdown.firstChild) {
+      throw new Error('Dropdown does not have a firstChild');
+    }
+    fireEvent.keyDown(dropdown.firstChild, { key: 'ArrowDown' });
+    await waitFor(() => expect(screen.getByText(glyph.filename)).toBeInTheDocument());
+    fireEvent.click(screen.getByText(glyph.filename));
+
+    await waitFor(() => {
+      const imgPreview: HTMLImageElement = screen.getByTestId('layer-image-preview');
+      const decodedSrc = decodeURIComponent(imgPreview.src);
+      expect(decodedSrc).toContain(`glyphs/${glyph.id}/fileContent`);
+    });
+  });
+
+  it('should handle form submission correctly', async () => {
+    mockedAxiosNewClient
+      .onPost(apiPath.addLayerImageObject(0, 1))
+      .reply(HttpStatusCode.Ok, layerImageFixture);
+    renderComponent();
+
+    const submitButton = screen.getByText(/Submit/i);
+
+    await act(async () => {
+      fireEvent.click(submitButton);
+    });
+
+    expect(showToast).toHaveBeenCalledWith({
+      message: 'A new image object has been successfully added',
+      type: 'success',
+    });
+  });
+
+  it('should display "No Image" when there is no image file', () => {
+    const { store } = renderComponent();
+
+    store.dispatch({
+      type: 'glyphs/clearGlyphData',
+    });
+
+    expect(screen.getByText(/No Image/i)).toBeInTheDocument();
+  });
+});
diff --git a/src/components/FunctionalArea/Modal/LayerImageObjectFactoryModal/LayerImageObjectFactoryModal.component.tsx b/src/components/FunctionalArea/Modal/LayerImageObjectFactoryModal/LayerImageObjectFactoryModal.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b750d0618794a63ed1219c7c3aa35c8bf471f450
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/LayerImageObjectFactoryModal/LayerImageObjectFactoryModal.component.tsx
@@ -0,0 +1,186 @@
+/* eslint-disable no-magic-numbers */
+import React, { useState, useRef } from 'react';
+import { glyphsDataSelector } from '@/redux/glyphs/glyphs.selectors';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { layerImageObjectFactoryStateSelector } from '@/redux/modal/modal.selector';
+import { Button } from '@/shared/Button';
+import { BASE_NEW_API_URL } from '@/constants';
+import { apiPath } from '@/redux/apiPath';
+import { Input } from '@/shared/Input';
+import Image from 'next/image';
+import { Glyph } from '@/types/models';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { currentModelIdSelector } from '@/redux/models/models.selectors';
+import { highestZIndexSelector, layersActiveLayerSelector } from '@/redux/layers/layers.selectors';
+import { addLayerImageObject } from '@/redux/layers/layers.thunks';
+import { addGlyph } from '@/redux/glyphs/glyphs.thunks';
+import { SerializedError } from '@reduxjs/toolkit';
+import { showToast } from '@/utils/showToast';
+import { closeModal } from '@/redux/modal/modal.slice';
+import { LoadingIndicator } from '@/shared/LoadingIndicator';
+import './LayerImageObjectFactoryModal.styles.css';
+import { useMapInstance } from '@/utils/context/mapInstanceContext';
+import { layerAddImage } from '@/redux/layers/layers.slice';
+import { Autocomplete } from '@/shared/Autocomplete';
+
+export const LayerImageObjectFactoryModal: React.FC = () => {
+  const glyphs: Glyph[] = useAppSelector(glyphsDataSelector);
+  const currentModelId = useAppSelector(currentModelIdSelector);
+  const activeLayer = useAppSelector(layersActiveLayerSelector);
+  const layerImageObjectFactoryState = useAppSelector(layerImageObjectFactoryStateSelector);
+  const dispatch = useAppDispatch();
+  const fileInputRef = useRef<HTMLInputElement>(null);
+  const highestZIndex = useAppSelector(highestZIndexSelector);
+  const { mapInstance } = useMapInstance();
+
+  const [selectedGlyph, setSelectedGlyph] = useState<number | null>(null);
+  const [file, setFile] = useState<File | null>(null);
+  const [isSending, setIsSending] = useState<boolean>(false);
+  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
+
+  const handleGlyphChange = (glyph: Glyph | null): void => {
+    const glyphId = glyph?.id || null;
+    setSelectedGlyph(glyphId);
+    if (!glyphId) {
+      return;
+    }
+    setFile(null);
+    setPreviewUrl(`${BASE_NEW_API_URL}${apiPath.getGlyphImage(glyphId)}`);
+
+    if (fileInputRef.current) {
+      fileInputRef.current.value = '';
+    }
+  };
+
+  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
+    const uploadedFile = e.target.files?.[0] || null;
+
+    setFile(uploadedFile);
+    if (!uploadedFile) {
+      return;
+    }
+
+    setSelectedGlyph(null);
+    if (uploadedFile) {
+      const url = URL.createObjectURL(uploadedFile);
+      setPreviewUrl(url);
+    } else {
+      setPreviewUrl(null);
+    }
+  };
+
+  const handleSubmit = async (): Promise<void> => {
+    if (!layerImageObjectFactoryState || !activeLayer) {
+      return;
+    }
+    setIsSending(true);
+    try {
+      let glyphId = selectedGlyph;
+      if (file) {
+        const data = await dispatch(addGlyph(file)).unwrap();
+        if (!data) {
+          return;
+        }
+        glyphId = data.id;
+      }
+      const imageData = await dispatch(
+        addLayerImageObject({
+          modelId: currentModelId,
+          layerId: activeLayer,
+          x: layerImageObjectFactoryState.x,
+          y: layerImageObjectFactoryState.y,
+          z: highestZIndex + 1,
+          width: layerImageObjectFactoryState.width,
+          height: layerImageObjectFactoryState.height,
+          glyph: glyphId,
+        }),
+      ).unwrap();
+      if (!imageData) {
+        showToast({
+          type: 'error',
+          message: 'Error during adding layer image object',
+        });
+        return;
+      }
+      dispatch(
+        layerAddImage({ modelId: currentModelId, layerId: activeLayer, layerImage: imageData }),
+      );
+      mapInstance?.getAllLayers().forEach(layer => {
+        if (layer.get('id') === activeLayer && layer.get('drawImage')) {
+          const drawImage = layer.get('drawImage');
+          if (drawImage instanceof Function) {
+            drawImage(imageData);
+          }
+        }
+      });
+      showToast({
+        type: 'success',
+        message: 'A new image object has been successfully added',
+      });
+      dispatch(closeModal());
+    } catch (error) {
+      const typedError = error as SerializedError;
+      showToast({
+        type: 'error',
+        message: typedError.message || 'An error occurred while adding a new image object',
+      });
+    } finally {
+      setIsSending(false);
+    }
+  };
+
+  return (
+    <div className="relative w-[800px] border border-t-[#E1E0E6] bg-white p-[24px]">
+      {isSending && (
+        <div className="c-layer-image-object-factory-loader">
+          <LoadingIndicator width={44} height={44} />
+        </div>
+      )}
+      <div className="grid grid-cols-2 gap-2">
+        <div className="mb-4 flex flex-col gap-2">
+          <span>Glyph:</span>
+          <Autocomplete<Glyph>
+            options={glyphs}
+            valueKey="id"
+            labelKey="filename"
+            onChange={handleGlyphChange}
+          />
+        </div>
+        <div className="mb-4 flex flex-col gap-2">
+          <span>File:</span>
+          <Input
+            ref={fileInputRef}
+            type="file"
+            accept="image/*"
+            onChange={handleFileChange}
+            data-testid="image-file-input"
+            className="w-full border border-[#ccc] bg-white p-2"
+          />
+        </div>
+      </div>
+
+      <div className="relative mb-4 flex h-[350px] w-full items-center justify-center overflow-hidden rounded border">
+        {previewUrl ? (
+          <Image
+            src={previewUrl}
+            alt="image preview"
+            fill
+            style={{ objectFit: 'contain' }}
+            className="rounded"
+            data-testid="layer-image-preview"
+          />
+        ) : (
+          <div className="text-gray-500">No Image</div>
+        )}
+      </div>
+
+      <Button
+        type="button"
+        onClick={handleSubmit}
+        className="w-full justify-center text-base font-medium"
+      >
+        Submit
+      </Button>
+    </div>
+  );
+};
diff --git a/src/components/FunctionalArea/Modal/LayerImageObjectFactoryModal/LayerImageObjectFactoryModal.styles.css b/src/components/FunctionalArea/Modal/LayerImageObjectFactoryModal/LayerImageObjectFactoryModal.styles.css
new file mode 100644
index 0000000000000000000000000000000000000000..db49e44351fd2a7f31f6ea29a7c1e0409e11b2ba
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/LayerImageObjectFactoryModal/LayerImageObjectFactoryModal.styles.css
@@ -0,0 +1,12 @@
+.c-layer-image-object-factory-loader {
+  width: 100%;
+  height: 100%;
+  margin-left: -24px;
+  margin-top: -24px;
+  background: #f9f9f980;
+  z-index: 1;
+  position: absolute;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
diff --git a/src/components/FunctionalArea/Modal/LayerImageObjectFactoryModal/index.ts b/src/components/FunctionalArea/Modal/LayerImageObjectFactoryModal/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..119478065cc2155d8a9123fa0ecf696cc4bc9e9d
--- /dev/null
+++ b/src/components/FunctionalArea/Modal/LayerImageObjectFactoryModal/index.ts
@@ -0,0 +1 @@
+export { LayerImageObjectFactoryModal } from './LayerImageObjectFactoryModal.component';
diff --git a/src/components/FunctionalArea/Modal/Modal.component.tsx b/src/components/FunctionalArea/Modal/Modal.component.tsx
index 8419791dee19ccf04d34e3c87eb731862356838d..feec78d941d8fb4fcee1bd192539a001907a46af 100644
--- a/src/components/FunctionalArea/Modal/Modal.component.tsx
+++ b/src/components/FunctionalArea/Modal/Modal.component.tsx
@@ -5,6 +5,7 @@ import { AccessDeniedModal } from '@/components/FunctionalArea/Modal/AccessDenie
 import { AddCommentModal } from '@/components/FunctionalArea/Modal/AddCommentModal/AddCommentModal.component';
 import { LicenseModal } from '@/components/FunctionalArea/Modal/LicenseModal';
 import { ToSModal } from '@/components/FunctionalArea/Modal/ToSModal/ToSModal.component';
+import { LayerImageObjectFactoryModal } from '@/components/FunctionalArea/Modal/LayerImageObjectFactoryModal';
 import { EditOverlayModal } from './EditOverlayModal';
 import { LoginModal } from './LoginModal';
 import { ErrorReportModal } from './ErrorReportModal';
@@ -85,6 +86,11 @@ export const Modal = (): React.ReactNode => {
           <LayerFactoryModal />
         </ModalLayout>
       )}
+      {isOpen && modalName === 'layer-image-object-factory' && (
+        <ModalLayout>
+          <LayerImageObjectFactoryModal />
+        </ModalLayout>
+      )}
     </>
   );
 };
diff --git a/src/components/FunctionalArea/Modal/ModalLayout/ModalLayout.component.tsx b/src/components/FunctionalArea/Modal/ModalLayout/ModalLayout.component.tsx
index 493f2bc7a4be1a57cf5218ca7505075893746d29..64816f0bda36c4261fc2735a9c397ba215b16590 100644
--- a/src/components/FunctionalArea/Modal/ModalLayout/ModalLayout.component.tsx
+++ b/src/components/FunctionalArea/Modal/ModalLayout/ModalLayout.component.tsx
@@ -34,6 +34,7 @@ export const ModalLayout = ({ children }: ModalLayoutProps): JSX.Element => {
             modalName === 'add-comment' && 'h-auto w-[400px]',
             modalName === 'error-report' && 'h-auto w-[800px]',
             modalName === 'layer-factory' && 'h-auto w-[400px]',
+            modalName === 'layer-image-object-factory' && 'h-auto w-[800px]',
             ['edit-overlay', 'logged-in-menu'].includes(modalName) && 'h-auto w-[432px]',
           )}
         >
diff --git a/src/components/Map/Drawer/LayersDrawer/LayersDrawer.component.tsx b/src/components/Map/Drawer/LayersDrawer/LayersDrawer.component.tsx
index b431590cad9fd0aca469b6b22e6d1345a69fe8b5..bbed3ad9ffe89dbdd02ad611b6b23ae56ce81208 100644
--- a/src/components/Map/Drawer/LayersDrawer/LayersDrawer.component.tsx
+++ b/src/components/Map/Drawer/LayersDrawer/LayersDrawer.component.tsx
@@ -5,7 +5,6 @@ import {
   layersForCurrentModelSelector,
   layersVisibilityForCurrentModelSelector,
 } from '@/redux/layers/layers.selectors';
-import { Switch } from '@/shared/Switch';
 import { setLayerVisibility } from '@/redux/layers/layers.slice';
 import { currentModelIdSelector } from '@/redux/models/models.selectors';
 import { Button } from '@/shared/Button';
@@ -15,6 +14,7 @@ import { useState } from 'react';
 import { getLayersForModel, removeLayer } from '@/redux/layers/layers.thunks';
 import { showToast } from '@/utils/showToast';
 import { SerializedError } from '@reduxjs/toolkit';
+import { LayersDrawerLayerActions } from '@/components/Map/Drawer/LayersDrawer/LayersDrawerLayerActions.component';
 
 export const LayersDrawer = (): JSX.Element => {
   const layersForCurrentModel = useAppSelector(layersForCurrentModelSelector);
@@ -83,28 +83,20 @@ export const LayersDrawer = (): JSX.Element => {
             className="flex items-center justify-between gap-3 border-b py-4"
           >
             <h1 className="truncate">{layer.details.name}</h1>
-            <div className="flex items-center gap-2">
-              <Switch
-                isChecked={layersVisibilityForCurrentModel[layer.details.id]}
-                onToggle={value =>
-                  dispatch(
-                    setLayerVisibility({
-                      modelId: currentModelId,
-                      visible: value,
-                      layerId: layer.details.id,
-                    }),
-                  )
-                }
-              />
-              <Button onClick={() => editLayer(layer.details.id)}>Edit</Button>
-              <Button
-                onClick={() => onRemoveLayer(layer.details.id)}
-                color="error"
-                variantStyles="remove"
-              >
-                Remove
-              </Button>
-            </div>
+            <LayersDrawerLayerActions
+              isChecked={layersVisibilityForCurrentModel[layer.details.id]}
+              editLayer={() => editLayer(layer.details.id)}
+              removeLayer={() => onRemoveLayer(layer.details.id)}
+              toggleVisibility={value =>
+                dispatch(
+                  setLayerVisibility({
+                    modelId: currentModelId,
+                    visible: value,
+                    layerId: layer.details.id,
+                  }),
+                )
+              }
+            />
           </div>
         ))}
       </div>
diff --git a/src/components/Map/Drawer/LayersDrawer/LayersDrawerLayerActions.component.tsx b/src/components/Map/Drawer/LayersDrawer/LayersDrawerLayerActions.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..02ea7ed9246cde4aa02b163288a561fedd8b4a5e
--- /dev/null
+++ b/src/components/Map/Drawer/LayersDrawer/LayersDrawerLayerActions.component.tsx
@@ -0,0 +1,26 @@
+import { Button } from '@/shared/Button';
+import { Switch } from '@/shared/Switch';
+
+type LayersDrawerLayerActionsProps = {
+  editLayer: () => void;
+  removeLayer: () => void;
+  isChecked: boolean;
+  toggleVisibility: (value: boolean) => void;
+};
+
+export const LayersDrawerLayerActions = ({
+  editLayer,
+  removeLayer,
+  isChecked,
+  toggleVisibility,
+}: LayersDrawerLayerActionsProps): JSX.Element => {
+  return (
+    <div className="flex items-center gap-2">
+      <Switch isChecked={isChecked} onToggle={value => toggleVisibility(value)} />
+      <Button onClick={() => editLayer()}>Edit</Button>
+      <Button onClick={() => removeLayer()} color="error" variantStyles="remove">
+        Remove
+      </Button>
+    </div>
+  );
+};
diff --git a/src/components/Map/Map.component.tsx b/src/components/Map/Map.component.tsx
index 02ee36d85777fc55ef92157dfe21deb5ea324593..0a57b1fb832379e00a7edaecf18749cf386b60e8 100644
--- a/src/components/Map/Map.component.tsx
+++ b/src/components/Map/Map.component.tsx
@@ -4,15 +4,20 @@ import { Legend } from '@/components/Map/Legend';
 import { MapViewer } from '@/components/Map/MapViewer';
 import { MapLoader } from '@/components/Map/MapLoader/MapLoader.component';
 import { MapVectorBackgroundSelector } from '@/components/Map/MapVectorBackgroundSelector/MapVectorBackgroundSelector.component';
+import { MapActiveLayerSelector } from '@/components/Map/MapActiveLayerSelector/MapActiveLayerSelector.component';
 import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { vectorRenderingSelector } from '@/redux/models/models.selectors';
 import { MapAdditionalLogos } from '@/components/Map/MapAdditionalLogos';
+import { MapDrawActions } from '@/components/Map/MapDrawActions/MapDrawActions.component';
+import { layersActiveLayerSelector } from '@/redux/layers/layers.selectors';
 import { MapAdditionalActions } from './MapAdditionalActions';
 import { MapAdditionalOptions } from './MapAdditionalOptions';
 import { PluginsDrawer } from './PluginsDrawer';
 
 export const Map = (): JSX.Element => {
   const vectorRendering = useAppSelector(vectorRenderingSelector);
+  const activeLayer = useAppSelector(layersActiveLayerSelector);
+
   return (
     <div
       className="relative z-0 h-screen w-full overflow-hidden bg-black"
@@ -20,7 +25,13 @@ export const Map = (): JSX.Element => {
     >
       <MapViewer />
       {!vectorRendering && <MapAdditionalOptions />}
-      {vectorRendering && <MapVectorBackgroundSelector />}
+      {vectorRendering && (
+        <>
+          <MapVectorBackgroundSelector />
+          <MapActiveLayerSelector />
+          {activeLayer && <MapDrawActions />}
+        </>
+      )}
       <Drawer />
       <PluginsDrawer />
       <Legend />
diff --git a/src/components/Map/MapActiveLayerSelector/MapActiveLayerSelector.component.tsx b/src/components/Map/MapActiveLayerSelector/MapActiveLayerSelector.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..10bde5c3f958375d77ae5a763d767d5c3a80dd52
--- /dev/null
+++ b/src/components/Map/MapActiveLayerSelector/MapActiveLayerSelector.component.tsx
@@ -0,0 +1,64 @@
+/* eslint-disable no-magic-numbers */
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { twMerge } from 'tailwind-merge';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { Select } from '@/shared/Select';
+import {
+  layersActiveLayerSelector,
+  layersForCurrentModelSelector,
+  layersVisibilityForCurrentModelSelector,
+} from '@/redux/layers/layers.selectors';
+import { useEffect, useMemo } from 'react';
+import { setActiveLayer } from '@/redux/layers/layers.slice';
+import { currentModelIdSelector } from '@/redux/models/models.selectors';
+import { mapEditToolsSetActiveAction } from '@/redux/mapEditTools/mapEditTools.slice';
+
+export const MapActiveLayerSelector = (): JSX.Element => {
+  const dispatch = useAppDispatch();
+  const layers = useAppSelector(layersForCurrentModelSelector);
+  const layersVisibility = useAppSelector(layersVisibilityForCurrentModelSelector);
+  const currentModelId = useAppSelector(currentModelIdSelector);
+  const activeLayer = useAppSelector(layersActiveLayerSelector);
+
+  const handleChange = (activeLayerId: number): void => {
+    dispatch(setActiveLayer({ modelId: currentModelId, layerId: activeLayerId }));
+  };
+
+  const options: Array<{ id: number; name: string }> = useMemo(() => {
+    return layers
+      .filter(layer => layersVisibility[layer.details.id])
+      .map(layer => {
+        return {
+          id: layer.details.id,
+          name: layer.details.name,
+        };
+      });
+  }, [layers, layersVisibility]);
+
+  useEffect(() => {
+    const selectedOption = options.find(option => option.id === activeLayer) || null;
+    if (selectedOption || !currentModelId) {
+      return;
+    }
+    if (options.length === 0) {
+      dispatch(setActiveLayer({ modelId: currentModelId, layerId: null }));
+    } else {
+      dispatch(setActiveLayer({ modelId: currentModelId, layerId: options[0].id }));
+    }
+  }, [activeLayer, currentModelId, dispatch, options]);
+
+  useEffect(() => {
+    if (!options.length) {
+      dispatch(setActiveLayer({ modelId: currentModelId, layerId: null }));
+      dispatch(mapEditToolsSetActiveAction(null));
+    }
+  }, [currentModelId, dispatch, options]);
+
+  return (
+    <div className={twMerge('absolute right-6 top-[calc(64px+40px+84px)] flex')}>
+      {Boolean(options.length) && (
+        <Select options={options} selectedId={activeLayer} onChange={handleChange} width={140} />
+      )}
+    </div>
+  );
+};
diff --git a/src/components/Map/MapDrawActions/MapDrawActions.component.test.tsx b/src/components/Map/MapDrawActions/MapDrawActions.component.test.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..d8f04b15f044ded805623060e372748b70f76ed1
--- /dev/null
+++ b/src/components/Map/MapDrawActions/MapDrawActions.component.test.tsx
@@ -0,0 +1,77 @@
+import { MapDrawActions } from '@/components/Map/MapDrawActions/MapDrawActions.component';
+import { fireEvent, render, screen } from '@testing-library/react';
+import { MAP_EDIT_ACTIONS } from '@/redux/mapEditTools/mapEditTools.constants';
+import { mapEditToolsSetActiveAction } from '@/redux/mapEditTools/mapEditTools.slice';
+import {
+  getReduxWrapperWithStore,
+  InitialStoreState,
+} from '@/utils/testing/getReduxWrapperWithStore';
+import { StoreType } from '@/redux/store';
+import { MAP_EDIT_TOOLS_STATE_INITIAL_MOCK } from '@/redux/mapEditTools/mapEditTools.mock';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import {
+  LAYER_STATE_DEFAULT_DATA,
+  LAYERS_STATE_INITIAL_LAYER_MOCK,
+} from '@/redux/layers/layers.mock';
+import { MAIN_MAP_ID } from '@/constants/mocks';
+import { layerFixture } from '@/models/fixtures/layerFixture';
+
+jest.mock('../../../redux/hooks/useAppDispatch', () => ({
+  useAppDispatch: jest.fn(),
+}));
+
+const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
+  const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
+
+  return (
+    render(
+      <Wrapper>
+        <MapDrawActions />
+      </Wrapper>,
+    ),
+    {
+      store,
+    }
+  );
+};
+
+describe('MapDrawActions', () => {
+  const mockDispatch = jest.fn(() => {});
+
+  beforeEach(() => {
+    (useAppDispatch as jest.Mock).mockReturnValue(mockDispatch);
+  });
+
+  it('renders the MapDrawActionsButton and toggles action on click', () => {
+    const layerId = 0;
+    renderComponent({
+      mapEditTools: MAP_EDIT_TOOLS_STATE_INITIAL_MOCK,
+      layers: {
+        [layerId]: {
+          ...LAYERS_STATE_INITIAL_LAYER_MOCK,
+          data: {
+            ...LAYER_STATE_DEFAULT_DATA,
+            layersVisibility: { [MAIN_MAP_ID]: true },
+            layers: [
+              {
+                details: { ...layerFixture, id: MAIN_MAP_ID },
+                texts: [],
+                rects: [],
+                ovals: [],
+                lines: [],
+                images: {},
+              },
+            ],
+          },
+        },
+      },
+    });
+    const button = screen.getByRole('button', { name: /draw image/i });
+    expect(button).toBeInTheDocument();
+    fireEvent.click(button);
+
+    expect(mockDispatch).toHaveBeenCalledWith(
+      mapEditToolsSetActiveAction(MAP_EDIT_ACTIONS.DRAW_IMAGE),
+    );
+  });
+});
diff --git a/src/components/Map/MapDrawActions/MapDrawActions.component.tsx b/src/components/Map/MapDrawActions/MapDrawActions.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..50deda14baae2d6318e793162819e7c106472dc6
--- /dev/null
+++ b/src/components/Map/MapDrawActions/MapDrawActions.component.tsx
@@ -0,0 +1,42 @@
+/* eslint-disable no-magic-numbers */
+import { MAP_EDIT_ACTIONS } from '@/redux/mapEditTools/mapEditTools.constants';
+import { mapEditToolsSetActiveAction } from '@/redux/mapEditTools/mapEditTools.slice';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { mapEditToolsActiveActionSelector } from '@/redux/mapEditTools/mapEditTools.selectors';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { MapDrawActionsButton } from '@/components/Map/MapDrawActions/MapDrawActionsButton.component';
+import { useMemo } from 'react';
+import {
+  layersForCurrentModelSelector,
+  layersVisibilityForCurrentModelSelector,
+} from '@/redux/layers/layers.selectors';
+
+export const MapDrawActions = (): React.JSX.Element | null => {
+  const activeAction = useAppSelector(mapEditToolsActiveActionSelector);
+  const dispatch = useAppDispatch();
+  const layers = useAppSelector(layersForCurrentModelSelector);
+  const layersVisibility = useAppSelector(layersVisibilityForCurrentModelSelector);
+
+  const toggleMapEditAction = (action: keyof typeof MAP_EDIT_ACTIONS): void => {
+    dispatch(mapEditToolsSetActiveAction(action));
+  };
+
+  const visibleLayersLength: number = useMemo(() => {
+    return layers.filter(layer => layersVisibility[layer.details.id]).length;
+  }, [layers, layersVisibility]);
+
+  if (visibleLayersLength === 0) {
+    return null;
+  }
+
+  return (
+    <div className="absolute right-6 top-[calc(64px+40px+144px)] z-10 flex flex-col gap-4">
+      <MapDrawActionsButton
+        isActive={activeAction === MAP_EDIT_ACTIONS.DRAW_IMAGE}
+        toggleMapEditAction={() => toggleMapEditAction(MAP_EDIT_ACTIONS.DRAW_IMAGE)}
+        icon="image"
+        title="Draw image"
+      />
+    </div>
+  );
+};
diff --git a/src/components/Map/MapDrawActions/MapDrawActionsButton.component.tsx b/src/components/Map/MapDrawActions/MapDrawActionsButton.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..eacefa311d4aa5be906188da02218b1341f7b011
--- /dev/null
+++ b/src/components/Map/MapDrawActions/MapDrawActionsButton.component.tsx
@@ -0,0 +1,33 @@
+/* eslint-disable no-magic-numbers */
+import { Icon } from '@/shared/Icon';
+import type { IconTypes } from '@/types/iconTypes';
+
+type MapDrawActionsButtonProps = {
+  isActive: boolean;
+  toggleMapEditAction: () => void;
+  icon: IconTypes;
+  title?: string;
+};
+
+export const MapDrawActionsButton = ({
+  isActive,
+  toggleMapEditAction,
+  icon,
+  title = '',
+}: MapDrawActionsButtonProps): React.JSX.Element => {
+  return (
+    <button
+      type="button"
+      className={`flex h-12 w-12 items-center justify-center rounded-full ${
+        isActive ? 'bg-primary-100' : 'bg-white drop-shadow-primary'
+      }`}
+      onClick={() => toggleMapEditAction()}
+      title={title}
+    >
+      <Icon
+        className={`h-[28px] w-[28px] ${isActive ? 'text-primary-500' : 'text-black'}`}
+        name={icon}
+      />
+    </button>
+  );
+};
diff --git a/src/components/Map/MapVectorBackgroundSelector/MapVectorBackgroundSelector.component.tsx b/src/components/Map/MapVectorBackgroundSelector/MapVectorBackgroundSelector.component.tsx
index 32af5af47762600163e1069fc76cb9b2ec892880..c4e2cafa626e4799c0e0dab6fef9721ec18c5d14 100644
--- a/src/components/Map/MapVectorBackgroundSelector/MapVectorBackgroundSelector.component.tsx
+++ b/src/components/Map/MapVectorBackgroundSelector/MapVectorBackgroundSelector.component.tsx
@@ -20,7 +20,7 @@ export const MapVectorBackgroundSelector = (): JSX.Element => {
         options={MAP_BACKGROUND_TYPES}
         selectedId={backgroundType}
         onChange={handleChange}
-        width={100}
+        width={140}
       />
     </div>
   );
diff --git a/src/components/Map/MapViewer/MapViewer.component.tsx b/src/components/Map/MapViewer/MapViewer.component.tsx
index 662384c9bd6b215ea3277fd94c0568eac7dc081d..0307cf080469c999aa576b3b7710fed9c1fffc67 100644
--- a/src/components/Map/MapViewer/MapViewer.component.tsx
+++ b/src/components/Map/MapViewer/MapViewer.component.tsx
@@ -1,15 +1,22 @@
 import 'ol/ol.css';
-import { MAP_VIEWER_ROLE } from './MapViewer.constants';
+import { twMerge } from 'tailwind-merge';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { isMapEditToolsActiveSelector } from '@/redux/mapEditTools/mapEditTools.selectors';
 import { useOlMap } from './utils/useOlMap';
+import { MAP_VIEWER_ROLE } from './MapViewer.constants';
 
 export const MapViewer = (): JSX.Element => {
   const { mapRef } = useOlMap();
+  const isMapEditToolsActive = useAppSelector(isMapEditToolsActiveSelector);
 
   return (
     <div
       ref={mapRef}
       role={MAP_VIEWER_ROLE}
-      className="absolute left-[88px] top-[104px] h-[calc(100%-104px)] w-[calc(100%-88px)] bg-white"
+      className={twMerge(
+        'absolute left-[88px] top-[104px] h-[calc(100%-104px)] w-[calc(100%-88px)] bg-white',
+        isMapEditToolsActive ? 'bg-[#e4e2de]' : 'bg-white',
+      )}
     />
   );
 };
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/additionalLayers/useOlMapAdditionalLayers.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/additionalLayers/useOlMapAdditionalLayers.ts
index 6b841f53d586921bb3644aa1bd60caf6755dd8d0..6f60435b25a935e5b0e3a5adf4968a5b01a1a2fe 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/config/additionalLayers/useOlMapAdditionalLayers.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/additionalLayers/useOlMapAdditionalLayers.ts
@@ -2,12 +2,13 @@
 import { Feature } from 'ol';
 import VectorLayer from 'ol/layer/Vector';
 import VectorSource from 'ol/source/Vector';
-import { useEffect, useMemo } from 'react';
+import { useEffect, useMemo, useState } from 'react';
 import { useSelector } from 'react-redux';
-import { currentModelIdSelector } from '@/redux/models/models.selectors';
+import { currentModelIdSelector, vectorRenderingSelector } from '@/redux/models/models.selectors';
 import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
 import { getLayersForModel } from '@/redux/layers/layers.thunks';
 import {
+  layersActiveLayerSelector,
   layersForCurrentModelSelector,
   layersLoadingSelector,
   layersVisibilityForCurrentModelSelector,
@@ -19,6 +20,12 @@ import Polygon from 'ol/geom/Polygon';
 import Layer from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer';
 import { arrowTypesSelector, lineTypesSelector } from '@/redux/shapes/shapes.selectors';
 import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { mapDataSizeSelector } from '@/redux/map/map.selectors';
+import getDrawImageInteraction from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/getDrawImageInteraction';
+import { LayerState } from '@/redux/layers/layers.types';
+import { mapEditToolsActiveActionSelector } from '@/redux/mapEditTools/mapEditTools.selectors';
+import { MAP_EDIT_ACTIONS } from '@/redux/mapEditTools/mapEditTools.constants';
+import { Extent } from 'ol/extent';
 
 export const useOlMapAdditionalLayers = (
   mapInstance: MapInstance,
@@ -27,17 +34,42 @@ export const useOlMapAdditionalLayers = (
     VectorSource<Feature<Point> | Feature<Polygon> | Feature<LineString> | Feature<MultiPolygon>>
   >
 > => {
+  const activeAction = useAppSelector(mapEditToolsActiveActionSelector);
   const dispatch = useAppDispatch();
+  const mapSize = useSelector(mapDataSizeSelector);
   const currentModelId = useSelector(currentModelIdSelector);
 
   const layersForCurrentModel = useAppSelector(layersForCurrentModelSelector);
   const layersLoading = useAppSelector(layersLoadingSelector);
   const layersVisibilityForCurrentModel = useAppSelector(layersVisibilityForCurrentModelSelector);
+  const activeLayer = useAppSelector(layersActiveLayerSelector);
+  const vectorRendering = useAppSelector(vectorRenderingSelector);
+
+  const [layersState, setLayersState] = useState<Array<LayerState>>([]);
+  const [layersLoadingState, setLayersLoadingState] = useState(false);
 
   const lineTypes = useSelector(lineTypesSelector);
   const arrowTypes = useSelector(arrowTypesSelector);
   const pointToProjection = usePointToProjection();
 
+  const restrictionExtent: Extent = useMemo(() => {
+    const restrictionMinPoint = pointToProjection({ x: 0, y: 0 });
+    const restrictionMaxPoint = pointToProjection({ x: mapSize.width, y: mapSize.height });
+    return [
+      restrictionMinPoint[0],
+      restrictionMaxPoint[1],
+      restrictionMaxPoint[0],
+      restrictionMinPoint[1],
+    ];
+  }, [mapSize, pointToProjection]);
+
+  const drawImageInteraction = useMemo(() => {
+    if (!mapSize || !dispatch) {
+      return null;
+    }
+    return getDrawImageInteraction(mapSize, dispatch, restrictionExtent);
+  }, [mapSize, dispatch, restrictionExtent]);
+
   useEffect(() => {
     if (!currentModelId) {
       return;
@@ -48,7 +80,7 @@ export const useOlMapAdditionalLayers = (
   }, [currentModelId, dispatch, layersLoading]);
 
   const vectorLayers = useMemo(() => {
-    return layersForCurrentModel.map(layer => {
+    return layersState.map(layer => {
       const additionalLayer = new Layer({
         texts: layer.texts,
         rects: layer.rects,
@@ -64,7 +96,16 @@ export const useOlMapAdditionalLayers = (
       });
       return additionalLayer.vectorLayer;
     });
-  }, [arrowTypes, lineTypes, mapInstance, layersForCurrentModel, pointToProjection]);
+  }, [layersState, lineTypes, arrowTypes, mapInstance, pointToProjection]);
+
+  useEffect(() => {
+    if (layersLoading === 'pending') {
+      setLayersLoadingState(true);
+    } else if (layersLoading === 'succeeded' && layersLoadingState) {
+      setLayersLoadingState(false);
+      setLayersState(layersForCurrentModel);
+    }
+  }, [layersForCurrentModel, layersLoading, layersLoadingState]);
 
   useEffect(() => {
     vectorLayers.forEach(layer => {
@@ -75,5 +116,23 @@ export const useOlMapAdditionalLayers = (
     });
   }, [layersVisibilityForCurrentModel, vectorLayers]);
 
+  useEffect(() => {
+    if (!drawImageInteraction) {
+      return;
+    }
+    mapInstance?.removeInteraction(drawImageInteraction);
+    if (!activeLayer || !vectorRendering || activeAction !== MAP_EDIT_ACTIONS.DRAW_IMAGE) {
+      return;
+    }
+    mapInstance?.addInteraction(drawImageInteraction);
+  }, [
+    activeAction,
+    activeLayer,
+    currentModelId,
+    drawImageInteraction,
+    mapInstance,
+    vectorRendering,
+  ]);
+
   return vectorLayers;
 };
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/mapCardLayer/useOlMapCardLayer.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/mapCardLayer/useOlMapCardLayer.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dd9a522058ac4677e9bd8d70767cd4d4edd85879
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/mapCardLayer/useOlMapCardLayer.test.ts
@@ -0,0 +1,79 @@
+/* eslint-disable no-magic-numbers */
+import { MAP_DATA_INITIAL_STATE, OPENED_MAPS_INITIAL_STATE } from '@/redux/map/map.constants';
+import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
+import { renderHook } from '@testing-library/react';
+import BaseLayer from 'ol/layer/Base';
+import VectorLayer from 'ol/layer/Vector';
+import React from 'react';
+import MapBackgroundsEnum from '@/redux/map/map.enums';
+import { useOlMapCardLayer } from './useOlMapCardLayer';
+
+const useRefValue = {
+  current: null,
+};
+
+Object.defineProperty(useRefValue, 'current', {
+  get: jest.fn(() => ({
+    innerHTML: '',
+    appendChild: jest.fn(),
+    addEventListener: jest.fn(),
+    getRootNode: jest.fn(),
+  })),
+  set: jest.fn(() => ({
+    innerHTML: '',
+    appendChild: jest.fn(),
+    addEventListener: jest.fn(),
+    getRootNode: jest.fn(),
+  })),
+});
+
+jest.spyOn(React, 'useRef').mockReturnValue(useRefValue);
+
+describe('useOlMapCardLayer - util', () => {
+  const getRenderedHookResults = (): BaseLayer => {
+    const { Wrapper } = getReduxWrapperWithStore({
+      map: {
+        data: {
+          ...MAP_DATA_INITIAL_STATE,
+          size: {
+            width: 256,
+            height: 256,
+            tileSize: 256,
+            minZoom: 1,
+            maxZoom: 1,
+          },
+          position: {
+            initial: {
+              x: 256,
+              y: 256,
+            },
+            last: {
+              x: 256,
+              y: 256,
+            },
+          },
+        },
+        loading: 'idle',
+        error: {
+          name: '',
+          message: '',
+        },
+        openedMaps: OPENED_MAPS_INITIAL_STATE,
+        backgroundType: MapBackgroundsEnum.SEMANTIC,
+      },
+    });
+
+    const { result } = renderHook(() => useOlMapCardLayer(), {
+      wrapper: Wrapper,
+    });
+
+    return result.current;
+  };
+
+  it('should return valid VectorLayer instance', () => {
+    const result = getRenderedHookResults();
+
+    expect(result).toBeInstanceOf(VectorLayer);
+    expect(result.getSourceState()).toBe('ready');
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/mapCardLayer/useOlMapCardLayer.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/mapCardLayer/useOlMapCardLayer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e1fb741ba7f9c9f103c192d133cb706b00fb0315
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/mapCardLayer/useOlMapCardLayer.ts
@@ -0,0 +1,50 @@
+import { Feature } from 'ol';
+import VectorLayer from 'ol/layer/Vector';
+import VectorSource from 'ol/source/Vector';
+import { useMemo } from 'react';
+import Polygon from 'ol/geom/Polygon';
+import { useSelector } from 'react-redux';
+import { mapDataSizeSelector } from '@/redux/map/map.selectors';
+import { usePointToProjection } from '@/utils/map/usePointToProjection';
+import Style from 'ol/style/Style';
+import { Fill } from 'ol/style';
+
+export const useOlMapCardLayer = (): VectorLayer<VectorSource<Feature<Polygon>>> => {
+  const mapSize = useSelector(mapDataSizeSelector);
+  const pointToProjection = usePointToProjection();
+
+  const rectangle = useMemo(() => {
+    return new Polygon([
+      [
+        pointToProjection({ x: 0, y: 0 }),
+        pointToProjection({ x: mapSize.width, y: 0 }),
+        pointToProjection({ x: mapSize.width, y: mapSize.height }),
+        pointToProjection({ x: 0, y: mapSize.height }),
+        pointToProjection({ x: 0, y: 0 }),
+      ],
+    ]);
+  }, [mapSize.height, mapSize.width, pointToProjection]);
+
+  const rectangleFeature = useMemo(() => {
+    return new Feature(rectangle);
+  }, [rectangle]);
+
+  const vectorSource = useMemo(() => {
+    return new VectorSource({
+      features: [rectangleFeature],
+    });
+  }, [rectangleFeature]);
+
+  return useMemo(
+    () =>
+      new VectorLayer({
+        source: vectorSource,
+        style: new Style({
+          fill: new Fill({
+            color: '#fff',
+          }),
+        }),
+      }),
+    [vectorSource],
+  );
+};
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.ts
index 822b54ddd79e39d5d43c58f19464c070f8d6222d..253c3a38954dac9549948a3c3e5f7536fc9399e6 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.ts
@@ -2,6 +2,7 @@
 import { MapInstance } from '@/types/map';
 import { useOlMapAdditionalLayers } from '@/components/Map/MapViewer/MapViewerVector/utils/config/additionalLayers/useOlMapAdditionalLayers';
 import { useMemo } from 'react';
+import { useOlMapCardLayer } from '@/components/Map/MapViewer/MapViewerVector/utils/config/mapCardLayer/useOlMapCardLayer';
 import { useOlMapReactionsLayer } from './reactionsLayer/useOlMapReactionsLayer';
 import { MapConfig } from '../../MapViewerVector.types';
 
@@ -12,8 +13,9 @@ interface UseOlMapLayersInput {
 export const useOlMapVectorLayers = ({ mapInstance }: UseOlMapLayersInput): MapConfig['layers'] => {
   const reactionsLayer = useOlMapReactionsLayer({ mapInstance });
   const additionalLayers = useOlMapAdditionalLayers(mapInstance);
+  const mapCardLayer = useOlMapCardLayer();
 
   return useMemo(() => {
-    return [reactionsLayer, ...additionalLayers];
-  }, [reactionsLayer, additionalLayers]);
+    return [mapCardLayer, reactionsLayer, ...additionalLayers];
+  }, [mapCardLayer, reactionsLayer, additionalLayers]);
 };
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph.test.ts
index f2fc4d4d3422efc7fae549446118bc37c8da47da..6ac8c5d29e0e6aa78f4b7edb05e1a2f61a626b25 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph.test.ts
@@ -59,13 +59,8 @@ describe('Glyph', () => {
   });
 
   it('should scale image based on map resolution', () => {
-    const getImageScale = glyph.feature.get('getImageScale');
     const getAnchorAndCoords = glyph.feature.get('getAnchorAndCoords');
     if (mapInstance) {
-      const resolution = mapInstance
-        .getView()
-        .getResolutionForZoom(mapInstance.getView().getMaxZoom());
-      expect(getImageScale(resolution)).toBe(1);
       expect(getAnchorAndCoords()).toEqual({ anchor: [0, 0], coords: [0, 0] });
     }
   });
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph.ts
index 41cc21a6bca428fb2c11b8c58e08b2bc8dc90796..c7844ae36e3acae71caa5accfab855a3e1d06b40 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph.ts
@@ -1,7 +1,7 @@
 /* eslint-disable no-magic-numbers */
 import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
 import { Feature } from 'ol';
-import Style from 'ol/style/Style';
+import { Style, Text } from 'ol/style';
 import Icon from 'ol/style/Icon';
 import { FeatureLike } from 'ol/Feature';
 import { MapInstance } from '@/types/map';
@@ -13,10 +13,12 @@ import { Coordinate } from 'ol/coordinate';
 import { FEATURE_TYPE } from '@/constants/features';
 import getStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle';
 import { WHITE_COLOR } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill';
+import getScaledElementStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getScaledElementStyle';
 
 export type GlyphProps = {
   elementId: number;
-  glyphId: number;
+  glyphId: number | null;
   x: number;
   y: number;
   width: number;
@@ -31,6 +33,8 @@ export default class Glyph {
 
   style: Style = new Style({});
 
+  noGlyphStyle: Style;
+
   imageScale: number = 1;
 
   polygonStyle: Style;
@@ -49,6 +53,8 @@ export default class Glyph {
 
   pixelRatio: number = 1;
 
+  minResolution: number;
+
   pointToProjection: UsePointToProjectionResult;
 
   constructor({
@@ -71,10 +77,10 @@ export default class Glyph {
     const point2 = this.pointToProjection({ x: this.width, y: this.height });
     this.widthOnMap = Math.abs(point2[0] - point1[0]);
     this.heightOnMap = Math.abs(point2[1] - point1[1]);
-    const minResolution = mapInstance?.getView().getMinResolution();
-    if (minResolution) {
-      this.pixelRatio = this.widthOnMap / minResolution / this.width;
-    }
+
+    const maxZoom = mapInstance?.getView().get('originalMaxZoom');
+    this.minResolution = mapInstance?.getView().getResolutionForZoom(maxZoom) || 1;
+    this.pixelRatio = this.widthOnMap / this.minResolution / this.width;
     const polygon = new Polygon([
       [
         pointToProjection({ x, y }),
@@ -84,23 +90,33 @@ export default class Glyph {
         pointToProjection({ x, y }),
       ],
     ]);
+
     this.polygonStyle = getStyle({
       geometry: polygon,
       zIndex,
       borderColor: { ...WHITE_COLOR, alpha: 0 },
       fillColor: { ...WHITE_COLOR, alpha: 0 },
     });
+
+    this.noGlyphStyle = getStyle({
+      geometry: polygon,
+      zIndex,
+      fillColor: '#E7E7E7',
+    });
+    this.noGlyphStyle.setText(
+      new Text({
+        text: 'No image',
+        font: '12pt Arial',
+        fill: getFill({ color: '#000' }),
+        overflow: true,
+      }),
+    );
+
     this.feature = new Feature({
       geometry: polygon,
       id: elementId,
       type: FEATURE_TYPE.GLYPH,
       zIndex,
-      getImageScale: (resolution: number): number => {
-        if (mapInstance) {
-          return mapInstance.getView().getMinResolution() / resolution;
-        }
-        return 1;
-      },
       getAnchorAndCoords: (): { anchor: Array<number>; coords: Coordinate } => {
         const center = mapInstance?.getView().getCenter();
         let anchorX = 0;
@@ -115,11 +131,23 @@ export default class Glyph {
       },
     });
 
+    this.feature.setStyle(this.getStyle.bind(this));
+    if (!glyphId) {
+      return;
+    }
     const img = new Image();
     img.onload = (): void => {
       const imageWidth = img.naturalWidth;
       const imageHeight = img.naturalHeight;
-      this.imageScale = width / imageWidth;
+      const heightScale = height / imageHeight;
+      const widthScale = width / imageWidth;
+      if (heightScale < widthScale) {
+        this.imageScale = heightScale;
+        this.widthOnMap = (this.heightOnMap * imageWidth) / imageHeight;
+      } else {
+        this.imageScale = widthScale;
+        this.heightOnMap = (this.widthOnMap * imageHeight) / imageWidth;
+      }
       this.style = new Style({
         image: new Icon({
           anchor: [0, 0],
@@ -128,30 +156,27 @@ export default class Glyph {
         }),
         zIndex,
       });
-      this.feature.setStyle(this.getStyle.bind(this));
     };
     img.src = `${BASE_NEW_API_URL}${apiPath.getGlyphImage(glyphId)}`;
   }
 
   protected getStyle(feature: FeatureLike, resolution: number): Style | Array<Style> | void {
-    const getImageScale = feature.get('getImageScale');
+    const scale = this.minResolution / resolution;
     const getAnchorAndCoords = feature.get('getAnchorAndCoords');
-    let imageScale = 1;
     let anchor = [0, 0];
     let coords = this.pointToProjection({ x: this.x, y: this.y });
-    if (getImageScale instanceof Function) {
-      imageScale = getImageScale(resolution);
-    }
+
     if (getAnchorAndCoords instanceof Function) {
       const anchorAndCoords = getAnchorAndCoords();
       anchor = anchorAndCoords.anchor;
       coords = anchorAndCoords.coords;
     }
     if (this.style.getImage()) {
-      this.style.getImage()?.setScale(imageScale * this.pixelRatio * this.imageScale);
+      this.style.getImage()?.setScale(scale * this.pixelRatio * this.imageScale);
       (this.style.getImage() as Icon).setAnchor(anchor);
       this.style.setGeometry(new Point(coords));
+      return [this.style, this.polygonStyle];
     }
-    return [this.style, this.polygonStyle];
+    return getScaledElementStyle(this.noGlyphStyle, undefined, scale);
   }
 }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.test.ts
index eeabb89c506ce0c4ffba0a0caebab0a7c1bbea95..1cdd1aef0edf1bfae3d0b785eb3080420c2e3fe9 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.test.ts
@@ -113,8 +113,8 @@ describe('Layer', () => {
           lineType: 'SOLID',
         },
       ],
-      images: [
-        {
+      images: {
+        1: {
           id: 1,
           glyph: 1,
           x: 1,
@@ -123,7 +123,7 @@ describe('Layer', () => {
           height: 1,
           z: 1,
         },
-      ],
+      },
       visible: true,
       layerId: 23,
       pointToProjection: jest.fn(point => [point.x, point.y]),
@@ -144,9 +144,6 @@ describe('Layer', () => {
   it('should initialize a Layer class', () => {
     const layer = new Layer(props);
 
-    expect(layer.textFeatures.length).toBe(1);
-    expect(layer.rectFeatures.length).toBe(1);
-    expect(layer.ovalFeatures.length).toBe(1);
     expect(layer.vectorSource).toBeInstanceOf(VectorSource);
     expect(layer.vectorLayer).toBeInstanceOf(VectorLayer);
   });
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
index ef12c427e4fecdb40d9ff5929abd162f15da1c2c..e48faf93c38db11b282b23a3a87e253979eee661 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
@@ -33,7 +33,7 @@ export interface LayerProps {
   rects: Array<LayerRect>;
   ovals: Array<LayerOval>;
   lines: Array<LayerLine>;
-  images: Array<LayerImage>;
+  images: { [key: string]: LayerImage };
   visible: boolean;
   layerId: number;
   lineTypes: LineTypeDict;
@@ -51,24 +51,12 @@ export default class Layer {
 
   lines: Array<LayerLine>;
 
-  images: Array<LayerImage>;
+  images: { [key: string]: LayerImage };
 
   lineTypes: LineTypeDict;
 
   arrowTypes: ArrowTypeDict;
 
-  textFeatures: Array<Feature<Point>>;
-
-  rectFeatures: Array<Feature<Polygon>>;
-
-  ovalFeatures: Array<Feature<Polygon>>;
-
-  imageFeatures: Array<Feature<Polygon>>;
-
-  lineFeatures: Array<Feature<LineString>>;
-
-  arrowFeatures: Array<Feature<MultiPolygon>>;
-
   pointToProjection: UsePointToProjectionResult;
 
   mapInstance: MapInstance;
@@ -94,6 +82,8 @@ export default class Layer {
     mapInstance,
     pointToProjection,
   }: LayerProps) {
+    this.vectorSource = new VectorSource({});
+
     this.texts = texts;
     this.rects = rects;
     this.ovals = ovals;
@@ -103,28 +93,25 @@ export default class Layer {
     this.arrowTypes = arrowTypes;
     this.pointToProjection = pointToProjection;
     this.mapInstance = mapInstance;
-    this.textFeatures = this.getTextsFeatures();
-    this.rectFeatures = this.getRectsFeatures();
-    this.ovalFeatures = this.getOvalsFeatures();
-    this.imageFeatures = this.getImagesFeatures();
+
+    this.vectorSource.addFeatures(this.getTextsFeatures());
+    this.vectorSource.addFeatures(this.getRectsFeatures());
+    this.vectorSource.addFeatures(this.getOvalsFeatures());
+    this.drawImages();
+
     const { linesFeatures, arrowsFeatures } = this.getLinesFeatures();
-    this.lineFeatures = linesFeatures;
-    this.arrowFeatures = arrowsFeatures;
-    this.vectorSource = new VectorSource({
-      features: [
-        ...this.textFeatures,
-        ...this.rectFeatures,
-        ...this.ovalFeatures,
-        ...this.lineFeatures,
-        ...this.arrowFeatures,
-        ...this.imageFeatures,
-      ],
-    });
+    this.vectorSource.addFeatures(linesFeatures);
+    this.vectorSource.addFeatures(arrowsFeatures);
+
     this.vectorLayer = new VectorLayer({
       source: this.vectorSource,
       visible,
+      updateWhileAnimating: true,
+      updateWhileInteracting: true,
     });
+
     this.vectorLayer.set('id', layerId);
+    this.vectorLayer.set('drawImage', this.drawImage.bind(this));
   }
 
   private getTextsFeatures = (): Array<Feature<Point>> => {
@@ -303,22 +290,26 @@ export default class Layer {
     return { linesFeatures, arrowsFeatures };
   };
 
-  private getImagesFeatures = (): Array<Feature<Polygon>> => {
-    return this.images.map(image => {
-      const glyph = new Glyph({
-        elementId: image.id,
-        glyphId: image.glyph,
-        x: image.x,
-        y: image.y,
-        width: image.width,
-        height: image.height,
-        zIndex: image.z,
-        pointToProjection: this.pointToProjection,
-        mapInstance: this.mapInstance,
-      });
-      return glyph.feature;
+  private drawImages(): void {
+    Object.values(this.images).forEach(image => {
+      this.drawImage(image);
     });
-  };
+  }
+
+  private drawImage(image: LayerImage): void {
+    const glyph = new Glyph({
+      elementId: image.id,
+      glyphId: image.glyph,
+      x: image.x,
+      y: image.y,
+      width: image.width,
+      height: image.height,
+      zIndex: image.z,
+      pointToProjection: this.pointToProjection,
+      mapInstance: this.mapInstance,
+    });
+    this.vectorSource.addFeature(glyph.feature);
+  }
 
   protected getStyle(feature: FeatureLike, resolution: number): Style | Array<Style> | void {
     const styles: Array<Style> = [];
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/getDrawImageInteraction.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/getDrawImageInteraction.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e0c9fb448df9c02b9c29aa36c12206be8c439216
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/getDrawImageInteraction.test.ts
@@ -0,0 +1,66 @@
+/* eslint-disable no-magic-numbers */
+import Draw from 'ol/interaction/Draw';
+import { latLngToPoint } from '@/utils/map/latLngToPoint';
+import modalReducer, { openLayerImageObjectFactoryModal } from '@/redux/modal/modal.slice';
+import { MapSize } from '@/redux/map/map.types';
+import {
+  createStoreInstanceUsingSliceReducer,
+  ToolkitStoreWithSingleSlice,
+} from '@/utils/createStoreInstanceUsingSliceReducer';
+import { ModalState } from '@/redux/modal/modal.types';
+import { DEFAULT_TILE_SIZE } from '@/constants/map';
+import { Map } from 'ol';
+import getDrawImageInteraction from './getDrawImageInteraction';
+
+jest.mock('../../../../../../../utils/map/latLngToPoint', () => ({
+  latLngToPoint: jest.fn(latLng => ({ x: latLng[0], y: latLng[1] })),
+}));
+
+describe('getDrawImageInteraction', () => {
+  let store = {} as ToolkitStoreWithSingleSlice<ModalState>;
+  const mockDispatch = jest.fn(() => {});
+
+  let mapSize: MapSize;
+
+  beforeEach(() => {
+    mapSize = {
+      width: 800,
+      height: 600,
+      minZoom: 1,
+      maxZoom: 9,
+      tileSize: DEFAULT_TILE_SIZE,
+    };
+    store = createStoreInstanceUsingSliceReducer('modal', modalReducer);
+    store.dispatch = mockDispatch;
+  });
+
+  it('returns a Draw interaction', () => {
+    const drawInteraction = getDrawImageInteraction(mapSize, store.dispatch, [0, 0, 10000, 10000]);
+    expect(drawInteraction).toBeInstanceOf(Draw);
+  });
+
+  it('dispatches openLayerImageObjectFactoryModal on drawend', () => {
+    const dummyElement = document.createElement('div');
+    const mapInstance = new Map({ target: dummyElement });
+    const drawInteraction = getDrawImageInteraction(mapSize, store.dispatch, [0, 0, 10000, 10000]);
+    mapInstance.addInteraction(drawInteraction);
+    drawInteraction.appendCoordinates([
+      [0, 0],
+      [10, 10],
+    ]);
+
+    drawInteraction.finishDrawing();
+
+    expect(latLngToPoint).toHaveBeenCalledTimes(4);
+    expect(store.dispatch).toHaveBeenCalledWith(
+      openLayerImageObjectFactoryModal(
+        expect.objectContaining({
+          x: expect.any(Number),
+          y: expect.any(Number),
+          width: expect.any(Number),
+          height: expect.any(Number),
+        }),
+      ),
+    );
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/getDrawImageInteraction.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/getDrawImageInteraction.ts
new file mode 100644
index 0000000000000000000000000000000000000000..55a459bcc74378a7a0e2cd289c27050308a7e4ea
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/getDrawImageInteraction.ts
@@ -0,0 +1,95 @@
+/* eslint-disable no-magic-numbers */
+import Draw from 'ol/interaction/Draw';
+import SimpleGeometry from 'ol/geom/SimpleGeometry';
+import Polygon from 'ol/geom/Polygon';
+import { toLonLat } from 'ol/proj';
+import { latLngToPoint } from '@/utils/map/latLngToPoint';
+import { MapSize } from '@/redux/map/map.types';
+import { AppDispatch } from '@/redux/store';
+import { Coordinate } from 'ol/coordinate';
+import { openLayerImageObjectFactoryModal } from '@/redux/modal/modal.slice';
+import { Extent } from 'ol/extent';
+
+export default function getDrawImageInteraction(
+  mapSize: MapSize,
+  dispatch: AppDispatch,
+  restrictionExtent: Extent,
+): Draw {
+  const drawImageInteraction = new Draw({
+    type: 'Circle',
+    freehand: false,
+    freehandCondition: (mapBrowserEvent): boolean => {
+      const coords = mapBrowserEvent.coordinate;
+      return (
+        coords[0] >= restrictionExtent[0] &&
+        coords[0] <= restrictionExtent[2] &&
+        coords[1] >= restrictionExtent[1] &&
+        coords[1] <= restrictionExtent[3]
+      );
+    },
+    geometryFunction: (coordinates, geometry): SimpleGeometry => {
+      const newGeometry = geometry || new Polygon([]);
+      if (!Array.isArray(coordinates) || coordinates.length < 2) {
+        return geometry;
+      }
+      const start = coordinates[0] as Coordinate;
+      const end = coordinates[1] as Coordinate;
+
+      const minX = Math.min(
+        restrictionExtent[2],
+        Math.max(restrictionExtent[0], Math.min(start[0], end[0])),
+      );
+      const minY = Math.min(
+        restrictionExtent[3],
+        Math.max(restrictionExtent[1], Math.min(start[1], end[1])),
+      );
+      const maxX = Math.max(
+        restrictionExtent[0],
+        Math.min(restrictionExtent[2], Math.max(start[0], end[0])),
+      );
+      const maxY = Math.max(
+        restrictionExtent[1],
+        Math.min(restrictionExtent[3], Math.max(start[1], end[1])),
+      );
+
+      const coords: Array<Coordinate> = [
+        [minX, minY],
+        [maxX, minY],
+        [maxX, maxY],
+        [minX, maxY],
+        [minX, minY],
+      ];
+
+      newGeometry.setCoordinates([coords]);
+
+      return newGeometry;
+    },
+  });
+
+  drawImageInteraction.on('drawend', event => {
+    const geometry = event.feature.getGeometry() as Polygon;
+    const extent = geometry.getExtent();
+
+    const [startLng, startLat] = toLonLat([extent[0], extent[3]]);
+    const startPoint = latLngToPoint([startLat, startLng], mapSize);
+    const [endLng, endLat] = toLonLat([extent[2], extent[1]]);
+    const endPoint = latLngToPoint([endLat, endLng], mapSize);
+
+    const width = Math.abs(endPoint.x - startPoint.x);
+    const height = Math.abs(endPoint.y - startPoint.y);
+
+    if (!width || !height) {
+      return;
+    }
+    dispatch(
+      openLayerImageObjectFactoryModal({
+        x: startPoint.x,
+        y: startPoint.y,
+        width,
+        height,
+      }),
+    );
+  });
+
+  return drawImageInteraction;
+}
diff --git a/src/components/SPA/MinervaSPA.component.tsx b/src/components/SPA/MinervaSPA.component.tsx
index 5762a59b846afa2583c0454ef3fcef59d036fddb..f086919b6aefbb458cd037d06c64459aacad5c8b 100644
--- a/src/components/SPA/MinervaSPA.component.tsx
+++ b/src/components/SPA/MinervaSPA.component.tsx
@@ -6,7 +6,7 @@ import { twMerge } from 'tailwind-merge';
 import { useEffect } from 'react';
 import { PluginsManager } from '@/services/pluginsManager';
 import { useInitializeStore } from '../../utils/initialize/useInitializeStore';
-import { Modal } from '../FunctionalArea/Modal';
+// import { Modal } from '../FunctionalArea/Modal';
 import { ContextMenu } from '../FunctionalArea/ContextMenu';
 import { CookieBanner } from '../FunctionalArea/CookieBanner';
 
@@ -24,7 +24,6 @@ export const MinervaSPA = (): JSX.Element => {
     <div className={twMerge('relative', manrope.variable)}>
       <FunctionalArea />
       <Map />
-      <Modal />
       <ContextMenu />
       <CookieBanner />
     </div>
diff --git a/src/models/fixtures/glyphsFixture.ts b/src/models/fixtures/glyphsFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..489f5015eaac881fe7509252d21ae767cc470328
--- /dev/null
+++ b/src/models/fixtures/glyphsFixture.ts
@@ -0,0 +1,10 @@
+import { ZOD_SEED } from '@/constants';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { createFixture } from 'zod-fixture';
+import { pageableSchema } from '@/models/pageableSchema';
+import { glyphSchema } from '@/models/glyphSchema';
+
+export const glyphsFixture = createFixture(pageableSchema(glyphSchema), {
+  seed: ZOD_SEED,
+  array: { min: 3, max: 3 },
+});
diff --git a/src/models/fixtures/layerImageFixture.ts b/src/models/fixtures/layerImageFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d386d544e29082fcc160df644a1ec24aa5822747
--- /dev/null
+++ b/src/models/fixtures/layerImageFixture.ts
@@ -0,0 +1,9 @@
+import { ZOD_SEED } from '@/constants';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { createFixture } from 'zod-fixture';
+import { layerImageSchema } from '@/models/layerImageSchema';
+
+export const layerImageFixture = createFixture(layerImageSchema, {
+  seed: ZOD_SEED,
+  array: { min: 1, max: 1 },
+});
diff --git a/src/models/fixtures/layerImagesFixture.ts b/src/models/fixtures/layerImagesFixture.ts
index 382b5f926d997699015f8f6c7e3aa080428a8abd..77c2d027fd3505ba97240c53a9c4da8575299b7e 100644
--- a/src/models/fixtures/layerImagesFixture.ts
+++ b/src/models/fixtures/layerImagesFixture.ts
@@ -6,5 +6,5 @@ import { layerImageSchema } from '@/models/layerImageSchema';
 
 export const layerImagesFixture = createFixture(pageableSchema(layerImageSchema), {
   seed: ZOD_SEED,
-  array: { min: 3, max: 3 },
+  array: { min: 1, max: 1 },
 });
diff --git a/src/models/glyphSchema.ts b/src/models/glyphSchema.ts
index eedb213a85e069120b3992a3cc62f271e00dbd59..319fd589fe81d23f621ec0321abde43a570abe6f 100644
--- a/src/models/glyphSchema.ts
+++ b/src/models/glyphSchema.ts
@@ -3,4 +3,5 @@ import { z } from 'zod';
 export const glyphSchema = z.object({
   id: z.number(),
   file: z.number(),
+  filename: z.string().optional().nullable(),
 });
diff --git a/src/models/layerImageSchema.ts b/src/models/layerImageSchema.ts
index 61a6df2dcb417cd9f4b5e389aed50345882ab0b3..8547b313555bdef3d17f894372d4e4ba75ce19d7 100644
--- a/src/models/layerImageSchema.ts
+++ b/src/models/layerImageSchema.ts
@@ -7,5 +7,5 @@ export const layerImageSchema = z.object({
   z: z.number(),
   width: z.number(),
   height: z.number(),
-  glyph: z.number(),
+  glyph: z.number().nullable(),
 });
diff --git a/src/redux/apiPath.ts b/src/redux/apiPath.ts
index 1e9f3a81a520446a85d529b5cde0c6fbe373ddd0..af6950d54d6226d48cbba1b00dd400ba27195294 100644
--- a/src/redux/apiPath.ts
+++ b/src/redux/apiPath.ts
@@ -65,10 +65,14 @@ export const apiPath = {
     `projects/${PROJECT_ID}/maps/${modelId}/layers/${layerId}`,
   removeLayer: (modelId: number, layerId: number): string =>
     `projects/${PROJECT_ID}/maps/${modelId}/layers/${layerId}`,
+  addLayerImageObject: (modelId: number, layerId: number): string =>
+    `projects/${PROJECT_ID}/maps/${modelId}/layers/${layerId}/images/`,
   getLayer: (modelId: number, layerId: number): string =>
     `projects/${PROJECT_ID}/maps/${modelId}/layers/${layerId}`,
   getGlyphImage: (glyphId: number): string =>
     `projects/${PROJECT_ID}/glyphs/${glyphId}/fileContent`,
+  getGlyphs: (): string => `projects/${PROJECT_ID}/glyphs/`,
+  addGlyph: (): string => `projects/${PROJECT_ID}/glyphs/`,
   getNewReactionsForModel: (modelId: number): string =>
     `projects/${PROJECT_ID}/maps/${modelId}/bioEntities/reactions/?size=10000`,
   getNewReaction: (modelId: number, reactionId: number): string =>
diff --git a/src/redux/glyphs/glyphs.constants.ts b/src/redux/glyphs/glyphs.constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e70a9a58ae42bc143b2b7952cc30aefcc862e9ba
--- /dev/null
+++ b/src/redux/glyphs/glyphs.constants.ts
@@ -0,0 +1 @@
+export const GLYPHS_FETCHING_ERROR_PREFIX = 'Failed to fetch glyphs';
diff --git a/src/redux/glyphs/glyphs.mock.ts b/src/redux/glyphs/glyphs.mock.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4e04e5d3b2f05025d6f57daaa01cb00950e081e4
--- /dev/null
+++ b/src/redux/glyphs/glyphs.mock.ts
@@ -0,0 +1,8 @@
+import { DEFAULT_ERROR } from '@/constants/errors';
+import { GlyphsState } from '@/redux/glyphs/glyphs.types';
+
+export const GLYPHS_STATE_INITIAL_MOCK: GlyphsState = {
+  data: [],
+  loading: 'idle',
+  error: DEFAULT_ERROR,
+};
diff --git a/src/redux/glyphs/glyphs.reducers.test.ts b/src/redux/glyphs/glyphs.reducers.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6a4264b57ac57c9f2a0c875ee0430f6b647c88ea
--- /dev/null
+++ b/src/redux/glyphs/glyphs.reducers.test.ts
@@ -0,0 +1,72 @@
+import { apiPath } from '@/redux/apiPath';
+import {
+  ToolkitStoreWithSingleSlice,
+  createStoreInstanceUsingSliceReducer,
+} from '@/utils/createStoreInstanceUsingSliceReducer';
+import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
+import { HttpStatusCode } from 'axios';
+import { unwrapResult } from '@reduxjs/toolkit';
+import { GLYPHS_STATE_INITIAL_MOCK } from '@/redux/glyphs/glyphs.mock';
+import { GlyphsState } from '@/redux/glyphs/glyphs.types';
+import { getGlyphs } from '@/redux/glyphs/glyphs.thunks';
+import { glyphsFixture } from '@/models/fixtures/glyphsFixture';
+import glyphsReducer from './glyphs.slice';
+
+const mockedAxiosClient = mockNetworkNewAPIResponse();
+
+const INITIAL_STATE: GlyphsState = GLYPHS_STATE_INITIAL_MOCK;
+
+describe('glyphs reducer', () => {
+  let store = {} as ToolkitStoreWithSingleSlice<GlyphsState>;
+  beforeEach(() => {
+    store = createStoreInstanceUsingSliceReducer('glyphs', glyphsReducer);
+  });
+
+  it('should match initial state', () => {
+    const action = { type: 'unknown' };
+
+    expect(glyphsReducer(undefined, action)).toEqual(INITIAL_STATE);
+  });
+
+  it('should update store after successful getGlyphs query', async () => {
+    mockedAxiosClient.onGet(apiPath.getGlyphs()).reply(HttpStatusCode.Ok, glyphsFixture);
+
+    const { type } = await store.dispatch(getGlyphs());
+    const { data, loading, error } = store.getState().glyphs;
+    expect(type).toBe('getGlyphs/fulfilled');
+    expect(loading).toEqual('succeeded');
+    expect(error).toEqual({ message: '', name: '' });
+    expect(data).toEqual(glyphsFixture.content);
+  });
+
+  it('should update store after failed getGlyphs query', async () => {
+    mockedAxiosClient.onGet(apiPath.getGlyphs()).reply(HttpStatusCode.NotFound, []);
+
+    const action = await store.dispatch(getGlyphs());
+    const { data, loading, error } = store.getState().glyphs;
+
+    expect(action.type).toBe('getGlyphs/rejected');
+    expect(() => unwrapResult(action)).toThrow(
+      "Failed to fetch glyphs: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
+    expect(loading).toEqual('failed');
+    expect(error).toEqual({ message: '', name: '' });
+    expect(data).toEqual([]);
+  });
+
+  it('should update store on loading getGlyphs query', async () => {
+    mockedAxiosClient.onGet(apiPath.getGlyphs()).reply(HttpStatusCode.Ok, glyphsFixture);
+
+    const glyphsPromise = store.dispatch(getGlyphs());
+
+    const { data, loading } = store.getState().glyphs;
+    expect(data).toEqual([]);
+    expect(loading).toEqual('pending');
+
+    glyphsPromise.then(() => {
+      const { data: dataPromiseFulfilled, loading: promiseFulfilled } = store.getState().glyphs;
+      expect(dataPromiseFulfilled).toEqual(glyphsFixture.content);
+      expect(promiseFulfilled).toEqual('succeeded');
+    });
+  });
+});
diff --git a/src/redux/glyphs/glyphs.reducers.ts b/src/redux/glyphs/glyphs.reducers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..30db814709668c7196cf927ed404b1acab3b3894
--- /dev/null
+++ b/src/redux/glyphs/glyphs.reducers.ts
@@ -0,0 +1,16 @@
+import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
+import { getGlyphs } from '@/redux/glyphs/glyphs.thunks';
+import { GlyphsState } from '@/redux/glyphs/glyphs.types';
+
+export const getGlyphsReducer = (builder: ActionReducerMapBuilder<GlyphsState>): void => {
+  builder.addCase(getGlyphs.pending, state => {
+    state.loading = 'pending';
+  });
+  builder.addCase(getGlyphs.fulfilled, (state, action) => {
+    state.data = action.payload || {};
+    state.loading = 'succeeded';
+  });
+  builder.addCase(getGlyphs.rejected, state => {
+    state.loading = 'failed';
+  });
+};
diff --git a/src/redux/glyphs/glyphs.selectors.ts b/src/redux/glyphs/glyphs.selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6f99b412a61aa37a7d10383424794f63e1b9638e
--- /dev/null
+++ b/src/redux/glyphs/glyphs.selectors.ts
@@ -0,0 +1,6 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { rootSelector } from '@/redux/root/root.selectors';
+
+export const glyphsSelector = createSelector(rootSelector, state => state.glyphs);
+
+export const glyphsDataSelector = createSelector(glyphsSelector, state => state.data);
diff --git a/src/redux/glyphs/glyphs.slice.ts b/src/redux/glyphs/glyphs.slice.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ff81f3e4588883ffe8e9b3506817d92d29ef1f4a
--- /dev/null
+++ b/src/redux/glyphs/glyphs.slice.ts
@@ -0,0 +1,15 @@
+import { createSlice } from '@reduxjs/toolkit';
+
+import { GLYPHS_STATE_INITIAL_MOCK } from '@/redux/glyphs/glyphs.mock';
+import { getGlyphsReducer } from '@/redux/glyphs/glyphs.reducers';
+
+export const glyphsSlice = createSlice({
+  name: 'glyphs',
+  initialState: GLYPHS_STATE_INITIAL_MOCK,
+  reducers: {},
+  extraReducers: builder => {
+    getGlyphsReducer(builder);
+  },
+});
+
+export default glyphsSlice.reducer;
diff --git a/src/redux/glyphs/glyphs.thunks.test.ts b/src/redux/glyphs/glyphs.thunks.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abd3a86d17b0935e507d1643e3a35b7ac5666e75
--- /dev/null
+++ b/src/redux/glyphs/glyphs.thunks.test.ts
@@ -0,0 +1,38 @@
+import { apiPath } from '@/redux/apiPath';
+import {
+  ToolkitStoreWithSingleSlice,
+  createStoreInstanceUsingSliceReducer,
+} from '@/utils/createStoreInstanceUsingSliceReducer';
+import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
+import { HttpStatusCode } from 'axios';
+import { GlyphsState } from '@/redux/glyphs/glyphs.types';
+import { getGlyphs } from '@/redux/glyphs/glyphs.thunks';
+import { glyphsFixture } from '@/models/fixtures/glyphsFixture';
+import glyphsReducer from './glyphs.slice';
+
+const mockedAxiosClient = mockNetworkNewAPIResponse();
+
+describe('glyphs thunks', () => {
+  let store = {} as ToolkitStoreWithSingleSlice<GlyphsState>;
+  beforeEach(() => {
+    store = createStoreInstanceUsingSliceReducer('glyphs', glyphsReducer);
+  });
+
+  describe('getGlyphs', () => {
+    it('should return data when data response from API is valid', async () => {
+      mockedAxiosClient.onGet(apiPath.getGlyphs()).reply(HttpStatusCode.Ok, glyphsFixture);
+
+      const { payload } = await store.dispatch(getGlyphs());
+      expect(payload).toEqual(glyphsFixture.content);
+    });
+
+    it('should return empty object when data response from API is not valid ', async () => {
+      mockedAxiosClient
+        .onGet(apiPath.getGlyphs())
+        .reply(HttpStatusCode.Ok, { randomProperty: 'randomValue' });
+
+      const { payload } = await store.dispatch(getGlyphs());
+      expect(payload).toEqual([]);
+    });
+  });
+});
diff --git a/src/redux/glyphs/glyphs.thunks.ts b/src/redux/glyphs/glyphs.thunks.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3ecfcc05fa465dd6e1646a1ff11beb88f35a4699
--- /dev/null
+++ b/src/redux/glyphs/glyphs.thunks.ts
@@ -0,0 +1,41 @@
+import { apiPath } from '@/redux/apiPath';
+import { Glyph, 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 { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
+import { glyphSchema } from '@/models/glyphSchema';
+import { GLYPHS_FETCHING_ERROR_PREFIX } from '@/redux/glyphs/glyphs.constants';
+import { pageableSchema } from '@/models/pageableSchema';
+
+export const getGlyphs = createAsyncThunk<Glyph[], void, ThunkConfig>('getGlyphs', async () => {
+  try {
+    const { data } = await axiosInstanceNewAPI.get<PageOf<Glyph>>(apiPath.getGlyphs());
+    const isDataValid = validateDataUsingZodSchema(data, pageableSchema(glyphSchema));
+    if (!isDataValid) {
+      return [];
+    }
+    return data.content;
+  } catch (error) {
+    return Promise.reject(getError({ error, prefix: GLYPHS_FETCHING_ERROR_PREFIX }));
+  }
+});
+
+export const addGlyph = createAsyncThunk<Glyph | undefined, File, ThunkConfig>(
+  'addGlyph',
+  async file => {
+    try {
+      const formData = new FormData();
+      formData.append('file', file);
+      const { data } = await axiosInstanceNewAPI.post<Glyph>(apiPath.addGlyph(), formData);
+      const isDataValid = validateDataUsingZodSchema(data, glyphSchema);
+      if (!isDataValid) {
+        return undefined;
+      }
+      return data;
+    } catch (error) {
+      return Promise.reject(getError({ error }));
+    }
+  },
+);
diff --git a/src/redux/glyphs/glyphs.types.ts b/src/redux/glyphs/glyphs.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..acf2e9056d8796f7041c7c420004c741490f175c
--- /dev/null
+++ b/src/redux/glyphs/glyphs.types.ts
@@ -0,0 +1,4 @@
+import { FetchDataState } from '@/types/fetchDataState';
+import { Glyph } from '@/types/models';
+
+export type GlyphsState = FetchDataState<Glyph[], []>;
diff --git a/src/redux/layers/layers.mock.ts b/src/redux/layers/layers.mock.ts
index 38e72675c3f471a4cc7033d2013eced09d87b0f9..729624ff10b3019218ee7d49e025d394cb3e4295 100644
--- a/src/redux/layers/layers.mock.ts
+++ b/src/redux/layers/layers.mock.ts
@@ -4,16 +4,16 @@ import { FetchDataState } from '@/types/fetchDataState';
 
 export const LAYERS_STATE_INITIAL_MOCK: LayersState = {};
 
+export const LAYER_STATE_DEFAULT_DATA = {
+  layers: [],
+  layersVisibility: {},
+  activeLayer: null,
+};
+
 export const LAYERS_STATE_INITIAL_LAYER_MOCK: FetchDataState<LayersVisibilitiesState> = {
   data: {
-    layers: [],
-    layersVisibility: {},
+    ...LAYER_STATE_DEFAULT_DATA,
   },
   loading: 'idle',
   error: DEFAULT_ERROR,
 };
-
-export const LAYER_STATE_DEFAULT_DATA = {
-  layers: [],
-  layersVisibility: {},
-};
diff --git a/src/redux/layers/layers.reducers.test.ts b/src/redux/layers/layers.reducers.test.ts
index 938b7f3e049b2fcfa9099dfdab1a473f8daa4c03..20d1cdbd261e35c48ade8c4c0c29287b504ba55f 100644
--- a/src/redux/layers/layers.reducers.test.ts
+++ b/src/redux/layers/layers.reducers.test.ts
@@ -58,6 +58,7 @@ describe('layers reducer', () => {
     expect(loading).toEqual('succeeded');
     expect(error).toEqual({ message: '', name: '' });
     expect(data).toEqual({
+      activeLayer: null,
       layers: [
         {
           details: layersFixture.content[0],
@@ -65,7 +66,7 @@ describe('layers reducer', () => {
           rects: layerRectsFixture.content,
           ovals: layerOvalsFixture.content,
           lines: layerLinesFixture.content,
-          images: layerImagesFixture.content,
+          images: { [layerImagesFixture.content[0].id]: layerImagesFixture.content[0] },
         },
       ],
       layersVisibility: {
@@ -86,7 +87,11 @@ describe('layers reducer', () => {
     );
     expect(loading).toEqual('failed');
     expect(error).toEqual({ message: '', name: '' });
-    expect(data).toEqual({ layers: [], layersVisibility: {} });
+    expect(data).toEqual({
+      activeLayer: null,
+      layers: [],
+      layersVisibility: {},
+    });
   });
 
   it('should update store on loading getLayers query', async () => {
@@ -117,6 +122,7 @@ describe('layers reducer', () => {
       const { data: dataPromiseFulfilled, loading: promiseFulfilled } = store.getState().layers[1];
 
       expect(dataPromiseFulfilled).toEqual({
+        activeLayer: null,
         layers: [
           {
             details: layersFixture.content[0],
@@ -124,7 +130,7 @@ describe('layers reducer', () => {
             rects: layerRectsFixture.content,
             ovals: layerOvalsFixture.content,
             lines: layerLinesFixture.content,
-            images: layerImagesFixture.content,
+            images: { [layerImagesFixture.content[0].id]: layerImagesFixture.content[0] },
           },
         ],
         layersVisibility: {
diff --git a/src/redux/layers/layers.reducers.ts b/src/redux/layers/layers.reducers.ts
index b0e601d57acd4fbe5bdf5b89d80ddb8d4a3a6e7e..9e41f8ded97ed3a120bc25c17e06d5f55ae886f4 100644
--- a/src/redux/layers/layers.reducers.ts
+++ b/src/redux/layers/layers.reducers.ts
@@ -7,6 +7,7 @@ import {
   LAYERS_STATE_INITIAL_LAYER_MOCK,
 } from '@/redux/layers/layers.mock';
 import { DEFAULT_ERROR } from '@/constants/errors';
+import { LayerImage } from '@/types/models';
 
 export const getLayersForModelReducer = (builder: ActionReducerMapBuilder<LayersState>): void => {
   builder.addCase(getLayersForModel.pending, (state, action) => {
@@ -50,3 +51,31 @@ export const setLayerVisibilityReducer = (
     data.layersVisibility[layerId] = visible;
   }
 };
+
+export const setActiveLayerReducer = (
+  state: LayersState,
+  action: PayloadAction<{ modelId: number; layerId: number | null }>,
+): void => {
+  const { modelId, layerId } = action.payload;
+  const { data } = state[modelId];
+  if (!data) {
+    return;
+  }
+  data.activeLayer = layerId;
+};
+
+export const layerAddImageReducer = (
+  state: LayersState,
+  action: PayloadAction<{ modelId: number; layerId: number; layerImage: LayerImage }>,
+): void => {
+  const { modelId, layerId, layerImage } = action.payload;
+  const { data } = state[modelId];
+  if (!data) {
+    return;
+  }
+  const layer = data.layers.find(layerState => layerState.details.id === layerId);
+  if (!layer) {
+    return;
+  }
+  layer.images[layerImage.id] = layerImage;
+};
diff --git a/src/redux/layers/layers.selectors.ts b/src/redux/layers/layers.selectors.ts
index 4d9e3f6d534b6891c8f9bc3eefbb18ecc6aedd03..aa71d5e482cbbe30ef92a9455e50c079f4311f9f 100644
--- a/src/redux/layers/layers.selectors.ts
+++ b/src/redux/layers/layers.selectors.ts
@@ -1,3 +1,4 @@
+/* eslint-disable no-magic-numbers */
 import { createSelector } from '@reduxjs/toolkit';
 import { rootSelector } from '@/redux/root/root.selectors';
 import { currentModelIdSelector } from '@/redux/models/models.selectors';
@@ -10,6 +11,11 @@ export const layersStateForCurrentModelSelector = createSelector(
   (state, currentModelId) => state[currentModelId],
 );
 
+export const layersActiveLayerSelector = createSelector(
+  layersStateForCurrentModelSelector,
+  state => state?.data?.activeLayer || null,
+);
+
 export const layersLoadingSelector = createSelector(
   layersStateForCurrentModelSelector,
   state => state?.loading,
@@ -24,3 +30,22 @@ export const layersForCurrentModelSelector = createSelector(
   layersStateForCurrentModelSelector,
   state => state?.data?.layers || [],
 );
+
+export const highestZIndexSelector = createSelector(layersForCurrentModelSelector, layers => {
+  if (!layers || layers.length === 0) return 0;
+
+  const getMaxZFromItems = <T extends { z?: number }>(items: T[] = []): number =>
+    items.length > 0 ? Math.max(...items.map(item => item.z || 0)) : 0;
+
+  return layers.reduce((maxZ, layer) => {
+    const textsMaxZ = getMaxZFromItems(layer.texts);
+    const rectsMaxZ = getMaxZFromItems(layer.rects);
+    const ovalsMaxZ = getMaxZFromItems(layer.ovals);
+    const linesMaxZ = getMaxZFromItems(layer.lines);
+    const imagesMaxZ = getMaxZFromItems(Object.values(layer.images));
+
+    const layerMaxZ = Math.max(textsMaxZ, rectsMaxZ, ovalsMaxZ, linesMaxZ, imagesMaxZ);
+
+    return Math.max(maxZ, layerMaxZ);
+  }, 0);
+});
diff --git a/src/redux/layers/layers.slice.ts b/src/redux/layers/layers.slice.ts
index 7c07cdc0a77d1b7cd9a912a0bccee08970f1e790..9f78f0dd112b9e817107f37b9958b738dfab0ff4 100644
--- a/src/redux/layers/layers.slice.ts
+++ b/src/redux/layers/layers.slice.ts
@@ -2,6 +2,8 @@ import { createSlice } from '@reduxjs/toolkit';
 import { LAYERS_STATE_INITIAL_MOCK } from '@/redux/layers/layers.mock';
 import {
   getLayersForModelReducer,
+  layerAddImageReducer,
+  setActiveLayerReducer,
   setLayerVisibilityReducer,
 } from '@/redux/layers/layers.reducers';
 
@@ -10,12 +12,14 @@ export const layersSlice = createSlice({
   initialState: LAYERS_STATE_INITIAL_MOCK,
   reducers: {
     setLayerVisibility: setLayerVisibilityReducer,
+    setActiveLayer: setActiveLayerReducer,
+    layerAddImage: layerAddImageReducer,
   },
   extraReducers: builder => {
     getLayersForModelReducer(builder);
   },
 });
 
-export const { setLayerVisibility } = layersSlice.actions;
+export const { setLayerVisibility, setActiveLayer, layerAddImage } = layersSlice.actions;
 
 export default layersSlice.reducer;
diff --git a/src/redux/layers/layers.thunks.test.ts b/src/redux/layers/layers.thunks.test.ts
index 975e8ec9e00604497e3bb8c4fe40d016f4e30976..218d9ce56ab4c1a69ad5cda6897f9236fa00991d 100644
--- a/src/redux/layers/layers.thunks.test.ts
+++ b/src/redux/layers/layers.thunks.test.ts
@@ -52,6 +52,7 @@ describe('layers thunks', () => {
 
       const { payload } = await store.dispatch(getLayersForModel(1));
       expect(payload).toEqual({
+        activeLayer: null,
         layers: [
           {
             details: layersFixture.content[0],
@@ -59,7 +60,7 @@ describe('layers thunks', () => {
             rects: layerRectsFixture.content,
             ovals: layerOvalsFixture.content,
             lines: layerLinesFixture.content,
-            images: layerImagesFixture.content,
+            images: { [layerImagesFixture.content[0].id]: layerImagesFixture.content[0] },
           },
         ],
         layersVisibility: {
diff --git a/src/redux/layers/layers.thunks.ts b/src/redux/layers/layers.thunks.ts
index 1fd5d6d443d08f83993450e9886d513163713ef4..354eb086e91b2bb6d3fa59a8c4c7093b9a5f4788 100644
--- a/src/redux/layers/layers.thunks.ts
+++ b/src/redux/layers/layers.thunks.ts
@@ -1,6 +1,7 @@
-import { z } from 'zod';
+/* eslint-disable no-magic-numbers */
+import { z as zod } from 'zod';
 import { apiPath } from '@/redux/apiPath';
-import { Layer, Layers } from '@/types/models';
+import { Layer, LayerImage, Layers } from '@/types/models';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { createAsyncThunk } from '@reduxjs/toolkit';
 import { ThunkConfig } from '@/types/store';
@@ -19,6 +20,7 @@ import { pageableSchema } from '@/models/pageableSchema';
 import { layerOvalSchema } from '@/models/layerOvalSchema';
 import { layerLineSchema } from '@/models/layerLineSchema';
 import { layerImageSchema } from '@/models/layerImageSchema';
+import arrayToKeyValue from '@/utils/array/arrayToKeyValue';
 
 export const getLayer = createAsyncThunk<
   Layer | null,
@@ -57,33 +59,38 @@ export const getLayersForModel = createAsyncThunk<
             axiosInstanceNewAPI.get(apiPath.getLayerLines(modelId, layer.id)),
             axiosInstanceNewAPI.get(apiPath.getLayerImages(modelId, layer.id)),
           ]);
-
         return {
           details: layer,
           texts: textsResponse.data.content,
           rects: rectsResponse.data.content,
           ovals: ovalsResponse.data.content,
           lines: linesResponse.data.content,
-          images: imagesResponse.data.content,
+          images: arrayToKeyValue(imagesResponse.data.content as Array<LayerImage>, 'id'),
         };
       }),
     );
     layers = layers.filter(layer => {
       return (
-        z.array(layerTextSchema).safeParse(layer.texts).success &&
-        z.array(layerRectSchema).safeParse(layer.rects).success &&
-        z.array(layerOvalSchema).safeParse(layer.ovals).success &&
-        z.array(layerLineSchema).safeParse(layer.lines).success &&
-        z.array(layerImageSchema).safeParse(layer.images).success
+        zod.array(layerTextSchema).safeParse(layer.texts).success &&
+        zod.array(layerRectSchema).safeParse(layer.rects).success &&
+        zod.array(layerOvalSchema).safeParse(layer.ovals).success &&
+        zod.array(layerLineSchema).safeParse(layer.lines).success &&
+        zod.array(layerImageSchema).safeParse(Object.values(layer.images)).success
       );
     });
     const layersVisibility = layers.reduce((acc: { [key: string]: boolean }, layer) => {
       acc[layer.details.id] = layer.details.visible;
       return acc;
     }, {});
+    let activeLayer = null;
+    const activeLayers = layers.filter(layer => layer.details.visible);
+    if (activeLayers.length) {
+      activeLayer = activeLayers[0].details.id;
+    }
     return {
       layers,
       layersVisibility,
+      activeLayer,
     };
   } catch (error) {
     return Promise.reject(getError({ error, prefix: LAYERS_FETCHING_ERROR_PREFIX }));
@@ -129,13 +136,47 @@ export const updateLayer = createAsyncThunk<Layer | null, LayerUpdateInterface,
 );
 
 export const removeLayer = createAsyncThunk<
-  void,
+  null,
   { modelId: number; layerId: number },
   ThunkConfig
-  // eslint-disable-next-line consistent-return
 >('vectorMap/removeLayer', async ({ modelId, layerId }) => {
   try {
     await axiosInstanceNewAPI.delete<void>(apiPath.removeLayer(modelId, layerId));
+    return null;
+  } catch (error) {
+    return Promise.reject(getError({ error }));
+  }
+});
+
+export const addLayerImageObject = createAsyncThunk<
+  LayerImage | null,
+  {
+    modelId: number;
+    layerId: number;
+    x: number;
+    y: number;
+    z: number;
+    width: number;
+    height: number;
+    glyph: number | null;
+  },
+  ThunkConfig
+>('vectorMap/addLayerImageObject', async ({ modelId, layerId, x, y, z, width, height, glyph }) => {
+  try {
+    const { data } = await axiosInstanceNewAPI.post<LayerImage>(
+      apiPath.addLayerImageObject(modelId, layerId),
+      {
+        x,
+        y,
+        z,
+        width,
+        height,
+        glyph,
+      },
+    );
+    const isDataValid = validateDataUsingZodSchema(data, layerImageSchema);
+
+    return isDataValid ? data : null;
   } catch (error) {
     return Promise.reject(getError({ error }));
   }
diff --git a/src/redux/layers/layers.types.ts b/src/redux/layers/layers.types.ts
index f228310603a20d38ebfd315d2757f2010126adee..27dc36fcd6cc2b8162c492c8ee87f0c95e4f79c5 100644
--- a/src/redux/layers/layers.types.ts
+++ b/src/redux/layers/layers.types.ts
@@ -22,7 +22,7 @@ export type LayerState = {
   rects: LayerRect[];
   ovals: LayerOval[];
   lines: LayerLine[];
-  images: LayerImage[];
+  images: { [key: string]: LayerImage };
 };
 
 export type LayerVisibilityState = {
@@ -32,6 +32,7 @@ export type LayerVisibilityState = {
 export type LayersVisibilitiesState = {
   layersVisibility: LayerVisibilityState;
   layers: LayerState[];
+  activeLayer: number | null;
 };
 
 export type LayersState = KeyedFetchDataState<LayersVisibilitiesState>;
diff --git a/src/redux/mapEditTools/mapEditTools.constants.ts b/src/redux/mapEditTools/mapEditTools.constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3f54d2b0e3720fe456fb0759663465d15b54c4b5
--- /dev/null
+++ b/src/redux/mapEditTools/mapEditTools.constants.ts
@@ -0,0 +1,3 @@
+export const MAP_EDIT_ACTIONS = {
+  DRAW_IMAGE: 'DRAW_IMAGE',
+} as const;
diff --git a/src/redux/mapEditTools/mapEditTools.mock.ts b/src/redux/mapEditTools/mapEditTools.mock.ts
new file mode 100644
index 0000000000000000000000000000000000000000..81dd081244074969ac072b418d32b62a2671e2b5
--- /dev/null
+++ b/src/redux/mapEditTools/mapEditTools.mock.ts
@@ -0,0 +1,5 @@
+import { MapEditToolsState } from '@/redux/mapEditTools/mapEditTools.types';
+
+export const MAP_EDIT_TOOLS_STATE_INITIAL_MOCK: MapEditToolsState = {
+  activeAction: null,
+};
diff --git a/src/redux/mapEditTools/mapEditTools.reducers.ts b/src/redux/mapEditTools/mapEditTools.reducers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..01334ba6aa845df55bc2436490fda314bb8d211d
--- /dev/null
+++ b/src/redux/mapEditTools/mapEditTools.reducers.ts
@@ -0,0 +1,15 @@
+/* eslint-disable no-magic-numbers */
+import { PayloadAction } from '@reduxjs/toolkit';
+import { MAP_EDIT_ACTIONS } from '@/redux/mapEditTools/mapEditTools.constants';
+import { MapEditToolsState } from '@/redux/mapEditTools/mapEditTools.types';
+
+export const mapEditToolsSetActiveActionReducer = (
+  state: MapEditToolsState,
+  action: PayloadAction<keyof typeof MAP_EDIT_ACTIONS | null>,
+): void => {
+  if (state.activeAction !== action.payload) {
+    state.activeAction = action.payload;
+  } else {
+    state.activeAction = null;
+  }
+};
diff --git a/src/redux/mapEditTools/mapEditTools.selectors.ts b/src/redux/mapEditTools/mapEditTools.selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ed51e7ab48c7c878d83fd6f2afb4617bb823d705
--- /dev/null
+++ b/src/redux/mapEditTools/mapEditTools.selectors.ts
@@ -0,0 +1,14 @@
+/* eslint-disable no-magic-numbers */
+import { createSelector } from '@reduxjs/toolkit';
+import { rootSelector } from '@/redux/root/root.selectors';
+
+export const mapEditToolsSelector = createSelector(rootSelector, state => state.mapEditTools);
+
+export const mapEditToolsActiveActionSelector = createSelector(
+  mapEditToolsSelector,
+  state => state.activeAction,
+);
+
+export const isMapEditToolsActiveSelector = createSelector(mapEditToolsSelector, state =>
+  Boolean(state.activeAction),
+);
diff --git a/src/redux/mapEditTools/mapEditTools.slice.ts b/src/redux/mapEditTools/mapEditTools.slice.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bea57d9cda02e1cc7ca20039cb296f23700bb8dd
--- /dev/null
+++ b/src/redux/mapEditTools/mapEditTools.slice.ts
@@ -0,0 +1,15 @@
+import { createSlice } from '@reduxjs/toolkit';
+import { MAP_EDIT_TOOLS_STATE_INITIAL_MOCK } from '@/redux/mapEditTools/mapEditTools.mock';
+import { mapEditToolsSetActiveActionReducer } from '@/redux/mapEditTools/mapEditTools.reducers';
+
+export const layersSlice = createSlice({
+  name: 'layers',
+  initialState: MAP_EDIT_TOOLS_STATE_INITIAL_MOCK,
+  reducers: {
+    mapEditToolsSetActiveAction: mapEditToolsSetActiveActionReducer,
+  },
+});
+
+export const { mapEditToolsSetActiveAction } = layersSlice.actions;
+
+export default layersSlice.reducer;
diff --git a/src/redux/mapEditTools/mapEditTools.types.ts b/src/redux/mapEditTools/mapEditTools.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8a000d1d076a6d7e194eec4bfd1c22d36672d15f
--- /dev/null
+++ b/src/redux/mapEditTools/mapEditTools.types.ts
@@ -0,0 +1,5 @@
+import { MAP_EDIT_ACTIONS } from '@/redux/mapEditTools/mapEditTools.constants';
+
+export type MapEditToolsState = {
+  activeAction: keyof typeof MAP_EDIT_ACTIONS | null;
+};
diff --git a/src/redux/modal/modal.constants.ts b/src/redux/modal/modal.constants.ts
index 1184d4ed526038773f9bb90853e52d8f0912dece..1b755f3bcb626e6183d3a5ab9a8e3032db3bc772 100644
--- a/src/redux/modal/modal.constants.ts
+++ b/src/redux/modal/modal.constants.ts
@@ -14,4 +14,5 @@ export const MODAL_INITIAL_STATE: ModalState = {
   editOverlayState: null,
   errorReportState: {},
   layerFactoryState: { id: undefined },
+  layerImageObjectFactoryState: undefined,
 };
diff --git a/src/redux/modal/modal.mock.ts b/src/redux/modal/modal.mock.ts
index 1a7a519f3509c02ae667c7e310eeb58af77c3af2..40464dd3594081af18545fe2b988a8ff154c82fd 100644
--- a/src/redux/modal/modal.mock.ts
+++ b/src/redux/modal/modal.mock.ts
@@ -14,4 +14,5 @@ export const MODAL_INITIAL_STATE_MOCK: ModalState = {
   editOverlayState: null,
   errorReportState: {},
   layerFactoryState: { id: undefined },
+  layerImageObjectFactoryState: undefined,
 };
diff --git a/src/redux/modal/modal.reducers.ts b/src/redux/modal/modal.reducers.ts
index 3371ed3c102a74f1f32e8b26807291c159ac751c..f678ea91f3f7524626b107a413d7766c3bcd5a20 100644
--- a/src/redux/modal/modal.reducers.ts
+++ b/src/redux/modal/modal.reducers.ts
@@ -138,3 +138,18 @@ export const openLayerFactoryModalReducer = (
     state.modalTitle = 'Add new layer';
   }
 };
+
+export const openLayerImageObjectFactoryModalReducer = (
+  state: ModalState,
+  action: PayloadAction<{
+    x: number;
+    y: number;
+    width: number;
+    height: number;
+  }>,
+): void => {
+  state.layerImageObjectFactoryState = action.payload;
+  state.isOpen = true;
+  state.modalName = 'layer-image-object-factory';
+  state.modalTitle = 'Select glyph or upload file';
+};
diff --git a/src/redux/modal/modal.selector.ts b/src/redux/modal/modal.selector.ts
index 7f7c444111a45551d76fa8b41f887e9ceb43fe4b..132472b6c923924d0942f7ce37aa09b691e100d4 100644
--- a/src/redux/modal/modal.selector.ts
+++ b/src/redux/modal/modal.selector.ts
@@ -30,3 +30,8 @@ export const currentErrorDataSelector = createSelector(
   modalSelector,
   modal => modal?.errorReportState.errorData || undefined,
 );
+
+export const layerImageObjectFactoryStateSelector = createSelector(
+  modalSelector,
+  modal => modal.layerImageObjectFactoryState,
+);
diff --git a/src/redux/modal/modal.slice.ts b/src/redux/modal/modal.slice.ts
index 8ed0421510457f64c783b7c0548d09977f7e8415..a9baf72a027e686a80e91491679078dace605c9f 100644
--- a/src/redux/modal/modal.slice.ts
+++ b/src/redux/modal/modal.slice.ts
@@ -17,6 +17,7 @@ import {
   openLicenseModalReducer,
   openToSModalReducer,
   openLayerFactoryModalReducer,
+  openLayerImageObjectFactoryModalReducer,
 } from './modal.reducers';
 
 const modalSlice = createSlice({
@@ -39,6 +40,7 @@ const modalSlice = createSlice({
     openLicenseModal: openLicenseModalReducer,
     openToSModal: openToSModalReducer,
     openLayerFactoryModal: openLayerFactoryModalReducer,
+    openLayerImageObjectFactoryModal: openLayerImageObjectFactoryModalReducer,
   },
 });
 
@@ -59,6 +61,7 @@ export const {
   openLicenseModal,
   openToSModal,
   openLayerFactoryModal,
+  openLayerImageObjectFactoryModal,
 } = modalSlice.actions;
 
 export default modalSlice.reducer;
diff --git a/src/redux/modal/modal.types.ts b/src/redux/modal/modal.types.ts
index 1b544f5282525ca4e9b1d6efa3835c0e175313d4..3b22209e0e0b833e8ad404b7d94bc7ab6582b30f 100644
--- a/src/redux/modal/modal.types.ts
+++ b/src/redux/modal/modal.types.ts
@@ -21,6 +21,15 @@ export type LayerFactoryState = {
   id: number | undefined;
 };
 
+export type LayerImageObjectFactoryState =
+  | {
+      x: number;
+      y: number;
+      width: number;
+      height: number;
+    }
+  | undefined;
+
 export interface ModalState {
   isOpen: boolean;
   modalName: ModalName;
@@ -30,6 +39,7 @@ export interface ModalState {
   errorReportState: ErrorRepostState;
   editOverlayState: EditOverlayState;
   layerFactoryState: LayerFactoryState;
+  layerImageObjectFactoryState: LayerImageObjectFactoryState;
 }
 
 export type OpenEditOverlayModalPayload = MapOverlay;
diff --git a/src/redux/root/init.thunks.ts b/src/redux/root/init.thunks.ts
index b5a5b4e0899bc7fe13ea745798a4f97abc5a4232..ba7f305972bdad744aa4ce396ae86f9a9f9b2cb6 100644
--- a/src/redux/root/init.thunks.ts
+++ b/src/redux/root/init.thunks.ts
@@ -23,6 +23,7 @@ import {
   USER_ACCEPTED_MATOMO_COOKIES_COOKIE_NAME,
 } from '@/components/FunctionalArea/CookieBanner/CookieBanner.constants';
 import { injectMatomoTracking } from '@/utils/injectMatomoTracking';
+import { getGlyphs } from '@/redux/glyphs/glyphs.thunks';
 import { getAllBackgroundsByProjectId } from '../backgrounds/backgrounds.thunks';
 import { getConfiguration, getConfigurationOptions } from '../configuration/configuration.thunks';
 import {
@@ -67,6 +68,7 @@ export const fetchInitialAppData = createAsyncThunk<
     dispatch(getAllPublicOverlaysByProjectId(PROJECT_ID)),
     dispatch(getModels()),
     dispatch(getShapes()),
+    dispatch(getGlyphs()),
     dispatch(getLineTypes()),
     dispatch(getArrowTypes()),
   ]);
diff --git a/src/redux/root/root.fixtures.ts b/src/redux/root/root.fixtures.ts
index 007c2b355b0bb4cc0dbf4a5d5e0a9d84b7b8d19c..c90d96c3c158bb301cd7533634e652c0e47fec7a 100644
--- a/src/redux/root/root.fixtures.ts
+++ b/src/redux/root/root.fixtures.ts
@@ -7,6 +7,8 @@ import { SHAPES_STATE_INITIAL_MOCK } from '@/redux/shapes/shapes.mock';
 import { MODEL_ELEMENTS_INITIAL_STATE_MOCK } from '@/redux/modelElements/modelElements.mock';
 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 { BACKGROUND_INITIAL_STATE_MOCK } from '../backgrounds/background.mock';
 import { BIOENTITY_INITIAL_STATE_MOCK } from '../bioEntity/bioEntity.mock';
 import { CHEMICALS_INITIAL_STATE_MOCK } from '../chemicals/chemicals.mock';
@@ -41,6 +43,7 @@ export const INITIAL_STORE_STATE_MOCK: RootState = {
   search: SEARCH_STATE_INITIAL_MOCK,
   project: PROJECT_STATE_INITIAL_MOCK,
   shapes: SHAPES_STATE_INITIAL_MOCK,
+  glyphs: GLYPHS_STATE_INITIAL_MOCK,
   projects: PROJECTS_STATE_INITIAL_MOCK,
   drugs: DRUGS_INITIAL_STATE_MOCK,
   chemicals: CHEMICALS_INITIAL_STATE_MOCK,
@@ -71,4 +74,5 @@ export const INITIAL_STORE_STATE_MOCK: RootState = {
   markers: MARKERS_INITIAL_STATE_MOCK,
   entityNumber: ENTITY_NUMBER_INITIAL_STATE_MOCK,
   comment: COMMENT_INITIAL_STATE_MOCK,
+  mapEditTools: MAP_EDIT_TOOLS_STATE_INITIAL_MOCK,
 };
diff --git a/src/redux/store.ts b/src/redux/store.ts
index a5d31bb57d0a4db6a0bae569a15a89579317c211..0e3a85b9cf87d116826427e61d973595c2c9fe79 100644
--- a/src/redux/store.ts
+++ b/src/redux/store.ts
@@ -11,6 +11,7 @@ import mapReducer from '@/redux/map/map.slice';
 import modalReducer from '@/redux/modal/modal.slice';
 import modelsReducer from '@/redux/models/models.slice';
 import shapesReducer from '@/redux/shapes/shapes.slice';
+import glyphsReducer from '@/redux/glyphs/glyphs.slice';
 import modelElementsReducer from '@/redux/modelElements/modelElements.slice';
 import layersReducer from '@/redux/layers/layers.slice';
 import oauthReducer from '@/redux/oauth/oauth.slice';
@@ -22,6 +23,7 @@ import reactionsReducer from '@/redux/reactions/reactions.slice';
 import newReactionsReducer from '@/redux/newReactions/newReactions.slice';
 import searchReducer from '@/redux/search/search.slice';
 import userReducer from '@/redux/user/user.slice';
+import mapEditToolsReducer from '@/redux/mapEditTools/mapEditTools.slice';
 import {
   autocompleteChemicalReducer,
   autocompleteDrugReducer,
@@ -64,6 +66,7 @@ export const reducers = {
   overlays: overlaysReducer,
   models: modelsReducer,
   shapes: shapesReducer,
+  glyphs: glyphsReducer,
   modelElements: modelElementsReducer,
   layers: layersReducer,
   reactions: reactionsReducer,
@@ -71,6 +74,7 @@ export const reducers = {
   contextMenu: contextMenuReducer,
   cookieBanner: cookieBannerReducer,
   user: userReducer,
+  mapEditTools: mapEditToolsReducer,
   configuration: configurationReducer,
   constant: constantReducer,
   overlayBioEntity: overlayBioEntityReducer,
diff --git a/src/shared/Autocomplete/Autocomplete.component.test.tsx b/src/shared/Autocomplete/Autocomplete.component.test.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..65662aed673d1ced4b27119d516f85a3c0655681
--- /dev/null
+++ b/src/shared/Autocomplete/Autocomplete.component.test.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import { render, screen, fireEvent } from '@testing-library/react';
+import { Autocomplete } from './Autocomplete.component';
+
+interface Option {
+  id: number;
+  name: string;
+}
+
+describe('Autocomplete', () => {
+  const options: Option[] = [
+    { id: 1, name: 'Option 1' },
+    { id: 2, name: 'Option 2' },
+    { id: 3, name: 'Option 3' },
+  ];
+
+  it('renders the component with placeholder', () => {
+    render(
+      <Autocomplete
+        options={options}
+        valueKey="id"
+        labelKey="name"
+        placeholder="Select an option"
+        onChange={() => {}}
+      />,
+    );
+
+    const placeholder = screen.getByText('Select an option');
+    expect(placeholder).toBeInTheDocument();
+  });
+
+  it('displays options and handles selection', () => {
+    const handleChange = jest.fn();
+
+    render(
+      <Autocomplete
+        options={options}
+        valueKey="id"
+        labelKey="name"
+        placeholder="Select an option"
+        onChange={handleChange}
+      />,
+    );
+
+    const dropdown = screen.getByTestId('autocomplete');
+    if (!dropdown.firstChild) {
+      throw new Error('Dropdown does not have a firstChild');
+    }
+    fireEvent.keyDown(dropdown.firstChild, { key: 'ArrowDown' });
+
+    const option1 = screen.getByText('Option 1');
+    const option2 = screen.getByText('Option 2');
+    expect(option1).toBeInTheDocument();
+    expect(option2).toBeInTheDocument();
+
+    fireEvent.click(option1);
+
+    expect(handleChange).toHaveBeenCalledWith({ id: 1, name: 'Option 1' });
+  });
+});
diff --git a/src/shared/Autocomplete/Autocomplete.component.tsx b/src/shared/Autocomplete/Autocomplete.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..232dca1940bbb8eb595bfeeaf633054af91e38c8
--- /dev/null
+++ b/src/shared/Autocomplete/Autocomplete.component.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import Select, { SingleValue } from 'react-select';
+import './Autocomplete.styles.css';
+
+type AutocompleteProps<T> = {
+  options: Array<T>;
+  valueKey?: keyof T;
+  labelKey?: keyof T;
+  placeholder?: string;
+  onChange: (value: T | null) => void;
+};
+
+type OptionType<T> = {
+  value: T[keyof T];
+  label: string;
+  originalOption: T;
+};
+
+export const Autocomplete = <T,>({
+  options,
+  valueKey = 'value' as keyof T,
+  labelKey = 'label' as keyof T,
+  placeholder = 'Select...',
+  onChange,
+}: AutocompleteProps<T>): React.JSX.Element => {
+  const formattedOptions = options.map(option => ({
+    value: option[valueKey],
+    label: option[labelKey] as string,
+    originalOption: option,
+  }));
+
+  const handleChange = (selectedOption: SingleValue<OptionType<T>>): void => {
+    onChange(selectedOption ? selectedOption.originalOption : null);
+  };
+
+  return (
+    <div data-testid="autocomplete">
+      <Select
+        options={formattedOptions}
+        onChange={handleChange}
+        placeholder={placeholder}
+        classNamePrefix="react-select"
+      />
+    </div>
+  );
+};
+
+Autocomplete.displayName = 'Autocomplete';
diff --git a/src/shared/Autocomplete/Autocomplete.styles.css b/src/shared/Autocomplete/Autocomplete.styles.css
new file mode 100644
index 0000000000000000000000000000000000000000..49f417b137df8d0f28da4a9fa1213898501b3356
--- /dev/null
+++ b/src/shared/Autocomplete/Autocomplete.styles.css
@@ -0,0 +1,7 @@
+.react-select__control {
+  height: 40px;
+}
+
+.react-select__menu {
+  margin: 0 !important;
+}
diff --git a/src/shared/Autocomplete/index.ts b/src/shared/Autocomplete/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f78074f3c5e84198705ad2a2d22e0b8b3bbd64f8
--- /dev/null
+++ b/src/shared/Autocomplete/index.ts
@@ -0,0 +1 @@
+export { Autocomplete } from './Autocomplete.component';
diff --git a/src/shared/Icon/Icon.component.tsx b/src/shared/Icon/Icon.component.tsx
index f22e882b052efc6e3c7c5d63836d015d210cf88b..784cfb0efe5c148cef4591a9f0753c809dab2a23 100644
--- a/src/shared/Icon/Icon.component.tsx
+++ b/src/shared/Icon/Icon.component.tsx
@@ -18,6 +18,7 @@ import { PlusIcon } from '@/shared/Icon/Icons/PlusIcon';
 
 import type { IconComponentType, IconTypes } from '@/types/iconTypes';
 import { DownloadIcon } from '@/shared/Icon/Icons/DownloadIcon';
+import { ImageIcon } from '@/shared/Icon/Icons/ImageIcon';
 import { LocationIcon } from './Icons/LocationIcon';
 import { MaginfierZoomInIcon } from './Icons/MagnifierZoomIn';
 import { MaginfierZoomOutIcon } from './Icons/MagnifierZoomOut';
@@ -59,6 +60,7 @@ const icons: Record<IconTypes, IconComponentType> = {
   clear: ClearIcon,
   user: UserIcon,
   'manage-user': ManageUserIcon,
+  image: ImageIcon,
 } as const;
 
 export const Icon = ({ name, className = '', ...rest }: IconProps): JSX.Element => {
diff --git a/src/shared/Icon/Icons/ImageIcon.tsx b/src/shared/Icon/Icons/ImageIcon.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..1c616b7fd83cb0bcdd543afb5f3223f041f0a55f
--- /dev/null
+++ b/src/shared/Icon/Icons/ImageIcon.tsx
@@ -0,0 +1,25 @@
+interface ImageIconProps {
+  className?: string;
+}
+
+export const ImageIcon = ({ className }: ImageIconProps): JSX.Element => (
+  <svg
+    width="24"
+    height="24"
+    viewBox="0 0 24 24"
+    fill="none"
+    className={className}
+    xmlns="http://www.w3.org/2000/svg"
+  >
+    <rect x="3" y="3" width="18" height="18" rx="2" stroke="currentColor" strokeWidth="1.5" />
+    <circle cx="8" cy="8" r="1.5" stroke="currentColor" strokeWidth="1.5" fill="none" />
+    <path
+      d="M4 18L9 13L12 16L16 12L20 18H4Z"
+      stroke="currentColor"
+      strokeWidth="1.5"
+      strokeLinecap="round"
+      strokeLinejoin="round"
+      fill="none"
+    />
+  </svg>
+);
diff --git a/src/shared/Input/Input.component.tsx b/src/shared/Input/Input.component.tsx
index 00f3e9240109b21b66e54974b9676daf5ee8c11b..96675fdf171d70071e29fdf5db71a16a06a59a1b 100644
--- a/src/shared/Input/Input.component.tsx
+++ b/src/shared/Input/Input.component.tsx
@@ -1,4 +1,4 @@
-import React, { InputHTMLAttributes } from 'react';
+import React, { InputHTMLAttributes, forwardRef } from 'react';
 import { twMerge } from 'tailwind-merge';
 
 type StyleVariant = 'primary' | 'primaryWithoutFull';
@@ -8,6 +8,7 @@ type InputProps = {
   className?: string;
   styleVariant?: StyleVariant;
   sizeVariant?: SizeVariant;
+  ref?: React.Ref<HTMLInputElement>;
 } & InputHTMLAttributes<HTMLInputElement>;
 
 const styleVariants = {
@@ -22,14 +23,17 @@ const sizeVariants = {
   medium: 'rounded-lg h-12 text-sm',
 } as const;
 
-export const Input = ({
-  className = '',
-  sizeVariant = 'small',
-  styleVariant = 'primary',
-  ...props
-}: InputProps): React.ReactNode => (
-  <input
-    {...props}
-    className={twMerge(styleVariants[styleVariant], sizeVariants[sizeVariant], className)}
-  />
+export const Input = forwardRef<HTMLInputElement, InputProps>(
+  (
+    { className = '', sizeVariant = 'small', styleVariant = 'primary', ...props }: InputProps,
+    ref,
+  ): React.ReactNode => (
+    <input
+      ref={ref}
+      {...props}
+      className={twMerge(styleVariants[styleVariant], sizeVariants[sizeVariant], className)}
+    />
+  ),
 );
+
+Input.displayName = 'Input';
diff --git a/src/shared/Select/Select.component.tsx b/src/shared/Select/Select.component.tsx
index aa3ab6d5fc11b0e8e0d7991a9d9e1e2eebd80f63..f0107571e2670d0693d1b4e198855e559420112b 100644
--- a/src/shared/Select/Select.component.tsx
+++ b/src/shared/Select/Select.component.tsx
@@ -5,7 +5,7 @@ import { Icon } from '@/shared/Icon';
 
 type SelectProps = {
   options: Array<{ id: number; name: string }>;
-  selectedId: number;
+  selectedId: number | null;
   onChange: (selectedId: number) => void;
   width?: string | number;
 };
@@ -16,7 +16,7 @@ export const Select = ({
   onChange,
   width = '100%',
 }: SelectProps): React.JSX.Element => {
-  const selectedOption = options.find(option => option.id === selectedId);
+  const selectedOption = options.find(option => option.id === selectedId) || null;
 
   const {
     isOpen,
@@ -63,7 +63,7 @@ export const Select = ({
       </div>
       <ul
         className={twMerge(
-          'absolute z-10 overflow-auto rounded-b bg-white shadow-lg',
+          'absolute z-20 overflow-auto rounded-b bg-white shadow-lg',
           !isOpen && 'hidden',
         )}
         style={widthStyle}
diff --git a/src/types/iconTypes.ts b/src/types/iconTypes.ts
index 7681839461a42db984af5556529f30e0ed4a57b0..0ef11e99da00f9fde333a19968bd5fc7e46dd9b0 100644
--- a/src/types/iconTypes.ts
+++ b/src/types/iconTypes.ts
@@ -24,6 +24,7 @@ export type IconTypes =
   | 'user'
   | 'manage-user'
   | 'download'
-  | 'question';
+  | 'question'
+  | 'image';
 
 export type IconComponentType = ({ className }: { className: string }) => JSX.Element;
diff --git a/src/types/modal.ts b/src/types/modal.ts
index 861bb29569581cb89fcc57b136a3ff7cd8c2dcfc..edf1c858843a5573baf94dd44b19eefe47743026 100644
--- a/src/types/modal.ts
+++ b/src/types/modal.ts
@@ -12,4 +12,5 @@ export type ModalName =
   | 'select-project'
   | 'terms-of-service'
   | 'logged-in-menu'
-  | 'layer-factory';
+  | 'layer-factory'
+  | 'layer-image-object-factory';
diff --git a/src/types/models.ts b/src/types/models.ts
index 6e84987532c71383c962942ab3d95ee476b6a092..c70bb0b7c2b737c78090520f96d4faf39845f1f8 100644
--- a/src/types/models.ts
+++ b/src/types/models.ts
@@ -84,6 +84,7 @@ import { operatorSchema } from '@/models/operatorSchema';
 import { modificationResiduesSchema } from '@/models/modificationResiduesSchema';
 import { segmentSchema } from '@/models/segmentSchema';
 import { layerImageSchema } from '@/models/layerImageSchema';
+import { glyphSchema } from '@/models/glyphSchema';
 
 export type Project = z.infer<typeof projectSchema>;
 export type OverviewImageView = z.infer<typeof overviewImageView>;
@@ -106,6 +107,7 @@ export type LayerOval = z.infer<typeof layerOvalSchema>;
 export type LayerLine = z.infer<typeof layerLineSchema>;
 export type LayerImage = z.infer<typeof layerImageSchema>;
 export type Arrow = z.infer<typeof arrowSchema>;
+export type Glyph = z.infer<typeof glyphSchema>;
 const modelElementsSchema = pageableSchema(modelElementSchema);
 export type ModelElements = z.infer<typeof modelElementsSchema>;
 export type ModelElement = z.infer<typeof modelElementSchema>;
diff --git a/src/utils/array/arrayToKeyValue.test.ts b/src/utils/array/arrayToKeyValue.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..347d3f982e3b3ec2c883450143b945e7b0b3b845
--- /dev/null
+++ b/src/utils/array/arrayToKeyValue.test.ts
@@ -0,0 +1,46 @@
+/* eslint-disable no-magic-numbers */
+import arrayToKeyValue from './arrayToKeyValue';
+
+describe('arrayToKeyValue', () => {
+  interface Person {
+    id: number;
+    name: string;
+    age: number;
+    isActive: boolean;
+  }
+
+  const people: Person[] = [
+    { id: 1, name: 'John', age: 30, isActive: true },
+    { id: 2, name: 'Anna', age: 25, isActive: false },
+    { id: 3, name: 'Peter', age: 28, isActive: true },
+  ];
+
+  it('create dict with key "id" and value "name"', () => {
+    const result = arrayToKeyValue(people, 'id');
+    expect(result).toEqual({
+      1: people[0],
+      2: people[1],
+      3: people[2],
+    });
+  });
+
+  it('create dict with key "name" and value "age"', () => {
+    const result = arrayToKeyValue(people, 'name');
+    expect(result).toEqual({
+      John: people[0],
+      Anna: people[1],
+      Peter: people[2],
+    });
+  });
+
+  it('handles duplicate keys, overwriting previous values', () => {
+    const duplicateData = [
+      { id: 1, name: 'John', age: 30, isActive: true },
+      { id: 1, name: 'Anna', age: 25, isActive: false },
+    ];
+    const result = arrayToKeyValue(duplicateData, 'id');
+    expect(result).toEqual({
+      1: duplicateData[1],
+    });
+  });
+});
diff --git a/src/utils/array/arrayToKeyValue.ts b/src/utils/array/arrayToKeyValue.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1b04bab52b244e7903f5521245890b9c632d2558
--- /dev/null
+++ b/src/utils/array/arrayToKeyValue.ts
@@ -0,0 +1,12 @@
+export default function arrayToKeyValue<T, K extends keyof T>(
+  array: T[],
+  key: K,
+): Record<T[K] & PropertyKey, T> {
+  return array.reduce(
+    (accumulator, currentItem) => {
+      accumulator[currentItem[key] as T[K] & PropertyKey] = currentItem;
+      return accumulator;
+    },
+    {} as Record<T[K] & PropertyKey, T>,
+  );
+}