diff --git a/.env b/.env
index 3f191e1fdfb47f26281eeb4f6ea52ea423ed0d8b..470be3d78ae1c38d2951c67acae288685d447b9c 100644
--- a/.env
+++ b/.env
@@ -1,5 +1,6 @@
 
 NEXT_PUBLIC_BASE_API_URL = 'https://corsproxy.io/?https://lux1.atcomp.pl/minerva/api'
 NEXT_PUBLIC_BASE_NEW_API_URL = 'https://corsproxy.io/?https://lux1.atcomp.pl/minerva/new_api/'
+BASE_MAP_IMAGES_URL = 'https://lux1.atcomp.pl/'
 NEXT_PUBLIC_PROJECT_ID = 'pdmap_appu_test'
 ZOD_SEED = 997
diff --git a/.eslintrc.json b/.eslintrc.json
index e60a21f06faa469391143c2f16c8d14f11ee5e97..3ed8a0dac26c9f37e4753346fdf8856d5351cabe 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -10,6 +10,7 @@
     "plugin:react/recommended",
     "plugin:jsx-a11y/recommended",
     "plugin:@next/next/recommended",
+    "plugin:prettier/recommended",
     "prettier",
     "next"
   ],
@@ -87,7 +88,8 @@
         "callees": ["twMerge"],
         "config": "./tailwind.config.ts"
       }
-    ]
+    ],
+    "prettier/prettier": "error"
   },
   "overrides": [
     {
diff --git a/package-lock.json b/package-lock.json
index cbe12b20544abcdb8444e4320827acbf4ece6cee..f41b112587042b8d19ebbab8add5288dcd762146 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -50,6 +50,7 @@
         "eslint-plugin-import": "^2.28.1",
         "eslint-plugin-jsx-a11y": "^6.7.1",
         "eslint-plugin-n": "^16.1.0",
+        "eslint-plugin-prettier": "^5.0.1",
         "eslint-plugin-promise": "^6.1.1",
         "eslint-plugin-react": "^7.33.2",
         "eslint-plugin-react-hooks": "^4.6.0",
@@ -1885,6 +1886,26 @@
       "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.4.tgz",
       "integrity": "sha512-kB+NJ5Br56ZhElKsf0pM7/PQfrDdDVMRz8f0JM6eVOGE+L89z9hwcst9QvWBBnazzuqGTGtPsJNZoQ1JdNiGSQ=="
     },
+    "node_modules/@pkgr/utils": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz",
+      "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==",
+      "dev": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "fast-glob": "^3.3.0",
+        "is-glob": "^4.0.3",
+        "open": "^9.1.0",
+        "picocolors": "^1.0.0",
+        "tslib": "^2.6.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/unts"
+      }
+    },
     "node_modules/@reduxjs/toolkit": {
       "version": "1.9.6",
       "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.6.tgz",
@@ -3208,6 +3229,15 @@
         "tweetnacl": "^0.14.3"
       }
     },
+    "node_modules/big-integer": {
+      "version": "1.6.51",
+      "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
+      "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
     "node_modules/binary-extensions": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -3239,6 +3269,18 @@
       "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
       "dev": true
     },
+    "node_modules/bplist-parser": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz",
+      "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==",
+      "dev": true,
+      "dependencies": {
+        "big-integer": "^1.6.44"
+      },
+      "engines": {
+        "node": ">= 5.10.0"
+      }
+    },
     "node_modules/brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -3347,6 +3389,21 @@
         "semver": "^7.0.0"
       }
     },
+    "node_modules/bundle-name": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz",
+      "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==",
+      "dev": true,
+      "dependencies": {
+        "run-applescript": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/busboy": {
       "version": "1.6.0",
       "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@@ -4379,6 +4436,150 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/default-browser": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz",
+      "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==",
+      "dev": true,
+      "dependencies": {
+        "bundle-name": "^3.0.0",
+        "default-browser-id": "^3.0.0",
+        "execa": "^7.1.1",
+        "titleize": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/default-browser-id": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz",
+      "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==",
+      "dev": true,
+      "dependencies": {
+        "bplist-parser": "^0.2.0",
+        "untildify": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/default-browser/node_modules/execa": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz",
+      "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==",
+      "dev": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^6.0.1",
+        "human-signals": "^4.3.0",
+        "is-stream": "^3.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^5.1.0",
+        "onetime": "^6.0.0",
+        "signal-exit": "^3.0.7",
+        "strip-final-newline": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.18.0 || ^16.14.0 || >=18.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
+    "node_modules/default-browser/node_modules/human-signals": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
+      "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=14.18.0"
+      }
+    },
+    "node_modules/default-browser/node_modules/is-stream": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+      "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+      "dev": true,
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/default-browser/node_modules/mimic-fn": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+      "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/default-browser/node_modules/npm-run-path": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
+      "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==",
+      "dev": true,
+      "dependencies": {
+        "path-key": "^4.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/default-browser/node_modules/onetime": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+      "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+      "dev": true,
+      "dependencies": {
+        "mimic-fn": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/default-browser/node_modules/path-key": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+      "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/default-browser/node_modules/strip-final-newline": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+      "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/defaults": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
@@ -4404,6 +4605,18 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/define-lazy-prop": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+      "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/define-properties": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
@@ -5211,6 +5424,35 @@
         "eslint": ">=7.0.0"
       }
     },
+    "node_modules/eslint-plugin-prettier": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz",
+      "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==",
+      "dev": true,
+      "dependencies": {
+        "prettier-linter-helpers": "^1.0.0",
+        "synckit": "^0.8.5"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/prettier"
+      },
+      "peerDependencies": {
+        "@types/eslint": ">=8.0.0",
+        "eslint": ">=8.0.0",
+        "prettier": ">=3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/eslint": {
+          "optional": true
+        },
+        "eslint-config-prettier": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/eslint-plugin-promise": {
       "version": "6.1.1",
       "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz",
@@ -5730,6 +5972,12 @@
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
       "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
     },
+    "node_modules/fast-diff": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+      "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+      "dev": true
+    },
     "node_modules/fast-glob": {
       "version": "3.3.1",
       "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
@@ -6882,6 +7130,21 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-docker": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+      "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+      "dev": true,
+      "bin": {
+        "is-docker": "cli.js"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -6944,6 +7207,24 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/is-inside-container": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
+      "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+      "dev": true,
+      "dependencies": {
+        "is-docker": "^3.0.0"
+      },
+      "bin": {
+        "is-inside-container": "cli.js"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/is-installed-globally": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz",
@@ -7206,6 +7487,33 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/is-wsl": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+      "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+      "dev": true,
+      "dependencies": {
+        "is-docker": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-wsl/node_modules/is-docker": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+      "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+      "dev": true,
+      "bin": {
+        "is-docker": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/isarray": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
@@ -9784,6 +10092,24 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/open": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz",
+      "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==",
+      "dev": true,
+      "dependencies": {
+        "default-browser": "^4.0.0",
+        "define-lazy-prop": "^3.0.0",
+        "is-inside-container": "^1.0.0",
+        "is-wsl": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/optionator": {
       "version": "0.9.3",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
@@ -10282,6 +10608,18 @@
         "url": "https://github.com/prettier/prettier?sponsor=1"
       }
     },
+    "node_modules/prettier-linter-helpers": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+      "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+      "dev": true,
+      "dependencies": {
+        "fast-diff": "^1.1.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
     "node_modules/prettier-plugin-tailwindcss": {
       "version": "0.5.6",
       "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.6.tgz",
@@ -11077,6 +11415,21 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/run-applescript": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz",
+      "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==",
+      "dev": true,
+      "dependencies": {
+        "execa": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/run-async": {
       "version": "2.4.1",
       "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
@@ -11720,6 +12073,22 @@
       "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
       "dev": true
     },
+    "node_modules/synckit": {
+      "version": "0.8.5",
+      "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz",
+      "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==",
+      "dev": true,
+      "dependencies": {
+        "@pkgr/utils": "^2.3.1",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/unts"
+      }
+    },
     "node_modules/tailwind-merge": {
       "version": "1.14.0",
       "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz",
@@ -11841,6 +12210,18 @@
         "readable-stream": "3"
       }
     },
+    "node_modules/titleize": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz",
+      "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/tmp": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
diff --git a/package.json b/package.json
index c359766c7f78ada52b5de72261d4290a64af0c10..c6c19cd69978bfb0f35c523f619b33883299f8d2 100644
--- a/package.json
+++ b/package.json
@@ -64,6 +64,7 @@
     "eslint-plugin-import": "^2.28.1",
     "eslint-plugin-jsx-a11y": "^6.7.1",
     "eslint-plugin-n": "^16.1.0",
+    "eslint-plugin-prettier": "^5.0.1",
     "eslint-plugin-promise": "^6.1.1",
     "eslint-plugin-react": "^7.33.2",
     "eslint-plugin-react-hooks": "^4.6.0",
diff --git a/prettier.config.js b/prettier.config.js
index fd41c7ea28115342ff628216fd2269ac7fb37e53..eec5dca5c09d171184a2d30ee3bd9e029b5e32c2 100644
--- a/prettier.config.js
+++ b/prettier.config.js
@@ -6,6 +6,7 @@ const config = {
   plugins: [import('prettier-plugin-tailwindcss')],
   tailwindConfig: './tailwind.config.ts',
   tailwindFunctions: ['twMerge'],
+  tabWidth: 2,
 };
 
 module.exports = config;
diff --git a/src/components/FunctionalArea/FunctionalArea.component.tsx b/src/components/FunctionalArea/FunctionalArea.component.tsx
index 784d7fdeb3f64fa5ee1e14e942fb3a556f558054..d38bcd195aa44fcd1c7f18c3dd0c2f43dc912797 100644
--- a/src/components/FunctionalArea/FunctionalArea.component.tsx
+++ b/src/components/FunctionalArea/FunctionalArea.component.tsx
@@ -1,6 +1,6 @@
-import { TopBar } from '@/components/FunctionalArea/TopBar';
-import { NavBar } from '@/components/FunctionalArea/NavBar';
 import { MapNavigation } from '@/components/FunctionalArea/MapNavigation';
+import { NavBar } from '@/components/FunctionalArea/NavBar';
+import { TopBar } from '@/components/FunctionalArea/TopBar';
 
 export const FunctionalArea = (): JSX.Element => (
   <>
diff --git a/src/components/Map/MapViewer/utils/config/getMapTileUrl.test.ts b/src/components/Map/MapViewer/utils/config/getMapTileUrl.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..91d8919adcef11d7f6b19d9291487a7eac13315d
--- /dev/null
+++ b/src/components/Map/MapViewer/utils/config/getMapTileUrl.test.ts
@@ -0,0 +1,34 @@
+import { BASE_MAP_IMAGES_URL } from '@/constants';
+import { getMapTileUrl } from './getMapTileUrl';
+
+describe('getMapTileUrl - util', () => {
+  describe('when projectDirectory is empty', () => {
+    it('should return empty value', () => {
+      const projectDirectory = undefined;
+      const currentBackgroundImagePath = 'currentBackgroundImagePath';
+      const result = '';
+
+      expect(
+        getMapTileUrl({
+          projectDirectory,
+          currentBackgroundImagePath,
+        }),
+      ).toBe(result);
+    });
+  });
+
+  describe('when all args are valid', () => {
+    it('should return correct value', () => {
+      const projectDirectory = 'directory';
+      const currentBackgroundImagePath = 'currentBackgroundImagePath';
+      const result = `${BASE_MAP_IMAGES_URL}/map_images/${projectDirectory}/${currentBackgroundImagePath}/{z}/{x}/{y}.PNG`;
+
+      expect(
+        getMapTileUrl({
+          projectDirectory,
+          currentBackgroundImagePath,
+        }),
+      ).toBe(result);
+    });
+  });
+});
diff --git a/src/components/Map/MapViewer/utils/config/getMapTileUrl.ts b/src/components/Map/MapViewer/utils/config/getMapTileUrl.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0ba5eed4032787a6c9b5d2b9d377a4b6248b821b
--- /dev/null
+++ b/src/components/Map/MapViewer/utils/config/getMapTileUrl.ts
@@ -0,0 +1,15 @@
+import { BASE_MAP_IMAGES_URL } from '@/constants';
+
+export const getMapTileUrl = ({
+  projectDirectory,
+  currentBackgroundImagePath,
+}: {
+  projectDirectory?: string;
+  currentBackgroundImagePath: string;
+}): string => {
+  if (!projectDirectory) {
+    return '';
+  }
+
+  return `${BASE_MAP_IMAGES_URL}/map_images/${projectDirectory}/${currentBackgroundImagePath}/{z}/{x}/{y}.PNG`;
+};
diff --git a/src/components/Map/MapViewer/utils/useOlMapConfig.test.ts b/src/components/Map/MapViewer/utils/config/useOlMapConfig.test.ts
similarity index 100%
rename from src/components/Map/MapViewer/utils/useOlMapConfig.test.ts
rename to src/components/Map/MapViewer/utils/config/useOlMapConfig.test.ts
diff --git a/src/components/Map/MapViewer/utils/useOlMapConfig.ts b/src/components/Map/MapViewer/utils/config/useOlMapConfig.ts
similarity index 73%
rename from src/components/Map/MapViewer/utils/useOlMapConfig.ts
rename to src/components/Map/MapViewer/utils/config/useOlMapConfig.ts
index 9ac87292dec1aea948fa26dc151d15aee6c2ca14..2a584d8764593a4cfaa69b8d98e33cfe3b172159 100644
--- a/src/components/Map/MapViewer/utils/useOlMapConfig.ts
+++ b/src/components/Map/MapViewer/utils/config/useOlMapConfig.ts
@@ -1,6 +1,8 @@
 /* eslint-disable no-magic-numbers */
 import { OPTIONS } from '@/constants/map';
+import { currentBackgroundImagePathSelector } from '@/redux/backgrounds/background.selectors';
 import { mapDataPositionSelector, mapDataSizeSelector } from '@/redux/map/map.selectors';
+import { projectDataSelector } from '@/redux/project/project.selectors';
 import { Point } from '@/types/map';
 import { usePointToProjection } from '@/utils/map/usePointToProjection';
 import { View } from 'ol';
@@ -9,6 +11,7 @@ import TileLayer from 'ol/layer/Tile';
 import { XYZ } from 'ol/source';
 import { useMemo } from 'react';
 import { useSelector } from 'react-redux';
+import { getMapTileUrl } from './getMapTileUrl';
 
 interface UseOlMapConfigResult {
   view: View;
@@ -18,6 +21,8 @@ interface UseOlMapConfigResult {
 export const useOlMapConfig = (): UseOlMapConfigResult => {
   const mapPosition = useSelector(mapDataPositionSelector);
   const mapSize = useSelector(mapDataSizeSelector);
+  const currentBackgroundImagePath = useSelector(currentBackgroundImagePathSelector);
+  const project = useSelector(projectDataSelector);
   const pointToProjection = usePointToProjection();
 
   const center = useMemo(() => {
@@ -40,19 +45,18 @@ export const useOlMapConfig = (): UseOlMapConfigResult => {
   );
 
   const tileLayer = useMemo(
-    () =>
+    (): TileLayer<XYZ> =>
       new TileLayer({
         visible: true,
         source: new XYZ({
-          url: 'https://pdmap.uni.lu/map_images/9d4911bdeeea752f076e57a91d9b1f45/_nested0/{z}/{x}/{y}.PNG',
-          // TODO: build url from data in redux
+          url: getMapTileUrl({ projectDirectory: project?.directory, currentBackgroundImagePath }),
           maxZoom: mapSize.maxZoom,
           minZoom: mapSize.minZoom,
           tileSize: mapSize.tileSize,
           wrapX: OPTIONS.wrapXInTileLayer,
         }),
       }),
-    [mapSize],
+    [mapSize, currentBackgroundImagePath, project?.directory],
   );
 
   return {
diff --git a/src/components/Map/MapViewer/utils/useOlMap.ts b/src/components/Map/MapViewer/utils/useOlMap.ts
index b7525d19859c5db6fefef9f9a8899b134bb91c7e..ca82591af5077a935f1a5833451885696e70a81c 100644
--- a/src/components/Map/MapViewer/utils/useOlMap.ts
+++ b/src/components/Map/MapViewer/utils/useOlMap.ts
@@ -1,8 +1,7 @@
 import Map from 'ol/Map';
 import React, { MutableRefObject, useEffect, useState } from 'react';
 import { MapInstance } from '../MapViewer.types';
-import { useOlMapConfig } from './useOlMapConfig';
-import { useOlMapInit } from './useOlMapInit';
+import { useOlMapConfig } from './config/useOlMapConfig';
 
 interface UseOlMapInput {
   target?: HTMLElement;
@@ -18,7 +17,6 @@ export const useOlMap: UseOlMap = ({ target } = {}) => {
   const mapRef = React.useRef<null | HTMLDivElement>(null);
   const [mapInstance, setMapInstance] = useState<MapInstance>(undefined);
   const mapConfig = useOlMapConfig();
-  useOlMapInit();
 
   useEffect(() => {
     // checking if innerHTML is empty due to possibility of target element cloning by openlayers map instance
diff --git a/src/components/Map/MapViewer/utils/useOlMapInit.test.ts b/src/components/Map/MapViewer/utils/useOlMapInit.test.ts
deleted file mode 100644
index 100eaaee71d7afc9ab80ffdd5f711ac10d306e38..0000000000000000000000000000000000000000
--- a/src/components/Map/MapViewer/utils/useOlMapInit.test.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-describe.skip('useOlMapConfig - util', () => {
-  // TODO: tests
-  // everything is mocked in the file, so we need to firstly wait for module API connection
-
-  it('noop', () => {
-    // eslint-disable-next-line no-magic-numbers
-    expect(1).toEqual(1);
-  });
-});
diff --git a/src/components/Map/MapViewer/utils/useOlMapInit.ts b/src/components/Map/MapViewer/utils/useOlMapInit.ts
deleted file mode 100644
index 335758e7f5ac6d9f7c7fed938b7389de3b41605b..0000000000000000000000000000000000000000
--- a/src/components/Map/MapViewer/utils/useOlMapInit.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-/* eslint-disable no-magic-numbers */
-// TODO: Remove mocks and implement communication with API
-
-import { DEFAULT_ZOOM } from '@/constants/map';
-import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
-import { setMapData } from '@/redux/map/map.slice';
-import { useCallback, useEffect } from 'react';
-
-const MOCK_PROJECT = {
-  version: '',
-  disease: {
-    link: 'http://id.nlm.nih.gov/mesh/D010300',
-    type: 'MESH_2012',
-    resource: 'D010300',
-    id: 3211856,
-    annotatorClassName: '',
-  },
-  idObject: 6065,
-  status: 'Ok',
-  directory: '9d4911bdeeea752f076e57a91d9b1f45',
-  progress: 100,
-  notifyEmail: 'ewa.smula@uni.lu',
-  logEntries: true,
-  name: "Parkinson's disease map",
-  sharedInMinervaNet: true,
-  owner: 'ewa.smula',
-  projectId: 'pd_map_winter_23',
-  creationDate: '2023-02-15 16:35:11',
-  mapCanvasType: 'OPEN_LAYERS',
-};
-
-const MOCK_MODEL = {
-  idObject: 5053,
-  width: 26779.25,
-  height: 13503,
-  defaultCenterX: null,
-  defaultCenterY: null,
-  description: '',
-  name: 'Core PD map',
-  defaultZoomLevel: null,
-  tileSize: 256,
-  references: [],
-  authors: [],
-  creationDate: null,
-  modificationDates: [],
-  minZoom: 2,
-  maxZoom: 9,
-};
-
-export const useOlMapInit = (): void => {
-  const dispatch = useAppDispatch();
-
-  const mapInit = useCallback(() => {
-    dispatch(
-      setMapData({
-        meshId: MOCK_PROJECT.disease.resource,
-        modelId: MOCK_MODEL.idObject,
-        size: {
-          width: MOCK_MODEL.width,
-          height: MOCK_MODEL.height,
-          tileSize: MOCK_MODEL.tileSize,
-          minZoom: MOCK_MODEL.minZoom,
-          maxZoom: MOCK_MODEL.maxZoom,
-        },
-        position: {
-          x: MOCK_MODEL.defaultCenterX || MOCK_MODEL.width / 2,
-          y: MOCK_MODEL.defaultCenterY || MOCK_MODEL.height / 2,
-          z: MOCK_MODEL.defaultZoomLevel || DEFAULT_ZOOM,
-        },
-      }),
-    );
-  }, [dispatch]);
-
-  useEffect(() => mapInit(), [mapInit]);
-};
diff --git a/src/components/SPA/MinervaSPA.component.tsx b/src/components/SPA/MinervaSPA.component.tsx
index c9b5381584b0a04df6a647779e15e82e4bff4962..856e3487b746b504123995ebdf46329736f81787 100644
--- a/src/components/SPA/MinervaSPA.component.tsx
+++ b/src/components/SPA/MinervaSPA.component.tsx
@@ -1,10 +1,8 @@
+import { FunctionalArea } from '@/components/FunctionalArea';
+import { Map } from '@/components/Map';
 import { Manrope } from '@next/font/google';
 import { twMerge } from 'tailwind-merge';
-import { Map } from '@/components/Map';
-import { FunctionalArea } from '@/components/FunctionalArea';
-import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
-import { useEffect } from 'react';
-import { getModels } from '@/redux/models/models.thunks';
+import { useInitializeStore } from './utils/useInitializeStore';
 
 const manrope = Manrope({
   variable: '--font-manrope',
@@ -14,11 +12,7 @@ const manrope = Manrope({
 });
 
 export const MinervaSPA = (): JSX.Element => {
-  const dispatch = useAppDispatch();
-
-  useEffect(() => {
-    dispatch(getModels());
-  }, [dispatch]);
+  useInitializeStore();
 
   return (
     <div className={twMerge('relative', manrope.variable)}>
diff --git a/src/components/SPA/utils/useInitializeStore.test.ts b/src/components/SPA/utils/useInitializeStore.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d1b7da08d0e98f26cc0fc4403efc1c333dfa877b
--- /dev/null
+++ b/src/components/SPA/utils/useInitializeStore.test.ts
@@ -0,0 +1,85 @@
+import { PROJECT_ID } from '@/constants';
+import { backgroundsFixture } from '@/models/fixtures/backgroundsFixture';
+import { modelsFixture } from '@/models/fixtures/modelsFixture';
+import { overlaysFixture } from '@/models/fixtures/overlaysFixture';
+import { projectFixture } from '@/models/fixtures/projectFixture';
+import { apiPath } from '@/redux/apiPath';
+import { backgroundsDataSelector } from '@/redux/backgrounds/background.selectors';
+import { modelsDataSelector } from '@/redux/models/models.selectors';
+import { overlaysDataSelector } from '@/redux/overlays/overlays.selectors';
+import { projectDataSelector } from '@/redux/project/project.selectors';
+import { initDataLoadingInitialized } from '@/redux/root/init.selectors';
+import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
+import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
+import { renderHook, waitFor } from '@testing-library/react';
+import { HttpStatusCode } from 'axios';
+import * as hook from './useInitializeStore';
+
+const mockedAxiosClient = mockNetworkResponse();
+
+describe('useInitializeStore - hook', () => {
+  describe('when fired', () => {
+    beforeAll(() => {
+      mockedAxiosClient.onGet(apiPath.getModelsString()).reply(HttpStatusCode.Ok, modelsFixture);
+      mockedAxiosClient
+        .onGet(apiPath.getAllOverlaysByProjectIdQuery(PROJECT_ID, { publicOverlay: true }))
+        .reply(HttpStatusCode.Ok, overlaysFixture);
+      mockedAxiosClient
+        .onGet(apiPath.getProjectById(PROJECT_ID))
+        .reply(HttpStatusCode.Ok, projectFixture);
+      mockedAxiosClient
+        .onGet(apiPath.getAllBackgroundsByProjectIdQuery(PROJECT_ID))
+        .reply(HttpStatusCode.Ok, backgroundsFixture);
+    });
+
+    it('should fetch project data in store', async () => {
+      const { Wrapper, store } = getReduxWrapperWithStore();
+      renderHook(() => hook.useInitializeStore(), { wrapper: Wrapper });
+
+      await waitFor(() => {
+        const data = projectDataSelector(store.getState());
+        expect(data).toEqual(projectFixture);
+      });
+    });
+
+    it('should fetch backgrounds data in store', async () => {
+      const { Wrapper, store } = getReduxWrapperWithStore();
+      renderHook(() => hook.useInitializeStore(), { wrapper: Wrapper });
+
+      await waitFor(() => {
+        const data = backgroundsDataSelector(store.getState());
+        expect(data).toEqual(backgroundsFixture);
+      });
+    });
+
+    it('should fetch overlays data in store', async () => {
+      const { Wrapper, store } = getReduxWrapperWithStore();
+      renderHook(() => hook.useInitializeStore(), { wrapper: Wrapper });
+
+      await waitFor(() => {
+        const data = overlaysDataSelector(store.getState());
+        expect(data).toEqual(overlaysFixture);
+      });
+    });
+
+    it('should fetch models data in store', async () => {
+      const { Wrapper, store } = getReduxWrapperWithStore();
+      renderHook(() => hook.useInitializeStore(), { wrapper: Wrapper });
+
+      await waitFor(() => {
+        const data = modelsDataSelector(store.getState());
+        expect(data).toEqual(modelsFixture);
+      });
+    });
+
+    it('should use valid initialize value', () => {
+      const { Wrapper, store } = getReduxWrapperWithStore();
+      const initializedeBefore = initDataLoadingInitialized(store.getState());
+      renderHook(() => hook.useInitializeStore(), { wrapper: Wrapper });
+      const initializedAfter = initDataLoadingInitialized(store.getState());
+
+      expect(initializedeBefore).toBe(false);
+      expect(initializedAfter).toBe(true);
+    });
+  });
+});
diff --git a/src/components/SPA/utils/useInitializeStore.ts b/src/components/SPA/utils/useInitializeStore.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7e2fb0af61681ddf2f73cefa1571580fd8b36022
--- /dev/null
+++ b/src/components/SPA/utils/useInitializeStore.ts
@@ -0,0 +1,29 @@
+import { PROJECT_ID } from '@/constants';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { initMapData } from '@/redux/map/map.thunks';
+import { getProjectById } from '@/redux/project/project.thunks';
+import { initDataLoadingInitialized } from '@/redux/root/init.selectors';
+import { AppDispatch } from '@/redux/store';
+import { useEffect } from 'react';
+import { useSelector } from 'react-redux';
+
+/* prettier-ignore */
+export const getInitStoreData =
+  () =>
+    (dispatch: AppDispatch): void => {
+      dispatch(getProjectById(PROJECT_ID));
+      dispatch(initMapData());
+    };
+
+export const useInitializeStore = (): void => {
+  const dispatch = useAppDispatch();
+  const isInitialized = useSelector(initDataLoadingInitialized);
+
+  useEffect(() => {
+    if (isInitialized) {
+      return;
+    }
+
+    dispatch(getInitStoreData());
+  }, [dispatch, isInitialized]);
+};
diff --git a/src/models/fixtures/modelsFixture.ts b/src/models/fixtures/modelsFixture.ts
index 2eb9109dbac56042aafff5dd0a25a49f61be2c64..91bc15d71cd0b9d631fe2ceb68aa3f09ee9e557d 100644
--- a/src/models/fixtures/modelsFixture.ts
+++ b/src/models/fixtures/modelsFixture.ts
@@ -8,3 +8,8 @@ export const modelsFixture = createFixture(z.array(mapModelSchema), {
   seed: ZOD_SEED,
   array: { min: 3, max: 3 },
 });
+
+export const singleModelFixture = createFixture(mapModelSchema, {
+  seed: ZOD_SEED,
+  array: { min: 3, max: 3 },
+});
diff --git a/src/redux/backgrounds/background.selectors.ts b/src/redux/backgrounds/background.selectors.ts
index 01b6ed81bf434fb40ee56ec1784e3365ea8c2666..16b233972a19c5eb7edc916f74c716c41550e48b 100644
--- a/src/redux/backgrounds/background.selectors.ts
+++ b/src/redux/backgrounds/background.selectors.ts
@@ -6,7 +6,7 @@ export const backgroundsSelector = createSelector(rootSelector, state => state.b
 
 export const backgroundsDataSelector = createSelector(
   backgroundsSelector,
-  backgrounds => backgrounds.data || [],
+  backgrounds => backgrounds?.data || [],
 );
 
 export const currentBackgroundSelector = createSelector(
diff --git a/src/redux/map/map.constants.ts b/src/redux/map/map.constants.ts
index a6307772f6c81239b02d58d839c0e050112fa761..ba88a03811be7e0e0d08fe58bb13ceaff6f51d65 100644
--- a/src/redux/map/map.constants.ts
+++ b/src/redux/map/map.constants.ts
@@ -26,3 +26,5 @@ export const MAP_DATA_INITIAL_STATE: MapData = {
     maxZoom: DEFAULT_MAX_ZOOM,
   },
 };
+
+export const MIDDLEWARE_ALLOWED_ACTIONS: string[] = ['map/setMapData', 'map/initMapData'];
diff --git a/src/redux/map/map.reducers.ts b/src/redux/map/map.reducers.ts
index 95d763254f76bcea970ec8f428e2082e80fba5d2..962704b36070701d2e763147d57f2f99c8751ae8 100644
--- a/src/redux/map/map.reducers.ts
+++ b/src/redux/map/map.reducers.ts
@@ -1,9 +1,22 @@
-import { PayloadAction } from '@reduxjs/toolkit';
-import { MapData, MapState } from './map.types';
+import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
+import { initMapData } from './map.thunks';
+import { MapState, SetMapDataAction } from './map.types';
 
-export const setMapDataReducer = (
-  state: MapState,
-  action: PayloadAction<Partial<MapData> | undefined>,
-): void => {
+export const setMapDataReducer = (state: MapState, action: SetMapDataAction): void => {
   state.data = { ...state.data, ...action.payload };
 };
+
+export const getMapReducers = (builder: ActionReducerMapBuilder<MapState>): void => {
+  builder.addCase(initMapData.pending, state => {
+    state.loading = 'pending';
+  });
+  builder.addCase(initMapData.fulfilled, (state, action) => {
+    const payload = action.payload || {};
+    state.data = { ...state.data, ...payload };
+    state.loading = 'succeeded';
+  });
+  builder.addCase(initMapData.rejected, state => {
+    state.loading = 'failed';
+    // TODO to discuss manage state of failure
+  });
+};
diff --git a/src/redux/map/map.slice.ts b/src/redux/map/map.slice.ts
index 62b95df2be671ec12d4dec2341978c586ef235c2..3565dc2500f9c45182b563f636f16fdf5b47f4a9 100644
--- a/src/redux/map/map.slice.ts
+++ b/src/redux/map/map.slice.ts
@@ -1,6 +1,6 @@
 import { createSlice } from '@reduxjs/toolkit';
 import { MAP_DATA_INITIAL_STATE } from './map.constants';
-import { setMapDataReducer } from './map.reducers';
+import { getMapReducers, setMapDataReducer } from './map.reducers';
 import { MapState } from './map.types';
 
 const initialState: MapState = {
@@ -15,6 +15,9 @@ const mapSlice = createSlice({
   reducers: {
     setMapData: setMapDataReducer,
   },
+  extraReducers: builder => {
+    getMapReducers(builder);
+  },
 });
 
 export const { setMapData } = mapSlice.actions;
diff --git a/src/redux/map/map.thunks.test.ts b/src/redux/map/map.thunks.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b14e744dab577afaa1b8f25b56d76f2162053908
--- /dev/null
+++ b/src/redux/map/map.thunks.test.ts
@@ -0,0 +1,87 @@
+import { PROJECT_ID } from '@/constants';
+import { backgroundsFixture } from '@/models/fixtures/backgroundsFixture';
+import { modelsFixture } from '@/models/fixtures/modelsFixture';
+import { overlaysFixture } from '@/models/fixtures/overlaysFixture';
+import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
+import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
+import { HttpStatusCode } from 'axios';
+import { apiPath } from '../apiPath';
+import { backgroundsDataSelector } from '../backgrounds/background.selectors';
+import { modelsDataSelector } from '../models/models.selectors';
+import { overlaysDataSelector } from '../overlays/overlays.selectors';
+import { AppDispatch, StoreType } from '../store';
+import { initMapData } from './map.thunks';
+import { InitMapDataActionPayload } from './map.types';
+
+const mockedAxiosClient = mockNetworkResponse();
+
+describe('map thunks', () => {
+  describe('initMapData - thunk', () => {
+    describe('when API is returning valid data', () => {
+      let store = {} as StoreType;
+      let payload = {} as InitMapDataActionPayload;
+
+      beforeAll(async () => {
+        mockedAxiosClient.resetHandlers();
+        mockedAxiosClient.onGet(apiPath.getModelsString()).reply(HttpStatusCode.Ok, modelsFixture);
+        mockedAxiosClient
+          .onGet(apiPath.getAllOverlaysByProjectIdQuery(PROJECT_ID, { publicOverlay: true }))
+          .reply(HttpStatusCode.Ok, overlaysFixture);
+        mockedAxiosClient
+          .onGet(apiPath.getAllBackgroundsByProjectIdQuery(PROJECT_ID))
+          .reply(HttpStatusCode.Ok, backgroundsFixture);
+
+        store = getReduxWrapperWithStore().store;
+        const dispatch = store.dispatch as AppDispatch;
+        payload = (await dispatch(initMapData())).payload as InitMapDataActionPayload;
+      });
+
+      it('should fetch backgrounds data in store', async () => {
+        const data = backgroundsDataSelector(store.getState());
+        expect(data).toEqual(backgroundsFixture);
+      });
+
+      it('should fetch overlays data in store', async () => {
+        const data = overlaysDataSelector(store.getState());
+        expect(data).toEqual(overlaysFixture);
+      });
+
+      it('should fetch models data in store', async () => {
+        const data = modelsDataSelector(store.getState());
+        expect(data).toEqual(modelsFixture);
+      });
+
+      it('should return valid payload', () => {
+        const FIRST = 0;
+
+        expect(payload).toMatchObject({
+          modelId: modelsFixture[FIRST].idObject,
+          backgroundId: backgroundsFixture[FIRST].id,
+        });
+      });
+    });
+
+    describe('when API is returning empty array', () => {
+      let store = {} as StoreType;
+      let payload = {} as InitMapDataActionPayload;
+
+      beforeEach(async () => {
+        mockedAxiosClient.onGet(apiPath.getModelsString()).reply(HttpStatusCode.Ok, []);
+        mockedAxiosClient
+          .onGet(apiPath.getAllOverlaysByProjectIdQuery(PROJECT_ID, { publicOverlay: true }))
+          .reply(HttpStatusCode.Ok, []);
+        mockedAxiosClient
+          .onGet(apiPath.getAllBackgroundsByProjectIdQuery(PROJECT_ID))
+          .reply(HttpStatusCode.Ok, []);
+
+        store = getReduxWrapperWithStore().store;
+        const dispatch = store.dispatch as AppDispatch;
+        payload = (await dispatch(initMapData())).payload as InitMapDataActionPayload;
+      });
+
+      it('should return empty payload', () => {
+        expect(payload).toStrictEqual({});
+      });
+    });
+  });
+});
diff --git a/src/redux/map/map.thunks.ts b/src/redux/map/map.thunks.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e6d8fef456ef2f0bda91c46d99cd7c45347c192e
--- /dev/null
+++ b/src/redux/map/map.thunks.ts
@@ -0,0 +1,41 @@
+import { PROJECT_ID } from '@/constants';
+import { createAsyncThunk } from '@reduxjs/toolkit';
+import { backgroundsDataSelector } from '../backgrounds/background.selectors';
+import { getAllBackgroundsByProjectId } from '../backgrounds/backgrounds.thunks';
+import { modelsDataSelector } from '../models/models.selectors';
+import { getModels } from '../models/models.thunks';
+import { getAllPublicOverlaysByProjectId } from '../overlays/overlays.thunks';
+import type { AppDispatch, RootState } from '../store';
+import { InitMapDataActionPayload } from './map.types';
+
+const getPayloadFromState = (state: RootState): InitMapDataActionPayload => {
+  const FIRST = 0;
+  const models = modelsDataSelector(state);
+  const backgrounds = backgroundsDataSelector(state);
+  const modelId = models?.[FIRST]?.idObject;
+  const backgroundId = backgrounds?.[FIRST]?.id;
+
+  if (!modelId || !backgroundId) {
+    return {};
+  }
+
+  return {
+    modelId,
+    backgroundId,
+  };
+};
+
+export const initMapData = createAsyncThunk<
+  InitMapDataActionPayload,
+  void,
+  { dispatch: AppDispatch; state: RootState }
+>('map/initMapData', async (_, { dispatch, getState }): Promise<InitMapDataActionPayload> => {
+  await Promise.all([
+    dispatch(getAllBackgroundsByProjectId(PROJECT_ID)),
+    dispatch(getAllPublicOverlaysByProjectId(PROJECT_ID)),
+    dispatch(getModels()),
+  ]);
+
+  const state = getState();
+  return getPayloadFromState(state);
+});
diff --git a/src/redux/map/map.types.ts b/src/redux/map/map.types.ts
index ac46d776e601f8ae23c18c2e0dd58de9edb8fe75..7051691ed57d56fe19e934c18e8fa020a9f9246e 100644
--- a/src/redux/map/map.types.ts
+++ b/src/redux/map/map.types.ts
@@ -1,5 +1,6 @@
 import { FetchDataState } from '@/types/fetchDataState';
 import { Point } from '@/types/map';
+import { PayloadAction } from '@reduxjs/toolkit';
 
 export interface MapSize {
   width: number;
@@ -24,3 +25,15 @@ export type MapData = {
 };
 
 export type MapState = FetchDataState<MapData, MapData>;
+
+export type SetMapDataActionPayload = Partial<MapData> | undefined;
+
+export type SetMapDataAction = PayloadAction<SetMapDataActionPayload>;
+
+export type InitMapDataActionPayload = { modelId: number; backgroundId: number } | object;
+
+export type InitMapDataAction = PayloadAction<SetMapDataAction>;
+
+export type MiddlewareAllowedAction = PayloadAction<
+  SetMapDataActionPayload | InitMapDataActionPayload
+>;
diff --git a/src/redux/map/middleware/checkIfIsMapUpdateActionValid.test.ts b/src/redux/map/middleware/checkIfIsMapUpdateActionValid.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aa889249020931981df2631afd7550950ced7dbf
--- /dev/null
+++ b/src/redux/map/middleware/checkIfIsMapUpdateActionValid.test.ts
@@ -0,0 +1,68 @@
+import { RootState } from '@/redux/store';
+import { Loading } from '@/types/loadingState';
+import { MAP_DATA_INITIAL_STATE, MIDDLEWARE_ALLOWED_ACTIONS } from '../map.constants';
+import { SetMapDataAction } from '../map.types';
+import { checkIfIsMapUpdateActionValid } from './checkIfIsMapUpdateActionValid';
+
+const state: Pick<RootState, 'map'> = {
+  map: {
+    data: {
+      ...MAP_DATA_INITIAL_STATE,
+      modelId: 2137,
+    },
+    loading: 'idle' as Loading,
+    error: { name: '', message: '' },
+  },
+};
+
+describe('checkIfIsActionValid - util', () => {
+  describe('when action payload has model id equal to current', () => {
+    const modelId = 2137;
+
+    it.each(MIDDLEWARE_ALLOWED_ACTIONS)('should return false', actionType => {
+      expect(
+        checkIfIsMapUpdateActionValid(
+          {
+            type: actionType,
+            payload: {
+              modelId,
+            },
+          } as SetMapDataAction,
+          state as RootState,
+        ),
+      ).toBe(false);
+    });
+  });
+
+  describe('when action payload has model id different than current', () => {
+    const modelId = 997;
+
+    it.each(MIDDLEWARE_ALLOWED_ACTIONS)('should return true', actionType => {
+      expect(
+        checkIfIsMapUpdateActionValid(
+          {
+            type: actionType,
+            payload: {
+              modelId,
+            },
+          } as SetMapDataAction,
+          state as RootState,
+        ),
+      ).toBe(true);
+    });
+  });
+
+  describe('when action payload has NOT model id', () => {
+    it.each(MIDDLEWARE_ALLOWED_ACTIONS)('should return true', actionType => {
+      expect(
+        checkIfIsMapUpdateActionValid(
+          {
+            type: actionType,
+            payload: {},
+          } as SetMapDataAction,
+          state as RootState,
+        ),
+      ).toBe(true);
+    });
+  });
+});
diff --git a/src/redux/map/middleware/checkIfIsMapUpdateActionValid.ts b/src/redux/map/middleware/checkIfIsMapUpdateActionValid.ts
new file mode 100644
index 0000000000000000000000000000000000000000..19379e967a2afcb41d9d21f646b210d2361519ff
--- /dev/null
+++ b/src/redux/map/middleware/checkIfIsMapUpdateActionValid.ts
@@ -0,0 +1,11 @@
+import type { RootState } from '@/redux/store';
+import { Action } from '@reduxjs/toolkit';
+import { getModelIdFromAction } from './getModelIdFromAction';
+
+export const checkIfIsMapUpdateActionValid = (action: Action, state: RootState): boolean => {
+  const { modelId: currentModelId } = state.map.data;
+  const payloadModelId = getModelIdFromAction(action);
+  const isModelIdTheSame = currentModelId === payloadModelId;
+
+  return !isModelIdTheSame;
+};
diff --git a/src/redux/map/middleware/getModelIdFromAction.ts b/src/redux/map/middleware/getModelIdFromAction.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c7e786ae947b5faa7328e540b3c5f003cb30441b
--- /dev/null
+++ b/src/redux/map/middleware/getModelIdFromAction.ts
@@ -0,0 +1,10 @@
+import { Action } from '@reduxjs/toolkit';
+
+export const getModelIdFromAction = (action: Action): number | null => {
+  try {
+    const payload = 'payload' in action ? (action.payload as object) : {};
+    return 'modelId' in payload ? (payload.modelId as number) : null;
+  } catch {
+    return null;
+  }
+};
diff --git a/src/redux/map/middleware/getUpdatedModel.test.ts b/src/redux/map/middleware/getUpdatedModel.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..65cd7f25616a14c8722c967f7bf850fd70174629
--- /dev/null
+++ b/src/redux/map/middleware/getUpdatedModel.test.ts
@@ -0,0 +1,53 @@
+/* eslint-disable no-magic-numbers */
+import { modelsFixture } from '@/models/fixtures/modelsFixture';
+import { RootState } from '@/redux/store';
+import { MIDDLEWARE_ALLOWED_ACTIONS } from '../map.constants';
+import { getUpdatedModel } from './getUpdatedModel';
+
+const state: Pick<RootState, 'models'> = {
+  models: {
+    data: modelsFixture,
+    loading: 'idle',
+    error: { name: '', message: '' },
+  },
+};
+
+describe('getUpdatedModel - util', () => {
+  describe('when payload has valid modelId', () => {
+    const model = modelsFixture[0];
+    const action = {
+      type: MIDDLEWARE_ALLOWED_ACTIONS[0],
+      payload: {
+        modelId: model.idObject,
+      },
+    };
+
+    it('returns model object', () => {
+      expect(getUpdatedModel(action, state as RootState)).toStrictEqual(model);
+    });
+  });
+
+  describe('when payload has invalid modelId', () => {
+    const action = {
+      type: MIDDLEWARE_ALLOWED_ACTIONS[0],
+      payload: {
+        modelId: null,
+      },
+    };
+
+    it('returns undefined', () => {
+      expect(getUpdatedModel(action, state as RootState)).toStrictEqual(undefined);
+    });
+  });
+
+  describe('when payload does not have modelId', () => {
+    const action = {
+      type: MIDDLEWARE_ALLOWED_ACTIONS[0],
+      payload: {},
+    };
+
+    it('returns undefined', () => {
+      expect(getUpdatedModel(action, state as RootState)).toStrictEqual(undefined);
+    });
+  });
+});
diff --git a/src/redux/map/middleware/getUpdatedModel.ts b/src/redux/map/middleware/getUpdatedModel.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4302c5525250f2d1fe9f980a5359d897ab3f3ef6
--- /dev/null
+++ b/src/redux/map/middleware/getUpdatedModel.ts
@@ -0,0 +1,12 @@
+import { modelsDataSelector } from '@/redux/models/models.selectors';
+import type { RootState } from '@/redux/store';
+import { MapModel } from '@/types/models';
+import { Action } from '@reduxjs/toolkit';
+import { getModelIdFromAction } from './getModelIdFromAction';
+
+export const getUpdatedModel = (action: Action, state: RootState): MapModel | undefined => {
+  const models = modelsDataSelector(state);
+  const payloadModelId = getModelIdFromAction(action);
+
+  return models.find(model => model.idObject === payloadModelId);
+};
diff --git a/src/redux/map/middleware/map.middleware.test.ts b/src/redux/map/middleware/map.middleware.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c821eeda0b149f11cfaab37b30feb5aa3e5061ce
--- /dev/null
+++ b/src/redux/map/middleware/map.middleware.test.ts
@@ -0,0 +1,134 @@
+/* eslint-disable no-magic-numbers */
+import { modelsFixture } from '@/models/fixtures/modelsFixture';
+import { Loading } from '@/types/loadingState';
+import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
+import { Action } from '@reduxjs/toolkit';
+import { MAP_DATA_INITIAL_STATE, MIDDLEWARE_ALLOWED_ACTIONS } from '../map.constants';
+import * as setMapData from '../map.slice';
+import * as checkIfIsMapUpdateActionValid from './checkIfIsMapUpdateActionValid';
+import * as getUpdatedModel from './getUpdatedModel';
+import { mapDataMiddlewareListener } from './map.middleware';
+
+jest.mock('./checkIfIsMapUpdateActionValid', () => {
+  return {
+    __esModule: true,
+    ...jest.requireActual('./checkIfIsMapUpdateActionValid'),
+  };
+});
+
+jest.mock('./getUpdatedModel', () => {
+  return {
+    __esModule: true,
+    ...jest.requireActual('./getUpdatedModel'),
+  };
+});
+
+jest.mock('./getUpdatedModel', () => {
+  return {
+    __esModule: true,
+    ...jest.requireActual('./getUpdatedModel'),
+  };
+});
+
+jest.mock('../map.slice', () => {
+  return {
+    __esModule: true,
+    ...jest.requireActual('../map.slice'),
+  };
+});
+
+const defaultSliceState = {
+  data: [],
+  loading: 'idle' as Loading,
+  error: { name: '', message: '' },
+};
+
+const checkIfIsMapUpdateActionValidSpy = jest.spyOn(
+  checkIfIsMapUpdateActionValid,
+  'checkIfIsMapUpdateActionValid',
+);
+const getUpdatedModelSpy = jest.spyOn(getUpdatedModel, 'getUpdatedModel');
+const setMapDataSpy = jest.spyOn(setMapData, 'setMapData');
+
+const { store } = getReduxWrapperWithStore({
+  map: {
+    ...defaultSliceState,
+    data: {
+      ...MAP_DATA_INITIAL_STATE,
+      modelId: modelsFixture[0].idObject,
+    },
+  },
+  models: {
+    ...defaultSliceState,
+    data: modelsFixture,
+  },
+});
+
+const doDispatch = jest.fn();
+const doGetState = store.getState;
+
+const handler = async (action: Action): Promise<void> =>
+  mapDataMiddlewareListener(action, {
+    dispatch: doDispatch,
+    getOriginalState: doGetState,
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  } as any);
+
+describe('map middleware', () => {
+  describe('on listen', () => {
+    afterEach(() => {
+      jest.clearAllMocks();
+    });
+
+    describe('when model is valid and different than current', () => {
+      it.each(MIDDLEWARE_ALLOWED_ACTIONS)('should dispatch setMapData', async actionType => {
+        const model = modelsFixture[1];
+
+        const action = {
+          payload: {
+            modelId: model.idObject,
+          },
+          type: actionType,
+        };
+
+        await handler(action);
+        expect(checkIfIsMapUpdateActionValidSpy).toHaveLastReturnedWith(true);
+        expect(getUpdatedModelSpy).toHaveLastReturnedWith(model);
+        expect(setMapDataSpy).toBeCalled();
+      });
+    });
+
+    describe('when model is valid and identical with current', () => {
+      it.each(MIDDLEWARE_ALLOWED_ACTIONS)('should NOT dispatch setMapData', async actionType => {
+        const model = modelsFixture[0];
+        const action = {
+          payload: {
+            modelId: model.idObject,
+          },
+          type: actionType,
+        };
+
+        await handler(action);
+        expect(checkIfIsMapUpdateActionValidSpy).toHaveLastReturnedWith(false);
+        expect(getUpdatedModelSpy).toHaveLastReturnedWith(model);
+        expect(setMapDataSpy).not.toBeCalled();
+      });
+    });
+
+    describe('when model is NOT valid', () => {
+      it.each(MIDDLEWARE_ALLOWED_ACTIONS)('should NOT dispatch setMapData', async actionType => {
+        const action = {
+          payload: {
+            modelId: null,
+          },
+          type: actionType,
+        };
+
+        await handler(action);
+        expect(checkIfIsMapUpdateActionValidSpy).toHaveLastReturnedWith(true);
+        expect(getUpdatedModelSpy).toHaveLastReturnedWith(undefined);
+        expect(setMapDataSpy).not.toBeCalled();
+      });
+    });
+  });
+});
diff --git a/src/redux/map/middleware/map.middleware.ts b/src/redux/map/middleware/map.middleware.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0221d9f6a2705f68cad2a51b2c1c24c7b2cb7565
--- /dev/null
+++ b/src/redux/map/middleware/map.middleware.ts
@@ -0,0 +1,37 @@
+import type { AppListenerEffectAPI, AppStartListening } from '@/redux/store';
+import { getUpdatedMapData } from '@/utils/map/getUpdatedMapData';
+import { Action, createListenerMiddleware } from '@reduxjs/toolkit';
+import { setMapData } from '../map.slice';
+import { initMapData } from '../map.thunks';
+import { checkIfIsMapUpdateActionValid } from './checkIfIsMapUpdateActionValid';
+import { getUpdatedModel } from './getUpdatedModel';
+
+export const mapListenerMiddleware = createListenerMiddleware();
+
+const startListening = mapListenerMiddleware.startListening as AppStartListening;
+
+export const mapDataMiddlewareListener = async (
+  action: Action,
+  { getOriginalState, dispatch }: AppListenerEffectAPI,
+): Promise<void> => {
+  const state = getOriginalState();
+  const updatedModel = getUpdatedModel(action, state);
+  const isActionValid = checkIfIsMapUpdateActionValid(action, state);
+
+  if (!updatedModel || !isActionValid) {
+    return;
+  }
+
+  const updatedMapData = getUpdatedMapData({ model: updatedModel });
+  dispatch(setMapData(updatedMapData));
+};
+
+startListening({
+  actionCreator: initMapData.fulfilled,
+  effect: mapDataMiddlewareListener,
+});
+
+startListening({
+  type: 'map/setMapData',
+  effect: mapDataMiddlewareListener,
+});
diff --git a/src/redux/models/models.selectors.ts b/src/redux/models/models.selectors.ts
index 1ae57c7e2e962dc33485300be74d02a61e888d91..8a9478b171d44dfc00afb4a23f34517b14ab6d33 100644
--- a/src/redux/models/models.selectors.ts
+++ b/src/redux/models/models.selectors.ts
@@ -4,7 +4,7 @@ import { mapDataSelector } from '../map/map.selectors';
 
 export const modelsSelector = createSelector(rootSelector, state => state.models);
 
-export const modelsDataSelector = createSelector(modelsSelector, models => models.data || []);
+export const modelsDataSelector = createSelector(modelsSelector, models => models?.data || []);
 
 export const currentModelSelector = createSelector(
   modelsDataSelector,
diff --git a/src/redux/models/models.thunks.test.ts b/src/redux/models/models.thunks.test.ts
index 3d85a15e274e0d0ee6352353d25e504de8097259..a8daf71d82f9a7ef7bd229df4afe8947e9c6c38a 100644
--- a/src/redux/models/models.thunks.test.ts
+++ b/src/redux/models/models.thunks.test.ts
@@ -1,12 +1,12 @@
-import { HttpStatusCode } from 'axios';
-import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
+import { modelsFixture } from '@/models/fixtures/modelsFixture';
+import { apiPath } from '@/redux/apiPath';
+import { ModelsState } from '@/redux/models/models.types';
 import {
   ToolkitStoreWithSingleSlice,
   createStoreInstanceUsingSliceReducer,
 } from '@/utils/createStoreInstanceUsingSliceReducer';
-import { ModelsState } from '@/redux/models/models.types';
-import { apiPath } from '@/redux/apiPath';
-import { modelsFixture } from '@/models/fixtures/modelsFixture';
+import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
+import { HttpStatusCode } from 'axios';
 import modelsReducer from './models.slice';
 import { getModels } from './models.thunks';
 
diff --git a/src/redux/overlays/overlays.selectors.ts b/src/redux/overlays/overlays.selectors.ts
index 80b0439bc90d4f04f09ac02c9b5a64803dff2fd4..f9d36cad0a63c93ae55565e795b897c6bbd4db3e 100644
--- a/src/redux/overlays/overlays.selectors.ts
+++ b/src/redux/overlays/overlays.selectors.ts
@@ -5,5 +5,5 @@ export const overlaysSelector = createSelector(rootSelector, state => state.over
 
 export const overlaysDataSelector = createSelector(
   overlaysSelector,
-  overlays => overlays.data || [],
+  overlays => overlays?.data || [],
 );
diff --git a/src/redux/project/project.selectors.ts b/src/redux/project/project.selectors.ts
index 9ba0ec033ee3257bc54f091e2194b7af739d4b60..2725956ae48088e4c4c8de5054b5425c3a73981d 100644
--- a/src/redux/project/project.selectors.ts
+++ b/src/redux/project/project.selectors.ts
@@ -3,4 +3,4 @@ import { rootSelector } from '../root/root.selectors';
 
 export const projectSelector = createSelector(rootSelector, state => state.project);
 
-export const projectDataSelector = createSelector(projectSelector, project => project.data);
+export const projectDataSelector = createSelector(projectSelector, project => project?.data);
diff --git a/src/redux/root/init.selectors.ts b/src/redux/root/init.selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..82176735f129c72fbb1161a73b6ee241f3e6909c
--- /dev/null
+++ b/src/redux/root/init.selectors.ts
@@ -0,0 +1,13 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { backgroundsSelector } from '../backgrounds/background.selectors';
+import { modelsSelector } from '../models/models.selectors';
+import { overlaysSelector } from '../overlays/overlays.selectors';
+import { projectSelector } from '../project/project.selectors';
+
+export const initDataLoadingInitialized = createSelector(
+  projectSelector,
+  backgroundsSelector,
+  modelsSelector,
+  overlaysSelector,
+  (...selectors) => selectors.every(selector => selector.loading !== 'idle'),
+);
diff --git a/src/redux/root/mapStages.selectors.ts b/src/redux/root/mapStages.selectors.ts
deleted file mode 100644
index cda0f3b219e9b7f7fe6e7d9ebd7099b9f0530271..0000000000000000000000000000000000000000
--- a/src/redux/root/mapStages.selectors.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { createSelector } from '@reduxjs/toolkit';
-import { backgroundsSelector } from '../backgrounds/background.selectors';
-import { modelsSelector } from '../models/models.selectors';
-import { overlaysSelector } from '../overlays/overlays.selectors';
-import { projectSelector } from '../project/project.selectors';
-
-export const mapLoadingFirstStageInitializedSelector = createSelector(
-  projectSelector,
-  project => project.loading !== 'idle',
-);
-
-export const mapLoadingFirstStageCompletedSelector = createSelector(
-  projectSelector,
-  project => project.loading === 'succeeded',
-);
-
-export const mapLoadingSecondStageInitializedSelector = createSelector(
-  backgroundsSelector,
-  modelsSelector,
-  overlaysSelector,
-  (backgrounds, models, overlays) =>
-    [backgrounds.loading, models.loading, overlays.loading].every(loading => loading !== 'idle'),
-);
-
-export const mapLoadingSecondStageCompletedSelector = createSelector(
-  backgroundsSelector,
-  modelsSelector,
-  overlaysSelector,
-  (backgrounds, models, overlays) =>
-    [backgrounds.loading, models.loading, overlays.loading].every(
-      loading => loading === 'succeeded',
-    ),
-);
-
-export const mapLoadingAllStagesCompletedSelector = createSelector(
-  mapLoadingFirstStageCompletedSelector,
-  mapLoadingSecondStageCompletedSelector,
-  (firstStageCompleted, secondStageCompleted) =>
-    [firstStageCompleted, secondStageCompleted].every(completed => completed === true),
-);
diff --git a/src/redux/store.ts b/src/redux/store.ts
index 0c59b4e1559b94a670f200f78ea4bee197498aa8..d0d3e67326e6a7fa49a0f5764368ea525da40eb6 100644
--- a/src/redux/store.ts
+++ b/src/redux/store.ts
@@ -9,7 +9,14 @@ import modelsReducer from '@/redux/models/models.slice';
 import overlaysReducer from '@/redux/overlays/overlays.slice';
 import projectReducer from '@/redux/project/project.slice';
 import searchReducer from '@/redux/search/search.slice';
-import { configureStore } from '@reduxjs/toolkit';
+import {
+  AnyAction,
+  ListenerEffectAPI,
+  ThunkDispatch,
+  TypedStartListening,
+  configureStore,
+} from '@reduxjs/toolkit';
+import { mapListenerMiddleware } from './map/middleware/map.middleware';
 
 export const reducers = {
   search: searchReducer,
@@ -25,13 +32,19 @@ export const reducers = {
   models: modelsReducer,
 };
 
+export const middlewares = [mapListenerMiddleware.middleware];
+
 export const store = configureStore({
   reducer: reducers,
   devTools: true,
+  middleware: getDefaultMiddleware => getDefaultMiddleware().prepend(...middlewares),
 });
 
 export type StoreType = typeof store;
 // Infer the `RootState` and `AppDispatch` types from the store itself
 export type RootState = ReturnType<typeof store.getState>;
 // Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
-export type AppDispatch = typeof store.dispatch;
+export type TypedDispatch<T> = ThunkDispatch<T, unknown, AnyAction>;
+export type AppDispatch = TypedDispatch<RootState>;
+export type AppStartListening = TypedStartListening<RootState, AppDispatch>;
+export type AppListenerEffectAPI = ListenerEffectAPI<RootState, AppDispatch>;
diff --git a/src/utils/map/getUpdatedMapData.test.ts b/src/utils/map/getUpdatedMapData.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5afdbc3c00a37776b425577324036ae739553b07
--- /dev/null
+++ b/src/utils/map/getUpdatedMapData.test.ts
@@ -0,0 +1,94 @@
+import { DEFAULT_ZOOM } from '@/constants/map';
+import { singleModelFixture } from '@/models/fixtures/modelsFixture';
+import { getUpdatedMapData } from './getUpdatedMapData';
+
+const HALF = 2;
+
+describe('getUpdatedMapData - util', () => {
+  describe('when model does not have default values', () => {
+    const model = {
+      ...singleModelFixture,
+      defaultCenterX: null,
+      defaultCenterY: null,
+      defaultZoomLevel: null,
+    };
+
+    it('should return correct value', () => {
+      const result = {
+        modelId: model.idObject,
+        size: {
+          width: model.width,
+          height: model.height,
+          tileSize: model.tileSize,
+          minZoom: model.minZoom,
+          maxZoom: model.maxZoom,
+        },
+        position: {
+          x: model.width / HALF,
+          y: model.height / HALF,
+          z: DEFAULT_ZOOM,
+        },
+      };
+
+      expect(getUpdatedMapData({ model })).toMatchObject(result);
+    });
+  });
+
+  describe('when model has default falsy values', () => {
+    const model = {
+      ...singleModelFixture,
+      defaultCenterX: 0,
+      defaultCenterY: 0,
+      defaultZoomLevel: null,
+    };
+
+    it('should return correct value', () => {
+      const result = {
+        modelId: model.idObject,
+        size: {
+          width: model.width,
+          height: model.height,
+          tileSize: model.tileSize,
+          minZoom: model.minZoom,
+          maxZoom: model.maxZoom,
+        },
+        position: {
+          x: 0,
+          y: 0,
+          z: DEFAULT_ZOOM,
+        },
+      };
+
+      expect(getUpdatedMapData({ model })).toMatchObject(result);
+    });
+  });
+
+  describe('when model has default truthy values', () => {
+    const model = {
+      ...singleModelFixture,
+      defaultCenterX: 10,
+      defaultCenterY: 10,
+      defaultZoomLevel: 1,
+    };
+
+    it('should return correct value', () => {
+      const result = {
+        modelId: model.idObject,
+        size: {
+          width: model.width,
+          height: model.height,
+          tileSize: model.tileSize,
+          minZoom: model.minZoom,
+          maxZoom: model.maxZoom,
+        },
+        position: {
+          x: 10,
+          y: 10,
+          z: 1,
+        },
+      };
+
+      expect(getUpdatedMapData({ model })).toMatchObject(result);
+    });
+  });
+});
diff --git a/src/utils/map/getUpdatedMapData.ts b/src/utils/map/getUpdatedMapData.ts
new file mode 100644
index 0000000000000000000000000000000000000000..552e29a64b20d955929f5e99a2233c915aebe9f0
--- /dev/null
+++ b/src/utils/map/getUpdatedMapData.ts
@@ -0,0 +1,27 @@
+import { DEFAULT_ZOOM } from '@/constants/map';
+import { MapData } from '@/redux/map/map.types';
+import { MapModel } from '@/types/models';
+
+interface GetUpdatedMapDataArgs {
+  model: MapModel;
+}
+
+type GetUpdatedMapDataResult = Pick<MapData, 'modelId' | 'size' | 'position'>;
+
+const HALF = 2;
+
+export const getUpdatedMapData = ({ model }: GetUpdatedMapDataArgs): GetUpdatedMapDataResult => ({
+  modelId: model.idObject,
+  size: {
+    width: model.width,
+    height: model.height,
+    tileSize: model.tileSize,
+    minZoom: model.minZoom,
+    maxZoom: model.maxZoom,
+  },
+  position: {
+    x: model.defaultCenterX ?? model.width / HALF,
+    y: model.defaultCenterY ?? model.height / HALF,
+    z: model.defaultZoomLevel ?? DEFAULT_ZOOM,
+  },
+});
diff --git a/src/utils/testing/getReduxWrapperWithStore.tsx b/src/utils/testing/getReduxWrapperWithStore.tsx
index 604f81c4c2ddec336dc0040bd1b57fa08e7f8f48..5c799a25d2413cc0d6fffdd57fc40537bd5c29bd 100644
--- a/src/utils/testing/getReduxWrapperWithStore.tsx
+++ b/src/utils/testing/getReduxWrapperWithStore.tsx
@@ -1,4 +1,4 @@
-import { RootState, StoreType, reducers } from '@/redux/store';
+import { RootState, StoreType, middlewares, reducers } from '@/redux/store';
 import { configureStore } from '@reduxjs/toolkit';
 import { Provider } from 'react-redux';
 
@@ -19,6 +19,7 @@ export const getReduxWrapperWithStore: GetReduxWrapperUsingSliceReducer = (
   const testStore = configureStore({
     reducer: reducers,
     preloadedState,
+    middleware: getDefaultMiddleware => getDefaultMiddleware().prepend(...middlewares),
   });
 
   const Wrapper = ({ children }: WrapperProps): JSX.Element => (
diff --git a/yarn.lock b/yarn.lock
index 4a3561b2d8ee49f9d76bbca06a2c8c3b7a945c24..c865fa7436c7be819c80651b30e867e2502d5574 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -878,6 +878,18 @@
   "resolved" "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.4.tgz"
   "version" "3.8.4"
 
+"@pkgr/utils@^2.3.1":
+  "integrity" "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw=="
+  "resolved" "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz"
+  "version" "2.4.2"
+  dependencies:
+    "cross-spawn" "^7.0.3"
+    "fast-glob" "^3.3.0"
+    "is-glob" "^4.0.3"
+    "open" "^9.1.0"
+    "picocolors" "^1.0.0"
+    "tslib" "^2.6.0"
+
 "@reduxjs/toolkit@^1.9.6":
   "integrity" "sha512-Gc4ikl90ORF4viIdAkY06JNUnODjKfGxZRwATM30EdHq8hLSVoSrwXne5dd739yenP5bJxAX7tLuOWK5RPGtrw=="
   "resolved" "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.6.tgz"
@@ -1780,6 +1792,11 @@
   dependencies:
     "tweetnacl" "^0.14.3"
 
+"big-integer@^1.6.44":
+  "integrity" "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg=="
+  "resolved" "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz"
+  "version" "1.6.51"
+
 "binary-extensions@^2.0.0":
   "integrity" "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
   "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz"
@@ -1804,6 +1821,13 @@
   "resolved" "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz"
   "version" "3.7.2"
 
+"bplist-parser@^0.2.0":
+  "integrity" "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw=="
+  "resolved" "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz"
+  "version" "0.2.0"
+  dependencies:
+    "big-integer" "^1.6.44"
+
 "brace-expansion@^1.1.7":
   "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="
   "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz"
@@ -1861,6 +1885,13 @@
   dependencies:
     "semver" "^7.0.0"
 
+"bundle-name@^3.0.0":
+  "integrity" "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw=="
+  "resolved" "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz"
+  "version" "3.0.0"
+  dependencies:
+    "run-applescript" "^5.0.0"
+
 "busboy@1.6.0":
   "integrity" "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="
   "resolved" "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz"
@@ -2437,6 +2468,11 @@
   "resolved" "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz"
   "version" "10.4.3"
 
+"decode-uri-component@^0.2.2":
+  "integrity" "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
+  "resolved" "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz"
+  "version" "0.2.2"
+
 "dedent@^1.0.0":
   "integrity" "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg=="
   "resolved" "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz"
@@ -2481,6 +2517,24 @@
   "resolved" "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz"
   "version" "4.3.1"
 
+"default-browser-id@^3.0.0":
+  "integrity" "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA=="
+  "resolved" "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz"
+  "version" "3.0.0"
+  dependencies:
+    "bplist-parser" "^0.2.0"
+    "untildify" "^4.0.0"
+
+"default-browser@^4.0.0":
+  "integrity" "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA=="
+  "resolved" "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz"
+  "version" "4.0.0"
+  dependencies:
+    "bundle-name" "^3.0.0"
+    "default-browser-id" "^3.0.0"
+    "execa" "^7.1.1"
+    "titleize" "^3.0.0"
+
 "defaults@^1.0.3":
   "integrity" "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="
   "resolved" "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz"
@@ -2497,6 +2551,11 @@
     "gopd" "^1.0.1"
     "has-property-descriptors" "^1.0.0"
 
+"define-lazy-prop@^3.0.0":
+  "integrity" "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="
+  "resolved" "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz"
+  "version" "3.0.0"
+
 "define-properties@^1.1.3", "define-properties@^1.1.4", "define-properties@^1.2.0", "define-properties@^1.2.1":
   "integrity" "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="
   "resolved" "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz"
@@ -2954,6 +3013,14 @@
     "resolve" "^1.22.2"
     "semver" "^7.5.3"
 
+"eslint-plugin-prettier@^5.0.1":
+  "integrity" "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg=="
+  "resolved" "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz"
+  "version" "5.0.1"
+  dependencies:
+    "prettier-linter-helpers" "^1.0.0"
+    "synckit" "^0.8.5"
+
 "eslint-plugin-promise@^6.0.0", "eslint-plugin-promise@^6.1.1":
   "integrity" "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig=="
   "resolved" "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz"
@@ -3022,7 +3089,7 @@
   "resolved" "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
   "version" "3.4.3"
 
-"eslint@*", "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", "eslint@^7.23.0 || ^8.0.0", "eslint@^7.32.0 || ^8.2.0", "eslint@^7.5.0 || ^8.0.0", "eslint@^8.0.1", "eslint@^8.49.0", "eslint@>=7.0.0", "eslint@>=8":
+"eslint@*", "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", "eslint@^7.23.0 || ^8.0.0", "eslint@^7.32.0 || ^8.2.0", "eslint@^7.5.0 || ^8.0.0", "eslint@^8.0.1", "eslint@^8.49.0", "eslint@>=7.0.0", "eslint@>=8", "eslint@>=8.0.0":
   "integrity" "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg=="
   "resolved" "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz"
   "version" "8.50.0"
@@ -3133,6 +3200,21 @@
     "signal-exit" "^3.0.3"
     "strip-final-newline" "^2.0.0"
 
+"execa@^7.1.1":
+  "integrity" "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA=="
+  "resolved" "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz"
+  "version" "7.2.0"
+  dependencies:
+    "cross-spawn" "^7.0.3"
+    "get-stream" "^6.0.1"
+    "human-signals" "^4.3.0"
+    "is-stream" "^3.0.0"
+    "merge-stream" "^2.0.0"
+    "npm-run-path" "^5.1.0"
+    "onetime" "^6.0.0"
+    "signal-exit" "^3.0.7"
+    "strip-final-newline" "^3.0.0"
+
 "execa@4.1.0":
   "integrity" "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA=="
   "resolved" "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz"
@@ -3228,7 +3310,12 @@
   "resolved" "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
   "version" "3.1.3"
 
-"fast-glob@^3.2.12", "fast-glob@^3.2.5", "fast-glob@^3.2.9", "fast-glob@^3.3.1":
+"fast-diff@^1.1.2":
+  "integrity" "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="
+  "resolved" "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz"
+  "version" "1.3.0"
+
+"fast-glob@^3.2.12", "fast-glob@^3.2.5", "fast-glob@^3.2.9", "fast-glob@^3.3.0", "fast-glob@^3.3.1":
   "integrity" "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="
   "resolved" "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz"
   "version" "3.3.1"
@@ -3291,6 +3378,11 @@
   dependencies:
     "to-regex-range" "^5.0.1"
 
+"filter-obj@^1.1.0":
+  "integrity" "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ=="
+  "resolved" "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz"
+  "version" "1.1.0"
+
 "find-node-modules@^2.1.2":
   "integrity" "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg=="
   "resolved" "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz"
@@ -4015,6 +4107,16 @@
   dependencies:
     "has-tostringtag" "^1.0.0"
 
+"is-docker@^2.0.0":
+  "integrity" "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="
+  "resolved" "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz"
+  "version" "2.2.1"
+
+"is-docker@^3.0.0":
+  "integrity" "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="
+  "resolved" "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz"
+  "version" "3.0.0"
+
 "is-extglob@^2.1.1":
   "integrity" "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
   "resolved" "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
@@ -4056,6 +4158,13 @@
   dependencies:
     "is-extglob" "^2.1.1"
 
+"is-inside-container@^1.0.0":
+  "integrity" "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="
+  "resolved" "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz"
+  "version" "1.0.0"
+  dependencies:
+    "is-docker" "^3.0.0"
+
 "is-installed-globally@~0.4.0":
   "integrity" "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ=="
   "resolved" "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz"
@@ -4209,6 +4318,13 @@
   "resolved" "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz"
   "version" "1.0.2"
 
+"is-wsl@^2.2.0":
+  "integrity" "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="
+  "resolved" "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz"
+  "version" "2.2.0"
+  dependencies:
+    "is-docker" "^2.0.0"
+
 "isarray@^2.0.5":
   "integrity" "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
   "resolved" "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz"
@@ -5243,7 +5359,12 @@
   "resolved" "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
   "version" "1.4.0"
 
-"next@13.4.19":
+"next-router-mock@^0.9.10":
+  "integrity" "sha512-bK6sRb/xGNFgHVUZuvuApn6KJBAKTPiP36A7a9mO77U4xQO5ukJx9WHlU67Tv8AuySd09pk0+Hu8qMVIAmLO6A=="
+  "resolved" "https://registry.npmjs.org/next-router-mock/-/next-router-mock-0.9.10.tgz"
+  "version" "0.9.10"
+
+"next@>=10.0.0", "next@13.4.19":
   "integrity" "sha512-HuPSzzAbJ1T4BD8e0bs6B9C1kWQ6gv8ykZoRWs5AQoiIuqbGHHdQO7Ljuvg05Q0Z24E2ABozHe6FxDvI6HfyAw=="
   "resolved" "https://registry.npmjs.org/next/-/next-13.4.19.tgz"
   "version" "13.4.19"
@@ -5440,6 +5561,16 @@
   dependencies:
     "mimic-fn" "^4.0.0"
 
+"open@^9.1.0":
+  "integrity" "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg=="
+  "resolved" "https://registry.npmjs.org/open/-/open-9.1.0.tgz"
+  "version" "9.1.0"
+  dependencies:
+    "default-browser" "^4.0.0"
+    "define-lazy-prop" "^3.0.0"
+    "is-inside-container" "^1.0.0"
+    "is-wsl" "^2.2.0"
+
 "optionator@^0.9.3":
   "integrity" "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg=="
   "resolved" "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz"
@@ -5708,12 +5839,19 @@
   "resolved" "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz"
   "version" "2.8.8"
 
+"prettier-linter-helpers@^1.0.0":
+  "integrity" "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w=="
+  "resolved" "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz"
+  "version" "1.0.0"
+  dependencies:
+    "fast-diff" "^1.1.2"
+
 "prettier-plugin-tailwindcss@^0.5.6":
   "integrity" "sha512-2Xgb+GQlkPAUCFi3sV+NOYcSI5XgduvDBL2Zt/hwJudeKXkyvRS65c38SB0yb9UB40+1rL83I6m0RtlOQ8eHdg=="
   "resolved" "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.6.tgz"
   "version" "0.5.6"
 
-"prettier@^3.0", "prettier@^3.0.3":
+"prettier@^3.0", "prettier@^3.0.3", "prettier@>=3.0.0":
   "integrity" "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg=="
   "resolved" "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz"
   "version" "3.0.3"
@@ -5817,6 +5955,16 @@
   dependencies:
     "side-channel" "^1.0.4"
 
+"query-string@7.1.3":
+  "integrity" "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg=="
+  "resolved" "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz"
+  "version" "7.1.3"
+  dependencies:
+    "decode-uri-component" "^0.2.2"
+    "filter-obj" "^1.1.0"
+    "split-on-first" "^1.0.0"
+    "strict-uri-encode" "^2.0.0"
+
 "querystringify@^2.1.1":
   "integrity" "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
   "resolved" "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz"
@@ -5897,7 +6045,7 @@
     "react-is" "^18.0.0"
     "use-sync-external-store" "^1.0.0"
 
-"react@^16.3.2 || ^17.0.0 || ^18.0.0", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0-0 || ^17.0.0 || ^18.0.0", "react@^16.9.0 || ^17.0.0 || ^18", "react@^18.0.0", "react@^18.2.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", "react@18.2.0":
+"react@^16.3.2 || ^17.0.0 || ^18.0.0", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0-0 || ^17.0.0 || ^18.0.0", "react@^16.9.0 || ^17.0.0 || ^18", "react@^18.0.0", "react@^18.2.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", "react@>=17.0.0", "react@18.2.0":
   "integrity" "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="
   "resolved" "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
   "version" "18.2.0"
@@ -6124,6 +6272,13 @@
   dependencies:
     "glob" "^7.1.3"
 
+"run-applescript@^5.0.0":
+  "integrity" "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg=="
+  "resolved" "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz"
+  "version" "5.0.0"
+  dependencies:
+    "execa" "^5.0.0"
+
 "run-async@^2.4.0":
   "integrity" "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ=="
   "resolved" "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz"
@@ -6328,6 +6483,11 @@
   "resolved" "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.15.tgz"
   "version" "3.0.15"
 
+"split-on-first@^1.0.0":
+  "integrity" "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
+  "resolved" "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz"
+  "version" "1.1.0"
+
 "split2@^3.0.0", "split2@^3.2.2":
   "integrity" "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg=="
   "resolved" "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz"
@@ -6374,6 +6534,11 @@
   "resolved" "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz"
   "version" "1.1.0"
 
+"strict-uri-encode@^2.0.0":
+  "integrity" "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ=="
+  "resolved" "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz"
+  "version" "2.0.0"
+
 "string_decoder@^1.1.1":
   "integrity" "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="
   "resolved" "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
@@ -6566,6 +6731,14 @@
   "resolved" "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz"
   "version" "3.2.4"
 
+"synckit@^0.8.5":
+  "integrity" "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q=="
+  "resolved" "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz"
+  "version" "0.8.5"
+  dependencies:
+    "@pkgr/utils" "^2.3.1"
+    "tslib" "^2.5.0"
+
 "tailwind-merge@^1.14.0":
   "integrity" "sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ=="
   "resolved" "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz"
@@ -6654,6 +6827,11 @@
   dependencies:
     "readable-stream" "3"
 
+"titleize@^3.0.0":
+  "integrity" "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ=="
+  "resolved" "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz"
+  "version" "3.0.0"
+
 "tmp@^0.0.33":
   "integrity" "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="
   "resolved" "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz"
@@ -6751,7 +6929,7 @@
   "resolved" "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz"
   "version" "1.14.1"
 
-"tslib@^2.1.0", "tslib@^2.4.0":
+"tslib@^2.1.0", "tslib@^2.4.0", "tslib@^2.5.0", "tslib@^2.6.0":
   "integrity" "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
   "resolved" "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz"
   "version" "2.6.2"