@burdenoff/microfe-admin 2026.610.1 → 2026.624.1

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 (75) hide show
  1. package/dist/admin/components/AccessDenied.js +2 -2
  2. package/dist/admin/components/AccessDenied.js.map +1 -1
  3. package/dist/admin/components/ActivityFeed.js +4 -4
  4. package/dist/admin/components/ActivityFeed.js.map +1 -1
  5. package/dist/admin/components/Charts.js +4 -4
  6. package/dist/admin/components/Charts.js.map +1 -1
  7. package/dist/admin/components/ConfirmDialog.js +6 -6
  8. package/dist/admin/components/ConfirmDialog.js.map +1 -1
  9. package/dist/admin/components/DataTable.js +7 -7
  10. package/dist/admin/components/DataTable.js.map +1 -1
  11. package/dist/admin/components/FormSheet.js +7 -7
  12. package/dist/admin/components/FormSheet.js.map +1 -1
  13. package/dist/admin/components/MetricCard.js +4 -4
  14. package/dist/admin/components/MetricCard.js.map +1 -1
  15. package/dist/admin/components/PageLayout.js +3 -3
  16. package/dist/admin/components/PageLayout.js.map +1 -1
  17. package/dist/admin/components/PermissionGuard.js +1 -1
  18. package/dist/admin/components/PermissionGuard.js.map +1 -1
  19. package/dist/admin/components/RowActions.js +2 -2
  20. package/dist/admin/components/RowActions.js.map +1 -1
  21. package/dist/admin/components/TenantMembersPanel.js +15 -15
  22. package/dist/admin/components/TenantMembersPanel.js.map +1 -1
  23. package/dist/admin/components/TenantSsoPanel.js +14 -14
  24. package/dist/admin/components/TenantSsoPanel.js.map +1 -1
  25. package/dist/admin/components/Toast.js +9 -9
  26. package/dist/admin/components/Toast.js.map +1 -1
  27. package/dist/admin/components/forms/SubscriptionForm.js +10 -10
  28. package/dist/admin/components/forms/SubscriptionForm.js.map +1 -1
  29. package/dist/admin/components/forms/TenantForm.js +8 -8
  30. package/dist/admin/components/forms/TenantForm.js.map +1 -1
  31. package/dist/admin/components/forms/TicketForm.js +2 -2
  32. package/dist/admin/components/forms/TicketForm.js.map +1 -1
  33. package/dist/admin/components/forms/UserForm.js +8 -8
  34. package/dist/admin/components/forms/UserForm.js.map +1 -1
  35. package/dist/admin/components/ui.js +16 -16
  36. package/dist/admin/components/ui.js.map +1 -1
  37. package/dist/admin/constants/index.js +8 -8
  38. package/dist/admin/constants/index.js.map +1 -1
  39. package/dist/admin/pages/AuditLogsPage.js +8 -8
  40. package/dist/admin/pages/AuditLogsPage.js.map +1 -1
  41. package/dist/admin/pages/CommunicationsPage.js +2 -2
  42. package/dist/admin/pages/CommunicationsPage.js.map +1 -1
  43. package/dist/admin/pages/FeatureFlagsPage.js +4 -4
  44. package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
  45. package/dist/admin/pages/OrganizationsListPage.js +6 -6
  46. package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
  47. package/dist/admin/pages/ProductsPage.js +7 -7
  48. package/dist/admin/pages/ProductsPage.js.map +1 -1
  49. package/dist/admin/pages/ReferralsPage.js +23 -23
  50. package/dist/admin/pages/ReferralsPage.js.map +1 -1
  51. package/dist/admin/pages/RolesPermissionsPage.js +8 -8
  52. package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
  53. package/dist/admin/pages/SREPage.js +8 -8
  54. package/dist/admin/pages/SREPage.js.map +1 -1
  55. package/dist/admin/pages/SandboxSettingsPage.js +20 -20
  56. package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
  57. package/dist/admin/pages/SettingsPage.js +20 -20
  58. package/dist/admin/pages/SettingsPage.js.map +1 -1
  59. package/dist/admin/pages/SyncFailuresPage.js +11 -11
  60. package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
  61. package/dist/admin/pages/TenantDetailPage.js +22 -22
  62. package/dist/admin/pages/TenantDetailPage.js.map +1 -1
  63. package/dist/admin/pages/TenantFormRoutePage.js +2 -2
  64. package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
  65. package/dist/admin/pages/TenantsListPage.js +5 -5
  66. package/dist/admin/pages/TenantsListPage.js.map +1 -1
  67. package/dist/admin/pages/UsersListPage.js +6 -6
  68. package/dist/admin/pages/UsersListPage.js.map +1 -1
  69. package/dist/admin/pages/WaitlistPage.js +7 -7
  70. package/dist/admin/pages/WaitlistPage.js.map +1 -1
  71. package/dist/admin/pages/WorkflowsPage.js +3 -3
  72. package/dist/admin/pages/WorkflowsPage.js.map +1 -1
  73. package/dist/admin/pages/WorkspacesListPage.js +8 -8
  74. package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
  75. package/package.json +1 -1
@@ -170,9 +170,9 @@ var k = [
170
170
  cache: /* @__PURE__ */ D(S, { className: "h-4 w-4" }),
171
171
  queue: /* @__PURE__ */ D(E, { className: "h-4 w-4" })
172
172
  }, N = {
173
- critical: "bg-destructive/10 text-destructive",
173
+ critical: "bg-status-error-bg/10 text-status-error-text",
174
174
  major: "bg-warning/10 text-warning",
175
- minor: "bg-muted text-muted-foreground"
175
+ minor: "bg-bg-sunken text-text-secondary"
176
176
  };
177
177
  function P() {
178
178
  let { canViewSRE: b, isLoading: x } = e(), [S, E] = g(!1), P = k.filter((e) => e.status === "healthy").length, F = k.filter((e) => e.status === "degraded").length;
@@ -234,7 +234,7 @@ function P() {
234
234
  }) }), /* @__PURE__ */ D(i, { children: /* @__PURE__ */ D("div", {
235
235
  className: "space-y-3",
236
236
  children: A.filter((e) => e.status !== "resolved").map((e) => /* @__PURE__ */ O("div", {
237
- className: "flex items-start justify-between rounded-lg border border-border bg-background p-4",
237
+ className: "flex items-start justify-between rounded-lg border border-border-subtle bg-bg-surface p-4",
238
238
  children: [/* @__PURE__ */ O("div", {
239
239
  className: "space-y-1",
240
240
  children: [
@@ -249,7 +249,7 @@ function P() {
249
249
  })]
250
250
  }),
251
251
  /* @__PURE__ */ O("div", {
252
- className: "flex items-center gap-4 text-sm text-muted-foreground",
252
+ className: "flex items-center gap-4 text-sm text-text-secondary",
253
253
  children: [
254
254
  /* @__PURE__ */ O("span", { children: ["Started ", d(e.startedAt)] }),
255
255
  /* @__PURE__ */ D("span", { children: "•" }),
@@ -321,7 +321,7 @@ function P() {
321
321
  width: "100px",
322
322
  align: "right",
323
323
  render: (e) => /* @__PURE__ */ O("span", {
324
- className: e.errorRate > 1 ? "text-destructive" : "",
324
+ className: e.errorRate > 1 ? "text-status-error-text" : "",
325
325
  children: [e.errorRate.toFixed(2), "%"]
326
326
  })
327
327
  },
@@ -330,7 +330,7 @@ function P() {
330
330
  header: "Region",
331
331
  width: "120px",
332
332
  render: (e) => /* @__PURE__ */ D("span", {
333
- className: "text-muted-foreground",
333
+ className: "text-text-secondary",
334
334
  children: e.region
335
335
  })
336
336
  }
@@ -360,7 +360,7 @@ function P() {
360
360
  header: "Version",
361
361
  width: "140px",
362
362
  render: (e) => /* @__PURE__ */ D("code", {
363
- className: "text-xs bg-muted px-1.5 py-0.5 rounded",
363
+ className: "text-xs bg-bg-sunken px-1.5 py-0.5 rounded",
364
364
  children: e.version
365
365
  })
366
366
  },
@@ -382,7 +382,7 @@ function P() {
382
382
  header: "Deployed",
383
383
  width: "140px",
384
384
  render: (e) => /* @__PURE__ */ D("span", {
385
- className: "text-muted-foreground",
385
+ className: "text-text-secondary",
386
386
  children: d(e.deployedAt)
387
387
  })
388
388
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SREPage.js","names":[],"sources":["../../../src/admin/pages/SREPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Activity,\n Server,\n Database,\n Cpu,\n HardDrive,\n Wifi,\n AlertTriangle,\n CheckCircle2,\n RefreshCw,\n TrendingUp,\n} from 'lucide-react';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport {\n PageLayout,\n MetricCard,\n MetricsGrid,\n Card,\n CardHeader,\n CardTitle,\n CardContent,\n Badge,\n Button,\n EmptyState,\n DataTable,\n HealthStatusBadge,\n DeploymentStatusBadge,\n PermissionGuard,\n} from '../components';\nimport { formatRelativeTime } from '../utils/formatters';\n\n/**\n * Service health entity\n */\ninterface ServiceHealth {\n id: string;\n name: string;\n type: 'api' | 'worker' | 'database' | 'cache' | 'queue';\n status: 'healthy' | 'degraded' | 'down';\n uptime: number; // percentage\n latency: number; // ms\n errorRate: number; // percentage\n lastChecked: string;\n region: string;\n}\n\n/**\n * Incident entity\n */\ninterface Incident {\n id: string;\n title: string;\n severity: 'critical' | 'major' | 'minor';\n status: 'active' | 'investigating' | 'resolved';\n affectedServices: string[];\n startedAt: string;\n resolvedAt?: string;\n}\n\n/**\n * Deployment entity\n */\ninterface Deployment {\n id: string;\n service: string;\n version: string;\n status: 'success' | 'failed' | 'in_progress' | 'rolled_back';\n deployedAt: string;\n deployedBy: string;\n duration: number; // seconds\n}\n\n// Mock data\nconst mockServices: ServiceHealth[] = [\n {\n id: 's1',\n name: 'wspace-public-gateway',\n type: 'api',\n status: 'healthy',\n uptime: 99.99,\n latency: 45,\n errorRate: 0.01,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's2',\n name: 'global-auth-svc',\n type: 'api',\n status: 'healthy',\n uptime: 99.95,\n latency: 32,\n errorRate: 0.02,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's3',\n name: 'wspace-tags-svc',\n type: 'api',\n status: 'healthy',\n uptime: 99.98,\n latency: 28,\n errorRate: 0.01,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's4',\n name: 'global-billing-svc',\n type: 'api',\n status: 'degraded',\n uptime: 98.5,\n latency: 250,\n errorRate: 2.5,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's5',\n name: 'wspace-calendar-svc',\n type: 'api',\n status: 'healthy',\n uptime: 99.97,\n latency: 35,\n errorRate: 0.03,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's6',\n name: 'postgresql-primary',\n type: 'database',\n status: 'healthy',\n uptime: 99.999,\n latency: 5,\n errorRate: 0,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's7',\n name: 'valkey-cluster',\n type: 'cache',\n status: 'healthy',\n uptime: 99.99,\n latency: 2,\n errorRate: 0,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's8',\n name: 'nats-cluster',\n type: 'queue',\n status: 'healthy',\n uptime: 99.98,\n latency: 8,\n errorRate: 0.01,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's9',\n name: 'temporal-worker',\n type: 'worker',\n status: 'healthy',\n uptime: 99.95,\n latency: 15,\n errorRate: 0.05,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n];\n\nconst mockIncidents: Incident[] = [\n {\n id: 'inc-1',\n title: 'Elevated latency on billing service',\n severity: 'major',\n status: 'investigating',\n affectedServices: ['global-billing-svc'],\n startedAt: new Date(Date.now() - 30 * 60 * 1000).toISOString(),\n },\n {\n id: 'inc-2',\n title: 'Intermittent database connection errors',\n severity: 'minor',\n status: 'resolved',\n affectedServices: ['postgresql-primary'],\n startedAt: new Date(Date.now() - 4 * 60 * 60 * 1000).toISOString(),\n resolvedAt: new Date(Date.now() - 3 * 60 * 60 * 1000).toISOString(),\n },\n];\n\nconst mockDeployments: Deployment[] = [\n {\n id: 'd1',\n service: 'wspace-tags-svc',\n version: '2025.01.18.1',\n status: 'success',\n deployedAt: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(),\n deployedBy: 'fluxcd',\n duration: 120,\n },\n {\n id: 'd2',\n service: 'global-auth-svc',\n version: '2025.01.18.0',\n status: 'success',\n deployedAt: new Date(Date.now() - 5 * 60 * 60 * 1000).toISOString(),\n deployedBy: 'fluxcd',\n duration: 95,\n },\n {\n id: 'd3',\n service: 'wspace-calendar-svc',\n version: '2025.01.17.2',\n status: 'rolled_back',\n deployedAt: new Date(Date.now() - 12 * 60 * 60 * 1000).toISOString(),\n deployedBy: 'fluxcd',\n duration: 180,\n },\n {\n id: 'd4',\n service: 'global-billing-svc',\n version: '2025.01.17.1',\n status: 'success',\n deployedAt: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(),\n deployedBy: 'fluxcd',\n duration: 110,\n },\n];\n\nconst typeIcons: Record<string, React.ReactNode> = {\n api: <Server className=\"h-4 w-4\" />,\n worker: <Cpu className=\"h-4 w-4\" />,\n database: <Database className=\"h-4 w-4\" />,\n cache: <HardDrive className=\"h-4 w-4\" />,\n queue: <Wifi className=\"h-4 w-4\" />,\n};\n\nconst severityColors: Record<string, string> = {\n critical: 'bg-destructive/10 text-destructive',\n major: 'bg-warning/10 text-warning',\n minor: 'bg-muted text-muted-foreground',\n};\n\n/**\n * SRE Monitoring page\n */\nexport function SREPage() {\n const { canViewSRE, isLoading: permissionsLoading } = useAdminPermissions();\n\n const [refreshing, setRefreshing] = useState(false);\n\n // Calculate metrics\n const healthyServices = mockServices.filter((s) => s.status === 'healthy').length;\n const degradedServices = mockServices.filter((s) => s.status === 'degraded').length;\n // Note: downServices available for future use when we have down services\n const _downServices = mockServices.filter((s) => s.status === 'down').length;\n void _downServices;\n const avgUptime = mockServices.reduce((sum, s) => sum + s.uptime, 0) / mockServices.length;\n const avgLatency = mockServices.reduce((sum, s) => sum + s.latency, 0) / mockServices.length;\n const activeIncidents = mockIncidents.filter((i) => i.status !== 'resolved').length;\n\n const handleRefresh = () => {\n setRefreshing(true);\n setTimeout(() => setRefreshing(false), 1000);\n };\n\n const serviceColumns = [\n {\n key: 'name',\n header: 'Service',\n render: (service: ServiceHealth) => (\n <div className=\"flex items-center gap-2\">\n {typeIcons[service.type]}\n <span className=\"font-medium\">{service.name}</span>\n </div>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n width: '100px',\n render: (service: ServiceHealth) => <HealthStatusBadge status={service.status} />,\n },\n {\n key: 'uptime',\n header: 'Uptime',\n width: '100px',\n align: 'right' as const,\n render: (service: ServiceHealth) => (\n <span className={service.uptime < 99 ? 'text-warning' : 'text-success'}>\n {service.uptime.toFixed(2)}%\n </span>\n ),\n },\n {\n key: 'latency',\n header: 'Latency',\n width: '100px',\n align: 'right' as const,\n render: (service: ServiceHealth) => (\n <span className={service.latency > 100 ? 'text-warning' : ''}>{service.latency}ms</span>\n ),\n },\n {\n key: 'errorRate',\n header: 'Error Rate',\n width: '100px',\n align: 'right' as const,\n render: (service: ServiceHealth) => (\n <span className={service.errorRate > 1 ? 'text-destructive' : ''}>\n {service.errorRate.toFixed(2)}%\n </span>\n ),\n },\n {\n key: 'region',\n header: 'Region',\n width: '120px',\n render: (service: ServiceHealth) => (\n <span className=\"text-muted-foreground\">{service.region}</span>\n ),\n },\n ];\n\n const deploymentColumns = [\n {\n key: 'service',\n header: 'Service',\n render: (d: Deployment) => <span className=\"font-medium\">{d.service}</span>,\n },\n {\n key: 'version',\n header: 'Version',\n width: '140px',\n render: (d: Deployment) => (\n <code className=\"text-xs bg-muted px-1.5 py-0.5 rounded\">{d.version}</code>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n width: '120px',\n render: (d: Deployment) => <DeploymentStatusBadge status={d.status} />,\n },\n {\n key: 'duration',\n header: 'Duration',\n width: '100px',\n align: 'right' as const,\n render: (d: Deployment) => `${d.duration}s`,\n },\n {\n key: 'deployedAt',\n header: 'Deployed',\n width: '140px',\n render: (d: Deployment) => (\n <span className=\"text-muted-foreground\">{formatRelativeTime(d.deployedAt)}</span>\n ),\n },\n ];\n\n return (\n <PermissionGuard\n allowed={canViewSRE}\n isLoading={permissionsLoading}\n pageTitle=\"SRE Monitoring\"\n denyMessage=\"You don't have permission to view SRE monitoring.\"\n >\n <PageLayout\n title=\"SRE Monitoring\"\n description=\"Service health, incidents, and deployment monitoring\"\n actions={\n <Button variant=\"outline\" onClick={handleRefresh} disabled={refreshing}>\n <RefreshCw className={`mr-2 h-4 w-4 ${refreshing ? 'animate-spin' : ''}`} />\n Refresh\n </Button>\n }\n >\n <div className=\"space-y-6\">\n {/* Metrics */}\n <MetricsGrid>\n <MetricCard\n title=\"Healthy Services\"\n value={healthyServices}\n icon={<CheckCircle2 className=\"h-5 w-5 text-success\" />}\n subtitle={`of ${mockServices.length} total`}\n />\n <MetricCard\n title=\"Degraded\"\n value={degradedServices}\n icon={<AlertTriangle className=\"h-5 w-5 text-warning\" />}\n priority={degradedServices > 0 ? 'high' : 'normal'}\n />\n <MetricCard\n title=\"Avg Uptime\"\n value={`${avgUptime.toFixed(2)}%`}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n />\n <MetricCard\n title=\"Avg Latency\"\n value={`${avgLatency.toFixed(0)}ms`}\n icon={<Activity className=\"h-5 w-5\" />}\n />\n </MetricsGrid>\n\n {/* Active Incidents */}\n {activeIncidents > 0 && (\n <Card className=\"border-warning/50 bg-warning/5\">\n <CardHeader>\n <CardTitle className=\"text-lg flex items-center gap-2 text-warning\">\n <AlertTriangle className=\"h-5 w-5\" />\n Active Incidents ({activeIncidents})\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {mockIncidents\n .filter((i) => i.status !== 'resolved')\n .map((incident) => (\n <div\n key={incident.id}\n className=\"flex items-start justify-between rounded-lg border border-border bg-background p-4\"\n >\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <Badge className={severityColors[incident.severity]}>\n {incident.severity.toUpperCase()}\n </Badge>\n <span className=\"font-medium\">{incident.title}</span>\n </div>\n <div className=\"flex items-center gap-4 text-sm text-muted-foreground\">\n <span>Started {formatRelativeTime(incident.startedAt)}</span>\n <span>•</span>\n <span>Status: {incident.status}</span>\n </div>\n <div className=\"flex flex-wrap gap-1 mt-2\">\n {incident.affectedServices.map((svc) => (\n <Badge key={svc} variant=\"outline\" className=\"text-xs\">\n {svc}\n </Badge>\n ))}\n </div>\n </div>\n <Button size=\"sm\" variant=\"outline\">\n View\n </Button>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Service Health */}\n <Card>\n <CardHeader>\n <CardTitle className=\"text-lg flex items-center gap-2\">\n <Server className=\"h-5 w-5\" />\n Service Health\n </CardTitle>\n </CardHeader>\n <CardContent>\n <DataTable\n columns={serviceColumns}\n data={mockServices}\n keyExtractor={(s) => s.id}\n emptyState={\n <EmptyState icon={<Server className=\"h-8 w-8\" />} title=\"No services found\" />\n }\n />\n </CardContent>\n </Card>\n\n {/* Recent Deployments */}\n <Card>\n <CardHeader>\n <CardTitle className=\"text-lg flex items-center gap-2\">\n <RefreshCw className=\"h-5 w-5\" />\n Recent Deployments\n </CardTitle>\n </CardHeader>\n <CardContent>\n <DataTable\n columns={deploymentColumns}\n data={mockDeployments}\n keyExtractor={(d) => d.id}\n emptyState={\n <EmptyState\n icon={<RefreshCw className=\"h-8 w-8\" />}\n title=\"No recent deployments\"\n />\n }\n />\n </CardContent>\n </Card>\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SREPage.js","names":[],"sources":["../../../src/admin/pages/SREPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Activity,\n Server,\n Database,\n Cpu,\n HardDrive,\n Wifi,\n AlertTriangle,\n CheckCircle2,\n RefreshCw,\n TrendingUp,\n} from 'lucide-react';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport {\n PageLayout,\n MetricCard,\n MetricsGrid,\n Card,\n CardHeader,\n CardTitle,\n CardContent,\n Badge,\n Button,\n EmptyState,\n DataTable,\n HealthStatusBadge,\n DeploymentStatusBadge,\n PermissionGuard,\n} from '../components';\nimport { formatRelativeTime } from '../utils/formatters';\n\n/**\n * Service health entity\n */\ninterface ServiceHealth {\n id: string;\n name: string;\n type: 'api' | 'worker' | 'database' | 'cache' | 'queue';\n status: 'healthy' | 'degraded' | 'down';\n uptime: number; // percentage\n latency: number; // ms\n errorRate: number; // percentage\n lastChecked: string;\n region: string;\n}\n\n/**\n * Incident entity\n */\ninterface Incident {\n id: string;\n title: string;\n severity: 'critical' | 'major' | 'minor';\n status: 'active' | 'investigating' | 'resolved';\n affectedServices: string[];\n startedAt: string;\n resolvedAt?: string;\n}\n\n/**\n * Deployment entity\n */\ninterface Deployment {\n id: string;\n service: string;\n version: string;\n status: 'success' | 'failed' | 'in_progress' | 'rolled_back';\n deployedAt: string;\n deployedBy: string;\n duration: number; // seconds\n}\n\n// Mock data\nconst mockServices: ServiceHealth[] = [\n {\n id: 's1',\n name: 'wspace-public-gateway',\n type: 'api',\n status: 'healthy',\n uptime: 99.99,\n latency: 45,\n errorRate: 0.01,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's2',\n name: 'global-auth-svc',\n type: 'api',\n status: 'healthy',\n uptime: 99.95,\n latency: 32,\n errorRate: 0.02,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's3',\n name: 'wspace-tags-svc',\n type: 'api',\n status: 'healthy',\n uptime: 99.98,\n latency: 28,\n errorRate: 0.01,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's4',\n name: 'global-billing-svc',\n type: 'api',\n status: 'degraded',\n uptime: 98.5,\n latency: 250,\n errorRate: 2.5,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's5',\n name: 'wspace-calendar-svc',\n type: 'api',\n status: 'healthy',\n uptime: 99.97,\n latency: 35,\n errorRate: 0.03,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's6',\n name: 'postgresql-primary',\n type: 'database',\n status: 'healthy',\n uptime: 99.999,\n latency: 5,\n errorRate: 0,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's7',\n name: 'valkey-cluster',\n type: 'cache',\n status: 'healthy',\n uptime: 99.99,\n latency: 2,\n errorRate: 0,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's8',\n name: 'nats-cluster',\n type: 'queue',\n status: 'healthy',\n uptime: 99.98,\n latency: 8,\n errorRate: 0.01,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n {\n id: 's9',\n name: 'temporal-worker',\n type: 'worker',\n status: 'healthy',\n uptime: 99.95,\n latency: 15,\n errorRate: 0.05,\n lastChecked: new Date().toISOString(),\n region: 'ap-south-1',\n },\n];\n\nconst mockIncidents: Incident[] = [\n {\n id: 'inc-1',\n title: 'Elevated latency on billing service',\n severity: 'major',\n status: 'investigating',\n affectedServices: ['global-billing-svc'],\n startedAt: new Date(Date.now() - 30 * 60 * 1000).toISOString(),\n },\n {\n id: 'inc-2',\n title: 'Intermittent database connection errors',\n severity: 'minor',\n status: 'resolved',\n affectedServices: ['postgresql-primary'],\n startedAt: new Date(Date.now() - 4 * 60 * 60 * 1000).toISOString(),\n resolvedAt: new Date(Date.now() - 3 * 60 * 60 * 1000).toISOString(),\n },\n];\n\nconst mockDeployments: Deployment[] = [\n {\n id: 'd1',\n service: 'wspace-tags-svc',\n version: '2025.01.18.1',\n status: 'success',\n deployedAt: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(),\n deployedBy: 'fluxcd',\n duration: 120,\n },\n {\n id: 'd2',\n service: 'global-auth-svc',\n version: '2025.01.18.0',\n status: 'success',\n deployedAt: new Date(Date.now() - 5 * 60 * 60 * 1000).toISOString(),\n deployedBy: 'fluxcd',\n duration: 95,\n },\n {\n id: 'd3',\n service: 'wspace-calendar-svc',\n version: '2025.01.17.2',\n status: 'rolled_back',\n deployedAt: new Date(Date.now() - 12 * 60 * 60 * 1000).toISOString(),\n deployedBy: 'fluxcd',\n duration: 180,\n },\n {\n id: 'd4',\n service: 'global-billing-svc',\n version: '2025.01.17.1',\n status: 'success',\n deployedAt: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(),\n deployedBy: 'fluxcd',\n duration: 110,\n },\n];\n\nconst typeIcons: Record<string, React.ReactNode> = {\n api: <Server className=\"h-4 w-4\" />,\n worker: <Cpu className=\"h-4 w-4\" />,\n database: <Database className=\"h-4 w-4\" />,\n cache: <HardDrive className=\"h-4 w-4\" />,\n queue: <Wifi className=\"h-4 w-4\" />,\n};\n\nconst severityColors: Record<string, string> = {\n critical: 'bg-status-error-bg/10 text-status-error-text',\n major: 'bg-warning/10 text-warning',\n minor: 'bg-bg-sunken text-text-secondary',\n};\n\n/**\n * SRE Monitoring page\n */\nexport function SREPage() {\n const { canViewSRE, isLoading: permissionsLoading } = useAdminPermissions();\n\n const [refreshing, setRefreshing] = useState(false);\n\n // Calculate metrics\n const healthyServices = mockServices.filter((s) => s.status === 'healthy').length;\n const degradedServices = mockServices.filter((s) => s.status === 'degraded').length;\n // Note: downServices available for future use when we have down services\n const _downServices = mockServices.filter((s) => s.status === 'down').length;\n void _downServices;\n const avgUptime = mockServices.reduce((sum, s) => sum + s.uptime, 0) / mockServices.length;\n const avgLatency = mockServices.reduce((sum, s) => sum + s.latency, 0) / mockServices.length;\n const activeIncidents = mockIncidents.filter((i) => i.status !== 'resolved').length;\n\n const handleRefresh = () => {\n setRefreshing(true);\n setTimeout(() => setRefreshing(false), 1000);\n };\n\n const serviceColumns = [\n {\n key: 'name',\n header: 'Service',\n render: (service: ServiceHealth) => (\n <div className=\"flex items-center gap-2\">\n {typeIcons[service.type]}\n <span className=\"font-medium\">{service.name}</span>\n </div>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n width: '100px',\n render: (service: ServiceHealth) => <HealthStatusBadge status={service.status} />,\n },\n {\n key: 'uptime',\n header: 'Uptime',\n width: '100px',\n align: 'right' as const,\n render: (service: ServiceHealth) => (\n <span className={service.uptime < 99 ? 'text-warning' : 'text-success'}>\n {service.uptime.toFixed(2)}%\n </span>\n ),\n },\n {\n key: 'latency',\n header: 'Latency',\n width: '100px',\n align: 'right' as const,\n render: (service: ServiceHealth) => (\n <span className={service.latency > 100 ? 'text-warning' : ''}>{service.latency}ms</span>\n ),\n },\n {\n key: 'errorRate',\n header: 'Error Rate',\n width: '100px',\n align: 'right' as const,\n render: (service: ServiceHealth) => (\n <span className={service.errorRate > 1 ? 'text-status-error-text' : ''}>\n {service.errorRate.toFixed(2)}%\n </span>\n ),\n },\n {\n key: 'region',\n header: 'Region',\n width: '120px',\n render: (service: ServiceHealth) => (\n <span className=\"text-text-secondary\">{service.region}</span>\n ),\n },\n ];\n\n const deploymentColumns = [\n {\n key: 'service',\n header: 'Service',\n render: (d: Deployment) => <span className=\"font-medium\">{d.service}</span>,\n },\n {\n key: 'version',\n header: 'Version',\n width: '140px',\n render: (d: Deployment) => (\n <code className=\"text-xs bg-bg-sunken px-1.5 py-0.5 rounded\">{d.version}</code>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n width: '120px',\n render: (d: Deployment) => <DeploymentStatusBadge status={d.status} />,\n },\n {\n key: 'duration',\n header: 'Duration',\n width: '100px',\n align: 'right' as const,\n render: (d: Deployment) => `${d.duration}s`,\n },\n {\n key: 'deployedAt',\n header: 'Deployed',\n width: '140px',\n render: (d: Deployment) => (\n <span className=\"text-text-secondary\">{formatRelativeTime(d.deployedAt)}</span>\n ),\n },\n ];\n\n return (\n <PermissionGuard\n allowed={canViewSRE}\n isLoading={permissionsLoading}\n pageTitle=\"SRE Monitoring\"\n denyMessage=\"You don't have permission to view SRE monitoring.\"\n >\n <PageLayout\n title=\"SRE Monitoring\"\n description=\"Service health, incidents, and deployment monitoring\"\n actions={\n <Button variant=\"outline\" onClick={handleRefresh} disabled={refreshing}>\n <RefreshCw className={`mr-2 h-4 w-4 ${refreshing ? 'animate-spin' : ''}`} />\n Refresh\n </Button>\n }\n >\n <div className=\"space-y-6\">\n {/* Metrics */}\n <MetricsGrid>\n <MetricCard\n title=\"Healthy Services\"\n value={healthyServices}\n icon={<CheckCircle2 className=\"h-5 w-5 text-success\" />}\n subtitle={`of ${mockServices.length} total`}\n />\n <MetricCard\n title=\"Degraded\"\n value={degradedServices}\n icon={<AlertTriangle className=\"h-5 w-5 text-warning\" />}\n priority={degradedServices > 0 ? 'high' : 'normal'}\n />\n <MetricCard\n title=\"Avg Uptime\"\n value={`${avgUptime.toFixed(2)}%`}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n />\n <MetricCard\n title=\"Avg Latency\"\n value={`${avgLatency.toFixed(0)}ms`}\n icon={<Activity className=\"h-5 w-5\" />}\n />\n </MetricsGrid>\n\n {/* Active Incidents */}\n {activeIncidents > 0 && (\n <Card className=\"border-warning/50 bg-warning/5\">\n <CardHeader>\n <CardTitle className=\"text-lg flex items-center gap-2 text-warning\">\n <AlertTriangle className=\"h-5 w-5\" />\n Active Incidents ({activeIncidents})\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {mockIncidents\n .filter((i) => i.status !== 'resolved')\n .map((incident) => (\n <div\n key={incident.id}\n className=\"flex items-start justify-between rounded-lg border border-border-subtle bg-bg-surface p-4\"\n >\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <Badge className={severityColors[incident.severity]}>\n {incident.severity.toUpperCase()}\n </Badge>\n <span className=\"font-medium\">{incident.title}</span>\n </div>\n <div className=\"flex items-center gap-4 text-sm text-text-secondary\">\n <span>Started {formatRelativeTime(incident.startedAt)}</span>\n <span>•</span>\n <span>Status: {incident.status}</span>\n </div>\n <div className=\"flex flex-wrap gap-1 mt-2\">\n {incident.affectedServices.map((svc) => (\n <Badge key={svc} variant=\"outline\" className=\"text-xs\">\n {svc}\n </Badge>\n ))}\n </div>\n </div>\n <Button size=\"sm\" variant=\"outline\">\n View\n </Button>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Service Health */}\n <Card>\n <CardHeader>\n <CardTitle className=\"text-lg flex items-center gap-2\">\n <Server className=\"h-5 w-5\" />\n Service Health\n </CardTitle>\n </CardHeader>\n <CardContent>\n <DataTable\n columns={serviceColumns}\n data={mockServices}\n keyExtractor={(s) => s.id}\n emptyState={\n <EmptyState icon={<Server className=\"h-8 w-8\" />} title=\"No services found\" />\n }\n />\n </CardContent>\n </Card>\n\n {/* Recent Deployments */}\n <Card>\n <CardHeader>\n <CardTitle className=\"text-lg flex items-center gap-2\">\n <RefreshCw className=\"h-5 w-5\" />\n Recent Deployments\n </CardTitle>\n </CardHeader>\n <CardContent>\n <DataTable\n columns={deploymentColumns}\n data={mockDeployments}\n keyExtractor={(d) => d.id}\n emptyState={\n <EmptyState\n icon={<RefreshCw className=\"h-8 w-8\" />}\n title=\"No recent deployments\"\n />\n }\n />\n </CardContent>\n </Card>\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
@@ -161,7 +161,7 @@ function j() {
161
161
  L,
162
162
  R,
163
163
  z
164
- ]), Y = "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm";
164
+ ]), Y = "h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm";
165
165
  return /* @__PURE__ */ T(m, {
166
166
  allowed: j,
167
167
  isLoading: P,
@@ -174,7 +174,7 @@ function j() {
174
174
  className: "mb-6",
175
175
  children: [/* @__PURE__ */ E(s, { children: [/* @__PURE__ */ E("div", {
176
176
  className: "flex items-center gap-2",
177
- children: [/* @__PURE__ */ T(v, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ T(c, {
177
+ children: [/* @__PURE__ */ T(v, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ T(c, {
178
178
  className: "text-lg",
179
179
  children: "Sandbox Stats"
180
180
  })]
@@ -205,7 +205,7 @@ function j() {
205
205
  children: [
206
206
  /* @__PURE__ */ E(i, { children: [/* @__PURE__ */ E(s, { children: [/* @__PURE__ */ E("div", {
207
207
  className: "flex items-center gap-2",
208
- children: [/* @__PURE__ */ T(C, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ T(c, {
208
+ children: [/* @__PURE__ */ T(C, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ T(c, {
209
209
  className: "text-lg",
210
210
  children: "Time-to-Live (TTL)"
211
211
  })]
@@ -230,7 +230,7 @@ function j() {
230
230
  }, e.value))
231
231
  }),
232
232
  /* @__PURE__ */ T("p", {
233
- className: "text-xs text-muted-foreground",
233
+ className: "text-xs text-text-secondary",
234
234
  children: "Default lifetime for new sandboxes"
235
235
  })
236
236
  ]
@@ -251,7 +251,7 @@ function j() {
251
251
  }, e.value))
252
252
  }),
253
253
  /* @__PURE__ */ T("p", {
254
- className: "text-xs text-muted-foreground",
254
+ className: "text-xs text-text-secondary",
255
255
  children: "Maximum allowed TTL (must be >= default)"
256
256
  })
257
257
  ]
@@ -260,31 +260,31 @@ function j() {
260
260
  })] }),
261
261
  /* @__PURE__ */ E(i, { children: [/* @__PURE__ */ E(s, { children: [/* @__PURE__ */ E("div", {
262
262
  className: "flex items-center gap-2",
263
- children: [/* @__PURE__ */ T(y, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ T(c, {
263
+ children: [/* @__PURE__ */ T(y, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ T(c, {
264
264
  className: "text-lg",
265
265
  children: "Allowed Sandbox Types"
266
266
  })]
267
267
  }), /* @__PURE__ */ T(o, { children: "Select which sandbox types users are permitted to create" })] }), /* @__PURE__ */ T(a, { children: /* @__PURE__ */ T("div", {
268
268
  className: "space-y-3",
269
269
  children: D.map((e) => /* @__PURE__ */ E("label", {
270
- className: "flex items-center gap-3 rounded-lg border border-border p-4 cursor-pointer hover:bg-muted/50 transition-colors",
270
+ className: "flex items-center gap-3 rounded-lg border border-border-subtle p-4 cursor-pointer hover:bg-bg-sunken/50 transition-colors",
271
271
  children: [/* @__PURE__ */ T("input", {
272
272
  type: "checkbox",
273
273
  checked: B.allowedSandboxTypes.includes(e),
274
274
  onChange: () => q(e),
275
- className: "h-4 w-4 rounded border-input text-primary focus:ring-primary"
275
+ className: "h-4 w-4 rounded border-border-default text-primary focus:ring-primary"
276
276
  }), /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("div", {
277
277
  className: "font-medium text-sm",
278
278
  children: M(e)
279
279
  }), /* @__PURE__ */ T("div", {
280
- className: "text-xs text-muted-foreground",
280
+ className: "text-xs text-text-secondary",
281
281
  children: N(e)
282
282
  })] })]
283
283
  }, e))
284
284
  }) })] }),
285
285
  /* @__PURE__ */ E(i, { children: [/* @__PURE__ */ E(s, { children: [/* @__PURE__ */ E("div", {
286
286
  className: "flex items-center gap-2",
287
- children: [/* @__PURE__ */ T(b, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ T(c, {
287
+ children: [/* @__PURE__ */ T(b, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ T(c, {
288
288
  className: "text-lg",
289
289
  children: "Default Resource Limits"
290
290
  })]
@@ -306,7 +306,7 @@ function j() {
306
306
  placeholder: "500m"
307
307
  }),
308
308
  /* @__PURE__ */ T("p", {
309
- className: "text-xs text-muted-foreground",
309
+ className: "text-xs text-text-secondary",
310
310
  children: "Kubernetes CPU request (e.g. 500m, 1)"
311
311
  })
312
312
  ]
@@ -323,7 +323,7 @@ function j() {
323
323
  placeholder: "2"
324
324
  }),
325
325
  /* @__PURE__ */ T("p", {
326
- className: "text-xs text-muted-foreground",
326
+ className: "text-xs text-text-secondary",
327
327
  children: "Kubernetes CPU limit (e.g. 1, 2, 4)"
328
328
  })
329
329
  ]
@@ -345,7 +345,7 @@ function j() {
345
345
  placeholder: "512Mi"
346
346
  }),
347
347
  /* @__PURE__ */ T("p", {
348
- className: "text-xs text-muted-foreground",
348
+ className: "text-xs text-text-secondary",
349
349
  children: "Kubernetes memory request (e.g. 512Mi, 1Gi)"
350
350
  })
351
351
  ]
@@ -362,7 +362,7 @@ function j() {
362
362
  placeholder: "4Gi"
363
363
  }),
364
364
  /* @__PURE__ */ T("p", {
365
- className: "text-xs text-muted-foreground",
365
+ className: "text-xs text-text-secondary",
366
366
  children: "Kubernetes memory limit (e.g. 2Gi, 4Gi, 8Gi)"
367
367
  })
368
368
  ]
@@ -376,7 +376,7 @@ function j() {
376
376
  children: [
377
377
  /* @__PURE__ */ E(i, { children: [/* @__PURE__ */ T(s, { children: /* @__PURE__ */ E("div", {
378
378
  className: "flex items-center gap-2",
379
- children: [/* @__PURE__ */ T(w, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ T(c, {
379
+ children: [/* @__PURE__ */ T(w, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ T(c, {
380
380
  className: "text-lg",
381
381
  children: "Concurrency"
382
382
  })]
@@ -398,7 +398,7 @@ function j() {
398
398
  onChange: (e) => K("maxConcurrentPerUser", Number(e.target.value))
399
399
  }),
400
400
  /* @__PURE__ */ T("p", {
401
- className: "text-xs text-muted-foreground",
401
+ className: "text-xs text-text-secondary",
402
402
  children: "Maximum concurrent sandboxes per user"
403
403
  })
404
404
  ]
@@ -419,7 +419,7 @@ function j() {
419
419
  onChange: (e) => K("maxConcurrentPerWorkspace", Number(e.target.value))
420
420
  }),
421
421
  /* @__PURE__ */ T("p", {
422
- className: "text-xs text-muted-foreground",
422
+ className: "text-xs text-text-secondary",
423
423
  children: "Maximum concurrent sandboxes per workspace"
424
424
  })
425
425
  ]
@@ -428,7 +428,7 @@ function j() {
428
428
  })] }),
429
429
  /* @__PURE__ */ E(i, { children: [/* @__PURE__ */ T(s, { children: /* @__PURE__ */ E("div", {
430
430
  className: "flex items-center gap-2",
431
- children: [/* @__PURE__ */ T(x, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ T(c, {
431
+ children: [/* @__PURE__ */ T(x, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ T(c, {
432
432
  className: "text-lg",
433
433
  children: "Resource Info"
434
434
  })]
@@ -436,12 +436,12 @@ function j() {
436
436
  className: "space-y-3",
437
437
  children: [
438
438
  /* @__PURE__ */ T("p", {
439
- className: "text-sm text-muted-foreground",
439
+ className: "text-sm text-text-secondary",
440
440
  children: "Resource defaults are applied to all new sandboxes. Individual sandboxes can override these within the max limits defined by the sandbox type."
441
441
  }),
442
442
  /* @__PURE__ */ T(u, {}),
443
443
  /* @__PURE__ */ E("div", {
444
- className: "space-y-2 text-xs text-muted-foreground",
444
+ className: "space-y-2 text-xs text-text-secondary",
445
445
  children: [/* @__PURE__ */ E("div", {
446
446
  className: "flex justify-between",
447
447
  children: [/* @__PURE__ */ T("span", { children: "CPU units" }), /* @__PURE__ */ T("span", {
@@ -1 +1 @@
1
- {"version":3,"file":"SandboxSettingsPage.js","names":[],"sources":["../../../src/admin/pages/SandboxSettingsPage.tsx"],"sourcesContent":["import { useState, useCallback, useEffect } from 'react';\nimport { Box, Cpu, MemoryStick, Timer, Users, Building2, CheckSquare, Save } from 'lucide-react';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport {\n PageLayout,\n Card,\n CardHeader,\n CardTitle,\n CardContent,\n CardDescription,\n Button,\n Input,\n Separator,\n MetricsGrid,\n MetricCard,\n useToast,\n PermissionGuard,\n} from '../components';\nimport { useAdmin } from '../providers/AdminProvider';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nconst SANDBOX_TYPES = [\n 'VIBECONTROLS',\n 'AI_BUILDER',\n 'WORKFLOW_MULTI_NODE',\n 'MISC_SHELL',\n 'CUSTOM',\n] as const;\n\ntype SandboxType = (typeof SANDBOX_TYPES)[number];\n\nconst TTL_OPTIONS = [\n { label: '30 minutes', value: 1800 },\n { label: '1 hour', value: 3600 },\n { label: '2 hours', value: 7200 },\n { label: '4 hours', value: 14400 },\n { label: '8 hours', value: 28800 },\n { label: '24 hours', value: 86400 },\n] as const;\n\ninterface SandboxConfig {\n defaultTtl: number;\n maxTtl: number;\n allowedSandboxTypes: SandboxType[];\n maxConcurrentPerUser: number;\n maxConcurrentPerWorkspace: number;\n defaultCpuRequest: string;\n defaultCpuLimit: string;\n defaultMemoryRequest: string;\n defaultMemoryLimit: string;\n}\n\n// ============================================================================\n// Mocked Stats (will be wired to query later)\n// ============================================================================\n\nconst MOCK_STATS = {\n totalActive: 12,\n totalQueued: 3,\n totalCompleted: 847,\n totalFailed: 14,\n};\n\n// ============================================================================\n// Helpers — Gateway URL derivation\n// ============================================================================\n\n/**\n * Derive the workspace gateway URL from the global gateway URL.\n *\n * Convention:\n * global gateway → https://graphql.burdenoff.com/global/graphql\n * wspace gateway → https://graphqlworkspaces.burdenoff.com/workspaces/graphql\n *\n * For local dev the global gateway is at localhost:4000 and the workspace\n * gateway is at localhost:4003, so we swap the port as a fallback.\n */\nfunction deriveWorkspaceGatewayUrl(apiGatewayUrl: string | undefined): string {\n if (!apiGatewayUrl) {\n return 'http://localhost:4003/workspaces/graphql';\n }\n\n // Local port-forward convention: 4000 → 4003\n if (apiGatewayUrl.includes('localhost:4000')) {\n return apiGatewayUrl\n .replace('localhost:4000', 'localhost:4003')\n .replace('/global/', '/workspaces/');\n }\n\n // Alpha / prod convention: alphagraphql → alphagraphqlworkspaces, graphql → graphqlworkspaces\n if (apiGatewayUrl.includes('/global/')) {\n return apiGatewayUrl\n .replace(/\\/\\/graphql\\./, '//graphqlworkspaces.')\n .replace(/\\/\\/alphagraphql\\./, '//alphagraphqlworkspaces.')\n .replace('/global/', '/workspaces/');\n }\n\n // Fallback: assume local dev\n return 'http://localhost:4003/workspaces/graphql';\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * Sandbox Settings page for superadmin sandbox configuration.\n * Manages platform-wide sandbox defaults including TTLs, resource limits,\n * allowed types, and concurrency caps.\n */\nexport function SandboxSettingsPage() {\n const { canManageSandbox, isLoading: permissionsLoading } = useAdminPermissions();\n const toast = useToast();\n const { apiGatewayUrl, authToken, tenantId } = useAdmin();\n const workspaceGatewayUrl = deriveWorkspaceGatewayUrl(apiGatewayUrl);\n\n const [config, setConfig] = useState<SandboxConfig>({\n defaultTtl: 3600,\n maxTtl: 14400,\n allowedSandboxTypes: [\n 'VIBECONTROLS',\n 'AI_BUILDER',\n 'WORKFLOW_MULTI_NODE',\n 'MISC_SHELL',\n 'CUSTOM',\n ],\n maxConcurrentPerUser: 3,\n maxConcurrentPerWorkspace: 10,\n defaultCpuRequest: '500m',\n defaultCpuLimit: '2',\n defaultMemoryRequest: '512Mi',\n defaultMemoryLimit: '4Gi',\n });\n\n const [isSaving, setIsSaving] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n\n // ---------------------------------------------------------------------------\n // Load config on mount\n // ---------------------------------------------------------------------------\n useEffect(() => {\n if (!tenantId || permissionsLoading || !canManageSandbox) return;\n\n // Auth tokens go to the gateway (edge) which validates and strips them.\n // X-Workspace-ID is forwarded to downstream subgraphs as workspace context.\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (authToken) headers['Authorization'] = `Bearer ${authToken}`;\n if (tenantId) headers['X-Workspace-ID'] = tenantId;\n\n setIsLoading(true);\n fetch(workspaceGatewayUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n query: `query GetSandboxConfig($context: ContextInput!) {\n getSandboxConfig(context: $context) {\n id\n defaultTtlSeconds\n maxTtlSeconds\n allowedTemplates\n maxConcurrentPerUser\n maxConcurrentPerWorkspace\n defaultCpuRequest\n defaultCpuLimit\n defaultMemoryRequest\n defaultMemoryLimit\n }\n }`,\n variables: { context: { workspaceId: tenantId } },\n }),\n })\n .then((r) => r.json())\n .then((result) => {\n if (result.data?.getSandboxConfig) {\n const c = result.data.getSandboxConfig;\n setConfig({\n defaultTtl: c.defaultTtlSeconds,\n maxTtl: c.maxTtlSeconds,\n allowedSandboxTypes: c.allowedTemplates,\n maxConcurrentPerUser: c.maxConcurrentPerUser,\n maxConcurrentPerWorkspace: c.maxConcurrentPerWorkspace,\n defaultCpuRequest: c.defaultCpuRequest,\n defaultCpuLimit: c.defaultCpuLimit,\n defaultMemoryRequest: c.defaultMemoryRequest,\n defaultMemoryLimit: c.defaultMemoryLimit,\n });\n }\n })\n .catch((err) => {\n console.error('Failed to load sandbox config:', err);\n toast.error('Load Failed', 'Failed to load sandbox configuration.');\n })\n .finally(() => setIsLoading(false));\n // eslint-disable-next-line react-hooks/exhaustive-deps -- toast excluded to avoid re-fetching when its reference changes\n }, [tenantId, authToken, workspaceGatewayUrl, permissionsLoading, canManageSandbox]);\n\n const updateConfig = useCallback(\n <K extends keyof SandboxConfig>(key: K, value: SandboxConfig[K]) => {\n setConfig((prev) => ({ ...prev, [key]: value }));\n },\n []\n );\n\n const toggleSandboxType = useCallback((type: SandboxType) => {\n setConfig((prev) => {\n const types = prev.allowedSandboxTypes.includes(type)\n ? prev.allowedSandboxTypes.filter((t) => t !== type)\n : [...prev.allowedSandboxTypes, type];\n return { ...prev, allowedSandboxTypes: types };\n });\n }, []);\n\n const handleSave = useCallback(async () => {\n // Validate max TTL >= default TTL\n if (config.maxTtl < config.defaultTtl) {\n toast.error('Validation Error', 'Max TTL must be greater than or equal to Default TTL.');\n return;\n }\n\n if (config.allowedSandboxTypes.length === 0) {\n toast.error('Validation Error', 'At least one sandbox type must be allowed.');\n return;\n }\n\n setIsSaving(true);\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (authToken) headers['Authorization'] = `Bearer ${authToken}`;\n if (tenantId) headers['X-Workspace-ID'] = tenantId;\n\n const response = await fetch(workspaceGatewayUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n query: `mutation UpdateSandboxConfig($input: UpdateSandboxConfigInput!) {\n updateSandboxConfig(input: $input) {\n id\n defaultTtlSeconds\n maxTtlSeconds\n allowedTemplates\n maxConcurrentPerUser\n maxConcurrentPerWorkspace\n defaultCpuRequest\n defaultCpuLimit\n defaultMemoryRequest\n defaultMemoryLimit\n }\n }`,\n variables: {\n input: {\n defaultTtlSeconds: config.defaultTtl,\n maxTtlSeconds: config.maxTtl,\n allowedTemplates: config.allowedSandboxTypes,\n maxConcurrentPerUser: config.maxConcurrentPerUser,\n maxConcurrentPerWorkspace: config.maxConcurrentPerWorkspace,\n defaultCpuRequest: config.defaultCpuRequest,\n defaultCpuLimit: config.defaultCpuLimit,\n defaultMemoryRequest: config.defaultMemoryRequest,\n defaultMemoryLimit: config.defaultMemoryLimit,\n context: { workspaceId: tenantId },\n },\n },\n }),\n });\n\n const result = await response.json();\n if (result.errors) {\n throw new Error(result.errors[0]?.message || 'Failed to save sandbox configuration');\n }\n\n toast.success('Settings Saved', 'Sandbox configuration has been updated successfully.');\n } catch (err) {\n toast.error(\n 'Save Failed',\n err instanceof Error\n ? err.message\n : 'Failed to update sandbox configuration. Please try again.'\n );\n } finally {\n setIsSaving(false);\n }\n }, [config, toast, authToken, tenantId, workspaceGatewayUrl]);\n\n const selectClasses =\n 'h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm';\n\n return (\n <PermissionGuard\n allowed={canManageSandbox}\n isLoading={permissionsLoading}\n pageTitle=\"Sandbox Settings\"\n denyMessage=\"You don't have permission to manage sandbox settings.\"\n >\n <PageLayout\n title=\"Sandbox Settings\"\n description=\"Configure platform-wide sandbox defaults and resource limits\"\n >\n {/* Sandbox Stats */}\n <Card className=\"mb-6\">\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Box className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-lg\">Sandbox Stats</CardTitle>\n </div>\n <CardDescription>Current sandbox usage across the platform</CardDescription>\n </CardHeader>\n <CardContent>\n <MetricsGrid>\n <MetricCard title=\"Active Sandboxes\" value={MOCK_STATS.totalActive} trend=\"up\" />\n <MetricCard title=\"Queued\" value={MOCK_STATS.totalQueued} />\n <MetricCard title=\"Completed (all time)\" value={MOCK_STATS.totalCompleted} />\n <MetricCard title=\"Failed (all time)\" value={MOCK_STATS.totalFailed} trend=\"down\" />\n </MetricsGrid>\n </CardContent>\n </Card>\n\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Main settings */}\n <div className=\"space-y-6 lg:col-span-2\">\n {/* TTL Configuration */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Timer className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-lg\">Time-to-Live (TTL)</CardTitle>\n </div>\n <CardDescription>\n Control how long sandboxes stay alive before automatic cleanup\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Default TTL</label>\n <select\n className={selectClasses}\n value={config.defaultTtl}\n onChange={(e) => updateConfig('defaultTtl', Number(e.target.value))}\n >\n {TTL_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n <p className=\"text-xs text-muted-foreground\">\n Default lifetime for new sandboxes\n </p>\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Max TTL</label>\n <select\n className={selectClasses}\n value={config.maxTtl}\n onChange={(e) => updateConfig('maxTtl', Number(e.target.value))}\n >\n {TTL_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n <p className=\"text-xs text-muted-foreground\">\n Maximum allowed TTL (must be &gt;= default)\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* Allowed Sandbox Types */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <CheckSquare className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-lg\">Allowed Sandbox Types</CardTitle>\n </div>\n <CardDescription>\n Select which sandbox types users are permitted to create\n </CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {SANDBOX_TYPES.map((type) => (\n <label\n key={type}\n className=\"flex items-center gap-3 rounded-lg border border-border p-4 cursor-pointer hover:bg-muted/50 transition-colors\"\n >\n <input\n type=\"checkbox\"\n checked={config.allowedSandboxTypes.includes(type)}\n onChange={() => toggleSandboxType(type)}\n className=\"h-4 w-4 rounded border-input text-primary focus:ring-primary\"\n />\n <div>\n <div className=\"font-medium text-sm\">{formatSandboxType(type)}</div>\n <div className=\"text-xs text-muted-foreground\">\n {getSandboxTypeDescription(type)}\n </div>\n </div>\n </label>\n ))}\n </div>\n </CardContent>\n </Card>\n\n {/* Resource Limits */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Cpu className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-lg\">Default Resource Limits</CardTitle>\n </div>\n <CardDescription>\n Kubernetes resource requests and limits applied to new sandboxes\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">CPU Request</label>\n <Input\n value={config.defaultCpuRequest}\n onChange={(e) => updateConfig('defaultCpuRequest', e.target.value)}\n placeholder=\"500m\"\n />\n <p className=\"text-xs text-muted-foreground\">\n Kubernetes CPU request (e.g. 500m, 1)\n </p>\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">CPU Limit</label>\n <Input\n value={config.defaultCpuLimit}\n onChange={(e) => updateConfig('defaultCpuLimit', e.target.value)}\n placeholder=\"2\"\n />\n <p className=\"text-xs text-muted-foreground\">\n Kubernetes CPU limit (e.g. 1, 2, 4)\n </p>\n </div>\n </div>\n <Separator />\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Memory Request</label>\n <Input\n value={config.defaultMemoryRequest}\n onChange={(e) => updateConfig('defaultMemoryRequest', e.target.value)}\n placeholder=\"512Mi\"\n />\n <p className=\"text-xs text-muted-foreground\">\n Kubernetes memory request (e.g. 512Mi, 1Gi)\n </p>\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Memory Limit</label>\n <Input\n value={config.defaultMemoryLimit}\n onChange={(e) => updateConfig('defaultMemoryLimit', e.target.value)}\n placeholder=\"4Gi\"\n />\n <p className=\"text-xs text-muted-foreground\">\n Kubernetes memory limit (e.g. 2Gi, 4Gi, 8Gi)\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Concurrency Limits */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Users className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-lg\">Concurrency</CardTitle>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Max Per User</label>\n <Input\n type=\"number\"\n min={1}\n max={50}\n value={config.maxConcurrentPerUser}\n onChange={(e) => updateConfig('maxConcurrentPerUser', Number(e.target.value))}\n />\n <p className=\"text-xs text-muted-foreground\">\n Maximum concurrent sandboxes per user\n </p>\n </div>\n <Separator />\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Max Per Workspace</label>\n <Input\n type=\"number\"\n min={1}\n max={200}\n value={config.maxConcurrentPerWorkspace}\n onChange={(e) =>\n updateConfig('maxConcurrentPerWorkspace', Number(e.target.value))\n }\n />\n <p className=\"text-xs text-muted-foreground\">\n Maximum concurrent sandboxes per workspace\n </p>\n </div>\n </CardContent>\n </Card>\n\n {/* Resource Presets Info */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <MemoryStick className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-lg\">Resource Info</CardTitle>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n Resource defaults are applied to all new sandboxes. Individual sandboxes can\n override these within the max limits defined by the sandbox type.\n </p>\n <Separator />\n <div className=\"space-y-2 text-xs text-muted-foreground\">\n <div className=\"flex justify-between\">\n <span>CPU units</span>\n <span className=\"font-mono\">millicores (m)</span>\n </div>\n <div className=\"flex justify-between\">\n <span>Memory units</span>\n <span className=\"font-mono\">Mi / Gi</span>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* Save Button */}\n <Card>\n <CardContent className=\"p-6\">\n <Button className=\"w-full\" onClick={handleSave} disabled={isSaving || isLoading}>\n <Save className=\"mr-2 h-4 w-4\" />\n {isSaving ? 'Saving...' : isLoading ? 'Loading...' : 'Save Configuration'}\n </Button>\n </CardContent>\n </Card>\n </div>\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction formatSandboxType(type: SandboxType): string {\n const labels: Record<SandboxType, string> = {\n VIBECONTROLS: 'VibeControls',\n AI_BUILDER: 'AI Builder',\n WORKFLOW_MULTI_NODE: 'Workflow (Multi-Node)',\n MISC_SHELL: 'Misc Shell',\n CUSTOM: 'Custom',\n };\n return labels[type];\n}\n\nfunction getSandboxTypeDescription(type: SandboxType): string {\n const descriptions: Record<SandboxType, string> = {\n VIBECONTROLS: 'Full VibeControls development environment with pre-configured tooling',\n AI_BUILDER: 'AI agent development sandbox with OpenCode SDK and model access',\n WORKFLOW_MULTI_NODE: 'Multi-node workflow execution environment for complex pipelines',\n MISC_SHELL: 'General-purpose shell environment for ad-hoc tasks',\n CUSTOM: 'User-defined sandbox from custom container images',\n };\n return descriptions[type];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SandboxSettingsPage.js","names":[],"sources":["../../../src/admin/pages/SandboxSettingsPage.tsx"],"sourcesContent":["import { useState, useCallback, useEffect } from 'react';\nimport { Box, Cpu, MemoryStick, Timer, Users, Building2, CheckSquare, Save } from 'lucide-react';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport {\n PageLayout,\n Card,\n CardHeader,\n CardTitle,\n CardContent,\n CardDescription,\n Button,\n Input,\n Separator,\n MetricsGrid,\n MetricCard,\n useToast,\n PermissionGuard,\n} from '../components';\nimport { useAdmin } from '../providers/AdminProvider';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nconst SANDBOX_TYPES = [\n 'VIBECONTROLS',\n 'AI_BUILDER',\n 'WORKFLOW_MULTI_NODE',\n 'MISC_SHELL',\n 'CUSTOM',\n] as const;\n\ntype SandboxType = (typeof SANDBOX_TYPES)[number];\n\nconst TTL_OPTIONS = [\n { label: '30 minutes', value: 1800 },\n { label: '1 hour', value: 3600 },\n { label: '2 hours', value: 7200 },\n { label: '4 hours', value: 14400 },\n { label: '8 hours', value: 28800 },\n { label: '24 hours', value: 86400 },\n] as const;\n\ninterface SandboxConfig {\n defaultTtl: number;\n maxTtl: number;\n allowedSandboxTypes: SandboxType[];\n maxConcurrentPerUser: number;\n maxConcurrentPerWorkspace: number;\n defaultCpuRequest: string;\n defaultCpuLimit: string;\n defaultMemoryRequest: string;\n defaultMemoryLimit: string;\n}\n\n// ============================================================================\n// Mocked Stats (will be wired to query later)\n// ============================================================================\n\nconst MOCK_STATS = {\n totalActive: 12,\n totalQueued: 3,\n totalCompleted: 847,\n totalFailed: 14,\n};\n\n// ============================================================================\n// Helpers — Gateway URL derivation\n// ============================================================================\n\n/**\n * Derive the workspace gateway URL from the global gateway URL.\n *\n * Convention:\n * global gateway → https://graphql.burdenoff.com/global/graphql\n * wspace gateway → https://graphqlworkspaces.burdenoff.com/workspaces/graphql\n *\n * For local dev the global gateway is at localhost:4000 and the workspace\n * gateway is at localhost:4003, so we swap the port as a fallback.\n */\nfunction deriveWorkspaceGatewayUrl(apiGatewayUrl: string | undefined): string {\n if (!apiGatewayUrl) {\n return 'http://localhost:4003/workspaces/graphql';\n }\n\n // Local port-forward convention: 4000 → 4003\n if (apiGatewayUrl.includes('localhost:4000')) {\n return apiGatewayUrl\n .replace('localhost:4000', 'localhost:4003')\n .replace('/global/', '/workspaces/');\n }\n\n // Alpha / prod convention: alphagraphql → alphagraphqlworkspaces, graphql → graphqlworkspaces\n if (apiGatewayUrl.includes('/global/')) {\n return apiGatewayUrl\n .replace(/\\/\\/graphql\\./, '//graphqlworkspaces.')\n .replace(/\\/\\/alphagraphql\\./, '//alphagraphqlworkspaces.')\n .replace('/global/', '/workspaces/');\n }\n\n // Fallback: assume local dev\n return 'http://localhost:4003/workspaces/graphql';\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * Sandbox Settings page for superadmin sandbox configuration.\n * Manages platform-wide sandbox defaults including TTLs, resource limits,\n * allowed types, and concurrency caps.\n */\nexport function SandboxSettingsPage() {\n const { canManageSandbox, isLoading: permissionsLoading } = useAdminPermissions();\n const toast = useToast();\n const { apiGatewayUrl, authToken, tenantId } = useAdmin();\n const workspaceGatewayUrl = deriveWorkspaceGatewayUrl(apiGatewayUrl);\n\n const [config, setConfig] = useState<SandboxConfig>({\n defaultTtl: 3600,\n maxTtl: 14400,\n allowedSandboxTypes: [\n 'VIBECONTROLS',\n 'AI_BUILDER',\n 'WORKFLOW_MULTI_NODE',\n 'MISC_SHELL',\n 'CUSTOM',\n ],\n maxConcurrentPerUser: 3,\n maxConcurrentPerWorkspace: 10,\n defaultCpuRequest: '500m',\n defaultCpuLimit: '2',\n defaultMemoryRequest: '512Mi',\n defaultMemoryLimit: '4Gi',\n });\n\n const [isSaving, setIsSaving] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n\n // ---------------------------------------------------------------------------\n // Load config on mount\n // ---------------------------------------------------------------------------\n useEffect(() => {\n if (!tenantId || permissionsLoading || !canManageSandbox) return;\n\n // Auth tokens go to the gateway (edge) which validates and strips them.\n // X-Workspace-ID is forwarded to downstream subgraphs as workspace context.\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (authToken) headers['Authorization'] = `Bearer ${authToken}`;\n if (tenantId) headers['X-Workspace-ID'] = tenantId;\n\n setIsLoading(true);\n fetch(workspaceGatewayUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n query: `query GetSandboxConfig($context: ContextInput!) {\n getSandboxConfig(context: $context) {\n id\n defaultTtlSeconds\n maxTtlSeconds\n allowedTemplates\n maxConcurrentPerUser\n maxConcurrentPerWorkspace\n defaultCpuRequest\n defaultCpuLimit\n defaultMemoryRequest\n defaultMemoryLimit\n }\n }`,\n variables: { context: { workspaceId: tenantId } },\n }),\n })\n .then((r) => r.json())\n .then((result) => {\n if (result.data?.getSandboxConfig) {\n const c = result.data.getSandboxConfig;\n setConfig({\n defaultTtl: c.defaultTtlSeconds,\n maxTtl: c.maxTtlSeconds,\n allowedSandboxTypes: c.allowedTemplates,\n maxConcurrentPerUser: c.maxConcurrentPerUser,\n maxConcurrentPerWorkspace: c.maxConcurrentPerWorkspace,\n defaultCpuRequest: c.defaultCpuRequest,\n defaultCpuLimit: c.defaultCpuLimit,\n defaultMemoryRequest: c.defaultMemoryRequest,\n defaultMemoryLimit: c.defaultMemoryLimit,\n });\n }\n })\n .catch((err) => {\n console.error('Failed to load sandbox config:', err);\n toast.error('Load Failed', 'Failed to load sandbox configuration.');\n })\n .finally(() => setIsLoading(false));\n // eslint-disable-next-line react-hooks/exhaustive-deps -- toast excluded to avoid re-fetching when its reference changes\n }, [tenantId, authToken, workspaceGatewayUrl, permissionsLoading, canManageSandbox]);\n\n const updateConfig = useCallback(\n <K extends keyof SandboxConfig>(key: K, value: SandboxConfig[K]) => {\n setConfig((prev) => ({ ...prev, [key]: value }));\n },\n []\n );\n\n const toggleSandboxType = useCallback((type: SandboxType) => {\n setConfig((prev) => {\n const types = prev.allowedSandboxTypes.includes(type)\n ? prev.allowedSandboxTypes.filter((t) => t !== type)\n : [...prev.allowedSandboxTypes, type];\n return { ...prev, allowedSandboxTypes: types };\n });\n }, []);\n\n const handleSave = useCallback(async () => {\n // Validate max TTL >= default TTL\n if (config.maxTtl < config.defaultTtl) {\n toast.error('Validation Error', 'Max TTL must be greater than or equal to Default TTL.');\n return;\n }\n\n if (config.allowedSandboxTypes.length === 0) {\n toast.error('Validation Error', 'At least one sandbox type must be allowed.');\n return;\n }\n\n setIsSaving(true);\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (authToken) headers['Authorization'] = `Bearer ${authToken}`;\n if (tenantId) headers['X-Workspace-ID'] = tenantId;\n\n const response = await fetch(workspaceGatewayUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n query: `mutation UpdateSandboxConfig($input: UpdateSandboxConfigInput!) {\n updateSandboxConfig(input: $input) {\n id\n defaultTtlSeconds\n maxTtlSeconds\n allowedTemplates\n maxConcurrentPerUser\n maxConcurrentPerWorkspace\n defaultCpuRequest\n defaultCpuLimit\n defaultMemoryRequest\n defaultMemoryLimit\n }\n }`,\n variables: {\n input: {\n defaultTtlSeconds: config.defaultTtl,\n maxTtlSeconds: config.maxTtl,\n allowedTemplates: config.allowedSandboxTypes,\n maxConcurrentPerUser: config.maxConcurrentPerUser,\n maxConcurrentPerWorkspace: config.maxConcurrentPerWorkspace,\n defaultCpuRequest: config.defaultCpuRequest,\n defaultCpuLimit: config.defaultCpuLimit,\n defaultMemoryRequest: config.defaultMemoryRequest,\n defaultMemoryLimit: config.defaultMemoryLimit,\n context: { workspaceId: tenantId },\n },\n },\n }),\n });\n\n const result = await response.json();\n if (result.errors) {\n throw new Error(result.errors[0]?.message || 'Failed to save sandbox configuration');\n }\n\n toast.success('Settings Saved', 'Sandbox configuration has been updated successfully.');\n } catch (err) {\n toast.error(\n 'Save Failed',\n err instanceof Error\n ? err.message\n : 'Failed to update sandbox configuration. Please try again.'\n );\n } finally {\n setIsSaving(false);\n }\n }, [config, toast, authToken, tenantId, workspaceGatewayUrl]);\n\n const selectClasses =\n 'h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm';\n\n return (\n <PermissionGuard\n allowed={canManageSandbox}\n isLoading={permissionsLoading}\n pageTitle=\"Sandbox Settings\"\n denyMessage=\"You don't have permission to manage sandbox settings.\"\n >\n <PageLayout\n title=\"Sandbox Settings\"\n description=\"Configure platform-wide sandbox defaults and resource limits\"\n >\n {/* Sandbox Stats */}\n <Card className=\"mb-6\">\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Box className=\"h-5 w-5 text-text-secondary\" />\n <CardTitle className=\"text-lg\">Sandbox Stats</CardTitle>\n </div>\n <CardDescription>Current sandbox usage across the platform</CardDescription>\n </CardHeader>\n <CardContent>\n <MetricsGrid>\n <MetricCard title=\"Active Sandboxes\" value={MOCK_STATS.totalActive} trend=\"up\" />\n <MetricCard title=\"Queued\" value={MOCK_STATS.totalQueued} />\n <MetricCard title=\"Completed (all time)\" value={MOCK_STATS.totalCompleted} />\n <MetricCard title=\"Failed (all time)\" value={MOCK_STATS.totalFailed} trend=\"down\" />\n </MetricsGrid>\n </CardContent>\n </Card>\n\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Main settings */}\n <div className=\"space-y-6 lg:col-span-2\">\n {/* TTL Configuration */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Timer className=\"h-5 w-5 text-text-secondary\" />\n <CardTitle className=\"text-lg\">Time-to-Live (TTL)</CardTitle>\n </div>\n <CardDescription>\n Control how long sandboxes stay alive before automatic cleanup\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Default TTL</label>\n <select\n className={selectClasses}\n value={config.defaultTtl}\n onChange={(e) => updateConfig('defaultTtl', Number(e.target.value))}\n >\n {TTL_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n <p className=\"text-xs text-text-secondary\">\n Default lifetime for new sandboxes\n </p>\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Max TTL</label>\n <select\n className={selectClasses}\n value={config.maxTtl}\n onChange={(e) => updateConfig('maxTtl', Number(e.target.value))}\n >\n {TTL_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n <p className=\"text-xs text-text-secondary\">\n Maximum allowed TTL (must be &gt;= default)\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* Allowed Sandbox Types */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <CheckSquare className=\"h-5 w-5 text-text-secondary\" />\n <CardTitle className=\"text-lg\">Allowed Sandbox Types</CardTitle>\n </div>\n <CardDescription>\n Select which sandbox types users are permitted to create\n </CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {SANDBOX_TYPES.map((type) => (\n <label\n key={type}\n className=\"flex items-center gap-3 rounded-lg border border-border-subtle p-4 cursor-pointer hover:bg-bg-sunken/50 transition-colors\"\n >\n <input\n type=\"checkbox\"\n checked={config.allowedSandboxTypes.includes(type)}\n onChange={() => toggleSandboxType(type)}\n className=\"h-4 w-4 rounded border-border-default text-primary focus:ring-primary\"\n />\n <div>\n <div className=\"font-medium text-sm\">{formatSandboxType(type)}</div>\n <div className=\"text-xs text-text-secondary\">\n {getSandboxTypeDescription(type)}\n </div>\n </div>\n </label>\n ))}\n </div>\n </CardContent>\n </Card>\n\n {/* Resource Limits */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Cpu className=\"h-5 w-5 text-text-secondary\" />\n <CardTitle className=\"text-lg\">Default Resource Limits</CardTitle>\n </div>\n <CardDescription>\n Kubernetes resource requests and limits applied to new sandboxes\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">CPU Request</label>\n <Input\n value={config.defaultCpuRequest}\n onChange={(e) => updateConfig('defaultCpuRequest', e.target.value)}\n placeholder=\"500m\"\n />\n <p className=\"text-xs text-text-secondary\">\n Kubernetes CPU request (e.g. 500m, 1)\n </p>\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">CPU Limit</label>\n <Input\n value={config.defaultCpuLimit}\n onChange={(e) => updateConfig('defaultCpuLimit', e.target.value)}\n placeholder=\"2\"\n />\n <p className=\"text-xs text-text-secondary\">\n Kubernetes CPU limit (e.g. 1, 2, 4)\n </p>\n </div>\n </div>\n <Separator />\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Memory Request</label>\n <Input\n value={config.defaultMemoryRequest}\n onChange={(e) => updateConfig('defaultMemoryRequest', e.target.value)}\n placeholder=\"512Mi\"\n />\n <p className=\"text-xs text-text-secondary\">\n Kubernetes memory request (e.g. 512Mi, 1Gi)\n </p>\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Memory Limit</label>\n <Input\n value={config.defaultMemoryLimit}\n onChange={(e) => updateConfig('defaultMemoryLimit', e.target.value)}\n placeholder=\"4Gi\"\n />\n <p className=\"text-xs text-text-secondary\">\n Kubernetes memory limit (e.g. 2Gi, 4Gi, 8Gi)\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Concurrency Limits */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Users className=\"h-5 w-5 text-text-secondary\" />\n <CardTitle className=\"text-lg\">Concurrency</CardTitle>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Max Per User</label>\n <Input\n type=\"number\"\n min={1}\n max={50}\n value={config.maxConcurrentPerUser}\n onChange={(e) => updateConfig('maxConcurrentPerUser', Number(e.target.value))}\n />\n <p className=\"text-xs text-text-secondary\">\n Maximum concurrent sandboxes per user\n </p>\n </div>\n <Separator />\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Max Per Workspace</label>\n <Input\n type=\"number\"\n min={1}\n max={200}\n value={config.maxConcurrentPerWorkspace}\n onChange={(e) =>\n updateConfig('maxConcurrentPerWorkspace', Number(e.target.value))\n }\n />\n <p className=\"text-xs text-text-secondary\">\n Maximum concurrent sandboxes per workspace\n </p>\n </div>\n </CardContent>\n </Card>\n\n {/* Resource Presets Info */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <MemoryStick className=\"h-5 w-5 text-text-secondary\" />\n <CardTitle className=\"text-lg\">Resource Info</CardTitle>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-text-secondary\">\n Resource defaults are applied to all new sandboxes. Individual sandboxes can\n override these within the max limits defined by the sandbox type.\n </p>\n <Separator />\n <div className=\"space-y-2 text-xs text-text-secondary\">\n <div className=\"flex justify-between\">\n <span>CPU units</span>\n <span className=\"font-mono\">millicores (m)</span>\n </div>\n <div className=\"flex justify-between\">\n <span>Memory units</span>\n <span className=\"font-mono\">Mi / Gi</span>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* Save Button */}\n <Card>\n <CardContent className=\"p-6\">\n <Button className=\"w-full\" onClick={handleSave} disabled={isSaving || isLoading}>\n <Save className=\"mr-2 h-4 w-4\" />\n {isSaving ? 'Saving...' : isLoading ? 'Loading...' : 'Save Configuration'}\n </Button>\n </CardContent>\n </Card>\n </div>\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction formatSandboxType(type: SandboxType): string {\n const labels: Record<SandboxType, string> = {\n VIBECONTROLS: 'VibeControls',\n AI_BUILDER: 'AI Builder',\n WORKFLOW_MULTI_NODE: 'Workflow (Multi-Node)',\n MISC_SHELL: 'Misc Shell',\n CUSTOM: 'Custom',\n };\n return labels[type];\n}\n\nfunction getSandboxTypeDescription(type: SandboxType): string {\n const descriptions: Record<SandboxType, string> = {\n VIBECONTROLS: 'Full VibeControls development environment with pre-configured tooling',\n AI_BUILDER: 'AI agent development sandbox with OpenCode SDK and model access',\n WORKFLOW_MULTI_NODE: 'Multi-node workflow execution environment for complex pipelines',\n MISC_SHELL: 'General-purpose shell environment for ad-hoc tasks',\n CUSTOM: 'User-defined sandbox from custom container images',\n };\n return descriptions[type];\n}\n"],"mappings":"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"}