Files
coder/site/src/router.tsx
T
Jaayden Halko 3daac86efe refactor(site): add tabbed layout for single group page (#22486)
## Summary

Replaces the standalone **Settings** button on the single-group page
with a tabbed layout containing **Group members** and **Group settings**
tabs.

This uses the new figma designs here:
https://www.figma.com/design/klGTlHSPQwI4KBvAMdebrx/Customer-Usage-Controls-for-AI-Governance-Add-On?node-id=51-4907&m=dev

<img width="797" height="371" alt="Screenshot 2026-03-02 at 22 53 28"
src="https://github.com/user-attachments/assets/88d2ca8e-928f-404d-8569-ec4aba6c2ce4"
/>



### What changed

| File | Change |
|------|--------|
| `site/src/router.tsx` | Nested `settings` route under `:groupName`
layout route; added `GroupMembersPage` lazy import |
| `site/src/pages/GroupsPage/GroupPage.tsx` | Converted to shared
layout: header + tabs (`Tabs`/`TabsList`/`TabLink`) + `<Outlet />` with
context. Removed settings button and member-management code |
| `site/src/pages/GroupsPage/GroupMembersPage.tsx` | **New file** —
extracted member-management UI (add/remove members, member table) from
GroupPage; consumes data via `useOutletContext` |
| `site/src/pages/GroupsPage/GroupSettingsPage.tsx` | Switched from
independent group query to outlet context; removed duplicate
loading/error/title handling |
| `site/src/pages/GroupsPage/GroupSettingsPageView.tsx` | Removed
duplicate `ResourcePageHeader`; renders only the settings form |
| `site/e2e/tests/organizationGroups.spec.ts` | Updated selectors from
`"Settings"` link to `"Group settings"` link |

### How it works

- `:groupName` route now renders `GroupPage` as a **layout route** with
header, tabs, and `<Outlet />`.
- Index child route renders `GroupMembersPage` (member table +
add/remove).
- `settings` child route renders `GroupSettingsPage` (group settings
form).
- Shared group data + permissions are passed via React Router outlet
context, eliminating duplicate queries.
- URL structure is unchanged: `/organizations/:org/groups/:groupName`
(members) and `.../settings` (settings).

### Verification

- `pnpm exec tsc --noEmit` — passes
- `pnpm exec biome check --error-on-warnings` on all touched files —
passes
2026-03-02 18:12:40 +00:00

660 lines
21 KiB
TypeScript

import { GlobalErrorBoundary } from "components/ErrorBoundary/GlobalErrorBoundary";
import { TemplateRedirectController } from "pages/TemplatePage/TemplateRedirectController";
import { lazy, Suspense } from "react";
import {
createBrowserRouter,
createRoutesFromChildren,
Navigate,
Outlet,
Route,
ScrollRestoration,
} from "react-router";
import { Loader } from "./components/Loader/Loader";
import { RequireAuth } from "./contexts/auth/RequireAuth";
import { DashboardLayout } from "./modules/dashboard/DashboardLayout";
import AuditPage from "./pages/AuditPage/AuditPage";
import ConnectionLogPage from "./pages/ConnectionLogPage/ConnectionLogPage";
import { HealthLayout } from "./pages/HealthPage/HealthLayout";
import LoginOAuthDevicePage from "./pages/LoginOAuthDevicePage/LoginOAuthDevicePage";
import LoginPage from "./pages/LoginPage/LoginPage";
import { SetupPage } from "./pages/SetupPage/SetupPage";
import { TemplateLayout } from "./pages/TemplatePage/TemplateLayout";
import { TemplateSettingsLayout } from "./pages/TemplateSettingsPage/TemplateSettingsLayout";
import TemplatesPage from "./pages/TemplatesPage/TemplatesPage";
import UserSettingsLayout from "./pages/UserSettingsPage/Layout";
import UsersPage from "./pages/UsersPage/UsersPage";
import { WorkspaceSettingsLayout } from "./pages/WorkspaceSettingsPage/WorkspaceSettingsLayout";
import WorkspacesPage from "./pages/WorkspacesPage/WorkspacesPage";
// Lazy load pages
// - Pages that are secondary, not in the main navigation or not usually accessed
// - Pages that use heavy dependencies like charts or time libraries
const NotFoundPage = lazy(() => import("./pages/404Page/404Page"));
const DeploymentSettingsLayout = lazy(
() => import("./modules/management/DeploymentSettingsLayout"),
);
const DeploymentConfigProvider = lazy(
() => import("./modules/management/DeploymentConfigProvider"),
);
const OrganizationSidebarLayout = lazy(
() => import("./modules/management/OrganizationSidebarLayout"),
);
const OrganizationSettingsLayout = lazy(
() => import("./modules/management/OrganizationSettingsLayout"),
);
const CliAuthPage = lazy(() => import("./pages/CliAuthPage/CliAuthPage"));
const CliInstallPage = lazy(
() => import("./pages/CliInstallPage/CliInstallPage"),
);
const AccountPage = lazy(
() => import("./pages/UserSettingsPage/AccountPage/AccountPage"),
);
const AppearancePage = lazy(
() => import("./pages/UserSettingsPage/AppearancePage/AppearancePage"),
);
const SchedulePage = lazy(
() => import("./pages/UserSettingsPage/SchedulePage/SchedulePage"),
);
const SecurityPage = lazy(
() => import("./pages/UserSettingsPage/SecurityPage/SecurityPage"),
);
const SSHKeysPage = lazy(
() => import("./pages/UserSettingsPage/SSHKeysPage/SSHKeysPage"),
);
const TokensPage = lazy(
() => import("./pages/UserSettingsPage/TokensPage/TokensPage"),
);
const WorkspaceProxyPage = lazy(
() =>
import("./pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyPage"),
);
const CreateUserPage = lazy(
() => import("./pages/CreateUserPage/CreateUserPage"),
);
const WorkspaceBuildPage = lazy(
() => import("./pages/WorkspaceBuildPage/WorkspaceBuildPage"),
);
const WorkspacePage = lazy(() => import("./pages/WorkspacePage/WorkspacePage"));
const WorkspaceSchedulePage = lazy(
() =>
import(
"./pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage"
),
);
const WorkspaceParametersExperimentRouter = lazy(
() =>
import(
"./pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersExperimentRouter"
),
);
const WorkspaceSharingPage = lazy(
() =>
import(
"./pages/WorkspaceSettingsPage/WorkspaceSharingPage/WorkspaceSharingPage"
),
);
const TerminalPage = lazy(() => import("./pages/TerminalPage/TerminalPage"));
const TemplatePermissionsPage = lazy(
() =>
import(
"./pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPage"
),
);
const TemplateResourcesPage = lazy(
() =>
import("./pages/TemplatePage/TemplateResourcesPage/TemplateResourcesPage"),
);
const CreateWorkspacePage = lazy(
() => import("./pages/CreateWorkspacePage/CreateWorkspacePage"),
);
const OverviewPage = lazy(
() => import("./pages/DeploymentSettingsPage/OverviewPage/OverviewPage"),
);
const SecuritySettingsPage = lazy(
() =>
import(
"./pages/DeploymentSettingsPage/SecuritySettingsPage/SecuritySettingsPage"
),
);
const AppearanceSettingsPage = lazy(
() =>
import(
"./pages/DeploymentSettingsPage/AppearanceSettingsPage/AppearanceSettingsPage"
),
);
const UserAuthSettingsPage = lazy(
() =>
import(
"./pages/DeploymentSettingsPage/UserAuthSettingsPage/UserAuthSettingsPage"
),
);
const ExternalAuthSettingsPage = lazy(
() =>
import(
"./pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPage"
),
);
const OAuth2AppsSettingsPage = lazy(
() =>
import(
"./pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPage"
),
);
const EditOAuth2AppPage = lazy(
() =>
import(
"./pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/EditOAuth2AppPage"
),
);
const CreateOAuth2AppPage = lazy(
() =>
import(
"./pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPage"
),
);
const NetworkSettingsPage = lazy(
() =>
import(
"./pages/DeploymentSettingsPage/NetworkSettingsPage/NetworkSettingsPage"
),
);
const ObservabilitySettingsPage = lazy(
() =>
import(
"./pages/DeploymentSettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPage"
),
);
const AIGovernanceSettingsPage = lazy(
() =>
import(
"./pages/DeploymentSettingsPage/AIGovernanceSettingsPage/AIGovernanceSettingsPage"
),
);
const ExternalAuthPage = lazy(
() => import("./pages/ExternalAuthPage/ExternalAuthPage"),
);
const UserExternalAuthSettingsPage = lazy(
() => import("./pages/UserSettingsPage/ExternalAuthPage/ExternalAuthPage"),
);
const UserOAuth2ProviderSettingsPage = lazy(
() =>
import("./pages/UserSettingsPage/OAuth2ProviderPage/OAuth2ProviderPage"),
);
const TemplateVersionPage = lazy(
() => import("./pages/TemplateVersionPage/TemplateVersionPage"),
);
const TemplateVersionEditorPage = lazy(
() => import("./pages/TemplateVersionEditorPage/TemplateVersionEditorPage"),
);
const CreateTemplateGalleryPage = lazy(
() => import("./pages/CreateTemplateGalleryPage/CreateTemplateGalleryPage"),
);
const StarterTemplatePage = lazy(
() => import("pages/StarterTemplatePage/StarterTemplatePage"),
);
const CreateTemplatePage = lazy(
() => import("./pages/CreateTemplatePage/CreateTemplatePage"),
);
const TemplateVariablesPage = lazy(
() =>
import(
"./pages/TemplateSettingsPage/TemplateVariablesPage/TemplateVariablesPage"
),
);
const WorkspaceSettingsPage = lazy(
() => import("./pages/WorkspaceSettingsPage/WorkspaceSettingsPage"),
);
const CreateTokenPage = lazy(
() => import("./pages/CreateTokenPage/CreateTokenPage"),
);
const TemplateDocsPage = lazy(
() => import("./pages/TemplatePage/TemplateDocsPage/TemplateDocsPage"),
);
const TemplateFilesPage = lazy(
() => import("./pages/TemplatePage/TemplateFilesPage/TemplateFilesPage"),
);
const TemplateVersionsPage = lazy(
() =>
import("./pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage"),
);
const TemplateSchedulePage = lazy(
() =>
import(
"./pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage"
),
);
const TemplateSettingsPage = lazy(
() =>
import(
"./pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage"
),
);
const LicensesSettingsPage = lazy(
() =>
import(
"./pages/DeploymentSettingsPage/LicensesSettingsPage/LicensesSettingsPage"
),
);
const AddNewLicensePage = lazy(
() =>
import(
"./pages/DeploymentSettingsPage/LicensesSettingsPage/AddNewLicensePage"
),
);
const OrganizationRedirect = lazy(
() => import("./pages/OrganizationSettingsPage/OrganizationRedirect"),
);
const CreateOrganizationPage = lazy(
() => import("./pages/OrganizationSettingsPage/CreateOrganizationPage"),
);
const OrganizationSettingsPage = lazy(
() => import("./pages/OrganizationSettingsPage/OrganizationSettingsPage"),
);
const GroupsPageProvider = lazy(
() => import("./pages/GroupsPage/GroupsPageProvider"),
);
const GroupsPage = lazy(() => import("./pages/GroupsPage/GroupsPage"));
const CreateGroupPage = lazy(
() => import("./pages/GroupsPage/CreateGroupPage"),
);
const GroupPage = lazy(() => import("./pages/GroupsPage/GroupPage"));
const GroupMembersPage = lazy(
() => import("./pages/GroupsPage/GroupMembersPage"),
);
const GroupSettingsPage = lazy(
() => import("./pages/GroupsPage/GroupSettingsPage"),
);
const OrganizationMembersPage = lazy(
() => import("./pages/OrganizationSettingsPage/OrganizationMembersPage"),
);
const OrganizationCustomRolesPage = lazy(
() =>
import("./pages/OrganizationSettingsPage/CustomRolesPage/CustomRolesPage"),
);
const OrganizationIdPSyncPage = lazy(
() => import("./pages/OrganizationSettingsPage/IdpSyncPage/IdpSyncPage"),
);
const CreateEditRolePage = lazy(
() =>
import(
"./pages/OrganizationSettingsPage/CustomRolesPage/CreateEditRolePage"
),
);
const ProvisionersPage = lazy(
() =>
import(
"./pages/OrganizationSettingsPage/OrganizationProvisionersPage/OrganizationProvisionersPage"
),
);
const TemplateEmbedExperimentRouter = lazy(
() =>
import(
"./pages/TemplatePage/TemplateEmbedPage/TemplateEmbedExperimentRouter"
),
);
const TemplateInsightsPage = lazy(
() =>
import("./pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage"),
);
const TemplatePrebuildsPage = lazy(
() =>
import("./pages/TemplatePage/TemplatePrebuildsPage/TemplatePrebuildsPage"),
);
const PremiumPage = lazy(
() => import("./pages/DeploymentSettingsPage/PremiumPage/PremiumPage"),
);
const IconsPage = lazy(() => import("./pages/IconsPage/IconsPage"));
const AccessURLPage = lazy(() => import("./pages/HealthPage/AccessURLPage"));
const DatabasePage = lazy(() => import("./pages/HealthPage/DatabasePage"));
const DERPPage = lazy(() => import("./pages/HealthPage/DERPPage"));
const DERPRegionPage = lazy(() => import("./pages/HealthPage/DERPRegionPage"));
const WebsocketPage = lazy(() => import("./pages/HealthPage/WebsocketPage"));
const WorkspaceProxyHealthPage = lazy(
() => import("./pages/HealthPage/WorkspaceProxyPage"),
);
const ProvisionerDaemonsHealthPage = lazy(
() => import("./pages/HealthPage/ProvisionerDaemonsPage"),
);
const UserNotificationsPage = lazy(
() => import("./pages/UserSettingsPage/NotificationsPage/NotificationsPage"),
);
const DeploymentNotificationsPage = lazy(
() =>
import(
"./pages/DeploymentSettingsPage/NotificationsPage/NotificationsPage"
),
);
const RequestOTPPage = lazy(
() => import("./pages/ResetPasswordPage/RequestOTPPage"),
);
const ChangePasswordPage = lazy(
() => import("./pages/ResetPasswordPage/ChangePasswordPage"),
);
const IdpOrgSyncPage = lazy(
() => import("./pages/DeploymentSettingsPage/IdpOrgSyncPage/IdpOrgSyncPage"),
);
const ProvisionerKeysPage = lazy(
() =>
import(
"./pages/OrganizationSettingsPage/OrganizationProvisionerKeysPage/OrganizationProvisionerKeysPage"
),
);
const ProvisionerJobsPage = lazy(
() =>
import(
"./pages/OrganizationSettingsPage/OrganizationProvisionerJobsPage/OrganizationProvisionerJobsPage"
),
);
const AgentsPage = lazy(() => import("./pages/AgentsPage/AgentsPage"));
const AgentDetail = lazy(() => import("./pages/AgentsPage/AgentDetail"));
import {
AgentDetailSkeleton,
AgentsPageSkeleton,
} from "./pages/AgentsPage/AgentsSkeletons";
const TasksPage = lazy(() => import("./pages/TasksPage/TasksPage"));
const TaskPage = lazy(() => import("./pages/TaskPage/TaskPage"));
const AIBridgeLayout = lazy(
() => import("./pages/AIBridgePage/AIBridgeLayout"),
);
const AIBridgeRequestLogsPage = lazy(
() => import("./pages/AIBridgePage/RequestLogsPage/RequestLogsPage"),
);
const GlobalLayout = () => {
return (
<Suspense fallback={<Loader fullscreen />}>
<Outlet />
<ScrollRestoration />
</Suspense>
);
};
const templateRouter = () => {
return (
<Route path=":template">
<Route element={<TemplateRedirectController />}>
<Route element={<TemplateLayout />}>
<Route index element={<Navigate to="docs" replace />} />
<Route path="docs" element={<TemplateDocsPage />} />
<Route path="files" element={<TemplateFilesPage />} />
<Route path="resources" element={<TemplateResourcesPage />} />
<Route path="versions" element={<TemplateVersionsPage />} />
<Route path="embed" element={<TemplateEmbedExperimentRouter />} />
<Route path="insights" element={<TemplateInsightsPage />} />
<Route path="prebuilds" element={<TemplatePrebuildsPage />} />
</Route>
<Route path="workspace" element={<CreateWorkspacePage />} />
<Route path="settings" element={<TemplateSettingsLayout />}>
<Route index element={<TemplateSettingsPage />} />
<Route path="permissions" element={<TemplatePermissionsPage />} />
<Route path="variables" element={<TemplateVariablesPage />} />
<Route path="schedule" element={<TemplateSchedulePage />} />
</Route>
<Route path="versions">
<Route path=":version">
<Route index element={<TemplateVersionPage />} />
</Route>
</Route>
</Route>
</Route>
);
};
const groupsRouter = () => {
return (
<Route path="groups">
<Route element={<GroupsPageProvider />}>
<Route index element={<GroupsPage />} />
<Route path="create" element={<CreateGroupPage />} />
<Route path=":groupName" element={<GroupPage />}>
<Route index element={<GroupMembersPage />} />
<Route path="settings" element={<GroupSettingsPage />} />
</Route>
</Route>
</Route>
);
};
export const router = createBrowserRouter(
createRoutesFromChildren(
<Route element={<GlobalLayout />} errorElement={<GlobalErrorBoundary />}>
<Route path="login" element={<LoginPage />} />
<Route path="login/device" element={<LoginOAuthDevicePage />} />
<Route path="setup" element={<SetupPage />} />
<Route path="reset-password">
<Route index element={<RequestOTPPage />} />
<Route path="change" element={<ChangePasswordPage />} />
</Route>
{/* Dashboard routes */}
<Route element={<RequireAuth />}>
<Route element={<DashboardLayout />}>
<Route index element={<Navigate to="/workspaces" replace />} />
<Route
path="/external-auth/:provider"
element={<ExternalAuthPage />}
/>
<Route path="/workspaces" element={<WorkspacesPage />} />
<Route path="/starter-templates">
<Route index element={<CreateTemplateGalleryPage />} />
<Route path=":exampleId" element={<StarterTemplatePage />} />
</Route>
<Route path="/templates">
<Route index element={<TemplatesPage />} />
<Route path="new" element={<CreateTemplatePage />} />
<Route path=":organization">{templateRouter()}</Route>
{templateRouter()}
</Route>
<Route
path="/users/*"
element={<Navigate to="/deployment/users" replace />}
/>
<Route
path="/groups/*"
element={<Navigate to="/deployment/groups" replace />}
/>
<Route path="/audit" element={<AuditPage />} />
<Route path="/connectionlog" element={<ConnectionLogPage />} />
<Route path="/tasks" element={<TasksPage />} />
<Route path="/organizations" element={<OrganizationSettingsLayout />}>
<Route path="new" element={<CreateOrganizationPage />} />
{/* General settings for the default org can omit the organization name */}
<Route index element={<OrganizationRedirect />} />
<Route path=":organization" element={<OrganizationSidebarLayout />}>
<Route index element={<OrganizationMembersPage />} />
{groupsRouter()}
<Route path="roles">
<Route index element={<OrganizationCustomRolesPage />} />
<Route path="create" element={<CreateEditRolePage />} />
<Route path=":roleName" element={<CreateEditRolePage />} />
</Route>
<Route path="provisioners" element={<ProvisionersPage />} />
<Route
path="provisioner-jobs"
element={<ProvisionerJobsPage />}
/>
<Route
path="provisioner-keys"
element={<ProvisionerKeysPage />}
/>
<Route path="idp-sync" element={<OrganizationIdPSyncPage />} />
<Route path="settings" element={<OrganizationSettingsPage />} />
</Route>
</Route>
<Route path="/deployment" element={<DeploymentSettingsLayout />}>
<Route element={<DeploymentConfigProvider />}>
<Route path="overview" element={<OverviewPage />} />
<Route path="security" element={<SecuritySettingsPage />} />
<Route
path="observability"
element={<ObservabilitySettingsPage />}
/>
<Route
path="ai-governance"
element={<AIGovernanceSettingsPage />}
/>
<Route path="network" element={<NetworkSettingsPage />} />
<Route path="userauth" element={<UserAuthSettingsPage />} />
<Route
path="external-auth"
element={<ExternalAuthSettingsPage />}
/>
<Route
path="notifications"
element={<DeploymentNotificationsPage />}
/>
</Route>
<Route path="licenses">
<Route index element={<LicensesSettingsPage />} />
<Route path="add" element={<AddNewLicensePage />} />
</Route>
<Route path="appearance" element={<AppearanceSettingsPage />} />
<Route path="workspace-proxies" element={<WorkspaceProxyPage />} />
<Route path="oauth2-provider">
<Route index element={<NotFoundPage />} />
<Route path="apps">
<Route index element={<OAuth2AppsSettingsPage />} />
<Route path="add" element={<CreateOAuth2AppPage />} />
<Route path=":appId" element={<EditOAuth2AppPage />} />
</Route>
</Route>
<Route path="users" element={<UsersPage />} />
<Route path="users/create" element={<CreateUserPage />} />
{groupsRouter()}
<Route path="idp-org-sync" element={<IdpOrgSyncPage />} />
<Route path="premium" element={<PremiumPage />} />
</Route>
<Route path="/settings" element={<UserSettingsLayout />}>
<Route path="account" element={<AccountPage />} />
<Route path="appearance" element={<AppearancePage />} />
<Route path="schedule" element={<SchedulePage />} />
<Route path="security" element={<SecurityPage />} />
<Route path="ssh-keys" element={<SSHKeysPage />} />
<Route
path="external-auth"
element={<UserExternalAuthSettingsPage />}
/>
<Route
path="oauth2-provider"
element={<UserOAuth2ProviderSettingsPage />}
/>
<Route path="tokens">
<Route index element={<TokensPage />} />
<Route path="new" element={<CreateTokenPage />} />
</Route>
<Route path="notifications" element={<UserNotificationsPage />} />
</Route>
{/* In order for the 404 page to work properly the routes that start with
top level parameter must be fully qualified. */}
<Route path="/:username/:workspace">
<Route index element={<WorkspacePage />} />
<Route
path="builds/:buildNumber"
element={<WorkspaceBuildPage />}
/>
<Route path="settings" element={<WorkspaceSettingsLayout />}>
<Route index element={<WorkspaceSettingsPage />} />
<Route
path="parameters"
element={<WorkspaceParametersExperimentRouter />}
/>
<Route path="schedule" element={<WorkspaceSchedulePage />} />
<Route path="sharing" element={<WorkspaceSharingPage />} />
</Route>
</Route>
<Route path="/aibridge" element={<AIBridgeLayout />}>
<Route index element={<Navigate to="request-logs" replace />} />
<Route path="request-logs" element={<AIBridgeRequestLogsPage />} />
</Route>
<Route path="/health" element={<HealthLayout />}>
<Route index element={<Navigate to="access-url" replace />} />
<Route path="access-url" element={<AccessURLPage />} />
<Route path="database" element={<DatabasePage />} />
<Route path="derp" element={<DERPPage />} />
<Route path="derp/regions/:regionId" element={<DERPRegionPage />} />
<Route path="websocket" element={<WebsocketPage />} />
<Route
path="workspace-proxy"
element={<WorkspaceProxyHealthPage />}
/>
<Route
path="provisioner-daemons"
element={<ProvisionerDaemonsHealthPage />}
/>
</Route>
<Route path="/install" element={<CliInstallPage />} />
{/* Using path="*"" means "match anything", so this route
acts like a catch-all for URLs that we don't have explicit
routes for. */}
<Route path="*" element={<NotFoundPage />} />
</Route>
{/* Pages that don't have the dashboard layout */}
<Route
path="/templates/:template/versions/:version/edit"
element={<TemplateVersionEditorPage />}
/>
<Route
path="/templates/:organization/:template/versions/:version/edit"
element={<TemplateVersionEditorPage />}
/>
<Route
path="/:username/:workspace/terminal"
element={<TerminalPage />}
/>
<Route path="/cli-auth" element={<CliAuthPage />} />
<Route path="/icons" element={<IconsPage />} />
<Route path="/tasks/:username/:taskId" element={<TaskPage />} />
<Route
path="/agents"
element={
<Suspense fallback={<AgentsPageSkeleton />}>
<AgentsPage />
</Suspense>
}
>
<Route
path=":agentId"
element={
<Suspense fallback={<AgentDetailSkeleton />}>
<AgentDetail />
</Suspense>
}
/>
</Route>
</Route>
</Route>,
),
);