diff --git a/ui/biome.json b/ui/biome.json index 2e26df7..0570020 100644 --- a/ui/biome.json +++ b/ui/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.5.15/schema.json", + "$schema": "https://biomejs.dev/schemas/2.6.4/schema.json", "vcs": { "enabled": true, diff --git a/ui/package-lock.json b/ui/package-lock.json index 7f57620..a39f376 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -11,19 +11,19 @@ "@base-ui/react": "^1.9.0", "@fontsource-variable/geist": "^5.3.0", "@fontsource/ibm-plex-mono": "^5.3.0", - "@tanstack/react-query": "^5.104.4", + "@tanstack/react-query": "^5.105.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "keycloak-js": "^26.2.4", - "lucide-react": "^1.59.0", + "keycloak-js": "^26.2.5", + "lucide-react": "^1.64.0", "react": "^19.3.1", "react-dom": "^19.3.1", - "react-router-dom": "^7.18.4", + "react-router-dom": "^7.18.5", "tailwind-merge": "^3.7.0" }, "devDependencies": { "@axe-core/playwright": "^4.14.0", - "@biomejs/biome": "2.5.15", + "@biomejs/biome": "2.6.4", "@playwright/test": "^1.64.1", "@tailwindcss/vite": "^4.3.3", "@testing-library/dom": "^10.4.2", @@ -244,9 +244,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.5.15", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.15.tgz", - "integrity": "sha512-WZTW4slm/pdkh92K6t/3aEN++44JD1PQ7squ7RCMLI1flHGl43DVOIGXvCjHiZgBMO1V4uYxoFNtqrIA4qGuIQ==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.6.4.tgz", + "integrity": "sha512-8sKKwaNW4ot22awZ3lBjwWLHnUFXgXnrBPYS2gNlBAXRbDC++7kqjK2H7dR/FgCDtPvO1xfKpgEhJkGxAKYkxA==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -260,20 +260,56 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.5.15", - "@biomejs/cli-darwin-x64": "2.5.15", - "@biomejs/cli-linux-arm64": "2.5.15", - "@biomejs/cli-linux-arm64-musl": "2.5.15", - "@biomejs/cli-linux-x64": "2.5.15", - "@biomejs/cli-linux-x64-musl": "2.5.15", - "@biomejs/cli-win32-arm64": "2.5.15", - "@biomejs/cli-win32-x64": "2.5.15" + "@biomejs/cli-android-arm64": "2.6.4", + "@biomejs/cli-android-x64": "2.6.4", + "@biomejs/cli-darwin-arm64": "2.6.4", + "@biomejs/cli-darwin-x64": "2.6.4", + "@biomejs/cli-linux-arm64": "2.6.4", + "@biomejs/cli-linux-arm64-musl": "2.6.4", + "@biomejs/cli-linux-x64": "2.6.4", + "@biomejs/cli-linux-x64-musl": "2.6.4", + "@biomejs/cli-win32-arm64": "2.6.4", + "@biomejs/cli-win32-x64": "2.6.4" + } + }, + "node_modules/@biomejs/cli-android-arm64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-android-arm64/-/cli-android-arm64-2.6.4.tgz", + "integrity": "sha512-jwrwigP4tBcHxzuV8izioX/BOUhJL/4e2rNSOE5nCjBkVC018LQAQUGiH+CNkFYvLtQ1dcZo5/xXERmaAGc7JQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-android-x64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-android-x64/-/cli-android-x64-2.6.4.tgz", + "integrity": "sha512-HdGInPJ4nZFwHxzvEtcp2nmSBtUyJrurmtIK43upFckuS3+ZqVM05HtsxQWnqGsuy/pxckIAjyKWlEKRlBXUyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.21.3" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.5.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.15.tgz", - "integrity": "sha512-BZVzFhJ/mUvTLMYbc9x6el0o2Uv5zP7bACYyhFb6fSc1giroXm7PL0a5KqMJ9F+/BnkYRjagD/ROSDVOZ6eapg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.6.4.tgz", + "integrity": "sha512-0VvcUz46wbg59Q4TZ59KkIjDJrxEyu4Eoe79+F4SChxoWe+W7AEyIp6F5r72C9+p6MsB6dIovnZt1D30lGM3XA==", "cpu": [ "arm64" ], @@ -288,9 +324,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.5.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.15.tgz", - "integrity": "sha512-V5Kw63V+fVGNFhFrizDxMbGXAzZCh8kYzJpy3GI6gTapWDg7bZK9oYC9CbVgQSEdTnupz0U5Efz8Ev+5vdBBXw==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.6.4.tgz", + "integrity": "sha512-fxmvkF81srlS3DiMyfm+aG3J0eG+6/BjaxRyjy57KbLSafnq0PO+hHfVWmtH2ty0S7nm6yDXBRwySfFeaRJKRQ==", "cpu": [ "x64" ], @@ -305,13 +341,16 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.5.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.15.tgz", - "integrity": "sha512-XaG7P7eeSLYETD3K9grfB0mQmpLQZjygdbsxekWBJAg4am79fGAtRpfRyNnTvXB2fZFNnJZG3nXm7HBat0+VUw==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.6.4.tgz", + "integrity": "sha512-6+bK1Y/evmIUiJr7auertuMbiaYVicJhqA1bZyL47lCMWugv8l0gFQCqvR/gN1vZMHYm+HwoTKgeMxdNFCoQTg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -322,13 +361,16 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.5.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.15.tgz", - "integrity": "sha512-tGzZUTcJCV7tj5Adh/Gn6nR4MycqA3iohq2LEcrarAgRBkU0h0OKd2UzCRkOsOYOuUGaUJb4YmqRqyy2feGGSw==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.6.4.tgz", + "integrity": "sha512-31ZaH/MHqPX+O+evRwRrCHQ8W5/9adY+/eMytGGWZnRx1T41QsWIF09+/WujynLBhWZUxNGS5CtsWLwgOFTAyg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -339,13 +381,16 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.5.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.15.tgz", - "integrity": "sha512-xE4iEW/3LqlYj9GFgGrFsFSH51dEEpUbYWBfeOv0q87WkUDxK2o/HhreSb7qMJqck70RVM6Lg96hgedcAYcOkA==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.6.4.tgz", + "integrity": "sha512-tbFZMdL6dP/ws4edXxYG9WhvaVa1ZDzL4G5DzahnWW7Psib9H+VTdyU4XXVxpJk5j/nYZroSbvSNyYlpW3yCIw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -356,13 +401,16 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.5.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.15.tgz", - "integrity": "sha512-IlxUcyxilVGPsE008x13pWdkTbU3nQpP2i9b5UrbOYj6goBkKsRX1XB7yJLcV+O1H9LBchIIm4adOaVLZBX0ZQ==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.6.4.tgz", + "integrity": "sha512-bomSa2XdQAGGj6Cwdm4M13qE+lN9+AOlW8KtI4hdyVyRRF/Z71qPAi95T4wtdZK0QpBbkTW8hgbZZCwPi4xzIw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -373,9 +421,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.5.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.15.tgz", - "integrity": "sha512-2kPKzhNlm8C+Ru3GcO0Me2ODkCBLrVOUNuyi84RJyWDVKAA4+Kjj3jMjL938lF6BRfvltC4vB2nPY9SvN51Ovg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.6.4.tgz", + "integrity": "sha512-eCsqt8Ma3eVap+oyJCDnJZH60SIU464B66n3y38EUTJdM1T/HfZol1FJC9vb+sHToX0ix8XzqPcwJnOzyAJz0g==", "cpu": [ "arm64" ], @@ -390,9 +438,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.5.15", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.15.tgz", - "integrity": "sha512-yAzh4UqEImV6Hcy0zjUqNfJAsUhoD64FBQCdTG8Md/Z2wQeVP/ZmJ3XRYAs1g8J9fjTFyl2fCtc5FB5til47+g==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.6.4.tgz", + "integrity": "sha512-pYsMy5AThiHFOZAsngg8qN4PurM6nInKBhVX7sbj3eNpRQqo1Hb0MgCwPL7VqVMrmcEEMj9Ks3qbkallR65e8g==", "cpu": [ "x64" ], @@ -1310,9 +1358,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.104.4", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.104.4.tgz", - "integrity": "sha512-nU4Yrb8Sjn0DeVb7PKG9uiepNavys3yoxy1FX0hViY4/P6Ng/mBje2SMl7i9812rtyHKtHyqiaC/td6GkI8StA==", + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.105.0.tgz", + "integrity": "sha512-7p56vRmT/z4UJ6sctGIdl5WRUQyUv53IUpUwEKi2g97zMbO7zh8ZsCLffGAvCPBR4xD72e3VXsDyLwXHhoNTMQ==", "license": "MIT", "funding": { "type": "github", @@ -1320,12 +1368,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.104.4", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.104.4.tgz", - "integrity": "sha512-YD5QkvUNy8Ey3LbOy2vwL97JjoYycwDv++cWAibTe+9BLYvl3c09XpYp5dZ2VcxcguwoDhGF+utYJYb79RB36w==", + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.105.0.tgz", + "integrity": "sha512-y+hMuDERvgg6ay1da0V1a6ZC0ZONt7dELxSFkqRZuwm7p1TTZs0MoXrPlBRflIpRoYjvzQVYxycnLImpFLhWrw==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.104.4" + "@tanstack/query-core": "5.105.0" }, "funding": { "type": "github", @@ -2015,9 +2063,9 @@ } }, "node_modules/keycloak-js": { - "version": "26.2.4", - "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-26.2.4.tgz", - "integrity": "sha512-PnXpR3ubETGOt0B/Qt2lxmPbkZr5bc3vlQsOqDoTPPQsZRp7JjhTKxlJ187uWh8qJhvBab6Gsjb06a8ayOPfuw==", + "version": "26.2.5", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-26.2.5.tgz", + "integrity": "sha512-iTR3hmYXlGZbBTskgr2PrGIfIzLrts8nwja2+D1gW6Q/S83C487rhM+WvPV9dxqXv81N0QwNG9BJAM60eDQ1Yw==", "license": "Apache-2.0", "workspaces": [ "test" @@ -2295,9 +2343,9 @@ } }, "node_modules/lucide-react": { - "version": "1.59.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.59.0.tgz", - "integrity": "sha512-5qgMyyCPU6OvnKpLS2t693+ZtiqA8oaSweWoUows0BLFKFf6V75m4+CptpmohZHpD5IuOVIBXHBBiv2vpLJmtw==", + "version": "1.64.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.64.0.tgz", + "integrity": "sha512-v626F/bcLNvFom6vzztHILtG9tUgXXbgA5AGkd82YkV401+Ko/NHyrWafbPwGofa+6p/EgDJcvEkIwsMH9098w==", "license": "ISC", "peerDependencies": { "@types/react": "*", @@ -2536,9 +2584,9 @@ "license": "MIT" }, "node_modules/react-router": { - "version": "7.18.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.4.tgz", - "integrity": "sha512-PUPQcMhMGRAslLcvtlPz/kmzBEWPhLdgLFrL7pLNepBL6dX0lWj4WD2cUYVgYCuT3jxvghYFg81cDTj44DhetQ==", + "version": "7.18.5", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.5.tgz", + "integrity": "sha512-dTgvcXDOrLPrlLHrcZAqWveiK1hMSqN+rGAJzegP8HtmNkfRxOoshGWBNX3ZJ0cZjOHkRdlUJtaIhZMLLyGyPw==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -2558,12 +2606,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.18.4", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.4.tgz", - "integrity": "sha512-yrfmJHIpDG7taCpqKjT1G5B6q3O2K+RN8/fgNf0lTjCwiPbQ0ei6vXX9ZjQR+7ld8Tr7Z5xmyMnZ8YJrphWQUw==", + "version": "7.18.5", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.5.tgz", + "integrity": "sha512-9HcfDMU6JJ/fp6gJZYGwNE6RknVGS4kBjH1HkNw959mCJi70TVwdipQWv2FEol9uGpi+SZTFtVpJPtEQfayeOw==", "license": "MIT", "dependencies": { - "react-router": "7.18.4" + "react-router": "7.18.5" }, "engines": { "node": ">=20.0.0" @@ -2657,9 +2705,9 @@ "license": "MIT" }, "node_modules/set-cookie-parser": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", - "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.3.tgz", + "integrity": "sha512-irZHtTqurLna57KQG/s+fzOwv/J6XSH4cs8M/vsrY3uCaQ3S6+QJguAH0WdN47SXlk82voMZ4naWMQf8fdEoIg==", "license": "MIT" }, "node_modules/source-map-js": { diff --git a/ui/package.json b/ui/package.json index 7db0b1a..7d447b1 100644 --- a/ui/package.json +++ b/ui/package.json @@ -20,19 +20,19 @@ "@base-ui/react": "^1.9.0", "@fontsource-variable/geist": "^5.3.0", "@fontsource/ibm-plex-mono": "^5.3.0", - "@tanstack/react-query": "^5.104.4", + "@tanstack/react-query": "^5.105.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "keycloak-js": "^26.2.4", - "lucide-react": "^1.59.0", + "keycloak-js": "^26.2.5", + "lucide-react": "^1.64.0", "react": "^19.3.1", "react-dom": "^19.3.1", - "react-router-dom": "^7.18.4", + "react-router-dom": "^7.18.5", "tailwind-merge": "^3.7.0" }, "devDependencies": { "@axe-core/playwright": "^4.14.0", - "@biomejs/biome": "2.5.15", + "@biomejs/biome": "2.6.4", "@playwright/test": "^1.64.1", "@tailwindcss/vite": "^4.3.3", "@testing-library/dom": "^10.4.2", @@ -53,6 +53,6 @@ }, "bundleBudget": { "initial": 195, - "totalJs": 245 + "totalJs": 250 } } diff --git a/ui/src/hooks/use-app-actions.ts b/ui/src/hooks/use-app-actions.ts index 1d6dc81..d64d0b6 100644 --- a/ui/src/hooks/use-app-actions.ts +++ b/ui/src/hooks/use-app-actions.ts @@ -53,38 +53,44 @@ export function useInvalidateApps() { }; } +/** One App action as a mutation, reporting its result in a toast and refreshing every query that shows the App. */ +function useAppAction( + action: AppAction, + invalidate: ReturnType, + onDeleted?: (app: App) => void, +) { + return useMutation({ + mutationFn: (app: App) => runAppAction(action, app), + onSuccess: (_data, app) => { + invalidate(app); + const done = DONE[action]; + toast.add({ + type: "success", + title: `${done.title} ${appLabel(app)}`, + description: done.description, + }); + if (action === "delete") onDeleted?.(app); + }, + onError: (err, app) => + toast.add({ + type: "error", + title: `Failed to ${action} ${appLabel(app)}`, + description: errorMessage(err), + }), + }); +} + /** * Start, stop, restart and delete for a single App, each reporting its result * in a toast and refreshing every query that shows the App. */ export function useAppActions({ onDeleted }: { onDeleted?: (app: App) => void } = {}) { const invalidate = useInvalidateApps(); - const useAction = (action: AppAction) => - useMutation({ - mutationFn: (app: App) => runAppAction(action, app), - onSuccess: (_data, app) => { - invalidate(app); - const done = DONE[action]; - toast.add({ - type: "success", - title: `${done.title} ${appLabel(app)}`, - description: done.description, - }); - if (action === "delete") onDeleted?.(app); - }, - onError: (err, app) => - toast.add({ - type: "error", - title: `Failed to ${action} ${appLabel(app)}`, - description: errorMessage(err), - }), - }); - const actions = { - start: useAction("start"), - stop: useAction("stop"), - restart: useAction("restart"), - remove: useAction("delete"), + start: useAppAction("start", invalidate, onDeleted), + stop: useAppAction("stop", invalidate, onDeleted), + restart: useAppAction("restart", invalidate, onDeleted), + remove: useAppAction("delete", invalidate, onDeleted), }; /** Whether `mutation` is currently running for `app`. */ const isPendingFor = (mutation: (typeof actions)[keyof typeof actions], app: App) =>