@burdenoff/microfe-admin 2026.608.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 (80) hide show
  1. package/dist/admin/AdminRoutes.js +14 -10
  2. package/dist/admin/AdminRoutes.js.map +1 -1
  3. package/dist/admin/components/AccessDenied.js +2 -2
  4. package/dist/admin/components/AccessDenied.js.map +1 -1
  5. package/dist/admin/components/ActivityFeed.js +4 -4
  6. package/dist/admin/components/ActivityFeed.js.map +1 -1
  7. package/dist/admin/components/Charts.js +4 -4
  8. package/dist/admin/components/Charts.js.map +1 -1
  9. package/dist/admin/components/ConfirmDialog.js +44 -25
  10. package/dist/admin/components/ConfirmDialog.js.map +1 -1
  11. package/dist/admin/components/DataTable.js +7 -7
  12. package/dist/admin/components/DataTable.js.map +1 -1
  13. package/dist/admin/components/FormSheet.js +7 -7
  14. package/dist/admin/components/FormSheet.js.map +1 -1
  15. package/dist/admin/components/MetricCard.js +4 -4
  16. package/dist/admin/components/MetricCard.js.map +1 -1
  17. package/dist/admin/components/PageLayout.js +3 -3
  18. package/dist/admin/components/PageLayout.js.map +1 -1
  19. package/dist/admin/components/PermissionGuard.js +1 -1
  20. package/dist/admin/components/PermissionGuard.js.map +1 -1
  21. package/dist/admin/components/RowActions.js +2 -2
  22. package/dist/admin/components/RowActions.js.map +1 -1
  23. package/dist/admin/components/TenantMembersPanel.js +15 -15
  24. package/dist/admin/components/TenantMembersPanel.js.map +1 -1
  25. package/dist/admin/components/TenantSsoPanel.js +14 -14
  26. package/dist/admin/components/TenantSsoPanel.js.map +1 -1
  27. package/dist/admin/components/Toast.js +9 -9
  28. package/dist/admin/components/Toast.js.map +1 -1
  29. package/dist/admin/components/forms/SubscriptionForm.js +10 -10
  30. package/dist/admin/components/forms/SubscriptionForm.js.map +1 -1
  31. package/dist/admin/components/forms/TenantForm.js +8 -8
  32. package/dist/admin/components/forms/TenantForm.js.map +1 -1
  33. package/dist/admin/components/forms/TicketForm.js +2 -2
  34. package/dist/admin/components/forms/TicketForm.js.map +1 -1
  35. package/dist/admin/components/forms/UserForm.js +8 -8
  36. package/dist/admin/components/forms/UserForm.js.map +1 -1
  37. package/dist/admin/components/ui.js +16 -16
  38. package/dist/admin/components/ui.js.map +1 -1
  39. package/dist/admin/constants/index.js +8 -8
  40. package/dist/admin/constants/index.js.map +1 -1
  41. package/dist/admin/pages/AuditLogsPage.js +8 -8
  42. package/dist/admin/pages/AuditLogsPage.js.map +1 -1
  43. package/dist/admin/pages/CommunicationsPage.js +2 -2
  44. package/dist/admin/pages/CommunicationsPage.js.map +1 -1
  45. package/dist/admin/pages/ContentPagesPage.js +661 -0
  46. package/dist/admin/pages/ContentPagesPage.js.map +1 -0
  47. package/dist/admin/pages/FeatureFlagsPage.js +4 -4
  48. package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
  49. package/dist/admin/pages/OrganizationsListPage.js +6 -6
  50. package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
  51. package/dist/admin/pages/ProductsPage.js +7 -7
  52. package/dist/admin/pages/ProductsPage.js.map +1 -1
  53. package/dist/admin/pages/ReferralsPage.js +23 -23
  54. package/dist/admin/pages/ReferralsPage.js.map +1 -1
  55. package/dist/admin/pages/RolesPermissionsPage.js +8 -8
  56. package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
  57. package/dist/admin/pages/SREPage.js +8 -8
  58. package/dist/admin/pages/SREPage.js.map +1 -1
  59. package/dist/admin/pages/SandboxSettingsPage.js +20 -20
  60. package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
  61. package/dist/admin/pages/SettingsPage.js +20 -20
  62. package/dist/admin/pages/SettingsPage.js.map +1 -1
  63. package/dist/admin/pages/SyncFailuresPage.js +11 -11
  64. package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
  65. package/dist/admin/pages/TenantDetailPage.js +22 -22
  66. package/dist/admin/pages/TenantDetailPage.js.map +1 -1
  67. package/dist/admin/pages/TenantFormRoutePage.js +2 -2
  68. package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
  69. package/dist/admin/pages/TenantsListPage.js +5 -5
  70. package/dist/admin/pages/TenantsListPage.js.map +1 -1
  71. package/dist/admin/pages/UsersListPage.js +6 -6
  72. package/dist/admin/pages/UsersListPage.js.map +1 -1
  73. package/dist/admin/pages/WaitlistPage.js +7 -7
  74. package/dist/admin/pages/WaitlistPage.js.map +1 -1
  75. package/dist/admin/pages/WorkflowsPage.js +3 -3
  76. package/dist/admin/pages/WorkflowsPage.js.map +1 -1
  77. package/dist/admin/pages/WorkspacesListPage.js +8 -8
  78. package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
  79. package/dist/admin/pages/index.js +1 -0
  80. package/package.json +1 -1
@@ -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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"}
@@ -46,7 +46,7 @@ function x() {
46
46
  children: [
47
47
  /* @__PURE__ */ y(n, { children: [/* @__PURE__ */ y(a, { children: [/* @__PURE__ */ y("div", {
48
48
  className: "flex items-center gap-2",
49
- children: [/* @__PURE__ */ v(g, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ v(o, {
49
+ children: [/* @__PURE__ */ v(g, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ v(o, {
50
50
  className: "text-lg",
51
51
  children: w("admin.settings.general.title", "General")
52
52
  })]
@@ -78,7 +78,7 @@ function x() {
78
78
  className: "text-sm font-medium",
79
79
  children: w("admin.settings.general.defaultTimezone", "Default Timezone")
80
80
  }), /* @__PURE__ */ y("select", {
81
- className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
81
+ className: "h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm",
82
82
  children: [
83
83
  /* @__PURE__ */ v("option", {
84
84
  value: "UTC",
@@ -111,7 +111,7 @@ function x() {
111
111
  })] }),
112
112
  /* @__PURE__ */ y(n, { children: [/* @__PURE__ */ y(a, { children: [/* @__PURE__ */ y("div", {
113
113
  className: "flex items-center gap-2",
114
- children: [/* @__PURE__ */ v(_, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ v(o, {
114
+ children: [/* @__PURE__ */ v(_, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ v(o, {
115
115
  className: "text-lg",
116
116
  children: w("admin.settings.security.title", "Security")
117
117
  })]
@@ -119,12 +119,12 @@ function x() {
119
119
  className: "space-y-4",
120
120
  children: [
121
121
  /* @__PURE__ */ y("div", {
122
- className: "flex items-center justify-between rounded-lg border border-border p-4",
122
+ className: "flex items-center justify-between rounded-lg border border-border-subtle p-4",
123
123
  children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("div", {
124
124
  className: "font-medium",
125
125
  children: w("admin.settings.security.twoFactor.title", "Two-Factor Authentication")
126
126
  }), /* @__PURE__ */ v("div", {
127
- className: "text-sm text-muted-foreground",
127
+ className: "text-sm text-text-secondary",
128
128
  children: w("admin.settings.security.twoFactor.description", "Require 2FA for all admin accounts")
129
129
  })] }), /* @__PURE__ */ v(t, {
130
130
  variant: "outline",
@@ -132,15 +132,15 @@ function x() {
132
132
  })]
133
133
  }),
134
134
  /* @__PURE__ */ y("div", {
135
- className: "flex items-center justify-between rounded-lg border border-border p-4",
135
+ className: "flex items-center justify-between rounded-lg border border-border-subtle p-4",
136
136
  children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("div", {
137
137
  className: "font-medium",
138
138
  children: w("admin.settings.security.sessionTimeout.title", "Session Timeout")
139
139
  }), /* @__PURE__ */ v("div", {
140
- className: "text-sm text-muted-foreground",
140
+ className: "text-sm text-text-secondary",
141
141
  children: w("admin.settings.security.sessionTimeout.description", "Auto-logout after inactivity")
142
142
  })] }), /* @__PURE__ */ y("select", {
143
- className: "h-9 rounded-md border border-input bg-background px-3 text-sm",
143
+ className: "h-9 rounded-md border border-border-default bg-bg-surface px-3 text-sm",
144
144
  children: [
145
145
  /* @__PURE__ */ v("option", {
146
146
  value: "15",
@@ -162,12 +162,12 @@ function x() {
162
162
  })]
163
163
  }),
164
164
  /* @__PURE__ */ y("div", {
165
- className: "flex items-center justify-between rounded-lg border border-border p-4",
165
+ className: "flex items-center justify-between rounded-lg border border-border-subtle p-4",
166
166
  children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("div", {
167
167
  className: "font-medium",
168
168
  children: w("admin.settings.security.ipAllowlist.title", "IP Allowlist")
169
169
  }), /* @__PURE__ */ v("div", {
170
- className: "text-sm text-muted-foreground",
170
+ className: "text-sm text-text-secondary",
171
171
  children: w("admin.settings.security.ipAllowlist.description", "Restrict access to specific IPs")
172
172
  })] }), /* @__PURE__ */ v(t, {
173
173
  variant: "outline",
@@ -178,19 +178,19 @@ function x() {
178
178
  })] }),
179
179
  /* @__PURE__ */ y(n, { children: [/* @__PURE__ */ y(a, { children: [/* @__PURE__ */ y("div", {
180
180
  className: "flex items-center gap-2",
181
- children: [/* @__PURE__ */ v(f, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ v(o, {
181
+ children: [/* @__PURE__ */ v(f, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ v(o, {
182
182
  className: "text-lg",
183
183
  children: w("admin.settings.notifications.title", "Notifications")
184
184
  })]
185
185
  }), /* @__PURE__ */ v(i, { children: w("admin.settings.notifications.description", "Configure notification preferences") })] }), /* @__PURE__ */ v(r, {
186
186
  className: "space-y-4",
187
187
  children: T.map((e) => /* @__PURE__ */ y("div", {
188
- className: "flex items-center justify-between rounded-lg border border-border p-4",
188
+ className: "flex items-center justify-between rounded-lg border border-border-subtle p-4",
189
189
  children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("div", {
190
190
  className: "font-medium",
191
191
  children: e.label
192
192
  }), /* @__PURE__ */ v("div", {
193
- className: "text-sm text-muted-foreground",
193
+ className: "text-sm text-text-secondary",
194
194
  children: e.description
195
195
  })] }), /* @__PURE__ */ y("label", {
196
196
  className: "relative inline-flex cursor-pointer items-center",
@@ -198,7 +198,7 @@ function x() {
198
198
  type: "checkbox",
199
199
  defaultChecked: !0,
200
200
  className: "peer sr-only"
201
- }), /* @__PURE__ */ v("div", { className: "peer h-6 w-11 rounded-full bg-muted after:absolute after:left-[2px] after:top-0.5 after:h-5 after:w-5 after:rounded-full after:bg-background after:transition-all peer-checked:bg-primary peer-checked:after:translate-x-full peer-focus:ring-2 peer-focus:ring-ring" })]
201
+ }), /* @__PURE__ */ v("div", { className: "peer h-6 w-11 rounded-full bg-bg-sunken after:absolute after:left-[2px] after:top-0.5 after:h-5 after:w-5 after:rounded-full after:bg-bg-surface after:transition-all peer-checked:bg-action-primary-bg peer-checked:after:translate-x-full peer-focus:ring-2 peer-focus:ring-[var(--color-focus-ring)]" })]
202
202
  })]
203
203
  }, e.key))
204
204
  })] })
@@ -208,14 +208,14 @@ function x() {
208
208
  children: [
209
209
  /* @__PURE__ */ y(n, { children: [/* @__PURE__ */ v(a, { children: /* @__PURE__ */ y("div", {
210
210
  className: "flex items-center gap-2",
211
- children: [/* @__PURE__ */ v(h, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ v(o, {
211
+ children: [/* @__PURE__ */ v(h, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ v(o, {
212
212
  className: "text-lg",
213
213
  children: w("admin.settings.apiKeys.title", "API Keys")
214
214
  })]
215
215
  }) }), /* @__PURE__ */ y(r, {
216
216
  className: "space-y-3",
217
217
  children: [/* @__PURE__ */ v("p", {
218
- className: "text-sm text-muted-foreground",
218
+ className: "text-sm text-text-secondary",
219
219
  children: w("admin.settings.apiKeys.description", "Manage API keys for programmatic access to the admin API.")
220
220
  }), /* @__PURE__ */ v(t, {
221
221
  variant: "outline",
@@ -225,14 +225,14 @@ function x() {
225
225
  })] }),
226
226
  /* @__PURE__ */ y(n, { children: [/* @__PURE__ */ v(a, { children: /* @__PURE__ */ y("div", {
227
227
  className: "flex items-center gap-2",
228
- children: [/* @__PURE__ */ v(m, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ v(o, {
228
+ children: [/* @__PURE__ */ v(m, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ v(o, {
229
229
  className: "text-lg",
230
230
  children: w("admin.settings.integrations.title", "Integrations")
231
231
  })]
232
232
  }) }), /* @__PURE__ */ y(r, {
233
233
  className: "space-y-3",
234
234
  children: [/* @__PURE__ */ v("p", {
235
- className: "text-sm text-muted-foreground",
235
+ className: "text-sm text-text-secondary",
236
236
  children: w("admin.settings.integrations.description", "Connect third-party services and tools.")
237
237
  }), /* @__PURE__ */ v(t, {
238
238
  variant: "outline",
@@ -242,7 +242,7 @@ function x() {
242
242
  })] }),
243
243
  /* @__PURE__ */ y(n, { children: [/* @__PURE__ */ v(a, { children: /* @__PURE__ */ y("div", {
244
244
  className: "flex items-center gap-2",
245
- children: [/* @__PURE__ */ v(p, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ v(o, {
245
+ children: [/* @__PURE__ */ v(p, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ v(o, {
246
246
  className: "text-lg",
247
247
  children: w("admin.settings.data.title", "Data")
248
248
  })]
@@ -262,7 +262,7 @@ function x() {
262
262
  /* @__PURE__ */ v(c, {}),
263
263
  /* @__PURE__ */ v(t, {
264
264
  variant: "outline",
265
- className: "w-full justify-start text-destructive",
265
+ className: "w-full justify-start text-status-error-text",
266
266
  children: w("admin.settings.data.dangerZone", "Danger Zone")
267
267
  })
268
268
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsPage.js","names":[],"sources":["../../../src/admin/pages/SettingsPage.tsx"],"sourcesContent":["import { Settings, Shield, Key, Bell, Globe, Database } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n PageLayout,\n Card,\n CardHeader,\n CardTitle,\n CardContent,\n CardDescription,\n Button,\n Input,\n Separator,\n PermissionGuard,\n} from '../components';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport { tWithFallback } from '../utils/i18n';\n\n/**\n * Settings page\n */\nexport function SettingsPage() {\n const { canManageSettings, isLoading: permissionsLoading } = useAdminPermissions();\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params);\n\n const notificationSettings = [\n {\n key: 'newTenantSignups',\n label: tr('admin.settings.notifications.items.newTenantSignups.label', 'New tenant signups'),\n description: tr(\n 'admin.settings.notifications.items.newTenantSignups.description',\n 'Get notified when new tenants register'\n ),\n },\n {\n key: 'paymentFailures',\n label: tr('admin.settings.notifications.items.paymentFailures.label', 'Payment failures'),\n description: tr(\n 'admin.settings.notifications.items.paymentFailures.description',\n 'Alert when payments fail'\n ),\n },\n {\n key: 'criticalTickets',\n label: tr('admin.settings.notifications.items.criticalTickets.label', 'Critical tickets'),\n description: tr(\n 'admin.settings.notifications.items.criticalTickets.description',\n 'Notify on critical support tickets'\n ),\n },\n {\n key: 'systemAlerts',\n label: tr('admin.settings.notifications.items.systemAlerts.label', 'System alerts'),\n description: tr(\n 'admin.settings.notifications.items.systemAlerts.description',\n 'Infrastructure and health alerts'\n ),\n },\n ];\n\n return (\n <PermissionGuard\n allowed={canManageSettings}\n isLoading={permissionsLoading}\n pageTitle={tr('admin.settings.title', 'Settings')}\n denyMessage={tr(\n 'admin.settings.denyMessage',\n \"You don't have permission to manage platform settings.\"\n )}\n >\n <PageLayout\n title={tr('admin.settings.title', 'Settings')}\n description={tr(\n 'admin.settings.description',\n 'Configure platform settings and preferences'\n )}\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 {/* General */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Settings className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-lg\">\n {tr('admin.settings.general.title', 'General')}\n </CardTitle>\n </div>\n <CardDescription>\n {tr('admin.settings.general.description', 'Basic platform configuration')}\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\">\n {tr('admin.settings.general.platformName', 'Platform Name')}\n </label>\n <Input defaultValue=\"Burdenoff Admin\" />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {tr('admin.settings.general.supportEmail', 'Support Email')}\n </label>\n <Input defaultValue=\"support@burdenoff.com\" type=\"email\" />\n </div>\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {tr('admin.settings.general.defaultTimezone', 'Default Timezone')}\n </label>\n <select className=\"h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\">\n <option value=\"UTC\">UTC</option>\n <option value=\"America/New_York\">\n {tr('admin.settings.general.timezones.eastern', 'Eastern Time (ET)')}\n </option>\n <option value=\"America/Los_Angeles\">\n {tr('admin.settings.general.timezones.pacific', 'Pacific Time (PT)')}\n </option>\n <option value=\"Europe/London\">\n {tr('admin.settings.general.timezones.london', 'London (GMT)')}\n </option>\n <option value=\"Asia/Kolkata\">\n {tr('admin.settings.general.timezones.india', 'India (IST)')}\n </option>\n </select>\n </div>\n <div className=\"flex justify-end\">\n <Button>{tr('admin.settings.general.saveChanges', 'Save Changes')}</Button>\n </div>\n </CardContent>\n </Card>\n\n {/* Security */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Shield className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-lg\">\n {tr('admin.settings.security.title', 'Security')}\n </CardTitle>\n </div>\n <CardDescription>\n {tr(\n 'admin.settings.security.description',\n 'Security and authentication settings'\n )}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex items-center justify-between rounded-lg border border-border p-4\">\n <div>\n <div className=\"font-medium\">\n {tr('admin.settings.security.twoFactor.title', 'Two-Factor Authentication')}\n </div>\n <div className=\"text-sm text-muted-foreground\">\n {tr(\n 'admin.settings.security.twoFactor.description',\n 'Require 2FA for all admin accounts'\n )}\n </div>\n </div>\n <Button variant=\"outline\">\n {tr('admin.settings.security.twoFactor.configure', 'Configure')}\n </Button>\n </div>\n <div className=\"flex items-center justify-between rounded-lg border border-border p-4\">\n <div>\n <div className=\"font-medium\">\n {tr('admin.settings.security.sessionTimeout.title', 'Session Timeout')}\n </div>\n <div className=\"text-sm text-muted-foreground\">\n {tr(\n 'admin.settings.security.sessionTimeout.description',\n 'Auto-logout after inactivity'\n )}\n </div>\n </div>\n <select className=\"h-9 rounded-md border border-input bg-background px-3 text-sm\">\n <option value=\"15\">\n {tr('admin.settings.security.sessionTimeout.options.15m', '15 minutes')}\n </option>\n <option value=\"30\">\n {tr('admin.settings.security.sessionTimeout.options.30m', '30 minutes')}\n </option>\n <option value=\"60\">\n {tr('admin.settings.security.sessionTimeout.options.1h', '1 hour')}\n </option>\n <option value=\"480\">\n {tr('admin.settings.security.sessionTimeout.options.8h', '8 hours')}\n </option>\n </select>\n </div>\n <div className=\"flex items-center justify-between rounded-lg border border-border p-4\">\n <div>\n <div className=\"font-medium\">\n {tr('admin.settings.security.ipAllowlist.title', 'IP Allowlist')}\n </div>\n <div className=\"text-sm text-muted-foreground\">\n {tr(\n 'admin.settings.security.ipAllowlist.description',\n 'Restrict access to specific IPs'\n )}\n </div>\n </div>\n <Button variant=\"outline\">\n {tr('admin.settings.security.ipAllowlist.manage', 'Manage')}\n </Button>\n </div>\n </CardContent>\n </Card>\n\n {/* Notifications */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Bell className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-lg\">\n {tr('admin.settings.notifications.title', 'Notifications')}\n </CardTitle>\n </div>\n <CardDescription>\n {tr(\n 'admin.settings.notifications.description',\n 'Configure notification preferences'\n )}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {notificationSettings.map((item) => (\n <div\n key={item.key}\n className=\"flex items-center justify-between rounded-lg border border-border p-4\"\n >\n <div>\n <div className=\"font-medium\">{item.label}</div>\n <div className=\"text-sm text-muted-foreground\">{item.description}</div>\n </div>\n <label className=\"relative inline-flex cursor-pointer items-center\">\n <input type=\"checkbox\" defaultChecked className=\"peer sr-only\" />\n <div className=\"peer h-6 w-11 rounded-full bg-muted after:absolute after:left-[2px] after:top-0.5 after:h-5 after:w-5 after:rounded-full after:bg-background after:transition-all peer-checked:bg-primary peer-checked:after:translate-x-full peer-focus:ring-2 peer-focus:ring-ring\" />\n </label>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* API Keys */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Key className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-lg\">\n {tr('admin.settings.apiKeys.title', 'API Keys')}\n </CardTitle>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'admin.settings.apiKeys.description',\n 'Manage API keys for programmatic access to the admin API.'\n )}\n </p>\n <Button variant=\"outline\" className=\"w-full\">\n {tr('admin.settings.apiKeys.manage', 'Manage API Keys')}\n </Button>\n </CardContent>\n </Card>\n\n {/* Integrations */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Globe className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-lg\">\n {tr('admin.settings.integrations.title', 'Integrations')}\n </CardTitle>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'admin.settings.integrations.description',\n 'Connect third-party services and tools.'\n )}\n </p>\n <Button variant=\"outline\" className=\"w-full\">\n {tr('admin.settings.integrations.view', 'View Integrations')}\n </Button>\n </CardContent>\n </Card>\n\n {/* Data Management */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Database className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-lg\">\n {tr('admin.settings.data.title', 'Data')}\n </CardTitle>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <Button variant=\"outline\" className=\"w-full justify-start\">\n {tr('admin.settings.data.export', 'Export Data')}\n </Button>\n <Button variant=\"outline\" className=\"w-full justify-start\">\n {tr('admin.settings.data.auditLogs', 'Audit Logs')}\n </Button>\n <Separator />\n <Button variant=\"outline\" className=\"w-full justify-start text-destructive\">\n {tr('admin.settings.data.dangerZone', 'Danger Zone')}\n </Button>\n </CardContent>\n </Card>\n </div>\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SettingsPage.js","names":[],"sources":["../../../src/admin/pages/SettingsPage.tsx"],"sourcesContent":["import { Settings, Shield, Key, Bell, Globe, Database } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n PageLayout,\n Card,\n CardHeader,\n CardTitle,\n CardContent,\n CardDescription,\n Button,\n Input,\n Separator,\n PermissionGuard,\n} from '../components';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport { tWithFallback } from '../utils/i18n';\n\n/**\n * Settings page\n */\nexport function SettingsPage() {\n const { canManageSettings, isLoading: permissionsLoading } = useAdminPermissions();\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params);\n\n const notificationSettings = [\n {\n key: 'newTenantSignups',\n label: tr('admin.settings.notifications.items.newTenantSignups.label', 'New tenant signups'),\n description: tr(\n 'admin.settings.notifications.items.newTenantSignups.description',\n 'Get notified when new tenants register'\n ),\n },\n {\n key: 'paymentFailures',\n label: tr('admin.settings.notifications.items.paymentFailures.label', 'Payment failures'),\n description: tr(\n 'admin.settings.notifications.items.paymentFailures.description',\n 'Alert when payments fail'\n ),\n },\n {\n key: 'criticalTickets',\n label: tr('admin.settings.notifications.items.criticalTickets.label', 'Critical tickets'),\n description: tr(\n 'admin.settings.notifications.items.criticalTickets.description',\n 'Notify on critical support tickets'\n ),\n },\n {\n key: 'systemAlerts',\n label: tr('admin.settings.notifications.items.systemAlerts.label', 'System alerts'),\n description: tr(\n 'admin.settings.notifications.items.systemAlerts.description',\n 'Infrastructure and health alerts'\n ),\n },\n ];\n\n return (\n <PermissionGuard\n allowed={canManageSettings}\n isLoading={permissionsLoading}\n pageTitle={tr('admin.settings.title', 'Settings')}\n denyMessage={tr(\n 'admin.settings.denyMessage',\n \"You don't have permission to manage platform settings.\"\n )}\n >\n <PageLayout\n title={tr('admin.settings.title', 'Settings')}\n description={tr(\n 'admin.settings.description',\n 'Configure platform settings and preferences'\n )}\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 {/* General */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Settings className=\"h-5 w-5 text-text-secondary\" />\n <CardTitle className=\"text-lg\">\n {tr('admin.settings.general.title', 'General')}\n </CardTitle>\n </div>\n <CardDescription>\n {tr('admin.settings.general.description', 'Basic platform configuration')}\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\">\n {tr('admin.settings.general.platformName', 'Platform Name')}\n </label>\n <Input defaultValue=\"Burdenoff Admin\" />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {tr('admin.settings.general.supportEmail', 'Support Email')}\n </label>\n <Input defaultValue=\"support@burdenoff.com\" type=\"email\" />\n </div>\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {tr('admin.settings.general.defaultTimezone', 'Default Timezone')}\n </label>\n <select className=\"h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\">\n <option value=\"UTC\">UTC</option>\n <option value=\"America/New_York\">\n {tr('admin.settings.general.timezones.eastern', 'Eastern Time (ET)')}\n </option>\n <option value=\"America/Los_Angeles\">\n {tr('admin.settings.general.timezones.pacific', 'Pacific Time (PT)')}\n </option>\n <option value=\"Europe/London\">\n {tr('admin.settings.general.timezones.london', 'London (GMT)')}\n </option>\n <option value=\"Asia/Kolkata\">\n {tr('admin.settings.general.timezones.india', 'India (IST)')}\n </option>\n </select>\n </div>\n <div className=\"flex justify-end\">\n <Button>{tr('admin.settings.general.saveChanges', 'Save Changes')}</Button>\n </div>\n </CardContent>\n </Card>\n\n {/* Security */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Shield className=\"h-5 w-5 text-text-secondary\" />\n <CardTitle className=\"text-lg\">\n {tr('admin.settings.security.title', 'Security')}\n </CardTitle>\n </div>\n <CardDescription>\n {tr(\n 'admin.settings.security.description',\n 'Security and authentication settings'\n )}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex items-center justify-between rounded-lg border border-border-subtle p-4\">\n <div>\n <div className=\"font-medium\">\n {tr('admin.settings.security.twoFactor.title', 'Two-Factor Authentication')}\n </div>\n <div className=\"text-sm text-text-secondary\">\n {tr(\n 'admin.settings.security.twoFactor.description',\n 'Require 2FA for all admin accounts'\n )}\n </div>\n </div>\n <Button variant=\"outline\">\n {tr('admin.settings.security.twoFactor.configure', 'Configure')}\n </Button>\n </div>\n <div className=\"flex items-center justify-between rounded-lg border border-border-subtle p-4\">\n <div>\n <div className=\"font-medium\">\n {tr('admin.settings.security.sessionTimeout.title', 'Session Timeout')}\n </div>\n <div className=\"text-sm text-text-secondary\">\n {tr(\n 'admin.settings.security.sessionTimeout.description',\n 'Auto-logout after inactivity'\n )}\n </div>\n </div>\n <select className=\"h-9 rounded-md border border-border-default bg-bg-surface px-3 text-sm\">\n <option value=\"15\">\n {tr('admin.settings.security.sessionTimeout.options.15m', '15 minutes')}\n </option>\n <option value=\"30\">\n {tr('admin.settings.security.sessionTimeout.options.30m', '30 minutes')}\n </option>\n <option value=\"60\">\n {tr('admin.settings.security.sessionTimeout.options.1h', '1 hour')}\n </option>\n <option value=\"480\">\n {tr('admin.settings.security.sessionTimeout.options.8h', '8 hours')}\n </option>\n </select>\n </div>\n <div className=\"flex items-center justify-between rounded-lg border border-border-subtle p-4\">\n <div>\n <div className=\"font-medium\">\n {tr('admin.settings.security.ipAllowlist.title', 'IP Allowlist')}\n </div>\n <div className=\"text-sm text-text-secondary\">\n {tr(\n 'admin.settings.security.ipAllowlist.description',\n 'Restrict access to specific IPs'\n )}\n </div>\n </div>\n <Button variant=\"outline\">\n {tr('admin.settings.security.ipAllowlist.manage', 'Manage')}\n </Button>\n </div>\n </CardContent>\n </Card>\n\n {/* Notifications */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Bell className=\"h-5 w-5 text-text-secondary\" />\n <CardTitle className=\"text-lg\">\n {tr('admin.settings.notifications.title', 'Notifications')}\n </CardTitle>\n </div>\n <CardDescription>\n {tr(\n 'admin.settings.notifications.description',\n 'Configure notification preferences'\n )}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {notificationSettings.map((item) => (\n <div\n key={item.key}\n className=\"flex items-center justify-between rounded-lg border border-border-subtle p-4\"\n >\n <div>\n <div className=\"font-medium\">{item.label}</div>\n <div className=\"text-sm text-text-secondary\">{item.description}</div>\n </div>\n <label className=\"relative inline-flex cursor-pointer items-center\">\n <input type=\"checkbox\" defaultChecked className=\"peer sr-only\" />\n <div className=\"peer h-6 w-11 rounded-full bg-bg-sunken after:absolute after:left-[2px] after:top-0.5 after:h-5 after:w-5 after:rounded-full after:bg-bg-surface after:transition-all peer-checked:bg-action-primary-bg peer-checked:after:translate-x-full peer-focus:ring-2 peer-focus:ring-[var(--color-focus-ring)]\" />\n </label>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* API Keys */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Key className=\"h-5 w-5 text-text-secondary\" />\n <CardTitle className=\"text-lg\">\n {tr('admin.settings.apiKeys.title', 'API Keys')}\n </CardTitle>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'admin.settings.apiKeys.description',\n 'Manage API keys for programmatic access to the admin API.'\n )}\n </p>\n <Button variant=\"outline\" className=\"w-full\">\n {tr('admin.settings.apiKeys.manage', 'Manage API Keys')}\n </Button>\n </CardContent>\n </Card>\n\n {/* Integrations */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Globe className=\"h-5 w-5 text-text-secondary\" />\n <CardTitle className=\"text-lg\">\n {tr('admin.settings.integrations.title', 'Integrations')}\n </CardTitle>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'admin.settings.integrations.description',\n 'Connect third-party services and tools.'\n )}\n </p>\n <Button variant=\"outline\" className=\"w-full\">\n {tr('admin.settings.integrations.view', 'View Integrations')}\n </Button>\n </CardContent>\n </Card>\n\n {/* Data Management */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Database className=\"h-5 w-5 text-text-secondary\" />\n <CardTitle className=\"text-lg\">\n {tr('admin.settings.data.title', 'Data')}\n </CardTitle>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <Button variant=\"outline\" className=\"w-full justify-start\">\n {tr('admin.settings.data.export', 'Export Data')}\n </Button>\n <Button variant=\"outline\" className=\"w-full justify-start\">\n {tr('admin.settings.data.auditLogs', 'Audit Logs')}\n </Button>\n <Separator />\n <Button variant=\"outline\" className=\"w-full justify-start text-status-error-text\">\n {tr('admin.settings.data.dangerZone', 'Danger Zone')}\n </Button>\n </CardContent>\n </Card>\n </div>\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
@@ -33,7 +33,7 @@ var j = {
33
33
  PENDING: "bg-warning/10 text-warning border-warning/30",
34
34
  RETRYING: "bg-warning/10 text-warning border-warning/30",
35
35
  SUCCESS: "bg-success/10 text-success border-success/30",
36
- PERMANENTLY_FAILED: "bg-destructive/10 text-destructive border-destructive/30"
36
+ PERMANENTLY_FAILED: "bg-status-error-bg/10 text-status-error-text border-status-error-border/30"
37
37
  }, M = {
38
38
  PENDING: "Pending",
39
39
  RETRYING: "Retrying",
@@ -84,7 +84,7 @@ function N() {
84
84
  header: "Operation",
85
85
  width: "180px",
86
86
  render: (e) => /* @__PURE__ */ T("code", {
87
- className: "text-xs bg-muted px-1.5 py-0.5 rounded",
87
+ className: "text-xs bg-bg-sunken px-1.5 py-0.5 rounded",
88
88
  children: e.operation
89
89
  })
90
90
  },
@@ -92,7 +92,7 @@ function N() {
92
92
  key: "workspaceId",
93
93
  header: "Workspace",
94
94
  render: (e) => /* @__PURE__ */ T("span", {
95
- className: "font-mono text-xs text-muted-foreground",
95
+ className: "font-mono text-xs text-text-secondary",
96
96
  children: e.workspaceId
97
97
  })
98
98
  },
@@ -110,7 +110,7 @@ function N() {
110
110
  render: (e) => /* @__PURE__ */ E("span", {
111
111
  className: "text-sm",
112
112
  children: [e.attempts, /* @__PURE__ */ E("span", {
113
- className: "text-muted-foreground",
113
+ className: "text-text-secondary",
114
114
  children: ["/", e.maxAttempts]
115
115
  })]
116
116
  })
@@ -120,10 +120,10 @@ function N() {
120
120
  header: "Next retry",
121
121
  width: "160px",
122
122
  render: (e) => e.nextRetryAt ? /* @__PURE__ */ T("span", {
123
- className: "text-sm text-muted-foreground",
123
+ className: "text-sm text-text-secondary",
124
124
  children: l(e.nextRetryAt)
125
125
  }) : /* @__PURE__ */ T("span", {
126
- className: "text-xs text-muted-foreground",
126
+ className: "text-xs text-text-secondary",
127
127
  children: "—"
128
128
  })
129
129
  },
@@ -132,7 +132,7 @@ function N() {
132
132
  header: "Created",
133
133
  width: "160px",
134
134
  render: (e) => /* @__PURE__ */ T("span", {
135
- className: "text-sm text-muted-foreground",
135
+ className: "text-sm text-text-secondary",
136
136
  children: l(e.createdAt)
137
137
  })
138
138
  },
@@ -221,7 +221,7 @@ function N() {
221
221
  }, e))
222
222
  }),
223
223
  W && /* @__PURE__ */ T(i, { children: /* @__PURE__ */ E(a, {
224
- className: "py-4 text-sm text-destructive",
224
+ className: "py-4 text-sm text-status-error-text",
225
225
  children: ["Failed to load sync outbox entries: ", W.message]
226
226
  }) }),
227
227
  /* @__PURE__ */ T(f, {
@@ -245,16 +245,16 @@ function N() {
245
245
  className: "font-medium",
246
246
  children: "Last error"
247
247
  }), /* @__PURE__ */ T("span", {
248
- className: "text-xs text-muted-foreground",
248
+ className: "text-xs text-text-secondary",
249
249
  children: e.lastAttemptAt ? `Last attempt: ${c(e.lastAttemptAt)}` : "Never attempted"
250
250
  })]
251
251
  }),
252
252
  /* @__PURE__ */ T("pre", {
253
- className: "whitespace-pre-wrap break-words rounded bg-muted px-3 py-2 text-xs text-muted-foreground",
253
+ className: "whitespace-pre-wrap break-words rounded bg-bg-sunken px-3 py-2 text-xs text-text-secondary",
254
254
  children: e.lastError ?? "No error recorded"
255
255
  }),
256
256
  /* @__PURE__ */ E("div", {
257
- className: "text-xs text-muted-foreground",
257
+ className: "text-xs text-text-secondary",
258
258
  children: ["Entry id: ", /* @__PURE__ */ T("code", {
259
259
  className: "font-mono",
260
260
  children: e.id