diff --git a/app/pages/system/silos/SiloFleetRolesTab.tsx b/app/pages/system/silos/SiloFleetRolesTab.tsx index 25aa21c74..e8897cb14 100644 --- a/app/pages/system/silos/SiloFleetRolesTab.tsx +++ b/app/pages/system/silos/SiloFleetRolesTab.tsx @@ -10,6 +10,7 @@ import { usePrefetchedApiQuery } from '@oxide/api' import { Cloud24Icon, NextArrow12Icon } from '@oxide/design-system/icons/react' import { Badge } from '@oxide/design-system/ui' +import { makeCrumb } from '~/hooks/use-crumbs' import { useSiloSelector } from '~/hooks/use-params' import { EmptyMessage } from '~/ui/lib/EmptyMessage' import { TableEmptyBox } from '~/ui/lib/Table' @@ -51,3 +52,5 @@ export default function SiloFleetRolesTab() { ) } + +export const handle = makeCrumb('Fleet Roles') diff --git a/app/pages/system/silos/SiloIdpsTab.tsx b/app/pages/system/silos/SiloIdpsTab.tsx index 7db16cad2..4f5411dbe 100644 --- a/app/pages/system/silos/SiloIdpsTab.tsx +++ b/app/pages/system/silos/SiloIdpsTab.tsx @@ -13,6 +13,7 @@ import { Cloud24Icon } from '@oxide/design-system/icons/react' import { Badge } from '@oxide/design-system/ui' import { getListQFn, queryClient, type IdentityProvider } from '~/api' +import { makeCrumb } from '~/hooks/use-crumbs' import { getSiloSelector, useSiloSelector } from '~/hooks/use-params' import { LinkCell } from '~/table/cells/LinkCell' import { Columns } from '~/table/columns/common' @@ -74,3 +75,7 @@ export default function SiloIdpsTab() { ) } + +export const handle = makeCrumb('Identity Providers', (params) => + pb.siloIdps(getSiloSelector(params)) +) diff --git a/app/pages/system/silos/SiloIpPoolsTab.tsx b/app/pages/system/silos/SiloIpPoolsTab.tsx index ddeb6feb6..94645064e 100644 --- a/app/pages/system/silos/SiloIpPoolsTab.tsx +++ b/app/pages/system/silos/SiloIpPoolsTab.tsx @@ -23,6 +23,7 @@ import { Networking24Icon } from '@oxide/design-system/icons/react' import { ComboboxField } from '~/components/form/fields/ComboboxField' import { HL } from '~/components/HL' +import { makeCrumb } from '~/hooks/use-crumbs' import { getSiloSelector, useSiloSelector } from '~/hooks/use-params' import { confirmAction } from '~/stores/confirm-action' import { addToast } from '~/stores/toast' @@ -198,6 +199,8 @@ export default function SiloIpPoolsTab() { ) } +export const handle = makeCrumb('IP Pools') + type LinkPoolFormValues = { pool: string | undefined } diff --git a/app/pages/system/silos/SiloPage.tsx b/app/pages/system/silos/SiloPage.tsx index b23597e63..ff12c9a2f 100644 --- a/app/pages/system/silos/SiloPage.tsx +++ b/app/pages/system/silos/SiloPage.tsx @@ -25,7 +25,10 @@ export async function clientLoader({ params }: LoaderFunctionArgs) { return null } -export const handle = makeCrumb((p) => p.silo!) +export const handle = makeCrumb( + (p) => p.silo!, + (p) => pb.silo(getSiloSelector(p)) +) export default function SiloPage() { const siloSelector = useSiloSelector() diff --git a/app/pages/system/silos/SiloQuotasTab.tsx b/app/pages/system/silos/SiloQuotasTab.tsx index c65ca1055..fa313f75c 100644 --- a/app/pages/system/silos/SiloQuotasTab.tsx +++ b/app/pages/system/silos/SiloQuotasTab.tsx @@ -20,6 +20,7 @@ import { } from '~/api' import { NumberField } from '~/components/form/fields/NumberField' import { SideModalForm } from '~/components/form/SideModalForm' +import { makeCrumb } from '~/hooks/use-crumbs' import { getSiloSelector, useSiloSelector } from '~/hooks/use-params' import { addToast } from '~/stores/toast' import { Button } from '~/ui/lib/Button' @@ -97,6 +98,8 @@ export default function SiloQuotasTab() { ) } +export const handle = makeCrumb('Quotas') + function EditQuotasForm({ onDismiss }: { onDismiss: () => void }) { const { silo } = useSiloSelector() const { data: utilization } = usePrefetchedQuery( diff --git a/app/pages/system/silos/SiloScimTab.tsx b/app/pages/system/silos/SiloScimTab.tsx index e23333745..1bc8029bd 100644 --- a/app/pages/system/silos/SiloScimTab.tsx +++ b/app/pages/system/silos/SiloScimTab.tsx @@ -19,6 +19,7 @@ import { usePrefetchedApiQuery, type ScimClientBearerToken, } from '~/api' +import { makeCrumb } from '~/hooks/use-crumbs' import { getSiloSelector, useSiloSelector } from '~/hooks/use-params' import { confirmDelete } from '~/stores/confirm-delete' import { addToast } from '~/stores/toast' @@ -174,6 +175,8 @@ export default function SiloScimTab() { ) } +export const handle = makeCrumb('SCIM') + function CreateTokenModal({ siloSelector, onDismiss, diff --git a/app/util/__snapshots__/path-builder.spec.ts.snap b/app/util/__snapshots__/path-builder.spec.ts.snap index 069c7927d..0666f345a 100644 --- a/app/util/__snapshots__/path-builder.spec.ts.snap +++ b/app/util/__snapshots__/path-builder.spec.ts.snap @@ -512,7 +512,11 @@ exports[`breadcrumbs 2`] = ` }, { "label": "s", - "path": "/system/silos/s", + "path": "/system/silos/s/idps", + }, + { + "label": "Identity Providers", + "path": "/system/silos/s/idps", }, ], "serialConsole (/projects/p/instances/i/serial-console)": [ @@ -544,7 +548,11 @@ exports[`breadcrumbs 2`] = ` }, { "label": "s", - "path": "/system/silos/s", + "path": "/system/silos/s/idps", + }, + { + "label": "Identity Providers", + "path": "/system/silos/s/idps", }, ], "siloAccess (/access)": [ @@ -560,7 +568,11 @@ exports[`breadcrumbs 2`] = ` }, { "label": "s", - "path": "/system/silos/s", + "path": "/system/silos/s/idps", + }, + { + "label": "Fleet Roles", + "path": "/system/silos/s/fleet-roles", }, ], "siloIdps (/system/silos/s/idps)": [ @@ -570,7 +582,11 @@ exports[`breadcrumbs 2`] = ` }, { "label": "s", - "path": "/system/silos/s", + "path": "/system/silos/s/idps", + }, + { + "label": "Identity Providers", + "path": "/system/silos/s/idps", }, ], "siloIdpsNew (/system/silos/s/idps-new)": [ @@ -580,7 +596,11 @@ exports[`breadcrumbs 2`] = ` }, { "label": "s", - "path": "/system/silos/s", + "path": "/system/silos/s/idps", + }, + { + "label": "Identity Providers", + "path": "/system/silos/s/idps", }, ], "siloImageEdit (/images/im/edit)": [ @@ -602,7 +622,11 @@ exports[`breadcrumbs 2`] = ` }, { "label": "s", - "path": "/system/silos/s", + "path": "/system/silos/s/idps", + }, + { + "label": "IP Pools", + "path": "/system/silos/s/ip-pools", }, ], "siloQuotas (/system/silos/s/quotas)": [ @@ -612,7 +636,11 @@ exports[`breadcrumbs 2`] = ` }, { "label": "s", - "path": "/system/silos/s", + "path": "/system/silos/s/idps", + }, + { + "label": "Quotas", + "path": "/system/silos/s/quotas", }, ], "siloScim (/system/silos/s/scim)": [ @@ -622,7 +650,11 @@ exports[`breadcrumbs 2`] = ` }, { "label": "s", - "path": "/system/silos/s", + "path": "/system/silos/s/idps", + }, + { + "label": "SCIM", + "path": "/system/silos/s/scim", }, ], "siloUtilization (/utilization)": [ diff --git a/test/e2e/breadcrumbs.e2e.ts b/test/e2e/breadcrumbs.e2e.ts index 5f3393ea2..695e89521 100644 --- a/test/e2e/breadcrumbs.e2e.ts +++ b/test/e2e/breadcrumbs.e2e.ts @@ -65,7 +65,8 @@ test('breadcrumbs', async ({ page }) => { await page.goto('/system/silos/maze-war') const siloCrumbs: Pair[] = [ ['Silos', '/system/silos'], - ['maze-war', '/system/silos/maze-war'], + ['maze-war', '/system/silos/maze-war/idps'], + ['Identity Providers', '/system/silos/maze-war/idps'], ] await expectCrumbs(page, siloCrumbs) // same crumbs on IdP detail side modal @@ -73,6 +74,15 @@ test('breadcrumbs', async ({ page }) => { await expect(page).toHaveURL('/system/silos/maze-war/idps/saml/mock-idp') await expectCrumbs(page, siloCrumbs) + await page.keyboard.press('Escape') + await page.getByRole('tab', { name: 'Fleet Roles' }).click() + await expect(page).toHaveURL('/system/silos/maze-war/fleet-roles') + await expectCrumbs(page, [ + ['Silos', '/system/silos'], + ['maze-war', '/system/silos/maze-war/idps'], + ['Fleet Roles', '/system/silos/maze-war/fleet-roles'], + ]) + await page.goto('/system/networking/ip-pools/ip-pool-1') const poolCrumbs: Pair[] = [ ['IP Pools', '/system/networking/ip-pools'],