diff --git a/.eslintrc.json b/.eslintrc.json
index 94cbfd15580063d9b7ce1313d8af37d2e955d2e2..658e31dfcf75661ecaca6dd9c363d6322e793bd7 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -65,7 +65,9 @@
           "**/jest.setup.ts", // jest setup
           "**/setupTests.ts"
         ],
-        "optionalDependencies": false
+        "optionalDependencies": false,
+        "peerDependencies": false,
+        "packageDir": "./"
       }
     ],
     "indent": ["error", 2],
@@ -83,6 +85,12 @@
       "rules": {
         "@typescript-eslint/explicit-function-return-type": "error"
       }
+    },
+    {
+      // feel free to replace with your preferred file pattern - eg. 'src/**/*Slice.ts'
+      "files": ["src/**/*.slice.ts", "src/**/*.reducers.ts"],
+      // avoid state param assignment
+      "rules": { "no-param-reassign": ["error", { "props": false }] }
     }
   ],
   "settings": {
diff --git a/package-lock.json b/package-lock.json
index 32205168fb58eae02fa3d4a9f2c7aa851396f44f..1a168c993612d2b08165f53978c6de6cb80047f9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,23 +10,29 @@
       "hasInstallScript": true,
       "dependencies": {
         "@next/font": "^13.5.2",
+        "@reduxjs/toolkit": "^1.9.6",
         "@types/node": "20.6.2",
         "@types/react": "18.2.21",
         "@types/react-dom": "18.2.7",
         "autoprefixer": "10.4.15",
+        "axios": "^1.5.1",
         "eslint-config-next": "13.4.19",
         "next": "13.4.19",
         "postcss": "8.4.29",
         "react": "18.2.0",
         "react-dom": "18.2.0",
+        "react-redux": "^8.1.2",
         "tailwind-merge": "^1.14.0",
-        "tailwindcss": "3.3.3"
+        "tailwindcss": "3.3.3",
+        "zod": "^3.22.2"
       },
       "devDependencies": {
         "@commitlint/cli": "^17.7.1",
         "@commitlint/config-conventional": "^17.7.0",
         "@testing-library/jest-dom": "^6.1.3",
         "@testing-library/react": "^14.0.0",
+        "@types/jest": "^29.5.5",
+        "@types/react-redux": "^7.1.26",
         "@typescript-eslint/eslint-plugin": "^6.7.0",
         "@typescript-eslint/parser": "^6.7.0",
         "cypress": "^13.2.0",
@@ -2129,6 +2135,29 @@
         "node": ">= 8"
       }
     },
+    "node_modules/@reduxjs/toolkit": {
+      "version": "1.9.6",
+      "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.6.tgz",
+      "integrity": "sha512-Gc4ikl90ORF4viIdAkY06JNUnODjKfGxZRwATM30EdHq8hLSVoSrwXne5dd739yenP5bJxAX7tLuOWK5RPGtrw==",
+      "dependencies": {
+        "immer": "^9.0.21",
+        "redux": "^4.2.1",
+        "redux-thunk": "^2.4.2",
+        "reselect": "^4.1.8"
+      },
+      "peerDependencies": {
+        "react": "^16.9.0 || ^17.0.0 || ^18",
+        "react-redux": "^7.2.1 || ^8.0.2"
+      },
+      "peerDependenciesMeta": {
+        "react": {
+          "optional": true
+        },
+        "react-redux": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@rushstack/eslint-patch": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.4.0.tgz",
@@ -2355,6 +2384,15 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/hoist-non-react-statics": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+      "integrity": "sha512-YIQtIg4PKr7ZyqNPZObpxfHsHEmuB8dXCxd6qVcGuQVDK2bpsF7bYNnBJ4Nn7giuACZg+WewExgrtAJ3XnA4Xw==",
+      "dependencies": {
+        "@types/react": "*",
+        "hoist-non-react-statics": "^3.3.0"
+      }
+    },
     "node_modules/@types/istanbul-lib-coverage": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
@@ -2384,8 +2422,6 @@
       "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.5.tgz",
       "integrity": "sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==",
       "dev": true,
-      "optional": true,
-      "peer": true,
       "dependencies": {
         "expect": "^29.0.0",
         "pretty-format": "^29.0.0"
@@ -2396,8 +2432,6 @@
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
       "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
       "dev": true,
-      "optional": true,
-      "peer": true,
       "engines": {
         "node": ">=10"
       },
@@ -2410,8 +2444,6 @@
       "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
       "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
       "dev": true,
-      "optional": true,
-      "peer": true,
       "dependencies": {
         "@jest/schemas": "^29.6.3",
         "ansi-styles": "^5.0.0",
@@ -2425,9 +2457,7 @@
       "version": "18.2.0",
       "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
       "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
-      "dev": true,
-      "optional": true,
-      "peer": true
+      "dev": true
     },
     "node_modules/@types/jsdom": {
       "version": "20.0.1",
@@ -2491,6 +2521,18 @@
         "@types/react": "*"
       }
     },
+    "node_modules/@types/react-redux": {
+      "version": "7.1.27",
+      "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.27.tgz",
+      "integrity": "sha512-xj7d9z32p1K/eBmO+OEy+qfaWXtcPlN8f1Xk3Ne0p/ZRQ867RI5bQ/bpBtxbqU1AHNhKJSgGvld/P2myU2uYkg==",
+      "dev": true,
+      "dependencies": {
+        "@types/hoist-non-react-statics": "^3.3.0",
+        "@types/react": "*",
+        "hoist-non-react-statics": "^3.3.0",
+        "redux": "^4.0.0"
+      }
+    },
     "node_modules/@types/scheduler": {
       "version": "0.16.3",
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
@@ -2526,6 +2568,11 @@
       "integrity": "sha512-THo502dA5PzG/sfQH+42Lw3fvmYkceefOspdCwpHRul8ik2Jv1K8I5OZz1AT3/rs46kwgMCe9bSBmDLYkkOMGg==",
       "dev": true
     },
+    "node_modules/@types/use-sync-external-store": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
+      "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
+    },
     "node_modules/@types/yargs": {
       "version": "17.0.24",
       "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz",
@@ -3118,8 +3165,7 @@
     "node_modules/asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
-      "dev": true
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
     },
     "node_modules/at-least-node": {
       "version": "1.0.0",
@@ -3200,6 +3246,21 @@
         "node": ">=4"
       }
     },
+    "node_modules/axios": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz",
+      "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==",
+      "dependencies": {
+        "follow-redirects": "^1.15.0",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/axios/node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
     "node_modules/axobject-query": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@@ -3914,7 +3975,6 @@
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
       "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
       "dependencies": {
         "delayed-stream": "~1.0.0"
       },
@@ -4920,7 +4980,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
       "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
-      "dev": true,
       "engines": {
         "node": ">=0.4.0"
       }
@@ -6337,6 +6396,25 @@
       "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz",
       "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ=="
     },
+    "node_modules/follow-redirects": {
+      "version": "1.15.3",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
+      "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/for-each": {
       "version": "0.3.3",
       "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@@ -6358,7 +6436,6 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
       "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
-      "dev": true,
       "dependencies": {
         "asynckit": "^0.4.0",
         "combined-stream": "^1.0.8",
@@ -6798,6 +6875,14 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/hoist-non-react-statics": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+      "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+      "dependencies": {
+        "react-is": "^16.7.0"
+      }
+    },
     "node_modules/homedir-polyfill": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
@@ -6945,6 +7030,15 @@
         "node": ">= 4"
       }
     },
+    "node_modules/immer": {
+      "version": "9.0.21",
+      "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
+      "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/immer"
+      }
+    },
     "node_modules/import-fresh": {
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -9449,7 +9543,6 @@
       "version": "1.52.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
       "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
-      "dev": true,
       "engines": {
         "node": ">= 0.6"
       }
@@ -9458,7 +9551,6 @@
       "version": "2.1.35",
       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
       "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
-      "dev": true,
       "dependencies": {
         "mime-db": "1.52.0"
       },
@@ -9644,6 +9736,14 @@
         "node": "^10 || ^12 || >=14"
       }
     },
+    "node_modules/next/node_modules/zod": {
+      "version": "3.21.4",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",
+      "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==",
+      "funding": {
+        "url": "https://github.com/sponsors/colinhacks"
+      }
+    },
     "node_modules/node-int64": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@@ -10583,6 +10683,49 @@
       "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
       "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
     },
+    "node_modules/react-redux": {
+      "version": "8.1.2",
+      "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.2.tgz",
+      "integrity": "sha512-xJKYI189VwfsFc4CJvHqHlDrzyFTY/3vZACbE+rr/zQ34Xx1wQfB4OTOSeOSNrF6BDVe8OOdxIrAnMGXA3ggfw==",
+      "dependencies": {
+        "@babel/runtime": "^7.12.1",
+        "@types/hoist-non-react-statics": "^3.3.1",
+        "@types/use-sync-external-store": "^0.0.3",
+        "hoist-non-react-statics": "^3.3.2",
+        "react-is": "^18.0.0",
+        "use-sync-external-store": "^1.0.0"
+      },
+      "peerDependencies": {
+        "@types/react": "^16.8 || ^17.0 || ^18.0",
+        "@types/react-dom": "^16.8 || ^17.0 || ^18.0",
+        "react": "^16.8 || ^17.0 || ^18.0",
+        "react-dom": "^16.8 || ^17.0 || ^18.0",
+        "react-native": ">=0.59",
+        "redux": "^4 || ^5.0.0-beta.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        },
+        "react-dom": {
+          "optional": true
+        },
+        "react-native": {
+          "optional": true
+        },
+        "redux": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/react-redux/node_modules/react-is": {
+      "version": "18.2.0",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+      "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
+    },
     "node_modules/read-cache": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -10758,6 +10901,22 @@
         "node": ">=8"
       }
     },
+    "node_modules/redux": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
+      "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
+      "dependencies": {
+        "@babel/runtime": "^7.9.2"
+      }
+    },
+    "node_modules/redux-thunk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz",
+      "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==",
+      "peerDependencies": {
+        "redux": "^4"
+      }
+    },
     "node_modules/reflect.getprototypeof": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz",
@@ -10831,6 +10990,11 @@
       "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
       "dev": true
     },
+    "node_modules/reselect": {
+      "version": "4.1.8",
+      "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
+      "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="
+    },
     "node_modules/resolve": {
       "version": "1.22.6",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz",
@@ -12137,6 +12301,14 @@
         "requires-port": "^1.0.0"
       }
     },
+    "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",
+      "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+      }
+    },
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -12627,9 +12799,9 @@
       }
     },
     "node_modules/zod": {
-      "version": "3.21.4",
-      "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",
-      "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==",
+      "version": "3.22.2",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.2.tgz",
+      "integrity": "sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==",
       "funding": {
         "url": "https://github.com/sponsors/colinhacks"
       }
diff --git a/package.json b/package.json
index a840cf2fad07c115fec8d24c65a9e063e32b8bdd..30e182e0c278104441384872a10a7c858e2ff27c 100644
--- a/package.json
+++ b/package.json
@@ -28,23 +28,29 @@
   },
   "dependencies": {
     "@next/font": "^13.5.2",
+    "@reduxjs/toolkit": "^1.9.6",
     "@types/node": "20.6.2",
     "@types/react": "18.2.21",
     "@types/react-dom": "18.2.7",
     "autoprefixer": "10.4.15",
+    "axios": "^1.5.1",
     "eslint-config-next": "13.4.19",
     "next": "13.4.19",
     "postcss": "8.4.29",
     "react": "18.2.0",
     "react-dom": "18.2.0",
+    "react-redux": "^8.1.2",
     "tailwind-merge": "^1.14.0",
-    "tailwindcss": "3.3.3"
+    "tailwindcss": "3.3.3",
+    "zod": "^3.22.2"
   },
   "devDependencies": {
     "@commitlint/cli": "^17.7.1",
     "@commitlint/config-conventional": "^17.7.0",
     "@testing-library/jest-dom": "^6.1.3",
     "@testing-library/react": "^14.0.0",
+    "@types/jest": "^29.5.5",
+    "@types/react-redux": "^7.1.26",
     "@typescript-eslint/eslint-plugin": "^6.7.0",
     "@typescript-eslint/parser": "^6.7.0",
     "cypress": "^13.2.0",
diff --git a/pages/_app.tsx b/pages/_app.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..f0e8ed0cf72767f1f7198d9eb7c5ed36575ded60
--- /dev/null
+++ b/pages/_app.tsx
@@ -0,0 +1,10 @@
+import { AppWrapper } from '@/components/AppWrapper';
+import type { AppProps } from 'next/app';
+
+const MyApp = ({ Component, pageProps }: AppProps): JSX.Element => (
+  <AppWrapper>
+    <Component {...pageProps} />
+  </AppWrapper>
+);
+
+export default MyApp;
diff --git a/pages/redux-api-poc.tsx b/pages/redux-api-poc.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..4a195e6e0c7a488c2d633aa0ed792d534c2bf622
--- /dev/null
+++ b/pages/redux-api-poc.tsx
@@ -0,0 +1,28 @@
+import { useSelector } from 'react-redux';
+import { selectSearchValue } from '@/redux/search/search.selectors';
+import { setSearchValue } from '@/redux/search/search.slice';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { getProjectById } from '@/redux/project/project.thunks';
+
+const ReduxPage = (): JSX.Element => {
+  const dispatch = useAppDispatch();
+  const searchValue = useSelector(selectSearchValue);
+
+  const triggerSyncUpdate = (): void => {
+    // eslint-disable-next-line prefer-template
+    const newValue = searchValue + 'test';
+    dispatch(setSearchValue(newValue));
+    dispatch(getProjectById('pd_map_winter_23'));
+  };
+
+  return (
+    <div>
+      {searchValue}
+      <button type="button" onClick={triggerSyncUpdate}>
+        sync update
+      </button>
+    </div>
+  );
+};
+
+export default ReduxPage;
diff --git a/src/components/AppWrapper/AppWrapper.component.tsx b/src/components/AppWrapper/AppWrapper.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2bb7e192c61b37fef97dafaf58cd0bfc1d94395d
--- /dev/null
+++ b/src/components/AppWrapper/AppWrapper.component.tsx
@@ -0,0 +1,11 @@
+import { ReactNode } from 'react';
+import { Provider } from 'react-redux';
+import { store } from '@/redux/store';
+
+interface AppWrapperProps {
+  children: ReactNode;
+}
+
+export const AppWrapper = ({ children }: AppWrapperProps): JSX.Element => (
+  <Provider store={store}>{children}</Provider>
+);
diff --git a/src/components/AppWrapper/index.ts b/src/components/AppWrapper/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fed05f3d6bfceba57c760c1a873640b3f7c496f7
--- /dev/null
+++ b/src/components/AppWrapper/index.ts
@@ -0,0 +1 @@
+export { AppWrapper } from './AppWrapper.component';
diff --git a/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.tsx b/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.tsx
index f2eedb817c632a0a97398310a5a6d75f92efaca1..3dfa2e6cfd26f0b5c7d82aa1049e518b76994e08 100644
--- a/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.tsx
+++ b/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.tsx
@@ -3,6 +3,6 @@ import avatarImg from '@/assets/images/user-avatar.png';
 
 export const UserAvatar = (): JSX.Element => (
   <div className="w-8 h-8 mr-7" data-testid="user-avatar">
-    <Image src={avatarImg} alt="user avatar" height={32} width={32} />
+    <Image src={avatarImg} alt="user avatar" width={32} height={32} />
   </div>
 );
diff --git a/src/constants/.gitkeep b/src/constants/.gitkeep
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/constants/api.ts b/src/constants/api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f3ebe0ec039ef618048a28016238eb1733bbf817
--- /dev/null
+++ b/src/constants/api.ts
@@ -0,0 +1 @@
+export const BASE_API_URL = 'https://corsproxy.io/?https://pdmap.uni.lu/minerva/api';
diff --git a/src/models/disease.ts b/src/models/disease.ts
new file mode 100644
index 0000000000000000000000000000000000000000..65079e66740c8eb2f4488a9f5798339cc9a4abe4
--- /dev/null
+++ b/src/models/disease.ts
@@ -0,0 +1,9 @@
+import { z } from 'zod';
+
+export const disease = z.object({
+  link: z.string(),
+  type: z.string(),
+  resource: z.string(),
+  id: z.number(),
+  annotatorClassName: z.string(),
+});
diff --git a/src/models/organism.ts b/src/models/organism.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4b003eefff187e85f7b27831e55d5c09ea3b5c62
--- /dev/null
+++ b/src/models/organism.ts
@@ -0,0 +1,9 @@
+import { z } from 'zod';
+
+export const organism = z.object({
+  link: z.string(),
+  type: z.string(),
+  resource: z.string(),
+  id: z.number(),
+  annotatorClassName: z.string(),
+});
diff --git a/src/models/project.ts b/src/models/project.ts
new file mode 100644
index 0000000000000000000000000000000000000000..051e5ca197d0bf239e1f525e8a460dfa499467cb
--- /dev/null
+++ b/src/models/project.ts
@@ -0,0 +1,47 @@
+import { z } from 'zod';
+import { disease } from './disease';
+import { organism } from './organism';
+
+export const projectSchema = z.object({
+  version: z.string(),
+  disease,
+  organism,
+  idObject: z.number(),
+  status: z.string(),
+  directory: z.string(),
+  progress: z.number(),
+  notifyEmail: z.string(),
+  logEntries: z.boolean(),
+  name: z.string(),
+  sharedInMinervaNet: z.boolean(),
+  owner: z.string(),
+  projectId: z.string(),
+  creationDate: z.string(),
+  mapCanvasType: z.string(),
+  overviewImageViews: z.array(
+    z.object({
+      idObject: z.number(),
+      filename: z.string(),
+      width: z.number(),
+      height: z.number(),
+      links: z.array(
+        z.union([
+          z.object({
+            idObject: z.number(),
+            polygon: z.array(z.object({ x: z.number(), y: z.number() })),
+            imageLinkId: z.number(),
+            type: z.string(),
+          }),
+          z.object({
+            idObject: z.number(),
+            polygon: z.array(z.object({ x: z.number(), y: z.number() })),
+            zoomLevel: z.number(),
+            modelPoint: z.object({ x: z.number(), y: z.number() }),
+            modelLinkId: z.number(),
+            type: z.string(),
+          }),
+        ]),
+      ),
+    }),
+  ),
+});
diff --git a/src/redux/hooks/useAppDispatch.ts b/src/redux/hooks/useAppDispatch.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a5d7fc0984e330c9467772db4596ca4adeb74b18
--- /dev/null
+++ b/src/redux/hooks/useAppDispatch.ts
@@ -0,0 +1,4 @@
+import { useDispatch } from 'react-redux';
+import { AppDispatch } from '@/redux/store';
+
+export const useAppDispatch: () => AppDispatch = useDispatch;
diff --git a/src/redux/hooks/useAppSelector.ts b/src/redux/hooks/useAppSelector.ts
new file mode 100644
index 0000000000000000000000000000000000000000..953f1a353aa1fd916034f92e0911fe6ab855f9d5
--- /dev/null
+++ b/src/redux/hooks/useAppSelector.ts
@@ -0,0 +1,6 @@
+import { useSelector } from 'react-redux';
+import type { TypedUseSelectorHook } from 'react-redux';
+import { RootState } from '@/redux/store';
+
+// Use throughout your app instead of plain `useDispatch` and `useSelector`
+export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
diff --git a/src/redux/project/project.reducers.ts b/src/redux/project/project.reducers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aee885ae235dace1b0345e3ee4f6a7d42ad8b3e8
--- /dev/null
+++ b/src/redux/project/project.reducers.ts
@@ -0,0 +1,10 @@
+import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
+import { ProjectState } from '@/redux/project/project.types';
+import { getProjectById } from '@/redux/project/project.thunks';
+
+export const getProjectByIdReducer = (builder: ActionReducerMapBuilder<ProjectState>): void => {
+  builder.addCase(getProjectById.fulfilled, (state, action) => {
+    state.data = action.payload;
+    state.loading = 'succeeded';
+  });
+};
diff --git a/src/redux/project/project.slice.ts b/src/redux/project/project.slice.ts
new file mode 100644
index 0000000000000000000000000000000000000000..994cb48464988a1ea7b97d4baf788e55dc623b8e
--- /dev/null
+++ b/src/redux/project/project.slice.ts
@@ -0,0 +1,20 @@
+import { createSlice } from '@reduxjs/toolkit';
+import { ProjectState } from '@/redux/project/project.types';
+import { getProjectByIdReducer } from './project.reducers';
+
+const initialState: ProjectState = {
+  data: [],
+  loading: 'idle',
+  error: { name: '', message: '' },
+};
+
+const projectSlice = createSlice({
+  name: 'project',
+  initialState,
+  reducers: {},
+  extraReducers: builder => {
+    getProjectByIdReducer(builder);
+  },
+});
+
+export default projectSlice.reducer;
diff --git a/src/redux/project/project.thunks.ts b/src/redux/project/project.thunks.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d26bbdda8ce975ac42a703712aeed1df28e69d2e
--- /dev/null
+++ b/src/redux/project/project.thunks.ts
@@ -0,0 +1,16 @@
+import { createAsyncThunk } from '@reduxjs/toolkit';
+import { axiosInstance } from '@/services/api/utils/axiosInstance';
+import { Project } from '@/types/api';
+import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
+import { projectSchema } from '@/models/project';
+
+export const getProjectById = createAsyncThunk(
+  'project/getUsersByIdStatus',
+  async (id: string): Promise<Project | undefined> => {
+    const response = await axiosInstance.get<Project>(`projects/${id}`);
+
+    const isDataValid = validateDataUsingZodSchema(response.data, projectSchema);
+
+    return isDataValid ? response.data : undefined;
+  },
+);
diff --git a/src/redux/project/project.types.ts b/src/redux/project/project.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b5ace9d7c0214a4de80316f4b4acb2addeccf6aa
--- /dev/null
+++ b/src/redux/project/project.types.ts
@@ -0,0 +1,7 @@
+import { Project } from '@/types/api';
+
+export type ProjectState = {
+  data: Project | undefined | [];
+  loading: 'idle' | 'pending' | 'succeeded' | 'failed';
+  error: Error;
+};
diff --git a/src/redux/search/search.reducers.ts b/src/redux/search/search.reducers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4f6f747d357f19c9a48c43041c484c4052c21600
--- /dev/null
+++ b/src/redux/search/search.reducers.ts
@@ -0,0 +1,7 @@
+// updating state
+import { SearchState } from '@/redux/search/search.types';
+import { PayloadAction } from '@reduxjs/toolkit';
+
+export const setSearchValueReducer = (state: SearchState, action: PayloadAction<string>): void => {
+  state.searchValue = action.payload;
+};
diff --git a/src/redux/search/search.selectors.ts b/src/redux/search/search.selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c845eecd0c4220dc245e95335f575ece55ecb804
--- /dev/null
+++ b/src/redux/search/search.selectors.ts
@@ -0,0 +1,4 @@
+import type { RootState } from '@/redux/store';
+
+// THIS IS EXAMPLE, it's not memoised!!!! Check redux-tookit docs.
+export const selectSearchValue = (state: RootState): string => state.search.searchValue;
diff --git a/src/redux/search/search.slice.ts b/src/redux/search/search.slice.ts
new file mode 100644
index 0000000000000000000000000000000000000000..92357f8c943fea34aae15b7978266df4c89ae760
--- /dev/null
+++ b/src/redux/search/search.slice.ts
@@ -0,0 +1,23 @@
+import { createSlice } from '@reduxjs/toolkit';
+import { SearchState } from '@/redux/search/search.types';
+import { setSearchValueReducer } from '@/redux/search/search.reducers';
+
+const initialState: SearchState = {
+  searchValue: '',
+  searchResult: {
+    content: '',
+    drugs: '',
+  },
+};
+
+export const searchSlice = createSlice({
+  name: 'search',
+  initialState,
+  reducers: {
+    setSearchValue: setSearchValueReducer,
+  },
+});
+
+export const { setSearchValue } = searchSlice.actions;
+
+export default searchSlice.reducer;
diff --git a/src/redux/search/search.types.ts b/src/redux/search/search.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6b6316a881f83aed58588d03b75c2f0ca0d5d9a1
--- /dev/null
+++ b/src/redux/search/search.types.ts
@@ -0,0 +1,9 @@
+export interface SearchResult {
+  content: string;
+  drugs: string;
+}
+
+export interface SearchState {
+  searchValue: string;
+  searchResult: SearchResult;
+}
diff --git a/src/redux/store.ts b/src/redux/store.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d1335018a8dc60c21274dfa9f543fdedaad9ec72
--- /dev/null
+++ b/src/redux/store.ts
@@ -0,0 +1,16 @@
+import { configureStore } from '@reduxjs/toolkit';
+import searchReducer from '@/redux/search/search.slice';
+import projectSlice from '@/redux/project/project.slice';
+
+export const store = configureStore({
+  reducer: {
+    search: searchReducer,
+    project: projectSlice,
+  },
+  devTools: true,
+});
+
+// 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;
diff --git a/src/services/.gitkeep b/src/services/.gitkeep
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/services/api/utils/axiosInstance.ts b/src/services/api/utils/axiosInstance.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5e50fba77c865c1e222f51a4c451c9d14197242e
--- /dev/null
+++ b/src/services/api/utils/axiosInstance.ts
@@ -0,0 +1,6 @@
+import axios from 'axios';
+import { BASE_API_URL } from '@/constants/api';
+
+export const axiosInstance = axios.create({
+  baseURL: BASE_API_URL,
+});
diff --git a/src/services/api/utils/useApiQuery.ts b/src/services/api/utils/useApiQuery.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d150d65626e1f3f7d086ef39a5a12bc9adc73c0c
--- /dev/null
+++ b/src/services/api/utils/useApiQuery.ts
@@ -0,0 +1,39 @@
+import { BASE_API_URL } from '@/constants/api';
+import { QueryOptions } from '@/types/api';
+
+import useAxios, { UseAxiosResult } from 'axios-hooks';
+import { useMemo } from 'react';
+import { AnyZodObject, ZodError } from 'zod';
+
+type UseApiQuery = <TResponse extends AnyZodObject>(
+  queryOptions: QueryOptions<TResponse>,
+) => UseAxiosResult<TResponse>;
+
+export const useApiQuery: UseApiQuery = <TResponse extends AnyZodObject>({
+  method,
+  path,
+  response,
+}: QueryOptions<TResponse>) => {
+  const [{ data: fetchData, loading, error }, refetch, cancelRequest] = useAxios<TResponse>({
+    method,
+    url: `${BASE_API_URL}${path}`,
+  });
+
+  const dataValidation: { success: boolean; error?: ZodError<TResponse> } = useMemo(() => {
+    if (!fetchData) {
+      return { success: false, error: undefined };
+    }
+
+    return {
+      error: undefined,
+      ...response.safeParse(fetchData),
+    };
+  }, [fetchData, response]);
+
+  const data = useMemo(
+    () => (dataValidation.success ? fetchData : undefined),
+    [dataValidation.success, fetchData],
+  );
+
+  return [{ data, loading, error }, refetch, cancelRequest];
+};
diff --git a/src/types/.gitkeep b/src/types/.gitkeep
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/types/api.ts b/src/types/api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..98a130766a65dbf3c7297c141a5776d741a8eaf0
--- /dev/null
+++ b/src/types/api.ts
@@ -0,0 +1,18 @@
+import { z } from 'zod';
+import { disease } from '@/models/disease';
+import { organism } from '@/models/organism';
+import { projectSchema } from '@/models/project';
+
+export interface QueryOptions<Response> {
+  method: 'GET' | 'POST';
+  path: string;
+  response: Response;
+}
+
+export interface Query<Params, Response> {
+  (params: Params): QueryOptions<Response>;
+}
+
+export type Project = z.infer<typeof projectSchema>;
+export type Organism = z.infer<typeof organism>;
+export type Disease = z.infer<typeof disease>;
diff --git a/src/utils/.gitkeep b/src/utils/.gitkeep
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/utils/validateDataUsingZodSchema.ts b/src/utils/validateDataUsingZodSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a7b0cf0858cc2e62842047cfea56bb5066324457
--- /dev/null
+++ b/src/utils/validateDataUsingZodSchema.ts
@@ -0,0 +1,15 @@
+import { ZodSchema } from 'zod';
+
+type IsApiResponseValid = <TData>(data: TData, schema: ZodSchema) => boolean;
+
+export const validateDataUsingZodSchema: IsApiResponseValid = (data, schema: ZodSchema) => {
+  const validationResults = schema.safeParse(data);
+
+  if (validationResults.success === false) {
+    // TODO - probably need to rething way of handling parsing errors, for now let's leave it to console.log
+    // eslint-disable-next-line no-console
+    console.error('Error on parsing data', validationResults.error);
+  }
+
+  return validationResults.success;
+};