@rebasepro/app 0.0.1-canary.4829d6e

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 (268) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/dist/auth/api.d.ts +66 -0
  4. package/dist/auth/index.d.ts +13 -0
  5. package/dist/auth/types.d.ts +127 -0
  6. package/dist/auth/useRebaseAuthController.d.ts +13 -0
  7. package/dist/components/AIIcon.d.ts +17 -0
  8. package/dist/components/AuthSimulationMenu.d.ts +0 -0
  9. package/dist/components/ConfirmationDialog.d.ts +9 -0
  10. package/dist/components/Debug/UIReferenceView.d.ts +13 -0
  11. package/dist/components/Debug/UIStyleGuide.d.ts +2 -0
  12. package/dist/components/Debug/collection-views/CardViewDemo.d.ts +2 -0
  13. package/dist/components/Debug/collection-views/CollectionTableDemo.d.ts +2 -0
  14. package/dist/components/Debug/collection-views/KanbanBoardDemo.d.ts +2 -0
  15. package/dist/components/Debug/collection-views/index.d.ts +3 -0
  16. package/dist/components/Debug/collection-views/sample_data.d.ts +23 -0
  17. package/dist/components/Debug/crm-dashboard/CalendarWidget.d.ts +22 -0
  18. package/dist/components/Debug/crm-dashboard/CrmDashboardDemo.d.ts +2 -0
  19. package/dist/components/Debug/crm-dashboard/DashboardMetrics.d.ts +15 -0
  20. package/dist/components/Debug/crm-dashboard/PipelineOverview.d.ts +34 -0
  21. package/dist/components/Debug/crm-dashboard/RecentActivity.d.ts +19 -0
  22. package/dist/components/Debug/crm-dashboard/TaskFilters.d.ts +30 -0
  23. package/dist/components/Debug/crm-dashboard/TaskTable.d.ts +50 -0
  24. package/dist/components/Debug/crm-dashboard/TasksView.d.ts +41 -0
  25. package/dist/components/ErrorTooltip.d.ts +3 -0
  26. package/dist/components/ErrorView.d.ts +21 -0
  27. package/dist/components/LanguageToggle.d.ts +2 -0
  28. package/dist/components/LoginView/LoginView.d.ts +113 -0
  29. package/dist/components/LoginView/index.d.ts +2 -0
  30. package/dist/components/NotFoundPage.d.ts +2 -0
  31. package/dist/components/RebaseAuth.d.ts +10 -0
  32. package/dist/components/RebaseLogo.d.ts +7 -0
  33. package/dist/components/SchemaDriftBanner.d.ts +27 -0
  34. package/dist/components/UnsavedChangesDialog.d.ts +9 -0
  35. package/dist/components/UserDisplay.d.ts +7 -0
  36. package/dist/components/UserSelectPopover.d.ts +63 -0
  37. package/dist/components/UserSettingsView.d.ts +2 -0
  38. package/dist/components/common/index.d.ts +6 -0
  39. package/dist/components/common/table_height.d.ts +5 -0
  40. package/dist/components/common/types.d.ts +66 -0
  41. package/dist/components/common/useColumnsIds.d.ts +9 -0
  42. package/dist/components/common/useDataTableController.d.ts +45 -0
  43. package/dist/components/common/useDebouncedData.d.ts +9 -0
  44. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  45. package/dist/components/index.d.ts +18 -0
  46. package/dist/contexts/AdminModeController.d.ts +4 -0
  47. package/dist/contexts/AnalyticsContext.d.ts +3 -0
  48. package/dist/contexts/AuthControllerContext.d.ts +3 -0
  49. package/dist/contexts/CollectionScopeContext.d.ts +50 -0
  50. package/dist/contexts/ComponentOverrideContext.d.ts +42 -0
  51. package/dist/contexts/CustomizationControllerContext.d.ts +3 -0
  52. package/dist/contexts/DataDriverContext.d.ts +3 -0
  53. package/dist/contexts/DataSourcesContext.d.ts +31 -0
  54. package/dist/contexts/DatabaseAdminContext.d.ts +3 -0
  55. package/dist/contexts/DialogsProvider.d.ts +4 -0
  56. package/dist/contexts/EffectiveRoleController.d.ts +4 -0
  57. package/dist/contexts/ModeController.d.ts +4 -0
  58. package/dist/contexts/RebaseClientInstanceContext.d.ts +6 -0
  59. package/dist/contexts/RebaseDataContext.d.ts +3 -0
  60. package/dist/contexts/SnackbarProvider.d.ts +2 -0
  61. package/dist/contexts/StorageSourceContext.d.ts +3 -0
  62. package/dist/contexts/StorageSourcesContext.d.ts +25 -0
  63. package/dist/contexts/UserConfigurationPersistenceContext.d.ts +3 -0
  64. package/dist/contexts/index.d.ts +17 -0
  65. package/dist/core/PluginLifecycleManager.d.ts +17 -0
  66. package/dist/core/PluginProviderStack.d.ts +21 -0
  67. package/dist/core/Rebase.d.ts +15 -0
  68. package/dist/core/RebaseProps.d.ts +291 -0
  69. package/dist/core/RebaseRouter.d.ts +4 -0
  70. package/dist/core/RebaseRoutes.d.ts +17 -0
  71. package/dist/core/index.d.ts +4 -0
  72. package/dist/hooks/ApiConfigContext.d.ts +24 -0
  73. package/dist/hooks/data/delete.d.ts +33 -0
  74. package/dist/hooks/data/save.d.ts +40 -0
  75. package/dist/hooks/data/useCollection.d.ts +62 -0
  76. package/dist/hooks/data/useData.d.ts +13 -0
  77. package/dist/hooks/data/useDataOrder.d.ts +12 -0
  78. package/dist/hooks/data/useFetch.d.ts +43 -0
  79. package/dist/hooks/data/useRelationSelector.d.ts +52 -0
  80. package/dist/hooks/index.d.ts +36 -0
  81. package/dist/hooks/useAdminModeController.d.ts +19 -0
  82. package/dist/hooks/useAnalyticsController.d.ts +5 -0
  83. package/dist/hooks/useAuthController.d.ts +11 -0
  84. package/dist/hooks/useAuthSubscription.d.ts +2 -0
  85. package/dist/hooks/useBackendStorageSource.d.ts +30 -0
  86. package/dist/hooks/useBridgeRegistration.d.ts +18 -0
  87. package/dist/hooks/useBrowserTitleAndIcon.d.ts +6 -0
  88. package/dist/hooks/useBuildAdminModeController.d.ts +6 -0
  89. package/dist/hooks/useBuildEffectiveRoleController.d.ts +8 -0
  90. package/dist/hooks/useBuildLocalConfigurationPersistence.d.ts +2 -0
  91. package/dist/hooks/useBuildModeController.d.ts +6 -0
  92. package/dist/hooks/useClipboard.d.ts +57 -0
  93. package/dist/hooks/useCollapsedGroups.d.ts +27 -0
  94. package/dist/hooks/useComponentOverride.d.ts +32 -0
  95. package/dist/hooks/useCustomizationController.d.ts +11 -0
  96. package/dist/hooks/useDialogsController.d.ts +11 -0
  97. package/dist/hooks/useEffectiveRoleController.d.ts +7 -0
  98. package/dist/hooks/useLargeLayout.d.ts +1 -0
  99. package/dist/hooks/useModeController.d.ts +19 -0
  100. package/dist/hooks/usePermissions.d.ts +12 -0
  101. package/dist/hooks/useRebaseClient.d.ts +5 -0
  102. package/dist/hooks/useRebaseContext.d.ts +11 -0
  103. package/dist/hooks/useRebaseRegistry.d.ts +34 -0
  104. package/dist/hooks/useResolvedComponent.d.ts +47 -0
  105. package/dist/hooks/useSlot.d.ts +18 -0
  106. package/dist/hooks/useSnackbarController.d.ts +20 -0
  107. package/dist/hooks/useStorageSource.d.ts +6 -0
  108. package/dist/hooks/useStudioBridge.d.ts +79 -0
  109. package/dist/hooks/useTranslation.d.ts +17 -0
  110. package/dist/hooks/useUnsavedChangesDialog.d.ts +12 -0
  111. package/dist/hooks/useUserConfigurationPersistence.d.ts +8 -0
  112. package/dist/i18n/RebaseI18nProvider.d.ts +33 -0
  113. package/dist/index.d.ts +16 -0
  114. package/dist/index.es.js +16843 -0
  115. package/dist/index.es.js.map +1 -0
  116. package/dist/index.umd.js +17021 -0
  117. package/dist/index.umd.js.map +1 -0
  118. package/dist/internal/common.d.ts +14 -0
  119. package/dist/internal/useRestoreScroll.d.ts +15 -0
  120. package/dist/locales/de.d.ts +2 -0
  121. package/dist/locales/en.d.ts +10 -0
  122. package/dist/locales/es.d.ts +10 -0
  123. package/dist/locales/fr.d.ts +2 -0
  124. package/dist/locales/hi.d.ts +2 -0
  125. package/dist/locales/it.d.ts +2 -0
  126. package/dist/locales/pt.d.ts +7 -0
  127. package/dist/util/constants.d.ts +1 -0
  128. package/dist/util/createFormexStub.d.ts +2 -0
  129. package/dist/util/entity_cache.d.ts +22 -0
  130. package/dist/util/enums.d.ts +5 -0
  131. package/dist/util/icon_list.d.ts +5 -0
  132. package/dist/util/icons.d.ts +20 -0
  133. package/dist/util/index.d.ts +8 -0
  134. package/dist/util/previews.d.ts +4 -0
  135. package/dist/util/useStorageUploadController.d.ts +44 -0
  136. package/dist/vitePlugin.d.ts +60 -0
  137. package/dist/vitePlugin.js +113 -0
  138. package/package.json +145 -0
  139. package/src/auth/api.ts +148 -0
  140. package/src/auth/index.ts +25 -0
  141. package/src/auth/types.ts +115 -0
  142. package/src/auth/useRebaseAuthController.ts +328 -0
  143. package/src/components/AIIcon.tsx +47 -0
  144. package/src/components/AuthSimulationMenu.tsx +0 -0
  145. package/src/components/ConfirmationDialog.tsx +48 -0
  146. package/src/components/Debug/UIReferenceView.tsx +958 -0
  147. package/src/components/Debug/UIStyleGuide.tsx +156 -0
  148. package/src/components/Debug/collection-views/CardViewDemo.tsx +85 -0
  149. package/src/components/Debug/collection-views/CollectionTableDemo.tsx +107 -0
  150. package/src/components/Debug/collection-views/KanbanBoardDemo.tsx +90 -0
  151. package/src/components/Debug/collection-views/index.ts +3 -0
  152. package/src/components/Debug/collection-views/sample_data.ts +42 -0
  153. package/src/components/Debug/crm-dashboard/CalendarWidget.tsx +389 -0
  154. package/src/components/Debug/crm-dashboard/CrmDashboardDemo.tsx +1102 -0
  155. package/src/components/Debug/crm-dashboard/DashboardMetrics.tsx +133 -0
  156. package/src/components/Debug/crm-dashboard/PipelineOverview.tsx +290 -0
  157. package/src/components/Debug/crm-dashboard/RecentActivity.tsx +215 -0
  158. package/src/components/Debug/crm-dashboard/TaskFilters.tsx +184 -0
  159. package/src/components/Debug/crm-dashboard/TaskTable.tsx +427 -0
  160. package/src/components/Debug/crm-dashboard/TasksView.tsx +234 -0
  161. package/src/components/ErrorTooltip.tsx +12 -0
  162. package/src/components/ErrorView.tsx +94 -0
  163. package/src/components/LanguageToggle.tsx +59 -0
  164. package/src/components/LoginView/LoginView.tsx +932 -0
  165. package/src/components/LoginView/index.ts +2 -0
  166. package/src/components/NotFoundPage.tsx +27 -0
  167. package/src/components/RebaseAuth.tsx +27 -0
  168. package/src/components/RebaseLogo.tsx +29 -0
  169. package/src/components/SchemaDriftBanner.tsx +102 -0
  170. package/src/components/UnsavedChangesDialog.tsx +46 -0
  171. package/src/components/UserDisplay.tsx +54 -0
  172. package/src/components/UserSelectPopover.tsx +399 -0
  173. package/src/components/UserSettingsView.tsx +318 -0
  174. package/src/components/common/index.ts +6 -0
  175. package/src/components/common/table_height.tsx +21 -0
  176. package/src/components/common/types.tsx +66 -0
  177. package/src/components/common/useColumnsIds.tsx +222 -0
  178. package/src/components/common/useDataTableController.tsx +514 -0
  179. package/src/components/common/useDebouncedData.ts +49 -0
  180. package/src/components/common/useScrollRestoration.tsx +68 -0
  181. package/src/components/index.tsx +26 -0
  182. package/src/contexts/AdminModeController.tsx +11 -0
  183. package/src/contexts/AnalyticsContext.tsx +4 -0
  184. package/src/contexts/AuthControllerContext.tsx +4 -0
  185. package/src/contexts/CollectionScopeContext.tsx +67 -0
  186. package/src/contexts/ComponentOverrideContext.tsx +81 -0
  187. package/src/contexts/CustomizationControllerContext.tsx +4 -0
  188. package/src/contexts/DataDriverContext.tsx +4 -0
  189. package/src/contexts/DataSourcesContext.tsx +36 -0
  190. package/src/contexts/DatabaseAdminContext.tsx +4 -0
  191. package/src/contexts/DialogsProvider.tsx +56 -0
  192. package/src/contexts/EffectiveRoleController.tsx +12 -0
  193. package/src/contexts/ModeController.tsx +11 -0
  194. package/src/contexts/RebaseClientInstanceContext.tsx +7 -0
  195. package/src/contexts/RebaseDataContext.tsx +4 -0
  196. package/src/contexts/SnackbarProvider.tsx +15 -0
  197. package/src/contexts/StorageSourceContext.tsx +4 -0
  198. package/src/contexts/StorageSourcesContext.tsx +30 -0
  199. package/src/contexts/UserConfigurationPersistenceContext.tsx +4 -0
  200. package/src/contexts/index.ts +17 -0
  201. package/src/core/PluginLifecycleManager.tsx +95 -0
  202. package/src/core/PluginProviderStack.tsx +40 -0
  203. package/src/core/Rebase.tsx +439 -0
  204. package/src/core/RebaseProps.tsx +320 -0
  205. package/src/core/RebaseRouter.tsx +17 -0
  206. package/src/core/RebaseRoutes.tsx +24 -0
  207. package/src/core/index.tsx +5 -0
  208. package/src/hooks/ApiConfigContext.tsx +41 -0
  209. package/src/hooks/data/delete.ts +55 -0
  210. package/src/hooks/data/save.ts +77 -0
  211. package/src/hooks/data/useCollection.tsx +176 -0
  212. package/src/hooks/data/useData.tsx +18 -0
  213. package/src/hooks/data/useDataOrder.ts +26 -0
  214. package/src/hooks/data/useFetch.tsx +140 -0
  215. package/src/hooks/data/useRelationSelector.tsx +234 -0
  216. package/src/hooks/index.tsx +45 -0
  217. package/src/hooks/useAdminModeController.tsx +23 -0
  218. package/src/hooks/useAnalyticsController.tsx +8 -0
  219. package/src/hooks/useAuthController.tsx +14 -0
  220. package/src/hooks/useAuthSubscription.ts +87 -0
  221. package/src/hooks/useBackendStorageSource.ts +288 -0
  222. package/src/hooks/useBridgeRegistration.tsx +32 -0
  223. package/src/hooks/useBrowserTitleAndIcon.tsx +23 -0
  224. package/src/hooks/useBuildAdminModeController.tsx +24 -0
  225. package/src/hooks/useBuildEffectiveRoleController.tsx +31 -0
  226. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +75 -0
  227. package/src/hooks/useBuildModeController.tsx +71 -0
  228. package/src/hooks/useClipboard.tsx +158 -0
  229. package/src/hooks/useCollapsedGroups.ts +116 -0
  230. package/src/hooks/useComponentOverride.tsx +59 -0
  231. package/src/hooks/useCustomizationController.tsx +14 -0
  232. package/src/hooks/useDialogsController.tsx +15 -0
  233. package/src/hooks/useEffectiveRoleController.tsx +10 -0
  234. package/src/hooks/useLargeLayout.tsx +65 -0
  235. package/src/hooks/useModeController.tsx +23 -0
  236. package/src/hooks/usePermissions.ts +44 -0
  237. package/src/hooks/useRebaseClient.tsx +10 -0
  238. package/src/hooks/useRebaseContext.tsx +83 -0
  239. package/src/hooks/useRebaseRegistry.tsx +91 -0
  240. package/src/hooks/useResolvedComponent.tsx +159 -0
  241. package/src/hooks/useSlot.tsx +64 -0
  242. package/src/hooks/useSnackbarController.tsx +52 -0
  243. package/src/hooks/useStorageSource.tsx +11 -0
  244. package/src/hooks/useStudioBridge.tsx +209 -0
  245. package/src/hooks/useTranslation.ts +33 -0
  246. package/src/hooks/useUnsavedChangesDialog.tsx +64 -0
  247. package/src/hooks/useUserConfigurationPersistence.tsx +11 -0
  248. package/src/i18n/RebaseI18nProvider.tsx +160 -0
  249. package/src/index.ts +25 -0
  250. package/src/internal/common.tsx +16 -0
  251. package/src/internal/useRestoreScroll.tsx +69 -0
  252. package/src/locales/de.ts +902 -0
  253. package/src/locales/en.ts +980 -0
  254. package/src/locales/es.ts +922 -0
  255. package/src/locales/fr.ts +901 -0
  256. package/src/locales/hi.ts +901 -0
  257. package/src/locales/it.ts +901 -0
  258. package/src/locales/pt.ts +909 -0
  259. package/src/util/constants.ts +7 -0
  260. package/src/util/createFormexStub.tsx +66 -0
  261. package/src/util/entity_cache.ts +239 -0
  262. package/src/util/enums.ts +32 -0
  263. package/src/util/icon_list.ts +22 -0
  264. package/src/util/icons.tsx +149 -0
  265. package/src/util/index.ts +12 -0
  266. package/src/util/previews.ts +105 -0
  267. package/src/util/useStorageUploadController.tsx +350 -0
  268. package/src/vitePlugin.ts +196 -0
@@ -0,0 +1,133 @@
1
+ import React from "react";
2
+ import {
3
+ Typography,
4
+ cls,
5
+ Card,
6
+ Skeleton
7
+ } from "@rebasepro/ui";
8
+ import {
9
+ Users as UsersIcon,
10
+ DollarSign as DollarSignIcon,
11
+ AlertTriangle,
12
+ TrendingUp
13
+ } from "lucide-react";
14
+
15
+ /* ── Types ────────────────────────────────────────────── */
16
+
17
+ interface InsightsData {
18
+ activeClients: number;
19
+ potentialRevenue: number;
20
+ contractedRevenue: number;
21
+ overdueTaskCount: number;
22
+ }
23
+
24
+ export interface DashboardMetricsProps {
25
+ loading: boolean;
26
+ insights: InsightsData | null;
27
+ totalInPipeline: number;
28
+ totalRevenue: number;
29
+ }
30
+
31
+ /* ── Component ────────────────────────────────────────── */
32
+
33
+ export function DashboardMetrics({
34
+ loading,
35
+ insights,
36
+ totalInPipeline,
37
+ totalRevenue
38
+ }: DashboardMetricsProps) {
39
+ return (
40
+ <div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
41
+ {/* In Pipeline */}
42
+ <Card className="p-4">
43
+ <div className="flex items-center gap-3">
44
+ <div className="flex items-center justify-center w-5 h-5 text-surface-400 dark:text-surface-500">
45
+ <UsersIcon className="h-4 w-4" />
46
+ </div>
47
+ <div className="flex-1 min-w-0">
48
+ <Typography variant="caption" color="secondary" className="uppercase tracking-[0.05em] text-[10px]">In Pipeline</Typography>
49
+ {loading ? (
50
+ <Skeleton className="h-7 w-12 mt-0.5" />
51
+ ) : (
52
+ <Typography variant="h5" className="tabular-nums mt-0.5">
53
+ {totalInPipeline}
54
+ </Typography>
55
+ )}
56
+ </div>
57
+ </div>
58
+ </Card>
59
+
60
+ {/* Active Clients */}
61
+ <Card className="p-4">
62
+ <div className="flex items-center gap-3">
63
+ <div className="flex items-center justify-center w-5 h-5 text-surface-400 dark:text-surface-500">
64
+ <TrendingUp className="h-4 w-4" />
65
+ </div>
66
+ <div className="flex-1 min-w-0">
67
+ <Typography variant="caption" color="secondary" className="uppercase tracking-[0.05em] text-[10px]">Active Clients</Typography>
68
+ {loading ? (
69
+ <Skeleton className="h-7 w-12 mt-0.5" />
70
+ ) : (
71
+ <Typography variant="h5" className="tabular-nums mt-0.5">
72
+ {insights?.activeClients ?? 0}
73
+ </Typography>
74
+ )}
75
+ </div>
76
+ </div>
77
+ </Card>
78
+
79
+ {/* Total Revenue */}
80
+ <Card className="p-4">
81
+ <div className="flex items-center gap-3">
82
+ <div className="flex items-center justify-center w-5 h-5 text-surface-400 dark:text-surface-500">
83
+ <DollarSignIcon className="h-4 w-4" />
84
+ </div>
85
+ <div className="flex-1 min-w-0">
86
+ <Typography variant="caption" color="secondary" className="uppercase tracking-[0.05em] text-[10px]">Revenue</Typography>
87
+ {loading ? (
88
+ <Skeleton className="h-7 w-20 mt-0.5" />
89
+ ) : (
90
+ <div className="flex flex-col sm:flex-row sm:items-baseline gap-1 sm:gap-2 mt-0.5">
91
+ <Typography variant="h5" className="tabular-nums">
92
+ ${totalRevenue.toLocaleString()}
93
+ </Typography>
94
+ {insights && insights.contractedRevenue > 0 && (
95
+ <Typography variant="caption" color="secondary" className="text-[10px]">
96
+ (${insights.contractedRevenue.toLocaleString()} confirmed)
97
+ </Typography>
98
+ )}
99
+ </div>
100
+ )}
101
+ </div>
102
+ </div>
103
+ </Card>
104
+
105
+ {/* Overdue Tasks */}
106
+ <Card className="p-4">
107
+ <div className="flex items-center gap-3">
108
+ <div className={cls(
109
+ "flex items-center justify-center w-5 h-5",
110
+ insights && insights.overdueTaskCount > 0
111
+ ? "text-red-500"
112
+ : "text-surface-400 dark:text-surface-500"
113
+ )}>
114
+ <AlertTriangle className="h-4 w-4" />
115
+ </div>
116
+ <div className="flex-1 min-w-0">
117
+ <Typography variant="caption" color="secondary" className="uppercase tracking-[0.05em] text-[10px]">Overdue Tasks</Typography>
118
+ {loading ? (
119
+ <Skeleton className="h-7 w-8 mt-0.5" />
120
+ ) : (
121
+ <Typography variant="h5" className={cls(
122
+ "tabular-nums mt-0.5",
123
+ insights && insights.overdueTaskCount > 0 && "text-red-600 dark:text-red-400"
124
+ )}>
125
+ {insights?.overdueTaskCount ?? 0}
126
+ </Typography>
127
+ )}
128
+ </div>
129
+ </div>
130
+ </Card>
131
+ </div>
132
+ );
133
+ }
@@ -0,0 +1,290 @@
1
+ import React from "react";
2
+ import {
3
+ Typography,
4
+ cls,
5
+ Card,
6
+ Button,
7
+ Skeleton,
8
+ Tooltip,
9
+ Select,
10
+ SelectItem
11
+ } from "@rebasepro/ui";
12
+ import {
13
+ Users as UsersIcon,
14
+ ChevronRight,
15
+ Settings
16
+ } from "lucide-react";
17
+
18
+ /* ── Types ────────────────────────────────────────────── */
19
+
20
+ interface PipelineStage {
21
+ stageKey: string;
22
+ label: string;
23
+ shortLabel: string;
24
+ color: string;
25
+ icon: string;
26
+ sortOrder: number;
27
+ isTerminal: boolean;
28
+ isLostStage: boolean;
29
+ count: number;
30
+ }
31
+
32
+ interface PipelineOption {
33
+ id: string;
34
+ name: string;
35
+ isDefault: boolean;
36
+ color: string;
37
+ }
38
+
39
+ export interface PipelineOverviewProps {
40
+ loading: boolean;
41
+ insights: {
42
+ pipelines: PipelineOption[];
43
+ } | null;
44
+ selectedPipelineId: string | null;
45
+ onPipelineChange: (newPipelineId: string) => void;
46
+ onNavigate: (path: string) => void;
47
+ sortedStages: PipelineStage[];
48
+ activeStages: PipelineStage[];
49
+ closedStages: PipelineStage[];
50
+ totalInPipeline: number;
51
+ resolveColor: (colorName: string) => string;
52
+ }
53
+
54
+ /* ── Component ────────────────────────────────────────── */
55
+
56
+ export function PipelineOverview({
57
+ loading,
58
+ insights,
59
+ selectedPipelineId,
60
+ onPipelineChange,
61
+ onNavigate,
62
+ sortedStages,
63
+ activeStages,
64
+ closedStages,
65
+ totalInPipeline,
66
+ resolveColor
67
+ }: PipelineOverviewProps) {
68
+ const useShortLabels = sortedStages.length > 6;
69
+ const needsScroll = sortedStages.length > 10;
70
+
71
+ return (
72
+ <Card className="p-4 mb-6">
73
+ <div className="flex items-center justify-between mb-4">
74
+ <div className="flex items-center gap-3">
75
+ <Typography variant="subtitle1">
76
+ Pipeline{!loading && totalInPipeline > 0 ? ` (${totalInPipeline} clients)` : ""}
77
+ </Typography>
78
+
79
+ {/* Pipeline selector — using @rebasepro/ui Select */}
80
+ {insights && insights.pipelines.length > 1 && (
81
+ <Select
82
+ value={selectedPipelineId ?? ""}
83
+ onValueChange={(v) => onPipelineChange(v as string)}
84
+ size="smallest"
85
+ renderValue={(v) => {
86
+ const selected = insights.pipelines.find(p => p.id === v);
87
+ return selected ? selected.name : "Pipeline";
88
+ }}
89
+ position="popper"
90
+ >
91
+ {insights.pipelines.map(p => (
92
+ <SelectItem key={p.id} value={p.id}>
93
+ {p.name}
94
+ </SelectItem>
95
+ ))}
96
+ </Select>
97
+ )}
98
+ </div>
99
+ <Button
100
+ variant="text"
101
+ size="small"
102
+ onClick={() => { onNavigate('/c/engagements'); }}
103
+ endIcon={<ChevronRight className="h-4 w-4" />}
104
+ >
105
+ View All
106
+ </Button>
107
+ </div>
108
+
109
+ {loading ? (
110
+ <Skeleton className="h-20 w-full rounded-lg" />
111
+ ) : insights && sortedStages.length === 0 ? (
112
+ /* No stages configured */
113
+ <div className="flex flex-col items-center justify-center py-8">
114
+ <div className="flex items-center justify-center w-5 h-5 text-surface-400 dark:text-surface-500 mb-3">
115
+ <Settings className="h-4 w-4" />
116
+ </div>
117
+ <Typography variant="body2" color="secondary" className="mb-1">
118
+ No pipeline stages configured
119
+ </Typography>
120
+ <Typography variant="caption" color="disabled" className="mb-4 text-center">
121
+ Set up your pipeline stages in Settings → Pipeline Stages
122
+ </Typography>
123
+ <Button
124
+ variant="outlined"
125
+ size="small"
126
+ onClick={() => onNavigate('/pipeline-builder')}
127
+ >
128
+ Configure Stages
129
+ </Button>
130
+ </div>
131
+ ) : insights && totalInPipeline === 0 ? (
132
+ /* No clients in pipeline */
133
+ <div className="flex flex-col items-center justify-center py-8">
134
+ <div className="flex items-center justify-center w-5 h-5 text-surface-400 dark:text-surface-500 mb-3">
135
+ <UsersIcon className="h-4 w-4" />
136
+ </div>
137
+ <Typography variant="body2" color="secondary" className="mb-1">
138
+ No clients in pipeline yet
139
+ </Typography>
140
+ <Typography variant="caption" color="disabled" className="mb-4">
141
+ Create your first client to get started.
142
+ </Typography>
143
+ </div>
144
+ ) : insights ? (
145
+ /* Pipeline bar */
146
+ <div className="relative">
147
+ {needsScroll && (
148
+ <>
149
+ <div className="absolute left-0 top-0 bottom-0 w-6 bg-gradient-to-r from-white dark:from-surface-900 to-transparent z-10 pointer-events-none rounded-l-lg" />
150
+ <div className="absolute right-0 top-0 bottom-0 w-6 bg-gradient-to-l from-white dark:from-surface-900 to-transparent z-10 pointer-events-none rounded-r-lg" />
151
+ </>
152
+ )}
153
+ <div className={cls(
154
+ "flex items-stretch gap-0",
155
+ needsScroll && "overflow-x-auto"
156
+ )}>
157
+ {/* Active stages */}
158
+ {activeStages.map((stage, idx) => {
159
+ const isEmpty = stage.count === 0;
160
+ const widthPercent = totalInPipeline > 0
161
+ ? Math.max(8, (stage.count / totalInPipeline) * 100)
162
+ : 100 / Math.max(activeStages.length, 1);
163
+ const stageColor = resolveColor(stage.color);
164
+ const displayLabel = useShortLabels ? stage.shortLabel : stage.label;
165
+
166
+ return (
167
+ <React.Fragment key={stage.stageKey}>
168
+ {idx > 0 && (
169
+ <div className="flex items-center shrink-0 text-surface-300 dark:text-surface-600 -mx-0.5">
170
+ <ChevronRight className="h-4 w-4" />
171
+ </div>
172
+ )}
173
+ <Tooltip title={`${stage.label} — ${stage.count} client${stage.count !== 1 ? "s" : ""}`}>
174
+ <div
175
+ className={cls(
176
+ "flex flex-col items-center justify-center rounded-lg px-3 py-3 min-w-[72px] transition-colors duration-150 cursor-pointer",
177
+ "hover:bg-surface-accent-100 dark:hover:bg-surface-800",
178
+ isEmpty
179
+ ? "bg-surface-50 dark:bg-surface-900/20"
180
+ : "bg-surface-100/50 dark:bg-surface-900/60"
181
+ )}
182
+ style={{ flex: `${widthPercent} 1 0%` }}
183
+ onClick={() => onNavigate(`/c/engagements?stage=${stage.stageKey}`)}
184
+ >
185
+ <Typography
186
+ variant="h6"
187
+ className={cls(
188
+ "tabular-nums leading-none font-semibold",
189
+ isEmpty && "opacity-25"
190
+ )}
191
+ >
192
+ {stage.count}
193
+ </Typography>
194
+ <div className="flex items-center gap-1.5 mt-1.5">
195
+ <span
196
+ className="w-1.5 h-1.5 rounded-full shrink-0"
197
+ style={{
198
+ backgroundColor: stageColor,
199
+ opacity: isEmpty ? 0.25 : 1
200
+ }}
201
+ />
202
+ <Typography
203
+ variant="caption"
204
+ color="secondary"
205
+ className={cls(
206
+ "whitespace-nowrap text-[11px]",
207
+ isEmpty && "opacity-35"
208
+ )}
209
+ >
210
+ {displayLabel}
211
+ </Typography>
212
+ </div>
213
+ </div>
214
+ </Tooltip>
215
+ </React.Fragment>
216
+ );
217
+ })}
218
+
219
+ {/* Separator between active and closed stages */}
220
+ {activeStages.length > 0 && closedStages.length > 0 && (
221
+ <div className="flex items-center shrink-0 mx-2">
222
+ <div className="w-px h-8 bg-surface-200 dark:bg-surface-700" />
223
+ </div>
224
+ )}
225
+
226
+ {/* Closed / terminal stages — muted styling */}
227
+ {closedStages.map((stage, idx) => {
228
+ const isEmpty = stage.count === 0;
229
+ const stageColor = resolveColor(stage.color);
230
+ const displayLabel = useShortLabels ? stage.shortLabel : stage.label;
231
+
232
+ return (
233
+ <React.Fragment key={stage.stageKey}>
234
+ {idx > 0 && (
235
+ <div className="flex items-center shrink-0 text-surface-300 dark:text-surface-600 -mx-0.5">
236
+ <ChevronRight className="h-4 w-4" />
237
+ </div>
238
+ )}
239
+ <Tooltip title={`${stage.label} — ${stage.count} client${stage.count !== 1 ? "s" : ""}`}>
240
+ <div
241
+ className={cls(
242
+ "flex flex-col items-center justify-center rounded-lg px-3 py-3 min-w-[72px] transition-colors duration-150 cursor-pointer",
243
+ "hover:bg-surface-accent-100 dark:hover:bg-surface-800",
244
+ "opacity-60",
245
+ isEmpty
246
+ ? "bg-surface-50 dark:bg-surface-900/10"
247
+ : "bg-surface-100/30 dark:bg-surface-900/30"
248
+ )}
249
+ style={{ flex: "0 0 auto" }}
250
+ onClick={() => onNavigate(`/c/engagements?stage=${stage.stageKey}`)}
251
+ >
252
+ <Typography
253
+ variant="h6"
254
+ className={cls(
255
+ "tabular-nums leading-none font-semibold",
256
+ isEmpty && "opacity-25"
257
+ )}
258
+ >
259
+ {stage.count}
260
+ </Typography>
261
+ <div className="flex items-center gap-1.5 mt-1.5">
262
+ <span
263
+ className="w-1.5 h-1.5 rounded-full shrink-0"
264
+ style={{
265
+ backgroundColor: stageColor,
266
+ opacity: isEmpty ? 0.25 : 1
267
+ }}
268
+ />
269
+ <Typography
270
+ variant="caption"
271
+ color="secondary"
272
+ className={cls(
273
+ "whitespace-nowrap text-[11px]",
274
+ isEmpty && "opacity-35"
275
+ )}
276
+ >
277
+ {displayLabel}
278
+ </Typography>
279
+ </div>
280
+ </div>
281
+ </Tooltip>
282
+ </React.Fragment>
283
+ );
284
+ })}
285
+ </div>
286
+ </div>
287
+ ) : null}
288
+ </Card>
289
+ );
290
+ }
@@ -0,0 +1,215 @@
1
+ import React from "react";
2
+ import {
3
+ Typography,
4
+ Card,
5
+ Skeleton,
6
+ Separator,
7
+ Dialog,
8
+ DialogTitle,
9
+ DialogContent,
10
+ DialogActions,
11
+ Button
12
+ } from "@rebasepro/ui";
13
+ import {
14
+ CircleDot,
15
+ ArrowRightLeft,
16
+ Mail,
17
+ FileText,
18
+ PenLine,
19
+ Video,
20
+ MessageCircle,
21
+ StickyNote,
22
+ ListPlus,
23
+ CheckCircle2,
24
+ Wrench,
25
+ Pin
26
+ } from "lucide-react";
27
+
28
+ /* ── Activity type → icon mapping ─────────────────── */
29
+ const ACTIVITY_ICONS: Record<string, React.ReactNode> = {
30
+ incoming: <CircleDot className="h-4 w-4 text-blue-500" />,
31
+ status_changed: <ArrowRightLeft className="h-4 w-4 text-violet-500" />,
32
+ email_sent: <Mail className="h-4 w-4 text-sky-500" />,
33
+ agreements_sent: <FileText className="h-4 w-4 text-amber-500" />,
34
+ agreements_signed: <PenLine className="h-4 w-4 text-emerald-500" />,
35
+ zoom_setup: <Video className="h-4 w-4 text-blue-600" />,
36
+ whatsapp_setup: <MessageCircle className="h-4 w-4 text-green-500" />,
37
+ note: <StickyNote className="h-4 w-4 text-yellow-500" />,
38
+ task_created: <ListPlus className="h-4 w-4 text-teal-500" />,
39
+ task_completed: <CheckCircle2 className="h-4 w-4 text-emerald-500" />,
40
+ manual_action: <Wrench className="h-4 w-4 text-orange-500" />
41
+ };
42
+
43
+ /* ── Relative time helper ──────────────────────────── */
44
+ function relativeTime(dateStr: string): string {
45
+ const now = Date.now();
46
+ const then = new Date(dateStr).getTime();
47
+ const diffSec = Math.round((now - then) / 1000);
48
+ if (diffSec < 60) return 'just now';
49
+ const diffMin = Math.floor(diffSec / 60);
50
+ if (diffMin < 60) return `${diffMin}m ago`;
51
+ const diffHr = Math.floor(diffMin / 60);
52
+ if (diffHr < 24) return `${diffHr}h ago`;
53
+ const diffDay = Math.floor(diffHr / 24);
54
+ if (diffDay < 7) return `${diffDay}d ago`;
55
+ return new Date(dateStr).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
56
+ }
57
+
58
+ /* ── Types ────────────────────────────────────────────── */
59
+
60
+ export interface RecentActivityProps {
61
+ activities: any[];
62
+ activitiesLoading: boolean;
63
+ tasksMap: Map<string, any>;
64
+ onCompleteTask: (activity: any) => void;
65
+ onViewEmail: (activity: any) => void;
66
+ onOpenTask: (activity: any) => void;
67
+ emailViewerOpen: boolean;
68
+ onEmailViewerOpenChange: (open: boolean) => void;
69
+ selectedEmail: { to: string; subject: string; body: string; html?: string; mode?: string } | null;
70
+ }
71
+
72
+ /* ── Component ────────────────────────────────────────── */
73
+
74
+ export function RecentActivity({
75
+ activities,
76
+ activitiesLoading,
77
+ tasksMap,
78
+ onCompleteTask,
79
+ onViewEmail,
80
+ onOpenTask,
81
+ emailViewerOpen,
82
+ onEmailViewerOpenChange,
83
+ selectedEmail
84
+ }: RecentActivityProps) {
85
+ return (
86
+ <>
87
+ <Card className="p-4 flex-1">
88
+ <Typography variant="subtitle1" className="mb-3">Recent Activity</Typography>
89
+
90
+ {activitiesLoading ? (
91
+ <div className="flex flex-col gap-3">
92
+ {Array.from({ length: 6 }).map((_, i) => (
93
+ <div key={i} className="flex items-center gap-3">
94
+ <Skeleton className="h-5 w-5 rounded-full shrink-0" />
95
+ <Skeleton className="h-4 flex-1" />
96
+ </div>
97
+ ))}
98
+ </div>
99
+ ) : activities.length === 0 ? (
100
+ <div className="flex flex-col items-center py-6">
101
+ <Typography variant="body2" color="secondary">
102
+ No recent activity
103
+ </Typography>
104
+ </div>
105
+ ) : (
106
+ <div className="flex flex-col">
107
+ {activities.map((activity: any, idx: number) => {
108
+ const actType = activity.values?.type;
109
+ const taskId = activity.values?.metadata?.taskId;
110
+ const relatedTask = taskId ? tasksMap.get(taskId) : null;
111
+ const isTaskActivity = actType === "task_created" || actType === "task_completed";
112
+ const isEmailActivity = actType === "email_sent";
113
+
114
+ return (
115
+ <React.Fragment key={activity.id}>
116
+ {idx > 0 && <Separator orientation="horizontal" className="my-0" />}
117
+ <div className="flex items-start gap-2.5 py-2.5">
118
+ <span className="shrink-0 mt-0.5 flex items-center">
119
+ {ACTIVITY_ICONS[actType] ?? <Pin className="h-4 w-4 text-surface-400 dark:text-surface-500" />}
120
+ </span>
121
+ <div className="flex-1 min-w-0">
122
+ <Typography variant="body2" className="truncate leading-snug">
123
+ {activity.values?.description || actType || "Activity"}
124
+ </Typography>
125
+ {activity.values?.createdAt && (
126
+ <Typography variant="caption" color="secondary" className="text-[10px]">
127
+ {relativeTime(activity.values.createdAt)}
128
+ </Typography>
129
+ )}
130
+ {/* Action buttons using @rebasepro/ui Buttons */}
131
+ <div className="flex items-center gap-3 mt-1 flex-wrap">
132
+ {/* Complete Task — only for task_created with a pending task */}
133
+ {isTaskActivity && relatedTask && relatedTask.values?.status !== "completed" && (
134
+ <Button
135
+ variant="text"
136
+ size="small"
137
+ color="primary"
138
+ className="text-emerald-600 dark:text-emerald-400 font-medium hover:underline p-0 h-auto min-w-0"
139
+ onClick={() => onCompleteTask(activity)}
140
+ >
141
+ Complete Task
142
+ </Button>
143
+ )}
144
+ {/* View Sent Email */}
145
+ {isEmailActivity && (
146
+ <Button
147
+ variant="text"
148
+ size="small"
149
+ color="primary"
150
+ className="font-medium hover:underline p-0 h-auto min-w-0"
151
+ onClick={() => onViewEmail(activity)}
152
+ >
153
+ View Sent Email
154
+ </Button>
155
+ )}
156
+ {/* Open task in side panel */}
157
+ {isTaskActivity && taskId && (
158
+ <Button
159
+ variant="text"
160
+ size="small"
161
+ color="neutral"
162
+ className="font-medium hover:underline p-0 h-auto min-w-0 text-surface-500 hover:text-surface-700 dark:text-surface-400 dark:hover:text-surface-300"
163
+ onClick={() => onOpenTask(activity)}
164
+ >
165
+ Open Task
166
+ </Button>
167
+ )}
168
+ </div>
169
+ </div>
170
+ </div>
171
+ </React.Fragment>
172
+ );
173
+ })}
174
+ </div>
175
+ )}
176
+ </Card>
177
+
178
+ {/* ── Email Viewer Dialog ── */}
179
+ <Dialog
180
+ open={emailViewerOpen}
181
+ onOpenChange={onEmailViewerOpenChange}
182
+ maxWidth="xl"
183
+ >
184
+ <DialogTitle>{selectedEmail?.subject || "Email"}</DialogTitle>
185
+ <DialogContent>
186
+ {selectedEmail && (
187
+ <div className="space-y-3">
188
+ <Typography variant="caption" color="secondary">
189
+ To: {selectedEmail.to}
190
+ </Typography>
191
+ {selectedEmail.html ? (
192
+ <div
193
+ className="prose prose-sm dark:prose-invert max-w-none text-text-primary dark:text-text-primary-dark"
194
+ dangerouslySetInnerHTML={{ __html: selectedEmail.html }}
195
+ />
196
+ ) : (
197
+ <Typography variant="body2" className="whitespace-pre-wrap">
198
+ {selectedEmail.body}
199
+ </Typography>
200
+ )}
201
+ {selectedEmail.mode === "simulated" && (
202
+ <Typography variant="caption" color="secondary" className="italic mt-2 block">
203
+ Simulated — email was not actually sent.
204
+ </Typography>
205
+ )}
206
+ </div>
207
+ )}
208
+ </DialogContent>
209
+ <DialogActions>
210
+ <Button variant="text" onClick={() => onEmailViewerOpenChange(false)}>Close</Button>
211
+ </DialogActions>
212
+ </Dialog>
213
+ </>
214
+ );
215
+ }