@burdenoff/microfe-admin 2026.625.1 → 2026.712.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 (77) hide show
  1. package/dist/admin/AdminRoot.js +2 -2
  2. package/dist/admin/AdminRoot.js.map +1 -1
  3. package/dist/admin/AdminRoutes.js +130 -78
  4. package/dist/admin/AdminRoutes.js.map +1 -1
  5. package/dist/admin/components/AccessDenied.js +2 -2
  6. package/dist/admin/components/AccessDenied.js.map +1 -1
  7. package/dist/admin/components/ActivityFeed.js +4 -4
  8. package/dist/admin/components/ActivityFeed.js.map +1 -1
  9. package/dist/admin/components/Charts.js.map +1 -1
  10. package/dist/admin/components/ConfirmDialog.js +1 -1
  11. package/dist/admin/components/ConfirmDialog.js.map +1 -1
  12. package/dist/admin/components/DataTable.js.map +1 -1
  13. package/dist/admin/components/FormSheet.js +1 -1
  14. package/dist/admin/components/FormSheet.js.map +1 -1
  15. package/dist/admin/components/TenantSsoPanel.js +1 -1
  16. package/dist/admin/components/TenantSsoPanel.js.map +1 -1
  17. package/dist/admin/components/Toast.js +11 -11
  18. package/dist/admin/components/Toast.js.map +1 -1
  19. package/dist/admin/components/forms/SubscriptionForm.js +2 -2
  20. package/dist/admin/components/forms/SubscriptionForm.js.map +1 -1
  21. package/dist/admin/components/forms/TenantForm.js +1 -1
  22. package/dist/admin/components/forms/UserForm.js +1 -1
  23. package/dist/admin/components/ui.js +3 -3
  24. package/dist/admin/components/ui.js.map +1 -1
  25. package/dist/admin/constants/featureFlags.js +12 -0
  26. package/dist/admin/constants/featureFlags.js.map +1 -0
  27. package/dist/admin/constants/index.js +33 -24
  28. package/dist/admin/constants/index.js.map +1 -1
  29. package/dist/admin/pages/AuditLogsPage.js +92 -77
  30. package/dist/admin/pages/AuditLogsPage.js.map +1 -1
  31. package/dist/admin/pages/BillingPage.js +95 -51
  32. package/dist/admin/pages/BillingPage.js.map +1 -1
  33. package/dist/admin/pages/CommunicationsPage.js +97 -92
  34. package/dist/admin/pages/CommunicationsPage.js.map +1 -1
  35. package/dist/admin/pages/ContentPagesPage.js +274 -259
  36. package/dist/admin/pages/ContentPagesPage.js.map +1 -1
  37. package/dist/admin/pages/DashboardPage.js +113 -84
  38. package/dist/admin/pages/DashboardPage.js.map +1 -1
  39. package/dist/admin/pages/FeatureFlagsPage.js +93 -75
  40. package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
  41. package/dist/admin/pages/OrganizationsListPage.js +105 -87
  42. package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
  43. package/dist/admin/pages/ProductsPage.js +138 -130
  44. package/dist/admin/pages/ProductsPage.js.map +1 -1
  45. package/dist/admin/pages/ReferralsPage.js +70 -52
  46. package/dist/admin/pages/ReferralsPage.js.map +1 -1
  47. package/dist/admin/pages/RolesPermissionsPage.js +136 -118
  48. package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
  49. package/dist/admin/pages/SREPage.js +34 -32
  50. package/dist/admin/pages/SREPage.js.map +1 -1
  51. package/dist/admin/pages/SandboxSettingsPage.js +360 -322
  52. package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
  53. package/dist/admin/pages/SettingsPage.js +64 -61
  54. package/dist/admin/pages/SettingsPage.js.map +1 -1
  55. package/dist/admin/pages/SyncFailuresPage.js +120 -118
  56. package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
  57. package/dist/admin/pages/TenantDetailPage.js +286 -231
  58. package/dist/admin/pages/TenantDetailPage.js.map +1 -1
  59. package/dist/admin/pages/TenantFormRoutePage.js +78 -73
  60. package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
  61. package/dist/admin/pages/TenantsListPage.js +182 -155
  62. package/dist/admin/pages/TenantsListPage.js.map +1 -1
  63. package/dist/admin/pages/UsersListPage.js +128 -113
  64. package/dist/admin/pages/UsersListPage.js.map +1 -1
  65. package/dist/admin/pages/WaitlistPage.js +78 -63
  66. package/dist/admin/pages/WaitlistPage.js.map +1 -1
  67. package/dist/admin/pages/WorkflowsPage.js +107 -102
  68. package/dist/admin/pages/WorkflowsPage.js.map +1 -1
  69. package/dist/admin/pages/WorkspacesListPage.js +61 -49
  70. package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
  71. package/dist/node_modules/@hookform/resolvers/dist/resolvers.js +2 -2
  72. package/dist/node_modules/@hookform/resolvers/dist/resolvers.js.map +1 -1
  73. package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js +35 -26
  74. package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js.map +1 -1
  75. package/dist/node_modules/react-hook-form/dist/index.esm.js +609 -516
  76. package/dist/node_modules/react-hook-form/dist/index.esm.js.map +1 -1
  77. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"ConfirmDialog.js","names":[],"sources":["../../../src/admin/components/ConfirmDialog.tsx"],"sourcesContent":["import { useEffect, useRef, type ReactNode } from 'react';\nimport { AlertTriangle, Loader2 } from 'lucide-react';\nimport { Button } from './ui';\nimport { cn } from '../utils/cn';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ConfirmDialogProps {\n /** Whether the dialog is open */\n open: boolean;\n /** Callback when the dialog should close */\n onClose: () => void;\n /** Callback when confirmed */\n onConfirm: () => void | Promise<void>;\n /** Dialog title */\n title: string;\n /** Dialog description */\n description: ReactNode;\n /** Confirm button text */\n confirmLabel?: string;\n /** Cancel button text */\n cancelLabel?: string;\n /** Variant for styling */\n variant?: 'default' | 'destructive';\n /** Loading state */\n loading?: boolean;\n /** Icon to display */\n icon?: ReactNode;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * ConfirmDialog - Modal dialog for confirming actions\n *\n * Use for destructive actions like deleting, or important state changes.\n */\nexport function ConfirmDialog({\n open,\n onClose,\n onConfirm,\n title,\n description,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n variant = 'default',\n loading = false,\n icon,\n}: ConfirmDialogProps) {\n const confirmRef = useRef<HTMLButtonElement>(null);\n\n // Handle escape key\n useEffect(() => {\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && open && !loading) {\n onClose();\n }\n };\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [open, onClose, loading]);\n\n // Focus confirm button when opened\n useEffect(() => {\n if (open) {\n // Small delay to ensure dialog is rendered\n const timer = setTimeout(() => {\n confirmRef.current?.focus();\n }, 50);\n return () => clearTimeout(timer);\n }\n }, [open]);\n\n // Prevent body scroll when open\n useEffect(() => {\n if (open) {\n document.body.style.overflow = 'hidden';\n } else {\n document.body.style.overflow = '';\n }\n return () => {\n document.body.style.overflow = '';\n };\n }, [open]);\n\n if (!open) return null;\n\n const handleConfirm = async () => {\n await onConfirm();\n };\n\n const isDestructive = variant === 'destructive';\n const defaultIcon = isDestructive ? <AlertTriangle className=\"h-6 w-6 text-status-error-text\" /> : null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <div\n className=\"fixed inset-0 bg-black/50 transition-opacity\"\n onClick={loading ? undefined : onClose}\n aria-hidden=\"true\"\n />\n\n {/* Dialog */}\n <div\n className={cn(\n 'relative z-10 w-full max-w-md rounded-lg bg-bg-surface p-6 shadow-xl',\n 'animate-in zoom-in-95 fade-in duration-200'\n )}\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby=\"confirm-dialog-title\"\n aria-describedby=\"confirm-dialog-description\"\n >\n {/* Icon & Content */}\n <div className=\"flex gap-4\">\n {(icon || defaultIcon) && (\n <div\n className={cn(\n 'flex h-12 w-12 shrink-0 items-center justify-center rounded-full',\n isDestructive ? 'bg-status-error-bg/10' : 'bg-action-primary-bg/10'\n )}\n >\n {icon || defaultIcon}\n </div>\n )}\n <div className=\"flex-1\">\n <h3 id=\"confirm-dialog-title\" className=\"text-lg font-semibold text-text-primary\">\n {title}\n </h3>\n <div id=\"confirm-dialog-description\" className=\"mt-2 text-sm text-text-secondary\">\n {description}\n </div>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"mt-6 flex justify-end gap-3\">\n <Button variant=\"outline\" onClick={onClose} disabled={loading}>\n {cancelLabel}\n </Button>\n <Button\n ref={confirmRef}\n variant={isDestructive ? 'destructive' : 'default'}\n onClick={handleConfirm}\n disabled={loading}\n >\n {loading && <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />}\n {confirmLabel}\n </Button>\n </div>\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// Delete Confirmation Helper\n// ============================================================================\n\nexport interface DeleteConfirmDialogProps {\n open: boolean;\n onClose: () => void;\n onConfirm: () => void | Promise<void>;\n /** Name of item being deleted */\n itemName: string;\n /** Type of item (e.g., \"tenant\", \"user\") */\n itemType: string;\n loading?: boolean;\n}\n\n/**\n * DeleteConfirmDialog - Pre-configured dialog for delete confirmations\n */\nexport function DeleteConfirmDialog({\n open,\n onClose,\n onConfirm,\n itemName,\n itemType,\n loading,\n}: DeleteConfirmDialogProps) {\n return (\n <ConfirmDialog\n open={open}\n onClose={onClose}\n onConfirm={onConfirm}\n title={`Delete ${itemType}?`}\n description={\n <>\n Are you sure you want to delete <strong className=\"text-text-primary\">{itemName}</strong>?\n This action cannot be undone.\n </>\n }\n confirmLabel=\"Delete\"\n variant=\"destructive\"\n loading={loading}\n />\n );\n}\n"],"mappings":";;;;;;AAyCA,SAAgB,EAAc,EAC5B,SACA,YACA,cACA,UACA,gBACA,kBAAe,WACf,iBAAc,UACd,aAAU,WACV,aAAU,IACV,WACqB;CACrB,IAAM,IAAa,EAA0B,KAAK;AAoClD,KAjCA,QAAgB;EACd,IAAM,KAAgB,MAAqB;AACzC,GAAI,EAAE,QAAQ,YAAY,KAAQ,CAAC,KACjC,GAAS;;AAIb,SADA,SAAS,iBAAiB,WAAW,EAAa,QACrC,SAAS,oBAAoB,WAAW,EAAa;IACjE;EAAC;EAAM;EAAS;EAAQ,CAAC,EAG5B,QAAgB;AACd,MAAI,GAAM;GAER,IAAM,IAAQ,iBAAiB;AAC7B,MAAW,SAAS,OAAO;MAC1B,GAAG;AACN,gBAAa,aAAa,EAAM;;IAEjC,CAAC,EAAK,CAAC,EAGV,SACM,IACF,SAAS,KAAK,MAAM,WAAW,WAE/B,SAAS,KAAK,MAAM,WAAW,UAEpB;AACX,WAAS,KAAK,MAAM,WAAW;KAEhC,CAAC,EAAK,CAAC,EAEN,CAAC,EAAM,QAAO;CAElB,IAAM,IAAgB,YAAY;AAChC,QAAM,GAAW;IAGb,IAAgB,MAAY,eAC5B,IAAc,IAAgB,kBAAC,GAAD,EAAe,WAAU,kCAAmC,CAAA,GAAG;AAEnG,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEE,kBAAC,OAAD;GACE,WAAU;GACV,SAAS,IAAU,KAAA,IAAY;GAC/B,eAAY;GACZ,CAAA,EAGF,kBAAC,OAAD;GACE,WAAW,EACT,wEACA,6CACD;GACD,MAAK;GACL,cAAW;GACX,mBAAgB;GAChB,oBAAiB;aARnB,CAWE,kBAAC,OAAD;IAAK,WAAU;cAAf,EACI,KAAQ,MACR,kBAAC,OAAD;KACE,WAAW,EACT,oEACA,IAAgB,0BAA0B,0BAC3C;eAEA,KAAQ;KACL,CAAA,EAER,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,IAAG;MAAuB,WAAU;gBACrC;MACE,CAAA,EACL,kBAAC,OAAD;MAAK,IAAG;MAA6B,WAAU;gBAC5C;MACG,CAAA,CACF;OACF;OAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAQ,SAAQ;KAAU,SAAS;KAAS,UAAU;eACnD;KACM,CAAA,EACT,kBAAC,GAAD;KACE,KAAK;KACL,SAAS,IAAgB,gBAAgB;KACzC,SAAS;KACT,UAAU;eAJZ,CAMG,KAAW,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,EAC5D,EACM;OACL;MACF;KACF;;;AAsBV,SAAgB,EAAoB,EAClC,SACA,YACA,cACA,aACA,aACA,cAC2B;AAC3B,QACE,kBAAC,GAAD;EACQ;EACG;EACE;EACX,OAAO,UAAU,EAAS;EAC1B,aACE,kBAAA,GAAA,EAAA,UAAA;GAAE;GACgC,kBAAC,UAAD;IAAQ,WAAU;cAAqB;IAAkB,CAAA;;GAExF,EAAA,CAAA;EAEL,cAAa;EACb,SAAQ;EACC;EACT,CAAA"}
1
+ {"version":3,"file":"ConfirmDialog.js","names":[],"sources":["../../../src/admin/components/ConfirmDialog.tsx"],"sourcesContent":["import { useEffect, useRef, type ReactNode } from 'react';\nimport { AlertTriangle, Loader2 } from 'lucide-react';\nimport { Button } from './ui';\nimport { cn } from '../utils/cn';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ConfirmDialogProps {\n /** Whether the dialog is open */\n open: boolean;\n /** Callback when the dialog should close */\n onClose: () => void;\n /** Callback when confirmed */\n onConfirm: () => void | Promise<void>;\n /** Dialog title */\n title: string;\n /** Dialog description */\n description: ReactNode;\n /** Confirm button text */\n confirmLabel?: string;\n /** Cancel button text */\n cancelLabel?: string;\n /** Variant for styling */\n variant?: 'default' | 'destructive';\n /** Loading state */\n loading?: boolean;\n /** Icon to display */\n icon?: ReactNode;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * ConfirmDialog - Modal dialog for confirming actions\n *\n * Use for destructive actions like deleting, or important state changes.\n */\nexport function ConfirmDialog({\n open,\n onClose,\n onConfirm,\n title,\n description,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n variant = 'default',\n loading = false,\n icon,\n}: ConfirmDialogProps) {\n const confirmRef = useRef<HTMLButtonElement>(null);\n\n // Handle escape key\n useEffect(() => {\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && open && !loading) {\n onClose();\n }\n };\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [open, onClose, loading]);\n\n // Focus confirm button when opened\n useEffect(() => {\n if (open) {\n // Small delay to ensure dialog is rendered\n const timer = setTimeout(() => {\n confirmRef.current?.focus();\n }, 50);\n return () => clearTimeout(timer);\n }\n }, [open]);\n\n // Prevent body scroll when open\n useEffect(() => {\n if (open) {\n document.body.style.overflow = 'hidden';\n } else {\n document.body.style.overflow = '';\n }\n return () => {\n document.body.style.overflow = '';\n };\n }, [open]);\n\n if (!open) return null;\n\n const handleConfirm = async () => {\n await onConfirm();\n };\n\n const isDestructive = variant === 'destructive';\n const defaultIcon = isDestructive ? (\n <AlertTriangle className=\"h-6 w-6 text-status-error-text\" />\n ) : null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <div\n className=\"fixed inset-0 bg-overlay transition-opacity\"\n onClick={loading ? undefined : onClose}\n aria-hidden=\"true\"\n />\n\n {/* Dialog */}\n <div\n className={cn(\n 'relative z-10 w-full max-w-md rounded-lg bg-bg-surface p-6 shadow-xl',\n 'animate-in zoom-in-95 fade-in duration-200'\n )}\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby=\"confirm-dialog-title\"\n aria-describedby=\"confirm-dialog-description\"\n >\n {/* Icon & Content */}\n <div className=\"flex gap-4\">\n {(icon || defaultIcon) && (\n <div\n className={cn(\n 'flex h-12 w-12 shrink-0 items-center justify-center rounded-full',\n isDestructive ? 'bg-status-error-bg/10' : 'bg-action-primary-bg/10'\n )}\n >\n {icon || defaultIcon}\n </div>\n )}\n <div className=\"flex-1\">\n <h3 id=\"confirm-dialog-title\" className=\"text-lg font-semibold text-text-primary\">\n {title}\n </h3>\n <div id=\"confirm-dialog-description\" className=\"mt-2 text-sm text-text-secondary\">\n {description}\n </div>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"mt-6 flex justify-end gap-3\">\n <Button variant=\"outline\" onClick={onClose} disabled={loading}>\n {cancelLabel}\n </Button>\n <Button\n ref={confirmRef}\n variant={isDestructive ? 'destructive' : 'default'}\n onClick={handleConfirm}\n disabled={loading}\n >\n {loading && <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />}\n {confirmLabel}\n </Button>\n </div>\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// Delete Confirmation Helper\n// ============================================================================\n\nexport interface DeleteConfirmDialogProps {\n open: boolean;\n onClose: () => void;\n onConfirm: () => void | Promise<void>;\n /** Name of item being deleted */\n itemName: string;\n /** Type of item (e.g., \"tenant\", \"user\") */\n itemType: string;\n loading?: boolean;\n}\n\n/**\n * DeleteConfirmDialog - Pre-configured dialog for delete confirmations\n */\nexport function DeleteConfirmDialog({\n open,\n onClose,\n onConfirm,\n itemName,\n itemType,\n loading,\n}: DeleteConfirmDialogProps) {\n return (\n <ConfirmDialog\n open={open}\n onClose={onClose}\n onConfirm={onConfirm}\n title={`Delete ${itemType}?`}\n description={\n <>\n Are you sure you want to delete <strong className=\"text-text-primary\">{itemName}</strong>?\n This action cannot be undone.\n </>\n }\n confirmLabel=\"Delete\"\n variant=\"destructive\"\n loading={loading}\n />\n );\n}\n"],"mappings":";;;;;;AAyCA,SAAgB,EAAc,EAC5B,SACA,YACA,cACA,UACA,gBACA,kBAAe,WACf,iBAAc,UACd,aAAU,WACV,aAAU,IACV,WACqB;CACrB,IAAM,IAAa,EAA0B,KAAK;AAoClD,KAjCA,QAAgB;EACd,IAAM,KAAgB,MAAqB;AACzC,GAAI,EAAE,QAAQ,YAAY,KAAQ,CAAC,KACjC,GAAS;;AAIb,SADA,SAAS,iBAAiB,WAAW,EAAa,QACrC,SAAS,oBAAoB,WAAW,EAAa;IACjE;EAAC;EAAM;EAAS;EAAQ,CAAC,EAG5B,QAAgB;AACd,MAAI,GAAM;GAER,IAAM,IAAQ,iBAAiB;AAC7B,MAAW,SAAS,OAAO;MAC1B,GAAG;AACN,gBAAa,aAAa,EAAM;;IAEjC,CAAC,EAAK,CAAC,EAGV,SACM,IACF,SAAS,KAAK,MAAM,WAAW,WAE/B,SAAS,KAAK,MAAM,WAAW,UAEpB;AACX,WAAS,KAAK,MAAM,WAAW;KAEhC,CAAC,EAAK,CAAC,EAEN,CAAC,EAAM,QAAO;CAElB,IAAM,IAAgB,YAAY;AAChC,QAAM,GAAW;IAGb,IAAgB,MAAY,eAC5B,IAAc,IAClB,kBAAC,GAAD,EAAe,WAAU,kCAAmC,CAAA,GAC1D;AAEJ,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEE,kBAAC,OAAD;GACE,WAAU;GACV,SAAS,IAAU,KAAA,IAAY;GAC/B,eAAY;GACZ,CAAA,EAGF,kBAAC,OAAD;GACE,WAAW,EACT,wEACA,6CACD;GACD,MAAK;GACL,cAAW;GACX,mBAAgB;GAChB,oBAAiB;aARnB,CAWE,kBAAC,OAAD;IAAK,WAAU;cAAf,EACI,KAAQ,MACR,kBAAC,OAAD;KACE,WAAW,EACT,oEACA,IAAgB,0BAA0B,0BAC3C;eAEA,KAAQ;KACL,CAAA,EAER,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,IAAG;MAAuB,WAAU;gBACrC;MACE,CAAA,EACL,kBAAC,OAAD;MAAK,IAAG;MAA6B,WAAU;gBAC5C;MACG,CAAA,CACF;OACF;OAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAQ,SAAQ;KAAU,SAAS;KAAS,UAAU;eACnD;KACM,CAAA,EACT,kBAAC,GAAD;KACE,KAAK;KACL,SAAS,IAAgB,gBAAgB;KACzC,SAAS;KACT,UAAU;eAJZ,CAMG,KAAW,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,EAC5D,EACM;OACL;MACF;KACF;;;AAsBV,SAAgB,EAAoB,EAClC,SACA,YACA,cACA,aACA,aACA,cAC2B;AAC3B,QACE,kBAAC,GAAD;EACQ;EACG;EACE;EACX,OAAO,UAAU,EAAS;EAC1B,aACE,kBAAA,GAAA,EAAA,UAAA;GAAE;GACgC,kBAAC,UAAD;IAAQ,WAAU;cAAqB;IAAkB,CAAA;;GAExF,EAAA,CAAA;EAEL,cAAa;EACb,SAAQ;EACC;EACT,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"DataTable.js","names":[],"sources":["../../../src/admin/components/DataTable.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';\nimport { Button, Skeleton } from './ui';\nimport { cn } from '../utils/cn';\n\ninterface Column<T> {\n /** Column key (used for accessing data) */\n key: string;\n /** Column header */\n header: string;\n /** Custom render function */\n render?: (item: T, index: number) => ReactNode;\n /** Column width */\n width?: string;\n /** Align content */\n align?: 'left' | 'center' | 'right';\n /** Sortable */\n sortable?: boolean;\n}\n\ninterface DataTableProps<T> {\n /** Column definitions */\n columns: Column<T>[];\n /** Data to display */\n data: T[];\n /** Key extractor for rows */\n keyExtractor: (item: T) => string;\n /** Loading state */\n loading?: boolean;\n /** Number of skeleton rows when loading */\n skeletonRows?: number;\n /** Empty state content */\n emptyState?: ReactNode;\n /** On row click handler */\n onRowClick?: (item: T) => void;\n /** Current sort column */\n sortBy?: string;\n /** Sort direction */\n sortOrder?: 'asc' | 'desc';\n /** On sort change */\n onSort?: (column: string, order: 'asc' | 'desc') => void;\n /** Additional class name */\n className?: string;\n}\n\n/**\n * Reusable data table component\n */\nexport function DataTable<T>({\n columns,\n data,\n keyExtractor,\n loading,\n skeletonRows = 5,\n emptyState,\n onRowClick,\n sortBy,\n sortOrder,\n onSort,\n className,\n}: DataTableProps<T>) {\n const handleSort = (column: Column<T>) => {\n if (!column.sortable || !onSort) return;\n const newOrder = sortBy === column.key && sortOrder === 'asc' ? 'desc' : 'asc';\n onSort(column.key, newOrder);\n };\n\n const alignClasses = {\n left: 'text-left',\n center: 'text-center',\n right: 'text-right',\n };\n\n return (\n <div className={cn('overflow-hidden rounded-card border border-border-seam', className)}>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full border-collapse\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken/40\">\n {columns.map((column) => (\n <th\n key={column.key}\n className={cn(\n 'px-4 py-3 text-sm font-medium text-text-secondary',\n alignClasses[column.align || 'left'],\n column.sortable && 'cursor-pointer select-none hover:text-text-primary',\n column.width\n )}\n style={column.width ? { width: column.width } : undefined}\n onClick={() => handleSort(column)}\n >\n <div className=\"flex items-center gap-2\">\n {column.header}\n {column.sortable && sortBy === column.key && (\n <span className=\"text-xs\">{sortOrder === 'asc' ? '↑' : '↓'}</span>\n )}\n </div>\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {loading ? (\n Array.from({ length: skeletonRows }).map((_, rowIndex) => (\n <tr key={rowIndex} className=\"border-b border-border-seam last:border-0\">\n {columns.map((column) => (\n <td key={column.key} className=\"px-4 py-3\">\n <Skeleton className=\"h-5 w-full motion-safe:animate-vc-shimmer\" />\n </td>\n ))}\n </tr>\n ))\n ) : data.length === 0 ? (\n <tr>\n <td colSpan={columns.length} className=\"px-4 py-8\">\n {emptyState || (\n <div className=\"text-center text-sm text-text-secondary\">\n No data available\n </div>\n )}\n </td>\n </tr>\n ) : (\n data.map((item, index) => (\n <tr\n key={keyExtractor(item)}\n className={cn(\n 'border-b border-border-seam last:border-0 transition-colors',\n onRowClick && 'cursor-pointer hover:bg-bg-sunken/40'\n )}\n onClick={() => onRowClick?.(item)}\n >\n {columns.map((column) => (\n <td\n key={column.key}\n className={cn('px-4 py-3 text-sm', alignClasses[column.align || 'left'])}\n >\n {column.render\n ? column.render(item, index)\n : String((item as Record<string, unknown>)[column.key] ?? '')}\n </td>\n ))}\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n </div>\n );\n}\n\ninterface PaginationProps {\n /** Current page (1-indexed) */\n page: number;\n /** Total pages */\n totalPages: number;\n /** Total items */\n totalItems: number;\n /** Items per page */\n pageSize: number;\n /** On page change */\n onPageChange: (page: number) => void;\n /** Additional class name */\n className?: string;\n}\n\n/**\n * Pagination controls\n */\nexport function Pagination({\n page,\n totalPages,\n totalItems,\n pageSize,\n onPageChange,\n className,\n}: PaginationProps) {\n const startItem = (page - 1) * pageSize + 1;\n const endItem = Math.min(page * pageSize, totalItems);\n\n return (\n <div className={cn('flex items-center justify-between px-4 py-3', className)}>\n <div className=\"text-sm text-text-secondary\">\n Showing {startItem} to {endItem} of {totalItems} results\n </div>\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => onPageChange(1)}\n disabled={page === 1}\n aria-label=\"First page\"\n >\n <ChevronsLeft className=\"h-4 w-4\" />\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => onPageChange(page - 1)}\n disabled={page === 1}\n aria-label=\"Previous page\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </Button>\n <span className=\"px-3 text-sm\">\n Page {page} of {totalPages}\n </span>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => onPageChange(page + 1)}\n disabled={page === totalPages}\n aria-label=\"Next page\"\n >\n <ChevronRight className=\"h-4 w-4\" />\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => onPageChange(totalPages)}\n disabled={page === totalPages}\n aria-label=\"Last page\"\n >\n <ChevronsRight className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DataTable.js","names":[],"sources":["../../../src/admin/components/DataTable.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';\nimport { Button, Skeleton } from './ui';\nimport { cn } from '../utils/cn';\n\ninterface Column<T> {\n /** Column key (used for accessing data) */\n key: string;\n /** Column header */\n header: string;\n /** Custom render function */\n render?: (item: T, index: number) => ReactNode;\n /** Column width */\n width?: string;\n /** Align content */\n align?: 'left' | 'center' | 'right';\n /** Sortable */\n sortable?: boolean;\n}\n\ninterface DataTableProps<T> {\n /** Column definitions */\n columns: Column<T>[];\n /** Data to display */\n data: T[];\n /** Key extractor for rows */\n keyExtractor: (item: T) => string;\n /** Loading state */\n loading?: boolean;\n /** Number of skeleton rows when loading */\n skeletonRows?: number;\n /** Empty state content */\n emptyState?: ReactNode;\n /** On row click handler */\n onRowClick?: (item: T) => void;\n /** Current sort column */\n sortBy?: string;\n /** Sort direction */\n sortOrder?: 'asc' | 'desc';\n /** On sort change */\n onSort?: (column: string, order: 'asc' | 'desc') => void;\n /** Additional class name */\n className?: string;\n}\n\n/**\n * Reusable data table component\n */\nexport function DataTable<T>({\n columns,\n data,\n keyExtractor,\n loading,\n skeletonRows = 5,\n emptyState,\n onRowClick,\n sortBy,\n sortOrder,\n onSort,\n className,\n}: DataTableProps<T>) {\n const handleSort = (column: Column<T>) => {\n if (!column.sortable || !onSort) return;\n const newOrder = sortBy === column.key && sortOrder === 'asc' ? 'desc' : 'asc';\n onSort(column.key, newOrder);\n };\n\n const alignClasses = {\n left: 'text-left',\n center: 'text-center',\n right: 'text-right',\n };\n\n return (\n <div className={cn('overflow-hidden rounded-card border border-border-seam', className)}>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full border-collapse\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken/40\">\n {columns.map((column) => (\n <th\n key={column.key}\n className={cn(\n 'px-4 py-3 text-sm font-medium text-text-secondary',\n alignClasses[column.align || 'left'],\n column.sortable && 'cursor-pointer select-none hover:text-text-primary',\n column.width\n )}\n style={column.width ? { width: column.width } : undefined}\n onClick={() => handleSort(column)}\n >\n <div className=\"flex items-center gap-2\">\n {column.header}\n {column.sortable && sortBy === column.key && (\n <span className=\"text-xs\">{sortOrder === 'asc' ? '↑' : '↓'}</span>\n )}\n </div>\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {loading ? (\n Array.from({ length: skeletonRows }).map((_, rowIndex) => (\n <tr key={rowIndex} className=\"border-b border-border-seam last:border-0\">\n {columns.map((column) => (\n <td key={column.key} className=\"px-4 py-3\">\n <Skeleton className=\"h-5 w-full motion-safe:animate-vc-shimmer\" />\n </td>\n ))}\n </tr>\n ))\n ) : data.length === 0 ? (\n <tr>\n <td colSpan={columns.length} className=\"px-4 py-8\">\n {emptyState || (\n <div className=\"text-center text-sm text-text-secondary\">No data available</div>\n )}\n </td>\n </tr>\n ) : (\n data.map((item, index) => (\n <tr\n key={keyExtractor(item)}\n className={cn(\n 'border-b border-border-seam last:border-0 transition-colors',\n onRowClick && 'cursor-pointer hover:bg-bg-sunken/40'\n )}\n onClick={() => onRowClick?.(item)}\n >\n {columns.map((column) => (\n <td\n key={column.key}\n className={cn('px-4 py-3 text-sm', alignClasses[column.align || 'left'])}\n >\n {column.render\n ? column.render(item, index)\n : String((item as Record<string, unknown>)[column.key] ?? '')}\n </td>\n ))}\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n </div>\n );\n}\n\ninterface PaginationProps {\n /** Current page (1-indexed) */\n page: number;\n /** Total pages */\n totalPages: number;\n /** Total items */\n totalItems: number;\n /** Items per page */\n pageSize: number;\n /** On page change */\n onPageChange: (page: number) => void;\n /** Additional class name */\n className?: string;\n}\n\n/**\n * Pagination controls\n */\nexport function Pagination({\n page,\n totalPages,\n totalItems,\n pageSize,\n onPageChange,\n className,\n}: PaginationProps) {\n const startItem = (page - 1) * pageSize + 1;\n const endItem = Math.min(page * pageSize, totalItems);\n\n return (\n <div className={cn('flex items-center justify-between px-4 py-3', className)}>\n <div className=\"text-sm text-text-secondary\">\n Showing {startItem} to {endItem} of {totalItems} results\n </div>\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => onPageChange(1)}\n disabled={page === 1}\n aria-label=\"First page\"\n >\n <ChevronsLeft className=\"h-4 w-4\" />\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => onPageChange(page - 1)}\n disabled={page === 1}\n aria-label=\"Previous page\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </Button>\n <span className=\"px-3 text-sm\">\n Page {page} of {totalPages}\n </span>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => onPageChange(page + 1)}\n disabled={page === totalPages}\n aria-label=\"Next page\"\n >\n <ChevronRight className=\"h-4 w-4\" />\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => onPageChange(totalPages)}\n disabled={page === totalPages}\n aria-label=\"Last page\"\n >\n <ChevronsRight className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -21,7 +21,7 @@ function c({ open: r, onClose: c, title: l, description: u, children: d, width:
21
21
  }), [r]), r ? /* @__PURE__ */ o("div", {
22
22
  className: "fixed inset-0 z-50",
23
23
  children: [/* @__PURE__ */ a("div", {
24
- className: "fixed inset-0 bg-black/50 transition-opacity",
24
+ className: "fixed inset-0 bg-overlay transition-opacity",
25
25
  onClick: c,
26
26
  "aria-hidden": "true"
27
27
  }), /* @__PURE__ */ o("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"FormSheet.js","names":[],"sources":["../../../src/admin/components/FormSheet.tsx"],"sourcesContent":["import * as React from 'react';\nimport { X, Loader2 } from 'lucide-react';\nimport { cn } from '../utils/cn';\nimport { Button } from './ui';\n\n/**\n * FormSheet props\n */\nexport interface FormSheetProps {\n /** Whether the sheet is open */\n open: boolean;\n /** Callback when the sheet should close */\n onClose: () => void;\n /** Title of the sheet */\n title: string;\n /** Optional description */\n description?: string;\n /** Form content */\n children: React.ReactNode;\n /** Width of the sheet */\n width?: 'sm' | 'md' | 'lg' | 'xl';\n /** Optional footer actions */\n footer?: React.ReactNode;\n}\n\nconst widthClasses = {\n sm: 'max-w-sm',\n md: 'max-w-md',\n lg: 'max-w-lg',\n xl: 'max-w-xl',\n};\n\n/**\n * FormSheet - Slide-over panel for forms\n *\n * A side panel component for displaying forms that slides in from the right.\n * Includes backdrop overlay, header with close button, and optional footer.\n */\nexport function FormSheet({\n open,\n onClose,\n title,\n description,\n children,\n width = 'md',\n footer,\n}: FormSheetProps) {\n // Handle escape key\n React.useEffect(() => {\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && open) {\n onClose();\n }\n };\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [open, onClose]);\n\n // Prevent body scroll when open\n React.useEffect(() => {\n if (open) {\n document.body.style.overflow = 'hidden';\n } else {\n document.body.style.overflow = '';\n }\n return () => {\n document.body.style.overflow = '';\n };\n }, [open]);\n\n if (!open) return null;\n\n return (\n <div className=\"fixed inset-0 z-50\">\n {/* Backdrop */}\n <div\n className=\"fixed inset-0 bg-black/50 transition-opacity\"\n onClick={onClose}\n aria-hidden=\"true\"\n />\n\n {/* Sheet */}\n <div\n className={cn(\n 'fixed inset-y-0 right-0 flex w-full flex-col bg-bg-surface shadow-xl',\n 'animate-in slide-in-from-right duration-300',\n widthClasses[width]\n )}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"sheet-title\"\n >\n {/* Header */}\n <div className=\"flex items-start justify-between border-b border-border-subtle px-6 py-4\">\n <div>\n <h2 id=\"sheet-title\" className=\"text-lg font-semibold text-text-primary\">\n {title}\n </h2>\n {description && <p className=\"mt-1 text-sm text-text-secondary\">{description}</p>}\n </div>\n <Button variant=\"ghost\" size=\"icon\" onClick={onClose} aria-label=\"Close\">\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto px-6 py-4\">{children}</div>\n\n {/* Footer */}\n {footer && <div className=\"border-t border-border-subtle px-6 py-4\">{footer}</div>}\n </div>\n </div>\n );\n}\n\n/**\n * FormSheetFooter props\n */\nexport interface FormSheetFooterProps {\n /** Primary action button text */\n submitLabel?: string;\n /** Cancel button text */\n cancelLabel?: string;\n /** Callback for cancel */\n onCancel: () => void;\n /** Whether form is submitting */\n isSubmitting?: boolean;\n /** Whether submit is disabled */\n disabled?: boolean;\n /** Form ID to connect submit button */\n formId?: string;\n /** Callback for submit (if not using form) */\n onSubmit?: () => void;\n /** Variant for submit button */\n submitVariant?: 'default' | 'destructive';\n}\n\n/**\n * FormSheetFooter - Standard footer for FormSheet with cancel/submit buttons\n */\nexport function FormSheetFooter({\n submitLabel = 'Save',\n cancelLabel = 'Cancel',\n onCancel,\n isSubmitting = false,\n disabled = false,\n formId,\n onSubmit,\n submitVariant = 'default',\n}: FormSheetFooterProps) {\n return (\n <div className=\"flex items-center justify-end gap-3\">\n <Button variant=\"outline\" onClick={onCancel} disabled={isSubmitting}>\n {cancelLabel}\n </Button>\n <Button\n type={formId ? 'submit' : 'button'}\n form={formId}\n onClick={onSubmit}\n disabled={disabled || isSubmitting}\n variant={submitVariant}\n >\n {isSubmitting && <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />}\n {submitLabel}\n </Button>\n </div>\n );\n}\n\n/**\n * FormSection - Group related form fields\n */\nexport interface FormSectionProps {\n /** Section title */\n title?: string;\n /** Section description */\n description?: string;\n /** Form fields */\n children: React.ReactNode;\n /** Additional className */\n className?: string;\n}\n\nexport function FormSection({ title, description, children, className }: FormSectionProps) {\n return (\n <div className={cn('space-y-4', className)}>\n {(title || description) && (\n <div className=\"space-y-1\">\n {title && <h3 className=\"text-sm font-medium text-text-primary\">{title}</h3>}\n {description && <p className=\"text-xs text-text-secondary\">{description}</p>}\n </div>\n )}\n <div className=\"space-y-4\">{children}</div>\n </div>\n );\n}\n\n/**\n * FormRow - Horizontal row for form fields\n */\nexport interface FormRowProps {\n /** Form fields */\n children: React.ReactNode;\n /** Number of columns */\n cols?: 2 | 3 | 4;\n /** Additional className */\n className?: string;\n}\n\nexport function FormRow({ children, cols = 2, className }: FormRowProps) {\n const colsClass = {\n 2: 'grid-cols-2',\n 3: 'grid-cols-3',\n 4: 'grid-cols-4',\n };\n return <div className={cn('grid gap-4', colsClass[cols], className)}>{children}</div>;\n}\n"],"mappings":";;;;;;AAyBA,IAAM,IAAe;CACnB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACL;AAQD,SAAgB,EAAU,EACxB,SACA,YACA,UACA,gBACA,aACA,WAAQ,MACR,aACiB;AA0BjB,QAxBA,EAAM,gBAAgB;EACpB,IAAM,KAAgB,MAAqB;AACzC,GAAI,EAAE,QAAQ,YAAY,KACxB,GAAS;;AAIb,SADA,SAAS,iBAAiB,WAAW,EAAa,QACrC,SAAS,oBAAoB,WAAW,EAAa;IACjE,CAAC,GAAM,EAAQ,CAAC,EAGnB,EAAM,iBACA,IACF,SAAS,KAAK,MAAM,WAAW,WAE/B,SAAS,KAAK,MAAM,WAAW,UAEpB;AACX,WAAS,KAAK,MAAM,WAAW;KAEhC,CAAC,EAAK,CAAC,EAEL,IAGH,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEE,kBAAC,OAAD;GACE,WAAU;GACV,SAAS;GACT,eAAY;GACZ,CAAA,EAGF,kBAAC,OAAD;GACE,WAAW,EACT,wEACA,+CACA,EAAa,GACd;GACD,MAAK;GACL,cAAW;GACX,mBAAgB;aARlB;IAWE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MAAI,IAAG;MAAc,WAAU;gBAC5B;MACE,CAAA,EACJ,KAAe,kBAAC,KAAD;MAAG,WAAU;gBAAoC;MAAgB,CAAA,CAC7E,EAAA,CAAA,EACN,kBAAC,GAAD;MAAQ,SAAQ;MAAQ,MAAK;MAAO,SAAS;MAAS,cAAW;gBAC/D,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA;MAClB,CAAA,CACL;;IAGN,kBAAC,OAAD;KAAK,WAAU;KAAoC;KAAe,CAAA;IAGjE,KAAU,kBAAC,OAAD;KAAK,WAAU;eAA2C;KAAa,CAAA;IAC9E;KACF;MAzCU;;AAsEpB,SAAgB,EAAgB,EAC9B,iBAAc,QACd,iBAAc,UACd,aACA,kBAAe,IACf,cAAW,IACX,WACA,aACA,mBAAgB,aACO;AACvB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GAAQ,SAAQ;GAAU,SAAS;GAAU,UAAU;aACpD;GACM,CAAA,EACT,kBAAC,GAAD;GACE,MAAM,IAAS,WAAW;GAC1B,MAAM;GACN,SAAS;GACT,UAAU,KAAY;GACtB,SAAS;aALX,CAOG,KAAgB,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,EACjE,EACM;KACL;;;AAkBV,SAAgB,EAAY,EAAE,UAAO,gBAAa,aAAU,gBAA+B;AACzF,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,aAAa,EAAU;YAA1C,EACI,KAAS,MACT,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,KAAS,kBAAC,MAAD;IAAI,WAAU;cAAyC;IAAW,CAAA,EAC3E,KAAe,kBAAC,KAAD;IAAG,WAAU;cAA+B;IAAgB,CAAA,CACxE;MAER,kBAAC,OAAD;GAAK,WAAU;GAAa;GAAe,CAAA,CACvC;;;AAgBV,SAAgB,EAAQ,EAAE,aAAU,UAAO,GAAG,gBAA2B;AAMvE,QAAO,kBAAC,OAAD;EAAK,WAAW,EAAG,cALR;GAChB,GAAG;GACH,GAAG;GACH,GAAG;GACJ,CACiD,IAAO,EAAU;EAAG;EAAe,CAAA"}
1
+ {"version":3,"file":"FormSheet.js","names":[],"sources":["../../../src/admin/components/FormSheet.tsx"],"sourcesContent":["import * as React from 'react';\nimport { X, Loader2 } from 'lucide-react';\nimport { cn } from '../utils/cn';\nimport { Button } from './ui';\n\n/**\n * FormSheet props\n */\nexport interface FormSheetProps {\n /** Whether the sheet is open */\n open: boolean;\n /** Callback when the sheet should close */\n onClose: () => void;\n /** Title of the sheet */\n title: string;\n /** Optional description */\n description?: string;\n /** Form content */\n children: React.ReactNode;\n /** Width of the sheet */\n width?: 'sm' | 'md' | 'lg' | 'xl';\n /** Optional footer actions */\n footer?: React.ReactNode;\n}\n\nconst widthClasses = {\n sm: 'max-w-sm',\n md: 'max-w-md',\n lg: 'max-w-lg',\n xl: 'max-w-xl',\n};\n\n/**\n * FormSheet - Slide-over panel for forms\n *\n * A side panel component for displaying forms that slides in from the right.\n * Includes backdrop overlay, header with close button, and optional footer.\n */\nexport function FormSheet({\n open,\n onClose,\n title,\n description,\n children,\n width = 'md',\n footer,\n}: FormSheetProps) {\n // Handle escape key\n React.useEffect(() => {\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && open) {\n onClose();\n }\n };\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [open, onClose]);\n\n // Prevent body scroll when open\n React.useEffect(() => {\n if (open) {\n document.body.style.overflow = 'hidden';\n } else {\n document.body.style.overflow = '';\n }\n return () => {\n document.body.style.overflow = '';\n };\n }, [open]);\n\n if (!open) return null;\n\n return (\n <div className=\"fixed inset-0 z-50\">\n {/* Backdrop */}\n <div\n className=\"fixed inset-0 bg-overlay transition-opacity\"\n onClick={onClose}\n aria-hidden=\"true\"\n />\n\n {/* Sheet */}\n <div\n className={cn(\n 'fixed inset-y-0 right-0 flex w-full flex-col bg-bg-surface shadow-xl',\n 'animate-in slide-in-from-right duration-300',\n widthClasses[width]\n )}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"sheet-title\"\n >\n {/* Header */}\n <div className=\"flex items-start justify-between border-b border-border-subtle px-6 py-4\">\n <div>\n <h2 id=\"sheet-title\" className=\"text-lg font-semibold text-text-primary\">\n {title}\n </h2>\n {description && <p className=\"mt-1 text-sm text-text-secondary\">{description}</p>}\n </div>\n <Button variant=\"ghost\" size=\"icon\" onClick={onClose} aria-label=\"Close\">\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto px-6 py-4\">{children}</div>\n\n {/* Footer */}\n {footer && <div className=\"border-t border-border-subtle px-6 py-4\">{footer}</div>}\n </div>\n </div>\n );\n}\n\n/**\n * FormSheetFooter props\n */\nexport interface FormSheetFooterProps {\n /** Primary action button text */\n submitLabel?: string;\n /** Cancel button text */\n cancelLabel?: string;\n /** Callback for cancel */\n onCancel: () => void;\n /** Whether form is submitting */\n isSubmitting?: boolean;\n /** Whether submit is disabled */\n disabled?: boolean;\n /** Form ID to connect submit button */\n formId?: string;\n /** Callback for submit (if not using form) */\n onSubmit?: () => void;\n /** Variant for submit button */\n submitVariant?: 'default' | 'destructive';\n}\n\n/**\n * FormSheetFooter - Standard footer for FormSheet with cancel/submit buttons\n */\nexport function FormSheetFooter({\n submitLabel = 'Save',\n cancelLabel = 'Cancel',\n onCancel,\n isSubmitting = false,\n disabled = false,\n formId,\n onSubmit,\n submitVariant = 'default',\n}: FormSheetFooterProps) {\n return (\n <div className=\"flex items-center justify-end gap-3\">\n <Button variant=\"outline\" onClick={onCancel} disabled={isSubmitting}>\n {cancelLabel}\n </Button>\n <Button\n type={formId ? 'submit' : 'button'}\n form={formId}\n onClick={onSubmit}\n disabled={disabled || isSubmitting}\n variant={submitVariant}\n >\n {isSubmitting && <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />}\n {submitLabel}\n </Button>\n </div>\n );\n}\n\n/**\n * FormSection - Group related form fields\n */\nexport interface FormSectionProps {\n /** Section title */\n title?: string;\n /** Section description */\n description?: string;\n /** Form fields */\n children: React.ReactNode;\n /** Additional className */\n className?: string;\n}\n\nexport function FormSection({ title, description, children, className }: FormSectionProps) {\n return (\n <div className={cn('space-y-4', className)}>\n {(title || description) && (\n <div className=\"space-y-1\">\n {title && <h3 className=\"text-sm font-medium text-text-primary\">{title}</h3>}\n {description && <p className=\"text-xs text-text-secondary\">{description}</p>}\n </div>\n )}\n <div className=\"space-y-4\">{children}</div>\n </div>\n );\n}\n\n/**\n * FormRow - Horizontal row for form fields\n */\nexport interface FormRowProps {\n /** Form fields */\n children: React.ReactNode;\n /** Number of columns */\n cols?: 2 | 3 | 4;\n /** Additional className */\n className?: string;\n}\n\nexport function FormRow({ children, cols = 2, className }: FormRowProps) {\n const colsClass = {\n 2: 'grid-cols-2',\n 3: 'grid-cols-3',\n 4: 'grid-cols-4',\n };\n return <div className={cn('grid gap-4', colsClass[cols], className)}>{children}</div>;\n}\n"],"mappings":";;;;;;AAyBA,IAAM,IAAe;CACnB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACL;AAQD,SAAgB,EAAU,EACxB,SACA,YACA,UACA,gBACA,aACA,WAAQ,MACR,aACiB;AA0BjB,QAxBA,EAAM,gBAAgB;EACpB,IAAM,KAAgB,MAAqB;AACzC,GAAI,EAAE,QAAQ,YAAY,KACxB,GAAS;;AAIb,SADA,SAAS,iBAAiB,WAAW,EAAa,QACrC,SAAS,oBAAoB,WAAW,EAAa;IACjE,CAAC,GAAM,EAAQ,CAAC,EAGnB,EAAM,iBACA,IACF,SAAS,KAAK,MAAM,WAAW,WAE/B,SAAS,KAAK,MAAM,WAAW,UAEpB;AACX,WAAS,KAAK,MAAM,WAAW;KAEhC,CAAC,EAAK,CAAC,EAEL,IAGH,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEE,kBAAC,OAAD;GACE,WAAU;GACV,SAAS;GACT,eAAY;GACZ,CAAA,EAGF,kBAAC,OAAD;GACE,WAAW,EACT,wEACA,+CACA,EAAa,GACd;GACD,MAAK;GACL,cAAW;GACX,mBAAgB;aARlB;IAWE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MAAI,IAAG;MAAc,WAAU;gBAC5B;MACE,CAAA,EACJ,KAAe,kBAAC,KAAD;MAAG,WAAU;gBAAoC;MAAgB,CAAA,CAC7E,EAAA,CAAA,EACN,kBAAC,GAAD;MAAQ,SAAQ;MAAQ,MAAK;MAAO,SAAS;MAAS,cAAW;gBAC/D,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA;MAClB,CAAA,CACL;;IAGN,kBAAC,OAAD;KAAK,WAAU;KAAoC;KAAe,CAAA;IAGjE,KAAU,kBAAC,OAAD;KAAK,WAAU;eAA2C;KAAa,CAAA;IAC9E;KACF;MAzCU;;AAsEpB,SAAgB,EAAgB,EAC9B,iBAAc,QACd,iBAAc,UACd,aACA,kBAAe,IACf,cAAW,IACX,WACA,aACA,mBAAgB,aACO;AACvB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GAAQ,SAAQ;GAAU,SAAS;GAAU,UAAU;aACpD;GACM,CAAA,EACT,kBAAC,GAAD;GACE,MAAM,IAAS,WAAW;GAC1B,MAAM;GACN,SAAS;GACT,UAAU,KAAY;GACtB,SAAS;aALX,CAOG,KAAgB,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,EACjE,EACM;KACL;;;AAkBV,SAAgB,EAAY,EAAE,UAAO,gBAAa,aAAU,gBAA+B;AACzF,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,aAAa,EAAU;YAA1C,EACI,KAAS,MACT,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,KAAS,kBAAC,MAAD;IAAI,WAAU;cAAyC;IAAW,CAAA,EAC3E,KAAe,kBAAC,KAAD;IAAG,WAAU;cAA+B;IAAgB,CAAA,CACxE;MAER,kBAAC,OAAD;GAAK,WAAU;GAAa;GAAe,CAAA,CACvC;;;AAgBV,SAAgB,EAAQ,EAAE,aAAU,UAAO,GAAG,gBAA2B;AAMvE,QAAO,kBAAC,OAAD;EAAK,WAAW,EAAG,cALR;GAChB,GAAG;GACH,GAAG;GACH,GAAG;GACJ,CACiD,IAAO,EAAU;EAAG;EAAe,CAAA"}
@@ -234,7 +234,7 @@ function E({ tenantId: E, canManage: D }) {
234
234
  enabled: !e.enabled
235
235
  })),
236
236
  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 ${F.enabled ? "bg-action-primary-bg" : "bg-bg-sunken"}`,
237
- children: /* @__PURE__ */ S("span", { className: `pointer-events-none inline-block h-4 w-4 transform rounded-full bg-white shadow-lg transition-transform ${F.enabled ? "translate-x-4" : "translate-x-0"}` })
237
+ children: /* @__PURE__ */ S("span", { className: `pointer-events-none inline-block h-4 w-4 transform rounded-full bg-bg-surface shadow-lg transition-transform ${F.enabled ? "translate-x-4" : "translate-x-0"}` })
238
238
  }), /* @__PURE__ */ S("span", {
239
239
  className: "text-sm",
240
240
  children: k("admin.tenantDetail.sso.enabledLabel", "Enable SSO for this tenant")
@@ -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":"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"}
@@ -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";
@@ -48,9 +48,9 @@ u.displayName = "CardFooter";
48
48
  var d = {
49
49
  default: "bg-action-primary-bg text-action-primary-text",
50
50
  secondary: "bg-secondary text-secondary-foreground",
51
- success: "bg-success/10 text-success border-success/20",
52
- warning: "bg-warning/10 text-warning border-warning/20",
53
- 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",
54
54
  outline: "border-border-subtle bg-transparent text-text-primary"
55
55
  }, f = t(({ className: t, variant: r = "default", children: i, ...a }, o) => /* @__PURE__ */ n("span", {
56
56
  ref: o,
@@ -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 { 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-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 * 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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"}
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":";;;;;AAkBA,IAAa,IAAO,GACjB,EAAE,cAAW,aAAU,GAAG,KAAS,MAClC,kBAAC,OAAD;CACO;CACL,WAAW,EACT,0KACA,EACD;CACD,GAAI;CAEH;CACG,CAAA,CAET;AACD,EAAK,cAAc;AAEnB,IAAa,IAAa,GACvB,EAAE,cAAW,aAAU,GAAG,KAAS,MAClC,kBAAC,OAAD;CAAU;CAAK,WAAW,EAAG,iCAAiC,EAAU;CAAE,GAAI;CAC3E;CACG,CAAA,CAET;AACD,EAAW,cAAc;AAEzB,IAAa,IAAY,GACtB,EAAE,cAAW,aAAU,GAAG,KAAS,MAClC,kBAAC,MAAD;CACO;CACL,WAAW,EAAG,sDAAsD,EAAU;CAC9E,GAAI;CAEH;CACE,CAAA,CAER;AACD,EAAU,cAAc;AAExB,IAAa,IAAkB,GAG5B,EAAE,cAAW,aAAU,GAAG,KAAS,MACpC,kBAAC,KAAD;CAAQ;CAAK,WAAW,EAAG,+BAA+B,EAAU;CAAE,GAAI;CACvE;CACC,CAAA,CACJ;AACF,EAAgB,cAAc;AAE9B,IAAa,IAAc,GACxB,EAAE,cAAW,aAAU,GAAG,KAAS,MAClC,kBAAC,OAAD;CAAU;CAAK,WAAW,EAAG,YAAY,EAAU;CAAE,GAAI;CACtD;CACG,CAAA,CAET;AACD,EAAY,cAAc;AAE1B,IAAa,IAAa,GACvB,EAAE,cAAW,aAAU,GAAG,KAAS,MAClC,kBAAC,OAAD;CAAU;CAAK,WAAW,EAAG,8BAA8B,EAAU;CAAE,GAAI;CACxE;CACG,CAAA,CAET;AACD,EAAW,cAAc;AAUzB,IAAM,IAAgB;CACpB,SAAS;CACT,WAAW;CACX,SAAS;CACT,SAAS;CACT,aAAa;CACb,SAAS;CACV,EAEY,IAAQ,GAClB,EAAE,cAAW,aAAU,WAAW,aAAU,GAAG,KAAS,MACvD,kBAAC,QAAD;CACO;CACL,WAAW,EACT,sGACA,EAAc,IACd,EACD;CACD,GAAI;CAEH;CACI,CAAA,CAEV;AACD,EAAM,cAAc;AAWpB,IAAM,IAAiB;CACrB,SAAS;CACT,WAAW;CACX,SAAS;CACT,OAAO;CACP,aAAa;CACb,MAAM;CACP,EAEK,IAAc;CAClB,SAAS;CACT,IAAI;CACJ,IAAI;CACJ,MAAM;CACP,EAEY,IAAS,GACnB,EAAE,cAAW,aAAU,WAAW,UAAO,WAAW,aAAU,GAAG,KAAS,MACzE,kBAAC,UAAD;CACO;CACL,WAAW,EACT,qIACA,4HACA,oDACA,EAAe,IACf,EAAY,IACZ,EACD;CACD,GAAI;CAEH;CACM,CAAA,CAEZ;AACD,EAAO,cAAc;AAQrB,IAAa,IAAQ,GAClB,EAAE,cAAW,SAAM,GAAG,KAAS,MAC9B,kBAAC,SAAD;CACQ;CACN,WAAW,EACT,mHACA,0FACA,mCACA,4HACA,mDACA,EACD;CACI;CACL,GAAI;CACJ,CAAA,CAEL;AACD,EAAM,cAAc;AAQpB,IAAa,IAAW,GACrB,EAAE,cAAW,GAAG,KAAS,MACxB,kBAAC,OAAD;CAAU;CAAK,WAAW,EAAG,yCAAyC,EAAU;CAAE,GAAI;CAAS,CAAA,CAElG;AACD,EAAS,cAAc;AAUvB,IAAa,IAAY,GACtB,EAAE,cAAW,iBAAc,cAAc,GAAG,KAAS,MACpD,kBAAC,OAAD;CACO;CACL,WAAW,EACT,sBACA,MAAgB,eAAe,mBAAmB,kBAClD,EACD;CACD,GAAI;CACJ,CAAA,CAEL;AACD,EAAU,cAAc;AAaxB,IAAM,IAAc;CAClB,IAAI;CACJ,SAAS;CACT,IAAI;CACL,EAEY,IAAS,GACnB,EAAE,cAAW,QAAK,QAAK,aAAU,UAAO,WAAW,GAAG,KAAS,MAC9D,kBAAC,OAAD;CACO;CACL,WAAW,EACT,oEACA,EAAY,IACZ,EACD;CACD,GAAI;WAEH,IACC,kBAAC,OAAD;EAAU;EAAU;EAAK,WAAU;EAA6C,CAAA,GAEhF,kBAAC,QAAD;EAAM,WAAU;YACb;EACI,CAAA;CAEL,CAAA,CAET;AACD,EAAO,cAAc;AAqBrB,SAAgB,EAAW,EAAE,SAAM,UAAO,gBAAa,WAAQ,mBAAiC;AAY9F,QAXI,IAEA,kBAAC,GAAD;EACgB;EACP;EACM;EACL;EACR,WAAU;EACV,CAAA,GAIJ,kBAAC,OAAD;EAAK,WAAU;YAAf;GACG,KAAQ,kBAAC,OAAD;IAAK,WAAU;cAA0D;IAAW,CAAA;GAC7F,kBAAC,MAAD;IAAI,WAAU;cAA8B;IAAW,CAAA;GACtD,KAAe,kBAAC,KAAD;IAAG,WAAU;cAA6C;IAAgB,CAAA;GACzF;GACG"}
@@ -0,0 +1,12 @@
1
+ //#region src/admin/constants/featureFlags.ts
2
+ var e = {
3
+ management: "tenant.management",
4
+ list: "tenant.list",
5
+ detail: "tenant.detail",
6
+ create: "tenant.create",
7
+ edit: "tenant.edit"
8
+ };
9
+ //#endregion
10
+ export { e as TENANT_FEATURE_FLAGS };
11
+
12
+ //# sourceMappingURL=featureFlags.js.map