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