@burdenoff/microfe-admin 2026.624.1 → 2026.626.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.
- package/dist/admin/components/AccessDenied.js +2 -2
- package/dist/admin/components/AccessDenied.js.map +1 -1
- package/dist/admin/components/ActivityFeed.js +4 -4
- package/dist/admin/components/ActivityFeed.js.map +1 -1
- package/dist/admin/components/Charts.js.map +1 -1
- package/dist/admin/components/ConfirmDialog.js +1 -1
- package/dist/admin/components/ConfirmDialog.js.map +1 -1
- package/dist/admin/components/DataTable.js +5 -5
- package/dist/admin/components/DataTable.js.map +1 -1
- package/dist/admin/components/FormSheet.js +1 -1
- package/dist/admin/components/FormSheet.js.map +1 -1
- package/dist/admin/components/MetricCard.js +40 -37
- package/dist/admin/components/MetricCard.js.map +1 -1
- package/dist/admin/components/PageLayout.js +28 -22
- package/dist/admin/components/PageLayout.js.map +1 -1
- package/dist/admin/components/TenantSsoPanel.js +1 -1
- package/dist/admin/components/TenantSsoPanel.js.map +1 -1
- package/dist/admin/components/Toast.js +11 -11
- package/dist/admin/components/Toast.js.map +1 -1
- package/dist/admin/components/forms/SubscriptionForm.js +2 -2
- package/dist/admin/components/forms/SubscriptionForm.js.map +1 -1
- package/dist/admin/components/forms/TenantForm.js +1 -1
- package/dist/admin/components/forms/UserForm.js +1 -1
- package/dist/admin/components/ui.js +48 -41
- package/dist/admin/components/ui.js.map +1 -1
- package/dist/admin/constants/index.js +33 -24
- package/dist/admin/constants/index.js.map +1 -1
- package/dist/admin/pages/AuditLogsPage.js +94 -77
- package/dist/admin/pages/AuditLogsPage.js.map +1 -1
- package/dist/admin/pages/BillingPage.js +70 -18
- package/dist/admin/pages/BillingPage.js.map +1 -1
- package/dist/admin/pages/CommunicationsPage.js +98 -90
- package/dist/admin/pages/CommunicationsPage.js.map +1 -1
- package/dist/admin/pages/ContentPagesPage.js +276 -259
- package/dist/admin/pages/ContentPagesPage.js.map +1 -1
- package/dist/admin/pages/DashboardPage.js +114 -82
- package/dist/admin/pages/DashboardPage.js.map +1 -1
- package/dist/admin/pages/FeatureFlagsPage.js +94 -74
- package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
- package/dist/admin/pages/OrganizationsListPage.js +106 -86
- package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
- package/dist/admin/pages/ProductsPage.js +139 -128
- package/dist/admin/pages/ProductsPage.js.map +1 -1
- package/dist/admin/pages/ReferralsPage.js +74 -52
- package/dist/admin/pages/ReferralsPage.js.map +1 -1
- package/dist/admin/pages/RolesPermissionsPage.js +137 -116
- package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
- package/dist/admin/pages/SREPage.js +38 -32
- package/dist/admin/pages/SREPage.js.map +1 -1
- package/dist/admin/pages/SandboxSettingsPage.js +361 -322
- package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
- package/dist/admin/pages/SettingsPage.js +145 -130
- package/dist/admin/pages/SettingsPage.js.map +1 -1
- package/dist/admin/pages/SyncFailuresPage.js +123 -118
- package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
- package/dist/admin/pages/TenantDetailPage.js +261 -218
- package/dist/admin/pages/TenantDetailPage.js.map +1 -1
- package/dist/admin/pages/TenantFormRoutePage.js +65 -61
- package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
- package/dist/admin/pages/TenantsListPage.js +88 -71
- package/dist/admin/pages/TenantsListPage.js.map +1 -1
- package/dist/admin/pages/UsersListPage.js +129 -112
- package/dist/admin/pages/UsersListPage.js.map +1 -1
- package/dist/admin/pages/WaitlistPage.js +80 -63
- package/dist/admin/pages/WaitlistPage.js.map +1 -1
- package/dist/admin/pages/WorkflowsPage.js +108 -100
- package/dist/admin/pages/WorkflowsPage.js.map +1 -1
- package/dist/admin/pages/WorkspacesListPage.js +62 -49
- package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
- package/dist/node_modules/@hookform/resolvers/dist/resolvers.js +2 -2
- package/dist/node_modules/@hookform/resolvers/dist/resolvers.js.map +1 -1
- package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js +35 -26
- package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js.map +1 -1
- package/dist/node_modules/react-hook-form/dist/index.esm.js +609 -516
- package/dist/node_modules/react-hook-form/dist/index.esm.js.map +1 -1
- package/package.json +2 -2
|
@@ -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-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 >= 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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"}
|
|
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, 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 { PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\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 <PagePurpose className=\"mb-6\">\n Sandboxes are the disposable, isolated environments users spin up to safely run or\n preview their code without touching anything real. This page sets the platform-wide\n defaults for them — how long a sandbox lives before it's cleaned up, how much CPU\n and memory it gets, and how many a user or tenant may run at once. Tune these to balance\n a smooth experience against runaway resource costs.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"admin-sandbox\"\n steps={[\n {\n id: 'set-ttl',\n label: 'Set the sandbox TTL',\n description: 'Decide how long idle sandboxes live before automatic cleanup.',\n href: '#ttl',\n },\n {\n id: 'set-limits',\n label: 'Cap CPU & memory',\n description: 'Set the resource ceiling each sandbox is allowed.',\n href: '#resources',\n },\n {\n id: 'save-config',\n label: 'Save your configuration',\n description: 'Apply these defaults across the whole platform.',\n onClick: () => void handleSave(),\n },\n ]}\n />\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\" hero 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 id=\"ttl\">\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 >= 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 id=\"resources\">\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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"}
|
|
@@ -6,157 +6,168 @@ import { PermissionGuard as d } from "../components/PermissionGuard.js";
|
|
|
6
6
|
import "../components/index.js";
|
|
7
7
|
import { Bell as f, Database as p, Globe as m, Key as h, Settings as g, Shield as _ } from "lucide-react";
|
|
8
8
|
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
9
|
-
import {
|
|
9
|
+
import { EmphasisPanel as b, PagePurpose as x, ShowMore as S } from "@burdenoff/fe-libs/ui";
|
|
10
|
+
import { useI18n as C } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
10
11
|
//#region src/admin/pages/SettingsPage.tsx
|
|
11
|
-
function
|
|
12
|
-
let { canManageSettings:
|
|
12
|
+
function w() {
|
|
13
|
+
let { canManageSettings: w, isLoading: T } = e(), { t: E } = C(), D = (e, t, n) => u(E, e, t, n), O = [
|
|
13
14
|
{
|
|
14
15
|
key: "newTenantSignups",
|
|
15
|
-
label:
|
|
16
|
-
description:
|
|
16
|
+
label: D("admin.settings.notifications.items.newTenantSignups.label", "New tenant signups"),
|
|
17
|
+
description: D("admin.settings.notifications.items.newTenantSignups.description", "Get notified when new tenants register")
|
|
17
18
|
},
|
|
18
19
|
{
|
|
19
20
|
key: "paymentFailures",
|
|
20
|
-
label:
|
|
21
|
-
description:
|
|
21
|
+
label: D("admin.settings.notifications.items.paymentFailures.label", "Payment failures"),
|
|
22
|
+
description: D("admin.settings.notifications.items.paymentFailures.description", "Alert when payments fail")
|
|
22
23
|
},
|
|
23
24
|
{
|
|
24
25
|
key: "criticalTickets",
|
|
25
|
-
label:
|
|
26
|
-
description:
|
|
26
|
+
label: D("admin.settings.notifications.items.criticalTickets.label", "Critical tickets"),
|
|
27
|
+
description: D("admin.settings.notifications.items.criticalTickets.description", "Notify on critical support tickets")
|
|
27
28
|
},
|
|
28
29
|
{
|
|
29
30
|
key: "systemAlerts",
|
|
30
|
-
label:
|
|
31
|
-
description:
|
|
31
|
+
label: D("admin.settings.notifications.items.systemAlerts.label", "System alerts"),
|
|
32
|
+
description: D("admin.settings.notifications.items.systemAlerts.description", "Infrastructure and health alerts")
|
|
32
33
|
}
|
|
33
34
|
];
|
|
34
35
|
return /* @__PURE__ */ v(d, {
|
|
35
|
-
allowed:
|
|
36
|
-
isLoading:
|
|
37
|
-
pageTitle:
|
|
38
|
-
denyMessage:
|
|
39
|
-
children: /* @__PURE__ */
|
|
40
|
-
title:
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
allowed: w,
|
|
37
|
+
isLoading: T,
|
|
38
|
+
pageTitle: D("admin.settings.title", "Settings"),
|
|
39
|
+
denyMessage: D("admin.settings.denyMessage", "You don't have permission to manage platform settings."),
|
|
40
|
+
children: /* @__PURE__ */ y(l, {
|
|
41
|
+
title: D("admin.settings.title", "Settings"),
|
|
42
|
+
gradientWord: D("admin.settings.title", "Settings"),
|
|
43
|
+
description: D("admin.settings.description", "Configure platform settings and preferences"),
|
|
44
|
+
children: [/* @__PURE__ */ v(x, {
|
|
45
|
+
className: "mb-6",
|
|
46
|
+
children: D("admin.settings.purpose", "Platform-wide configuration in one place — the general details every product inherits, security defaults, API access, and which events page you. Change these to set the baseline behaviour for the whole platform, not a single tenant.")
|
|
47
|
+
}), /* @__PURE__ */ y("div", {
|
|
43
48
|
className: "grid gap-6 lg:grid-cols-3",
|
|
44
49
|
children: [/* @__PURE__ */ y("div", {
|
|
45
50
|
className: "space-y-6 lg:col-span-2",
|
|
46
51
|
children: [
|
|
47
|
-
/* @__PURE__ */
|
|
48
|
-
className: "
|
|
49
|
-
children:
|
|
50
|
-
className: "
|
|
51
|
-
children:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
/* @__PURE__ */ y("div", {
|
|
57
|
-
className: "grid gap-4 sm:grid-cols-2",
|
|
58
|
-
children: [/* @__PURE__ */ y("div", {
|
|
59
|
-
className: "space-y-2",
|
|
60
|
-
children: [/* @__PURE__ */ v("label", {
|
|
61
|
-
className: "text-sm font-medium",
|
|
62
|
-
children: w("admin.settings.general.platformName", "Platform Name")
|
|
63
|
-
}), /* @__PURE__ */ v(s, { defaultValue: "Burdenoff Admin" })]
|
|
64
|
-
}), /* @__PURE__ */ y("div", {
|
|
65
|
-
className: "space-y-2",
|
|
66
|
-
children: [/* @__PURE__ */ v("label", {
|
|
67
|
-
className: "text-sm font-medium",
|
|
68
|
-
children: w("admin.settings.general.supportEmail", "Support Email")
|
|
69
|
-
}), /* @__PURE__ */ v(s, {
|
|
70
|
-
defaultValue: "support@burdenoff.com",
|
|
71
|
-
type: "email"
|
|
72
|
-
})]
|
|
52
|
+
/* @__PURE__ */ v(b, {
|
|
53
|
+
className: "p-0",
|
|
54
|
+
children: /* @__PURE__ */ y(n, {
|
|
55
|
+
className: "border-0 bg-transparent shadow-none hover:border-0",
|
|
56
|
+
children: [/* @__PURE__ */ y(a, { children: [/* @__PURE__ */ y("div", {
|
|
57
|
+
className: "flex items-center gap-2",
|
|
58
|
+
children: [/* @__PURE__ */ v(g, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ v(o, {
|
|
59
|
+
className: "text-lg",
|
|
60
|
+
children: D("admin.settings.general.title", "General")
|
|
73
61
|
})]
|
|
74
|
-
}),
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
62
|
+
}), /* @__PURE__ */ v(i, { children: D("admin.settings.general.description", "Basic platform configuration") })] }), /* @__PURE__ */ y(r, {
|
|
63
|
+
className: "space-y-4",
|
|
64
|
+
children: [
|
|
65
|
+
/* @__PURE__ */ y("div", {
|
|
66
|
+
className: "grid gap-4 sm:grid-cols-2",
|
|
67
|
+
children: [/* @__PURE__ */ y("div", {
|
|
68
|
+
className: "space-y-2",
|
|
69
|
+
children: [/* @__PURE__ */ v("label", {
|
|
70
|
+
className: "text-sm font-medium",
|
|
71
|
+
children: D("admin.settings.general.platformName", "Platform Name")
|
|
72
|
+
}), /* @__PURE__ */ v(s, { defaultValue: "Burdenoff Admin" })]
|
|
73
|
+
}), /* @__PURE__ */ y("div", {
|
|
74
|
+
className: "space-y-2",
|
|
75
|
+
children: [/* @__PURE__ */ v("label", {
|
|
76
|
+
className: "text-sm font-medium",
|
|
77
|
+
children: D("admin.settings.general.supportEmail", "Support Email")
|
|
78
|
+
}), /* @__PURE__ */ v(s, {
|
|
79
|
+
defaultValue: "support@burdenoff.com",
|
|
80
|
+
type: "email"
|
|
81
|
+
})]
|
|
82
|
+
})]
|
|
83
|
+
}),
|
|
84
|
+
/* @__PURE__ */ y("div", {
|
|
85
|
+
className: "space-y-2",
|
|
86
|
+
children: [/* @__PURE__ */ v("label", {
|
|
87
|
+
className: "text-sm font-medium",
|
|
88
|
+
children: D("admin.settings.general.defaultTimezone", "Default Timezone")
|
|
89
|
+
}), /* @__PURE__ */ y("select", {
|
|
90
|
+
className: "h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm",
|
|
91
|
+
children: [
|
|
92
|
+
/* @__PURE__ */ v("option", {
|
|
93
|
+
value: "UTC",
|
|
94
|
+
children: "UTC"
|
|
95
|
+
}),
|
|
96
|
+
/* @__PURE__ */ v("option", {
|
|
97
|
+
value: "America/New_York",
|
|
98
|
+
children: D("admin.settings.general.timezones.eastern", "Eastern Time (ET)")
|
|
99
|
+
}),
|
|
100
|
+
/* @__PURE__ */ v("option", {
|
|
101
|
+
value: "America/Los_Angeles",
|
|
102
|
+
children: D("admin.settings.general.timezones.pacific", "Pacific Time (PT)")
|
|
103
|
+
}),
|
|
104
|
+
/* @__PURE__ */ v("option", {
|
|
105
|
+
value: "Europe/London",
|
|
106
|
+
children: D("admin.settings.general.timezones.london", "London (GMT)")
|
|
107
|
+
}),
|
|
108
|
+
/* @__PURE__ */ v("option", {
|
|
109
|
+
value: "Asia/Kolkata",
|
|
110
|
+
children: D("admin.settings.general.timezones.india", "India (IST)")
|
|
111
|
+
})
|
|
112
|
+
]
|
|
113
|
+
})]
|
|
114
|
+
}),
|
|
115
|
+
/* @__PURE__ */ v("div", {
|
|
116
|
+
className: "flex justify-end",
|
|
117
|
+
children: /* @__PURE__ */ v(t, { children: D("admin.settings.general.saveChanges", "Save Changes") })
|
|
118
|
+
})
|
|
119
|
+
]
|
|
120
|
+
})]
|
|
121
|
+
})
|
|
122
|
+
}),
|
|
112
123
|
/* @__PURE__ */ y(n, { children: [/* @__PURE__ */ y(a, { children: [/* @__PURE__ */ y("div", {
|
|
113
124
|
className: "flex items-center gap-2",
|
|
114
125
|
children: [/* @__PURE__ */ v(_, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ v(o, {
|
|
115
126
|
className: "text-lg",
|
|
116
|
-
children:
|
|
127
|
+
children: D("admin.settings.security.title", "Security")
|
|
117
128
|
})]
|
|
118
|
-
}), /* @__PURE__ */ v(i, { children:
|
|
129
|
+
}), /* @__PURE__ */ v(i, { children: D("admin.settings.security.description", "Security and authentication settings") })] }), /* @__PURE__ */ y(r, {
|
|
119
130
|
className: "space-y-4",
|
|
120
131
|
children: [
|
|
121
132
|
/* @__PURE__ */ y("div", {
|
|
122
133
|
className: "flex items-center justify-between rounded-lg border border-border-subtle p-4",
|
|
123
134
|
children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("div", {
|
|
124
135
|
className: "font-medium",
|
|
125
|
-
children:
|
|
136
|
+
children: D("admin.settings.security.twoFactor.title", "Two-Factor Authentication")
|
|
126
137
|
}), /* @__PURE__ */ v("div", {
|
|
127
138
|
className: "text-sm text-text-secondary",
|
|
128
|
-
children:
|
|
139
|
+
children: D("admin.settings.security.twoFactor.description", "Require 2FA for all admin accounts")
|
|
129
140
|
})] }), /* @__PURE__ */ v(t, {
|
|
130
141
|
variant: "outline",
|
|
131
|
-
children:
|
|
142
|
+
children: D("admin.settings.security.twoFactor.configure", "Configure")
|
|
132
143
|
})]
|
|
133
144
|
}),
|
|
134
145
|
/* @__PURE__ */ y("div", {
|
|
135
146
|
className: "flex items-center justify-between rounded-lg border border-border-subtle p-4",
|
|
136
147
|
children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("div", {
|
|
137
148
|
className: "font-medium",
|
|
138
|
-
children:
|
|
149
|
+
children: D("admin.settings.security.sessionTimeout.title", "Session Timeout")
|
|
139
150
|
}), /* @__PURE__ */ v("div", {
|
|
140
151
|
className: "text-sm text-text-secondary",
|
|
141
|
-
children:
|
|
152
|
+
children: D("admin.settings.security.sessionTimeout.description", "Auto-logout after inactivity")
|
|
142
153
|
})] }), /* @__PURE__ */ y("select", {
|
|
143
154
|
className: "h-9 rounded-md border border-border-default bg-bg-surface px-3 text-sm",
|
|
144
155
|
children: [
|
|
145
156
|
/* @__PURE__ */ v("option", {
|
|
146
157
|
value: "15",
|
|
147
|
-
children:
|
|
158
|
+
children: D("admin.settings.security.sessionTimeout.options.15m", "15 minutes")
|
|
148
159
|
}),
|
|
149
160
|
/* @__PURE__ */ v("option", {
|
|
150
161
|
value: "30",
|
|
151
|
-
children:
|
|
162
|
+
children: D("admin.settings.security.sessionTimeout.options.30m", "30 minutes")
|
|
152
163
|
}),
|
|
153
164
|
/* @__PURE__ */ v("option", {
|
|
154
165
|
value: "60",
|
|
155
|
-
children:
|
|
166
|
+
children: D("admin.settings.security.sessionTimeout.options.1h", "1 hour")
|
|
156
167
|
}),
|
|
157
168
|
/* @__PURE__ */ v("option", {
|
|
158
169
|
value: "480",
|
|
159
|
-
children:
|
|
170
|
+
children: D("admin.settings.security.sessionTimeout.options.8h", "8 hours")
|
|
160
171
|
})
|
|
161
172
|
]
|
|
162
173
|
})]
|
|
@@ -165,13 +176,13 @@ function x() {
|
|
|
165
176
|
className: "flex items-center justify-between rounded-lg border border-border-subtle p-4",
|
|
166
177
|
children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("div", {
|
|
167
178
|
className: "font-medium",
|
|
168
|
-
children:
|
|
179
|
+
children: D("admin.settings.security.ipAllowlist.title", "IP Allowlist")
|
|
169
180
|
}), /* @__PURE__ */ v("div", {
|
|
170
181
|
className: "text-sm text-text-secondary",
|
|
171
|
-
children:
|
|
182
|
+
children: D("admin.settings.security.ipAllowlist.description", "Restrict access to specific IPs")
|
|
172
183
|
})] }), /* @__PURE__ */ v(t, {
|
|
173
184
|
variant: "outline",
|
|
174
|
-
children:
|
|
185
|
+
children: D("admin.settings.security.ipAllowlist.manage", "Manage")
|
|
175
186
|
})]
|
|
176
187
|
})
|
|
177
188
|
]
|
|
@@ -180,28 +191,32 @@ function x() {
|
|
|
180
191
|
className: "flex items-center gap-2",
|
|
181
192
|
children: [/* @__PURE__ */ v(f, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ v(o, {
|
|
182
193
|
className: "text-lg",
|
|
183
|
-
children:
|
|
194
|
+
children: D("admin.settings.notifications.title", "Notifications")
|
|
184
195
|
})]
|
|
185
|
-
}), /* @__PURE__ */ v(i, { children:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
196
|
+
}), /* @__PURE__ */ v(i, { children: D("admin.settings.notifications.description", "Configure notification preferences") })] }), /* @__PURE__ */ v(r, { children: /* @__PURE__ */ v(S, {
|
|
197
|
+
collapsedHeight: 180,
|
|
198
|
+
label: "More notification settings",
|
|
199
|
+
children: /* @__PURE__ */ v("div", {
|
|
200
|
+
className: "space-y-4",
|
|
201
|
+
children: O.map((e) => /* @__PURE__ */ y("div", {
|
|
202
|
+
className: "flex items-center justify-between rounded-lg border border-border-seam p-4",
|
|
203
|
+
children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("div", {
|
|
204
|
+
className: "font-medium",
|
|
205
|
+
children: e.label
|
|
206
|
+
}), /* @__PURE__ */ v("div", {
|
|
207
|
+
className: "text-sm text-text-secondary",
|
|
208
|
+
children: e.description
|
|
209
|
+
})] }), /* @__PURE__ */ y("label", {
|
|
210
|
+
className: "relative inline-flex cursor-pointer items-center",
|
|
211
|
+
children: [/* @__PURE__ */ v("input", {
|
|
212
|
+
type: "checkbox",
|
|
213
|
+
defaultChecked: !0,
|
|
214
|
+
className: "peer sr-only"
|
|
215
|
+
}), /* @__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)]" })]
|
|
216
|
+
})]
|
|
217
|
+
}, e.key))
|
|
218
|
+
})
|
|
219
|
+
}) })] })
|
|
205
220
|
]
|
|
206
221
|
}), /* @__PURE__ */ y("div", {
|
|
207
222
|
className: "space-y-6",
|
|
@@ -210,41 +225,41 @@ function x() {
|
|
|
210
225
|
className: "flex items-center gap-2",
|
|
211
226
|
children: [/* @__PURE__ */ v(h, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ v(o, {
|
|
212
227
|
className: "text-lg",
|
|
213
|
-
children:
|
|
228
|
+
children: D("admin.settings.apiKeys.title", "API Keys")
|
|
214
229
|
})]
|
|
215
230
|
}) }), /* @__PURE__ */ y(r, {
|
|
216
231
|
className: "space-y-3",
|
|
217
232
|
children: [/* @__PURE__ */ v("p", {
|
|
218
233
|
className: "text-sm text-text-secondary",
|
|
219
|
-
children:
|
|
234
|
+
children: D("admin.settings.apiKeys.description", "Manage API keys for programmatic access to the admin API.")
|
|
220
235
|
}), /* @__PURE__ */ v(t, {
|
|
221
236
|
variant: "outline",
|
|
222
237
|
className: "w-full",
|
|
223
|
-
children:
|
|
238
|
+
children: D("admin.settings.apiKeys.manage", "Manage API Keys")
|
|
224
239
|
})]
|
|
225
240
|
})] }),
|
|
226
241
|
/* @__PURE__ */ y(n, { children: [/* @__PURE__ */ v(a, { children: /* @__PURE__ */ y("div", {
|
|
227
242
|
className: "flex items-center gap-2",
|
|
228
243
|
children: [/* @__PURE__ */ v(m, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ v(o, {
|
|
229
244
|
className: "text-lg",
|
|
230
|
-
children:
|
|
245
|
+
children: D("admin.settings.integrations.title", "Integrations")
|
|
231
246
|
})]
|
|
232
247
|
}) }), /* @__PURE__ */ y(r, {
|
|
233
248
|
className: "space-y-3",
|
|
234
249
|
children: [/* @__PURE__ */ v("p", {
|
|
235
250
|
className: "text-sm text-text-secondary",
|
|
236
|
-
children:
|
|
251
|
+
children: D("admin.settings.integrations.description", "Connect third-party services and tools.")
|
|
237
252
|
}), /* @__PURE__ */ v(t, {
|
|
238
253
|
variant: "outline",
|
|
239
254
|
className: "w-full",
|
|
240
|
-
children:
|
|
255
|
+
children: D("admin.settings.integrations.view", "View Integrations")
|
|
241
256
|
})]
|
|
242
257
|
})] }),
|
|
243
258
|
/* @__PURE__ */ y(n, { children: [/* @__PURE__ */ v(a, { children: /* @__PURE__ */ y("div", {
|
|
244
259
|
className: "flex items-center gap-2",
|
|
245
260
|
children: [/* @__PURE__ */ v(p, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ v(o, {
|
|
246
261
|
className: "text-lg",
|
|
247
|
-
children:
|
|
262
|
+
children: D("admin.settings.data.title", "Data")
|
|
248
263
|
})]
|
|
249
264
|
}) }), /* @__PURE__ */ y(r, {
|
|
250
265
|
className: "space-y-3",
|
|
@@ -252,28 +267,28 @@ function x() {
|
|
|
252
267
|
/* @__PURE__ */ v(t, {
|
|
253
268
|
variant: "outline",
|
|
254
269
|
className: "w-full justify-start",
|
|
255
|
-
children:
|
|
270
|
+
children: D("admin.settings.data.export", "Export Data")
|
|
256
271
|
}),
|
|
257
272
|
/* @__PURE__ */ v(t, {
|
|
258
273
|
variant: "outline",
|
|
259
274
|
className: "w-full justify-start",
|
|
260
|
-
children:
|
|
275
|
+
children: D("admin.settings.data.auditLogs", "Audit Logs")
|
|
261
276
|
}),
|
|
262
277
|
/* @__PURE__ */ v(c, {}),
|
|
263
278
|
/* @__PURE__ */ v(t, {
|
|
264
279
|
variant: "outline",
|
|
265
280
|
className: "w-full justify-start text-status-error-text",
|
|
266
|
-
children:
|
|
281
|
+
children: D("admin.settings.data.dangerZone", "Danger Zone")
|
|
267
282
|
})
|
|
268
283
|
]
|
|
269
284
|
})] })
|
|
270
285
|
]
|
|
271
286
|
})]
|
|
272
|
-
})
|
|
287
|
+
})]
|
|
273
288
|
})
|
|
274
289
|
});
|
|
275
290
|
}
|
|
276
291
|
//#endregion
|
|
277
|
-
export {
|
|
292
|
+
export { w as SettingsPage };
|
|
278
293
|
|
|
279
294
|
//# sourceMappingURL=SettingsPage.js.map
|