@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.
Files changed (76) hide show
  1. package/dist/admin/components/AccessDenied.js +2 -2
  2. package/dist/admin/components/AccessDenied.js.map +1 -1
  3. package/dist/admin/components/ActivityFeed.js +4 -4
  4. package/dist/admin/components/ActivityFeed.js.map +1 -1
  5. package/dist/admin/components/Charts.js.map +1 -1
  6. package/dist/admin/components/ConfirmDialog.js +1 -1
  7. package/dist/admin/components/ConfirmDialog.js.map +1 -1
  8. package/dist/admin/components/DataTable.js +5 -5
  9. package/dist/admin/components/DataTable.js.map +1 -1
  10. package/dist/admin/components/FormSheet.js +1 -1
  11. package/dist/admin/components/FormSheet.js.map +1 -1
  12. package/dist/admin/components/MetricCard.js +40 -37
  13. package/dist/admin/components/MetricCard.js.map +1 -1
  14. package/dist/admin/components/PageLayout.js +28 -22
  15. package/dist/admin/components/PageLayout.js.map +1 -1
  16. package/dist/admin/components/TenantSsoPanel.js +1 -1
  17. package/dist/admin/components/TenantSsoPanel.js.map +1 -1
  18. package/dist/admin/components/Toast.js +11 -11
  19. package/dist/admin/components/Toast.js.map +1 -1
  20. package/dist/admin/components/forms/SubscriptionForm.js +2 -2
  21. package/dist/admin/components/forms/SubscriptionForm.js.map +1 -1
  22. package/dist/admin/components/forms/TenantForm.js +1 -1
  23. package/dist/admin/components/forms/UserForm.js +1 -1
  24. package/dist/admin/components/ui.js +48 -41
  25. package/dist/admin/components/ui.js.map +1 -1
  26. package/dist/admin/constants/index.js +33 -24
  27. package/dist/admin/constants/index.js.map +1 -1
  28. package/dist/admin/pages/AuditLogsPage.js +94 -77
  29. package/dist/admin/pages/AuditLogsPage.js.map +1 -1
  30. package/dist/admin/pages/BillingPage.js +70 -18
  31. package/dist/admin/pages/BillingPage.js.map +1 -1
  32. package/dist/admin/pages/CommunicationsPage.js +98 -90
  33. package/dist/admin/pages/CommunicationsPage.js.map +1 -1
  34. package/dist/admin/pages/ContentPagesPage.js +276 -259
  35. package/dist/admin/pages/ContentPagesPage.js.map +1 -1
  36. package/dist/admin/pages/DashboardPage.js +114 -82
  37. package/dist/admin/pages/DashboardPage.js.map +1 -1
  38. package/dist/admin/pages/FeatureFlagsPage.js +94 -74
  39. package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
  40. package/dist/admin/pages/OrganizationsListPage.js +106 -86
  41. package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
  42. package/dist/admin/pages/ProductsPage.js +139 -128
  43. package/dist/admin/pages/ProductsPage.js.map +1 -1
  44. package/dist/admin/pages/ReferralsPage.js +74 -52
  45. package/dist/admin/pages/ReferralsPage.js.map +1 -1
  46. package/dist/admin/pages/RolesPermissionsPage.js +137 -116
  47. package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
  48. package/dist/admin/pages/SREPage.js +38 -32
  49. package/dist/admin/pages/SREPage.js.map +1 -1
  50. package/dist/admin/pages/SandboxSettingsPage.js +361 -322
  51. package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
  52. package/dist/admin/pages/SettingsPage.js +145 -130
  53. package/dist/admin/pages/SettingsPage.js.map +1 -1
  54. package/dist/admin/pages/SyncFailuresPage.js +123 -118
  55. package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
  56. package/dist/admin/pages/TenantDetailPage.js +261 -218
  57. package/dist/admin/pages/TenantDetailPage.js.map +1 -1
  58. package/dist/admin/pages/TenantFormRoutePage.js +65 -61
  59. package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
  60. package/dist/admin/pages/TenantsListPage.js +88 -71
  61. package/dist/admin/pages/TenantsListPage.js.map +1 -1
  62. package/dist/admin/pages/UsersListPage.js +129 -112
  63. package/dist/admin/pages/UsersListPage.js.map +1 -1
  64. package/dist/admin/pages/WaitlistPage.js +80 -63
  65. package/dist/admin/pages/WaitlistPage.js.map +1 -1
  66. package/dist/admin/pages/WorkflowsPage.js +108 -100
  67. package/dist/admin/pages/WorkflowsPage.js.map +1 -1
  68. package/dist/admin/pages/WorkspacesListPage.js +62 -49
  69. package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
  70. package/dist/node_modules/@hookform/resolvers/dist/resolvers.js +2 -2
  71. package/dist/node_modules/@hookform/resolvers/dist/resolvers.js.map +1 -1
  72. package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js +35 -26
  73. package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js.map +1 -1
  74. package/dist/node_modules/react-hook-form/dist/index.esm.js +609 -516
  75. package/dist/node_modules/react-hook-form/dist/index.esm.js.map +1 -1
  76. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"TenantSsoPanel.js","names":[],"sources":["../../../src/admin/components/TenantSsoPanel.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { ShieldCheck, Loader2, Trash2, Save, ChevronDown, ChevronUp } from 'lucide-react';\nimport {\n useAdminGetSsoConfigQuery,\n useAdminSaveSsoConfigMutation,\n useAdminDeleteSsoConfigMutation,\n} from '../../generated/global-operations';\nimport { Card, CardHeader, CardTitle, CardContent, Button, Input, Separator } from './ui';\nimport { useToast } from './Toast';\nimport { ConfirmDialog } from './ConfirmDialog';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\n\ninterface TenantSsoPanelProps {\n tenantId: string;\n canManage: boolean;\n}\n\ninterface SsoFormData {\n entityId: string;\n ssoUrl: string;\n sloUrl: string;\n certificate: string;\n enabled: boolean;\n}\n\nconst EMPTY_FORM: SsoFormData = {\n entityId: '',\n ssoUrl: '',\n sloUrl: '',\n certificate: '',\n enabled: true,\n};\n\nexport function TenantSsoPanel({ tenantId, canManage }: TenantSsoPanelProps) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params);\n const toast = useToast();\n\n const [expanded, setExpanded] = useState(false);\n const [editing, setEditing] = useState(false);\n const [form, setForm] = useState<SsoFormData>(EMPTY_FORM);\n const [confirmDelete, setConfirmDelete] = useState(false);\n\n const { data, loading, error, refetch } = useAdminGetSsoConfigQuery({\n variables: { tenantId },\n fetchPolicy: 'cache-and-network',\n });\n\n const [saveSso, { loading: saving }] = useAdminSaveSsoConfigMutation();\n const [deleteSso, { loading: deleting }] = useAdminDeleteSsoConfigMutation();\n\n const config = data?.getSSOConfig;\n\n useEffect(() => {\n if (config) {\n setForm({\n entityId: config.entityId,\n ssoUrl: config.ssoUrl,\n sloUrl: config.sloUrl ?? '',\n certificate: config.certificate,\n enabled: config.enabled,\n });\n } else {\n setForm(EMPTY_FORM);\n }\n }, [config]);\n\n const handleSave = async (e: React.FormEvent) => {\n e.preventDefault();\n try {\n await saveSso({\n variables: {\n tenantId,\n input: {\n entityId: form.entityId,\n ssoUrl: form.ssoUrl,\n sloUrl: form.sloUrl || undefined,\n certificate: form.certificate,\n enabled: form.enabled,\n },\n },\n context: { headers: { 'x-workspace-id': '' } },\n });\n toast.success(\n tr('admin.tenantDetail.sso.savedTitle', 'SSO configuration saved'),\n tr(\n 'admin.tenantDetail.sso.savedDescription',\n 'SAML SSO has been configured for this tenant.'\n )\n );\n setEditing(false);\n void refetch();\n } catch (err) {\n toast.error(\n tr('admin.tenantDetail.sso.errorTitle', 'Failed to save SSO config'),\n err instanceof Error\n ? err.message\n : tr('admin.tenantDetail.sso.unknownError', 'Unknown error')\n );\n }\n };\n\n const handleDelete = async () => {\n try {\n await deleteSso({\n variables: { tenantId },\n context: { headers: { 'x-workspace-id': '' } },\n });\n toast.success(\n tr('admin.tenantDetail.sso.deletedTitle', 'SSO configuration removed'),\n tr('admin.tenantDetail.sso.deletedDescription', 'SSO has been disabled for this tenant.')\n );\n setConfirmDelete(false);\n setEditing(false);\n void refetch();\n } catch (err) {\n toast.error(\n tr('admin.tenantDetail.sso.errorTitle', 'Failed to delete SSO config'),\n err instanceof Error\n ? err.message\n : tr('admin.tenantDetail.sso.unknownError', 'Unknown error')\n );\n }\n };\n\n return (\n <>\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"flex items-center gap-2 text-lg\">\n <ShieldCheck className=\"h-5 w-5\" />\n {tr('admin.tenantDetail.sso.title', 'SSO Configuration')}\n </CardTitle>\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"flex items-center gap-1 text-sm text-text-secondary hover:text-text-primary transition-colors\"\n aria-expanded={expanded}\n aria-label={\n expanded\n ? tr('admin.tenantDetail.sso.collapse', 'Collapse SSO configuration')\n : tr('admin.tenantDetail.sso.expand', 'Expand SSO configuration')\n }\n >\n {expanded ? <ChevronUp className=\"h-4 w-4\" /> : <ChevronDown className=\"h-4 w-4\" />}\n </button>\n </div>\n </CardHeader>\n\n {expanded && (\n <CardContent className=\"space-y-4\">\n {loading ? (\n <div className=\"flex items-center justify-center py-6\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : error ? (\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n ) : config && !editing ? (\n /* ── Configured state — read-only view ── */\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <span\n className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${\n config.enabled\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-secondary'\n }`}\n >\n {config.enabled\n ? tr('admin.tenantDetail.sso.enabled', 'Enabled')\n : tr('admin.tenantDetail.sso.disabled', 'Disabled')}\n </span>\n </div>\n\n <dl className=\"grid gap-2 text-sm\">\n <div className=\"flex flex-col gap-0.5\">\n <dt className=\"text-xs text-text-secondary\">\n {tr('admin.tenantDetail.sso.entityId', 'Entity ID')}\n </dt>\n <dd className=\"font-mono text-xs break-all\">{config.entityId}</dd>\n </div>\n <div className=\"flex flex-col gap-0.5\">\n <dt className=\"text-xs text-text-secondary\">\n {tr('admin.tenantDetail.sso.ssoUrl', 'SSO URL')}\n </dt>\n <dd className=\"font-mono text-xs break-all\">{config.ssoUrl}</dd>\n </div>\n {config.sloUrl && (\n <div className=\"flex flex-col gap-0.5\">\n <dt className=\"text-xs text-text-secondary\">\n {tr('admin.tenantDetail.sso.sloUrl', 'SLO URL')}\n </dt>\n <dd className=\"font-mono text-xs break-all\">{config.sloUrl}</dd>\n </div>\n )}\n </dl>\n\n {canManage && (\n <div className=\"flex gap-2 pt-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => setEditing(true)}>\n {tr('admin.tenantDetail.sso.editButton', 'Edit')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"text-status-error-text hover:text-status-error-text\"\n onClick={() => setConfirmDelete(true)}\n >\n <Trash2 className=\"mr-1.5 h-3.5 w-3.5\" />\n {tr('admin.tenantDetail.sso.deleteButton', 'Remove')}\n </Button>\n </div>\n )}\n </div>\n ) : (\n /* ── Not configured or editing — show form ── */\n <form onSubmit={handleSave} className=\"space-y-4\">\n {!config && (\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'admin.tenantDetail.sso.notConfigured',\n 'No SSO configuration. Fill in the SAML details below to enable enterprise SSO.'\n )}\n </p>\n )}\n\n <Separator />\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"sm:col-span-2 space-y-1.5\">\n <label className=\"text-sm font-medium\" htmlFor={`sso-entityId-${tenantId}`}>\n {tr('admin.tenantDetail.sso.entityId', 'Entity ID')}\n <span className=\"ml-1 text-status-error-text\">*</span>\n </label>\n <Input\n id={`sso-entityId-${tenantId}`}\n value={form.entityId}\n onChange={(e) => setForm((f) => ({ ...f, entityId: e.target.value }))}\n placeholder=\"https://idp.example.com/entity\"\n required\n />\n </div>\n\n <div className=\"sm:col-span-2 space-y-1.5\">\n <label className=\"text-sm font-medium\" htmlFor={`sso-ssoUrl-${tenantId}`}>\n {tr('admin.tenantDetail.sso.ssoUrl', 'SSO URL')}\n <span className=\"ml-1 text-status-error-text\">*</span>\n </label>\n <Input\n id={`sso-ssoUrl-${tenantId}`}\n value={form.ssoUrl}\n onChange={(e) => setForm((f) => ({ ...f, ssoUrl: e.target.value }))}\n placeholder=\"https://idp.example.com/sso\"\n required\n />\n </div>\n\n <div className=\"sm:col-span-2 space-y-1.5\">\n <label className=\"text-sm font-medium\" htmlFor={`sso-sloUrl-${tenantId}`}>\n {tr('admin.tenantDetail.sso.sloUrl', 'SLO URL')}\n <span className=\"ml-0.5 text-xs text-text-secondary\">\n {tr('admin.tenantDetail.sso.optional', '(optional)')}\n </span>\n </label>\n <Input\n id={`sso-sloUrl-${tenantId}`}\n value={form.sloUrl}\n onChange={(e) => setForm((f) => ({ ...f, sloUrl: e.target.value }))}\n placeholder=\"https://idp.example.com/slo\"\n />\n </div>\n\n <div className=\"sm:col-span-2 space-y-1.5\">\n <label className=\"text-sm font-medium\" htmlFor={`sso-cert-${tenantId}`}>\n {tr('admin.tenantDetail.sso.certificate', 'X.509 Certificate')}\n <span className=\"ml-1 text-status-error-text\">*</span>\n </label>\n <textarea\n id={`sso-cert-${tenantId}`}\n value={form.certificate}\n onChange={(e) => setForm((f) => ({ ...f, certificate: e.target.value }))}\n placeholder=\"-----BEGIN CERTIFICATE-----&#10;...&#10;-----END CERTIFICATE-----\"\n required\n rows={5}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 font-mono text-xs text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 resize-none\"\n />\n </div>\n\n <div className=\"sm:col-span-2 flex items-center gap-3\">\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={form.enabled}\n aria-label={tr(\n 'admin.tenantDetail.sso.enabledLabel',\n 'Enable SSO for this tenant'\n )}\n onClick={() => setForm((f) => ({ ...f, enabled: !f.enabled }))}\n className={`relative inline-flex h-5 w-9 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:ring-offset-2 ${\n form.enabled ? 'bg-action-primary-bg' : 'bg-bg-sunken'\n }`}\n >\n <span\n className={`pointer-events-none inline-block h-4 w-4 transform rounded-full bg-white shadow-lg transition-transform ${\n form.enabled ? 'translate-x-4' : 'translate-x-0'\n }`}\n />\n </button>\n <span className=\"text-sm\">\n {tr('admin.tenantDetail.sso.enabledLabel', 'Enable SSO for this tenant')}\n </span>\n </div>\n </div>\n\n <Separator />\n\n <div className=\"flex gap-2\">\n <Button type=\"submit\" size=\"sm\" disabled={saving}>\n {saving ? (\n <Loader2 className=\"mr-1.5 h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Save className=\"mr-1.5 h-3.5 w-3.5\" />\n )}\n {tr('admin.tenantDetail.sso.saveButton', 'Save SSO Config')}\n </Button>\n {editing && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setEditing(false)}\n >\n {tr('admin.tenantDetail.sso.cancelButton', 'Cancel')}\n </Button>\n )}\n </div>\n </form>\n )}\n </CardContent>\n )}\n </Card>\n\n <ConfirmDialog\n open={confirmDelete}\n onClose={() => setConfirmDelete(false)}\n title={tr('admin.tenantDetail.sso.deleteTitle', 'Remove SSO configuration?')}\n description={tr(\n 'admin.tenantDetail.sso.deleteDescription',\n 'This will remove the SAML SSO configuration. Users will no longer be able to sign in via SSO.'\n )}\n confirmLabel={tr('admin.tenantDetail.sso.deleteConfirm', 'Remove SSO')}\n variant=\"destructive\"\n onConfirm={handleDelete}\n loading={deleting}\n />\n </>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TenantSsoPanel.js","names":[],"sources":["../../../src/admin/components/TenantSsoPanel.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { ShieldCheck, Loader2, Trash2, Save, ChevronDown, ChevronUp } from 'lucide-react';\nimport {\n useAdminGetSsoConfigQuery,\n useAdminSaveSsoConfigMutation,\n useAdminDeleteSsoConfigMutation,\n} from '../../generated/global-operations';\nimport { Card, CardHeader, CardTitle, CardContent, Button, Input, Separator } from './ui';\nimport { useToast } from './Toast';\nimport { ConfirmDialog } from './ConfirmDialog';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\n\ninterface TenantSsoPanelProps {\n tenantId: string;\n canManage: boolean;\n}\n\ninterface SsoFormData {\n entityId: string;\n ssoUrl: string;\n sloUrl: string;\n certificate: string;\n enabled: boolean;\n}\n\nconst EMPTY_FORM: SsoFormData = {\n entityId: '',\n ssoUrl: '',\n sloUrl: '',\n certificate: '',\n enabled: true,\n};\n\nexport function TenantSsoPanel({ tenantId, canManage }: TenantSsoPanelProps) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params);\n const toast = useToast();\n\n const [expanded, setExpanded] = useState(false);\n const [editing, setEditing] = useState(false);\n const [form, setForm] = useState<SsoFormData>(EMPTY_FORM);\n const [confirmDelete, setConfirmDelete] = useState(false);\n\n const { data, loading, error, refetch } = useAdminGetSsoConfigQuery({\n variables: { tenantId },\n fetchPolicy: 'cache-and-network',\n });\n\n const [saveSso, { loading: saving }] = useAdminSaveSsoConfigMutation();\n const [deleteSso, { loading: deleting }] = useAdminDeleteSsoConfigMutation();\n\n const config = data?.getSSOConfig;\n\n useEffect(() => {\n if (config) {\n setForm({\n entityId: config.entityId,\n ssoUrl: config.ssoUrl,\n sloUrl: config.sloUrl ?? '',\n certificate: config.certificate,\n enabled: config.enabled,\n });\n } else {\n setForm(EMPTY_FORM);\n }\n }, [config]);\n\n const handleSave = async (e: React.FormEvent) => {\n e.preventDefault();\n try {\n await saveSso({\n variables: {\n tenantId,\n input: {\n entityId: form.entityId,\n ssoUrl: form.ssoUrl,\n sloUrl: form.sloUrl || undefined,\n certificate: form.certificate,\n enabled: form.enabled,\n },\n },\n context: { headers: { 'x-workspace-id': '' } },\n });\n toast.success(\n tr('admin.tenantDetail.sso.savedTitle', 'SSO configuration saved'),\n tr(\n 'admin.tenantDetail.sso.savedDescription',\n 'SAML SSO has been configured for this tenant.'\n )\n );\n setEditing(false);\n void refetch();\n } catch (err) {\n toast.error(\n tr('admin.tenantDetail.sso.errorTitle', 'Failed to save SSO config'),\n err instanceof Error\n ? err.message\n : tr('admin.tenantDetail.sso.unknownError', 'Unknown error')\n );\n }\n };\n\n const handleDelete = async () => {\n try {\n await deleteSso({\n variables: { tenantId },\n context: { headers: { 'x-workspace-id': '' } },\n });\n toast.success(\n tr('admin.tenantDetail.sso.deletedTitle', 'SSO configuration removed'),\n tr('admin.tenantDetail.sso.deletedDescription', 'SSO has been disabled for this tenant.')\n );\n setConfirmDelete(false);\n setEditing(false);\n void refetch();\n } catch (err) {\n toast.error(\n tr('admin.tenantDetail.sso.errorTitle', 'Failed to delete SSO config'),\n err instanceof Error\n ? err.message\n : tr('admin.tenantDetail.sso.unknownError', 'Unknown error')\n );\n }\n };\n\n return (\n <>\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"flex items-center gap-2 text-lg\">\n <ShieldCheck className=\"h-5 w-5\" />\n {tr('admin.tenantDetail.sso.title', 'SSO Configuration')}\n </CardTitle>\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"flex items-center gap-1 text-sm text-text-secondary hover:text-text-primary transition-colors\"\n aria-expanded={expanded}\n aria-label={\n expanded\n ? tr('admin.tenantDetail.sso.collapse', 'Collapse SSO configuration')\n : tr('admin.tenantDetail.sso.expand', 'Expand SSO configuration')\n }\n >\n {expanded ? <ChevronUp className=\"h-4 w-4\" /> : <ChevronDown className=\"h-4 w-4\" />}\n </button>\n </div>\n </CardHeader>\n\n {expanded && (\n <CardContent className=\"space-y-4\">\n {loading ? (\n <div className=\"flex items-center justify-center py-6\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : error ? (\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n ) : config && !editing ? (\n /* ── Configured state — read-only view ── */\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <span\n className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${\n config.enabled\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-secondary'\n }`}\n >\n {config.enabled\n ? tr('admin.tenantDetail.sso.enabled', 'Enabled')\n : tr('admin.tenantDetail.sso.disabled', 'Disabled')}\n </span>\n </div>\n\n <dl className=\"grid gap-2 text-sm\">\n <div className=\"flex flex-col gap-0.5\">\n <dt className=\"text-xs text-text-secondary\">\n {tr('admin.tenantDetail.sso.entityId', 'Entity ID')}\n </dt>\n <dd className=\"font-mono text-xs break-all\">{config.entityId}</dd>\n </div>\n <div className=\"flex flex-col gap-0.5\">\n <dt className=\"text-xs text-text-secondary\">\n {tr('admin.tenantDetail.sso.ssoUrl', 'SSO URL')}\n </dt>\n <dd className=\"font-mono text-xs break-all\">{config.ssoUrl}</dd>\n </div>\n {config.sloUrl && (\n <div className=\"flex flex-col gap-0.5\">\n <dt className=\"text-xs text-text-secondary\">\n {tr('admin.tenantDetail.sso.sloUrl', 'SLO URL')}\n </dt>\n <dd className=\"font-mono text-xs break-all\">{config.sloUrl}</dd>\n </div>\n )}\n </dl>\n\n {canManage && (\n <div className=\"flex gap-2 pt-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => setEditing(true)}>\n {tr('admin.tenantDetail.sso.editButton', 'Edit')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"text-status-error-text hover:text-status-error-text\"\n onClick={() => setConfirmDelete(true)}\n >\n <Trash2 className=\"mr-1.5 h-3.5 w-3.5\" />\n {tr('admin.tenantDetail.sso.deleteButton', 'Remove')}\n </Button>\n </div>\n )}\n </div>\n ) : (\n /* ── Not configured or editing — show form ── */\n <form onSubmit={handleSave} className=\"space-y-4\">\n {!config && (\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'admin.tenantDetail.sso.notConfigured',\n 'No SSO configuration. Fill in the SAML details below to enable enterprise SSO.'\n )}\n </p>\n )}\n\n <Separator />\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"sm:col-span-2 space-y-1.5\">\n <label className=\"text-sm font-medium\" htmlFor={`sso-entityId-${tenantId}`}>\n {tr('admin.tenantDetail.sso.entityId', 'Entity ID')}\n <span className=\"ml-1 text-status-error-text\">*</span>\n </label>\n <Input\n id={`sso-entityId-${tenantId}`}\n value={form.entityId}\n onChange={(e) => setForm((f) => ({ ...f, entityId: e.target.value }))}\n placeholder=\"https://idp.example.com/entity\"\n required\n />\n </div>\n\n <div className=\"sm:col-span-2 space-y-1.5\">\n <label className=\"text-sm font-medium\" htmlFor={`sso-ssoUrl-${tenantId}`}>\n {tr('admin.tenantDetail.sso.ssoUrl', 'SSO URL')}\n <span className=\"ml-1 text-status-error-text\">*</span>\n </label>\n <Input\n id={`sso-ssoUrl-${tenantId}`}\n value={form.ssoUrl}\n onChange={(e) => setForm((f) => ({ ...f, ssoUrl: e.target.value }))}\n placeholder=\"https://idp.example.com/sso\"\n required\n />\n </div>\n\n <div className=\"sm:col-span-2 space-y-1.5\">\n <label className=\"text-sm font-medium\" htmlFor={`sso-sloUrl-${tenantId}`}>\n {tr('admin.tenantDetail.sso.sloUrl', 'SLO URL')}\n <span className=\"ml-0.5 text-xs text-text-secondary\">\n {tr('admin.tenantDetail.sso.optional', '(optional)')}\n </span>\n </label>\n <Input\n id={`sso-sloUrl-${tenantId}`}\n value={form.sloUrl}\n onChange={(e) => setForm((f) => ({ ...f, sloUrl: e.target.value }))}\n placeholder=\"https://idp.example.com/slo\"\n />\n </div>\n\n <div className=\"sm:col-span-2 space-y-1.5\">\n <label className=\"text-sm font-medium\" htmlFor={`sso-cert-${tenantId}`}>\n {tr('admin.tenantDetail.sso.certificate', 'X.509 Certificate')}\n <span className=\"ml-1 text-status-error-text\">*</span>\n </label>\n <textarea\n id={`sso-cert-${tenantId}`}\n value={form.certificate}\n onChange={(e) => setForm((f) => ({ ...f, certificate: e.target.value }))}\n placeholder=\"-----BEGIN CERTIFICATE-----&#10;...&#10;-----END CERTIFICATE-----\"\n required\n rows={5}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 font-mono text-xs text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 resize-none\"\n />\n </div>\n\n <div className=\"sm:col-span-2 flex items-center gap-3\">\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={form.enabled}\n aria-label={tr(\n 'admin.tenantDetail.sso.enabledLabel',\n 'Enable SSO for this tenant'\n )}\n onClick={() => setForm((f) => ({ ...f, enabled: !f.enabled }))}\n className={`relative inline-flex h-5 w-9 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:ring-offset-2 ${\n form.enabled ? 'bg-action-primary-bg' : 'bg-bg-sunken'\n }`}\n >\n <span\n className={`pointer-events-none inline-block h-4 w-4 transform rounded-full bg-bg-surface shadow-lg transition-transform ${\n form.enabled ? 'translate-x-4' : 'translate-x-0'\n }`}\n />\n </button>\n <span className=\"text-sm\">\n {tr('admin.tenantDetail.sso.enabledLabel', 'Enable SSO for this tenant')}\n </span>\n </div>\n </div>\n\n <Separator />\n\n <div className=\"flex gap-2\">\n <Button type=\"submit\" size=\"sm\" disabled={saving}>\n {saving ? (\n <Loader2 className=\"mr-1.5 h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Save className=\"mr-1.5 h-3.5 w-3.5\" />\n )}\n {tr('admin.tenantDetail.sso.saveButton', 'Save SSO Config')}\n </Button>\n {editing && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setEditing(false)}\n >\n {tr('admin.tenantDetail.sso.cancelButton', 'Cancel')}\n </Button>\n )}\n </div>\n </form>\n )}\n </CardContent>\n )}\n </Card>\n\n <ConfirmDialog\n open={confirmDelete}\n onClose={() => setConfirmDelete(false)}\n title={tr('admin.tenantDetail.sso.deleteTitle', 'Remove SSO configuration?')}\n description={tr(\n 'admin.tenantDetail.sso.deleteDescription',\n 'This will remove the SAML SSO configuration. Users will no longer be able to sign in via SSO.'\n )}\n confirmLabel={tr('admin.tenantDetail.sso.deleteConfirm', 'Remove SSO')}\n variant=\"destructive\"\n onConfirm={handleDelete}\n loading={deleting}\n />\n </>\n );\n}\n"],"mappings":";;;;;;;;;;AA0BA,IAAM,IAA0B;CAC9B,UAAU;CACV,QAAQ;CACR,QAAQ;CACR,aAAa;CACb,SAAS;CACV;AAED,SAAgB,EAAe,EAAE,aAAU,gBAAkC;CAC3E,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,GAAkB,MACzC,EAAc,GAAG,GAAK,GAAU,EAAO,EACnC,IAAQ,GAAU,EAElB,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAS,KAAc,EAAS,GAAM,EACvC,CAAC,GAAM,KAAW,EAAsB,EAAW,EACnD,CAAC,GAAe,KAAoB,EAAS,GAAM,EAEnD,EAAE,SAAM,YAAS,UAAO,eAAY,EAA0B;EAClE,WAAW,EAAE,aAAU;EACvB,aAAa;EACd,CAAC,EAEI,CAAC,GAAS,EAAE,SAAS,OAAY,GAA+B,EAChE,CAAC,GAAW,EAAE,SAAS,OAAc,GAAiC,EAEtE,IAAS,GAAM;AA0ErB,QAxEA,QAAgB;AACd,EACE,EADE,IACM;GACN,UAAU,EAAO;GACjB,QAAQ,EAAO;GACf,QAAQ,EAAO,UAAU;GACzB,aAAa,EAAO;GACpB,SAAS,EAAO;GACjB,GAEO,EAAW;IAEpB,CAAC,EAAO,CAAC,EA6DV,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GAAW,WAAU;aAArB,CACE,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA,EAClC,EAAG,gCAAgC,oBAAoB,CAC9C;MACZ,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,GAAa,MAAM,CAAC,EAAE;GACrC,WAAU;GACV,iBAAe;GACf,cACE,IACI,EAAG,mCAAmC,6BAA6B,GACnE,EAAG,iCAAiC,2BAA2B;aAGzD,EAAX,IAAY,IAAoC,GAArC,EAAW,WAAU,WAAY,CAAsC;GAC5E,CAAA,CACL;KACK,CAAA,EAEZ,KACC,kBAAC,GAAD;EAAa,WAAU;YACpB,IACC,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD,EAAS,WAAU,4CAA6C,CAAA;GAC5D,CAAA,GACJ,IACF,kBAAC,KAAD;GAAG,WAAU;aAAkC,EAAM;GAAY,CAAA,GAC/D,KAAU,CAAC,IAEb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,QAAD;MACE,WAAW,2EACT,EAAO,UACH,yDACA;gBAGL,EAAO,UACJ,EAAG,kCAAkC,UAAU,GAC/C,EAAG,mCAAmC,WAAW;MAChD,CAAA;KACH,CAAA;IAEN,kBAAC,MAAD;KAAI,WAAU;eAAd;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAG,mCAAmC,YAAY;QAChD,CAAA,EACL,kBAAC,MAAD;QAAI,WAAU;kBAA+B,EAAO;QAAc,CAAA,CAC9D;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAG,iCAAiC,UAAU;QAC5C,CAAA,EACL,kBAAC,MAAD;QAAI,WAAU;kBAA+B,EAAO;QAAY,CAAA,CAC5D;;MACL,EAAO,UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAG,iCAAiC,UAAU;QAC5C,CAAA,EACL,kBAAC,MAAD;QAAI,WAAU;kBAA+B,EAAO;QAAY,CAAA,CAC5D;;MAEL;;IAEJ,KACC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAQ,SAAQ;MAAU,MAAK;MAAK,eAAe,EAAW,GAAK;gBAChE,EAAG,qCAAqC,OAAO;MACzC,CAAA,EACT,kBAAC,GAAD;MACE,SAAQ;MACR,MAAK;MACL,WAAU;MACV,eAAe,EAAiB,GAAK;gBAJvC,CAME,kBAAC,GAAD,EAAQ,WAAU,sBAAuB,CAAA,EACxC,EAAG,uCAAuC,SAAS,CAC7C;QACL;;IAEJ;OAGN,kBAAC,QAAD;GAAM,UAtJC,OAAO,MAAuB;AAC/C,MAAE,gBAAgB;AAClB,QAAI;AAsBG,KArBL,MAAM,EAAQ;MACZ,WAAW;OACT;OACA,OAAO;QACL,UAAU,EAAK;QACf,QAAQ,EAAK;QACb,QAAQ,EAAK,UAAU,KAAA;QACvB,aAAa,EAAK;QAClB,SAAS,EAAK;QACf;OACF;MACD,SAAS,EAAE,SAAS,EAAE,kBAAkB,IAAI,EAAE;MAC/C,CAAC,EACF,EAAM,QACJ,EAAG,qCAAqC,0BAA0B,EAClE,EACE,2CACA,gDACD,CACF,EACD,EAAW,GAAM,EACZ,GAAS;aACP,GAAK;AACZ,OAAM,MACJ,EAAG,qCAAqC,4BAA4B,EACpE,aAAe,QACX,EAAI,UACJ,EAAG,uCAAuC,gBAAgB,CAC/D;;;GAuHmC,WAAU;aAAtC;IACG,CAAC,KACA,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,wCACA,iFACD;KACC,CAAA;IAGN,kBAAC,GAAD,EAAa,CAAA;IAEb,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;QAAsB,SAAS,gBAAgB;kBAAhE,CACG,EAAG,mCAAmC,YAAY,EACnD,kBAAC,QAAD;SAAM,WAAU;mBAA8B;SAAQ,CAAA,CAChD;WACR,kBAAC,GAAD;QACE,IAAI,gBAAgB;QACpB,OAAO,EAAK;QACZ,WAAW,MAAM,GAAS,OAAO;SAAE,GAAG;SAAG,UAAU,EAAE,OAAO;SAAO,EAAE;QACrE,aAAY;QACZ,UAAA;QACA,CAAA,CACE;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;QAAsB,SAAS,cAAc;kBAA9D,CACG,EAAG,iCAAiC,UAAU,EAC/C,kBAAC,QAAD;SAAM,WAAU;mBAA8B;SAAQ,CAAA,CAChD;WACR,kBAAC,GAAD;QACE,IAAI,cAAc;QAClB,OAAO,EAAK;QACZ,WAAW,MAAM,GAAS,OAAO;SAAE,GAAG;SAAG,QAAQ,EAAE,OAAO;SAAO,EAAE;QACnE,aAAY;QACZ,UAAA;QACA,CAAA,CACE;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;QAAsB,SAAS,cAAc;kBAA9D,CACG,EAAG,iCAAiC,UAAU,EAC/C,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAG,mCAAmC,aAAa;SAC/C,CAAA,CACD;WACR,kBAAC,GAAD;QACE,IAAI,cAAc;QAClB,OAAO,EAAK;QACZ,WAAW,MAAM,GAAS,OAAO;SAAE,GAAG;SAAG,QAAQ,EAAE,OAAO;SAAO,EAAE;QACnE,aAAY;QACZ,CAAA,CACE;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;QAAsB,SAAS,YAAY;kBAA5D,CACG,EAAG,sCAAsC,oBAAoB,EAC9D,kBAAC,QAAD;SAAM,WAAU;mBAA8B;SAAQ,CAAA,CAChD;WACR,kBAAC,YAAD;QACE,IAAI,YAAY;QAChB,OAAO,EAAK;QACZ,WAAW,MAAM,GAAS,OAAO;SAAE,GAAG;SAAG,aAAa,EAAE,OAAO;SAAO,EAAE;QACxE,aAAY;QACZ,UAAA;QACA,MAAM;QACN,WAAU;QACV,CAAA,CACE;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,MAAK;QACL,gBAAc,EAAK;QACnB,cAAY,EACV,uCACA,6BACD;QACD,eAAe,GAAS,OAAO;SAAE,GAAG;SAAG,SAAS,CAAC,EAAE;SAAS,EAAE;QAC9D,WAAW,iNACT,EAAK,UAAU,yBAAyB;kBAG1C,kBAAC,QAAD,EACE,WAAW,gHACT,EAAK,UAAU,kBAAkB,mBAEnC,CAAA;QACK,CAAA,EACT,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAG,uCAAuC,6BAA6B;QACnE,CAAA,CACH;;MACF;;IAEN,kBAAC,GAAD,EAAa,CAAA;IAEb,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAQ,MAAK;MAAS,MAAK;MAAK,UAAU;gBAA1C,CACG,IACC,kBAAC,GAAD,EAAS,WAAU,mCAAoC,CAAA,GAEvD,kBAAC,GAAD,EAAM,WAAU,sBAAuB,CAAA,EAExC,EAAG,qCAAqC,kBAAkB,CACpD;SACR,KACC,kBAAC,GAAD;MACE,MAAK;MACL,SAAQ;MACR,MAAK;MACL,eAAe,EAAW,GAAM;gBAE/B,EAAG,uCAAuC,SAAS;MAC7C,CAAA,CAEP;;IACD;;EAEG,CAAA,CAEX,EAAA,CAAA,EAEP,kBAAC,GAAD;EACE,MAAM;EACN,eAAe,EAAiB,GAAM;EACtC,OAAO,EAAG,sCAAsC,4BAA4B;EAC5E,aAAa,EACX,4CACA,gGACD;EACD,cAAc,EAAG,wCAAwC,aAAa;EACtE,SAAQ;EACR,WA3Pe,YAAY;AAC/B,OAAI;AAWG,IAVL,MAAM,EAAU;KACd,WAAW,EAAE,aAAU;KACvB,SAAS,EAAE,SAAS,EAAE,kBAAkB,IAAI,EAAE;KAC/C,CAAC,EACF,EAAM,QACJ,EAAG,uCAAuC,4BAA4B,EACtE,EAAG,6CAA6C,yCAAyC,CAC1F,EACD,EAAiB,GAAM,EACvB,EAAW,GAAM,EACZ,GAAS;YACP,GAAK;AACZ,MAAM,MACJ,EAAG,qCAAqC,8BAA8B,EACtE,aAAe,QACX,EAAI,UACJ,EAAG,uCAAuC,gBAAgB,CAC/D;;;EAyOC,SAAS;EACT,CAAA,CACD,EAAA,CAAA"}
@@ -75,27 +75,27 @@ function g({ toasts: e, removeToast: t }) {
75
75
  var _ = {
76
76
  success: {
77
77
  icon: c,
78
- bg: "bg-success/10",
79
- border: "border-success/30",
80
- iconColor: "text-success"
78
+ bg: "bg-status-success-bg",
79
+ border: "border-status-success-border",
80
+ iconColor: "text-status-success-text"
81
81
  },
82
82
  error: {
83
83
  icon: o,
84
- bg: "bg-status-error-bg/10",
85
- border: "border-status-error-border/30",
84
+ bg: "bg-status-error-bg",
85
+ border: "border-status-error-border",
86
86
  iconColor: "text-status-error-text"
87
87
  },
88
88
  warning: {
89
89
  icon: s,
90
- bg: "bg-warning/10",
91
- border: "border-warning/30",
92
- iconColor: "text-warning"
90
+ bg: "bg-status-warning-bg",
91
+ border: "border-status-warning-border",
92
+ iconColor: "text-status-warning-text"
93
93
  },
94
94
  info: {
95
95
  icon: l,
96
- bg: "bg-action-primary-bg/10",
97
- border: "border-primary/30",
98
- iconColor: "text-primary"
96
+ bg: "bg-status-info-bg",
97
+ border: "border-status-info-border",
98
+ iconColor: "text-status-info-text"
99
99
  }
100
100
  };
101
101
  function v({ toast: t, onDismiss: n }) {
@@ -1 +1 @@
1
- {"version":3,"file":"Toast.js","names":[],"sources":["../../../src/admin/components/Toast.tsx"],"sourcesContent":["import { createContext, useContext, useState, useCallback, useEffect, type ReactNode } from 'react';\nimport { X, CheckCircle2, AlertCircle, AlertTriangle, Info } from 'lucide-react';\nimport { cn } from '../utils/cn';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ToastType = 'success' | 'error' | 'warning' | 'info';\n\nexport interface Toast {\n id: string;\n type: ToastType;\n title: string;\n description?: string;\n duration?: number;\n}\n\ninterface ToastContextValue {\n toasts: Toast[];\n addToast: (toast: Omit<Toast, 'id'>) => void;\n removeToast: (id: string) => void;\n success: (title: string, description?: string) => void;\n error: (title: string, description?: string) => void;\n warning: (title: string, description?: string) => void;\n info: (title: string, description?: string) => void;\n}\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst ToastContext = createContext<ToastContextValue | null>(null);\n\nexport function useToast() {\n const context = useContext(ToastContext);\n if (!context) {\n throw new Error('useToast must be used within a ToastProvider');\n }\n return context;\n}\n\n// ============================================================================\n// Provider\n// ============================================================================\n\ninterface ToastProviderProps {\n children: ReactNode;\n}\n\nexport function ToastProvider({ children }: ToastProviderProps) {\n const [toasts, setToasts] = useState<Toast[]>([]);\n\n const addToast = useCallback((toast: Omit<Toast, 'id'>) => {\n const id = `toast-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n setToasts((prev) => [...prev, { ...toast, id }]);\n }, []);\n\n const removeToast = useCallback((id: string) => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n }, []);\n\n const success = useCallback(\n (title: string, description?: string) => {\n addToast({ type: 'success', title, description, duration: 4000 });\n },\n [addToast]\n );\n\n const error = useCallback(\n (title: string, description?: string) => {\n addToast({ type: 'error', title, description, duration: 6000 });\n },\n [addToast]\n );\n\n const warning = useCallback(\n (title: string, description?: string) => {\n addToast({ type: 'warning', title, description, duration: 5000 });\n },\n [addToast]\n );\n\n const info = useCallback(\n (title: string, description?: string) => {\n addToast({ type: 'info', title, description, duration: 4000 });\n },\n [addToast]\n );\n\n return (\n <ToastContext.Provider value={{ toasts, addToast, removeToast, success, error, warning, info }}>\n {children}\n <ToastContainer toasts={toasts} removeToast={removeToast} />\n </ToastContext.Provider>\n );\n}\n\n// ============================================================================\n// Toast Container\n// ============================================================================\n\ninterface ToastContainerProps {\n toasts: Toast[];\n removeToast: (id: string) => void;\n}\n\nfunction ToastContainer({ toasts, removeToast }: ToastContainerProps) {\n if (toasts.length === 0) return null;\n\n return (\n <div className=\"fixed bottom-4 right-4 z-[100] flex flex-col gap-2 max-w-sm\">\n {toasts.map((toast) => (\n <ToastItem key={toast.id} toast={toast} onDismiss={() => removeToast(toast.id)} />\n ))}\n </div>\n );\n}\n\n// ============================================================================\n// Toast Item\n// ============================================================================\n\ninterface ToastItemProps {\n toast: Toast;\n onDismiss: () => void;\n}\n\nconst toastStyles: Record<\n ToastType,\n { icon: typeof CheckCircle2; bg: string; border: string; iconColor: string }\n> = {\n success: {\n icon: CheckCircle2,\n bg: 'bg-success/10',\n border: 'border-success/30',\n iconColor: 'text-success',\n },\n error: {\n icon: AlertCircle,\n bg: 'bg-status-error-bg/10',\n border: 'border-status-error-border/30',\n iconColor: 'text-status-error-text',\n },\n warning: {\n icon: AlertTriangle,\n bg: 'bg-warning/10',\n border: 'border-warning/30',\n iconColor: 'text-warning',\n },\n info: {\n icon: Info,\n bg: 'bg-action-primary-bg/10',\n border: 'border-primary/30',\n iconColor: 'text-primary',\n },\n};\n\nfunction ToastItem({ toast, onDismiss }: ToastItemProps) {\n const { icon: Icon, bg, border, iconColor } = toastStyles[toast.type];\n\n // Auto-dismiss\n useEffect(() => {\n const duration = toast.duration ?? 4000;\n const timer = setTimeout(onDismiss, duration);\n return () => clearTimeout(timer);\n }, [toast.duration, onDismiss]);\n\n return (\n <div\n className={cn(\n 'flex items-start gap-3 rounded-lg border p-4 shadow-lg backdrop-blur-sm',\n 'animate-in slide-in-from-right-full duration-300',\n 'bg-bg-surface/95',\n bg,\n border\n )}\n role=\"alert\"\n >\n <Icon className={cn('h-5 w-5 shrink-0 mt-0.5', iconColor)} />\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">{toast.title}</p>\n {toast.description && (\n <p className=\"mt-1 text-xs text-text-secondary\">{toast.description}</p>\n )}\n </div>\n <button\n onClick={onDismiss}\n className=\"shrink-0 rounded-md p-1 hover:bg-bg-sunken transition-colors\"\n aria-label=\"Dismiss\"\n >\n <X className=\"h-4 w-4 text-text-secondary\" />\n </button>\n </div>\n );\n}\n"],"mappings":";;;;;AAgCA,IAAM,IAAe,EAAwC,KAAK;AAElE,SAAgB,IAAW;CACzB,IAAM,IAAU,EAAW,EAAa;AACxC,KAAI,CAAC,EACH,OAAU,MAAM,+CAA+C;AAEjE,QAAO;;AAWT,SAAgB,EAAc,EAAE,eAAgC;CAC9D,IAAM,CAAC,GAAQ,KAAa,EAAkB,EAAE,CAAC,EAE3C,IAAW,GAAa,MAA6B;EACzD,IAAM,IAAK,SAAS,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;AACxE,KAAW,MAAS,CAAC,GAAG,GAAM;GAAE,GAAG;GAAO;GAAI,CAAC,CAAC;IAC/C,EAAE,CAAC,EAEA,IAAc,GAAa,MAAe;AAC9C,KAAW,MAAS,EAAK,QAAQ,MAAM,EAAE,OAAO,EAAG,CAAC;IACnD,EAAE,CAAC,EAEA,IAAU,GACb,GAAe,MAAyB;AACvC,IAAS;GAAE,MAAM;GAAW;GAAO;GAAa,UAAU;GAAM,CAAC;IAEnE,CAAC,EAAS,CACX,EAEK,IAAQ,GACX,GAAe,MAAyB;AACvC,IAAS;GAAE,MAAM;GAAS;GAAO;GAAa,UAAU;GAAM,CAAC;IAEjE,CAAC,EAAS,CACX,EAEK,IAAU,GACb,GAAe,MAAyB;AACvC,IAAS;GAAE,MAAM;GAAW;GAAO;GAAa,UAAU;GAAM,CAAC;IAEnE,CAAC,EAAS,CACX,EAEK,IAAO,GACV,GAAe,MAAyB;AACvC,IAAS;GAAE,MAAM;GAAQ;GAAO;GAAa,UAAU;GAAM,CAAC;IAEhE,CAAC,EAAS,CACX;AAED,QACE,kBAAC,EAAa,UAAd;EAAuB,OAAO;GAAE;GAAQ;GAAU;GAAa;GAAS;GAAO;GAAS;GAAM;YAA9F,CACG,GACD,kBAAC,GAAD;GAAwB;GAAqB;GAAe,CAAA,CACtC;;;AAa5B,SAAS,EAAe,EAAE,WAAQ,kBAAoC;AAGpE,QAFI,EAAO,WAAW,IAAU,OAG9B,kBAAC,OAAD;EAAK,WAAU;YACZ,EAAO,KAAK,MACX,kBAAC,GAAD;GAAiC;GAAO,iBAAiB,EAAY,EAAM,GAAG;GAAI,EAAlE,EAAM,GAA4D,CAClF;EACE,CAAA;;AAaV,IAAM,IAGF;CACF,SAAS;EACP,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,WAAW;EACZ;CACD,OAAO;EACL,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,WAAW;EACZ;CACD,SAAS;EACP,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,WAAW;EACZ;CACD,MAAM;EACJ,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,WAAW;EACZ;CACF;AAED,SAAS,EAAU,EAAE,UAAO,gBAA6B;CACvD,IAAM,EAAE,MAAM,GAAM,OAAI,WAAQ,iBAAc,EAAY,EAAM;AAShE,QANA,QAAgB;EACd,IAAM,IAAW,EAAM,YAAY,KAC7B,IAAQ,WAAW,GAAW,EAAS;AAC7C,eAAa,aAAa,EAAM;IAC/B,CAAC,EAAM,UAAU,EAAU,CAAC,EAG7B,kBAAC,OAAD;EACE,WAAW,EACT,2EACA,oDACA,oBACA,GACA,EACD;EACD,MAAK;YARP;GAUE,kBAAC,GAAD,EAAM,WAAW,EAAG,2BAA2B,EAAU,EAAI,CAAA;GAC7D,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAAyC,EAAM;KAAU,CAAA,EACrE,EAAM,eACL,kBAAC,KAAD;KAAG,WAAU;eAAoC,EAAM;KAAgB,CAAA,CAErE;;GACN,kBAAC,UAAD;IACE,SAAS;IACT,WAAU;IACV,cAAW;cAEX,kBAAC,GAAD,EAAG,WAAU,+BAAgC,CAAA;IACtC,CAAA;GACL"}
1
+ {"version":3,"file":"Toast.js","names":[],"sources":["../../../src/admin/components/Toast.tsx"],"sourcesContent":["import { createContext, useContext, useState, useCallback, useEffect, type ReactNode } from 'react';\nimport { X, CheckCircle2, AlertCircle, AlertTriangle, Info } from 'lucide-react';\nimport { cn } from '../utils/cn';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ToastType = 'success' | 'error' | 'warning' | 'info';\n\nexport interface Toast {\n id: string;\n type: ToastType;\n title: string;\n description?: string;\n duration?: number;\n}\n\ninterface ToastContextValue {\n toasts: Toast[];\n addToast: (toast: Omit<Toast, 'id'>) => void;\n removeToast: (id: string) => void;\n success: (title: string, description?: string) => void;\n error: (title: string, description?: string) => void;\n warning: (title: string, description?: string) => void;\n info: (title: string, description?: string) => void;\n}\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst ToastContext = createContext<ToastContextValue | null>(null);\n\nexport function useToast() {\n const context = useContext(ToastContext);\n if (!context) {\n throw new Error('useToast must be used within a ToastProvider');\n }\n return context;\n}\n\n// ============================================================================\n// Provider\n// ============================================================================\n\ninterface ToastProviderProps {\n children: ReactNode;\n}\n\nexport function ToastProvider({ children }: ToastProviderProps) {\n const [toasts, setToasts] = useState<Toast[]>([]);\n\n const addToast = useCallback((toast: Omit<Toast, 'id'>) => {\n const id = `toast-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n setToasts((prev) => [...prev, { ...toast, id }]);\n }, []);\n\n const removeToast = useCallback((id: string) => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n }, []);\n\n const success = useCallback(\n (title: string, description?: string) => {\n addToast({ type: 'success', title, description, duration: 4000 });\n },\n [addToast]\n );\n\n const error = useCallback(\n (title: string, description?: string) => {\n addToast({ type: 'error', title, description, duration: 6000 });\n },\n [addToast]\n );\n\n const warning = useCallback(\n (title: string, description?: string) => {\n addToast({ type: 'warning', title, description, duration: 5000 });\n },\n [addToast]\n );\n\n const info = useCallback(\n (title: string, description?: string) => {\n addToast({ type: 'info', title, description, duration: 4000 });\n },\n [addToast]\n );\n\n return (\n <ToastContext.Provider value={{ toasts, addToast, removeToast, success, error, warning, info }}>\n {children}\n <ToastContainer toasts={toasts} removeToast={removeToast} />\n </ToastContext.Provider>\n );\n}\n\n// ============================================================================\n// Toast Container\n// ============================================================================\n\ninterface ToastContainerProps {\n toasts: Toast[];\n removeToast: (id: string) => void;\n}\n\nfunction ToastContainer({ toasts, removeToast }: ToastContainerProps) {\n if (toasts.length === 0) return null;\n\n return (\n <div className=\"fixed bottom-4 right-4 z-[100] flex flex-col gap-2 max-w-sm\">\n {toasts.map((toast) => (\n <ToastItem key={toast.id} toast={toast} onDismiss={() => removeToast(toast.id)} />\n ))}\n </div>\n );\n}\n\n// ============================================================================\n// Toast Item\n// ============================================================================\n\ninterface ToastItemProps {\n toast: Toast;\n onDismiss: () => void;\n}\n\nconst toastStyles: Record<\n ToastType,\n { icon: typeof CheckCircle2; bg: string; border: string; iconColor: string }\n> = {\n success: {\n icon: CheckCircle2,\n bg: 'bg-status-success-bg',\n border: 'border-status-success-border',\n iconColor: 'text-status-success-text',\n },\n error: {\n icon: AlertCircle,\n bg: 'bg-status-error-bg',\n border: 'border-status-error-border',\n iconColor: 'text-status-error-text',\n },\n warning: {\n icon: AlertTriangle,\n bg: 'bg-status-warning-bg',\n border: 'border-status-warning-border',\n iconColor: 'text-status-warning-text',\n },\n info: {\n icon: Info,\n bg: 'bg-status-info-bg',\n border: 'border-status-info-border',\n iconColor: 'text-status-info-text',\n },\n};\n\nfunction ToastItem({ toast, onDismiss }: ToastItemProps) {\n const { icon: Icon, bg, border, iconColor } = toastStyles[toast.type];\n\n // Auto-dismiss\n useEffect(() => {\n const duration = toast.duration ?? 4000;\n const timer = setTimeout(onDismiss, duration);\n return () => clearTimeout(timer);\n }, [toast.duration, onDismiss]);\n\n return (\n <div\n className={cn(\n 'flex items-start gap-3 rounded-lg border p-4 shadow-lg backdrop-blur-sm',\n 'animate-in slide-in-from-right-full duration-300',\n 'bg-bg-surface/95',\n bg,\n border\n )}\n role=\"alert\"\n >\n <Icon className={cn('h-5 w-5 shrink-0 mt-0.5', iconColor)} />\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">{toast.title}</p>\n {toast.description && (\n <p className=\"mt-1 text-xs text-text-secondary\">{toast.description}</p>\n )}\n </div>\n <button\n onClick={onDismiss}\n className=\"shrink-0 rounded-md p-1 hover:bg-bg-sunken transition-colors\"\n aria-label=\"Dismiss\"\n >\n <X className=\"h-4 w-4 text-text-secondary\" />\n </button>\n </div>\n );\n}\n"],"mappings":";;;;;AAgCA,IAAM,IAAe,EAAwC,KAAK;AAElE,SAAgB,IAAW;CACzB,IAAM,IAAU,EAAW,EAAa;AACxC,KAAI,CAAC,EACH,OAAU,MAAM,+CAA+C;AAEjE,QAAO;;AAWT,SAAgB,EAAc,EAAE,eAAgC;CAC9D,IAAM,CAAC,GAAQ,KAAa,EAAkB,EAAE,CAAC,EAE3C,IAAW,GAAa,MAA6B;EACzD,IAAM,IAAK,SAAS,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;AACxE,KAAW,MAAS,CAAC,GAAG,GAAM;GAAE,GAAG;GAAO;GAAI,CAAC,CAAC;IAC/C,EAAE,CAAC,EAEA,IAAc,GAAa,MAAe;AAC9C,KAAW,MAAS,EAAK,QAAQ,MAAM,EAAE,OAAO,EAAG,CAAC;IACnD,EAAE,CAAC,EAEA,IAAU,GACb,GAAe,MAAyB;AACvC,IAAS;GAAE,MAAM;GAAW;GAAO;GAAa,UAAU;GAAM,CAAC;IAEnE,CAAC,EAAS,CACX,EAEK,IAAQ,GACX,GAAe,MAAyB;AACvC,IAAS;GAAE,MAAM;GAAS;GAAO;GAAa,UAAU;GAAM,CAAC;IAEjE,CAAC,EAAS,CACX,EAEK,IAAU,GACb,GAAe,MAAyB;AACvC,IAAS;GAAE,MAAM;GAAW;GAAO;GAAa,UAAU;GAAM,CAAC;IAEnE,CAAC,EAAS,CACX,EAEK,IAAO,GACV,GAAe,MAAyB;AACvC,IAAS;GAAE,MAAM;GAAQ;GAAO;GAAa,UAAU;GAAM,CAAC;IAEhE,CAAC,EAAS,CACX;AAED,QACE,kBAAC,EAAa,UAAd;EAAuB,OAAO;GAAE;GAAQ;GAAU;GAAa;GAAS;GAAO;GAAS;GAAM;YAA9F,CACG,GACD,kBAAC,GAAD;GAAwB;GAAqB;GAAe,CAAA,CACtC;;;AAa5B,SAAS,EAAe,EAAE,WAAQ,kBAAoC;AAGpE,QAFI,EAAO,WAAW,IAAU,OAG9B,kBAAC,OAAD;EAAK,WAAU;YACZ,EAAO,KAAK,MACX,kBAAC,GAAD;GAAiC;GAAO,iBAAiB,EAAY,EAAM,GAAG;GAAI,EAAlE,EAAM,GAA4D,CAClF;EACE,CAAA;;AAaV,IAAM,IAGF;CACF,SAAS;EACP,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,WAAW;EACZ;CACD,OAAO;EACL,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,WAAW;EACZ;CACD,SAAS;EACP,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,WAAW;EACZ;CACD,MAAM;EACJ,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,WAAW;EACZ;CACF;AAED,SAAS,EAAU,EAAE,UAAO,gBAA6B;CACvD,IAAM,EAAE,MAAM,GAAM,OAAI,WAAQ,iBAAc,EAAY,EAAM;AAShE,QANA,QAAgB;EACd,IAAM,IAAW,EAAM,YAAY,KAC7B,IAAQ,WAAW,GAAW,EAAS;AAC7C,eAAa,aAAa,EAAM;IAC/B,CAAC,EAAM,UAAU,EAAU,CAAC,EAG7B,kBAAC,OAAD;EACE,WAAW,EACT,2EACA,oDACA,oBACA,GACA,EACD;EACD,MAAK;YARP;GAUE,kBAAC,GAAD,EAAM,WAAW,EAAG,2BAA2B,EAAU,EAAI,CAAA;GAC7D,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAAyC,EAAM;KAAU,CAAA,EACrE,EAAM,eACL,kBAAC,KAAD;KAAG,WAAU;eAAoC,EAAM;KAAgB,CAAA,CAErE;;GACN,kBAAC,UAAD;IACE,SAAS;IACT,WAAU;IACV,cAAW;cAEX,kBAAC,GAAD,EAAG,WAAU,+BAAgC,CAAA;IACtC,CAAA;GACL"}
@@ -2,7 +2,7 @@ import { cn as e } from "../../utils/cn.js";
2
2
  import { Input as t } from "../ui.js";
3
3
  import { FormRow as n, FormSection as r, FormSheet as i, FormSheetFooter as a } from "../FormSheet.js";
4
4
  import { useForm as o } from "../../../node_modules/react-hook-form/dist/index.esm.js";
5
- import { a as s } from "../../../node_modules/@hookform/resolvers/zod/dist/zod.js";
5
+ import { u as s } from "../../../node_modules/@hookform/resolvers/zod/dist/zod.js";
6
6
  import { coerce as c, enumType as l, objectType as u, stringType as d } from "../../../node_modules/zod/v3/types.js";
7
7
  import * as f from "react";
8
8
  import { jsx as p, jsxs as m } from "react/jsx-runtime";
@@ -265,7 +265,7 @@ function x({ open: e, onClose: c, subscription: l, tenantId: u, tenants: d = [],
265
265
  }), /* @__PURE__ */ m("span", { children: ["× ", P] })]
266
266
  }),
267
267
  N === "yearly" && /* @__PURE__ */ m("div", {
268
- className: "flex justify-between text-sm text-success",
268
+ className: "flex justify-between text-sm text-status-success-text",
269
269
  children: [/* @__PURE__ */ p("span", { children: "Annual Discount" }), /* @__PURE__ */ p("span", { children: "-20%" })]
270
270
  }),
271
271
  /* @__PURE__ */ p("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"SubscriptionForm.js","names":[],"sources":["../../../../src/admin/components/forms/SubscriptionForm.tsx"],"sourcesContent":["import * as React from 'react';\nimport { useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { z } from 'zod';\nimport { FormSheet, FormSheetFooter, FormSection, FormRow } from '../FormSheet';\nimport { Input } from '../ui';\nimport { cn } from '../../utils/cn';\nimport type { Subscription } from '../../types';\n\n/**\n * Form field wrapper with label and error\n */\ninterface FormFieldProps {\n label: string;\n error?: string;\n required?: boolean;\n children: React.ReactNode;\n description?: string;\n}\n\nfunction FormField({ label, error, required, children, description }: FormFieldProps) {\n return (\n <div className=\"space-y-1.5\">\n <label className=\"text-sm font-medium text-text-primary\">\n {label}\n {required && <span className=\"text-status-error-text ml-0.5\">*</span>}\n </label>\n {children}\n {description && !error && <p className=\"text-xs text-text-secondary\">{description}</p>}\n {error && <p className=\"text-xs text-status-error-text\">{error}</p>}\n </div>\n );\n}\n\n/**\n * Select wrapper styled like Input\n */\ninterface SelectFieldProps extends React.SelectHTMLAttributes<HTMLSelectElement> {\n options: { value: string; label: string }[];\n placeholder?: string;\n}\n\nconst SelectField = React.forwardRef<HTMLSelectElement, SelectFieldProps>(\n ({ className, options, placeholder, ...props }, ref) => (\n <select\n ref={ref}\n className={cn(\n 'flex h-9 w-full rounded-md border border-border-default bg-transparent px-3 py-1 text-sm shadow-sm transition-colors',\n 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n className\n )}\n {...props}\n >\n {placeholder && (\n <option value=\"\" disabled>\n {placeholder}\n </option>\n )}\n {options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n )\n);\nSelectField.displayName = 'SelectField';\n\n// Unified form schema (handles both create and edit)\nconst subscriptionFormSchema = z.object({\n tenantId: z.string().min(1, 'Tenant is required'),\n plan: z.enum(['Free', 'Starter', 'Professional', 'Enterprise', 'Custom']),\n billingCycle: z.enum(['monthly', 'yearly']),\n seats: z.coerce.number().int().min(1, 'At least 1 seat is required'),\n status: z.enum(['active', 'cancelled', 'past_due', 'trialing', 'paused']).optional(),\n startDate: z.string().optional(),\n trialEndDate: z.string().optional(),\n});\n\ntype SubscriptionFormData = z.infer<typeof subscriptionFormSchema>;\n\n/**\n * SubscriptionForm props\n */\nexport interface SubscriptionFormProps {\n /** Whether the form is open */\n open: boolean;\n /** Callback when the form should close */\n onClose: () => void;\n /** Existing subscription data for editing (undefined for create) */\n subscription?: Subscription;\n /** Pre-selected tenant ID */\n tenantId?: string;\n /** List of available tenants for selection */\n tenants?: { id: string; name: string }[];\n /** Callback on successful save */\n onSave: (data: SubscriptionFormData & { id?: string }) => Promise<void>;\n}\n\nconst planOptions = [\n { value: 'Free', label: 'Free - $0/mo' },\n { value: 'Starter', label: 'Starter - $29/mo' },\n { value: 'Professional', label: 'Professional - $99/mo' },\n { value: 'Enterprise', label: 'Enterprise - $299/mo' },\n { value: 'Custom', label: 'Custom Pricing' },\n];\n\nconst billingCycleOptions = [\n { value: 'monthly', label: 'Monthly' },\n { value: 'yearly', label: 'Yearly (Save 20%)' },\n];\n\nconst statusOptions = [\n { value: 'active', label: 'Active' },\n { value: 'cancelled', label: 'Cancelled' },\n { value: 'past_due', label: 'Past Due' },\n { value: 'trialing', label: 'Trialing' },\n { value: 'paused', label: 'Paused' },\n];\n\n/**\n * SubscriptionForm - Create/Edit subscription form in a slide-over panel\n */\nexport function SubscriptionForm({\n open,\n onClose,\n subscription,\n tenantId,\n tenants = [],\n onSave,\n}: SubscriptionFormProps) {\n const isEditing = !!subscription;\n const formId = 'subscription-form';\n\n const defaultValues: SubscriptionFormData = {\n tenantId: subscription?.tenantId ?? tenantId ?? '',\n plan: (subscription?.plan ?? 'Starter') as SubscriptionFormData['plan'],\n billingCycle: (subscription?.billingCycle ?? 'monthly') as SubscriptionFormData['billingCycle'],\n seats: subscription?.seats ?? 1,\n status: subscription?.status as SubscriptionFormData['status'],\n startDate: subscription?.startDate\n ? new Date(subscription.startDate).toISOString().split('T')[0]\n : new Date().toISOString().split('T')[0],\n trialEndDate: subscription?.trialEndDate\n ? new Date(subscription.trialEndDate).toISOString().split('T')[0]\n : undefined,\n };\n\n const {\n register,\n handleSubmit,\n formState: { errors, isSubmitting },\n reset,\n watch,\n } = useForm<SubscriptionFormData>({\n resolver: zodResolver(subscriptionFormSchema),\n defaultValues,\n });\n\n // Reset form when subscription or tenantId changes\n React.useEffect(() => {\n if (open) {\n reset(defaultValues);\n }\n }, [open, subscription?.id, tenantId]);\n\n const onSubmit = async (data: SubscriptionFormData) => {\n try {\n await onSave({ ...data, id: subscription?.id });\n onClose();\n } catch (error) {\n console.error('Failed to save subscription:', error);\n }\n };\n\n // Calculate estimated cost\n const plan = watch('plan');\n const billingCycle = watch('billingCycle');\n const seats = watch('seats') || 1;\n\n const planPrices: Record<string, number> = {\n Free: 0,\n Starter: 29,\n Professional: 99,\n Enterprise: 299,\n Custom: 0,\n };\n\n const monthlyPrice = planPrices[plan] || 0;\n const yearlyMultiplier = billingCycle === 'yearly' ? 0.8 : 1; // 20% discount for yearly\n const estimatedCost = monthlyPrice * seats * yearlyMultiplier;\n const billingPeriod = billingCycle === 'yearly' ? 12 : 1;\n const totalPerPeriod = estimatedCost * billingPeriod;\n\n return (\n <FormSheet\n open={open}\n onClose={onClose}\n title={isEditing ? 'Edit Subscription' : 'Create Subscription'}\n description={\n isEditing ? 'Modify subscription details' : 'Set up a new subscription for a tenant'\n }\n width=\"md\"\n footer={\n <FormSheetFooter\n formId={formId}\n submitLabel={isEditing ? 'Save Changes' : 'Create Subscription'}\n onCancel={onClose}\n isSubmitting={isSubmitting}\n />\n }\n >\n <form id={formId} onSubmit={handleSubmit(onSubmit)} className=\"space-y-6\">\n {/* Tenant Selection */}\n {tenants.length > 0 && !tenantId && (\n <FormSection>\n <FormField\n label=\"Tenant\"\n required\n error={errors.tenantId?.message}\n description=\"Select the tenant for this subscription\"\n >\n <SelectField\n {...register('tenantId')}\n options={tenants.map((t) => ({ value: t.id, label: t.name }))}\n placeholder=\"Select a tenant...\"\n />\n </FormField>\n </FormSection>\n )}\n\n {/* Plan Selection */}\n <FormSection title=\"Plan Details\">\n <FormRow>\n <FormField label=\"Plan\" required>\n <SelectField {...register('plan')} options={planOptions} />\n </FormField>\n\n <FormField label=\"Billing Cycle\" required>\n <SelectField {...register('billingCycle')} options={billingCycleOptions} />\n </FormField>\n </FormRow>\n\n <FormField\n label=\"Seats\"\n required\n error={errors.seats?.message}\n description=\"Number of user seats included\"\n >\n <Input type=\"number\" {...register('seats')} placeholder=\"1\" min={1} max={10000} />\n </FormField>\n\n {isEditing && (\n <FormField label=\"Status\">\n <SelectField {...register('status')} options={statusOptions} />\n </FormField>\n )}\n </FormSection>\n\n {/* Dates */}\n <FormSection title=\"Dates\">\n <FormRow>\n <FormField label=\"Start Date\" error={errors.startDate?.message}>\n <Input type=\"date\" {...register('startDate')} />\n </FormField>\n\n <FormField\n label=\"Trial End Date\"\n error={errors.trialEndDate?.message}\n description=\"Leave empty for no trial\"\n >\n <Input type=\"date\" {...register('trialEndDate')} />\n </FormField>\n </FormRow>\n </FormSection>\n\n {/* Cost Summary */}\n {plan !== 'Custom' && monthlyPrice > 0 && (\n <FormSection title=\"Cost Summary\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-4\">\n <div className=\"space-y-2\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Base Price</span>\n <span>${monthlyPrice}/user/month</span>\n </div>\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Seats</span>\n <span>× {seats}</span>\n </div>\n {billingCycle === 'yearly' && (\n <div className=\"flex justify-between text-sm text-success\">\n <span>Annual Discount</span>\n <span>-20%</span>\n </div>\n )}\n <div className=\"border-t border-border-subtle pt-2 mt-2\">\n <div className=\"flex justify-between font-medium\">\n <span>{billingCycle === 'yearly' ? 'Annual Total' : 'Monthly Total'}</span>\n <span className=\"text-lg\">\n ${totalPerPeriod.toFixed(2)}\n <span className=\"text-sm font-normal text-text-secondary\">\n /{billingCycle === 'yearly' ? 'year' : 'month'}\n </span>\n </span>\n </div>\n </div>\n </div>\n </div>\n </FormSection>\n )}\n </form>\n </FormSheet>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SubscriptionForm.js","names":[],"sources":["../../../../src/admin/components/forms/SubscriptionForm.tsx"],"sourcesContent":["import * as React from 'react';\nimport { useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { z } from 'zod';\nimport { FormSheet, FormSheetFooter, FormSection, FormRow } from '../FormSheet';\nimport { Input } from '../ui';\nimport { cn } from '../../utils/cn';\nimport type { Subscription } from '../../types';\n\n/**\n * Form field wrapper with label and error\n */\ninterface FormFieldProps {\n label: string;\n error?: string;\n required?: boolean;\n children: React.ReactNode;\n description?: string;\n}\n\nfunction FormField({ label, error, required, children, description }: FormFieldProps) {\n return (\n <div className=\"space-y-1.5\">\n <label className=\"text-sm font-medium text-text-primary\">\n {label}\n {required && <span className=\"text-status-error-text ml-0.5\">*</span>}\n </label>\n {children}\n {description && !error && <p className=\"text-xs text-text-secondary\">{description}</p>}\n {error && <p className=\"text-xs text-status-error-text\">{error}</p>}\n </div>\n );\n}\n\n/**\n * Select wrapper styled like Input\n */\ninterface SelectFieldProps extends React.SelectHTMLAttributes<HTMLSelectElement> {\n options: { value: string; label: string }[];\n placeholder?: string;\n}\n\nconst SelectField = React.forwardRef<HTMLSelectElement, SelectFieldProps>(\n ({ className, options, placeholder, ...props }, ref) => (\n <select\n ref={ref}\n className={cn(\n 'flex h-9 w-full rounded-md border border-border-default bg-transparent px-3 py-1 text-sm shadow-sm transition-colors',\n 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n className\n )}\n {...props}\n >\n {placeholder && (\n <option value=\"\" disabled>\n {placeholder}\n </option>\n )}\n {options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n )\n);\nSelectField.displayName = 'SelectField';\n\n// Unified form schema (handles both create and edit)\nconst subscriptionFormSchema = z.object({\n tenantId: z.string().min(1, 'Tenant is required'),\n plan: z.enum(['Free', 'Starter', 'Professional', 'Enterprise', 'Custom']),\n billingCycle: z.enum(['monthly', 'yearly']),\n seats: z.coerce.number().int().min(1, 'At least 1 seat is required'),\n status: z.enum(['active', 'cancelled', 'past_due', 'trialing', 'paused']).optional(),\n startDate: z.string().optional(),\n trialEndDate: z.string().optional(),\n});\n\ntype SubscriptionFormData = z.infer<typeof subscriptionFormSchema>;\n\n/**\n * SubscriptionForm props\n */\nexport interface SubscriptionFormProps {\n /** Whether the form is open */\n open: boolean;\n /** Callback when the form should close */\n onClose: () => void;\n /** Existing subscription data for editing (undefined for create) */\n subscription?: Subscription;\n /** Pre-selected tenant ID */\n tenantId?: string;\n /** List of available tenants for selection */\n tenants?: { id: string; name: string }[];\n /** Callback on successful save */\n onSave: (data: SubscriptionFormData & { id?: string }) => Promise<void>;\n}\n\nconst planOptions = [\n { value: 'Free', label: 'Free - $0/mo' },\n { value: 'Starter', label: 'Starter - $29/mo' },\n { value: 'Professional', label: 'Professional - $99/mo' },\n { value: 'Enterprise', label: 'Enterprise - $299/mo' },\n { value: 'Custom', label: 'Custom Pricing' },\n];\n\nconst billingCycleOptions = [\n { value: 'monthly', label: 'Monthly' },\n { value: 'yearly', label: 'Yearly (Save 20%)' },\n];\n\nconst statusOptions = [\n { value: 'active', label: 'Active' },\n { value: 'cancelled', label: 'Cancelled' },\n { value: 'past_due', label: 'Past Due' },\n { value: 'trialing', label: 'Trialing' },\n { value: 'paused', label: 'Paused' },\n];\n\n/**\n * SubscriptionForm - Create/Edit subscription form in a slide-over panel\n */\nexport function SubscriptionForm({\n open,\n onClose,\n subscription,\n tenantId,\n tenants = [],\n onSave,\n}: SubscriptionFormProps) {\n const isEditing = !!subscription;\n const formId = 'subscription-form';\n\n const defaultValues: SubscriptionFormData = {\n tenantId: subscription?.tenantId ?? tenantId ?? '',\n plan: (subscription?.plan ?? 'Starter') as SubscriptionFormData['plan'],\n billingCycle: (subscription?.billingCycle ?? 'monthly') as SubscriptionFormData['billingCycle'],\n seats: subscription?.seats ?? 1,\n status: subscription?.status as SubscriptionFormData['status'],\n startDate: subscription?.startDate\n ? new Date(subscription.startDate).toISOString().split('T')[0]\n : new Date().toISOString().split('T')[0],\n trialEndDate: subscription?.trialEndDate\n ? new Date(subscription.trialEndDate).toISOString().split('T')[0]\n : undefined,\n };\n\n const {\n register,\n handleSubmit,\n formState: { errors, isSubmitting },\n reset,\n watch,\n } = useForm<SubscriptionFormData>({\n resolver: zodResolver(subscriptionFormSchema),\n defaultValues,\n });\n\n // Reset form when subscription or tenantId changes\n React.useEffect(() => {\n if (open) {\n reset(defaultValues);\n }\n }, [open, subscription?.id, tenantId]);\n\n const onSubmit = async (data: SubscriptionFormData) => {\n try {\n await onSave({ ...data, id: subscription?.id });\n onClose();\n } catch (error) {\n console.error('Failed to save subscription:', error);\n }\n };\n\n // Calculate estimated cost\n const plan = watch('plan');\n const billingCycle = watch('billingCycle');\n const seats = watch('seats') || 1;\n\n const planPrices: Record<string, number> = {\n Free: 0,\n Starter: 29,\n Professional: 99,\n Enterprise: 299,\n Custom: 0,\n };\n\n const monthlyPrice = planPrices[plan] || 0;\n const yearlyMultiplier = billingCycle === 'yearly' ? 0.8 : 1; // 20% discount for yearly\n const estimatedCost = monthlyPrice * seats * yearlyMultiplier;\n const billingPeriod = billingCycle === 'yearly' ? 12 : 1;\n const totalPerPeriod = estimatedCost * billingPeriod;\n\n return (\n <FormSheet\n open={open}\n onClose={onClose}\n title={isEditing ? 'Edit Subscription' : 'Create Subscription'}\n description={\n isEditing ? 'Modify subscription details' : 'Set up a new subscription for a tenant'\n }\n width=\"md\"\n footer={\n <FormSheetFooter\n formId={formId}\n submitLabel={isEditing ? 'Save Changes' : 'Create Subscription'}\n onCancel={onClose}\n isSubmitting={isSubmitting}\n />\n }\n >\n <form id={formId} onSubmit={handleSubmit(onSubmit)} className=\"space-y-6\">\n {/* Tenant Selection */}\n {tenants.length > 0 && !tenantId && (\n <FormSection>\n <FormField\n label=\"Tenant\"\n required\n error={errors.tenantId?.message}\n description=\"Select the tenant for this subscription\"\n >\n <SelectField\n {...register('tenantId')}\n options={tenants.map((t) => ({ value: t.id, label: t.name }))}\n placeholder=\"Select a tenant...\"\n />\n </FormField>\n </FormSection>\n )}\n\n {/* Plan Selection */}\n <FormSection title=\"Plan Details\">\n <FormRow>\n <FormField label=\"Plan\" required>\n <SelectField {...register('plan')} options={planOptions} />\n </FormField>\n\n <FormField label=\"Billing Cycle\" required>\n <SelectField {...register('billingCycle')} options={billingCycleOptions} />\n </FormField>\n </FormRow>\n\n <FormField\n label=\"Seats\"\n required\n error={errors.seats?.message}\n description=\"Number of user seats included\"\n >\n <Input type=\"number\" {...register('seats')} placeholder=\"1\" min={1} max={10000} />\n </FormField>\n\n {isEditing && (\n <FormField label=\"Status\">\n <SelectField {...register('status')} options={statusOptions} />\n </FormField>\n )}\n </FormSection>\n\n {/* Dates */}\n <FormSection title=\"Dates\">\n <FormRow>\n <FormField label=\"Start Date\" error={errors.startDate?.message}>\n <Input type=\"date\" {...register('startDate')} />\n </FormField>\n\n <FormField\n label=\"Trial End Date\"\n error={errors.trialEndDate?.message}\n description=\"Leave empty for no trial\"\n >\n <Input type=\"date\" {...register('trialEndDate')} />\n </FormField>\n </FormRow>\n </FormSection>\n\n {/* Cost Summary */}\n {plan !== 'Custom' && monthlyPrice > 0 && (\n <FormSection title=\"Cost Summary\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-4\">\n <div className=\"space-y-2\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Base Price</span>\n <span>${monthlyPrice}/user/month</span>\n </div>\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Seats</span>\n <span>× {seats}</span>\n </div>\n {billingCycle === 'yearly' && (\n <div className=\"flex justify-between text-sm text-status-success-text\">\n <span>Annual Discount</span>\n <span>-20%</span>\n </div>\n )}\n <div className=\"border-t border-border-subtle pt-2 mt-2\">\n <div className=\"flex justify-between font-medium\">\n <span>{billingCycle === 'yearly' ? 'Annual Total' : 'Monthly Total'}</span>\n <span className=\"text-lg\">\n ${totalPerPeriod.toFixed(2)}\n <span className=\"text-sm font-normal text-text-secondary\">\n /{billingCycle === 'yearly' ? 'year' : 'month'}\n </span>\n </span>\n </div>\n </div>\n </div>\n </div>\n </FormSection>\n )}\n </form>\n </FormSheet>\n );\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { cn as e } from "../../utils/cn.js";
2
2
  import { Input as t } from "../ui.js";
3
3
  import { FormRow as n, FormSection as r, FormSheet as i, FormSheetFooter as a } from "../FormSheet.js";
4
4
  import { Controller as o, useForm as s } from "../../../node_modules/react-hook-form/dist/index.esm.js";
5
- import { a as c } from "../../../node_modules/@hookform/resolvers/zod/dist/zod.js";
5
+ import { u as c } from "../../../node_modules/@hookform/resolvers/zod/dist/zod.js";
6
6
  import { booleanType as l, coerce as u, literalType as d, objectType as f, stringType as p } from "../../../node_modules/zod/v3/types.js";
7
7
  import { tWithFallback as m } from "../../utils/i18n.js";
8
8
  import * as h from "react";
@@ -2,7 +2,7 @@ import { cn as e } from "../../utils/cn.js";
2
2
  import { Input as t } from "../ui.js";
3
3
  import { FormRow as n, FormSection as r, FormSheet as i, FormSheetFooter as a } from "../FormSheet.js";
4
4
  import { Controller as o, useForm as s } from "../../../node_modules/react-hook-form/dist/index.esm.js";
5
- import { a as c } from "../../../node_modules/@hookform/resolvers/zod/dist/zod.js";
5
+ import { u as c } from "../../../node_modules/@hookform/resolvers/zod/dist/zod.js";
6
6
  import { booleanType as l, enumType as u, objectType as d, stringType as f } from "../../../node_modules/zod/v3/types.js";
7
7
  import * as p from "react";
8
8
  import { jsx as m, jsxs as h } from "react/jsx-runtime";
@@ -1,108 +1,109 @@
1
1
  import { cn as e } from "../utils/cn.js";
2
2
  import { forwardRef as t } from "react";
3
3
  import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ import { IllustratedEmptyState as i } from "@burdenoff/fe-libs/ui";
4
5
  //#region src/admin/components/ui.tsx
5
- var i = t(({ className: t, children: r, ...i }, a) => /* @__PURE__ */ n("div", {
6
+ var a = t(({ className: t, children: r, ...i }, a) => /* @__PURE__ */ n("div", {
6
7
  ref: a,
7
- className: e("rounded-lg border border-border-subtle bg-bg-surface text-text-primary shadow-sm", t),
8
+ className: e("rounded-card border border-border-seam bg-bg-surface text-text-primary shadow-elevation-1 transition-[box-shadow,border-color] duration-200 hover:border-border-strong", t),
8
9
  ...i,
9
10
  children: r
10
11
  }));
11
- i.displayName = "Card";
12
- var a = t(({ className: t, children: r, ...i }, a) => /* @__PURE__ */ n("div", {
12
+ a.displayName = "Card";
13
+ var o = t(({ className: t, children: r, ...i }, a) => /* @__PURE__ */ n("div", {
13
14
  ref: a,
14
15
  className: e("flex flex-col space-y-1.5 p-6", t),
15
16
  ...i,
16
17
  children: r
17
18
  }));
18
- a.displayName = "CardHeader";
19
- var o = t(({ className: t, children: r, ...i }, a) => /* @__PURE__ */ n("h3", {
19
+ o.displayName = "CardHeader";
20
+ var s = t(({ className: t, children: r, ...i }, a) => /* @__PURE__ */ n("h3", {
20
21
  ref: a,
21
22
  className: e("text-2xl font-semibold leading-none tracking-tight", t),
22
23
  ...i,
23
24
  children: r
24
25
  }));
25
- o.displayName = "CardTitle";
26
- var s = t(({ className: t, children: r, ...i }, a) => /* @__PURE__ */ n("p", {
26
+ s.displayName = "CardTitle";
27
+ var c = t(({ className: t, children: r, ...i }, a) => /* @__PURE__ */ n("p", {
27
28
  ref: a,
28
29
  className: e("text-sm text-text-secondary", t),
29
30
  ...i,
30
31
  children: r
31
32
  }));
32
- s.displayName = "CardDescription";
33
- var c = t(({ className: t, children: r, ...i }, a) => /* @__PURE__ */ n("div", {
33
+ c.displayName = "CardDescription";
34
+ var l = t(({ className: t, children: r, ...i }, a) => /* @__PURE__ */ n("div", {
34
35
  ref: a,
35
36
  className: e("p-6 pt-0", t),
36
37
  ...i,
37
38
  children: r
38
39
  }));
39
- c.displayName = "CardContent";
40
- var l = t(({ className: t, children: r, ...i }, a) => /* @__PURE__ */ n("div", {
40
+ l.displayName = "CardContent";
41
+ var u = t(({ className: t, children: r, ...i }, a) => /* @__PURE__ */ n("div", {
41
42
  ref: a,
42
43
  className: e("flex items-center p-6 pt-0", t),
43
44
  ...i,
44
45
  children: r
45
46
  }));
46
- l.displayName = "CardFooter";
47
- var u = {
47
+ u.displayName = "CardFooter";
48
+ var d = {
48
49
  default: "bg-action-primary-bg text-action-primary-text",
49
50
  secondary: "bg-secondary text-secondary-foreground",
50
- success: "bg-success/10 text-success border-success/20",
51
- warning: "bg-warning/10 text-warning border-warning/20",
52
- destructive: "bg-status-error-bg/10 text-status-error-text border-status-error-border/20",
51
+ success: "bg-status-success-bg text-status-success-text border-status-success-border",
52
+ warning: "bg-status-warning-bg text-status-warning-text border-status-warning-border",
53
+ destructive: "bg-status-error-bg text-status-error-text border-status-error-border",
53
54
  outline: "border-border-subtle bg-transparent text-text-primary"
54
- }, d = t(({ className: t, variant: r = "default", children: i, ...a }, o) => /* @__PURE__ */ n("span", {
55
+ }, f = t(({ className: t, variant: r = "default", children: i, ...a }, o) => /* @__PURE__ */ n("span", {
55
56
  ref: o,
56
- className: e("inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors", u[r], t),
57
+ className: e("inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors", d[r], t),
57
58
  ...a,
58
59
  children: i
59
60
  }));
60
- d.displayName = "Badge";
61
- var f = {
61
+ f.displayName = "Badge";
62
+ var p = {
62
63
  default: "bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90",
63
64
  secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
64
65
  outline: "border border-border-default bg-bg-surface hover:bg-accent hover:text-text-primary",
65
66
  ghost: "hover:bg-accent hover:text-text-primary",
66
67
  destructive: "bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90",
67
68
  link: "text-primary underline-offset-4 hover:underline"
68
- }, p = {
69
+ }, m = {
69
70
  default: "h-10 px-4 py-2",
70
71
  sm: "h-9 rounded-md px-3",
71
72
  lg: "h-11 rounded-md px-8",
72
73
  icon: "h-10 w-10"
73
- }, m = t(({ className: t, variant: r = "default", size: i = "default", children: a, ...o }, s) => /* @__PURE__ */ n("button", {
74
+ }, h = t(({ className: t, variant: r = "default", size: i = "default", children: a, ...o }, s) => /* @__PURE__ */ n("button", {
74
75
  ref: s,
75
- className: e("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] focus-visible:ring-offset-2", "disabled:pointer-events-none disabled:opacity-50", f[r], p[i], t),
76
+ className: e("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] focus-visible:ring-offset-2", "disabled:pointer-events-none disabled:opacity-50", p[r], m[i], t),
76
77
  ...o,
77
78
  children: a
78
79
  }));
79
- m.displayName = "Button";
80
- var h = t(({ className: t, type: r, ...i }, a) => /* @__PURE__ */ n("input", {
80
+ h.displayName = "Button";
81
+ var g = t(({ className: t, type: r, ...i }, a) => /* @__PURE__ */ n("input", {
81
82
  type: r,
82
83
  className: e("flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm ring-offset-background", "file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-text-primary", "placeholder:text-text-secondary", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] focus-visible:ring-offset-2", "disabled:cursor-not-allowed disabled:opacity-50", t),
83
84
  ref: a,
84
85
  ...i
85
86
  }));
86
- h.displayName = "Input";
87
- var g = t(({ className: t, ...r }, i) => /* @__PURE__ */ n("div", {
87
+ g.displayName = "Input";
88
+ var _ = t(({ className: t, ...r }, i) => /* @__PURE__ */ n("div", {
88
89
  ref: i,
89
90
  className: e("animate-pulse rounded-md bg-bg-sunken", t),
90
91
  ...r
91
92
  }));
92
- g.displayName = "Skeleton";
93
- var _ = t(({ className: t, orientation: r = "horizontal", ...i }, a) => /* @__PURE__ */ n("div", {
93
+ _.displayName = "Skeleton";
94
+ var v = t(({ className: t, orientation: r = "horizontal", ...i }, a) => /* @__PURE__ */ n("div", {
94
95
  ref: a,
95
96
  className: e("shrink-0 bg-border", r === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", t),
96
97
  ...i
97
98
  }));
98
- _.displayName = "Separator";
99
- var v = {
99
+ v.displayName = "Separator";
100
+ var y = {
100
101
  sm: "h-8 w-8 text-xs",
101
102
  default: "h-10 w-10 text-sm",
102
103
  lg: "h-12 w-12 text-base"
103
- }, y = t(({ className: t, src: r, alt: i, fallback: a, size: o = "default", ...s }, c) => /* @__PURE__ */ n("div", {
104
+ }, b = t(({ className: t, src: r, alt: i, fallback: a, size: o = "default", ...s }, c) => /* @__PURE__ */ n("div", {
104
105
  ref: c,
105
- className: e("relative flex shrink-0 overflow-hidden rounded-full bg-bg-sunken", v[o], t),
106
+ className: e("relative flex shrink-0 overflow-hidden rounded-full bg-bg-sunken", y[o], t),
106
107
  ...s,
107
108
  children: r ? /* @__PURE__ */ n("img", {
108
109
  src: r,
@@ -113,9 +114,15 @@ var v = {
113
114
  children: a
114
115
  })
115
116
  }));
116
- y.displayName = "Avatar";
117
- function b({ icon: e, title: t, description: i, action: a }) {
118
- return /* @__PURE__ */ r("div", {
117
+ b.displayName = "Avatar";
118
+ function x({ icon: e, title: t, description: a, action: o, illustration: s }) {
119
+ return s ? /* @__PURE__ */ n(i, {
120
+ illustration: s,
121
+ title: t,
122
+ description: a,
123
+ action: o,
124
+ className: "py-12"
125
+ }) : /* @__PURE__ */ r("div", {
119
126
  className: "flex flex-col items-center justify-center py-12 text-center",
120
127
  children: [
121
128
  e && /* @__PURE__ */ n("div", {
@@ -126,15 +133,15 @@ function b({ icon: e, title: t, description: i, action: a }) {
126
133
  className: "mb-2 text-lg font-semibold",
127
134
  children: t
128
135
  }),
129
- i && /* @__PURE__ */ n("p", {
136
+ a && /* @__PURE__ */ n("p", {
130
137
  className: "mb-4 max-w-sm text-sm text-text-secondary",
131
- children: i
138
+ children: a
132
139
  }),
133
- a
140
+ o
134
141
  ]
135
142
  });
136
143
  }
137
144
  //#endregion
138
- export { y as Avatar, d as Badge, m as Button, i as Card, c as CardContent, s as CardDescription, l as CardFooter, a as CardHeader, o as CardTitle, b as EmptyState, h as Input, _ as Separator, g as Skeleton };
145
+ export { b as Avatar, f as Badge, h as Button, a as Card, l as CardContent, c as CardDescription, u as CardFooter, o as CardHeader, s as CardTitle, x as EmptyState, g as Input, v as Separator, _ as Skeleton };
139
146
 
140
147
  //# sourceMappingURL=ui.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ui.js","names":[],"sources":["../../../src/admin/components/ui.tsx"],"sourcesContent":["/**\n * Shared UI Components for Admin Console\n * Using semantic tokens for consistent theming\n */\n\nimport type { ReactNode, HTMLAttributes, ButtonHTMLAttributes } from 'react';\nimport { forwardRef } from 'react';\nimport { cn } from '../utils/cn';\n\n// ============================================================================\n// Card Components\n// ============================================================================\n\ninterface CardProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'rounded-lg border border-border-subtle bg-bg-surface text-text-primary shadow-sm',\n className\n )}\n {...props}\n >\n {children}\n </div>\n )\n);\nCard.displayName = 'Card';\n\nexport const CardHeader = forwardRef<HTMLDivElement, CardProps>(\n ({ className, children, ...props }, ref) => (\n <div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props}>\n {children}\n </div>\n )\n);\nCardHeader.displayName = 'CardHeader';\n\nexport const CardTitle = forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>(\n ({ className, children, ...props }, ref) => (\n <h3\n ref={ref}\n className={cn('text-2xl font-semibold leading-none tracking-tight', className)}\n {...props}\n >\n {children}\n </h3>\n )\n);\nCardTitle.displayName = 'CardTitle';\n\nexport const CardDescription = forwardRef<\n HTMLParagraphElement,\n HTMLAttributes<HTMLParagraphElement>\n>(({ className, children, ...props }, ref) => (\n <p ref={ref} className={cn('text-sm text-text-secondary', className)} {...props}>\n {children}\n </p>\n));\nCardDescription.displayName = 'CardDescription';\n\nexport const CardContent = forwardRef<HTMLDivElement, CardProps>(\n ({ className, children, ...props }, ref) => (\n <div ref={ref} className={cn('p-6 pt-0', className)} {...props}>\n {children}\n </div>\n )\n);\nCardContent.displayName = 'CardContent';\n\nexport const CardFooter = forwardRef<HTMLDivElement, CardProps>(\n ({ className, children, ...props }, ref) => (\n <div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props}>\n {children}\n </div>\n )\n);\nCardFooter.displayName = 'CardFooter';\n\n// ============================================================================\n// Badge Component\n// ============================================================================\n\ninterface BadgeProps extends HTMLAttributes<HTMLSpanElement> {\n variant?: 'default' | 'secondary' | 'success' | 'warning' | 'destructive' | 'outline';\n}\n\nconst badgeVariants = {\n default: 'bg-action-primary-bg text-action-primary-text',\n secondary: 'bg-secondary text-secondary-foreground',\n success: 'bg-success/10 text-success border-success/20',\n warning: 'bg-warning/10 text-warning border-warning/20',\n destructive: 'bg-status-error-bg/10 text-status-error-text border-status-error-border/20',\n outline: 'border-border-subtle bg-transparent text-text-primary',\n};\n\nexport const Badge = forwardRef<HTMLSpanElement, BadgeProps>(\n ({ className, variant = 'default', children, ...props }, ref) => (\n <span\n ref={ref}\n className={cn(\n 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors',\n badgeVariants[variant],\n className\n )}\n {...props}\n >\n {children}\n </span>\n )\n);\nBadge.displayName = 'Badge';\n\n// ============================================================================\n// Button Component\n// ============================================================================\n\ninterface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: 'default' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link';\n size?: 'default' | 'sm' | 'lg' | 'icon';\n}\n\nconst buttonVariants = {\n default: 'bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90',\n secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',\n outline: 'border border-border-default bg-bg-surface hover:bg-accent hover:text-text-primary',\n ghost: 'hover:bg-accent hover:text-text-primary',\n destructive: 'bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90',\n link: 'text-primary underline-offset-4 hover:underline',\n};\n\nconst buttonSizes = {\n default: 'h-10 px-4 py-2',\n sm: 'h-9 rounded-md px-3',\n lg: 'h-11 rounded-md px-8',\n icon: 'h-10 w-10',\n};\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant = 'default', size = 'default', children, ...props }, ref) => (\n <button\n ref={ref}\n className={cn(\n 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] focus-visible:ring-offset-2',\n 'disabled:pointer-events-none disabled:opacity-50',\n buttonVariants[variant],\n buttonSizes[size],\n className\n )}\n {...props}\n >\n {children}\n </button>\n )\n);\nButton.displayName = 'Button';\n\n// ============================================================================\n// Input Component\n// ============================================================================\n\ntype InputProps = React.InputHTMLAttributes<HTMLInputElement>;\n\nexport const Input = forwardRef<HTMLInputElement, InputProps>(\n ({ className, type, ...props }, ref) => (\n <input\n type={type}\n className={cn(\n 'flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm ring-offset-background',\n 'file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-text-primary',\n 'placeholder:text-text-secondary',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] focus-visible:ring-offset-2',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n className\n )}\n ref={ref}\n {...props}\n />\n )\n);\nInput.displayName = 'Input';\n\n// ============================================================================\n// Skeleton Component\n// ============================================================================\n\ntype SkeletonProps = HTMLAttributes<HTMLDivElement>;\n\nexport const Skeleton = forwardRef<HTMLDivElement, SkeletonProps>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={cn('animate-pulse rounded-md bg-bg-sunken', className)} {...props} />\n )\n);\nSkeleton.displayName = 'Skeleton';\n\n// ============================================================================\n// Separator Component\n// ============================================================================\n\ninterface SeparatorProps extends HTMLAttributes<HTMLDivElement> {\n orientation?: 'horizontal' | 'vertical';\n}\n\nexport const Separator = forwardRef<HTMLDivElement, SeparatorProps>(\n ({ className, orientation = 'horizontal', ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'shrink-0 bg-border',\n orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',\n className\n )}\n {...props}\n />\n )\n);\nSeparator.displayName = 'Separator';\n\n// ============================================================================\n// Avatar Component\n// ============================================================================\n\ninterface AvatarProps extends HTMLAttributes<HTMLDivElement> {\n src?: string;\n alt?: string;\n fallback: string;\n size?: 'sm' | 'default' | 'lg';\n}\n\nconst avatarSizes = {\n sm: 'h-8 w-8 text-xs',\n default: 'h-10 w-10 text-sm',\n lg: 'h-12 w-12 text-base',\n};\n\nexport const Avatar = forwardRef<HTMLDivElement, AvatarProps>(\n ({ className, src, alt, fallback, size = 'default', ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'relative flex shrink-0 overflow-hidden rounded-full bg-bg-sunken',\n avatarSizes[size],\n className\n )}\n {...props}\n >\n {src ? (\n <img src={src} alt={alt} className=\"aspect-square h-full w-full object-cover\" />\n ) : (\n <span className=\"flex h-full w-full items-center justify-center font-medium text-text-secondary\">\n {fallback}\n </span>\n )}\n </div>\n )\n);\nAvatar.displayName = 'Avatar';\n\n// ============================================================================\n// Empty State Component\n// ============================================================================\n\ninterface EmptyStateProps {\n icon?: ReactNode;\n title: string;\n description?: string;\n action?: ReactNode;\n}\n\nexport function EmptyState({ icon, title, description, action }: EmptyStateProps) {\n return (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n {icon && <div className=\"mb-4 rounded-full bg-bg-sunken p-4 text-text-secondary\">{icon}</div>}\n <h3 className=\"mb-2 text-lg font-semibold\">{title}</h3>\n {description && <p className=\"mb-4 max-w-sm text-sm text-text-secondary\">{description}</p>}\n {action}\n </div>\n );\n}\n\n// ============================================================================\n// Loading Spinner Component\n// ============================================================================\n\ninterface SpinnerProps extends HTMLAttributes<HTMLDivElement> {\n size?: 'sm' | 'default' | 'lg';\n}\n\nconst spinnerSizes = {\n sm: 'h-4 w-4',\n default: 'h-6 w-6',\n lg: 'h-8 w-8',\n};\n\nexport function Spinner({ className, size = 'default', ...props }: SpinnerProps) {\n return (\n <div\n className={cn(\n 'animate-spin rounded-full border-2 border-current border-t-transparent text-primary',\n spinnerSizes[size],\n className\n )}\n {...props}\n />\n );\n}\n\n// ============================================================================\n// Page Loading Fallback\n// ============================================================================\n\ninterface PageLoadingFallbackProps {\n message?: string;\n}\n\nexport function PageLoadingFallback({ message = 'Loading...' }: PageLoadingFallbackProps) {\n return (\n <div className=\"flex h-full min-h-[400px] flex-col items-center justify-center gap-4\">\n <Spinner size=\"lg\" />\n <p className=\"text-sm text-text-secondary\">{message}</p>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ui.js","names":[],"sources":["../../../src/admin/components/ui.tsx"],"sourcesContent":["/**\n * Shared UI Components for Admin Console\n * Using semantic tokens for consistent theming\n */\n\nimport type { ReactNode, HTMLAttributes, ButtonHTMLAttributes } from 'react';\nimport { forwardRef } from 'react';\nimport { IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\n// ============================================================================\n// Card Components\n// ============================================================================\n\ninterface CardProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'rounded-card border border-border-seam bg-bg-surface text-text-primary shadow-elevation-1 transition-[box-shadow,border-color] duration-200 hover:border-border-strong',\n className\n )}\n {...props}\n >\n {children}\n </div>\n )\n);\nCard.displayName = 'Card';\n\nexport const CardHeader = forwardRef<HTMLDivElement, CardProps>(\n ({ className, children, ...props }, ref) => (\n <div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props}>\n {children}\n </div>\n )\n);\nCardHeader.displayName = 'CardHeader';\n\nexport const CardTitle = forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>(\n ({ className, children, ...props }, ref) => (\n <h3\n ref={ref}\n className={cn('text-2xl font-semibold leading-none tracking-tight', className)}\n {...props}\n >\n {children}\n </h3>\n )\n);\nCardTitle.displayName = 'CardTitle';\n\nexport const CardDescription = forwardRef<\n HTMLParagraphElement,\n HTMLAttributes<HTMLParagraphElement>\n>(({ className, children, ...props }, ref) => (\n <p ref={ref} className={cn('text-sm text-text-secondary', className)} {...props}>\n {children}\n </p>\n));\nCardDescription.displayName = 'CardDescription';\n\nexport const CardContent = forwardRef<HTMLDivElement, CardProps>(\n ({ className, children, ...props }, ref) => (\n <div ref={ref} className={cn('p-6 pt-0', className)} {...props}>\n {children}\n </div>\n )\n);\nCardContent.displayName = 'CardContent';\n\nexport const CardFooter = forwardRef<HTMLDivElement, CardProps>(\n ({ className, children, ...props }, ref) => (\n <div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props}>\n {children}\n </div>\n )\n);\nCardFooter.displayName = 'CardFooter';\n\n// ============================================================================\n// Badge Component\n// ============================================================================\n\ninterface BadgeProps extends HTMLAttributes<HTMLSpanElement> {\n variant?: 'default' | 'secondary' | 'success' | 'warning' | 'destructive' | 'outline';\n}\n\nconst badgeVariants = {\n default: 'bg-action-primary-bg text-action-primary-text',\n secondary: 'bg-secondary text-secondary-foreground',\n success: 'bg-status-success-bg text-status-success-text border-status-success-border',\n warning: 'bg-status-warning-bg text-status-warning-text border-status-warning-border',\n destructive: 'bg-status-error-bg text-status-error-text border-status-error-border',\n outline: 'border-border-subtle bg-transparent text-text-primary',\n};\n\nexport const Badge = forwardRef<HTMLSpanElement, BadgeProps>(\n ({ className, variant = 'default', children, ...props }, ref) => (\n <span\n ref={ref}\n className={cn(\n 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors',\n badgeVariants[variant],\n className\n )}\n {...props}\n >\n {children}\n </span>\n )\n);\nBadge.displayName = 'Badge';\n\n// ============================================================================\n// Button Component\n// ============================================================================\n\ninterface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: 'default' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link';\n size?: 'default' | 'sm' | 'lg' | 'icon';\n}\n\nconst buttonVariants = {\n default: 'bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90',\n secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',\n outline: 'border border-border-default bg-bg-surface hover:bg-accent hover:text-text-primary',\n ghost: 'hover:bg-accent hover:text-text-primary',\n destructive: 'bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90',\n link: 'text-primary underline-offset-4 hover:underline',\n};\n\nconst buttonSizes = {\n default: 'h-10 px-4 py-2',\n sm: 'h-9 rounded-md px-3',\n lg: 'h-11 rounded-md px-8',\n icon: 'h-10 w-10',\n};\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant = 'default', size = 'default', children, ...props }, ref) => (\n <button\n ref={ref}\n className={cn(\n 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] focus-visible:ring-offset-2',\n 'disabled:pointer-events-none disabled:opacity-50',\n buttonVariants[variant],\n buttonSizes[size],\n className\n )}\n {...props}\n >\n {children}\n </button>\n )\n);\nButton.displayName = 'Button';\n\n// ============================================================================\n// Input Component\n// ============================================================================\n\ntype InputProps = React.InputHTMLAttributes<HTMLInputElement>;\n\nexport const Input = forwardRef<HTMLInputElement, InputProps>(\n ({ className, type, ...props }, ref) => (\n <input\n type={type}\n className={cn(\n 'flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm ring-offset-background',\n 'file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-text-primary',\n 'placeholder:text-text-secondary',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] focus-visible:ring-offset-2',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n className\n )}\n ref={ref}\n {...props}\n />\n )\n);\nInput.displayName = 'Input';\n\n// ============================================================================\n// Skeleton Component\n// ============================================================================\n\ntype SkeletonProps = HTMLAttributes<HTMLDivElement>;\n\nexport const Skeleton = forwardRef<HTMLDivElement, SkeletonProps>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={cn('animate-pulse rounded-md bg-bg-sunken', className)} {...props} />\n )\n);\nSkeleton.displayName = 'Skeleton';\n\n// ============================================================================\n// Separator Component\n// ============================================================================\n\ninterface SeparatorProps extends HTMLAttributes<HTMLDivElement> {\n orientation?: 'horizontal' | 'vertical';\n}\n\nexport const Separator = forwardRef<HTMLDivElement, SeparatorProps>(\n ({ className, orientation = 'horizontal', ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'shrink-0 bg-border',\n orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',\n className\n )}\n {...props}\n />\n )\n);\nSeparator.displayName = 'Separator';\n\n// ============================================================================\n// Avatar Component\n// ============================================================================\n\ninterface AvatarProps extends HTMLAttributes<HTMLDivElement> {\n src?: string;\n alt?: string;\n fallback: string;\n size?: 'sm' | 'default' | 'lg';\n}\n\nconst avatarSizes = {\n sm: 'h-8 w-8 text-xs',\n default: 'h-10 w-10 text-sm',\n lg: 'h-12 w-12 text-base',\n};\n\nexport const Avatar = forwardRef<HTMLDivElement, AvatarProps>(\n ({ className, src, alt, fallback, size = 'default', ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'relative flex shrink-0 overflow-hidden rounded-full bg-bg-sunken',\n avatarSizes[size],\n className\n )}\n {...props}\n >\n {src ? (\n <img src={src} alt={alt} className=\"aspect-square h-full w-full object-cover\" />\n ) : (\n <span className=\"flex h-full w-full items-center justify-center font-medium text-text-secondary\">\n {fallback}\n </span>\n )}\n </div>\n )\n);\nAvatar.displayName = 'Avatar';\n\n// ============================================================================\n// Empty State Component\n// ============================================================================\n\ninterface EmptyStateProps {\n icon?: ReactNode;\n title: string;\n description?: string;\n action?: ReactNode;\n /**\n * On-brand illustration name (Boff UI 2.0). When provided, renders the\n * IllustratedEmptyState (host serves /illustrations/<name>.webp). Standard\n * names: empty-data, empty-search, empty-files, empty-people,\n * empty-notifications, onboarding, empty-generic. Falls back to the icon\n * variant when omitted.\n */\n illustration?: string;\n}\n\nexport function EmptyState({ icon, title, description, action, illustration }: EmptyStateProps) {\n if (illustration) {\n return (\n <IllustratedEmptyState\n illustration={illustration}\n title={title}\n description={description}\n action={action}\n className=\"py-12\"\n />\n );\n }\n return (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n {icon && <div className=\"mb-4 rounded-full bg-bg-sunken p-4 text-text-secondary\">{icon}</div>}\n <h3 className=\"mb-2 text-lg font-semibold\">{title}</h3>\n {description && <p className=\"mb-4 max-w-sm text-sm text-text-secondary\">{description}</p>}\n {action}\n </div>\n );\n}\n\n// ============================================================================\n// Loading Spinner Component\n// ============================================================================\n\ninterface SpinnerProps extends HTMLAttributes<HTMLDivElement> {\n size?: 'sm' | 'default' | 'lg';\n}\n\nconst spinnerSizes = {\n sm: 'h-4 w-4',\n default: 'h-6 w-6',\n lg: 'h-8 w-8',\n};\n\nexport function Spinner({ className, size = 'default', ...props }: SpinnerProps) {\n return (\n <div\n className={cn(\n 'animate-spin rounded-full border-2 border-current border-t-transparent text-primary',\n spinnerSizes[size],\n className\n )}\n {...props}\n />\n );\n}\n\n// ============================================================================\n// Page Loading Fallback\n// ============================================================================\n\ninterface PageLoadingFallbackProps {\n message?: string;\n}\n\nexport function PageLoadingFallback({ message = 'Loading...' }: PageLoadingFallbackProps) {\n return (\n <div className=\"flex h-full min-h-[400px] flex-col items-center justify-center gap-4\">\n <Spinner size=\"lg\" />\n <p className=\"text-sm text-text-secondary\">{message}</p>\n </div>\n );\n}\n"],"mappings":"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"}