diff --git a/packages/system/dashboard/images/console/apps/console/src/App.routing.test.tsx b/packages/system/dashboard/images/console/apps/console/src/App.routing.test.tsx
index bffb82fd5..326b61b9d 100644
--- a/packages/system/dashboard/images/console/apps/console/src/App.routing.test.tsx
+++ b/packages/system/dashboard/images/console/apps/console/src/App.routing.test.tsx
@@ -134,6 +134,17 @@ describe("shell subtitle", () => {
expect(screen.queryByText("Tenant", { exact: true })).toBeNull()
})
+ // The rest of /admin mounts the same tenant-scoped resource pages as
+ // /console, and they resolve their namespace from the tenant context, so the
+ // picker has to stay: hiding it there leaves the active tenant invisible and
+ // unchangeable on the page it actually governs.
+ it("keeps the tenant picker on tenant-scoped admin pages", async () => {
+ const client = makeClient(TENANTS)
+ renderWithK8sProvider(, { client, initialRoute: "/admin/virtualmachines" })
+
+ expect(await screen.findByText("Tenant", { exact: true })).toBeTruthy()
+ })
+
it("still shows the tenant picker outside the admin portal", async () => {
const client = makeClient(TENANTS)
renderWithK8sProvider(, { client, initialRoute: "/console" })
diff --git a/packages/system/dashboard/images/console/apps/console/src/App.tsx b/packages/system/dashboard/images/console/apps/console/src/App.tsx
index 92b9121e6..a88a07257 100644
--- a/packages/system/dashboard/images/console/apps/console/src/App.tsx
+++ b/packages/system/dashboard/images/console/apps/console/src/App.tsx
@@ -18,11 +18,27 @@ import { CommandPalette } from "./components/command-palette/command-palette.tsx
import { AppConfigContext, useAppConfig, type AppConfig } from "./lib/config.ts"
import { DEFAULT_LANDING_PATH } from "./lib/portal.ts"
+const CLUSTER_SCOPED_ADMIN = [
+ "/admin/capacity",
+ "/admin/backups/backupclasses",
+ "/admin/external-ips",
+ "/admin/tenants",
+ "/admin/modules",
+]
+
function Shell({ username }: { username?: string }) {
const config = useAppConfig()
const { pathname } = useLocation()
const inMarketplace = pathname.startsWith("/marketplace")
const inAdmin = pathname.startsWith("/admin")
+ // Only the genuinely cluster-scoped admin pages have no tenant to show. The
+ // rest of /admin mounts the same tenant-scoped resource pages as /console
+ // (see lib/portal.ts), and those resolve their namespace from the tenant
+ // context, so hiding the picker there would leave the active tenant both
+ // invisible and unchangeable.
+ const inClusterScope = CLUSTER_SCOPED_ADMIN.some((prefix) =>
+ pathname.startsWith(prefix),
+ )
const marketplaceSections = useMarketplaceSidebarSections()
const consoleSections = useConsoleSidebarSections()
const adminSections = useAdminSidebarSections()
@@ -44,7 +60,7 @@ function Shell({ username }: { username?: string }) {
}
+ subtitle={inClusterScope ? undefined : }
onSearchClick={toggle}
version={config.version || import.meta.env.VITE_APP_VERSION}
logoSvg={config.logoSvg}
diff --git a/packages/system/dashboard/images/console/apps/console/src/components/AdditionalPropertiesField.tsx b/packages/system/dashboard/images/console/apps/console/src/components/AdditionalPropertiesField.tsx
index ac04d9b8e..e2c0371eb 100644
--- a/packages/system/dashboard/images/console/apps/console/src/components/AdditionalPropertiesField.tsx
+++ b/packages/system/dashboard/images/console/apps/console/src/components/AdditionalPropertiesField.tsx
@@ -1,12 +1,14 @@
import { useState, useMemo } from "react"
import type { FieldProps, RJSFSchema, TemplatesType } from "@rjsf/utils"
+import { RJSF_ID_SEPARATOR } from "../lib/rjsf-ids.ts"
import Form from "@rjsf/core"
import validator from "@rjsf/validator-ajv8"
import { customTemplates, customWidgets } from "./rjsf-registry.ts"
import { addDynamicOptionWidgets } from "../lib/dynamic-options.ts"
export function AdditionalPropertiesField(props: FieldProps) {
- const { schema, formData, onChange, readonly, disabled, name, required } = props
+ const { schema, formData, onChange, readonly, disabled, name, required, idSchema } =
+ props
const [newKey, setNewKey] = useState("")
// Get the schema for items from additionalProperties
@@ -87,6 +89,12 @@ export function AdditionalPropertiesField(props: FieldProps) {