You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iggy.apache.org by pi...@apache.org on 2025/08/11 07:30:55 UTC
(iggy) branch master updated: feat(web): enhance ui interactions and improve code quality (#2078)
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 <50...@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>
+