This is an automated email from the ASF dual-hosted git repository.

piotr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git


The following commit(s) were added to refs/heads/master by this push:
     new 8b1d8dd3 feat(web): enhance ui interactions and improve code quality 
(#2078)
8b1d8dd3 is described below

commit 8b1d8dd3e34a5998be47cc3eea5f4f25c315e997
Author: Piotr Ziółko <50527113+piotrzio...@users.noreply.github.com>
AuthorDate: Mon Aug 11 09:30:52 2025 +0200

    feat(web): enhance ui interactions and improve code quality (#2078)
    
    This PR enhances the web interface with improved keyboard navigation and
    modal interactions, strengthens type safety by migrating from Svelte's
    on:click to onclick handlers, fixes message count calculation in
    partition view, adds the missing max_topic_size field to topic schema,
    and removes unused imports for cleaner code organization.
---
 web/package-lock.json                              | 527 +++++++++++++++++++++
 web/src/lib/actions/tooltip.ts                     |   2 +-
 web/src/lib/api/ApiSchema.ts                       |   1 +
 web/src/lib/components/Button.svelte               |  49 +-
 web/src/lib/components/Checkbox.svelte             |   5 +-
 web/src/lib/components/Combobox.svelte             |   3 +-
 .../components/DeleteButtonWithConfirmation.svelte |   4 +-
 .../components/DropdownMenu/DropdownMenu.svelte    |   2 +-
 web/src/lib/components/Input.svelte                |  62 ---
 .../lib/components/Layouts/SettingsLayout.svelte   |   2 +-
 .../MessageDecoder/MessageDecoder.svelte           |   6 +-
 web/src/lib/components/ModalConfirmation.svelte    |  18 +-
 .../components/Modals/AddPartitionsModal.svelte    |   4 +-
 .../lib/components/Modals/AddStreamModal.svelte    |   5 +-
 web/src/lib/components/Modals/AddTopicModal.svelte |   5 +-
 web/src/lib/components/Modals/AddUserModal.svelte  |   4 +-
 web/src/lib/components/Modals/AppModals.svelte     |  24 +-
 .../components/Modals/DeletePartitionsModal.svelte |   8 +-
 .../lib/components/Modals/DeleteUserModal.svelte   |   2 +-
 web/src/lib/components/Modals/EditUserModal.svelte |   2 +-
 .../Modals/EditUserPermissionsModal.svelte         |   2 +-
 web/src/lib/components/Modals/ModalBase.svelte     |   3 +-
 .../components/Modals/StreamSettingsModal.svelte   |  10 +-
 .../components/Modals/TopicSettingsModal.svelte    |  23 +-
 web/src/lib/components/Paginator.svelte            |  10 +-
 web/src/lib/components/PasswordInput.svelte        |   5 +-
 web/src/lib/components/PeriodicInvalidator.svelte  |  15 +-
 web/src/lib/components/PermissionsManager.svelte   |   2 +-
 web/src/lib/components/RangeInput.svelte           |   4 -
 .../RouteComponents/Settings/UsersTab.svelte       |   8 +-
 .../Settings/UsersTabActions.svelte                |   7 +-
 web/src/lib/components/Select.svelte               |   2 +-
 web/src/lib/components/StopPropagation.svelte      |  11 +
 web/src/lib/components/ThemeController.svelte      |   4 +-
 web/src/lib/components/Toggler.svelte              |   3 +-
 web/src/lib/domain/Partition.ts                    |   2 +-
 web/src/lib/domain/Permissions.ts                  |   1 -
 web/src/lib/domain/Stream.ts                       |   2 +-
 web/src/lib/queries.ts                             |   5 -
 .../lib/utils/{dataHas.ts => constants/keys.ts}    |  83 ++--
 web/src/lib/utils/dataHas.ts                       |   2 -
 web/src/lib/utils/formatters/bytesFormatter.ts     |   2 -
 web/src/lib/utils/formatters/durationFormatter.ts  |   1 -
 web/src/routes/+error.svelte                       |   2 -
 web/src/routes/auth/sign-in/+page.svelte           |   7 +-
 web/src/routes/dashboard/overview/+page.svelte     |   2 +-
 .../routes/dashboard/settings/server/+page.svelte  |   4 -
 .../routes/dashboard/settings/users/+page.svelte   |  10 +-
 .../routes/dashboard/settings/webUI/+page.svelte   |   2 +-
 web/src/routes/dashboard/streams/+layout.svelte    |   7 +-
 .../dashboard/streams/[streamId=i32]/+page.svelte  | 167 +++----
 .../topics/[topicId=i32]/+page.svelte              | 176 +++----
 .../[partitionId=i32]/messages/+page.server.ts     |   6 +-
 .../[partitionId=i32]/messages/+page.svelte        | 228 ++++-----
 54 files changed, 1029 insertions(+), 524 deletions(-)

diff --git a/web/package-lock.json b/web/package-lock.json
index 6548368d..042db8e2 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -101,6 +101,474 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz";,
+      "integrity": 
"sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz";,
+      "integrity": 
"sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz";,
+      "integrity": 
"sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz";,
+      "integrity": 
"sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz";,
+      "integrity": 
"sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz";,
+      "integrity": 
"sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz";,
+      "integrity": 
"sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz";,
+      "integrity": 
"sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz";,
+      "integrity": 
"sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz";,
+      "integrity": 
"sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz";,
+      "integrity": 
"sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz";,
+      "integrity": 
"sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz";,
+      "integrity": 
"sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz";,
+      "integrity": 
"sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz";,
+      "integrity": 
"sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz";,
+      "integrity": 
"sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz";,
+      "integrity": 
"sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-arm64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz";,
+      "integrity": 
"sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz";,
+      "integrity": 
"sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz";,
+      "integrity": 
"sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz";,
+      "integrity": 
"sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openharmony-arm64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz";,
+      "integrity": 
"sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz";,
+      "integrity": 
"sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz";,
+      "integrity": 
"sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz";,
+      "integrity": 
"sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.25.8",
+      "resolved": 
"https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz";,
+      "integrity": 
"sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "peer": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/@eslint-community/eslint-utils": {
       "version": "4.7.0",
       "resolved": 
"https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz";,
@@ -1801,6 +2269,50 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/esbuild": {
+      "version": "0.25.8",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz";,
+      "integrity": 
"sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "peer": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.25.8",
+        "@esbuild/android-arm": "0.25.8",
+        "@esbuild/android-arm64": "0.25.8",
+        "@esbuild/android-x64": "0.25.8",
+        "@esbuild/darwin-arm64": "0.25.8",
+        "@esbuild/darwin-x64": "0.25.8",
+        "@esbuild/freebsd-arm64": "0.25.8",
+        "@esbuild/freebsd-x64": "0.25.8",
+        "@esbuild/linux-arm": "0.25.8",
+        "@esbuild/linux-arm64": "0.25.8",
+        "@esbuild/linux-ia32": "0.25.8",
+        "@esbuild/linux-loong64": "0.25.8",
+        "@esbuild/linux-mips64el": "0.25.8",
+        "@esbuild/linux-ppc64": "0.25.8",
+        "@esbuild/linux-riscv64": "0.25.8",
+        "@esbuild/linux-s390x": "0.25.8",
+        "@esbuild/linux-x64": "0.25.8",
+        "@esbuild/netbsd-arm64": "0.25.8",
+        "@esbuild/netbsd-x64": "0.25.8",
+        "@esbuild/openbsd-arm64": "0.25.8",
+        "@esbuild/openbsd-x64": "0.25.8",
+        "@esbuild/openharmony-arm64": "0.25.8",
+        "@esbuild/sunos-x64": "0.25.8",
+        "@esbuild/win32-arm64": "0.25.8",
+        "@esbuild/win32-ia32": "0.25.8",
+        "@esbuild/win32-x64": "0.25.8"
+      }
+    },
     "node_modules/esbuild-runner": {
       "version": "2.2.2",
       "resolved": 
"https://registry.npmjs.org/esbuild-runner/-/esbuild-runner-2.2.2.tgz";,
@@ -4041,6 +4553,21 @@
         }
       }
     },
+    "node_modules/svelte-check/node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz";,
+      "integrity": 
"sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "peer": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert";
+      }
+    },
     "node_modules/svelte-eslint-parser": {
       "version": "0.43.0",
       "resolved": 
"https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.43.0.tgz";,
diff --git a/web/src/lib/actions/tooltip.ts b/web/src/lib/actions/tooltip.ts
index a10b590d..1c180679 100644
--- a/web/src/lib/actions/tooltip.ts
+++ b/web/src/lib/actions/tooltip.ts
@@ -51,7 +51,7 @@ export function tooltip(
   let cleanup: VoidFunction | undefined;
 
   const unsub = openId.subscribe((val) => (val === id ? showTooltip() : 
hideTooltip()));
-  const openTooltip = () => openId.set(id);
+
   const closeTooltip = () => openId.set(null);
   const toggleOpen = () => openId.update((val) => (val === id ? null : id));
 
diff --git a/web/src/lib/api/ApiSchema.ts b/web/src/lib/api/ApiSchema.ts
index 0c041803..b553a77f 100644
--- a/web/src/lib/api/ApiSchema.ts
+++ b/web/src/lib/api/ApiSchema.ts
@@ -137,6 +137,7 @@ type Topics =
         name: string;
         message_expiry: number;
         compression_algorithm: number;
+        max_topic_size: number;
       };
     }
   | {
diff --git a/web/src/lib/components/Button.svelte 
b/web/src/lib/components/Button.svelte
index b6be20b5..ba20233a 100644
--- a/web/src/lib/components/Button.svelte
+++ b/web/src/lib/components/Button.svelte
@@ -1,6 +1,3 @@
-<!-- @migration-task Error while migrating Svelte code: This migration would 
change the name of a slot making the component unusable -->
-<!-- @migration-task Error while migrating Svelte code: This migration would 
change the name of a slot making the component unusable -->
-<!-- // TODO: check it - Error while migrating Svelte code: This migration 
would change the name of a slot making the component unusable -->
 <script lang="ts">
   import { tooltip } from '$lib/actions/tooltip';
   import type { Placement } from '@floating-ui/dom';
@@ -33,49 +30,39 @@
     xl: 'px-7 py-4 text-lg'
   };
 
-  interface $$Props extends HTMLButtonAttributes {
+  interface Props extends HTMLButtonAttributes {
     variant: keyof typeof variants;
     tooltipPlacement?: Placement;
     size?: keyof typeof sizes;
     class?: string;
+    children?: import('svelte').Snippet;
+    tooltip?: import('svelte').Snippet;
   }
 
-  export let variant: keyof typeof variants;
-  export let tooltipPlacement: Placement = 'right';
-  export let size: keyof typeof sizes = 'md';
-  let className = '';
-  export { className as class };
+  let { 
+    variant, 
+    tooltipPlacement = 'right', 
+    size = 'md', 
+    class: className = '',
+    children,
+    tooltip: tooltipSnippet,
+    onclick,
+    ...restProps 
+  }: Props = $props();
 </script>
 
-<!-- <div use:tooltip={{ placement: tooltipPlacement }} class="w-full">
-  <button
-    on:click
-    data-trigger
-    class={twMerge(baseClasses, disabledClasses, variants[variant], 
sizes[size], className)}
-    {...$$restProps}
-  >
-    <slot />
-  </button>
-
-  {#if $$slots.tooltip}
-    <div role="tooltip" class="tooltip">
-      <slot name="tooltip" />
-    </div>
-  {/if}
-</div> -->
-
 <button
-  on:click
+  {onclick}
   data-trigger
   use:tooltip={{ placement: tooltipPlacement, isTrigger: true }}
   class={twMerge(baseClasses, variants[variant], sizes[size], disabledClasses, 
className, ' ')}
-  {...$$restProps}
+  {...restProps}
 >
-  <slot />
+  {@render children?.()}
 
-  {#if $$slots.tooltip}
+  {#if tooltipSnippet}
     <div role="tooltip" class="tooltip ring-bl">
-      <slot name="tooltip" />
+      {@render tooltipSnippet()}
     </div>
   {/if}
 </button>
diff --git a/web/src/lib/components/Checkbox.svelte 
b/web/src/lib/components/Checkbox.svelte
index cd74c48b..8d8410c8 100644
--- a/web/src/lib/components/Checkbox.svelte
+++ b/web/src/lib/components/Checkbox.svelte
@@ -12,6 +12,7 @@
     name?: string | undefined;
     bindGroup?: string[] | undefined;
     disabled?: boolean;
+    onclick?: (e: Event) => void;
   }
 
   let {
@@ -20,7 +21,8 @@
     id = '',
     name = undefined,
     bindGroup = $bindable(undefined),
-    disabled = false
+    disabled = false,
+    onclick
   }: Props = $props();
 
   function onChange(e: Event) {
@@ -47,6 +49,7 @@
     {id}
     {disabled}
     onchange={handlers(onChange, bubble('change'))}
+    onclick={onclick}
     onmousedown={() => (isMouseDown = true)}
     onmouseup={() => (isMouseDown = false)}
     onmouseleave={() => (isMouseDown = false)}
diff --git a/web/src/lib/components/Combobox.svelte 
b/web/src/lib/components/Combobox.svelte
index 1f7c66de..dcc43d43 100644
--- a/web/src/lib/components/Combobox.svelte
+++ b/web/src/lib/components/Combobox.svelte
@@ -67,7 +67,7 @@
 
 <div class="flex flex-col gap-2">
   {#if label}
-    <label class="text-sm ml-1 text-color">
+    <label for="combobox-input" class="text-sm ml-1 text-color">
       {label}
     </label>
   {/if}
@@ -77,6 +77,7 @@
       class="rounded-md dark:bg-shadeD400 ring-1 text-color ring-gray-300 
dark:ring-gray-500 flex items-center h-[40px] text-color relative 
focus-within:ring-2 focus-within:ring-gray-400 transition group"
     >
       <input
+        id="combobox-input"
         use:combobox.input
         onselect={(e) => {
           selectedValue = noTypeCheck(e).detail.selected;
diff --git a/web/src/lib/components/DeleteButtonWithConfirmation.svelte 
b/web/src/lib/components/DeleteButtonWithConfirmation.svelte
index 24dc237e..82ad7cc0 100644
--- a/web/src/lib/components/DeleteButtonWithConfirmation.svelte
+++ b/web/src/lib/components/DeleteButtonWithConfirmation.svelte
@@ -31,13 +31,13 @@
 </script>
 
 <div bind:this={wrapperRef} use:tooltip={{ placement: 'top' }}>
-  <Button variant="containedRed" on:click={() => (isTooltipOpen = 
true)}>Delete</Button>
+  <Button variant="containedRed" onclick={() => (isTooltipOpen = 
true)}>Delete</Button>
 
   <div class="tooltip">
     <div class="flex flex-col gap-4 items-center justify-center p-2">
       <span>Are you sure? </span>
       <div class="flex flex-row gap-2">
-        <Button variant="text" type="button" on:click={() => (isTooltipOpen = 
false)} size="sm"
+        <Button variant="text" type="button" onclick={() => (isTooltipOpen = 
false)} size="sm"
           >No</Button
         >
         <Button
diff --git a/web/src/lib/components/DropdownMenu/DropdownMenu.svelte 
b/web/src/lib/components/DropdownMenu/DropdownMenu.svelte
index f2688f87..456c337c 100644
--- a/web/src/lib/components/DropdownMenu/DropdownMenu.svelte
+++ b/web/src/lib/components/DropdownMenu/DropdownMenu.svelte
@@ -36,7 +36,7 @@
   <div class="px-1 py-1">
     {#each group as { action, icon, className, label }}
       <button
-        on:click={() => {
+        onclick={() => {
           if (action) {
             action(() => tooltipRef.dispatchEvent(new Event('closeTooltip')));
           }
diff --git a/web/src/lib/components/Input.svelte 
b/web/src/lib/components/Input.svelte
index b8cbb303..cf484e6b 100644
--- a/web/src/lib/components/Input.svelte
+++ b/web/src/lib/components/Input.svelte
@@ -1,65 +1,3 @@
-<!-- <script lang="ts">
-  import type { HTMLInputAttributes, HTMLInputTypeAttribute } from 
'svelte/elements';
-  import { twMerge } from 'tailwind-merge';
-  import type { iconType } from './Icon.svelte';
-  import Icon from './Icon.svelte';
-
-  interface $$Props extends HTMLInputAttributes {
-    name: string;
-    id?: string;
-    label?: string;
-    value: string | number;
-    errorMessage?: string;
-    type?: HTMLInputTypeAttribute;
-  }
-
-  export let name: string;
-  export let id: string = crypto.randomUUID();
-  export let leadingIcon: iconType | undefined = undefined;
-  export let label: string | undefined = undefined;
-  export let value: string | number;
-  export let type: HTMLInputTypeAttribute = 'text';
-</script>
-
-<div class="flex flex-col gap-1">
-  {#if label}
-    <label for={id} class="text-sm ml-1 text-color">
-      {label}
-    </label>
-  {/if}
-
-  <div
-    class={twMerge(
-      'rounded-md border border-gray-300 dark:border-none outline-none 
dark:bg-shadeD400 text-color px-3 w-full relative',
-      error && 'border-red-600 text-red-600 border-none outline-red-600',
-      leadingIcon && 'pl-8',
-      $$restProps.class
-    )}
-  >
-    {#if leadingIcon}
-      <Icon
-        name={leadingIcon}
-        className="absolute text-gray-400 left-2 top-1/2 -translate-y-1/2 
w-[18px]"
-      />
-    {/if}
-
-    <input
-      bind:value
-      {...$$restProps}
-      autocomplete="off"
-      {id}
-      {name}
-      class="w-full h-full bg-transparent py-2 outline-none"
-    />
-  </div>
-
-  {#if errorMe}
-    <span class="text-red-600 text-xs ml-1 mt-1">
-      {error}
-    </span>
-  {/if}
-</div> -->
-
 <script lang="ts">
   import { createBubbler } from 'svelte/legacy';
 
diff --git a/web/src/lib/components/Layouts/SettingsLayout.svelte 
b/web/src/lib/components/Layouts/SettingsLayout.svelte
index af7b6c31..d5cd5b44 100644
--- a/web/src/lib/components/Layouts/SettingsLayout.svelte
+++ b/web/src/lib/components/Layouts/SettingsLayout.svelte
@@ -51,7 +51,7 @@
 </div>
 
 <div class="flex gap-12 border-b px-10">
-  {#each tabs as { icon, name, tab, href }, idx (idx)}
+  {#each tabs as { icon, name, href }, idx (idx)}
     {@const isActive = activeTab === href.split('/').slice(-1)[0]}
     <a
       {href}
diff --git a/web/src/lib/components/MessageDecoder/MessageDecoder.svelte 
b/web/src/lib/components/MessageDecoder/MessageDecoder.svelte
index a1d26c65..4754c70f 100644
--- a/web/src/lib/components/MessageDecoder/MessageDecoder.svelte
+++ b/web/src/lib/components/MessageDecoder/MessageDecoder.svelte
@@ -80,7 +80,7 @@
       />
     </div>
   {/if}
-  <Button variant="contained" on:click={decode}>Decode</Button>
+  <Button variant="contained" onclick={decode}>Decode</Button>
 {:else}
   {#if decodeState.status === 'error'}
     <div class="bg-red-100 dark:bg-red-900 p-4 rounded-md w-full">
@@ -92,7 +92,7 @@
   {#if decodeState.status === 'success'}
     <div class="flex items-center gap-3">
       <p class="font-bold">Codec: {selectedDecoder}</p>
-      <Button variant="text" on:click={() => (decodeState = { status: 'idle', 
forceChangeDecoder: true })}>Change decoder</Button>
+      <Button variant="text" onclick={() => (decodeState = { status: 'idle', 
forceChangeDecoder: true })}>Change decoder</Button>
     </div>
     <div class="w-full mt-2">
       <pre class="bg-gray-100 dark:bg-gray-800 p-1 rounded-md 
overflow-auto"><code
@@ -102,7 +102,7 @@
   {/if}
 
   <div class="mt-4 mb-2">
-    <Button variant="text" on:click={() => (showRaw = !showRaw)}>
+    <Button variant="text" onclick={() => (showRaw = !showRaw)}>
       {showRaw ? 'Hide' : 'Show'} raw payload (base64)
     </Button>
   </div>
diff --git a/web/src/lib/components/ModalConfirmation.svelte 
b/web/src/lib/components/ModalConfirmation.svelte
index 05a8d97b..8eeb06b9 100644
--- a/web/src/lib/components/ModalConfirmation.svelte
+++ b/web/src/lib/components/ModalConfirmation.svelte
@@ -4,6 +4,7 @@
   import { createEventDispatcher } from 'svelte';
   import Input from './Input.svelte';
   import Icon from './Icon.svelte';
+  import { Keys } from '$lib/utils/constants/keys';
 
   interface Props {
     open: boolean;
@@ -32,6 +33,15 @@
   <div
     transition:fade={{ duration: 100 }}
     onclick={() => dispatch('result', false)}
+    onkeydown={(e) => {
+      if (e.key === Keys.ENTER || e.key === Keys.SPACE) {
+        e.preventDefault();
+        dispatch('result', false);
+      }
+    }}
+    role="button"
+    tabindex="0"
+    aria-label="Close confirmation dialog"
     class="absolute z-40 backdrop-blur-xs rounded-2xl inset-3"
 ></div>
   <div
@@ -41,7 +51,7 @@
     <div class="p-5 pt-10 flex flex-col items-center border-b relative 
text-color">
       <Button
         variant="rounded"
-        on:click={() => dispatch('result', false)}
+        onclick={() => dispatch('result', false)}
         class="absolute top-3 right-3 p-2"
       >
         <Icon name="close" strokeWidth={2.3} />
@@ -63,7 +73,7 @@
       <div class="w-full">
         <Button
           variant="containedRed"
-          on:click={() => dispatch('result', true)}
+          onclick={() => dispatch('result', true)}
           disabled={retypedText !== retypeText}
           class="w-full">{deleteButtonTitle}</Button
         >
@@ -73,8 +83,8 @@
 {/if}
 
 <!-- <div class="flex gap-2 p-5 py-10 w-full">
-  <Button variant="containedRed" class="flex-1" on:click={() => 
dispatch('result', true)}
+  <Button variant="containedRed" class="flex-1" onclick={() => 
dispatch('result', true)}
     >Yes</Button
   >
-  <Button variant="text" class="flex-1" on:click={() => dispatch('result', 
false)}>No</Button>
+  <Button variant="text" class="flex-1" onclick={() => dispatch('result', 
false)}>No</Button>
 </div> -->
diff --git a/web/src/lib/components/Modals/AddPartitionsModal.svelte 
b/web/src/lib/components/Modals/AddPartitionsModal.svelte
index a887cccb..fd470d78 100644
--- a/web/src/lib/components/Modals/AddPartitionsModal.svelte
+++ b/web/src/lib/components/Modals/AddPartitionsModal.svelte
@@ -9,7 +9,6 @@
   import { fetchRouteApi } from '$lib/api/fetchRouteApi';
   import { page } from '$app/state';
   import { dataHas } from '$lib/utils/dataHas';
-  import { invalidateAll } from '$app/navigation';
   import { showToast } from '../AppToasts.svelte';
   import Button from '../Button.svelte';
   import { customInvalidateAll } from '../PeriodicInvalidator.svelte';
@@ -31,6 +30,7 @@
     taintedMessage: false,
     async onUpdate({ form }) {
       if (!form.valid) return;
+      if (!page.params.streamId || !page.params.topicId) return;
 
       const { data, ok } = await fetchRouteApi({
         method: 'POST',
@@ -72,7 +72,7 @@
     />
 
     <div class="flex justify-end gap-3 mt-auto">
-      <Button variant="text" type="button" class="w-2/5" on:click={() => 
closeModal()}
+      <Button variant="text" type="button" class="w-2/5" onclick={() => 
closeModal()}
         >Cancel</Button
       >
       <Button type="submit" variant="contained" class="w-2/5">Create</Button>
diff --git a/web/src/lib/components/Modals/AddStreamModal.svelte 
b/web/src/lib/components/Modals/AddStreamModal.svelte
index 709d68f3..3c82ed5a 100644
--- a/web/src/lib/components/Modals/AddStreamModal.svelte
+++ b/web/src/lib/components/Modals/AddStreamModal.svelte
@@ -9,7 +9,6 @@
   import { showToast } from '../AppToasts.svelte';
   import { fetchRouteApi } from '$lib/api/fetchRouteApi';
   import { dataHas } from '$lib/utils/dataHas';
-  import { invalidateAll } from '$app/navigation';
   import { numberSizes } from '$lib/utils/constants/numberSizes';
   import { customInvalidateAll } from '../PeriodicInvalidator.svelte';
 
@@ -28,7 +27,7 @@
       .max(255, 'Name must not exceed 255 characters')
   });
 
-  const { form, errors, enhance, constraints, submitting, reset } = superForm(
+  const { form, errors, enhance, constraints, submitting } = superForm(
     defaults(zod(schema)),
     {
       SPA: true,
@@ -84,7 +83,7 @@
     />
 
     <div class="flex justify-end gap-3 mt-auto w-full">
-      <Button type="button" variant="text" class="w-2/5" on:click={() => 
closeModal()}
+      <Button type="button" variant="text" class="w-2/5" onclick={() => 
closeModal()}
         >Cancel</Button
       >
       <Button type="submit" variant="contained" class="w-2/5" 
disabled={$submitting}>Create</Button>
diff --git a/web/src/lib/components/Modals/AddTopicModal.svelte 
b/web/src/lib/components/Modals/AddTopicModal.svelte
index 0717d32e..30cf4220 100644
--- a/web/src/lib/components/Modals/AddTopicModal.svelte
+++ b/web/src/lib/components/Modals/AddTopicModal.svelte
@@ -9,11 +9,9 @@
   import type { CloseModalFn } from '$lib/types/utilTypes';
   import type { StreamDetails } from '$lib/domain/StreamDetails';
   import { fetchRouteApi } from '$lib/api/fetchRouteApi';
-  import { intervalToDuration } from 'date-fns';
   import { durationFormatter } from '$lib/utils/formatters/durationFormatter';
   import { numberSizes } from '$lib/utils/constants/numberSizes';
   import { dataHas } from '$lib/utils/dataHas';
-  import { invalidateAll } from '$app/navigation';
   import { showToast } from '../AppToasts.svelte';
   import { customInvalidateAll } from '../PeriodicInvalidator.svelte';
 
@@ -48,6 +46,7 @@
         method: 'POST',
         path: `/streams/${streamDetails.id}/topics`,
         body: {
+          stream_id: streamDetails.id,
           topic_id: form.data.topic_id,
           name: form.data.name,
           partitions_count: form.data.partitions_count,
@@ -140,7 +139,7 @@
     />
 
     <div class="flex justify-end gap-3 mt-auto">
-      <Button variant="text" type="button" class="w-2/5" on:click={() => 
closeModal()}
+      <Button variant="text" type="button" class="w-2/5" onclick={() => 
closeModal()}
         >Cancel</Button
       >
       <Button type="submit" variant="contained" class="w-2/5">Create</Button>
diff --git a/web/src/lib/components/Modals/AddUserModal.svelte 
b/web/src/lib/components/Modals/AddUserModal.svelte
index 8c37d711..ed3725b0 100644
--- a/web/src/lib/components/Modals/AddUserModal.svelte
+++ b/web/src/lib/components/Modals/AddUserModal.svelte
@@ -92,7 +92,7 @@
           { name: 'Inactive', value: 'inactive' }
         ]}
         selectedValue={$form.status}
-        on:selectedValue={(e) => $form.status = e.detail}
+        on:selectedValue={(e) => $form.status = e.detail as 'active' | 
'inactive'}
       />
     </div>
 
@@ -102,7 +102,7 @@
     />
 
     <div class="flex justify-end gap-3 mt-16 w-[350px] ml-auto">
-      <Button variant="text" type="button" class="w-2/5" on:click={() => 
closeModal()}>
+      <Button variant="text" type="button" class="w-2/5" onclick={() => 
closeModal()}>
         Cancel
       </Button>
       <Button type="submit" variant="contained" class="w-2/5">Add</Button>
diff --git a/web/src/lib/components/Modals/AppModals.svelte 
b/web/src/lib/components/Modals/AppModals.svelte
index 043e97d9..c40e3549 100644
--- a/web/src/lib/components/Modals/AppModals.svelte
+++ b/web/src/lib/components/Modals/AppModals.svelte
@@ -1,5 +1,5 @@
 <script lang="ts" module>
-  import type { ComponentProps, ComponentType, SvelteComponent } from 'svelte';
+  import type { ComponentProps } from 'svelte';
   import AddPartitionsModal from './AddPartitionsModal.svelte';
   import AddStreamModal from './AddStreamModal.svelte';
   import AddTopicModal from './AddTopicModal.svelte';
@@ -14,6 +14,7 @@
   import { fade } from 'svelte/transition';
   import { noTypeCheck } from '$lib/utils/noTypeCheck';
   import { writable } from 'svelte/store';
+  import { Keys } from '$lib/utils/constants/keys';
 
   const modals = {
     AddPartitionsModal,
@@ -31,7 +32,7 @@
 
   type DistributiveOmit<T, K extends string> = T extends T ? Omit<T, K> : 
never;
   type AllModals = keyof typeof modals;
-  type ModalProps<T extends AllModals> = ComponentProps<InstanceType<(typeof 
modals)[T]>>;
+  type ModalProps<T extends AllModals> = ComponentProps<(typeof modals)[T]>;
   type ExtraProps<T extends AllModals> = DistributiveOmit<ModalProps<T>, 
'closeModal'>;
 
   const openedModal = writable<
@@ -68,14 +69,27 @@
 
 <svelte:window
   onkeydown={(e) => {
-    if (e.key === 'Escape' && $openedModal) {
-      $openedModal.props.closeModal();
+    if (e.key === Keys.ESCAPE && $openedModal) {
+      $openedModal?.props.closeModal();
     }
   }}
 />
 
 {#if $openedModal}
-  <div transition:fade={{ duration: 100 }} class="fixed inset-0 bg-black/40 
z-[500]" onclick={$openedModal.props.closeModal} role="button" 
tabindex={1}></div>
+  <div 
+    transition:fade={{ duration: 100 }} 
+    class="fixed inset-0 bg-black/40 z-[500]" 
+    onclick={() => $openedModal.props.closeModal()}
+    onkeydown={(e) => {
+       if (e.key === Keys.ENTER || e.key === Keys.SPACE) {
+         e.preventDefault();
+         $openedModal?.props.closeModal();
+       }
+     }}
+    role="button" 
+    tabindex="0"
+    aria-label="Close modal"
+  ></div>
   {@const SvelteComponent_1 = noTypeCheck(modals[$openedModal.modal])}
   <SvelteComponent_1
     {...$openedModal.props}
diff --git a/web/src/lib/components/Modals/DeletePartitionsModal.svelte 
b/web/src/lib/components/Modals/DeletePartitionsModal.svelte
index 484a1ca6..0d976ccb 100644
--- a/web/src/lib/components/Modals/DeletePartitionsModal.svelte
+++ b/web/src/lib/components/Modals/DeletePartitionsModal.svelte
@@ -11,7 +11,6 @@
   import ModalConfirmation from '../ModalConfirmation.svelte';
   import { fetchRouteApi } from '$lib/api/fetchRouteApi';
   import { page } from '$app/state';
-  import { invalidateAll } from '$app/navigation';
   import { showToast } from '../AppToasts.svelte';
   import { dataHas } from '$lib/utils/dataHas';
   import { customInvalidateAll } from '../PeriodicInvalidator.svelte';
@@ -41,7 +40,7 @@
     partitions_count: 
z.coerce.number().min(1).max(topic.partitionsCount).default(1)
   });
 
-  const { form, errors, enhance, constraints, submitting, validateForm } = 
superForm(
+  const { form, errors, enhance, constraints, validateForm } = superForm(
     defaults(zod(schema)),
     {
       SPA: true,
@@ -49,6 +48,7 @@
 
       async onUpdate({ form }) {
         if (!form.valid) return;
+        if (!page.params.streamId || !page.params.topicId) return;
 
         const { data, ok } = await fetchRouteApi({
           method: 'DELETE',
@@ -111,14 +111,14 @@
       />
 
       <div class="flex justify-end gap-3 mt-auto">
-        <Button type="button" variant="text" class="w-2/5" on:click={() => 
closeModal()}
+        <Button type="button" variant="text" class="w-2/5" onclick={() => 
closeModal()}
           >Cancel</Button
         >
         <Button
           type="button"
           variant="containedRed"
           class="w-2/5"
-          on:click={async () => {
+          onclick={async () => {
             const result = await validateForm();
             if (result.valid) confirmationOpen = true;
           }}>Delete</Button
diff --git a/web/src/lib/components/Modals/DeleteUserModal.svelte 
b/web/src/lib/components/Modals/DeleteUserModal.svelte
index ec6c5dc1..0ed1a5f5 100644
--- a/web/src/lib/components/Modals/DeleteUserModal.svelte
+++ b/web/src/lib/components/Modals/DeleteUserModal.svelte
@@ -13,7 +13,7 @@
 <ModalBase {closeModal} title="Delete user">
   <div class="h-[300px] flex flex-col">
     <!-- <div class="flex justify-end gap-3 mt-auto">
-              <Button variant="text" class="w-2/5" 
on:click={closeModal}>Cancel</Button>
+              <Button variant="text" class="w-2/5" 
onclick={closeModal}>Cancel</Button>
               <Button type="submit" variant="contained" 
class="w-2/5">Save</Button>
             </div> -->
   </div>
diff --git a/web/src/lib/components/Modals/EditUserModal.svelte 
b/web/src/lib/components/Modals/EditUserModal.svelte
index 17fe4d56..4af756fb 100644
--- a/web/src/lib/components/Modals/EditUserModal.svelte
+++ b/web/src/lib/components/Modals/EditUserModal.svelte
@@ -13,7 +13,7 @@
 <ModalBase {closeModal} title="Edit user">
   <div class="h-[300px] flex flex-col">
     <!-- <div class="flex justify-end gap-3 mt-auto">
-              <Button variant="text" class="w-2/5" 
on:click={closeModal}>Cancel</Button>
+              <Button variant="text" class="w-2/5" 
onclick={closeModal}>Cancel</Button>
               <Button type="submit" variant="contained" 
class="w-2/5">Save</Button>
             </div> -->
   </div>
diff --git a/web/src/lib/components/Modals/EditUserPermissionsModal.svelte 
b/web/src/lib/components/Modals/EditUserPermissionsModal.svelte
index 7d511fb8..da437642 100644
--- a/web/src/lib/components/Modals/EditUserPermissionsModal.svelte
+++ b/web/src/lib/components/Modals/EditUserPermissionsModal.svelte
@@ -13,7 +13,7 @@
 <ModalBase {closeModal} title="Edit user permissions">
   <div class="h-[300px] flex flex-col">
     <!-- <div class="flex justify-end gap-3 mt-auto">
-              <Button variant="text" class="w-2/5" 
on:click={closeModal}>Cancel</Button>
+              <Button variant="text" class="w-2/5" 
onclick={closeModal}>Cancel</Button>
               <Button type="submit" variant="contained" 
class="w-2/5">Save</Button>
             </div> -->
   </div>
diff --git a/web/src/lib/components/Modals/ModalBase.svelte 
b/web/src/lib/components/Modals/ModalBase.svelte
index b07a845b..ef302b82 100644
--- a/web/src/lib/components/Modals/ModalBase.svelte
+++ b/web/src/lib/components/Modals/ModalBase.svelte
@@ -1,5 +1,4 @@
 <script lang="ts">
-  import type { HTMLAttributes } from 'svelte/elements';
   import { twMerge } from 'tailwind-merge';
   import Icon from '../Icon.svelte';
   import type { TransitionConfig } from 'svelte/transition';
@@ -54,7 +53,7 @@
   )}
 >
   <div class="h-[15%]">
-    <Button variant="rounded" on:click={() => closeModal()} class="absolute 
p-2 top-5 right-5">
+    <Button variant="rounded" onclick={() => closeModal()} class="absolute p-2 
top-5 right-5">
       <Icon name="close" strokeWidth={2.3} />
     </Button>
 
diff --git a/web/src/lib/components/Modals/StreamSettingsModal.svelte 
b/web/src/lib/components/Modals/StreamSettingsModal.svelte
index 26da1276..4d620df8 100644
--- a/web/src/lib/components/Modals/StreamSettingsModal.svelte
+++ b/web/src/lib/components/Modals/StreamSettingsModal.svelte
@@ -11,7 +11,7 @@
   import { zod } from 'sveltekit-superforms/adapters';
   import { fetchRouteApi } from '$lib/api/fetchRouteApi';
   import { dataHas } from '$lib/utils/dataHas';
-  import { goto, invalidateAll } from '$app/navigation';
+  import { goto } from '$app/navigation';
   import { showToast } from '../AppToasts.svelte';
   import ModalConfirmation from '../ModalConfirmation.svelte';
   import { typedRoute } from '$lib/types/appRoutes';
@@ -36,7 +36,7 @@
       .default(stream.name)
   });
 
-  const { form, errors, enhance, constraints, submitting, reset, tainted } = 
superForm(
+  const { form, errors, enhance, submitting, tainted } = superForm(
     defaults(zod(schema)),
     {
       SPA: true,
@@ -77,7 +77,7 @@
     confirmationOpen = false;
 
     if (result) {
-      const { data, ok } = await fetchRouteApi({
+      const { ok } = await fetchRouteApi({
         method: 'DELETE',
         path: `/streams/${stream.id}`
       });
@@ -129,7 +129,7 @@
       />
 
       <div class="flex justify-end gap-3 w-full mt-auto">
-        <Button type="button" variant="text" class="w-2/5" on:click={() => 
closeModal()}
+        <Button type="button" variant="text" class="w-2/5" onclick={() => 
closeModal()}
           >Cancel</Button
         >
 
@@ -150,7 +150,7 @@
           <Button
             variant="containedRed"
             class="max-h-[36px]"
-            on:click={() => (confirmationOpen = true)}
+            onclick={() => (confirmationOpen = true)}
           >
             <Icon name="trash" class="w-[20px] -ml-1" />
             Delete</Button
diff --git a/web/src/lib/components/Modals/TopicSettingsModal.svelte 
b/web/src/lib/components/Modals/TopicSettingsModal.svelte
index 69248580..3172416c 100644
--- a/web/src/lib/components/Modals/TopicSettingsModal.svelte
+++ b/web/src/lib/components/Modals/TopicSettingsModal.svelte
@@ -1,6 +1,6 @@
 <script lang="ts">
-  import type { StreamDetails } from '$lib/domain/StreamDetails';
   import type { CloseModalFn } from '$lib/types/utilTypes';
+  import type { TopicDetails } from '$lib/domain/TopicDetails';
   import { z } from 'zod';
 
   import Button from '../Button.svelte';
@@ -11,15 +11,13 @@
   import { zod } from 'sveltekit-superforms/adapters';
   import { fetchRouteApi } from '$lib/api/fetchRouteApi';
   import { dataHas } from '$lib/utils/dataHas';
-  import { goto, invalidateAll } from '$app/navigation';
+  import { goto } from '$app/navigation';
   import { showToast } from '../AppToasts.svelte';
   import ModalConfirmation from '../ModalConfirmation.svelte';
-  import { typedRoute } from '$lib/types/appRoutes';
   import { browser } from '$app/environment';
-  import type { TopicDetails } from '$lib/domain/TopicDetails';
+  import { customInvalidateAll } from '../PeriodicInvalidator.svelte';
   import { numberSizes } from '$lib/utils/constants/numberSizes';
   import { page } from '$app/state';
-  import { customInvalidateAll } from '../PeriodicInvalidator.svelte';
   import { durationFormatter } from '$lib/utils/formatters/durationFormatter';
 
   interface Props {
@@ -41,7 +39,7 @@
     message_expiry: 
z.number().min(0).max(numberSizes.max.u32).default(topic.messageExpiry)
   });
 
-  const { form, errors, enhance, constraints, submitting, reset, tainted } = 
superForm(
+  const { form, errors, enhance, constraints, submitting, tainted } = 
superForm(
     defaults(zod(schema)),
     {
       SPA: true,
@@ -50,13 +48,16 @@
       taintedMessage: false,
       async onUpdate({ form }) {
         if (!form.valid) return;
+        if (!page.params.streamId) return;
 
         const { data, ok } = await fetchRouteApi({
           method: 'PUT',
           path: `/streams/${+page.params.streamId}/topics/${topic.id}`,
           body: {
             name: form.data.name,
-            message_expiry: form.data.message_expiry
+            message_expiry: form.data.message_expiry,
+            compression_algorithm: topic.compressionAlgorithm,
+            max_topic_size: 0
           }
         });
 
@@ -83,9 +84,9 @@
     confirmationOpen = false;
 
     if (result) {
-      const { data, ok } = await fetchRouteApi({
+      const { ok } = await fetchRouteApi({
         method: 'DELETE',
-        path: `/streams/${+page.params.streamId}/topics/${topic.id}`
+        path: `/streams/${+(page.params.streamId || '')}/topics/${topic.id}`
       });
 
       if (ok) {
@@ -149,7 +150,7 @@
       </span>
 
       <div class="flex justify-end gap-3 w-full mt-auto">
-        <Button type="button" variant="text" class="w-2/5" on:click={() => 
closeModal()}
+        <Button type="button" variant="text" class="w-2/5" onclick={() => 
closeModal()}
           >Cancel</Button
         >
 
@@ -170,7 +171,7 @@
           <Button
             variant="containedRed"
             class="max-h-[36px]"
-            on:click={() => (confirmationOpen = true)}
+            onclick={() => (confirmationOpen = true)}
           >
             <Icon name="trash" class="w-[20px] -ml-1" />
             Delete</Button
diff --git a/web/src/lib/components/Paginator.svelte 
b/web/src/lib/components/Paginator.svelte
index 6faf6f90..92d7fce7 100644
--- a/web/src/lib/components/Paginator.svelte
+++ b/web/src/lib/components/Paginator.svelte
@@ -48,14 +48,14 @@
 <div class="flex justify-center items-center space-x-2">
   <Button
     variant="text"
-    on:click={() => emitPageChange(currentPage - 1)}
+    onclick={() => emitPageChange(currentPage - 1)}
     disabled={currentPage === 1}
   >
     <Icon name="arrowLeft" />
   </Button>
 
   {#if visiblePages[0] > 1}
-    <Button variant="text" on:click={() => emitPageChange(1)}>1</Button>
+    <Button variant="text" onclick={() => emitPageChange(1)}>1</Button>
     {#if visiblePages[0] > 2}
       <span class="px-2">...</span>
     {/if}
@@ -64,7 +64,7 @@
   {#each visiblePages as page}
     <Button
       variant={currentPage === page ? 'contained' : 'text'}
-      on:click={() => emitPageChange(page)}
+      onclick={() => emitPageChange(page)}
     >
       {page}
     </Button>
@@ -74,12 +74,12 @@
     {#if visiblePages[visiblePages.length - 1] < totalPages - 1}
       <span class="px-2">...</span>
     {/if}
-    <Button variant="text" on:click={() => 
emitPageChange(totalPages)}>{totalPages}</Button>
+    <Button variant="text" onclick={() => 
emitPageChange(totalPages)}>{totalPages}</Button>
   {/if}
 
   <Button
     variant="text"
-    on:click={() => emitPageChange(currentPage + 1)}
+    onclick={() => emitPageChange(currentPage + 1)}
     disabled={currentPage === totalPages}
   >
     <Icon name="arrowRight" />
diff --git a/web/src/lib/components/PasswordInput.svelte 
b/web/src/lib/components/PasswordInput.svelte
index e4832fa5..b1fae866 100644
--- a/web/src/lib/components/PasswordInput.svelte
+++ b/web/src/lib/components/PasswordInput.svelte
@@ -1,11 +1,8 @@
 <script lang="ts">
-  import type { HTMLInputAttributes } from 'svelte/elements';
   import Input from '$lib/components/Input.svelte';
   import Icon from './Icon.svelte';
   import Button from './Button.svelte';
 
-  
-
   interface Props {
     label: string;
     id?: string;
@@ -41,7 +38,7 @@
       <Button
         variant="rounded"
         class="w-[33px] h-[33px] p-0 flex items-center justify-center"
-        on:click={(e) => {
+        onclick={(e) => {
           isVisible = !isVisible;
           e.preventDefault();
           e.stopPropagation();
diff --git a/web/src/lib/components/PeriodicInvalidator.svelte 
b/web/src/lib/components/PeriodicInvalidator.svelte
index 7daf8880..23cb2efb 100644
--- a/web/src/lib/components/PeriodicInvalidator.svelte
+++ b/web/src/lib/components/PeriodicInvalidator.svelte
@@ -50,12 +50,17 @@
   });
 </script>
 
-<Button variant="rounded" on:click={customInvalidateAll}>
-  <div class={twMerge($isInvalidatingClampMin && 'spin')}>
-    <Icon name="refresh" class=" dark:stroke-white" />
-  </div>
+<Button
+  variant="rounded"
+  onclick={customInvalidateAll}
+>
+  {#snippet children()}
+    <div class={twMerge($isInvalidatingClampMin && 'spin')}>
+      <Icon name="refresh" class="dark:text-white" />
+    </div>
+  {/snippet}
   {#snippet tooltip()}
-    <div >Refresh</div>
+    <div>Refresh</div>
   {/snippet}
 </Button>
 
diff --git a/web/src/lib/components/PermissionsManager.svelte 
b/web/src/lib/components/PermissionsManager.svelte
index 6d228f3b..a1757eb1 100644
--- a/web/src/lib/components/PermissionsManager.svelte
+++ b/web/src/lib/components/PermissionsManager.svelte
@@ -349,7 +349,7 @@
         value={globalPerms[key].name}
         id={`global-permissions-${key}`}
         name={globalPerms[key].name}
-        on:change={(e) => onGlobalPermChanged(key, 
noTypeCheck(e).target.checked)}
+        onclick={(e) => onGlobalPermChanged(key, 
noTypeCheck(e).target.checked)}
       />
       <span class="text-sm">{globalPerms[key].name}</span>
     </label>
diff --git a/web/src/lib/components/RangeInput.svelte 
b/web/src/lib/components/RangeInput.svelte
index 8c1c3a26..3c8a4395 100644
--- a/web/src/lib/components/RangeInput.svelte
+++ b/web/src/lib/components/RangeInput.svelte
@@ -8,8 +8,6 @@
   };
 
   interface Props {
-    min: number;
-    max: number;
     initValue: number;
     className?: string;
     size?: keyof typeof sizes;
@@ -17,8 +15,6 @@
   }
 
   let {
-    min,
-    max,
     initValue,
     className = '',
     size = 'medium',
diff --git a/web/src/lib/components/RouteComponents/Settings/UsersTab.svelte 
b/web/src/lib/components/RouteComponents/Settings/UsersTab.svelte
index ab8d6f2f..3505299f 100644
--- a/web/src/lib/components/RouteComponents/Settings/UsersTab.svelte
+++ b/web/src/lib/components/RouteComponents/Settings/UsersTab.svelte
@@ -32,21 +32,21 @@
       label: 'Edit',
       icon: 'editPen',
       action: () => {
-        openModal('EditUserModal', {});
+        openModal('EditUserModal');
       }
     },
     {
       label: 'Permissions',
       icon: 'shieldLock',
       action: () => {
-        openModal('EditUserPermissionsModal', {});
+        openModal('EditUserPermissionsModal');
       }
     },
     {
       label: 'Delete',
       icon: 'trash',
       action: () => {
-        openModal('DeleteUserModal', {});
+        openModal('DeleteUserModal');
       }
     }
   ] satisfies { label: string; icon: iconType; action: VoidFunction }[];
@@ -99,7 +99,7 @@
 >
   {#snippet header()}
     <div class="flex items-center justify-center" >
-      <Checkbox value="all" checked={allChecked} on:change={toggleAllChecked} 
/>
+      <Checkbox value="all" checked={allChecked} onclick={toggleAllChecked} />
     </div>
   {/snippet}
 
diff --git 
a/web/src/lib/components/RouteComponents/Settings/UsersTabActions.svelte 
b/web/src/lib/components/RouteComponents/Settings/UsersTabActions.svelte
index 7482d15a..be745bd9 100644
--- a/web/src/lib/components/RouteComponents/Settings/UsersTabActions.svelte
+++ b/web/src/lib/components/RouteComponents/Settings/UsersTabActions.svelte
@@ -1,10 +1,7 @@
 <script lang="ts">
-  import Button from '$lib/components/Button.svelte';
   import Icon from '$lib/components/Icon.svelte';
   import Input from '$lib/components/Input.svelte';
-  import { openModal } from '$lib/components/Modals/AppModals.svelte';
-  import { fade } from 'svelte/transition';
-  import { searchQuery, selectedUsersId, usersCount } from './UsersTab.svelte';
+  import { searchQuery, usersCount } from './UsersTab.svelte';
 </script>
 
 <div class="flex flex-col-reverse lg:flex-row gap-3 lg:gap-5 items-center">
@@ -34,7 +31,7 @@
         </Input>
       </div>
       <!-- 
-      <Button variant="contained" on:click={() => openModal('AddUserModal', 
{streams: })}>
+      <Button variant="contained" onclick={() => openModal('AddUserModal', 
{streams: })}>
         <Icon name="plus" />
         Add user
       </Button> -->
diff --git a/web/src/lib/components/Select.svelte 
b/web/src/lib/components/Select.svelte
index 28c83139..7457a837 100644
--- a/web/src/lib/components/Select.svelte
+++ b/web/src/lib/components/Select.svelte
@@ -2,7 +2,7 @@
   import { createBubbler } from 'svelte/legacy';
 
   const bubble = createBubbler();
-  import type { HTMLInputAttributes, HTMLInputTypeAttribute } from 
'svelte/elements';
+  import type { HTMLInputTypeAttribute } from 'svelte/elements';
   import { twMerge } from 'tailwind-merge';
 
   
diff --git a/web/src/lib/components/StopPropagation.svelte 
b/web/src/lib/components/StopPropagation.svelte
index 85c02b21..a4ccd985 100644
--- a/web/src/lib/components/StopPropagation.svelte
+++ b/web/src/lib/components/StopPropagation.svelte
@@ -1,4 +1,6 @@
 <script lang="ts">
+  import { Keys } from '$lib/utils/constants/keys';
+  
   interface Props {
     class?: string | undefined;
     children?: import('svelte').Snippet;
@@ -13,6 +15,15 @@
   onclick={(e) => {
     e.stopPropagation();
   }}
+  onkeydown={(e) => {
+    if (e.key === Keys.ENTER || e.key === Keys.SPACE) {
+      e.preventDefault();
+      e.stopPropagation();
+    }
+  }}
+  role="button"
+  tabindex="0"
+  aria-label="Stop propagation"
 >
   {@render children?.()}
 </div>
diff --git a/web/src/lib/components/ThemeController.svelte 
b/web/src/lib/components/ThemeController.svelte
index e30f8985..99c193e5 100644
--- a/web/src/lib/components/ThemeController.svelte
+++ b/web/src/lib/components/ThemeController.svelte
@@ -21,13 +21,13 @@
   export const setDarkMode = () => {
     document.body.classList.add('transitions-disabled');
     document.documentElement.classList.add('dark');
-    const _ = window.getComputedStyle(document.body).opacity;
+    void window.getComputedStyle(document.body).opacity;
     document.body.classList.remove('transitions-disabled');
   };
   export const setLightMode = () => {
     document.body.classList.add('transitions-disabled');
     document.documentElement.classList.remove('dark');
-    const _ = window.getComputedStyle(document.body).opacity;
+    void window.getComputedStyle(document.body).opacity;
     document.body.classList.remove('transitions-disabled');
   };
 
diff --git a/web/src/lib/components/Toggler.svelte 
b/web/src/lib/components/Toggler.svelte
index 72cb6b06..480c4628 100644
--- a/web/src/lib/components/Toggler.svelte
+++ b/web/src/lib/components/Toggler.svelte
@@ -3,10 +3,9 @@
 
   interface Props {
     checked?: boolean;
-    value?: string;
   }
 
-  let { checked = $bindable(false), value = '1' }: Props = $props();
+  let { checked = $bindable(false) }: Props = $props();
 </script>
 
 <label
diff --git a/web/src/lib/domain/Partition.ts b/web/src/lib/domain/Partition.ts
index c57b1bd6..e69e4eb2 100644
--- a/web/src/lib/domain/Partition.ts
+++ b/web/src/lib/domain/Partition.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { bytesFormatter } from '$lib/utils/formatters/bytesFormatter';
+
 import { formatDate } from '$lib/utils/formatters/dateFormatter';
 
 export type Partition = {
diff --git a/web/src/lib/domain/Permissions.ts 
b/web/src/lib/domain/Permissions.ts
index 4ed41596..17a13b77 100644
--- a/web/src/lib/domain/Permissions.ts
+++ b/web/src/lib/domain/Permissions.ts
@@ -17,7 +17,6 @@
  * under the License.
  */
 
-import type { KeysToSnakeCase } from '$lib/utils/utilTypes';
 
 export type GlobalPermissions = {
   manageServers: boolean;
diff --git a/web/src/lib/domain/Stream.ts b/web/src/lib/domain/Stream.ts
index 92b52f5e..49d70a88 100644
--- a/web/src/lib/domain/Stream.ts
+++ b/web/src/lib/domain/Stream.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { bytesFormatter } from '$lib/utils/formatters/bytesFormatter';
+
 
 export type Stream = {
   id: number;
diff --git a/web/src/lib/queries.ts b/web/src/lib/queries.ts
index 958f0515..2b88086b 100644
--- a/web/src/lib/queries.ts
+++ b/web/src/lib/queries.ts
@@ -17,13 +17,8 @@
  * under the License.
  */
 
-import { statsMapper } from '$lib/domain/Stats';
-import { streamDetailsMapper } from '$lib/domain/StreamDetails';
 // import { apiClient } from '$lib/utils/apiClient';
 
-import { streamMapper, type Stream } from './domain/Stream';
-import { topicDetailsMapper } from './domain/TopicDetails';
-
 const apiClient = async () => {};
 
 // export function getStatsQuery() {
diff --git a/web/src/lib/utils/dataHas.ts b/web/src/lib/utils/constants/keys.ts
similarity index 56%
copy from web/src/lib/utils/dataHas.ts
copy to web/src/lib/utils/constants/keys.ts
index 262ead38..059113db 100644
--- a/web/src/lib/utils/dataHas.ts
+++ b/web/src/lib/utils/constants/keys.ts
@@ -1,28 +1,55 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { setError } from 'sveltekit-superforms/client';
-
-export const dataHas = (data: unknown, ...args: string[]) => {
-  if (data && typeof data === 'object') {
-    return args.every((arg) => arg in data);
-  }
-
-  return false;
-};
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export const Keys = {
+  ENTER: 'Enter',
+  ESCAPE: 'Escape',
+  SPACE: ' ',
+  TAB: 'Tab',
+  BACKSPACE: 'Backspace',
+  DELETE: 'Delete',
+  
+  ARROW_UP: 'ArrowUp',
+  ARROW_DOWN: 'ArrowDown',
+  ARROW_LEFT: 'ArrowLeft',
+  ARROW_RIGHT: 'ArrowRight',
+  
+  PAGE_UP: 'PageUp',
+  PAGE_DOWN: 'PageDown',
+  HOME: 'Home',
+  END: 'End',
+  
+  SHIFT: 'Shift',
+  CONTROL: 'Control',
+  ALT: 'Alt',
+  META: 'Meta',
+  
+  F1: 'F1',
+  F2: 'F2',
+  F3: 'F3',
+  F4: 'F4',
+  F5: 'F5',
+  F6: 'F6',
+  F7: 'F7',
+  F8: 'F8',
+  F9: 'F9',
+  F10: 'F10',
+  F11: 'F11',
+  F12: 'F12',
+} as const;
diff --git a/web/src/lib/utils/dataHas.ts b/web/src/lib/utils/dataHas.ts
index 262ead38..013dfda7 100644
--- a/web/src/lib/utils/dataHas.ts
+++ b/web/src/lib/utils/dataHas.ts
@@ -17,8 +17,6 @@
  * under the License.
  */
 
-import { setError } from 'sveltekit-superforms/client';
-
 export const dataHas = (data: unknown, ...args: string[]) => {
   if (data && typeof data === 'object') {
     return args.every((arg) => arg in data);
diff --git a/web/src/lib/utils/formatters/bytesFormatter.ts 
b/web/src/lib/utils/formatters/bytesFormatter.ts
index 885dc00f..3f61325a 100644
--- a/web/src/lib/utils/formatters/bytesFormatter.ts
+++ b/web/src/lib/utils/formatters/bytesFormatter.ts
@@ -17,8 +17,6 @@
  * under the License.
  */
 
-import prettyBytes from 'pretty-bytes';
-
 export function bytesFormatter(bytes: any) {
   // The bytes size string is already formatted by the API
   return bytes;
diff --git a/web/src/lib/utils/formatters/durationFormatter.ts 
b/web/src/lib/utils/formatters/durationFormatter.ts
index 04ce7fa1..685507fe 100644
--- a/web/src/lib/utils/formatters/durationFormatter.ts
+++ b/web/src/lib/utils/formatters/durationFormatter.ts
@@ -18,7 +18,6 @@
  */
 
 import { formatDuration, intervalToDuration, isValid } from 'date-fns';
-import { number } from 'zod';
 
 export const durationFormatter = (seconds: number) => {
   if (seconds <= 0 || seconds.toString().length > 11 || !isValid(seconds)) 
return '';
diff --git a/web/src/routes/+error.svelte b/web/src/routes/+error.svelte
index ec5d1a05..acd1f9b2 100644
--- a/web/src/routes/+error.svelte
+++ b/web/src/routes/+error.svelte
@@ -1,7 +1,5 @@
 <script>
-  import { goto, invalidate, invalidateAll } from '$app/navigation';
   import { page } from '$app/state';
-  import { typedRoute } from '$lib/types/appRoutes';
 </script>
 
 {#if page.error && page.error.message === 'Not Found'}
diff --git a/web/src/routes/auth/sign-in/+page.svelte 
b/web/src/routes/auth/sign-in/+page.svelte
index 79fa60b9..a12466c5 100644
--- a/web/src/routes/auth/sign-in/+page.svelte
+++ b/web/src/routes/auth/sign-in/+page.svelte
@@ -1,5 +1,4 @@
 <script lang="ts">
-  import { updated } from '$app/state';
   import Button from '$lib/components/Button.svelte';
   import Checkbox from '$lib/components/Checkbox.svelte';
   import Icon from '$lib/components/Icon.svelte';
@@ -14,7 +13,7 @@
   }
 
   let { data }: Props = $props();
-  const { form, constraints, errors, message, reset } = superForm(data.form, 
{});
+  const { form, constraints, errors, message } = superForm(data.form, {});
 
   const remember = persistedStore('rememberMe', { rememberMe: true, username: 
'', password: '' });
 
@@ -44,7 +43,7 @@
   <Input
     label="Username"
     name="username"
-    errorMessage={$errors?.username?.join(',')}
+    errorMessage={Array.isArray($errors?.username) ? 
$errors.username.join(',') : String($errors?.username || '')}
     bind:value={$form.username}
     {...$constraints.username}
   />
@@ -52,7 +51,7 @@
   <PasswordInput
     label="Password"
     name="password"
-    errorMessage={$errors.password?.join(',')}
+    errorMessage={Array.isArray($errors?.password) ? 
$errors.password.join(',') : String($errors?.password || '')}
     bind:value={$form.password}
     {...$constraints.password}
   />
diff --git a/web/src/routes/dashboard/overview/+page.svelte 
b/web/src/routes/dashboard/overview/+page.svelte
index 45b7bc46..09ba1b38 100644
--- a/web/src/routes/dashboard/overview/+page.svelte
+++ b/web/src/routes/dashboard/overview/+page.svelte
@@ -5,7 +5,7 @@
 
   let { data }: Props = $props();
 
-  let statsValues = $derived(Object.values(data.stats).map((val) => val));
+  let statsValues = $derived(Object.values(data.stats).map((val) => val as { 
name: string; value: string }));
 </script>
 
 <div class="h-full overflow-auto p-10">
diff --git a/web/src/routes/dashboard/settings/server/+page.svelte 
b/web/src/routes/dashboard/settings/server/+page.svelte
index 4cb818b8..1979ba57 100644
--- a/web/src/routes/dashboard/settings/server/+page.svelte
+++ b/web/src/routes/dashboard/settings/server/+page.svelte
@@ -1,11 +1,9 @@
 <script lang="ts">
   import Button from '$lib/components/Button.svelte';
   import SettingsLayout from '$lib/components/Layouts/SettingsLayout.svelte';
-  import Loader from '$lib/components/Loader.svelte';
 
   import RangeInput from '$lib/components/RangeInput.svelte';
   import Toggler from '$lib/components/Toggler.svelte';
-  import type { Stats } from '$lib/domain/Stats';
 
   interface Props {
     data: any;
@@ -38,8 +36,6 @@
                 <RangeInput
                   className="w-[270px] h-[9px]"
                   size="big"
-                  min={0}
-                  max={100}
                   initValue={50}
                   bind:value={cacheValue}
                 />
diff --git a/web/src/routes/dashboard/settings/users/+page.svelte 
b/web/src/routes/dashboard/settings/users/+page.svelte
index fe5e9ed1..337857f7 100644
--- a/web/src/routes/dashboard/settings/users/+page.svelte
+++ b/web/src/routes/dashboard/settings/users/+page.svelte
@@ -34,21 +34,21 @@
       label: 'Edit',
       icon: 'editPen',
       action: () => {
-        openModal('EditUserModal', {});
+        openModal('EditUserModal');
       }
     },
     {
       label: 'Permissions',
       icon: 'shieldLock',
       action: () => {
-        openModal('EditUserPermissionsModal', {});
+        openModal('EditUserPermissionsModal');
       }
     },
     {
       label: 'Delete',
       icon: 'trash',
       action: () => {
-        openModal('DeleteUserModal', {});
+        openModal('DeleteUserModal');
       }
     }
   ] satisfies { label: string; icon: iconType; action: VoidFunction }[];
@@ -97,7 +97,7 @@
 
           <Button
             variant="contained"
-            on:click={() =>
+            onclick={() =>
               openModal('AddUserModal', {
                 streams: data.streams
               })}
@@ -145,7 +145,7 @@
   >
     {#snippet header()}
         <div class="flex items-center justify-center" >
-        <Checkbox value="all" checked={allChecked} 
on:change={toggleAllChecked} />
+        <Checkbox value="all" checked={allChecked} onclick={toggleAllChecked} 
/>
       </div>
       {/snippet}
 
diff --git a/web/src/routes/dashboard/settings/webUI/+page.svelte 
b/web/src/routes/dashboard/settings/webUI/+page.svelte
index ca744c79..c9f83f3a 100644
--- a/web/src/routes/dashboard/settings/webUI/+page.svelte
+++ b/web/src/routes/dashboard/settings/webUI/+page.svelte
@@ -36,7 +36,7 @@
         </span>
         <Button
           disabled={saveDisabled}
-          on:click={() => {
+          onclick={() => {
             $invalidateIntervalDuration = intervalValue;
           }}
           variant="contained">Save</Button
diff --git a/web/src/routes/dashboard/streams/+layout.svelte 
b/web/src/routes/dashboard/streams/+layout.svelte
index cceb5035..8172d85f 100644
--- a/web/src/routes/dashboard/streams/+layout.svelte
+++ b/web/src/routes/dashboard/streams/+layout.svelte
@@ -1,6 +1,6 @@
 <script lang="ts">
   import Icon from '$lib/components/Icon.svelte';
-  import { goto, invalidateAll, onNavigate } from '$app/navigation';
+  import { goto } from '$app/navigation';
   import { twMerge } from 'tailwind-merge';
   import { page } from '$app/state';
   import { openModal } from '$lib/components/Modals/AppModals.svelte';
@@ -9,9 +9,6 @@
   import { typedRoute } from '$lib/types/appRoutes';
   import { arrayMax } from '$lib/utils/arrayMax';
   import { onMount } from 'svelte';
-  import { noTypeCheck } from '$lib/utils/noTypeCheck.js';
-  import { slide } from 'svelte/transition';
-  import { bytesFormatter } from '$lib/utils/formatters/bytesFormatter';
 
   interface Props {
     data: any;
@@ -88,7 +85,7 @@
       <Button
         variant="outlined"
         class="w-full"
-        on:click={() =>
+        onclick={() =>
           openModal('AddStreamModal', {
             nextStreamId: arrayMax(data.streams.map((s) => s.id)) + 1
           })}
diff --git a/web/src/routes/dashboard/streams/[streamId=i32]/+page.svelte 
b/web/src/routes/dashboard/streams/[streamId=i32]/+page.svelte
index 44ac6615..c23d4b5f 100644
--- a/web/src/routes/dashboard/streams/[streamId=i32]/+page.svelte
+++ b/web/src/routes/dashboard/streams/[streamId=i32]/+page.svelte
@@ -1,82 +1,85 @@
-<script lang="ts">
-  import { page } from '$app/state';
-  import Button from '$lib/components/Button.svelte';
-  import Icon from '$lib/components/Icon.svelte';
-  import { openModal } from '$lib/components/Modals/AppModals.svelte';
-  import SortableList from '$lib/components/SortableList.svelte';
-  import { typedRoute } from '$lib/types/appRoutes';
-  import { arrayMax } from '$lib/utils/arrayMax';
-  import type { StreamDetails } from '$lib/domain/StreamDetails';
-
-  interface Props {
-    data: {
-      streamDetails: StreamDetails
-    };
-  }
-
-  let { data }: Props = $props();
-  let stream = $derived(data.streamDetails);
-</script>
-
-<div class="h-[80px] flex flex-row text-xs items-center px-7">
-  <h1 class="font-semibold text-xl dark:text-white max-w-[380px] break-words 
line-clamp-3">
-    Stream {stream.name}
-  </h1>
-
-  <Button
-    variant="rounded"
-    class="ml-3"
-    on:click={() => openModal('StreamSettingsModal', { stream })}
-  >
-    <Icon name="settings" class="dark:text-white" />
-    {#snippet tooltip()}
-        <div >Settings</div>
-      {/snippet}
-  </Button>
-
-  <div class="flex gap-3 ml-7">
-    <div class="chip">
-      <span>Id: {stream.id}</span>
-    </div>
-    <div class="chip">
-      <span>Size: {stream.sizeFormatted}</span>
-    </div>
-    <div class="chip">
-      <span>Messages: {stream.messagesCount}</span>
-    </div>
-    <div class="chip">
-      <span>Topics: {stream.topicsCount}</span>
-    </div>
-  </div>
-
-  <Button
-    variant="contained"
-    class="ml-auto"
-    on:click={() =>
-      openModal('AddTopicModal', {
-        streamDetails: stream,
-        nextTopicId: arrayMax(data.streamDetails.topics.map((t) => t.id)) + 1
-      })}
-  >
-    <Icon name="plus" class="w-[16px] h-[16px]" strokeWidth={2} />
-    Add Topic
-  </Button>
-</div>
-
-<SortableList
-  emptyDataMessage="This stream has no topics."
-  rowClass="grid grid-cols-[150px_3fr_2fr_2fr_2fr_2fr_3fr]"
-  data={stream.topics}
-  hrefBuilder={(topic) =>
-    
typedRoute(`/dashboard/streams/${+page.params.streamId}/topics/${topic.id}`)}
-  colNames={{
-    id: 'ID',
-    name: 'Name',
-    messagesCount: 'Messages',
-    partitionsCount: 'Partitions',
-    messageExpiryFormatted: 'Message expiry',
-    sizeFormatted: 'Size',
-    createdAt: 'Created',
-    sizeBytes: undefined
-  }}
-/>
+<script lang="ts">
+  import { page } from '$app/state';
+  import Button from '$lib/components/Button.svelte';
+  import Icon from '$lib/components/Icon.svelte';
+  import { openModal } from '$lib/components/Modals/AppModals.svelte';
+  import SortableList from '$lib/components/SortableList.svelte';
+  import { typedRoute } from '$lib/types/appRoutes';
+  import { arrayMax } from '$lib/utils/arrayMax';
+  import type { StreamDetails } from '$lib/domain/StreamDetails';
+
+  interface Props {
+    data: {
+      streamDetails: StreamDetails
+    };
+  }
+
+  let { data }: Props = $props();
+  let stream = $derived(data.streamDetails);
+</script>
+
+<div class="h-[80px] flex flex-row text-xs items-center px-7">
+  <h1 class="font-semibold text-xl dark:text-white max-w-[380px] break-words 
line-clamp-3">
+    Stream {stream.name}
+  </h1>
+
+  <Button
+    variant="rounded"
+    class="ml-3"
+    onclick={() => openModal('StreamSettingsModal', { stream })}
+  >
+    {#snippet children()}
+      <Icon name="settings" class="dark:text-white" />
+    {/snippet}
+    {#snippet tooltip()}
+      <div>Settings</div>
+    {/snippet}
+  </Button>
+
+  <div class="flex gap-3 ml-7">
+    <div class="chip">
+      <span>Id: {stream.id}</span>
+    </div>
+    <div class="chip">
+      <span>Size: {stream.sizeFormatted}</span>
+    </div>
+    <div class="chip">
+      <span>Messages: {stream.messagesCount}</span>
+    </div>
+    <div class="chip">
+      <span>Topics: {stream.topicsCount}</span>
+    </div>
+  </div>
+
+  <Button
+    variant="contained"
+    class="ml-auto"
+    onclick={() =>
+      openModal('AddTopicModal', {
+        streamDetails: stream,
+        nextTopicId: arrayMax(data.streamDetails.topics.map((t) => t.id)) + 1
+      })}
+  >
+    <Icon name="plus" class="w-[16px] h-[16px]" strokeWidth={2} />
+    Add Topic
+  </Button>
+</div>
+
+<SortableList
+  emptyDataMessage="This stream has no topics."
+  rowClass="grid grid-cols-[150px_3fr_2fr_2fr_2fr_2fr_3fr]"
+  data={stream.topics}
+  hrefBuilder={(topic) =>
+    typedRoute(`/dashboard/streams/${+(page.params.streamId || 
'')}/topics/${topic.id}`)}
+  colNames={{
+    id: 'ID',
+    name: 'Name',
+    messagesCount: 'Messages',
+    partitionsCount: 'Partitions',
+    messageExpiryFormatted: 'Message expiry',
+    sizeFormatted: 'Size',
+    createdAt: 'Created',
+    sizeBytes: undefined
+  }}
+/>
+
diff --git 
a/web/src/routes/dashboard/streams/[streamId=i32]/topics/[topicId=i32]/+page.svelte
 
b/web/src/routes/dashboard/streams/[streamId=i32]/topics/[topicId=i32]/+page.svelte
index 84e9b923..b748bdce 100644
--- 
a/web/src/routes/dashboard/streams/[streamId=i32]/topics/[topicId=i32]/+page.svelte
+++ 
b/web/src/routes/dashboard/streams/[streamId=i32]/topics/[topicId=i32]/+page.svelte
@@ -1,87 +1,89 @@
-<script lang="ts">
-  import { page } from '$app/state';
-  import Button from '$lib/components/Button.svelte';
-  import Icon from '$lib/components/Icon.svelte';
-  import { goto } from '$app/navigation';
-  import { typedRoute } from '$lib/types/appRoutes';
-  import { openModal } from '$lib/components/Modals/AppModals.svelte';
-  import SortableList from '$lib/components/SortableList.svelte';
-  import type { Topic } from '$lib/domain/Topic';
-  import type { Partition } from '$lib/domain/Partition';
-
-  interface Props {
-    data: {
-      topic: Topic & {
-        partitions: Partition[];
-      };
-    }
-  }
-
-  let { data }: Props = $props();
-  let topic = $derived(data.topic);
-  let prevPage = $derived(page.url.pathname.split('/').slice(0, 4).join('/') + 
'/');
-</script>
-
-<div class="h-[80px] flex text-xs items-center pl-2 pr-5">
-  <Button variant="rounded" class="mr-5" on:click={() => goto(prevPage)}>
-    <Icon name="arrowLeft" class="h-[40px] w-[30px]" />
-  </Button>
-
-  <h1 class="text-xl font-semibold text-color">Topic {topic.name}</h1>
-
-  <Button
-    variant="rounded"
-    class="ml-3"
-    on:click={() => openModal('TopicSettingsModal', { topic, 
onDeleteRedirectPath: prevPage })}
-  >
-    <Icon name="settings" />
-    {#snippet tooltip()}
-        <div >Settings</div>
-      {/snippet}
-  </Button>
-
-  <div class="flex gap-3 ml-7">
-    <div class="chip">
-      <span>Id: {topic.id}</span>
-    </div>
-    <div class="chip">
-      <span>Size: {topic.sizeFormatted}</span>
-    </div>
-    <div class="chip">
-      <span>Messages: {topic.messagesCount}</span>
-    </div>
-    <div class="chip">
-      <span>Partitions: {topic.partitionsCount}</span>
-    </div>
-  </div>
-
-  <div class="flex gap-2 ml-auto">
-    {#if topic.partitions.length > 0}
-      <Button variant="outlinedRed" on:click={() => 
openModal('DeletePartitionsModal', { topic })}
-        >Delete partitions</Button
-      >
-    {/if}
-    <Button variant="contained" on:click={() => 
openModal('AddPartitionsModal', {})}
-      >Add partitions</Button
-    >
-  </div>
-</div>
-
-<SortableList
-  emptyDataMessage="No partitions found."
-  rowClass="grid grid-cols-[150px_1fr_1fr_1fr_1fr_1fr]"
-  data={topic.partitions}
-  hrefBuilder={(partition) =>
-    typedRoute(
-      
`/dashboard/streams/${+page.params.streamId}/topics/${topic.id}/partitions/${partition.id}/messages`
-    )}
-  colNames={{
-    id: 'ID',
-    currentOffset: 'Offset',
-    segmentsCount: 'Segments',
-    messagesCount: 'Messages',
-    sizeFormatted: 'Size',
-    createdAt: 'Created',
-    sizeBytes: undefined
-  }}
-/>
+<script lang="ts">
+  import { page } from '$app/state';
+  import Button from '$lib/components/Button.svelte';
+  import Icon from '$lib/components/Icon.svelte';
+  import { goto } from '$app/navigation';
+  import { typedRoute } from '$lib/types/appRoutes';
+  import { openModal } from '$lib/components/Modals/AppModals.svelte';
+  import SortableList from '$lib/components/SortableList.svelte';
+  import type { Topic } from '$lib/domain/Topic';
+  import type { Partition } from '$lib/domain/Partition';
+
+  interface Props {
+    data: {
+      topic: Topic & {
+        partitions: Partition[];
+      };
+    }
+  }
+
+  let { data }: Props = $props();
+  let topic = $derived(data.topic);
+  let prevPage = $derived(page.url.pathname.split('/').slice(0, 4).join('/') + 
'/');
+</script>
+
+<div class="h-[80px] flex text-xs items-center pl-2 pr-5">
+  <Button variant="rounded" class="mr-5" onclick={() => goto(prevPage)}>
+    <Icon name="arrowLeft" class="h-[40px] w-[30px]" />
+  </Button>
+
+  <h1 class="text-xl font-semibold text-color">Topic {topic.name}</h1>
+
+  <Button
+    variant="rounded"
+    class="ml-3"
+    onclick={() => openModal('TopicSettingsModal', { topic, 
onDeleteRedirectPath: prevPage })}
+  >
+    {#snippet children()}
+      <Icon name="settings" class="dark:text-white" />
+    {/snippet}
+    {#snippet tooltip()}
+      <div>Settings</div>
+    {/snippet}
+  </Button>
+
+  <div class="flex gap-3 ml-7">
+    <div class="chip">
+      <span>Id: {topic.id}</span>
+    </div>
+    <div class="chip">
+      <span>Size: {topic.sizeFormatted}</span>
+    </div>
+    <div class="chip">
+      <span>Messages: {topic.messagesCount}</span>
+    </div>
+    <div class="chip">
+      <span>Partitions: {topic.partitionsCount}</span>
+    </div>
+  </div>
+
+  <div class="flex gap-2 ml-auto">
+    {#if topic.partitions.length > 0}
+      <Button variant="outlinedRed" onclick={() => 
openModal('DeletePartitionsModal', { topic })}
+        >Delete partitions</Button
+      >
+    {/if}
+    <Button variant="contained" onclick={() => openModal('AddPartitionsModal')}
+      >Add partitions</Button
+    >
+  </div>
+</div>
+
+<SortableList
+  emptyDataMessage="No partitions found."
+  rowClass="grid grid-cols-[150px_1fr_1fr_1fr_1fr_1fr]"
+  data={topic.partitions}
+  hrefBuilder={(partition) =>
+    typedRoute(
+      `/dashboard/streams/${+(page.params.streamId || 
'')}/topics/${topic.id}/partitions/${partition.id}/messages`
+    )}
+  colNames={{
+    id: 'ID',
+    currentOffset: 'Offset',
+    segmentsCount: 'Segments',
+    messagesCount: 'Messages',
+    sizeFormatted: 'Size',
+    createdAt: 'Created',
+    sizeBytes: undefined
+  }}
+/>
diff --git 
a/web/src/routes/dashboard/streams/[streamId=i32]/topics/[topicId=i32]/partitions/[partitionId=i32]/messages/+page.server.ts
 
b/web/src/routes/dashboard/streams/[streamId=i32]/topics/[topicId=i32]/partitions/[partitionId=i32]/messages/+page.server.ts
index f368ee88..7bf5ba31 100644
--- 
a/web/src/routes/dashboard/streams/[streamId=i32]/topics/[topicId=i32]/partitions/[partitionId=i32]/messages/+page.server.ts
+++ 
b/web/src/routes/dashboard/streams/[streamId=i32]/topics/[topicId=i32]/partitions/[partitionId=i32]/messages/+page.server.ts
@@ -45,10 +45,10 @@ export const load = async ({ params, cookies, url }) => {
     const { data: initialData } = await handleFetchErrors(initialResult, 
cookies);
     const initialMessages = partitionMessagesDetailsMapper(initialData as any);
 
-    const totalMessages = initialMessages.currentOffset;
+    const totalMessages = initialMessages.currentOffset + 1;
     const offset =
       url.searchParams.get('offset') ??
-      (direction === 'desc' ? Math.max(0, totalMessages - MESSAGES_PER_PAGE) : 
'0');
+      (direction === 'desc' ? Math.max(0, totalMessages - 
MESSAGES_PER_PAGE).toString() : '0');
 
     const result = await fetchIggyApi({
       method: 'GET',
@@ -84,7 +84,7 @@ export const load = async ({ params, cookies, url }) => {
     partitionMessages,
     topic,
     pagination: {
-      offset,
+      offset: parseInt(offset.toString()),
       count: MESSAGES_PER_PAGE
     }
   };
diff --git 
a/web/src/routes/dashboard/streams/[streamId=i32]/topics/[topicId=i32]/partitions/[partitionId=i32]/messages/+page.svelte
 
b/web/src/routes/dashboard/streams/[streamId=i32]/topics/[topicId=i32]/partitions/[partitionId=i32]/messages/+page.svelte
index a99fac53..30e8dce9 100644
--- 
a/web/src/routes/dashboard/streams/[streamId=i32]/topics/[topicId=i32]/partitions/[partitionId=i32]/messages/+page.svelte
+++ 
b/web/src/routes/dashboard/streams/[streamId=i32]/topics/[topicId=i32]/partitions/[partitionId=i32]/messages/+page.svelte
@@ -1,109 +1,119 @@
-<script lang="ts">
-  import { page } from '$app/state';
-  import Button from '$lib/components/Button.svelte';
-  import Icon from '$lib/components/Icon.svelte';
-  import { goto } from '$app/navigation';
-  import { openModal } from '$lib/components/Modals/AppModals.svelte';
-  import SortableList from '$lib/components/SortableList.svelte';
-  import Paginator from '$lib/components/Paginator.svelte';
-
-  interface Props {
-    data: any;
-  }
-
-  let { data }: Props = $props();
-  let topic = $derived(data.topic);
-  let partitionMessages = $derived(data.partitionMessages);
-  let prevPage = $derived(page.url.pathname.split('/').slice(0, 6).join('/') + 
'/');
-
-  let direction = $state(page.url.searchParams.get('direction') || 'desc');
-  let currentPage = $state(1);
-  let totalPages = $derived(
-    Math.ceil((partitionMessages.currentOffset + 1) / data.pagination.count)
-  );
-
-  async function loadPage(page: number) {
-    const messagesPerPage = data.pagination.count;
-    const totalMessages = partitionMessages.currentOffset + 1;
-
-    let offset: number;
-    if (direction === 'desc') {
-      offset = Math.max(0, totalMessages - page * messagesPerPage);
-    } else {
-      offset = (page - 1) * messagesPerPage;
-    }
-
-    const url = new URL(window.location.href);
-    url.searchParams.set('offset', offset.toString());
-    url.searchParams.set('direction', direction);
-    await goto(url, { keepFocus: true, noScroll: true });
-    currentPage = page;
-  }
-
-  function onPageChange(event: CustomEvent<number>) {
-    const page = event.detail;
-    loadPage(page);
-  }
-
-  function toggleDirection() {
-    direction = direction === 'desc' ? 'asc' : 'desc';
-    loadPage(1);
-  }
-</script>
-
-<div class="h-[80px] flex text-xs items-center pl-2 pr-5">
-  <Button variant="rounded" class="mr-5" on:click={() => goto(prevPage)}>
-    <Icon name="arrowLeft" class="h-[40px] w-[30px]" />
-  </Button>
-
-  <h1 class="text-xl font-semibold text-color">
-    Messages for {topic.name}, partition {partitionMessages.partitionId}
-  </h1>
-
-  <div class="flex gap-3 ml-7">
-    <div class="chip">
-      <span>Messages: {partitionMessages.currentOffset}</span>
-    </div>
-  </div>
-
-  <div class="flex gap-2 ml-auto">
-    <Button variant="contained" on:click={toggleDirection}>
-      <Icon name={direction === 'desc' ? 'arrowDown' : 'arrowUp'} class="h-5 
w-5 mr-2" />
-      {direction === 'desc' ? 'Newest first' : 'Oldest first'}
-    </Button>
-  </div>
-</div>
-
-<div class="flex-1 overflow-auto">
-  {#if partitionMessages.messages.length === 0}
-    <div class="flex items-center justify-center h-full text-gray-400 text-xl">
-      <em>No messages found.</em>
-    </div>
-  {:else}
-    <SortableList
-      emptyDataMessage="No messages found."
-      rowClass="grid grid-cols-[150px_2fr_1fr_1fr_1fr]"
-      data={direction === 'desc'
-        ? [...partitionMessages.messages].reverse()
-        : partitionMessages.messages}
-      onclickAction={(index) =>
-        openModal('InspectMessage', {
-          message:
-            partitionMessages.messages[
-              direction === 'desc' ? partitionMessages.messages.length - 1 - 
index : index
-            ]
-        })}
-      ariaRoleDescription="Display message details"
-      colNames={{
-        offset: 'Offset',
-        truncatedPayload: 'Payload',
-        formattedTimestamp: 'Timestamp',
-        checksum: 'Checksum'
-      }}
-    />
-  {/if}
-</div>
-
-<div class="mt-2 mb-2">
-  <Paginator {currentPage} {totalPages} maxVisiblePages={5} 
on:pageChange={onPageChange} />
-</div>
+<script lang="ts">
+  import { page } from '$app/state';
+  import Button from '$lib/components/Button.svelte';
+  import Icon from '$lib/components/Icon.svelte';
+  import { goto } from '$app/navigation';
+  import { openModal } from '$lib/components/Modals/AppModals.svelte';
+  import SortableList from '$lib/components/SortableList.svelte';
+  import Paginator from '$lib/components/Paginator.svelte';
+  import type { MessagePartition } from '$lib/domain/Message';
+  import type { TopicDetails } from '$lib/domain/TopicDetails';
+
+  interface Props {
+    data: {
+      partitionMessages: MessagePartition;
+      topic: TopicDetails;
+      pagination: {
+        offset: number;
+        count: number;
+      };
+    };
+  }
+
+  let { data }: Props = $props();
+  let topic = $derived(data.topic);
+  let partitionMessages = $derived(data.partitionMessages);
+  let prevPage = $derived(page.url.pathname.split('/').slice(0, 6).join('/') + 
'/');
+
+  let direction = $state(page.url.searchParams.get('direction') || 'desc');
+  let currentPage = $state(1);
+  let totalPages = $derived(
+    Math.ceil((partitionMessages.currentOffset + 1) / data.pagination.count)
+  );
+
+  async function loadPage(page: number) {
+    const messagesPerPage = data.pagination.count;
+    const totalMessages = partitionMessages.currentOffset + 1;
+
+    let offset: number;
+    if (direction === 'desc') {
+      offset = Math.max(0, totalMessages - page * messagesPerPage);
+    } else {
+      offset = (page - 1) * messagesPerPage;
+    }
+
+    const url = new URL(window.location.href);
+    url.searchParams.set('offset', offset.toString());
+    url.searchParams.set('direction', direction);
+    await goto(url, { keepFocus: true, noScroll: true });
+    currentPage = page;
+  }
+
+  function onPageChange(event: CustomEvent<number>) {
+    const page = event.detail;
+    loadPage(page);
+  }
+
+  function toggleDirection() {
+    direction = direction === 'desc' ? 'asc' : 'desc';
+    loadPage(1);
+  }
+</script>
+
+<div class="h-[80px] flex text-xs items-center pl-2 pr-5">
+  <Button variant="rounded" class="mr-5" onclick={() => goto(prevPage)}>
+    <Icon name="arrowLeft" class="h-[40px] w-[30px]" />
+  </Button>
+
+  <h1 class="text-xl font-semibold text-color">
+    Messages for {topic.name}, partition {partitionMessages.partitionId}
+  </h1>
+
+  <div class="flex gap-3 ml-7">
+    <div class="chip">
+      <span>Messages: {partitionMessages.messages.length > 0 ? 
partitionMessages.currentOffset + 1 : 0}</span>
+    </div>
+  </div>
+
+  <div class="flex gap-2 ml-auto">
+    <Button variant="contained" onclick={toggleDirection}>
+      <Icon name={direction === 'desc' ? 'arrowDown' : 'arrowUp'} class="h-5 
w-5 mr-2" />
+      {direction === 'desc' ? 'Newest first' : 'Oldest first'}
+    </Button>
+  </div>
+</div>
+
+<div class="flex-1 overflow-auto">
+  {#if partitionMessages.messages.length === 0}
+    <div class="flex items-center justify-center h-full text-gray-400 text-xl">
+      <em>No messages found.</em>
+    </div>
+  {:else}
+    <SortableList
+      emptyDataMessage="No messages found."
+      rowClass="grid grid-cols-[150px_2fr_1fr_1fr_1fr]"
+      data={direction === 'desc'
+        ? [...partitionMessages.messages].reverse()
+        : partitionMessages.messages}
+      onclickAction={(index) =>
+        openModal('InspectMessage', {
+          message:
+            partitionMessages.messages[
+              direction === 'desc' ? partitionMessages.messages.length - 1 - 
index : index
+            ]
+        })}
+      ariaRoleDescription="Display message details"
+      colNames={{
+        offset: 'Offset',
+        truncatedPayload: 'Payload',
+        formattedTimestamp: 'Timestamp',
+        checksum: 'Checksum'
+      }}
+    />
+  {/if}
+</div>
+
+<div class="mt-2 mb-2">
+  <Paginator {currentPage} {totalPages} maxVisiblePages={5} 
on:pageChange={onPageChange} />
+</div>
+


Reply via email to