@stigmer/react 0.0.98 → 0.0.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/index.d.ts +17 -3
  2. package/index.d.ts.map +1 -1
  3. package/index.js +16 -3
  4. package/index.js.map +1 -1
  5. package/internal/menu.d.ts +17 -0
  6. package/internal/menu.d.ts.map +1 -0
  7. package/internal/menu.js +43 -0
  8. package/internal/menu.js.map +1 -0
  9. package/library/LibraryBreadcrumbContext.d.ts +30 -0
  10. package/library/LibraryBreadcrumbContext.d.ts.map +1 -0
  11. package/library/LibraryBreadcrumbContext.js +39 -0
  12. package/library/LibraryBreadcrumbContext.js.map +1 -0
  13. package/library/index.d.ts +1 -0
  14. package/library/index.d.ts.map +1 -1
  15. package/library/index.js +1 -0
  16. package/library/index.js.map +1 -1
  17. package/organization/OrgProvider.d.ts +59 -0
  18. package/organization/OrgProvider.d.ts.map +1 -0
  19. package/organization/OrgProvider.js +130 -0
  20. package/organization/OrgProvider.js.map +1 -0
  21. package/organization/OrgSwitcher.d.ts +36 -0
  22. package/organization/OrgSwitcher.d.ts.map +1 -0
  23. package/organization/OrgSwitcher.js +73 -0
  24. package/organization/OrgSwitcher.js.map +1 -0
  25. package/organization/index.d.ts +6 -0
  26. package/organization/index.d.ts.map +1 -1
  27. package/organization/index.js +3 -0
  28. package/organization/index.js.map +1 -1
  29. package/organization/useOrgGate.d.ts +101 -0
  30. package/organization/useOrgGate.d.ts.map +1 -0
  31. package/organization/useOrgGate.js +99 -0
  32. package/organization/useOrgGate.js.map +1 -0
  33. package/package.json +5 -4
  34. package/runner/RunnerListPanel.d.ts +13 -8
  35. package/runner/RunnerListPanel.d.ts.map +1 -1
  36. package/runner/RunnerListPanel.js +10 -6
  37. package/runner/RunnerListPanel.js.map +1 -1
  38. package/settings/ApiKeysSection.d.ts +3 -0
  39. package/settings/ApiKeysSection.d.ts.map +1 -0
  40. package/settings/ApiKeysSection.js +30 -0
  41. package/settings/ApiKeysSection.js.map +1 -0
  42. package/settings/EnvironmentsSection.d.ts +3 -0
  43. package/settings/EnvironmentsSection.d.ts.map +1 -0
  44. package/settings/EnvironmentsSection.js +49 -0
  45. package/settings/EnvironmentsSection.js.map +1 -0
  46. package/settings/IdentityProvidersSection.d.ts +12 -0
  47. package/settings/IdentityProvidersSection.d.ts.map +1 -0
  48. package/settings/IdentityProvidersSection.js +34 -0
  49. package/settings/IdentityProvidersSection.js.map +1 -0
  50. package/settings/InvitationsSection.d.ts +3 -0
  51. package/settings/InvitationsSection.d.ts.map +1 -0
  52. package/settings/InvitationsSection.js +13 -0
  53. package/settings/InvitationsSection.js.map +1 -0
  54. package/settings/MembersSection.d.ts +3 -0
  55. package/settings/MembersSection.d.ts.map +1 -0
  56. package/settings/MembersSection.js +14 -0
  57. package/settings/MembersSection.js.map +1 -0
  58. package/settings/OAuthAppsSection.d.ts +3 -0
  59. package/settings/OAuthAppsSection.d.ts.map +1 -0
  60. package/settings/OAuthAppsSection.js +33 -0
  61. package/settings/OAuthAppsSection.js.map +1 -0
  62. package/settings/OrgProfileSection.d.ts +3 -0
  63. package/settings/OrgProfileSection.d.ts.map +1 -0
  64. package/settings/OrgProfileSection.js +15 -0
  65. package/settings/OrgProfileSection.js.map +1 -0
  66. package/settings/PlatformClientsSection.d.ts +3 -0
  67. package/settings/PlatformClientsSection.d.ts.map +1 -0
  68. package/settings/PlatformClientsSection.js +48 -0
  69. package/settings/PlatformClientsSection.js.map +1 -0
  70. package/settings/UsageSection.d.ts +3 -0
  71. package/settings/UsageSection.d.ts.map +1 -0
  72. package/settings/UsageSection.js +14 -0
  73. package/settings/UsageSection.js.map +1 -0
  74. package/settings/index.d.ts +13 -0
  75. package/settings/index.d.ts.map +1 -0
  76. package/settings/index.js +11 -0
  77. package/settings/index.js.map +1 -0
  78. package/settings/settings-nav.d.ts +25 -0
  79. package/settings/settings-nav.d.ts.map +1 -0
  80. package/settings/settings-nav.js +41 -0
  81. package/settings/settings-nav.js.map +1 -0
  82. package/src/index.ts +32 -1
  83. package/src/internal/menu.tsx +160 -0
  84. package/src/library/LibraryBreadcrumbContext.tsx +70 -0
  85. package/src/library/index.ts +6 -0
  86. package/src/organization/OrgProvider.tsx +184 -0
  87. package/src/organization/OrgSwitcher.tsx +275 -0
  88. package/src/organization/index.ts +10 -0
  89. package/src/organization/useOrgGate.ts +183 -0
  90. package/src/runner/RunnerListPanel.tsx +14 -9
  91. package/src/settings/ApiKeysSection.tsx +96 -0
  92. package/src/settings/EnvironmentsSection.tsx +162 -0
  93. package/src/settings/IdentityProvidersSection.tsx +123 -0
  94. package/src/settings/InvitationsSection.tsx +42 -0
  95. package/src/settings/MembersSection.tsx +41 -0
  96. package/src/settings/OAuthAppsSection.tsx +100 -0
  97. package/src/settings/OrgProfileSection.tsx +41 -0
  98. package/src/settings/PlatformClientsSection.tsx +149 -0
  99. package/src/settings/UsageSection.tsx +41 -0
  100. package/src/settings/index.ts +13 -0
  101. package/src/settings/settings-nav.ts +78 -0
  102. package/src/user/UserMenu.tsx +241 -0
  103. package/src/user/index.ts +2 -0
  104. package/styles.css +1 -1
  105. package/user/UserMenu.d.ts +82 -0
  106. package/user/UserMenu.d.ts.map +1 -0
  107. package/user/UserMenu.js +51 -0
  108. package/user/UserMenu.js.map +1 -0
  109. package/user/index.d.ts +3 -0
  110. package/user/index.d.ts.map +1 -0
  111. package/user/index.js +2 -0
  112. package/user/index.js.map +1 -0
@@ -0,0 +1,41 @@
1
+ "use client";
2
+
3
+ import { OrgMembersPanel } from "../iam-policy/OrgMembersPanel";
4
+ import { useResourceAvailable, ApiResourceKind } from "../deployment-mode";
5
+ import { CloudFeatureNotice } from "../internal/CloudFeatureNotice";
6
+ import { useOrg } from "../organization/OrgProvider";
7
+
8
+ /** Settings section for organization membership and role management. */
9
+ export function MembersSection() {
10
+ const { activeOrg } = useOrg();
11
+ const membersAvailable = useResourceAvailable(ApiResourceKind.iam_policy);
12
+ const orgId = activeOrg?.metadata?.id ?? "";
13
+
14
+ return (
15
+ <section aria-labelledby="members-heading">
16
+ <h2
17
+ id="members-heading"
18
+ className="text-foreground mb-1 text-sm font-semibold"
19
+ >
20
+ Members
21
+ </h2>
22
+ <p className="text-muted-foreground mb-4 text-xs">
23
+ Manage who has access to this organization and what they can do.
24
+ Members can be granted owner, admin, member, or viewer roles.
25
+ </p>
26
+
27
+ {!membersAvailable ? (
28
+ <CloudFeatureNotice>
29
+ Members management is not available in local mode. IAM policies
30
+ require Stigmer Cloud.
31
+ </CloudFeatureNotice>
32
+ ) : !orgId ? (
33
+ <p className="text-muted-foreground py-4 text-center text-xs">
34
+ Select an organization to manage members.
35
+ </p>
36
+ ) : (
37
+ <OrgMembersPanel orgId={orgId} />
38
+ )}
39
+ </section>
40
+ );
41
+ }
@@ -0,0 +1,100 @@
1
+ "use client";
2
+
3
+ import { useCallback, useRef, useState } from "react";
4
+ import type { OAuthApp } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/api_pb";
5
+ import { OAuthAppListPanel } from "../oauth-app/OAuthAppListPanel";
6
+ import { CreateOAuthAppForm } from "../oauth-app/CreateOAuthAppForm";
7
+ import { OAuthAppDetailPanel } from "../oauth-app/OAuthAppDetailPanel";
8
+ import { useResourceAvailable, ApiResourceKind } from "../deployment-mode";
9
+ import { CloudFeatureNotice } from "../internal/CloudFeatureNotice";
10
+ import { useActiveOrgSlug } from "../organization/OrgProvider";
11
+
12
+ type FlowState =
13
+ | { phase: "idle" }
14
+ | { phase: "creating" }
15
+ | { phase: "editing"; oauthApp: OAuthApp };
16
+
17
+ /** Settings section for organization OAuth app credentials. */
18
+ export function OAuthAppsSection() {
19
+ const org = useActiveOrgSlug();
20
+ const oauthAppsAvailable = useResourceAvailable(ApiResourceKind.oauth_app);
21
+
22
+ const [flow, setFlow] = useState<FlowState>({ phase: "idle" });
23
+ const listRefetchRef = useRef<(() => void) | null>(null);
24
+
25
+ const handleRefetchRef = useCallback((refetch: () => void) => {
26
+ listRefetchRef.current = refetch;
27
+ }, []);
28
+
29
+ const handleCreated = useCallback(() => {
30
+ listRefetchRef.current?.();
31
+ setFlow({ phase: "idle" });
32
+ }, []);
33
+
34
+ const handleUpdated = useCallback(() => {
35
+ listRefetchRef.current?.();
36
+ setFlow({ phase: "idle" });
37
+ }, []);
38
+
39
+ const handleDeleted = useCallback(() => {
40
+ listRefetchRef.current?.();
41
+ setFlow({ phase: "idle" });
42
+ }, []);
43
+
44
+ return (
45
+ <section aria-labelledby="oauth-apps-heading">
46
+ <div className="mb-3 flex items-center justify-between">
47
+ <h2
48
+ id="oauth-apps-heading"
49
+ className="text-foreground text-sm font-semibold"
50
+ >
51
+ OAuth Apps
52
+ </h2>
53
+
54
+ {oauthAppsAvailable && org && flow.phase === "idle" && (
55
+ <button
56
+ type="button"
57
+ onClick={() => setFlow({ phase: "creating" })}
58
+ className="text-primary hover:text-foreground text-xs font-medium transition-colors"
59
+ >
60
+ + New OAuth app
61
+ </button>
62
+ )}
63
+ </div>
64
+ <p className="text-muted-foreground mb-4 text-xs">
65
+ OAuth app credentials configured for your organization. Create new
66
+ apps here or bring your own from an MCP server&apos;s detail page.
67
+ </p>
68
+
69
+ {!oauthAppsAvailable ? (
70
+ <CloudFeatureNotice>
71
+ OAuth apps are not available in local mode. Bring-your-own-app
72
+ OAuth requires the cloud platform.
73
+ </CloudFeatureNotice>
74
+ ) : flow.phase === "creating" ? (
75
+ <div className="border-border bg-card rounded-lg border p-4">
76
+ <CreateOAuthAppForm
77
+ org={org}
78
+ onCreated={handleCreated}
79
+ onCancel={() => setFlow({ phase: "idle" })}
80
+ />
81
+ </div>
82
+ ) : flow.phase === "editing" ? (
83
+ <div className="border-border bg-card rounded-lg border p-4">
84
+ <OAuthAppDetailPanel
85
+ oauthApp={flow.oauthApp}
86
+ onUpdated={handleUpdated}
87
+ onDeleted={handleDeleted}
88
+ onBack={() => setFlow({ phase: "idle" })}
89
+ />
90
+ </div>
91
+ ) : (
92
+ <OAuthAppListPanel
93
+ org={org}
94
+ onEdit={(app) => setFlow({ phase: "editing", oauthApp: app })}
95
+ onRefetchRef={handleRefetchRef}
96
+ />
97
+ )}
98
+ </section>
99
+ );
100
+ }
@@ -0,0 +1,41 @@
1
+ "use client";
2
+
3
+ import { useCallback } from "react";
4
+ import type { Organization } from "@stigmer/protos/ai/stigmer/tenancy/organization/v1/api_pb";
5
+ import { OrgProfilePanel } from "../organization/OrgProfilePanel";
6
+ import { useOrg } from "../organization/OrgProvider";
7
+
8
+ /** Settings section for editing the active organization profile. */
9
+ export function OrgProfileSection() {
10
+ const { activeOrg, refresh } = useOrg();
11
+ const orgId = activeOrg?.metadata?.id ?? "";
12
+
13
+ const handleUpdated = useCallback(
14
+ (org: Organization) => {
15
+ refresh(org.metadata?.slug);
16
+ },
17
+ [refresh],
18
+ );
19
+
20
+ return (
21
+ <section aria-labelledby="org-profile-heading">
22
+ <h2
23
+ id="org-profile-heading"
24
+ className="text-foreground mb-1 text-sm font-semibold"
25
+ >
26
+ Organization Profile
27
+ </h2>
28
+ <p className="text-muted-foreground mb-6 text-xs">
29
+ Manage your organization&apos;s display name, description, and logo.
30
+ </p>
31
+
32
+ {!orgId ? (
33
+ <p className="text-muted-foreground py-4 text-center text-xs">
34
+ Select an organization to view its profile.
35
+ </p>
36
+ ) : (
37
+ <OrgProfilePanel orgId={orgId} onUpdated={handleUpdated} />
38
+ )}
39
+ </section>
40
+ );
41
+ }
@@ -0,0 +1,149 @@
1
+ "use client";
2
+
3
+ import { useCallback, useRef, useState } from "react";
4
+ import type { PlatformClient } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/api_pb";
5
+ import type { PlatformClientCreateResponse } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/io_pb";
6
+ import { PlatformClientListPanel } from "../platform-client/PlatformClientListPanel";
7
+ import { CreatePlatformClientForm } from "../platform-client/CreatePlatformClientForm";
8
+ import { PlatformClientDetailPanel } from "../platform-client/PlatformClientDetailPanel";
9
+ import { PlatformClientSecretAlert } from "../platform-client/PlatformClientSecretAlert";
10
+ import { useResourceAvailable, ApiResourceKind } from "../deployment-mode";
11
+ import { CloudFeatureNotice } from "../internal/CloudFeatureNotice";
12
+ import { useActiveOrgSlug } from "../organization/OrgProvider";
13
+
14
+ type FlowState =
15
+ | { phase: "idle" }
16
+ | { phase: "creating" }
17
+ | {
18
+ phase: "revealing";
19
+ clientId: string;
20
+ clientSecret: string;
21
+ context: "created" | "rotated";
22
+ }
23
+ | { phase: "editing"; platformClient: PlatformClient };
24
+
25
+ /** Settings section for creating and maintaining platform clients. */
26
+ export function PlatformClientsSection() {
27
+ const org = useActiveOrgSlug();
28
+ const pcAvailable = useResourceAvailable(ApiResourceKind.platform_client);
29
+
30
+ const [flow, setFlow] = useState<FlowState>({ phase: "idle" });
31
+ const listRefetchRef = useRef<(() => void) | null>(null);
32
+
33
+ const handleRefetchRef = useCallback((refetch: () => void) => {
34
+ listRefetchRef.current = refetch;
35
+ }, []);
36
+
37
+ const handleCreated = useCallback(
38
+ (response: PlatformClientCreateResponse) => {
39
+ listRefetchRef.current?.();
40
+ setFlow({
41
+ phase: "revealing",
42
+ clientId: response.platformClient?.spec?.clientId ?? "",
43
+ clientSecret: response.clientSecret,
44
+ context: "created",
45
+ });
46
+ },
47
+ [],
48
+ );
49
+
50
+ const handleSecretRotated = useCallback(
51
+ (response: PlatformClientCreateResponse) => {
52
+ listRefetchRef.current?.();
53
+ setFlow({
54
+ phase: "revealing",
55
+ clientId: response.platformClient?.spec?.clientId ?? "",
56
+ clientSecret: response.clientSecret,
57
+ context: "rotated",
58
+ });
59
+ },
60
+ [],
61
+ );
62
+
63
+ const handleUpdated = useCallback(() => {
64
+ listRefetchRef.current?.();
65
+ setFlow({ phase: "idle" });
66
+ }, []);
67
+
68
+ const handleDeleted = useCallback(() => {
69
+ listRefetchRef.current?.();
70
+ setFlow({ phase: "idle" });
71
+ }, []);
72
+
73
+ return (
74
+ <section aria-labelledby="platform-clients-heading">
75
+ <div className="mb-3 flex items-center justify-between">
76
+ <h2
77
+ id="platform-clients-heading"
78
+ className="text-foreground text-sm font-semibold"
79
+ >
80
+ Platform Clients
81
+ </h2>
82
+
83
+ {pcAvailable && org && flow.phase === "idle" && (
84
+ <button
85
+ type="button"
86
+ onClick={() => setFlow({ phase: "creating" })}
87
+ className="text-primary hover:text-foreground text-xs font-medium transition-colors"
88
+ >
89
+ + New platform client
90
+ </button>
91
+ )}
92
+ </div>
93
+ <p className="text-muted-foreground mb-4 text-xs">
94
+ Platform clients let your backend mint Stigmer user tokens so you
95
+ can embed Stigmer components in your application without requiring
96
+ OIDC federation.
97
+ </p>
98
+
99
+ {!pcAvailable ? (
100
+ <CloudFeatureNotice>
101
+ Platform clients are not available in local mode. Token minting
102
+ requires Stigmer Cloud.
103
+ </CloudFeatureNotice>
104
+ ) : flow.phase === "creating" ? (
105
+ <div className="border-border bg-card rounded-lg border p-4">
106
+ <CreatePlatformClientForm
107
+ org={org}
108
+ onCreated={handleCreated}
109
+ onCancel={() => setFlow({ phase: "idle" })}
110
+ />
111
+ </div>
112
+ ) : flow.phase === "revealing" ? (
113
+ <div className="space-y-4">
114
+ <PlatformClientSecretAlert
115
+ clientId={flow.clientId}
116
+ clientSecret={flow.clientSecret}
117
+ context={flow.context}
118
+ onDismiss={() => setFlow({ phase: "idle" })}
119
+ />
120
+ <PlatformClientListPanel
121
+ org={org}
122
+ onEdit={(pc) =>
123
+ setFlow({ phase: "editing", platformClient: pc })
124
+ }
125
+ onRefetchRef={handleRefetchRef}
126
+ />
127
+ </div>
128
+ ) : flow.phase === "editing" ? (
129
+ <div className="border-border bg-card rounded-lg border p-4">
130
+ <PlatformClientDetailPanel
131
+ platformClient={flow.platformClient}
132
+ onUpdated={handleUpdated}
133
+ onSecretRotated={handleSecretRotated}
134
+ onDeleted={handleDeleted}
135
+ onBack={() => setFlow({ phase: "idle" })}
136
+ />
137
+ </div>
138
+ ) : (
139
+ <PlatformClientListPanel
140
+ org={org}
141
+ onEdit={(pc) =>
142
+ setFlow({ phase: "editing", platformClient: pc })
143
+ }
144
+ onRefetchRef={handleRefetchRef}
145
+ />
146
+ )}
147
+ </section>
148
+ );
149
+ }
@@ -0,0 +1,41 @@
1
+ "use client";
2
+
3
+ import { OrgUsagePanel } from "../usage/OrgUsagePanel";
4
+ import { useDeploymentMode } from "../deployment-mode";
5
+ import { CloudFeatureNotice } from "../internal/CloudFeatureNotice";
6
+ import { useOrg } from "../organization/OrgProvider";
7
+
8
+ /** Settings section for organization usage and cost reporting. */
9
+ export function UsageSection() {
10
+ const { activeOrg } = useOrg();
11
+ const mode = useDeploymentMode();
12
+ const orgId = activeOrg?.metadata?.id ?? "";
13
+
14
+ return (
15
+ <section aria-labelledby="usage-heading">
16
+ <h2
17
+ id="usage-heading"
18
+ className="text-foreground mb-1 text-sm font-semibold"
19
+ >
20
+ Usage
21
+ </h2>
22
+ <p className="text-muted-foreground mb-4 text-xs">
23
+ Monitor token consumption, cost, and execution activity across
24
+ your organization.
25
+ </p>
26
+
27
+ {mode === "local" ? (
28
+ <CloudFeatureNotice>
29
+ Usage reports are not available in local mode. Connect to Stigmer
30
+ Cloud to view organization-level cost and token analytics.
31
+ </CloudFeatureNotice>
32
+ ) : !orgId ? (
33
+ <p className="text-muted-foreground py-4 text-center text-xs">
34
+ Select an organization to view usage.
35
+ </p>
36
+ ) : (
37
+ <OrgUsagePanel orgId={orgId} />
38
+ )}
39
+ </section>
40
+ );
41
+ }
@@ -0,0 +1,13 @@
1
+ export { SETTINGS_NAV_GROUPS } from "./settings-nav";
2
+ export type { SettingsNavItem, SettingsNavGroup } from "./settings-nav";
3
+
4
+ export { ApiKeysSection } from "./ApiKeysSection";
5
+ export { MembersSection } from "./MembersSection";
6
+ export { OrgProfileSection } from "./OrgProfileSection";
7
+ export { EnvironmentsSection } from "./EnvironmentsSection";
8
+ export { InvitationsSection } from "./InvitationsSection";
9
+ export { IdentityProvidersSection } from "./IdentityProvidersSection";
10
+ export type { IdentityProvidersSectionProps } from "./IdentityProvidersSection";
11
+ export { PlatformClientsSection } from "./PlatformClientsSection";
12
+ export { OAuthAppsSection } from "./OAuthAppsSection";
13
+ export { UsageSection } from "./UsageSection";
@@ -0,0 +1,78 @@
1
+ import type { ComponentType } from "react";
2
+ import {
3
+ AppWindow,
4
+ BarChart3,
5
+ Box,
6
+ Building2,
7
+ CreditCard,
8
+ KeyRound,
9
+ Link,
10
+ Plug,
11
+ ShieldCheck,
12
+ Users,
13
+ } from "lucide-react";
14
+
15
+ /** Single navigable entry in the settings sidebar. */
16
+ export interface SettingsNavItem {
17
+ /** Route for the settings page entry. */
18
+ readonly href: string;
19
+ /** Short, user-facing label shown in navigation. */
20
+ readonly label: string;
21
+ /** Lucide icon component rendered next to the label. */
22
+ readonly icon: ComponentType<{
23
+ /** Optional CSS class name applied to the icon. */
24
+ className?: string;
25
+ }>;
26
+ }
27
+
28
+ /** Grouping model used to render settings navigation sections. */
29
+ export interface SettingsNavGroup {
30
+ /** Group heading shown in the settings navigation. */
31
+ readonly label: string;
32
+ /** Helper text describing the group purpose. */
33
+ readonly description: string;
34
+ /** Entries that belong to this group. */
35
+ readonly items: readonly SettingsNavItem[];
36
+ }
37
+
38
+ /** Canonical settings sidebar/grouped navigation model. */
39
+ export const SETTINGS_NAV_GROUPS: readonly SettingsNavGroup[] = [
40
+ {
41
+ label: "Organization",
42
+ description:
43
+ "Manage your team, organization identity, and identity providers.",
44
+ items: [
45
+ { href: "/settings/org-profile", label: "Org Profile", icon: Building2 },
46
+ { href: "/settings/members", label: "Members", icon: Users },
47
+ { href: "/settings/invitations", label: "Invitations", icon: Link },
48
+ {
49
+ href: "/settings/identity-providers",
50
+ label: "Identity Providers",
51
+ icon: ShieldCheck,
52
+ },
53
+ ],
54
+ },
55
+ {
56
+ label: "Configuration",
57
+ description:
58
+ "API keys, environment variables, and OAuth app credentials for your integrations.",
59
+ items: [
60
+ { href: "/settings/api-keys", label: "API Keys", icon: KeyRound },
61
+ {
62
+ href: "/settings/platform-clients",
63
+ label: "Platform Clients",
64
+ icon: Plug,
65
+ },
66
+ { href: "/settings/environments", label: "Environments", icon: Box },
67
+ { href: "/settings/oauth-apps", label: "OAuth Apps", icon: AppWindow },
68
+ ],
69
+ },
70
+ {
71
+ label: "Billing & Usage",
72
+ description: "Subscription management and usage metrics.",
73
+ items: [
74
+ { href: "/settings/billing", label: "Billing", icon: CreditCard },
75
+ { href: "/settings/usage", label: "Usage", icon: BarChart3 },
76
+ ],
77
+ },
78
+ ];