@burdenoff/microfe-admin 2026.610.1 → 2026.625.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 (81) 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 +8 -8
  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 +43 -40
  14. package/dist/admin/components/MetricCard.js.map +1 -1
  15. package/dist/admin/components/PageLayout.js +28 -22
  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 +58 -51
  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 +10 -8
  40. package/dist/admin/pages/AuditLogsPage.js.map +1 -1
  41. package/dist/admin/pages/BillingPage.js +51 -43
  42. package/dist/admin/pages/BillingPage.js.map +1 -1
  43. package/dist/admin/pages/CommunicationsPage.js +5 -2
  44. package/dist/admin/pages/CommunicationsPage.js.map +1 -1
  45. package/dist/admin/pages/ContentPagesPage.js +2 -0
  46. package/dist/admin/pages/ContentPagesPage.js.map +1 -1
  47. package/dist/admin/pages/DashboardPage.js +7 -4
  48. package/dist/admin/pages/DashboardPage.js.map +1 -1
  49. package/dist/admin/pages/FeatureFlagsPage.js +6 -4
  50. package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
  51. package/dist/admin/pages/OrganizationsListPage.js +8 -6
  52. package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
  53. package/dist/admin/pages/ProductsPage.js +10 -7
  54. package/dist/admin/pages/ProductsPage.js.map +1 -1
  55. package/dist/admin/pages/ReferralsPage.js +27 -23
  56. package/dist/admin/pages/ReferralsPage.js.map +1 -1
  57. package/dist/admin/pages/RolesPermissionsPage.js +11 -8
  58. package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
  59. package/dist/admin/pages/SREPage.js +12 -8
  60. package/dist/admin/pages/SREPage.js.map +1 -1
  61. package/dist/admin/pages/SandboxSettingsPage.js +21 -20
  62. package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
  63. package/dist/admin/pages/SettingsPage.js +154 -142
  64. package/dist/admin/pages/SettingsPage.js.map +1 -1
  65. package/dist/admin/pages/SyncFailuresPage.js +14 -11
  66. package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
  67. package/dist/admin/pages/TenantDetailPage.js +22 -22
  68. package/dist/admin/pages/TenantDetailPage.js.map +1 -1
  69. package/dist/admin/pages/TenantFormRoutePage.js +2 -2
  70. package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
  71. package/dist/admin/pages/TenantsListPage.js +7 -5
  72. package/dist/admin/pages/TenantsListPage.js.map +1 -1
  73. package/dist/admin/pages/UsersListPage.js +8 -6
  74. package/dist/admin/pages/UsersListPage.js.map +1 -1
  75. package/dist/admin/pages/WaitlistPage.js +9 -7
  76. package/dist/admin/pages/WaitlistPage.js.map +1 -1
  77. package/dist/admin/pages/WorkflowsPage.js +6 -3
  78. package/dist/admin/pages/WorkflowsPage.js.map +1 -1
  79. package/dist/admin/pages/WorkspacesListPage.js +9 -8
  80. package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
  81. package/package.json +2 -2
@@ -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 gradientWord=\"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 hero\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\n illustration=\"empty-data\"\n icon={<Server className=\"h-8 w-8\" />}\n title=\"No services found\"\n />\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 illustration=\"empty-data\"\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,
@@ -169,12 +169,13 @@ function j() {
169
169
  denyMessage: "You don't have permission to manage sandbox settings.",
170
170
  children: /* @__PURE__ */ E(d, {
171
171
  title: "Sandbox Settings",
172
+ gradientWord: "Settings",
172
173
  description: "Configure platform-wide sandbox defaults and resource limits",
173
174
  children: [/* @__PURE__ */ E(i, {
174
175
  className: "mb-6",
175
176
  children: [/* @__PURE__ */ E(s, { children: [/* @__PURE__ */ E("div", {
176
177
  className: "flex items-center gap-2",
177
- children: [/* @__PURE__ */ T(v, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ T(c, {
178
+ children: [/* @__PURE__ */ T(v, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ T(c, {
178
179
  className: "text-lg",
179
180
  children: "Sandbox Stats"
180
181
  })]
@@ -205,7 +206,7 @@ function j() {
205
206
  children: [
206
207
  /* @__PURE__ */ E(i, { children: [/* @__PURE__ */ E(s, { children: [/* @__PURE__ */ E("div", {
207
208
  className: "flex items-center gap-2",
208
- children: [/* @__PURE__ */ T(C, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ T(c, {
209
+ children: [/* @__PURE__ */ T(C, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ T(c, {
209
210
  className: "text-lg",
210
211
  children: "Time-to-Live (TTL)"
211
212
  })]
@@ -230,7 +231,7 @@ function j() {
230
231
  }, e.value))
231
232
  }),
232
233
  /* @__PURE__ */ T("p", {
233
- className: "text-xs text-muted-foreground",
234
+ className: "text-xs text-text-secondary",
234
235
  children: "Default lifetime for new sandboxes"
235
236
  })
236
237
  ]
@@ -251,7 +252,7 @@ function j() {
251
252
  }, e.value))
252
253
  }),
253
254
  /* @__PURE__ */ T("p", {
254
- className: "text-xs text-muted-foreground",
255
+ className: "text-xs text-text-secondary",
255
256
  children: "Maximum allowed TTL (must be >= default)"
256
257
  })
257
258
  ]
@@ -260,31 +261,31 @@ function j() {
260
261
  })] }),
261
262
  /* @__PURE__ */ E(i, { children: [/* @__PURE__ */ E(s, { children: [/* @__PURE__ */ E("div", {
262
263
  className: "flex items-center gap-2",
263
- children: [/* @__PURE__ */ T(y, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ T(c, {
264
+ children: [/* @__PURE__ */ T(y, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ T(c, {
264
265
  className: "text-lg",
265
266
  children: "Allowed Sandbox Types"
266
267
  })]
267
268
  }), /* @__PURE__ */ T(o, { children: "Select which sandbox types users are permitted to create" })] }), /* @__PURE__ */ T(a, { children: /* @__PURE__ */ T("div", {
268
269
  className: "space-y-3",
269
270
  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",
271
+ className: "flex items-center gap-3 rounded-lg border border-border-subtle p-4 cursor-pointer hover:bg-bg-sunken/50 transition-colors",
271
272
  children: [/* @__PURE__ */ T("input", {
272
273
  type: "checkbox",
273
274
  checked: B.allowedSandboxTypes.includes(e),
274
275
  onChange: () => q(e),
275
- className: "h-4 w-4 rounded border-input text-primary focus:ring-primary"
276
+ className: "h-4 w-4 rounded border-border-default text-primary focus:ring-primary"
276
277
  }), /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("div", {
277
278
  className: "font-medium text-sm",
278
279
  children: M(e)
279
280
  }), /* @__PURE__ */ T("div", {
280
- className: "text-xs text-muted-foreground",
281
+ className: "text-xs text-text-secondary",
281
282
  children: N(e)
282
283
  })] })]
283
284
  }, e))
284
285
  }) })] }),
285
286
  /* @__PURE__ */ E(i, { children: [/* @__PURE__ */ E(s, { children: [/* @__PURE__ */ E("div", {
286
287
  className: "flex items-center gap-2",
287
- children: [/* @__PURE__ */ T(b, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ T(c, {
288
+ children: [/* @__PURE__ */ T(b, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ T(c, {
288
289
  className: "text-lg",
289
290
  children: "Default Resource Limits"
290
291
  })]
@@ -306,7 +307,7 @@ function j() {
306
307
  placeholder: "500m"
307
308
  }),
308
309
  /* @__PURE__ */ T("p", {
309
- className: "text-xs text-muted-foreground",
310
+ className: "text-xs text-text-secondary",
310
311
  children: "Kubernetes CPU request (e.g. 500m, 1)"
311
312
  })
312
313
  ]
@@ -323,7 +324,7 @@ function j() {
323
324
  placeholder: "2"
324
325
  }),
325
326
  /* @__PURE__ */ T("p", {
326
- className: "text-xs text-muted-foreground",
327
+ className: "text-xs text-text-secondary",
327
328
  children: "Kubernetes CPU limit (e.g. 1, 2, 4)"
328
329
  })
329
330
  ]
@@ -345,7 +346,7 @@ function j() {
345
346
  placeholder: "512Mi"
346
347
  }),
347
348
  /* @__PURE__ */ T("p", {
348
- className: "text-xs text-muted-foreground",
349
+ className: "text-xs text-text-secondary",
349
350
  children: "Kubernetes memory request (e.g. 512Mi, 1Gi)"
350
351
  })
351
352
  ]
@@ -362,7 +363,7 @@ function j() {
362
363
  placeholder: "4Gi"
363
364
  }),
364
365
  /* @__PURE__ */ T("p", {
365
- className: "text-xs text-muted-foreground",
366
+ className: "text-xs text-text-secondary",
366
367
  children: "Kubernetes memory limit (e.g. 2Gi, 4Gi, 8Gi)"
367
368
  })
368
369
  ]
@@ -376,7 +377,7 @@ function j() {
376
377
  children: [
377
378
  /* @__PURE__ */ E(i, { children: [/* @__PURE__ */ T(s, { children: /* @__PURE__ */ E("div", {
378
379
  className: "flex items-center gap-2",
379
- children: [/* @__PURE__ */ T(w, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ T(c, {
380
+ children: [/* @__PURE__ */ T(w, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ T(c, {
380
381
  className: "text-lg",
381
382
  children: "Concurrency"
382
383
  })]
@@ -398,7 +399,7 @@ function j() {
398
399
  onChange: (e) => K("maxConcurrentPerUser", Number(e.target.value))
399
400
  }),
400
401
  /* @__PURE__ */ T("p", {
401
- className: "text-xs text-muted-foreground",
402
+ className: "text-xs text-text-secondary",
402
403
  children: "Maximum concurrent sandboxes per user"
403
404
  })
404
405
  ]
@@ -419,7 +420,7 @@ function j() {
419
420
  onChange: (e) => K("maxConcurrentPerWorkspace", Number(e.target.value))
420
421
  }),
421
422
  /* @__PURE__ */ T("p", {
422
- className: "text-xs text-muted-foreground",
423
+ className: "text-xs text-text-secondary",
423
424
  children: "Maximum concurrent sandboxes per workspace"
424
425
  })
425
426
  ]
@@ -428,7 +429,7 @@ function j() {
428
429
  })] }),
429
430
  /* @__PURE__ */ E(i, { children: [/* @__PURE__ */ T(s, { children: /* @__PURE__ */ E("div", {
430
431
  className: "flex items-center gap-2",
431
- children: [/* @__PURE__ */ T(x, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ T(c, {
432
+ children: [/* @__PURE__ */ T(x, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ T(c, {
432
433
  className: "text-lg",
433
434
  children: "Resource Info"
434
435
  })]
@@ -436,12 +437,12 @@ function j() {
436
437
  className: "space-y-3",
437
438
  children: [
438
439
  /* @__PURE__ */ T("p", {
439
- className: "text-sm text-muted-foreground",
440
+ className: "text-sm text-text-secondary",
440
441
  children: "Resource defaults are applied to all new sandboxes. Individual sandboxes can override these within the max limits defined by the sandbox type."
441
442
  }),
442
443
  /* @__PURE__ */ T(u, {}),
443
444
  /* @__PURE__ */ E("div", {
444
- className: "space-y-2 text-xs text-muted-foreground",
445
+ className: "space-y-2 text-xs text-text-secondary",
445
446
  children: [/* @__PURE__ */ E("div", {
446
447
  className: "flex justify-between",
447
448
  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 gradientWord=\"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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"}