@burdenoff/microfe-admin 2026.610.1 → 2026.624.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/admin/components/AccessDenied.js +2 -2
  2. package/dist/admin/components/AccessDenied.js.map +1 -1
  3. package/dist/admin/components/ActivityFeed.js +4 -4
  4. package/dist/admin/components/ActivityFeed.js.map +1 -1
  5. package/dist/admin/components/Charts.js +4 -4
  6. package/dist/admin/components/Charts.js.map +1 -1
  7. package/dist/admin/components/ConfirmDialog.js +6 -6
  8. package/dist/admin/components/ConfirmDialog.js.map +1 -1
  9. package/dist/admin/components/DataTable.js +7 -7
  10. package/dist/admin/components/DataTable.js.map +1 -1
  11. package/dist/admin/components/FormSheet.js +7 -7
  12. package/dist/admin/components/FormSheet.js.map +1 -1
  13. package/dist/admin/components/MetricCard.js +4 -4
  14. package/dist/admin/components/MetricCard.js.map +1 -1
  15. package/dist/admin/components/PageLayout.js +3 -3
  16. package/dist/admin/components/PageLayout.js.map +1 -1
  17. package/dist/admin/components/PermissionGuard.js +1 -1
  18. package/dist/admin/components/PermissionGuard.js.map +1 -1
  19. package/dist/admin/components/RowActions.js +2 -2
  20. package/dist/admin/components/RowActions.js.map +1 -1
  21. package/dist/admin/components/TenantMembersPanel.js +15 -15
  22. package/dist/admin/components/TenantMembersPanel.js.map +1 -1
  23. package/dist/admin/components/TenantSsoPanel.js +14 -14
  24. package/dist/admin/components/TenantSsoPanel.js.map +1 -1
  25. package/dist/admin/components/Toast.js +9 -9
  26. package/dist/admin/components/Toast.js.map +1 -1
  27. package/dist/admin/components/forms/SubscriptionForm.js +10 -10
  28. package/dist/admin/components/forms/SubscriptionForm.js.map +1 -1
  29. package/dist/admin/components/forms/TenantForm.js +8 -8
  30. package/dist/admin/components/forms/TenantForm.js.map +1 -1
  31. package/dist/admin/components/forms/TicketForm.js +2 -2
  32. package/dist/admin/components/forms/TicketForm.js.map +1 -1
  33. package/dist/admin/components/forms/UserForm.js +8 -8
  34. package/dist/admin/components/forms/UserForm.js.map +1 -1
  35. package/dist/admin/components/ui.js +16 -16
  36. package/dist/admin/components/ui.js.map +1 -1
  37. package/dist/admin/constants/index.js +8 -8
  38. package/dist/admin/constants/index.js.map +1 -1
  39. package/dist/admin/pages/AuditLogsPage.js +8 -8
  40. package/dist/admin/pages/AuditLogsPage.js.map +1 -1
  41. package/dist/admin/pages/CommunicationsPage.js +2 -2
  42. package/dist/admin/pages/CommunicationsPage.js.map +1 -1
  43. package/dist/admin/pages/FeatureFlagsPage.js +4 -4
  44. package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
  45. package/dist/admin/pages/OrganizationsListPage.js +6 -6
  46. package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
  47. package/dist/admin/pages/ProductsPage.js +7 -7
  48. package/dist/admin/pages/ProductsPage.js.map +1 -1
  49. package/dist/admin/pages/ReferralsPage.js +23 -23
  50. package/dist/admin/pages/ReferralsPage.js.map +1 -1
  51. package/dist/admin/pages/RolesPermissionsPage.js +8 -8
  52. package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
  53. package/dist/admin/pages/SREPage.js +8 -8
  54. package/dist/admin/pages/SREPage.js.map +1 -1
  55. package/dist/admin/pages/SandboxSettingsPage.js +20 -20
  56. package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
  57. package/dist/admin/pages/SettingsPage.js +20 -20
  58. package/dist/admin/pages/SettingsPage.js.map +1 -1
  59. package/dist/admin/pages/SyncFailuresPage.js +11 -11
  60. package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
  61. package/dist/admin/pages/TenantDetailPage.js +22 -22
  62. package/dist/admin/pages/TenantDetailPage.js.map +1 -1
  63. package/dist/admin/pages/TenantFormRoutePage.js +2 -2
  64. package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
  65. package/dist/admin/pages/TenantsListPage.js +5 -5
  66. package/dist/admin/pages/TenantsListPage.js.map +1 -1
  67. package/dist/admin/pages/UsersListPage.js +6 -6
  68. package/dist/admin/pages/UsersListPage.js.map +1 -1
  69. package/dist/admin/pages/WaitlistPage.js +7 -7
  70. package/dist/admin/pages/WaitlistPage.js.map +1 -1
  71. package/dist/admin/pages/WorkflowsPage.js +3 -3
  72. package/dist/admin/pages/WorkflowsPage.js.map +1 -1
  73. package/dist/admin/pages/WorkspacesListPage.js +8 -8
  74. package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
  75. package/package.json +1 -1
@@ -12,11 +12,11 @@ var r = ({ message: r = "You don't have permission to access this page." }) => /
12
12
  children: /* @__PURE__ */ t(e, { className: "w-7 h-7 text-red-500" })
13
13
  }),
14
14
  /* @__PURE__ */ t("h2", {
15
- className: "text-lg font-semibold text-foreground",
15
+ className: "text-lg font-semibold text-text-primary",
16
16
  children: "Access Denied"
17
17
  }),
18
18
  /* @__PURE__ */ t("p", {
19
- className: "text-sm text-muted-foreground max-w-sm",
19
+ className: "text-sm text-text-secondary max-w-sm",
20
20
  children: r
21
21
  })
22
22
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"AccessDenied.js","names":[],"sources":["../../../src/admin/components/AccessDenied.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { ShieldOff } from 'lucide-react';\n\ninterface AccessDeniedProps {\n message?: string;\n}\n\nexport const AccessDenied: FC<AccessDeniedProps> = ({\n message = \"You don't have permission to access this page.\",\n}) => {\n return (\n <div role=\"alert\" className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-3\">\n <div className=\"w-14 h-14 mx-auto rounded-full bg-red-50 dark:bg-red-950/30 flex items-center justify-center\">\n <ShieldOff className=\"w-7 h-7 text-red-500\" />\n </div>\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-muted-foreground max-w-sm\">{message}</p>\n </div>\n </div>\n );\n};\n"],"mappings":";;;AAOA,IAAa,KAAuC,EAClD,aAAU,uDAGR,kBAAC,OAAD;CAAK,MAAK;CAAQ,WAAU;WAC1B,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD,EAAW,WAAU,wBAAyB,CAAA;IAC1C,CAAA;GACN,kBAAC,MAAD;IAAI,WAAU;cAAwC;IAAkB,CAAA;GACxE,kBAAC,KAAD;IAAG,WAAU;cAA0C;IAAY,CAAA;GAC/D;;CACF,CAAA"}
1
+ {"version":3,"file":"AccessDenied.js","names":[],"sources":["../../../src/admin/components/AccessDenied.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { ShieldOff } from 'lucide-react';\n\ninterface AccessDeniedProps {\n message?: string;\n}\n\nexport const AccessDenied: FC<AccessDeniedProps> = ({\n message = \"You don't have permission to access this page.\",\n}) => {\n return (\n <div role=\"alert\" className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-3\">\n <div className=\"w-14 h-14 mx-auto rounded-full bg-red-50 dark:bg-red-950/30 flex items-center justify-center\">\n <ShieldOff className=\"w-7 h-7 text-red-500\" />\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary max-w-sm\">{message}</p>\n </div>\n </div>\n );\n};\n"],"mappings":";;;AAOA,IAAa,KAAuC,EAClD,aAAU,uDAGR,kBAAC,OAAD;CAAK,MAAK;CAAQ,WAAU;WAC1B,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD,EAAW,WAAU,wBAAyB,CAAA;IAC1C,CAAA;GACN,kBAAC,MAAD;IAAI,WAAU;cAA0C;IAAkB,CAAA;GAC1E,kBAAC,KAAD;IAAG,WAAU;cAAwC;IAAY,CAAA;GAC7D;;CACF,CAAA"}
@@ -23,7 +23,7 @@ function S({ activities: e, maxItems: a = 10, loading: o, viewAllLink: s = "/act
23
23
  className: "space-y-4",
24
24
  children: Array.from({ length: 5 }).map((e, t) => /* @__PURE__ */ b(w, {}, t))
25
25
  }) : l.length === 0 ? /* @__PURE__ */ b("p", {
26
- className: "py-8 text-center text-sm text-muted-foreground",
26
+ className: "py-8 text-center text-sm text-text-secondary",
27
27
  children: "No recent activity"
28
28
  }) : /* @__PURE__ */ b("div", {
29
29
  className: "space-y-4",
@@ -39,17 +39,17 @@ function C({ activity: t }) {
39
39
  className: e("rounded-full p-2", {
40
40
  info: "bg-info/10 text-info",
41
41
  warning: "bg-warning/10 text-warning",
42
- error: "bg-destructive/10 text-destructive",
42
+ error: "bg-status-error-bg/10 text-status-error-text",
43
43
  success: "bg-success/10 text-success"
44
44
  }[t.severity || "info"]),
45
45
  children: n
46
46
  }), /* @__PURE__ */ x("div", {
47
47
  className: "min-w-0 flex-1",
48
48
  children: [/* @__PURE__ */ b("p", {
49
- className: "text-sm font-medium text-foreground",
49
+ className: "text-sm font-medium text-text-primary",
50
50
  children: t.description
51
51
  }), /* @__PURE__ */ x("div", {
52
- className: "mt-1 flex items-center gap-2 text-xs text-muted-foreground",
52
+ className: "mt-1 flex items-center gap-2 text-xs text-text-secondary",
53
53
  children: [
54
54
  /* @__PURE__ */ b("span", { children: t.actor }),
55
55
  /* @__PURE__ */ b("span", { children: "-" }),
@@ -1 +1 @@
1
- {"version":3,"file":"ActivityFeed.js","names":[],"sources":["../../../src/admin/components/ActivityFeed.tsx"],"sourcesContent":["import { Link } from 'react-router-dom';\nimport {\n Plus,\n Ban,\n AlertTriangle,\n CheckCircle,\n UserPlus,\n Rocket,\n XCircle,\n Activity,\n CreditCard,\n Mail,\n Settings,\n Shield,\n} from 'lucide-react';\nimport { Card, CardHeader, CardTitle, CardContent, Skeleton } from './ui';\nimport { cn } from '../utils/cn';\nimport { formatRelativeTime } from '../utils/formatters';\nimport type { Activity as ActivityType } from '../types';\n\ninterface ActivityFeedProps {\n /** List of activities */\n activities: ActivityType[];\n /** Maximum items to display */\n maxItems?: number;\n /** Loading state */\n loading?: boolean;\n /** Link to view all activities */\n viewAllLink?: string;\n /** Additional class name */\n className?: string;\n}\n\n/**\n * Activity feed component showing recent platform activities\n */\nexport function ActivityFeed({\n activities,\n maxItems = 10,\n loading,\n viewAllLink = '/activity',\n className,\n}: ActivityFeedProps) {\n const displayActivities = activities.slice(0, maxItems);\n\n return (\n <Card className={className}>\n <CardHeader className=\"flex flex-row items-center justify-between\">\n <CardTitle className=\"text-lg\">Recent Activity</CardTitle>\n <Link to={viewAllLink} className=\"text-sm text-primary hover:underline\">\n View All\n </Link>\n </CardHeader>\n <CardContent>\n {loading ? (\n <div className=\"space-y-4\">\n {Array.from({ length: 5 }).map((_, i) => (\n <ActivityItemSkeleton key={i} />\n ))}\n </div>\n ) : displayActivities.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-muted-foreground\">No recent activity</p>\n ) : (\n <div className=\"space-y-4\">\n {displayActivities.map((activity) => (\n <ActivityItem key={activity.id} activity={activity} />\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n );\n}\n\ninterface ActivityItemProps {\n activity: ActivityType;\n}\n\nfunction ActivityItem({ activity }: ActivityItemProps) {\n const icon = getActivityIcon(activity.type);\n const severityColors = {\n info: 'bg-info/10 text-info',\n warning: 'bg-warning/10 text-warning',\n error: 'bg-destructive/10 text-destructive',\n success: 'bg-success/10 text-success',\n };\n const severity = activity.severity || 'info';\n\n return (\n <div className=\"flex items-start gap-3\">\n <div className={cn('rounded-full p-2', severityColors[severity])}>{icon}</div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-foreground\">{activity.description}</p>\n <div className=\"mt-1 flex items-center gap-2 text-xs text-muted-foreground\">\n <span>{activity.actor}</span>\n <span>-</span>\n <span>{formatRelativeTime(activity.timestamp)}</span>\n </div>\n </div>\n </div>\n );\n}\n\nfunction ActivityItemSkeleton() {\n return (\n <div className=\"flex items-start gap-3\">\n <Skeleton className=\"h-9 w-9 rounded-full\" />\n <div className=\"flex-1 space-y-2\">\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </div>\n </div>\n );\n}\n\nfunction getActivityIcon(type: string) {\n const iconMap: Record<string, React.ReactNode> = {\n 'tenant.created': <Plus className=\"h-4 w-4\" />,\n 'tenant.suspended': <Ban className=\"h-4 w-4\" />,\n 'tenant.activated': <CheckCircle className=\"h-4 w-4\" />,\n 'payment.failed': <AlertTriangle className=\"h-4 w-4\" />,\n 'payment.succeeded': <CreditCard className=\"h-4 w-4\" />,\n 'user.created': <UserPlus className=\"h-4 w-4\" />,\n 'user.blocked': <Ban className=\"h-4 w-4\" />,\n 'deployment.started': <Rocket className=\"h-4 w-4\" />,\n 'deployment.completed': <CheckCircle className=\"h-4 w-4\" />,\n 'deployment.failed': <XCircle className=\"h-4 w-4\" />,\n 'newsletter.sent': <Mail className=\"h-4 w-4\" />,\n 'settings.changed': <Settings className=\"h-4 w-4\" />,\n 'security.alert': <Shield className=\"h-4 w-4\" />,\n };\n\n return iconMap[type] || <Activity className=\"h-4 w-4\" />;\n}\n"],"mappings":";;;;;;;AAoCA,SAAgB,EAAa,EAC3B,eACA,cAAW,IACX,YACA,iBAAc,aACd,gBACoB;CACpB,IAAM,IAAoB,EAAW,MAAM,GAAG,EAAS;AAEvD,QACE,kBAAC,GAAD;EAAiB;YAAjB,CACE,kBAAC,GAAD;GAAY,WAAU;aAAtB,CACE,kBAAC,GAAD;IAAW,WAAU;cAAU;IAA2B,CAAA,EAC1D,kBAAC,GAAD;IAAM,IAAI;IAAa,WAAU;cAAuC;IAEjE,CAAA,CACI;MACb,kBAAC,GAAD,EAAA,UACG,IACC,kBAAC,OAAD;GAAK,WAAU;aACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,GAAD,EAAgC,EAAL,EAAK,CAChC;GACE,CAAA,GACJ,EAAkB,WAAW,IAC/B,kBAAC,KAAD;GAAG,WAAU;aAAiD;GAAsB,CAAA,GAEpF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAkB,KAAK,MACtB,kBAAC,GAAD,EAA0C,aAAY,EAAnC,EAAS,GAA0B,CACtD;GACE,CAAA,EAEI,CAAA,CACT;;;AAQX,SAAS,EAAa,EAAE,eAA+B;CACrD,IAAM,IAAO,EAAgB,EAAS,KAAK;AAS3C,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAW,EAAG,oBAVA;IACrB,MAAM;IACN,SAAS;IACT,OAAO;IACP,SAAS;IACV,CACgB,EAAS,YAAY,QAI8B;aAAG;GAAW,CAAA,EAC9E,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAAuC,EAAS;IAAgB,CAAA,EAC7E,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD,EAAA,UAAO,EAAS,OAAa,CAAA;KAC7B,kBAAC,QAAD,EAAA,UAAM,KAAQ,CAAA;KACd,kBAAC,QAAD,EAAA,UAAO,EAAmB,EAAS,UAAU,EAAQ,CAAA;KACjD;MACF;KACF;;;AAIV,SAAS,IAAuB;AAC9B,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD,EAAU,WAAU,wBAAyB,CAAA,EAC7C,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAU,WAAU,aAAc,CAAA,EAClC,kBAAC,GAAD,EAAU,WAAU,aAAc,CAAA,CAC9B;KACF;;;AAIV,SAAS,EAAgB,GAAc;AAiBrC,QAhBiD;EAC/C,kBAAkB,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;EAC9C,oBAAoB,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA;EAC/C,oBAAoB,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;EACvD,kBAAkB,kBAAC,GAAD,EAAe,WAAU,WAAY,CAAA;EACvD,qBAAqB,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;EACvD,gBAAgB,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;EAChD,gBAAgB,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA;EAC3C,sBAAsB,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;EACpD,wBAAwB,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;EAC3D,qBAAqB,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA;EACpD,mBAAmB,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;EAC/C,oBAAoB,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;EACpD,kBAAkB,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;EACjD,CAEc,MAAS,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA"}
1
+ {"version":3,"file":"ActivityFeed.js","names":[],"sources":["../../../src/admin/components/ActivityFeed.tsx"],"sourcesContent":["import { Link } from 'react-router-dom';\nimport {\n Plus,\n Ban,\n AlertTriangle,\n CheckCircle,\n UserPlus,\n Rocket,\n XCircle,\n Activity,\n CreditCard,\n Mail,\n Settings,\n Shield,\n} from 'lucide-react';\nimport { Card, CardHeader, CardTitle, CardContent, Skeleton } from './ui';\nimport { cn } from '../utils/cn';\nimport { formatRelativeTime } from '../utils/formatters';\nimport type { Activity as ActivityType } from '../types';\n\ninterface ActivityFeedProps {\n /** List of activities */\n activities: ActivityType[];\n /** Maximum items to display */\n maxItems?: number;\n /** Loading state */\n loading?: boolean;\n /** Link to view all activities */\n viewAllLink?: string;\n /** Additional class name */\n className?: string;\n}\n\n/**\n * Activity feed component showing recent platform activities\n */\nexport function ActivityFeed({\n activities,\n maxItems = 10,\n loading,\n viewAllLink = '/activity',\n className,\n}: ActivityFeedProps) {\n const displayActivities = activities.slice(0, maxItems);\n\n return (\n <Card className={className}>\n <CardHeader className=\"flex flex-row items-center justify-between\">\n <CardTitle className=\"text-lg\">Recent Activity</CardTitle>\n <Link to={viewAllLink} className=\"text-sm text-primary hover:underline\">\n View All\n </Link>\n </CardHeader>\n <CardContent>\n {loading ? (\n <div className=\"space-y-4\">\n {Array.from({ length: 5 }).map((_, i) => (\n <ActivityItemSkeleton key={i} />\n ))}\n </div>\n ) : displayActivities.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-text-secondary\">No recent activity</p>\n ) : (\n <div className=\"space-y-4\">\n {displayActivities.map((activity) => (\n <ActivityItem key={activity.id} activity={activity} />\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n );\n}\n\ninterface ActivityItemProps {\n activity: ActivityType;\n}\n\nfunction ActivityItem({ activity }: ActivityItemProps) {\n const icon = getActivityIcon(activity.type);\n const severityColors = {\n info: 'bg-info/10 text-info',\n warning: 'bg-warning/10 text-warning',\n error: 'bg-status-error-bg/10 text-status-error-text',\n success: 'bg-success/10 text-success',\n };\n const severity = activity.severity || 'info';\n\n return (\n <div className=\"flex items-start gap-3\">\n <div className={cn('rounded-full p-2', severityColors[severity])}>{icon}</div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">{activity.description}</p>\n <div className=\"mt-1 flex items-center gap-2 text-xs text-text-secondary\">\n <span>{activity.actor}</span>\n <span>-</span>\n <span>{formatRelativeTime(activity.timestamp)}</span>\n </div>\n </div>\n </div>\n );\n}\n\nfunction ActivityItemSkeleton() {\n return (\n <div className=\"flex items-start gap-3\">\n <Skeleton className=\"h-9 w-9 rounded-full\" />\n <div className=\"flex-1 space-y-2\">\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </div>\n </div>\n );\n}\n\nfunction getActivityIcon(type: string) {\n const iconMap: Record<string, React.ReactNode> = {\n 'tenant.created': <Plus className=\"h-4 w-4\" />,\n 'tenant.suspended': <Ban className=\"h-4 w-4\" />,\n 'tenant.activated': <CheckCircle className=\"h-4 w-4\" />,\n 'payment.failed': <AlertTriangle className=\"h-4 w-4\" />,\n 'payment.succeeded': <CreditCard className=\"h-4 w-4\" />,\n 'user.created': <UserPlus className=\"h-4 w-4\" />,\n 'user.blocked': <Ban className=\"h-4 w-4\" />,\n 'deployment.started': <Rocket className=\"h-4 w-4\" />,\n 'deployment.completed': <CheckCircle className=\"h-4 w-4\" />,\n 'deployment.failed': <XCircle className=\"h-4 w-4\" />,\n 'newsletter.sent': <Mail className=\"h-4 w-4\" />,\n 'settings.changed': <Settings className=\"h-4 w-4\" />,\n 'security.alert': <Shield className=\"h-4 w-4\" />,\n };\n\n return iconMap[type] || <Activity className=\"h-4 w-4\" />;\n}\n"],"mappings":";;;;;;;AAoCA,SAAgB,EAAa,EAC3B,eACA,cAAW,IACX,YACA,iBAAc,aACd,gBACoB;CACpB,IAAM,IAAoB,EAAW,MAAM,GAAG,EAAS;AAEvD,QACE,kBAAC,GAAD;EAAiB;YAAjB,CACE,kBAAC,GAAD;GAAY,WAAU;aAAtB,CACE,kBAAC,GAAD;IAAW,WAAU;cAAU;IAA2B,CAAA,EAC1D,kBAAC,GAAD;IAAM,IAAI;IAAa,WAAU;cAAuC;IAEjE,CAAA,CACI;MACb,kBAAC,GAAD,EAAA,UACG,IACC,kBAAC,OAAD;GAAK,WAAU;aACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,GAAD,EAAgC,EAAL,EAAK,CAChC;GACE,CAAA,GACJ,EAAkB,WAAW,IAC/B,kBAAC,KAAD;GAAG,WAAU;aAA+C;GAAsB,CAAA,GAElF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAkB,KAAK,MACtB,kBAAC,GAAD,EAA0C,aAAY,EAAnC,EAAS,GAA0B,CACtD;GACE,CAAA,EAEI,CAAA,CACT;;;AAQX,SAAS,EAAa,EAAE,eAA+B;CACrD,IAAM,IAAO,EAAgB,EAAS,KAAK;AAS3C,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAW,EAAG,oBAVA;IACrB,MAAM;IACN,SAAS;IACT,OAAO;IACP,SAAS;IACV,CACgB,EAAS,YAAY,QAI8B;aAAG;GAAW,CAAA,EAC9E,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAAyC,EAAS;IAAgB,CAAA,EAC/E,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD,EAAA,UAAO,EAAS,OAAa,CAAA;KAC7B,kBAAC,QAAD,EAAA,UAAM,KAAQ,CAAA;KACd,kBAAC,QAAD,EAAA,UAAO,EAAmB,EAAS,UAAU,EAAQ,CAAA;KACjD;MACF;KACF;;;AAIV,SAAS,IAAuB;AAC9B,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD,EAAU,WAAU,wBAAyB,CAAA,EAC7C,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAU,WAAU,aAAc,CAAA,EAClC,kBAAC,GAAD,EAAU,WAAU,aAAc,CAAA,CAC9B;KACF;;;AAIV,SAAS,EAAgB,GAAc;AAiBrC,QAhBiD;EAC/C,kBAAkB,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;EAC9C,oBAAoB,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA;EAC/C,oBAAoB,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;EACvD,kBAAkB,kBAAC,GAAD,EAAe,WAAU,WAAY,CAAA;EACvD,qBAAqB,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;EACvD,gBAAgB,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;EAChD,gBAAgB,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA;EAC3C,sBAAsB,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;EACpD,wBAAwB,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;EAC3D,qBAAqB,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA;EACpD,mBAAmB,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;EAC/C,oBAAoB,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;EACpD,kBAAkB,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;EACjD,CAEc,MAAS,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA"}
@@ -13,17 +13,17 @@ function s({ links: s, className: c }) {
13
13
  className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
14
14
  children: s.map((t) => /* @__PURE__ */ o("a", {
15
15
  href: t.to,
16
- className: e("flex items-center gap-3 rounded-lg border border-border p-3", "transition-colors hover:bg-accent"),
16
+ className: e("flex items-center gap-3 rounded-lg border border-border-subtle p-3", "transition-colors hover:bg-accent"),
17
17
  children: [/* @__PURE__ */ a("div", {
18
- className: "rounded-md bg-primary/10 p-2 text-primary",
18
+ className: "rounded-md bg-action-primary-bg/10 p-2 text-primary",
19
19
  children: t.icon
20
20
  }), /* @__PURE__ */ o("div", {
21
21
  className: "min-w-0 flex-1",
22
22
  children: [/* @__PURE__ */ a("p", {
23
- className: "font-medium text-foreground",
23
+ className: "font-medium text-text-primary",
24
24
  children: t.label
25
25
  }), t.description && /* @__PURE__ */ a("p", {
26
- className: "truncate text-xs text-muted-foreground",
26
+ className: "truncate text-xs text-text-secondary",
27
27
  children: t.description
28
28
  })]
29
29
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"Charts.js","names":[],"sources":["../../../src/admin/components/Charts.tsx"],"sourcesContent":["import {\n LineChart,\n Line,\n AreaChart,\n Area,\n PieChart,\n Pie,\n Cell,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n Legend,\n ResponsiveContainer,\n} from 'recharts';\nimport { Card, CardHeader, CardTitle, CardContent, Skeleton } from './ui';\nimport { cn } from '../utils/cn';\nimport { formatCurrency, formatNumber } from '../utils/formatters';\nimport type { RevenueTrendPoint, TenantGrowthPoint, ProductUsagePoint } from '../types';\n\n// Chart colors using CSS variables for theme support\nconst CHART_COLORS = [\n 'hsl(var(--chart-1, 221.2 83.2% 53.3%))',\n 'hsl(var(--chart-2, 142.1 76.2% 36.3%))',\n 'hsl(var(--chart-3, 47.9 95.8% 53.1%))',\n 'hsl(var(--chart-4, 0 84.2% 60.2%))',\n 'hsl(var(--chart-5, 262.1 83.3% 57.8%))',\n];\n\n// ============================================================================\n// Revenue Trend Chart (Line Chart)\n// ============================================================================\n\ninterface RevenueTrendChartProps {\n data: RevenueTrendPoint[];\n loading?: boolean;\n className?: string;\n}\n\nexport function RevenueTrendChart({ data, loading, className }: RevenueTrendChartProps) {\n return (\n <Card className={className}>\n <CardHeader>\n <CardTitle className=\"text-lg\">Revenue Trend</CardTitle>\n </CardHeader>\n <CardContent>\n {loading ? (\n <Skeleton className=\"h-[300px] w-full\" />\n ) : (\n <ResponsiveContainer width=\"100%\" height={300}>\n <LineChart data={data} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>\n <CartesianGrid strokeDasharray=\"3 3\" className=\"stroke-border\" />\n <XAxis\n dataKey=\"date\"\n className=\"text-xs fill-muted-foreground\"\n tick={{ fill: 'currentColor' }}\n />\n <YAxis\n className=\"text-xs fill-muted-foreground\"\n tick={{ fill: 'currentColor' }}\n tickFormatter={(value) => formatCurrency(value)}\n />\n <Tooltip\n contentStyle={{\n backgroundColor: 'hsl(var(--card))',\n border: '1px solid hsl(var(--border))',\n borderRadius: '6px',\n }}\n labelStyle={{ color: 'hsl(var(--foreground))' }}\n formatter={(value: number) => [formatCurrency(value), '']}\n />\n <Legend />\n <Line\n type=\"monotone\"\n dataKey=\"mrr\"\n name=\"MRR\"\n stroke={CHART_COLORS[0]}\n strokeWidth={2}\n dot={false}\n activeDot={{ r: 6 }}\n />\n <Line\n type=\"monotone\"\n dataKey=\"arr\"\n name=\"ARR\"\n stroke={CHART_COLORS[1]}\n strokeWidth={2}\n dot={false}\n activeDot={{ r: 6 }}\n />\n </LineChart>\n </ResponsiveContainer>\n )}\n </CardContent>\n </Card>\n );\n}\n\n// ============================================================================\n// Tenant Growth Chart (Area Chart)\n// ============================================================================\n\ninterface TenantGrowthChartProps {\n data: TenantGrowthPoint[];\n loading?: boolean;\n className?: string;\n}\n\nexport function TenantGrowthChart({ data, loading, className }: TenantGrowthChartProps) {\n return (\n <Card className={className}>\n <CardHeader>\n <CardTitle className=\"text-lg\">Tenant Growth</CardTitle>\n </CardHeader>\n <CardContent>\n {loading ? (\n <Skeleton className=\"h-[250px] w-full\" />\n ) : (\n <ResponsiveContainer width=\"100%\" height={250}>\n <AreaChart data={data} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>\n <CartesianGrid strokeDasharray=\"3 3\" className=\"stroke-border\" />\n <XAxis\n dataKey=\"date\"\n className=\"text-xs fill-muted-foreground\"\n tick={{ fill: 'currentColor' }}\n />\n <YAxis className=\"text-xs fill-muted-foreground\" tick={{ fill: 'currentColor' }} />\n <Tooltip\n contentStyle={{\n backgroundColor: 'hsl(var(--card))',\n border: '1px solid hsl(var(--border))',\n borderRadius: '6px',\n }}\n labelStyle={{ color: 'hsl(var(--foreground))' }}\n />\n <Legend />\n <Area\n type=\"monotone\"\n dataKey=\"new\"\n name=\"New\"\n stackId=\"1\"\n stroke={CHART_COLORS[0]}\n fill={CHART_COLORS[0]}\n fillOpacity={0.6}\n />\n <Area\n type=\"monotone\"\n dataKey=\"active\"\n name=\"Active\"\n stackId=\"2\"\n stroke={CHART_COLORS[1]}\n fill={CHART_COLORS[1]}\n fillOpacity={0.6}\n />\n <Area\n type=\"monotone\"\n dataKey=\"churned\"\n name=\"Churned\"\n stackId=\"3\"\n stroke={CHART_COLORS[3]}\n fill={CHART_COLORS[3]}\n fillOpacity={0.6}\n />\n </AreaChart>\n </ResponsiveContainer>\n )}\n </CardContent>\n </Card>\n );\n}\n\n// ============================================================================\n// Product Usage Chart (Pie Chart)\n// ============================================================================\n\ninterface ProductUsageChartProps {\n data: ProductUsagePoint[];\n loading?: boolean;\n className?: string;\n}\n\nexport function ProductUsageChart({ data, loading, className }: ProductUsageChartProps) {\n return (\n <Card className={className}>\n <CardHeader>\n <CardTitle className=\"text-lg\">Product Usage</CardTitle>\n </CardHeader>\n <CardContent>\n {loading ? (\n <Skeleton className=\"h-[250px] w-full\" />\n ) : (\n <ResponsiveContainer width=\"100%\" height={250}>\n <PieChart>\n <Pie\n data={data}\n dataKey=\"tenants\"\n nameKey=\"product\"\n cx=\"50%\"\n cy=\"50%\"\n outerRadius={80}\n label={({ name, percent }: { name: string; percent: number }) =>\n `${name} (${(percent * 100).toFixed(0)}%)`\n }\n labelLine={false}\n >\n {data.map((entry, index) => (\n <Cell\n key={`cell-${index}`}\n fill={entry.color || CHART_COLORS[index % CHART_COLORS.length]}\n />\n ))}\n </Pie>\n <Tooltip\n contentStyle={{\n backgroundColor: 'hsl(var(--card))',\n border: '1px solid hsl(var(--border))',\n borderRadius: '6px',\n }}\n formatter={(value: number) => [formatNumber(value) + ' tenants', '']}\n />\n <Legend />\n </PieChart>\n </ResponsiveContainer>\n )}\n </CardContent>\n </Card>\n );\n}\n\n// ============================================================================\n// Quick Links Card\n// ============================================================================\n\ninterface QuickLink {\n to: string;\n icon: React.ReactNode;\n label: string;\n description?: string;\n}\n\ninterface QuickLinksProps {\n links: QuickLink[];\n className?: string;\n}\n\nexport function QuickLinks({ links, className }: QuickLinksProps) {\n return (\n <Card className={className}>\n <CardHeader>\n <CardTitle className=\"text-lg\">Quick Actions</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {links.map((link) => (\n <a\n key={link.to}\n href={link.to}\n className={cn(\n 'flex items-center gap-3 rounded-lg border border-border p-3',\n 'transition-colors hover:bg-accent'\n )}\n >\n <div className=\"rounded-md bg-primary/10 p-2 text-primary\">{link.icon}</div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-medium text-foreground\">{link.label}</p>\n {link.description && (\n <p className=\"truncate text-xs text-muted-foreground\">{link.description}</p>\n )}\n </div>\n </a>\n ))}\n </div>\n </CardContent>\n </Card>\n );\n}\n"],"mappings":";;;;;AAqPA,SAAgB,EAAW,EAAE,UAAO,gBAA8B;AAChE,QACE,kBAAC,GAAD;EAAiB;YAAjB,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;GAAW,WAAU;aAAU;GAAyB,CAAA,EAC7C,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAM,KAAK,MACV,kBAAC,KAAD;IAEE,MAAM,EAAK;IACX,WAAW,EACT,+DACA,oCACD;cANH,CAQE,kBAAC,OAAD;KAAK,WAAU;eAA6C,EAAK;KAAW,CAAA,EAC5E,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAA+B,EAAK;MAAU,CAAA,EAC1D,EAAK,eACJ,kBAAC,KAAD;MAAG,WAAU;gBAA0C,EAAK;MAAgB,CAAA,CAE1E;OACJ;MAdG,EAAK,GAcR,CACJ;GACE,CAAA,EACM,CAAA,CACT"}
1
+ {"version":3,"file":"Charts.js","names":[],"sources":["../../../src/admin/components/Charts.tsx"],"sourcesContent":["import {\n LineChart,\n Line,\n AreaChart,\n Area,\n PieChart,\n Pie,\n Cell,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n Legend,\n ResponsiveContainer,\n} from 'recharts';\nimport { Card, CardHeader, CardTitle, CardContent, Skeleton } from './ui';\nimport { cn } from '../utils/cn';\nimport { formatCurrency, formatNumber } from '../utils/formatters';\nimport type { RevenueTrendPoint, TenantGrowthPoint, ProductUsagePoint } from '../types';\n\n// Chart colors using CSS variables for theme support\nconst CHART_COLORS = [\n 'hsl(var(--chart-1, 221.2 83.2% 53.3%))',\n 'hsl(var(--chart-2, 142.1 76.2% 36.3%))',\n 'hsl(var(--chart-3, 47.9 95.8% 53.1%))',\n 'hsl(var(--chart-4, 0 84.2% 60.2%))',\n 'hsl(var(--chart-5, 262.1 83.3% 57.8%))',\n];\n\n// ============================================================================\n// Revenue Trend Chart (Line Chart)\n// ============================================================================\n\ninterface RevenueTrendChartProps {\n data: RevenueTrendPoint[];\n loading?: boolean;\n className?: string;\n}\n\nexport function RevenueTrendChart({ data, loading, className }: RevenueTrendChartProps) {\n return (\n <Card className={className}>\n <CardHeader>\n <CardTitle className=\"text-lg\">Revenue Trend</CardTitle>\n </CardHeader>\n <CardContent>\n {loading ? (\n <Skeleton className=\"h-[300px] w-full\" />\n ) : (\n <ResponsiveContainer width=\"100%\" height={300}>\n <LineChart data={data} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>\n <CartesianGrid strokeDasharray=\"3 3\" className=\"stroke-border\" />\n <XAxis\n dataKey=\"date\"\n className=\"text-xs fill-muted-foreground\"\n tick={{ fill: 'currentColor' }}\n />\n <YAxis\n className=\"text-xs fill-muted-foreground\"\n tick={{ fill: 'currentColor' }}\n tickFormatter={(value) => formatCurrency(value)}\n />\n <Tooltip\n contentStyle={{\n backgroundColor: 'hsl(var(--card))',\n border: '1px solid hsl(var(--border))',\n borderRadius: '6px',\n }}\n labelStyle={{ color: 'hsl(var(--foreground))' }}\n formatter={(value: number) => [formatCurrency(value), '']}\n />\n <Legend />\n <Line\n type=\"monotone\"\n dataKey=\"mrr\"\n name=\"MRR\"\n stroke={CHART_COLORS[0]}\n strokeWidth={2}\n dot={false}\n activeDot={{ r: 6 }}\n />\n <Line\n type=\"monotone\"\n dataKey=\"arr\"\n name=\"ARR\"\n stroke={CHART_COLORS[1]}\n strokeWidth={2}\n dot={false}\n activeDot={{ r: 6 }}\n />\n </LineChart>\n </ResponsiveContainer>\n )}\n </CardContent>\n </Card>\n );\n}\n\n// ============================================================================\n// Tenant Growth Chart (Area Chart)\n// ============================================================================\n\ninterface TenantGrowthChartProps {\n data: TenantGrowthPoint[];\n loading?: boolean;\n className?: string;\n}\n\nexport function TenantGrowthChart({ data, loading, className }: TenantGrowthChartProps) {\n return (\n <Card className={className}>\n <CardHeader>\n <CardTitle className=\"text-lg\">Tenant Growth</CardTitle>\n </CardHeader>\n <CardContent>\n {loading ? (\n <Skeleton className=\"h-[250px] w-full\" />\n ) : (\n <ResponsiveContainer width=\"100%\" height={250}>\n <AreaChart data={data} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>\n <CartesianGrid strokeDasharray=\"3 3\" className=\"stroke-border\" />\n <XAxis\n dataKey=\"date\"\n className=\"text-xs fill-muted-foreground\"\n tick={{ fill: 'currentColor' }}\n />\n <YAxis className=\"text-xs fill-muted-foreground\" tick={{ fill: 'currentColor' }} />\n <Tooltip\n contentStyle={{\n backgroundColor: 'hsl(var(--card))',\n border: '1px solid hsl(var(--border))',\n borderRadius: '6px',\n }}\n labelStyle={{ color: 'hsl(var(--foreground))' }}\n />\n <Legend />\n <Area\n type=\"monotone\"\n dataKey=\"new\"\n name=\"New\"\n stackId=\"1\"\n stroke={CHART_COLORS[0]}\n fill={CHART_COLORS[0]}\n fillOpacity={0.6}\n />\n <Area\n type=\"monotone\"\n dataKey=\"active\"\n name=\"Active\"\n stackId=\"2\"\n stroke={CHART_COLORS[1]}\n fill={CHART_COLORS[1]}\n fillOpacity={0.6}\n />\n <Area\n type=\"monotone\"\n dataKey=\"churned\"\n name=\"Churned\"\n stackId=\"3\"\n stroke={CHART_COLORS[3]}\n fill={CHART_COLORS[3]}\n fillOpacity={0.6}\n />\n </AreaChart>\n </ResponsiveContainer>\n )}\n </CardContent>\n </Card>\n );\n}\n\n// ============================================================================\n// Product Usage Chart (Pie Chart)\n// ============================================================================\n\ninterface ProductUsageChartProps {\n data: ProductUsagePoint[];\n loading?: boolean;\n className?: string;\n}\n\nexport function ProductUsageChart({ data, loading, className }: ProductUsageChartProps) {\n return (\n <Card className={className}>\n <CardHeader>\n <CardTitle className=\"text-lg\">Product Usage</CardTitle>\n </CardHeader>\n <CardContent>\n {loading ? (\n <Skeleton className=\"h-[250px] w-full\" />\n ) : (\n <ResponsiveContainer width=\"100%\" height={250}>\n <PieChart>\n <Pie\n data={data}\n dataKey=\"tenants\"\n nameKey=\"product\"\n cx=\"50%\"\n cy=\"50%\"\n outerRadius={80}\n label={({ name, percent }: { name: string; percent: number }) =>\n `${name} (${(percent * 100).toFixed(0)}%)`\n }\n labelLine={false}\n >\n {data.map((entry, index) => (\n <Cell\n key={`cell-${index}`}\n fill={entry.color || CHART_COLORS[index % CHART_COLORS.length]}\n />\n ))}\n </Pie>\n <Tooltip\n contentStyle={{\n backgroundColor: 'hsl(var(--card))',\n border: '1px solid hsl(var(--border))',\n borderRadius: '6px',\n }}\n formatter={(value: number) => [formatNumber(value) + ' tenants', '']}\n />\n <Legend />\n </PieChart>\n </ResponsiveContainer>\n )}\n </CardContent>\n </Card>\n );\n}\n\n// ============================================================================\n// Quick Links Card\n// ============================================================================\n\ninterface QuickLink {\n to: string;\n icon: React.ReactNode;\n label: string;\n description?: string;\n}\n\ninterface QuickLinksProps {\n links: QuickLink[];\n className?: string;\n}\n\nexport function QuickLinks({ links, className }: QuickLinksProps) {\n return (\n <Card className={className}>\n <CardHeader>\n <CardTitle className=\"text-lg\">Quick Actions</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {links.map((link) => (\n <a\n key={link.to}\n href={link.to}\n className={cn(\n 'flex items-center gap-3 rounded-lg border border-border-subtle p-3',\n 'transition-colors hover:bg-accent'\n )}\n >\n <div className=\"rounded-md bg-action-primary-bg/10 p-2 text-primary\">{link.icon}</div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-medium text-text-primary\">{link.label}</p>\n {link.description && (\n <p className=\"truncate text-xs text-text-secondary\">{link.description}</p>\n )}\n </div>\n </a>\n ))}\n </div>\n </CardContent>\n </Card>\n );\n}\n"],"mappings":";;;;;AAqPA,SAAgB,EAAW,EAAE,UAAO,gBAA8B;AAChE,QACE,kBAAC,GAAD;EAAiB;YAAjB,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;GAAW,WAAU;aAAU;GAAyB,CAAA,EAC7C,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAM,KAAK,MACV,kBAAC,KAAD;IAEE,MAAM,EAAK;IACX,WAAW,EACT,sEACA,oCACD;cANH,CAQE,kBAAC,OAAD;KAAK,WAAU;eAAuD,EAAK;KAAW,CAAA,EACtF,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAiC,EAAK;MAAU,CAAA,EAC5D,EAAK,eACJ,kBAAC,KAAD;MAAG,WAAU;gBAAwC,EAAK;MAAgB,CAAA,CAExE;OACJ;MAdG,EAAK,GAcR,CACJ;GACE,CAAA,EACM,CAAA,CACT"}
@@ -27,7 +27,7 @@ function l({ open: o, onClose: l, onConfirm: u, title: d, description: f, confir
27
27
  }), [o]), !o) return null;
28
28
  let y = async () => {
29
29
  await u();
30
- }, b = h === "destructive", x = b ? /* @__PURE__ */ s(i, { className: "h-6 w-6 text-destructive" }) : null;
30
+ }, b = h === "destructive", x = b ? /* @__PURE__ */ s(i, { className: "h-6 w-6 text-status-error-text" }) : null;
31
31
  return /* @__PURE__ */ c("div", {
32
32
  className: "fixed inset-0 z-50 flex items-center justify-center",
33
33
  children: [/* @__PURE__ */ s("div", {
@@ -35,7 +35,7 @@ function l({ open: o, onClose: l, onConfirm: u, title: d, description: f, confir
35
35
  onClick: g ? void 0 : l,
36
36
  "aria-hidden": "true"
37
37
  }), /* @__PURE__ */ c("div", {
38
- className: e("relative z-10 w-full max-w-md rounded-lg bg-background p-6 shadow-xl", "animate-in zoom-in-95 fade-in duration-200"),
38
+ className: e("relative z-10 w-full max-w-md rounded-lg bg-bg-surface p-6 shadow-xl", "animate-in zoom-in-95 fade-in duration-200"),
39
39
  role: "alertdialog",
40
40
  "aria-modal": "true",
41
41
  "aria-labelledby": "confirm-dialog-title",
@@ -43,17 +43,17 @@ function l({ open: o, onClose: l, onConfirm: u, title: d, description: f, confir
43
43
  children: [/* @__PURE__ */ c("div", {
44
44
  className: "flex gap-4",
45
45
  children: [(_ || x) && /* @__PURE__ */ s("div", {
46
- className: e("flex h-12 w-12 shrink-0 items-center justify-center rounded-full", b ? "bg-destructive/10" : "bg-primary/10"),
46
+ className: e("flex h-12 w-12 shrink-0 items-center justify-center rounded-full", b ? "bg-status-error-bg/10" : "bg-action-primary-bg/10"),
47
47
  children: _ || x
48
48
  }), /* @__PURE__ */ c("div", {
49
49
  className: "flex-1",
50
50
  children: [/* @__PURE__ */ s("h3", {
51
51
  id: "confirm-dialog-title",
52
- className: "text-lg font-semibold text-foreground",
52
+ className: "text-lg font-semibold text-text-primary",
53
53
  children: d
54
54
  }), /* @__PURE__ */ s("div", {
55
55
  id: "confirm-dialog-description",
56
- className: "mt-2 text-sm text-muted-foreground",
56
+ className: "mt-2 text-sm text-text-secondary",
57
57
  children: f
58
58
  })]
59
59
  })]
@@ -84,7 +84,7 @@ function u({ open: e, onClose: t, onConfirm: n, itemName: r, itemType: i, loadin
84
84
  description: /* @__PURE__ */ c(o, { children: [
85
85
  "Are you sure you want to delete ",
86
86
  /* @__PURE__ */ s("strong", {
87
- className: "text-foreground",
87
+ className: "text-text-primary",
88
88
  children: r
89
89
  }),
90
90
  "? This action cannot be undone."
@@ -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-destructive\" /> : 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-background 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-destructive/10' : 'bg-primary/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-foreground\">\n {title}\n </h3>\n <div id=\"confirm-dialog-description\" className=\"mt-2 text-sm text-muted-foreground\">\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-foreground\">{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,4BAA6B,CAAA,GAAG;AAE7F,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,sBAAsB,gBACvC;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;cAAmB;IAAkB,CAAA;;GAEtF,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 ? <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"}
@@ -14,15 +14,15 @@ function l({ columns: t, data: r, keyExtractor: i, loading: a, skeletonRows: o =
14
14
  right: "text-right"
15
15
  };
16
16
  return /* @__PURE__ */ s("div", {
17
- className: e("overflow-hidden rounded-lg border border-border", m),
17
+ className: e("overflow-hidden rounded-lg border border-border-subtle", m),
18
18
  children: /* @__PURE__ */ s("div", {
19
19
  className: "overflow-x-auto",
20
20
  children: /* @__PURE__ */ c("table", {
21
21
  className: "w-full border-collapse",
22
22
  children: [/* @__PURE__ */ s("thead", { children: /* @__PURE__ */ s("tr", {
23
- className: "border-b border-border bg-muted/50",
23
+ className: "border-b border-border-subtle bg-bg-sunken/50",
24
24
  children: t.map((t) => /* @__PURE__ */ s("th", {
25
- className: e("px-4 py-3 text-sm font-medium text-muted-foreground", g[t.align || "left"], t.sortable && "cursor-pointer select-none hover:text-foreground", t.width),
25
+ className: e("px-4 py-3 text-sm font-medium text-text-secondary", g[t.align || "left"], t.sortable && "cursor-pointer select-none hover:text-text-primary", t.width),
26
26
  style: t.width ? { width: t.width } : void 0,
27
27
  onClick: () => h(t),
28
28
  children: /* @__PURE__ */ c("div", {
@@ -34,7 +34,7 @@ function l({ columns: t, data: r, keyExtractor: i, loading: a, skeletonRows: o =
34
34
  })
35
35
  }, t.key))
36
36
  }) }), /* @__PURE__ */ s("tbody", { children: a ? Array.from({ length: o }).map((e, r) => /* @__PURE__ */ s("tr", {
37
- className: "border-b border-border last:border-0",
37
+ className: "border-b border-border-subtle last:border-0",
38
38
  children: t.map((e) => /* @__PURE__ */ s("td", {
39
39
  className: "px-4 py-3",
40
40
  children: /* @__PURE__ */ s(n, { className: "h-5 w-full" })
@@ -43,11 +43,11 @@ function l({ columns: t, data: r, keyExtractor: i, loading: a, skeletonRows: o =
43
43
  colSpan: t.length,
44
44
  className: "px-4 py-8",
45
45
  children: l || /* @__PURE__ */ s("div", {
46
- className: "text-center text-sm text-muted-foreground",
46
+ className: "text-center text-sm text-text-secondary",
47
47
  children: "No data available"
48
48
  })
49
49
  }) }) : r.map((n, r) => /* @__PURE__ */ s("tr", {
50
- className: e("border-b border-border last:border-0 transition-colors", u && "cursor-pointer hover:bg-muted/50"),
50
+ className: e("border-b border-border-subtle last:border-0 transition-colors", u && "cursor-pointer hover:bg-bg-sunken/50"),
51
51
  onClick: () => u?.(n),
52
52
  children: t.map((t) => /* @__PURE__ */ s("td", {
53
53
  className: e("px-4 py-3 text-sm", g[t.align || "left"]),
@@ -63,7 +63,7 @@ function u({ page: n, totalPages: l, totalItems: u, pageSize: d, onPageChange: f
63
63
  return /* @__PURE__ */ c("div", {
64
64
  className: e("flex items-center justify-between px-4 py-3", p),
65
65
  children: [/* @__PURE__ */ c("div", {
66
- className: "text-sm text-muted-foreground",
66
+ className: "text-sm text-text-secondary",
67
67
  children: [
68
68
  "Showing ",
69
69
  m,
@@ -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-lg border border-border', className)}>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full border-collapse\">\n <thead>\n <tr className=\"border-b border-border bg-muted/50\">\n {columns.map((column) => (\n <th\n key={column.key}\n className={cn(\n 'px-4 py-3 text-sm font-medium text-muted-foreground',\n alignClasses[column.align || 'left'],\n column.sortable && 'cursor-pointer select-none hover:text-foreground',\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 last:border-0\">\n {columns.map((column) => (\n <td key={column.key} className=\"px-4 py-3\">\n <Skeleton className=\"h-5 w-full\" />\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-muted-foreground\">\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 last:border-0 transition-colors',\n onRowClick && 'cursor-pointer hover:bg-muted/50'\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-muted-foreground\">\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":";;;;;AAgDA,SAAgB,EAAa,EAC3B,YACA,SACA,iBACA,YACA,kBAAe,GACf,eACA,eACA,WACA,cACA,WACA,gBACoB;CACpB,IAAM,KAAc,MAAsB;AACxC,MAAI,CAAC,EAAO,YAAY,CAAC,EAAQ;EACjC,IAAM,IAAW,MAAW,EAAO,OAAO,MAAc,QAAQ,SAAS;AACzE,IAAO,EAAO,KAAK,EAAS;IAGxB,IAAe;EACnB,MAAM;EACN,QAAQ;EACR,OAAO;EACR;AAED,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,mDAAmD,EAAU;YAC9E,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,SAAD;IAAO,WAAU;cAAjB,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAQ,KAAK,MACZ,kBAAC,MAAD;MAEE,WAAW,EACT,uDACA,EAAa,EAAO,SAAS,SAC7B,EAAO,YAAY,oDACnB,EAAO,MACR;MACD,OAAO,EAAO,QAAQ,EAAE,OAAO,EAAO,OAAO,GAAG,KAAA;MAChD,eAAe,EAAW,EAAO;gBAEjC,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,EAAO,QACP,EAAO,YAAY,MAAW,EAAO,OACpC,kBAAC,QAAD;QAAM,WAAU;kBAAW,MAAc,QAAQ,MAAM;QAAW,CAAA,CAEhE;;MACH,EAhBE,EAAO,IAgBT,CACL;KACC,CAAA,EACC,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,IACC,MAAM,KAAK,EAAE,QAAQ,GAAc,CAAC,CAAC,KAAK,GAAG,MAC3C,kBAAC,MAAD;KAAmB,WAAU;eAC1B,EAAQ,KAAK,MACZ,kBAAC,MAAD;MAAqB,WAAU;gBAC7B,kBAAC,GAAD,EAAU,WAAU,cAAe,CAAA;MAChC,EAFI,EAAO,IAEX,CACL;KACC,EANI,EAMJ,CACL,GACA,EAAK,WAAW,IAClB,kBAAC,MAAD,EAAA,UACE,kBAAC,MAAD;KAAI,SAAS,EAAQ;KAAQ,WAAU;eACpC,KACC,kBAAC,OAAD;MAAK,WAAU;gBAA4C;MAErD,CAAA;KAEL,CAAA,EACF,CAAA,GAEL,EAAK,KAAK,GAAM,MACd,kBAAC,MAAD;KAEE,WAAW,EACT,0DACA,KAAc,mCACf;KACD,eAAe,IAAa,EAAK;eAEhC,EAAQ,KAAK,MACZ,kBAAC,MAAD;MAEE,WAAW,EAAG,qBAAqB,EAAa,EAAO,SAAS,QAAQ;gBAEvE,EAAO,SACJ,EAAO,OAAO,GAAM,EAAM,GAC1B,OAAQ,EAAiC,EAAO,QAAQ,GAAG;MAC5D,EANE,EAAO,IAMT,CACL;KACC,EAjBE,EAAa,EAAK,CAiBpB,CACL,EAEE,CAAA,CACF;;GACJ,CAAA;EACF,CAAA;;AAsBV,SAAgB,EAAW,EACzB,SACA,eACA,eACA,aACA,iBACA,gBACkB;CAClB,IAAM,KAAa,IAAO,KAAK,IAAW,GACpC,IAAU,KAAK,IAAI,IAAO,GAAU,EAAW;AAErD,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,+CAA+C,EAAU;YAA5E,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IAA+C;IACpC;IAAU;IAAK;IAAQ;IAAK;IAAW;IAC5C;MACN,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,SAAQ;KACR,MAAK;KACL,eAAe,EAAa,EAAE;KAC9B,UAAU,MAAS;KACnB,cAAW;eAEX,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA;KAC7B,CAAA;IACT,kBAAC,GAAD;KACE,SAAQ;KACR,MAAK;KACL,eAAe,EAAa,IAAO,EAAE;KACrC,UAAU,MAAS;KACnB,cAAW;eAEX,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;KAC5B,CAAA;IACT,kBAAC,QAAD;KAAM,WAAU;eAAhB;MAA+B;MACvB;MAAK;MAAK;MACX;;IACP,kBAAC,GAAD;KACE,SAAQ;KACR,MAAK;KACL,eAAe,EAAa,IAAO,EAAE;KACrC,UAAU,MAAS;KACnB,cAAW;eAEX,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA;KAC7B,CAAA;IACT,kBAAC,GAAD;KACE,SAAQ;KACR,MAAK;KACL,eAAe,EAAa,EAAW;KACvC,UAAU,MAAS;KACnB,cAAW;eAEX,kBAAC,GAAD,EAAe,WAAU,WAAY,CAAA;KAC9B,CAAA;IACL;KACF"}
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-lg border border-border-subtle', className)}>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full border-collapse\">\n <thead>\n <tr className=\"border-b border-border-subtle bg-bg-sunken/50\">\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-subtle last:border-0\">\n {columns.map((column) => (\n <td key={column.key} className=\"px-4 py-3\">\n <Skeleton className=\"h-5 w-full\" />\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-subtle last:border-0 transition-colors',\n onRowClick && 'cursor-pointer hover:bg-bg-sunken/50'\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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"}
@@ -25,19 +25,19 @@ function c({ open: r, onClose: c, title: l, description: u, children: d, width:
25
25
  onClick: c,
26
26
  "aria-hidden": "true"
27
27
  }), /* @__PURE__ */ o("div", {
28
- className: e("fixed inset-y-0 right-0 flex w-full flex-col bg-background shadow-xl", "animate-in slide-in-from-right duration-300", s[f]),
28
+ className: e("fixed inset-y-0 right-0 flex w-full flex-col bg-bg-surface shadow-xl", "animate-in slide-in-from-right duration-300", s[f]),
29
29
  role: "dialog",
30
30
  "aria-modal": "true",
31
31
  "aria-labelledby": "sheet-title",
32
32
  children: [
33
33
  /* @__PURE__ */ o("div", {
34
- className: "flex items-start justify-between border-b border-border px-6 py-4",
34
+ className: "flex items-start justify-between border-b border-border-subtle px-6 py-4",
35
35
  children: [/* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("h2", {
36
36
  id: "sheet-title",
37
- className: "text-lg font-semibold text-foreground",
37
+ className: "text-lg font-semibold text-text-primary",
38
38
  children: l
39
39
  }), u && /* @__PURE__ */ a("p", {
40
- className: "mt-1 text-sm text-muted-foreground",
40
+ className: "mt-1 text-sm text-text-secondary",
41
41
  children: u
42
42
  })] }), /* @__PURE__ */ a(t, {
43
43
  variant: "ghost",
@@ -52,7 +52,7 @@ function c({ open: r, onClose: c, title: l, description: u, children: d, width:
52
52
  children: d
53
53
  }),
54
54
  p && /* @__PURE__ */ a("div", {
55
- className: "border-t border-border px-6 py-4",
55
+ className: "border-t border-border-subtle px-6 py-4",
56
56
  children: p
57
57
  })
58
58
  ]
@@ -83,10 +83,10 @@ function u({ title: t, description: n, children: r, className: i }) {
83
83
  children: [(t || n) && /* @__PURE__ */ o("div", {
84
84
  className: "space-y-1",
85
85
  children: [t && /* @__PURE__ */ a("h3", {
86
- className: "text-sm font-medium text-foreground",
86
+ className: "text-sm font-medium text-text-primary",
87
87
  children: t
88
88
  }), n && /* @__PURE__ */ a("p", {
89
- className: "text-xs text-muted-foreground",
89
+ className: "text-xs text-text-secondary",
90
90
  children: n
91
91
  })]
92
92
  }), /* @__PURE__ */ a("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-background 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 px-6 py-4\">\n <div>\n <h2 id=\"sheet-title\" className=\"text-lg font-semibold text-foreground\">\n {title}\n </h2>\n {description && <p className=\"mt-1 text-sm text-muted-foreground\">{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 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-foreground\">{title}</h3>}\n {description && <p className=\"text-xs text-muted-foreground\">{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;gBAAsC;MAAgB,CAAA,CAC/E,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;eAAoC;KAAa,CAAA;IACvE;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;cAAuC;IAAW,CAAA,EACzE,KAAe,kBAAC,KAAD;IAAG,WAAU;cAAiC;IAAgB,CAAA,CAC1E;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-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"}
@@ -15,10 +15,10 @@ function g({ title: g, value: _, change: v, changeType: y = "number", trend: b,
15
15
  children: [/* @__PURE__ */ h(i, {
16
16
  className: "flex flex-row items-center justify-between pb-2",
17
17
  children: [/* @__PURE__ */ m(a, {
18
- className: "text-sm font-medium text-muted-foreground",
18
+ className: "text-sm font-medium text-text-secondary",
19
19
  children: g
20
20
  }), S && /* @__PURE__ */ m("div", {
21
- className: "text-muted-foreground",
21
+ className: "text-text-secondary",
22
22
  children: S
23
23
  })]
24
24
  }), /* @__PURE__ */ m(r, { children: E ? /* @__PURE__ */ h("div", {
@@ -27,7 +27,7 @@ function g({ title: g, value: _, change: v, changeType: y = "number", trend: b,
27
27
  }) : /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ h("div", {
28
28
  className: "flex items-baseline justify-between",
29
29
  children: [/* @__PURE__ */ m("div", {
30
- className: "text-3xl font-bold text-foreground",
30
+ className: "text-3xl font-bold text-text-primary",
31
31
  children: k
32
32
  }), A !== null && /* @__PURE__ */ h(t, {
33
33
  variant: O ? "success" : "destructive",
@@ -35,7 +35,7 @@ function g({ title: g, value: _, change: v, changeType: y = "number", trend: b,
35
35
  children: [/* @__PURE__ */ m(j, { className: "h-3 w-3" }), A]
36
36
  })]
37
37
  }), C && /* @__PURE__ */ m("p", {
38
- className: "mt-1 text-xs text-muted-foreground",
38
+ className: "mt-1 text-xs text-text-secondary",
39
39
  children: C
40
40
  })] }) })]
41
41
  });
@@ -1 +1 @@
1
- {"version":3,"file":"MetricCard.js","names":[],"sources":["../../../src/admin/components/MetricCard.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { Link } from 'react-router-dom';\nimport { TrendingUp, TrendingDown, Minus } from 'lucide-react';\nimport { Card, CardHeader, CardTitle, CardContent, Badge, Skeleton } from './ui';\nimport { cn } from '../utils/cn';\nimport { formatNumber, formatCurrency, formatChange } from '../utils/formatters';\n\nexport interface MetricCardProps {\n /** Card title */\n title: string;\n /** Metric value (number or formatted string) */\n value: number | string;\n /** Change from previous period */\n change?: number;\n /** Type of change display */\n changeType?: 'number' | 'percentage' | 'currency';\n /** Trend direction */\n trend?: 'up' | 'down' | 'neutral';\n /** Whether change is positive or negative for styling */\n changeIsPositive?: boolean;\n /** Icon to display */\n icon?: ReactNode;\n /** Subtitle text */\n subtitle?: string;\n /** Link to navigate to on click */\n linkTo?: string;\n /** Priority indicator */\n priority?: 'normal' | 'high' | 'critical';\n /** Loading state */\n loading?: boolean;\n /** Additional class name */\n className?: string;\n}\n\n/**\n * Metric card component for dashboard\n */\nexport function MetricCard({\n title,\n value,\n change,\n changeType = 'number',\n trend,\n changeIsPositive,\n icon,\n subtitle,\n linkTo,\n priority,\n loading,\n className,\n}: MetricCardProps) {\n // Determine if change is positive based on trend or explicit prop\n const isPositive = changeIsPositive ?? trend === 'up';\n\n // Format the value\n const formattedValue =\n typeof value === 'number'\n ? changeType === 'currency'\n ? formatCurrency(value)\n : formatNumber(value)\n : value;\n\n // Format the change\n const formattedChange =\n change !== undefined ? formatChange(change, changeType === 'percentage') : null;\n\n // Get trend icon\n const TrendIcon = trend === 'up' ? TrendingUp : trend === 'down' ? TrendingDown : Minus;\n\n // Priority border classes\n const priorityClasses = {\n normal: '',\n high: 'border-l-4 border-l-warning',\n critical: 'border-l-4 border-l-destructive',\n };\n\n const cardContent = (\n <Card\n className={cn(\n 'transition-shadow hover:shadow-md',\n priority && priorityClasses[priority],\n className\n )}\n >\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"text-sm font-medium text-muted-foreground\">{title}</CardTitle>\n {icon && <div className=\"text-muted-foreground\">{icon}</div>}\n </CardHeader>\n <CardContent>\n {loading ? (\n <div className=\"space-y-2\">\n <Skeleton className=\"h-8 w-24\" />\n <Skeleton className=\"h-4 w-16\" />\n </div>\n ) : (\n <div>\n <div className=\"flex items-baseline justify-between\">\n <div className=\"text-3xl font-bold text-foreground\">{formattedValue}</div>\n {formattedChange !== null && (\n <Badge\n variant={isPositive ? 'success' : 'destructive'}\n className=\"flex items-center gap-1\"\n >\n <TrendIcon className=\"h-3 w-3\" />\n {formattedChange}\n </Badge>\n )}\n </div>\n {subtitle && <p className=\"mt-1 text-xs text-muted-foreground\">{subtitle}</p>}\n </div>\n )}\n </CardContent>\n </Card>\n );\n\n if (linkTo) {\n return (\n <Link to={linkTo} className=\"block\">\n {cardContent}\n </Link>\n );\n }\n\n return cardContent;\n}\n\n/**\n * Grid container for metric cards\n */\nexport function MetricsGrid({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <div className={cn('grid gap-4', 'grid-cols-1 sm:grid-cols-2 xl:grid-cols-4', className)}>\n {children}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAqCA,SAAgB,EAAW,EACzB,UACA,UACA,WACA,gBAAa,UACb,UACA,qBACA,SACA,aACA,WACA,aACA,YACA,gBACkB;CAElB,IAAM,IAAa,KAAoB,MAAU,MAG3C,IACJ,OAAO,KAAU,WACb,MAAe,aACb,EAAe,EAAM,GACrB,EAAa,EAAM,GACrB,GAGA,IACJ,MAAW,KAAA,IAAgE,OAApD,EAAa,GAAQ,MAAe,aAAa,EAGpE,IAAY,MAAU,OAAO,IAAa,MAAU,SAAS,IAAe,GAS5E,IACJ,kBAAC,GAAD;EACE,WAAW,EACT,qCACA,KAVkB;GACtB,QAAQ;GACR,MAAM;GACN,UAAU;GACX,CAMiC,IAC5B,EACD;YALH,CAOE,kBAAC,GAAD;GAAY,WAAU;aAAtB,CACE,kBAAC,GAAD;IAAW,WAAU;cAA6C;IAAkB,CAAA,EACnF,KAAQ,kBAAC,OAAD;IAAK,WAAU;cAAyB;IAAW,CAAA,CACjD;MACb,kBAAC,GAAD,EAAA,UACG,IACC,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,EACjC,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,CAC7B;OAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAsC;IAAqB,CAAA,EACzE,MAAoB,QACnB,kBAAC,GAAD;IACE,SAAS,IAAa,YAAY;IAClC,WAAU;cAFZ,CAIE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAChC,EACK;MAEN;MACL,KAAY,kBAAC,KAAD;GAAG,WAAU;aAAsC;GAAa,CAAA,CACzE,EAAA,CAAA,EAEI,CAAA,CACT;;AAWT,QARI,IAEA,kBAAC,GAAD;EAAM,IAAI;EAAQ,WAAU;YACzB;EACI,CAAA,GAIJ;;AAMT,SAAgB,EAAY,EAAE,aAAU,gBAA0D;AAChG,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,cAAc,6CAA6C,EAAU;EACrF;EACG,CAAA"}
1
+ {"version":3,"file":"MetricCard.js","names":[],"sources":["../../../src/admin/components/MetricCard.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { Link } from 'react-router-dom';\nimport { TrendingUp, TrendingDown, Minus } from 'lucide-react';\nimport { Card, CardHeader, CardTitle, CardContent, Badge, Skeleton } from './ui';\nimport { cn } from '../utils/cn';\nimport { formatNumber, formatCurrency, formatChange } from '../utils/formatters';\n\nexport interface MetricCardProps {\n /** Card title */\n title: string;\n /** Metric value (number or formatted string) */\n value: number | string;\n /** Change from previous period */\n change?: number;\n /** Type of change display */\n changeType?: 'number' | 'percentage' | 'currency';\n /** Trend direction */\n trend?: 'up' | 'down' | 'neutral';\n /** Whether change is positive or negative for styling */\n changeIsPositive?: boolean;\n /** Icon to display */\n icon?: ReactNode;\n /** Subtitle text */\n subtitle?: string;\n /** Link to navigate to on click */\n linkTo?: string;\n /** Priority indicator */\n priority?: 'normal' | 'high' | 'critical';\n /** Loading state */\n loading?: boolean;\n /** Additional class name */\n className?: string;\n}\n\n/**\n * Metric card component for dashboard\n */\nexport function MetricCard({\n title,\n value,\n change,\n changeType = 'number',\n trend,\n changeIsPositive,\n icon,\n subtitle,\n linkTo,\n priority,\n loading,\n className,\n}: MetricCardProps) {\n // Determine if change is positive based on trend or explicit prop\n const isPositive = changeIsPositive ?? trend === 'up';\n\n // Format the value\n const formattedValue =\n typeof value === 'number'\n ? changeType === 'currency'\n ? formatCurrency(value)\n : formatNumber(value)\n : value;\n\n // Format the change\n const formattedChange =\n change !== undefined ? formatChange(change, changeType === 'percentage') : null;\n\n // Get trend icon\n const TrendIcon = trend === 'up' ? TrendingUp : trend === 'down' ? TrendingDown : Minus;\n\n // Priority border classes\n const priorityClasses = {\n normal: '',\n high: 'border-l-4 border-l-warning',\n critical: 'border-l-4 border-l-destructive',\n };\n\n const cardContent = (\n <Card\n className={cn(\n 'transition-shadow hover:shadow-md',\n priority && priorityClasses[priority],\n className\n )}\n >\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"text-sm font-medium text-text-secondary\">{title}</CardTitle>\n {icon && <div className=\"text-text-secondary\">{icon}</div>}\n </CardHeader>\n <CardContent>\n {loading ? (\n <div className=\"space-y-2\">\n <Skeleton className=\"h-8 w-24\" />\n <Skeleton className=\"h-4 w-16\" />\n </div>\n ) : (\n <div>\n <div className=\"flex items-baseline justify-between\">\n <div className=\"text-3xl font-bold text-text-primary\">{formattedValue}</div>\n {formattedChange !== null && (\n <Badge\n variant={isPositive ? 'success' : 'destructive'}\n className=\"flex items-center gap-1\"\n >\n <TrendIcon className=\"h-3 w-3\" />\n {formattedChange}\n </Badge>\n )}\n </div>\n {subtitle && <p className=\"mt-1 text-xs text-text-secondary\">{subtitle}</p>}\n </div>\n )}\n </CardContent>\n </Card>\n );\n\n if (linkTo) {\n return (\n <Link to={linkTo} className=\"block\">\n {cardContent}\n </Link>\n );\n }\n\n return cardContent;\n}\n\n/**\n * Grid container for metric cards\n */\nexport function MetricsGrid({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <div className={cn('grid gap-4', 'grid-cols-1 sm:grid-cols-2 xl:grid-cols-4', className)}>\n {children}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAqCA,SAAgB,EAAW,EACzB,UACA,UACA,WACA,gBAAa,UACb,UACA,qBACA,SACA,aACA,WACA,aACA,YACA,gBACkB;CAElB,IAAM,IAAa,KAAoB,MAAU,MAG3C,IACJ,OAAO,KAAU,WACb,MAAe,aACb,EAAe,EAAM,GACrB,EAAa,EAAM,GACrB,GAGA,IACJ,MAAW,KAAA,IAAgE,OAApD,EAAa,GAAQ,MAAe,aAAa,EAGpE,IAAY,MAAU,OAAO,IAAa,MAAU,SAAS,IAAe,GAS5E,IACJ,kBAAC,GAAD;EACE,WAAW,EACT,qCACA,KAVkB;GACtB,QAAQ;GACR,MAAM;GACN,UAAU;GACX,CAMiC,IAC5B,EACD;YALH,CAOE,kBAAC,GAAD;GAAY,WAAU;aAAtB,CACE,kBAAC,GAAD;IAAW,WAAU;cAA2C;IAAkB,CAAA,EACjF,KAAQ,kBAAC,OAAD;IAAK,WAAU;cAAuB;IAAW,CAAA,CAC/C;MACb,kBAAC,GAAD,EAAA,UACG,IACC,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,EACjC,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,CAC7B;OAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAwC;IAAqB,CAAA,EAC3E,MAAoB,QACnB,kBAAC,GAAD;IACE,SAAS,IAAa,YAAY;IAClC,WAAU;cAFZ,CAIE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAChC,EACK;MAEN;MACL,KAAY,kBAAC,KAAD;GAAG,WAAU;aAAoC;GAAa,CAAA,CACvE,EAAA,CAAA,EAEI,CAAA,CACT;;AAWT,QARI,IAEA,kBAAC,GAAD;EAAM,IAAI;EAAQ,WAAU;YACzB;EACI,CAAA,GAIJ;;AAMT,SAAgB,EAAY,EAAE,aAAU,gBAA0D;AAChG,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,cAAc,6CAA6C,EAAU;EACrF;EACG,CAAA"}
@@ -5,14 +5,14 @@ function r({ title: r, description: i, actions: a, children: o, className: s })
5
5
  return /* @__PURE__ */ n("div", {
6
6
  className: e("flex h-full flex-col", s),
7
7
  children: [/* @__PURE__ */ n("header", {
8
- className: "flex flex-col gap-4 border-b border-border bg-background px-6 py-4 sm:flex-row sm:items-center sm:justify-between",
8
+ className: "flex flex-col gap-4 border-b border-border-subtle bg-bg-surface px-6 py-4 sm:flex-row sm:items-center sm:justify-between",
9
9
  children: [/* @__PURE__ */ n("div", {
10
10
  className: "min-w-0 flex-1",
11
11
  children: [/* @__PURE__ */ t("h1", {
12
- className: "text-2xl font-bold tracking-tight text-foreground sm:text-3xl",
12
+ className: "text-2xl font-bold tracking-tight text-text-primary sm:text-3xl",
13
13
  children: r
14
14
  }), i && /* @__PURE__ */ t("p", {
15
- className: "mt-1 text-sm text-muted-foreground sm:text-base",
15
+ className: "mt-1 text-sm text-text-secondary sm:text-base",
16
16
  children: i
17
17
  })]
18
18
  }), a && /* @__PURE__ */ t("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/admin/components/PageLayout.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { cn } from '../utils/cn';\n\ninterface PageLayoutProps {\n /** Page title */\n title: string;\n /** Optional description */\n description?: string;\n /** Actions to display in the header (buttons, etc.) */\n actions?: ReactNode;\n /** Page content */\n children: ReactNode;\n /** Additional class names */\n className?: string;\n}\n\n/**\n * Standard page layout component with title, description, and content area\n */\nexport function PageLayout({ title, description, actions, children, className }: PageLayoutProps) {\n return (\n <div className={cn('flex h-full flex-col', className)}>\n {/* Page Header */}\n <header className=\"flex flex-col gap-4 border-b border-border bg-background px-6 py-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"text-2xl font-bold tracking-tight text-foreground sm:text-3xl\">{title}</h1>\n {description && (\n <p className=\"mt-1 text-sm text-muted-foreground sm:text-base\">{description}</p>\n )}\n </div>\n {actions && (\n <div className=\"flex flex-shrink-0 flex-wrap items-center gap-2\">{actions}</div>\n )}\n </header>\n\n {/* Page Content — must not use a <main> landmark here because the\n host shell (AppShellLayout) already renders one; nesting <main>\n elements is invalid HTML and an a11y violation. */}\n <div className=\"flex-1 overflow-auto p-6\">{children}</div>\n </div>\n );\n}\n\ninterface PageSectionProps {\n /** Section title */\n title?: string;\n /** Optional description */\n description?: string;\n /** Section content */\n children: ReactNode;\n /** Additional class names */\n className?: string;\n}\n\n/**\n * Section within a page\n */\nexport function PageSection({ title, description, children, className }: PageSectionProps) {\n return (\n <section className={cn('space-y-4', className)}>\n {(title || description) && (\n <div>\n {title && <h2 className=\"text-lg font-semibold text-foreground\">{title}</h2>}\n {description && <p className=\"text-sm text-muted-foreground\">{description}</p>}\n </div>\n )}\n {children}\n </section>\n );\n}\n"],"mappings":";;;AAmBA,SAAgB,EAAW,EAAE,UAAO,gBAAa,YAAS,aAAU,gBAA8B;AAChG,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,wBAAwB,EAAU;YAArD,CAEE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eAAiE;KAAW,CAAA,EACzF,KACC,kBAAC,KAAD;KAAG,WAAU;eAAmD;KAAgB,CAAA,CAE9E;OACL,KACC,kBAAC,OAAD;IAAK,WAAU;cAAmD;IAAc,CAAA,CAE3E;MAKT,kBAAC,OAAD;GAAK,WAAU;GAA4B;GAAe,CAAA,CACtD"}
1
+ {"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/admin/components/PageLayout.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { cn } from '../utils/cn';\n\ninterface PageLayoutProps {\n /** Page title */\n title: string;\n /** Optional description */\n description?: string;\n /** Actions to display in the header (buttons, etc.) */\n actions?: ReactNode;\n /** Page content */\n children: ReactNode;\n /** Additional class names */\n className?: string;\n}\n\n/**\n * Standard page layout component with title, description, and content area\n */\nexport function PageLayout({ title, description, actions, children, className }: PageLayoutProps) {\n return (\n <div className={cn('flex h-full flex-col', className)}>\n {/* Page Header */}\n <header className=\"flex flex-col gap-4 border-b border-border-subtle bg-bg-surface px-6 py-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"text-2xl font-bold tracking-tight text-text-primary sm:text-3xl\">{title}</h1>\n {description && (\n <p className=\"mt-1 text-sm text-text-secondary sm:text-base\">{description}</p>\n )}\n </div>\n {actions && (\n <div className=\"flex flex-shrink-0 flex-wrap items-center gap-2\">{actions}</div>\n )}\n </header>\n\n {/* Page Content — must not use a <main> landmark here because the\n host shell (AppShellLayout) already renders one; nesting <main>\n elements is invalid HTML and an a11y violation. */}\n <div className=\"flex-1 overflow-auto p-6\">{children}</div>\n </div>\n );\n}\n\ninterface PageSectionProps {\n /** Section title */\n title?: string;\n /** Optional description */\n description?: string;\n /** Section content */\n children: ReactNode;\n /** Additional class names */\n className?: string;\n}\n\n/**\n * Section within a page\n */\nexport function PageSection({ title, description, children, className }: PageSectionProps) {\n return (\n <section className={cn('space-y-4', className)}>\n {(title || description) && (\n <div>\n {title && <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>}\n {description && <p className=\"text-sm text-text-secondary\">{description}</p>}\n </div>\n )}\n {children}\n </section>\n );\n}\n"],"mappings":";;;AAmBA,SAAgB,EAAW,EAAE,UAAO,gBAAa,YAAS,aAAU,gBAA8B;AAChG,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,wBAAwB,EAAU;YAArD,CAEE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eAAmE;KAAW,CAAA,EAC3F,KACC,kBAAC,KAAD;KAAG,WAAU;eAAiD;KAAgB,CAAA,CAE5E;OACL,KACC,kBAAC,OAAD;IAAK,WAAU;cAAmD;IAAc,CAAA,CAE3E;MAKT,kBAAC,OAAD;GAAK,WAAU;GAA4B;GAAe,CAAA,CACtD"}
@@ -5,7 +5,7 @@ import { Fragment as n, jsx as r } from "react/jsx-runtime";
5
5
  var i = ({ allowed: i, isLoading: a, pageTitle: o, denyMessage: s, children: c }) => a ? /* @__PURE__ */ r(e, {
6
6
  title: o,
7
7
  children: /* @__PURE__ */ r("div", {
8
- className: "text-center py-16 text-muted-foreground",
8
+ className: "text-center py-16 text-text-secondary",
9
9
  children: "Loading permissions..."
10
10
  })
11
11
  }) : i ? /* @__PURE__ */ r(n, { children: c }) : /* @__PURE__ */ r(e, {
@@ -1 +1 @@
1
- {"version":3,"file":"PermissionGuard.js","names":[],"sources":["../../../src/admin/components/PermissionGuard.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { PageLayout } from './PageLayout';\nimport { AccessDenied } from './AccessDenied';\n\ninterface PermissionGuardProps {\n allowed: boolean;\n isLoading: boolean;\n pageTitle: string;\n denyMessage?: string;\n children: ReactNode;\n}\n\nexport const PermissionGuard: FC<PermissionGuardProps> = ({\n allowed,\n isLoading,\n pageTitle,\n denyMessage,\n children,\n}) => {\n if (isLoading)\n return (\n <PageLayout title={pageTitle}>\n <div className=\"text-center py-16 text-muted-foreground\">Loading permissions...</div>\n </PageLayout>\n );\n if (!allowed)\n return (\n <PageLayout title={pageTitle}>\n <AccessDenied message={denyMessage} />\n </PageLayout>\n );\n return <>{children}</>;\n};\n"],"mappings":";;;;AAYA,IAAa,KAA6C,EACxD,YACA,cACA,cACA,gBACA,kBAEI,IAEA,kBAAC,GAAD;CAAY,OAAO;WACjB,kBAAC,OAAD;EAAK,WAAU;YAA0C;EAA4B,CAAA;CAC1E,CAAA,GAEZ,IAME,kBAAA,GAAA,EAAG,aAAY,CAAA,GAJlB,kBAAC,GAAD;CAAY,OAAO;WACjB,kBAAC,GAAD,EAAc,SAAS,GAAe,CAAA;CAC3B,CAAA"}
1
+ {"version":3,"file":"PermissionGuard.js","names":[],"sources":["../../../src/admin/components/PermissionGuard.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { PageLayout } from './PageLayout';\nimport { AccessDenied } from './AccessDenied';\n\ninterface PermissionGuardProps {\n allowed: boolean;\n isLoading: boolean;\n pageTitle: string;\n denyMessage?: string;\n children: ReactNode;\n}\n\nexport const PermissionGuard: FC<PermissionGuardProps> = ({\n allowed,\n isLoading,\n pageTitle,\n denyMessage,\n children,\n}) => {\n if (isLoading)\n return (\n <PageLayout title={pageTitle}>\n <div className=\"text-center py-16 text-text-secondary\">Loading permissions...</div>\n </PageLayout>\n );\n if (!allowed)\n return (\n <PageLayout title={pageTitle}>\n <AccessDenied message={denyMessage} />\n </PageLayout>\n );\n return <>{children}</>;\n};\n"],"mappings":";;;;AAYA,IAAa,KAA6C,EACxD,YACA,cACA,cACA,gBACA,kBAEI,IAEA,kBAAC,GAAD;CAAY,OAAO;WACjB,kBAAC,OAAD;EAAK,WAAU;YAAwC;EAA4B,CAAA;CACxE,CAAA,GAEZ,IAME,kBAAA,GAAA,EAAG,aAAY,CAAA,GAJlB,kBAAC,GAAD;CAAY,OAAO;WACjB,kBAAC,GAAD,EAAc,SAAS,GAAe,CAAA;CAC3B,CAAA"}