diff --git a/DESIGN.md b/DESIGN.md
index 85aca29b..39b8c7f9 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -168,6 +168,10 @@ behavior. Defaults can be overridden by the owning screen; inspect it first.
6px corners, Iconoir icons, and `aria-current` on the active destination.
The generic [sidebar menu](./components/ui/sidebar-menu.tsx) has different
defaults (32px rows, 4px corners); these are separate components.
+ Settings opens a secondary menu in the same sidebar, with a back arrow to
+ restore the main navigation. Each Settings section has a scoped page URL.
+ The mobile navigation sheet follows the same pattern and closes after a
+ section is selected. Billing alone has Billing Settings and Usage tabs.
- **[Dialog](./components/ui/dialog.tsx):** reuse the Radix-backed focus and
dismissal behavior, 6px corners, 24px padding, and responsive action layout.
Provide a title and description. Use inline disclosure when it can complete
diff --git a/app/(dashboard)/[scope]/settings/[section]/page.tsx b/app/(dashboard)/[scope]/settings/[section]/page.tsx
new file mode 100644
index 00000000..d63f5a2e
--- /dev/null
+++ b/app/(dashboard)/[scope]/settings/[section]/page.tsx
@@ -0,0 +1,33 @@
+import { notFound } from "next/navigation";
+import { getScopeContext } from "@/lib/scope-context";
+import { PERSONAL_SETTINGS, TEAM_SETTINGS } from "@/lib/settings-navigation";
+import { TeamSettingsClient } from "@/components/settings/team-settings-client";
+import { TeamsListSection } from "@/components/settings/teams-list-section";
+import { CliApiKeysSection } from "@/components/settings/cli-api-keys-section";
+import { PersonalAccountPage } from "../_components/personal-account-page";
+import { ProviderKeysPage } from "../_components/provider-keys-page";
+
+export default async function SettingsSectionPage({ params }: {
+ params: Promise<{ section: string }>;
+}) {
+ const [{ section }, scope] = await Promise.all([params, getScopeContext()]);
+ const items = scope.kind === "team" ? TEAM_SETTINGS : PERSONAL_SETTINGS;
+ const item = items.find((entry) => entry.id === section);
+ if (!item) notFound();
+
+ if (scope.kind === "team") {
+ // Billing and MCP have static route segments; this page owns the other sections.
+ if (section !== "members" && section !== "keys" && section !== "models" && section !== "audit") notFound();
+ return
Opening Settings…
; } - -export default SettingsPageClient diff --git a/components/app-sidebar.tsx b/components/app-sidebar.tsx index 40d59a0c..7fda47b2 100644 --- a/components/app-sidebar.tsx +++ b/components/app-sidebar.tsx @@ -19,6 +19,7 @@ import { DeliveryTruck, Flash, PlugTypeA, + NavArrowRight, Repository, Rocket, Search, @@ -33,6 +34,8 @@ import { import { formatUsd } from "@/lib/billing/catalog"; import type { CapacityBillingSummaryV2 } from "@/lib/billing/capacity-summary-types"; import { scopedHref } from "@/lib/scoped-href"; +import { SettingsNavigation } from "@/components/settings/settings-navigation"; +import { useSettingsNavigation } from "@/hooks/use-settings-navigation"; const SIDEBAR_WIDTH_KEY = "mogplex.appSidebar.width"; const SIDEBAR_COLLAPSED_KEY = "mogplex.appSidebar.collapsed"; @@ -40,6 +43,7 @@ const DEFAULT_WIDTH = 272; const MIN_WIDTH = 64; const COMPACT_THRESHOLD = 120; const MAX_WIDTH = 320; +const SETTINGS_MIN_WIDTH = 240; const NAV_ICONS = { control: Rocket, @@ -72,16 +76,22 @@ function SidebarNavLink({ item, compact, pathname, + onClick, + linkRef, }: { item: AppNavItem; compact: boolean; pathname: string; + onClick?: () => void; + linkRef?: React.RefNo usage costs yet.
- ) : ( - summary.recentCosts.map((cost) => ( -{cost.description}
-- {formatDate(cost.occurredAt)} ·{" "} - {cost.status.replaceAll("_", " ")} -
-- {cost.totalCents === null - ? "In progress" - : formatUsd(cost.totalCents)} -
-@@ -489,6 +471,6 @@ export function BillingSection({ embedded = false }: { embedded?: boolean }) { }} open={selectedAddOn !== null} /> -
No usage costs yet.
+ ) : summary.recentCosts.map((cost) => ( +{cost.description}
+{formatDate(cost.occurredAt)} · {cost.status.replaceAll("_", " ")}
+{cost.totalCents === null ? "In progress" : formatUsd(cost.totalCents)}
++ {membersError ? "Unable to load team permissions." : membersData ? "Only a team owner or admin can view the audit log." : "Loading team permissions…"} +
+ )}