@rebasepro/app 0.17.3 → 0.18.0

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,318 +0,0 @@
1
-
2
- import React, { useEffect, useState } from "react";
3
- import {
4
- Avatar,
5
- Button,
6
- Card,
7
- Chip,
8
- CircularProgress,
9
- cls,
10
- IconButton,
11
- Tab,
12
- Tabs,
13
- TextField,
14
- Trash2Icon,
15
- Typography
16
- } from "@rebasepro/ui";
17
- import { useAuthController, useTranslation } from "../hooks";
18
-
19
- interface SessionInfo {
20
- id: string;
21
- userAgent?: string;
22
- ipAddress?: string;
23
- createdAt: string;
24
- isCurrentSession?: boolean;
25
- }
26
-
27
- interface ExtendedAuthController {
28
- user: { displayName?: string | null; photoURL?: string | null; email?: string | null } | null;
29
- updateProfile?: (displayName: string, photoURL: string) => Promise<void>;
30
- changePassword?: (oldPassword: string, newPassword: string) => Promise<void>;
31
- fetchSessions?: () => Promise<SessionInfo[]>;
32
- revokeSession?: (id: string) => Promise<void>;
33
- revokeAllSessions?: () => Promise<void>;
34
- signOut: () => Promise<void>;
35
- }
36
-
37
- type ActiveTab = "profile" | "security" | "sessions";
38
-
39
- export function UserSettingsView() {
40
- const authController = useAuthController() as ExtendedAuthController;
41
- const user = authController.user;
42
- const { t } = useTranslation();
43
-
44
- const hasPasswordChange = !!authController.changePassword;
45
- const [activeTab, setActiveTab] = useState<ActiveTab>("profile");
46
-
47
- // Profile state
48
- const [displayName, setDisplayName] = useState(user?.displayName || "");
49
- const [photoURL, setPhotoURL] = useState(user?.photoURL || "");
50
- const [savingProfile, setSavingProfile] = useState(false);
51
- const [profileError, setProfileError] = useState<string | null>(null);
52
-
53
- // Password change state
54
- const [currentPassword, setCurrentPassword] = useState("");
55
- const [newPassword, setNewPassword] = useState("");
56
- const [confirmPassword, setConfirmPassword] = useState("");
57
- const [changingPassword, setChangingPassword] = useState(false);
58
- const [passwordError, setPasswordError] = useState<string | null>(null);
59
- const [passwordSuccess, setPasswordSuccess] = useState<string | null>(null);
60
-
61
- // Sessions state
62
- const [sessions, setSessions] = useState<SessionInfo[]>([]);
63
- const [loadingSessions, setLoadingSessions] = useState(false);
64
- const [sessionsError, setSessionsError] = useState<string | null>(null);
65
- const [revokingSessionId, setRevokingSessionId] = useState<string | null>(null);
66
- const [revokingAll, setRevokingAll] = useState(false);
67
-
68
- useEffect(() => {
69
- setDisplayName(user?.displayName || "");
70
- setPhotoURL(user?.photoURL || "");
71
- if (activeTab === "sessions") {
72
- loadSessions();
73
- }
74
- }, [activeTab, user]);
75
-
76
- const handleSaveProfile = async () => {
77
- setSavingProfile(true);
78
- setProfileError(null);
79
- try {
80
- if (authController.updateProfile) {
81
- await authController.updateProfile(displayName, photoURL);
82
- } else {
83
- throw new Error("updateProfile not implemented in this auth controller.");
84
- }
85
- } catch (e: unknown) {
86
- setProfileError(e instanceof Error ? e.message : String(e));
87
- } finally {
88
- setSavingProfile(false);
89
- }
90
- };
91
-
92
- const handleChangePassword = async () => {
93
- setPasswordError(null);
94
- setPasswordSuccess(null);
95
-
96
- // Validate
97
- if (newPassword.length < 8) {
98
- setPasswordError(t("password_too_short"));
99
- return;
100
- }
101
- if (newPassword !== confirmPassword) {
102
- setPasswordError(t("passwords_dont_match"));
103
- return;
104
- }
105
-
106
- setChangingPassword(true);
107
- try {
108
- if (authController.changePassword) {
109
- await authController.changePassword(currentPassword, newPassword);
110
- setPasswordSuccess(t("password_changed"));
111
- setCurrentPassword("");
112
- setNewPassword("");
113
- setConfirmPassword("");
114
- // Backend invalidates all sessions on password change,
115
- // so the user will be logged out shortly
116
- setTimeout(() => {
117
- authController.signOut();
118
- }, 2000);
119
- }
120
- } catch (e: unknown) {
121
- setPasswordError(e instanceof Error ? e.message : String(e));
122
- } finally {
123
- setChangingPassword(false);
124
- }
125
- };
126
-
127
- const loadSessions = async () => {
128
- setLoadingSessions(true);
129
- setSessionsError(null);
130
- try {
131
- if (authController.fetchSessions) {
132
- const fetchedSessions = await authController.fetchSessions();
133
- const sortedSessions = (fetchedSessions || []).sort((a: SessionInfo, b: SessionInfo) =>
134
- new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
135
- );
136
- setSessions(sortedSessions);
137
- } else {
138
- throw new Error("fetchSessions not implemented in this auth controller.");
139
- }
140
- } catch (e: unknown) {
141
- setSessionsError(e instanceof Error ? e.message : String(e));
142
- } finally {
143
- setLoadingSessions(false);
144
- }
145
- };
146
-
147
- const handleRevokeSession = async (id: string, isCurrentSession?: boolean) => {
148
- setRevokingSessionId(id);
149
- try {
150
- if (authController.revokeSession) {
151
- await authController.revokeSession(id);
152
- setSessions(sessions.filter(s => s.id !== id));
153
- if (isCurrentSession) {
154
- await authController.signOut();
155
- }
156
- } else {
157
- throw new Error("revokeSession not implemented in this auth controller.");
158
- }
159
- } catch (e: unknown) {
160
- setSessionsError(e instanceof Error ? e.message : String(e));
161
- } finally {
162
- setRevokingSessionId(null);
163
- }
164
- };
165
-
166
- const handleRevokeAll = async () => {
167
- setRevokingAll(true);
168
- try {
169
- if (authController.revokeAllSessions) {
170
- await authController.revokeAllSessions();
171
- } else {
172
- throw new Error("revokeAllSessions not implemented in this auth controller.");
173
- }
174
- } catch (e: unknown) {
175
- setSessionsError(e instanceof Error ? e.message : String(e));
176
- } finally {
177
- setRevokingAll(false);
178
- }
179
- };
180
-
181
- if (!user) return null;
182
-
183
- return (
184
- <div className="flex-grow max-w-4xl w-full mx-auto p-4 sm:p-6 md:p-12">
185
- <Typography variant="h4" className="mb-8">{t("account_settings")}</Typography>
186
-
187
- <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as ActiveTab)} className="mb-8">
188
- <Tab value="profile">{t("profile")}</Tab>
189
- {hasPasswordChange && <Tab value="security">{t("security")}</Tab>}
190
- <Tab value="sessions">{t("sessions")}</Tab>
191
- </Tabs>
192
-
193
- {activeTab === "profile" && (
194
- <div className="flex flex-col gap-6 max-w-xl">
195
- <div className="flex items-center gap-6 mb-2">
196
- <Avatar src={photoURL || undefined} className="w-24 h-24 text-3xl">
197
- {displayName ? displayName[0].toUpperCase() : (user.email ? user.email[0].toUpperCase() : "A")}
198
- </Avatar>
199
- </div>
200
- <TextField
201
- label={t("display_name")}
202
- value={displayName}
203
- onChange={(e) => setDisplayName(e.target.value)}
204
- />
205
- <TextField
206
- label={t("photo_url")}
207
- value={photoURL}
208
- onChange={(e) => setPhotoURL(e.target.value)}
209
- />
210
- {profileError && <Typography color="error">{profileError}</Typography>}
211
- <div className="mt-4">
212
- <Button variant="filled" onClick={handleSaveProfile} disabled={savingProfile}>
213
- {savingProfile ? t("saving") : t("save_profile")}
214
- </Button>
215
- </div>
216
- </div>
217
- )}
218
-
219
- {activeTab === "security" && hasPasswordChange && (
220
- <div className="flex flex-col gap-6 max-w-xl">
221
- <Typography variant="h6" className="mb-2">{t("change_password")}</Typography>
222
-
223
- <TextField
224
- label={t("current_password")}
225
- type="password"
226
- value={currentPassword}
227
- onChange={(e) => setCurrentPassword(e.target.value)}
228
- autoComplete="current-password"
229
- />
230
- <TextField
231
- label={t("new_password")}
232
- type="password"
233
- value={newPassword}
234
- onChange={(e) => setNewPassword(e.target.value)}
235
- autoComplete="new-password"
236
- />
237
- <TextField
238
- label={t("confirm_password")}
239
- type="password"
240
- value={confirmPassword}
241
- onChange={(e) => setConfirmPassword(e.target.value)}
242
- autoComplete="new-password"
243
- />
244
-
245
- {passwordError && (
246
- <Typography color="error">{passwordError}</Typography>
247
- )}
248
- {passwordSuccess && (
249
- <Typography className="text-emerald-600 dark:text-emerald-400">{passwordSuccess}</Typography>
250
- )}
251
-
252
- <div className="mt-4">
253
- <Button
254
- variant="filled"
255
- onClick={handleChangePassword}
256
- disabled={changingPassword || !currentPassword || !newPassword || !confirmPassword}
257
- >
258
- {changingPassword ? t("changing_password") : t("change_password")}
259
- </Button>
260
- </div>
261
- </div>
262
- )}
263
-
264
- {activeTab === "sessions" && (
265
- <div className="flex flex-col gap-4 max-w-3xl">
266
- {loadingSessions ? (
267
- <div className="flex justify-center p-8"><CircularProgress/></div>
268
- ) : sessionsError ? (
269
- <Typography color="error">{sessionsError}</Typography>
270
- ) : sessions.length === 0 ? (
271
- <Typography>{t("no_active_sessions")}</Typography>
272
- ) : (
273
- <div className="flex flex-col gap-4">
274
- <div className="flex justify-end mb-2">
275
- <Button
276
- variant="text"
277
- color="error"
278
- onClick={handleRevokeAll}
279
- disabled={revokingAll}
280
- >
281
- {revokingAll ? t("revoking") : t("revoke_all_sessions")}
282
- </Button>
283
- </div>
284
- {sessions.map(session => (
285
- <Card key={session.id} className="flex justify-between items-center p-4">
286
- <div className="flex flex-col">
287
- <div className="flex items-center gap-2 mb-1">
288
- <Typography variant="body1">
289
- {session.userAgent || t("unknown_device")}
290
- </Typography>
291
- {session.isCurrentSession && (
292
- <Chip colorScheme="blue" size="small">
293
- {t("current")}
294
- </Chip>
295
- )}
296
- </div>
297
- <Typography variant="caption" color="secondary">
298
- IP: {session.ipAddress || "Unknown"} • Created: {new Date(session.createdAt).toLocaleString()}
299
- </Typography>
300
- </div>
301
- <div className="ml-4">
302
- {revokingSessionId === session.id ? (
303
- <CircularProgress size="small"/>
304
- ) : (
305
- <IconButton onClick={() => handleRevokeSession(session.id, session.isCurrentSession)} aria-label="Revoke Session">
306
- <Trash2Icon/>
307
- </IconButton>
308
- )}
309
- </div>
310
- </Card>
311
- ))}
312
- </div>
313
- )}
314
- </div>
315
- )}
316
- </div>
317
- );
318
- }
@@ -1,6 +0,0 @@
1
- export * from "./types";
2
- export * from "./useDebouncedData";
3
- export * from "./useColumnsIds";
4
- export * from "./useDataTableController";
5
- export * from "./useScrollRestoration";
6
- export * from "./table_height";
@@ -1,21 +0,0 @@
1
- /**
2
- * @group Components
3
- */
4
- export type TableSize = "xs" | "s" | "m" | "l" | "xl";
5
-
6
- export function getRowHeight(size: TableSize): number {
7
- switch (size) {
8
- case "xl":
9
- return 400;
10
- case "l":
11
- return 280;
12
- case "m":
13
- return 140;
14
- case "s":
15
- return 80;
16
- case "xs":
17
- return 54;
18
- default:
19
- throw Error("Missing mapping for collection size -> height");
20
- }
21
- }
@@ -1,66 +0,0 @@
1
- import type { Property, Entity } from "@rebasepro/types";
2
- import { CollectionSize, SelectedCellProps } from "@rebasepro/cms-types";
3
-
4
- export type DataCollectionTableController<M extends Record<string, unknown>> = {
5
-
6
- /**
7
- * This cell is displayed as selected
8
- */
9
- selectedCell?: SelectedCellProps;
10
- /**
11
- * Store used to sync selection state across cells efficiently.
12
- */
13
- selectionStore?: { getEntity: () => SelectedCellProps | undefined; subscribe: (cb: () => void) => () => void };
14
- /**
15
- * Select a table cell
16
- * @param cell
17
- */
18
- select: (cell?: SelectedCellProps<M>) => void;
19
- /**
20
- * The cell that is displayed as a popup view.
21
- * @param cell
22
- */
23
- setPopupCell?: (cell?: SelectedCellProps<M>) => void;
24
- /**
25
- * Callback used when the value of a cell has changed.
26
- * @param params
27
- */
28
- onValueChange?: (params: OnCellValueChangeParams<unknown, Entity<M>>) => void;
29
- /**
30
- * Size of the elements in the collection
31
- */
32
- size: CollectionSize;
33
- }
34
-
35
- /**
36
- * Props passed in a callback when the content of a cell in a table has been edited
37
- * @group Collection components
38
- */
39
- export interface OnCellValueChangeParams<T = unknown, D = unknown> {
40
- value: T,
41
- propertyKey: string,
42
- data?: D,
43
- onValueUpdated: () => void
44
- setError: (e: Error | undefined) => void
45
- }
46
-
47
- /**
48
- * @group Collection components
49
- */
50
- export type UniqueFieldValidator = (props: {
51
- name: string,
52
- value: unknown,
53
- property: Property,
54
- entityId?: string | number
55
- }) => Promise<boolean>;
56
-
57
- /**
58
- * Callback when a cell has changed in a table
59
- * @group Collection components
60
- */
61
- export type OnCellValueChange<T, M extends Record<string, unknown>> = (params: OnCellValueChangeParams<T, Entity<M>>) => Promise<void> | void;
62
-
63
- /**
64
- * @group Collection components
65
- */
66
- export type OnColumnResizeParams = { width: number, key: string };
@@ -1,261 +0,0 @@
1
- import type { Property, MapProperty, Properties, RelationProperty } from "@rebasepro/types";
2
- import { useMemo } from "react";
3
- ;
4
- import { getChildViewDeclaringProperties, getChildViewRelationPropertyKeys, getSubcollections } from "@rebasepro/common";
5
- import type { AdminCollection } from "@rebasepro/cms-types";
6
- import { getPropertyInPath } from "../../collections/property-path";
7
- import { isDisabled } from "../../collections/property_presentation";
8
-
9
- export type PropertyColumnConfig = {
10
- key: string;
11
- disabled: boolean;
12
- };
13
-
14
- export function getSubcollectionColumnId(collection: AdminCollection<any>) {
15
- return `subcollection:${collection.slug}`;
16
- }
17
-
18
- /**
19
- * The jump-to-tab columns a collection table does not need, because the relation
20
- * behind them already has a column of its own.
21
- *
22
- * Every child view gets a 200px button column that opens its tab. For a relation
23
- * declared in `relations` that button is the relation's only presence in the
24
- * table. For one declared as a property it is the second: the property's own
25
- * column is already there, hydrated by the list fetch's `include: ["*"]`, showing
26
- * the child rows themselves — and carrying the *same heading*, because the tab
27
- * takes its name from the declaring property. Two columns called "Applications",
28
- * one of them a button, and nothing in the header to tell them apart.
29
- *
30
- * The property column wins: it shows what the children are, and each chip in it
31
- * opens one. The tab stays reachable by opening the record, which is what the
32
- * rest of the table's rows do anyway.
33
- *
34
- * Unless the author hid that column — `hideFromCollection` on the property, or a
35
- * `propertiesOrder` that omits it, is a statement about the column and not about
36
- * the relation, so the button comes back rather than the relation dropping out
37
- * of the table altogether.
38
- */
39
- export function getRedundantChildViewColumnIds<M extends Record<string, any>>(collection: AdminCollection<M>): Set<string> {
40
- const redundant = new Set<string>();
41
- const properties = collection.properties ?? {};
42
- const order = collection.propertiesOrder as string[] | undefined;
43
-
44
- for (const [viewKey, propertyKey] of getChildViewDeclaringProperties(collection)) {
45
- const property = properties[propertyKey] as Property | undefined;
46
- if (!property || property.admin?.hideFromCollection) continue;
47
- if (order && !order.includes(propertyKey)) continue;
48
- redundant.add(`subcollection:${viewKey}`);
49
- }
50
-
51
- return redundant;
52
- }
53
-
54
- export function useColumnIds<M extends Record<string, any>>(collection: AdminCollection<M>, includeSubcollections: boolean): PropertyColumnConfig[] {
55
- return useMemo(() => {
56
- if (collection.propertiesOrder) {
57
- return hideAndExpandKeys(collection, collection.propertiesOrder);
58
- }
59
- return getDefaultColumnKeys(collection, includeSubcollections);
60
- }, [collection, includeSubcollections]);
61
- }
62
-
63
- function hideAndExpandKeys<M extends Record<string, any>>(collection: AdminCollection<M>, keys: string[]): PropertyColumnConfig[] {
64
-
65
- // First, figure out which spread map roots have individual child keys in the order
66
- // If so, we should NOT auto-expand them - just use the explicit child keys
67
- const rootsWithExplicitChildren = new Set<string>();
68
- for (const key of keys) {
69
- if (key.includes(".")) {
70
- const rootKey = key.split(".")[0];
71
- const rootProperty = collection.properties[rootKey];
72
- if (rootProperty && rootProperty.type === "map" && rootProperty.admin?.spreadChildren && rootProperty.properties) {
73
- rootsWithExplicitChildren.add(rootKey);
74
- }
75
- }
76
- }
77
-
78
- // Track processed keys to avoid duplicates
79
- const processedPropertyKeys = new Set<string>();
80
-
81
- // Both column paths funnel through here, including a `propertiesOrder` that
82
- // was saved back with the button column in it.
83
- const redundantChildViewColumns = getRedundantChildViewColumnIds(collection);
84
-
85
- const result = keys.flatMap((key) => {
86
- // Skip if already processed (handles duplicates in propertiesOrder)
87
- if (processedPropertyKeys.has(key)) return [null];
88
- if (redundantChildViewColumns.has(key)) return [null];
89
-
90
- // Check if it's a top-level property
91
- const property = collection.properties[key];
92
- if (property) {
93
- processedPropertyKeys.add(key);
94
- if (property.admin?.hideFromCollection)
95
- return [null];
96
- if (property.admin?.disabled && typeof property.admin?.disabled === "object" && property.admin?.disabled.hidden)
97
- return [null];
98
-
99
- if (property.type === "map" && property.admin?.spreadChildren && property.properties) {
100
- // Check if this spread map has explicit child keys in propertiesOrder
101
- if (rootsWithExplicitChildren.has(key)) {
102
- // DON'T auto-expand - the children are explicitly listed elsewhere
103
- return [null];
104
- }
105
- // Auto-expand all children
106
- const childConfigs = getColumnKeysForProperty(property, key);
107
- childConfigs.forEach(c => processedPropertyKeys.add(c.key));
108
- return childConfigs;
109
- }
110
- return [{
111
- key,
112
- disabled: isDisabled(property) || Boolean(property.admin?.readOnly)
113
- }];
114
- }
115
-
116
- // Check if it's a nested key like "data.mode" (for spread map properties)
117
- if (key.includes(".")) {
118
- const rootKey = key.split(".")[0];
119
- const rootProperty = collection.properties[rootKey];
120
-
121
- if (rootProperty && rootProperty.type === "map" && rootProperty.properties) {
122
- const nestedProperty = getPropertyInPath(collection.properties, key) as Property | undefined;
123
- if (nestedProperty) {
124
- processedPropertyKeys.add(key);
125
- // Mark root as seen
126
- processedPropertyKeys.add(rootKey);
127
-
128
- if (nestedProperty.admin?.hideFromCollection)
129
- return [null];
130
- if (nestedProperty.admin?.disabled && typeof nestedProperty.admin?.disabled === "object" && nestedProperty.admin?.disabled.hidden)
131
- return [null];
132
-
133
- return [{
134
- key,
135
- disabled: isDisabled(rootProperty) || Boolean(rootProperty.admin?.readOnly) ||
136
- isDisabled(nestedProperty) || Boolean(nestedProperty.admin?.readOnly)
137
- }];
138
- }
139
- }
140
- }
141
-
142
- // Check additional fields
143
- const additionalField = collection.additionalFields?.find(field => field.key === key);
144
- if (additionalField) {
145
- return [{
146
- key,
147
- disabled: true
148
- }];
149
- }
150
-
151
- // Check subcollections
152
- const subcollections = getSubcollections(collection);
153
- if (subcollections) {
154
- const subCollection = subcollections
155
- .find(subCol => getSubcollectionColumnId(subCol) === key);
156
- if (subCollection) {
157
- return [{
158
- key,
159
- disabled: true
160
- }];
161
- }
162
- }
163
-
164
- return [null];
165
- }).filter(Boolean) as PropertyColumnConfig[];
166
-
167
- // Add any missing properties that weren't in propertiesOrder
168
- // This ensures properties NEVER disappear
169
- for (const propKey of Object.keys(collection.properties)) {
170
- // Skip if already processed
171
- if (processedPropertyKeys.has(propKey)) continue;
172
-
173
- const property = collection.properties[propKey];
174
- if (!property) continue;
175
- if (property.admin?.hideFromCollection) continue;
176
- if (property.admin?.disabled && typeof property.admin?.disabled === "object" && property.admin?.disabled.hidden) continue;
177
-
178
- if (property.type === "map" && property.admin?.spreadChildren && property.properties) {
179
- // For spread maps, add all children that weren't already added
180
- const allChildConfigs = getColumnKeysForProperty(property as MapProperty, propKey);
181
- for (const childConfig of allChildConfigs) {
182
- if (!processedPropertyKeys.has(childConfig.key)) {
183
- result.push(childConfig);
184
- processedPropertyKeys.add(childConfig.key);
185
- }
186
- }
187
- } else {
188
- result.push({
189
- key: propKey,
190
- disabled: isDisabled(property) || Boolean(property.admin?.readOnly)
191
- });
192
- processedPropertyKeys.add(propKey);
193
- }
194
- }
195
-
196
- return result;
197
- }
198
-
199
- function getDefaultColumnKeys<M extends Record<string, any> = any>(collection: AdminCollection<M>, includeSubCollections: boolean) {
200
- const propertyKeys = Object.keys(collection.properties);
201
-
202
- const additionalFields = collection.additionalFields ?? [];
203
- const subCollections: AdminCollection[] = getSubcollections(collection) ?? [];
204
-
205
- const columnIds: string[] = [
206
- ...propertyKeys,
207
- // Filter out additional fields whose key already exists in propertyKeys to avoid duplicate column keys
208
- ...additionalFields.filter((field) => !propertyKeys.includes(field.key)).map((field) => field.key)
209
- ];
210
-
211
- if (includeSubCollections) {
212
- const subCollectionIds = subCollections
213
- .map((collection) => getSubcollectionColumnId(collection));
214
- columnIds.push(...subCollectionIds.filter((subColId) => !columnIds.includes(subColId)));
215
- }
216
-
217
- return hideAndExpandKeys(collection, columnIds);
218
- }
219
-
220
- export function getColumnKeysForProperty(property: Property, key: string, disabled?: boolean): PropertyColumnConfig[] {
221
- // A key with no property behind it describes no column. Callers walk whole
222
- // property maps here to build a table's columns, so reading `.type` off a
223
- // hole threw away every other column with it.
224
- if (!property) return [];
225
- if (property.type === "map" && property.admin?.spreadChildren && property.properties) {
226
- return Object.entries(property.properties)
227
- .flatMap(([childKey, childProperty]) => getColumnKeysForProperty(
228
- childProperty as Readonly<Property>,
229
- `${key}.${childKey}`,
230
- disabled || isDisabled(property) || Boolean(property.admin?.readOnly))
231
- );
232
- }
233
- return [{
234
- key,
235
- disabled: disabled || isDisabled(property) || Boolean(property.admin?.readOnly)
236
- }];
237
- }
238
-
239
- export function getFormFieldKeys(collection: AdminCollection): string[] {
240
- const properties = collection.properties ?? {};
241
- // A many-relation the entity view already lists as a tab is not an input:
242
- // the tab is the treatment, and rendering the same declaration again as a
243
- // picker asked the author to select a collection's own children from a
244
- // dropdown. `renderInForm` is how a project that wants the picker asks for
245
- // it back — opt in, because the tab is the default.
246
- const consumedByChildView = getChildViewRelationPropertyKeys(collection);
247
- const propertyKeys = Object.keys(properties).filter(key => {
248
- if (!consumedByChildView.has(key)) return true;
249
- return (properties[key] as RelationProperty).admin?.renderInForm === true;
250
- });
251
- const additionalFields = collection.additionalFields ?? [];
252
- const allKeys = [
253
- ...propertyKeys,
254
- ...additionalFields.map((field) => field.key)
255
- ];
256
-
257
- if (collection.propertiesOrder) {
258
- return collection.propertiesOrder.filter(key => allKeys.includes(key));
259
- }
260
- return allKeys;
261
- }