@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,176 @@
1
+ import type { CollectionConfig } from "@rebasepro/types";
2
+ import { useEffect, useState, useMemo } from "react";
3
+ import { Entity, FilterValues, User } from "@rebasepro/types";
4
+ import { useData } from "./useData";
5
+ import { isSchemaDriftError, useSchemaDriftContext } from "../../components/SchemaDriftBanner";
6
+ /**
7
+ * @group Hooks and utilities
8
+ */
9
+ export interface CollectionProps<M extends Record<string, any>> {
10
+
11
+ /**
12
+ * Absolute collection path
13
+ */
14
+ path: string;
15
+
16
+ /**
17
+ * collection of the entity displayed by this collection
18
+ */
19
+ collection: CollectionConfig<M>
20
+
21
+ /**
22
+ * Number of entities to fetch
23
+ */
24
+ itemCount?: number;
25
+
26
+ /**
27
+ * Number of items to skip
28
+ */
29
+ offset?: number;
30
+
31
+ /**
32
+ * Page number (1-indexed), alternative to offset
33
+ */
34
+ page?: number;
35
+
36
+ /**
37
+ * Filter the fetched data by the property
38
+ */
39
+ filterValues?: FilterValues<Extract<keyof M, string>>;
40
+
41
+ /**
42
+ * Sort the results by
43
+ */
44
+ sortBy?: [Extract<keyof M, string>, "asc" | "desc"];
45
+
46
+ /**
47
+ * Search string
48
+ */
49
+ searchString?: string;
50
+ }
51
+
52
+ /**
53
+ * @group Hooks and utilities
54
+ */
55
+ export interface CollectionResult<M extends Record<string, any>> {
56
+ data: Entity<M>[];
57
+ dataLoading: boolean;
58
+ noMoreToLoad: boolean;
59
+ dataLoadingError?: Error;
60
+ totalCount?: number;
61
+ }
62
+
63
+ /**
64
+ * This hook is used to fetch collections using a given collection
65
+ * @param path
66
+ * @param collection
67
+ * @param filterValues
68
+ * @param sortBy
69
+ * @param itemCount
70
+ * @param offset
71
+ * @param page
72
+ * @param searchString
73
+ * @group Hooks and utilities
74
+ */
75
+ export function useCollection<M extends Record<string, any>, USER extends User>(
76
+ {
77
+ path,
78
+ collection,
79
+ filterValues,
80
+ sortBy,
81
+ itemCount,
82
+ offset,
83
+ page,
84
+ searchString
85
+ }: CollectionProps<M>): CollectionResult<M> {
86
+ const dataClient = useData();
87
+ const { reportSchemaDrift } = useSchemaDriftContext();
88
+
89
+ const sortByProperty = sortBy ? sortBy[0] : undefined;
90
+ const currentSort = sortBy ? sortBy[1] : undefined;
91
+ // Map to PostgREST format for orderBy
92
+ const orderByParams: [string, "asc" | "desc"] | undefined = sortBy ? [String(sortBy[0]), sortBy[1]] : undefined;
93
+
94
+ // filterValues is already FilterValues — pass directly
95
+ const whereParams = filterValues && Object.keys(filterValues).length > 0 ? filterValues : undefined;
96
+
97
+ const [data, setData] = useState<Entity<M>[]>([]);
98
+
99
+ const [dataLoading, setDataLoading] = useState<boolean>(false);
100
+ const [dataLoadingError, setDataLoadingError] = useState<Error | undefined>();
101
+ const [noMoreToLoad, setNoMoreToLoad] = useState<boolean>(false);
102
+ const [totalCount, setTotalCount] = useState<number | undefined>();
103
+
104
+ useEffect(() => {
105
+
106
+ setDataLoading(true);
107
+
108
+ const onEntitiesUpdate = async (res: { data: Entity<M>[], meta: { hasMore: boolean; total?: number } }) => {
109
+ const entities = res.data;
110
+ setDataLoading(false);
111
+ setDataLoadingError(undefined);
112
+ setData(entities.map(e => ({
113
+ ...e
114
+ })));
115
+ setNoMoreToLoad(!res.meta.hasMore);
116
+ setTotalCount(res.meta.total);
117
+ };
118
+
119
+ const onError = (error: Error) => {
120
+ console.error("ERROR", error);
121
+ setDataLoading(false);
122
+ setData([]);
123
+ setDataLoadingError(error);
124
+ setTotalCount(undefined);
125
+ // Report schema drift to the global banner context
126
+ if (isSchemaDriftError(error)) {
127
+ reportSchemaDrift(error.message);
128
+ }
129
+ };
130
+
131
+ const accessor = dataClient.collection(path);
132
+
133
+ // Eagerly include relations to avoid N+1 fetches.
134
+ const hasRelations = collection.properties && Object.values(collection.properties).some(
135
+ (p) => p.type === "relation" || p.type === "reference"
136
+ );
137
+ const includeParams = hasRelations ? ["*"] : undefined;
138
+
139
+ if (accessor.listen) {
140
+ return accessor.listen({
141
+ where: whereParams,
142
+ limit: itemCount,
143
+ offset,
144
+ page,
145
+ orderBy: orderByParams,
146
+ searchString,
147
+ include: includeParams
148
+ }, (res) => onEntitiesUpdate({ data: res.data as Entity<M>[],
149
+ meta: res.meta }), onError);
150
+ } else {
151
+ accessor.find({
152
+ where: whereParams,
153
+ limit: itemCount,
154
+ offset,
155
+ page,
156
+ orderBy: orderByParams,
157
+ searchString,
158
+ include: includeParams
159
+ })
160
+ .then((res) => onEntitiesUpdate({ data: res.data as Entity<M>[],
161
+ meta: res.meta }))
162
+ .catch(onError);
163
+ return () => {
164
+ };
165
+ }
166
+ }, [path, itemCount, offset, page, currentSort, sortByProperty, filterValues, searchString, dataClient, collection]);
167
+
168
+ return useMemo(() => ({
169
+ data,
170
+ dataLoading,
171
+ dataLoadingError,
172
+ noMoreToLoad,
173
+ totalCount
174
+ }), [data, dataLoading, dataLoadingError, noMoreToLoad, totalCount]);
175
+
176
+ }
@@ -0,0 +1,18 @@
1
+ import { useContext } from "react";
2
+ import { RebaseData } from "@rebasepro/types";
3
+ import { RebaseDataContext } from "../../contexts/RebaseDataContext";
4
+
5
+ /**
6
+ * Use this hook to access the unified data API.
7
+ *
8
+ * ```ts
9
+ * const data = useData();
10
+ * const { data: products } = await data.products.find({ where: { status: ["==", "published"] } });
11
+ * await data.products.create({ name: "Camera", price: 299 });
12
+ * ```
13
+ *
14
+ * @group Hooks and utilities
15
+ */
16
+ export const useData = (): RebaseData => {
17
+ return useContext(RebaseDataContext);
18
+ };
@@ -0,0 +1,26 @@
1
+ import { Entity } from "@rebasepro/types";
2
+
3
+ export interface DataOrderProps<M extends Record<string, any>> {
4
+ data: Entity<M>[];
5
+ entitiesDisplayedFirst?: Entity<M>[];
6
+ }
7
+
8
+ /**
9
+ * This hook is used to have some entities at the beginning of data.
10
+ * @param path
11
+ * @param entitiesDisplayedFirst
12
+ * @group Hooks and utilities
13
+ */
14
+ export function useDataOrder<M extends Record<string, any>>(
15
+ {
16
+ data,
17
+ entitiesDisplayedFirst
18
+ }: DataOrderProps<M>): Entity<M>[] {
19
+
20
+ if (!entitiesDisplayedFirst)
21
+ return data;
22
+
23
+ const displayedFirstId = new Set(entitiesDisplayedFirst.map((e) => e.id));
24
+ return [...entitiesDisplayedFirst, ...data.filter((e) => !displayedFirstId.has(e.id))];
25
+
26
+ }
@@ -0,0 +1,140 @@
1
+ import type { CollectionConfig } from "@rebasepro/types";
2
+ import { useEffect, useState, useMemo } from "react";
3
+ import { Entity, RebaseContext, User } from "@rebasepro/types";
4
+ import { useData } from "./useData";
5
+ import { useRebaseContext } from "../useRebaseContext";
6
+
7
+ /**
8
+ * @group Hooks and utilities
9
+ */
10
+ export interface FetchProps<M extends Record<string, any>, USER extends User = User> {
11
+ path: string;
12
+ entityId?: string | number;
13
+ databaseId?: string;
14
+ collection: CollectionConfig<M, USER>;
15
+ useCache?: boolean;
16
+ }
17
+
18
+ /**
19
+ * @group Hooks and utilities
20
+ */
21
+ export interface FetchResult<M extends Record<string, any>> {
22
+ entity?: Entity<M>,
23
+ dataLoading: boolean,
24
+ dataLoadingError?: Error
25
+ }
26
+
27
+ const CACHE: Record<string, Entity<any> | undefined> = {};
28
+
29
+ /**
30
+ * Pre-populate the entity fetch cache with entities loaded from a collection.
31
+ * This allows entity detail views to render instantly using cached data,
32
+ * while the background fetch/listener brings in fresh data.
33
+ * @param path - The collection path (e.g. "products")
34
+ * @param entities - Array of entities to cache
35
+ */
36
+ export function populateFetchCache<M extends Record<string, any>>(path: string, entities: Entity<M>[]): void {
37
+ for (const entity of entities) {
38
+ CACHE[`${path}/${entity.id}`] = entity;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Clear the entity fetch cache. Call this on auth state changes (e.g. logout)
44
+ * to prevent stale data from a previous session leaking into the next.
45
+ */
46
+ export function clearFetchCache(): void {
47
+ for (const key of Object.keys(CACHE)) {
48
+ delete CACHE[key];
49
+ }
50
+ }
51
+
52
+ /**
53
+ * This hook is used to fetch a entity.
54
+ * It gives real time updates if the driver supports it.
55
+ * @param path
56
+ * @param collection
57
+ * @param entityId
58
+ * @param useCache
59
+ * @group Hooks and utilities
60
+ */
61
+
62
+ export function useFetch<M extends Record<string, any>, USER extends User = User>(
63
+ {
64
+ path,
65
+ entityId,
66
+ collection,
67
+ databaseId,
68
+ useCache = false
69
+ }: FetchProps<M, USER>): FetchResult<M> {
70
+
71
+ const dataClient = useData();
72
+
73
+ const context: RebaseContext<USER> = useRebaseContext();
74
+
75
+ // Seed initial state from the cache to avoid skeleton flashes.
76
+ // Even when useCache is false, we show cached data instantly while
77
+ // the background fetch/listener brings in fresh data.
78
+ const cacheKey = entityId ? `${path}/${entityId}` : undefined;
79
+ const cachedEntity = cacheKey ? CACHE[cacheKey] as Entity<M> | undefined : undefined;
80
+
81
+ const [entity, setEntity] = useState<Entity<M> | undefined>(cachedEntity);
82
+ const [dataLoading, setDataLoading] = useState<boolean>(!cachedEntity);
83
+ const [dataLoadingError, setDataLoadingError] = useState<Error | undefined>();
84
+
85
+ useEffect(() => {
86
+
87
+ // Only show loading state if we have no cached entity to display
88
+ if (!CACHE[`${path}/${entityId}`]) {
89
+ setDataLoading(true);
90
+ }
91
+
92
+ const onEntityUpdate = async (updatedEntity?: Entity<M> | null) => {
93
+ CACHE[`${path}/${entityId}`] = updatedEntity ?? undefined;
94
+ setEntity(updatedEntity ?? undefined);
95
+ setDataLoading(false);
96
+ setDataLoadingError(undefined);
97
+ };
98
+
99
+ const onError = (error: Error) => {
100
+ console.error("ERROR fetching entity", error);
101
+ setDataLoading(false);
102
+ setEntity(undefined);
103
+ setDataLoadingError(error);
104
+ };
105
+
106
+ if (entityId && useCache && CACHE[`${path}/${entityId}`]) {
107
+ setEntity(CACHE[`${path}/${entityId}`]);
108
+ setDataLoading(false);
109
+ setDataLoadingError(undefined);
110
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
111
+ return () => {
112
+ };
113
+ } else if (entityId && path && collection) {
114
+ const accessor = dataClient.collection(path);
115
+
116
+ if (accessor.listenById) {
117
+ return accessor.listenById(entityId, (entity) => onEntityUpdate(entity as Entity<M> | undefined), onError);
118
+ } else {
119
+ accessor.findById(entityId)
120
+ .then((entity) => onEntityUpdate(entity as Entity<M> | undefined))
121
+ .catch(onError);
122
+ return () => {
123
+ };
124
+ }
125
+ }
126
+ // if no entityId is provided we do nothing
127
+ else {
128
+ onEntityUpdate(undefined);
129
+ return () => {
130
+ };
131
+ }
132
+ }, [entityId, path, dataClient, collection, useCache, databaseId]);
133
+
134
+ return useMemo(() => ({
135
+ entity,
136
+ dataLoading,
137
+ dataLoadingError
138
+ }), [entity, dataLoading, dataLoadingError]);
139
+
140
+ }
@@ -0,0 +1,234 @@
1
+ import type { CollectionConfig } from "@rebasepro/types";
2
+ import { useCallback, useEffect, useRef, useState, useMemo } from "react";
3
+ import { useData } from "./useData";
4
+ import { Entity, EntityRelation, FilterValues } from "@rebasepro/types";
5
+ export interface RelationItem {
6
+ id: string | number;
7
+ label: string;
8
+ description?: string;
9
+ data: Entity<any>;
10
+ relation: EntityRelation;
11
+ }
12
+
13
+ export interface UseRelationSelectorProps<M extends Record<string, any> = any> {
14
+ /**
15
+ * Full path where the relation data is located
16
+ */
17
+ path: string;
18
+ /**
19
+ * The collection that represents the relation entities
20
+ */
21
+ collection: CollectionConfig<M>;
22
+ /**
23
+ * Force filter to be applied to the relation search
24
+ */
25
+ fixedFilter?: FilterValues<string>;
26
+ /**
27
+ * Page size for pagination
28
+ */
29
+ pageSize?: number;
30
+ /**
31
+ * Function to extract the label from a entity
32
+ */
33
+ getLabelFromEntity?: (entity: Entity<M>) => string;
34
+ /**
35
+ * Function to extract the description from a entity
36
+ */
37
+ getDescriptionFromEntity?: (entity: Entity<M>) => string | undefined;
38
+ /**
39
+ * Property name to use as the secondary display field
40
+ */
41
+ descriptionProperty?: keyof M;
42
+ }
43
+
44
+ export interface RelationSelectorController {
45
+ items: RelationItem[];
46
+ isLoading: boolean;
47
+ error: Error | undefined;
48
+ search: (searchString: string) => void;
49
+ loadMore: () => void;
50
+ hasMore: boolean;
51
+ entityToRelationItem: (entity: Entity<any>, relation: EntityRelation) => RelationItem;
52
+ }
53
+
54
+ const DEFAULT_PAGE_SIZE = 10;
55
+
56
+ /**
57
+ * Hook to manage relation selection with data fetching from Rebase data source
58
+ */
59
+ export function useRelationSelector<M extends Record<string, any> = any>(
60
+ {
61
+ path,
62
+ collection,
63
+ fixedFilter,
64
+ pageSize = DEFAULT_PAGE_SIZE,
65
+ getLabelFromEntity,
66
+ getDescriptionFromEntity,
67
+ descriptionProperty
68
+ }: UseRelationSelectorProps<M>
69
+ ): RelationSelectorController {
70
+
71
+ const dataClient = useData();
72
+
73
+ const [items, setItems] = useState<RelationItem[]>([]);
74
+ const [isLoading, setIsLoading] = useState(false);
75
+ const isLoadingRef = useRef(false);
76
+ const [error, setError] = useState<Error | undefined>();
77
+ const [hasMore, setHasMore] = useState(true);
78
+ const [currentSearch, setCurrentSearch] = useState<string>("");
79
+ const [limit, setLimit] = useState<number>(pageSize);
80
+
81
+ const unsubscribeRef = useRef<(() => void) | null>(null);
82
+ const searchTimeoutRef = useRef<NodeJS.Timeout | null>(null);
83
+
84
+ const setLoading = useCallback((loading: boolean) => {
85
+ isLoadingRef.current = loading;
86
+ setIsLoading(loading);
87
+ }, []);
88
+
89
+ // Function to convert entity to RelationItem
90
+ const entityToRelationItem = useCallback((entity: Entity<M>, relation?: EntityRelation): RelationItem => {
91
+ let label: string;
92
+ let description: string | undefined;
93
+
94
+ if (getLabelFromEntity) {
95
+ label = getLabelFromEntity(entity);
96
+ } else {
97
+ // Fallback: try common label properties
98
+ const commonLabelProps = ["name", "title", "label", "displayName"];
99
+ let foundProp: string | undefined;
100
+
101
+ if (entity.values) {
102
+ foundProp = commonLabelProps.find(prop => entity.values[prop] != null && entity.values[prop] !== "");
103
+ }
104
+
105
+ if (foundProp && entity.values[foundProp]) {
106
+ label = String(entity.values[foundProp]);
107
+ } else {
108
+ // Ultimate fallback: use entity ID
109
+ label = String(entity.id);
110
+ }
111
+ }
112
+
113
+ if (getDescriptionFromEntity) {
114
+ description = getDescriptionFromEntity(entity);
115
+ } else if (descriptionProperty && entity.values && entity.values[descriptionProperty]) {
116
+ description = String(entity.values[descriptionProperty]);
117
+ }
118
+
119
+ return {
120
+ id: entity.id,
121
+ label,
122
+ description,
123
+ data: entity,
124
+ relation: relation ? relation : new EntityRelation(entity.id, path)
125
+ };
126
+ }, [getLabelFromEntity, getDescriptionFromEntity, descriptionProperty]);
127
+
128
+ // Clean up any existing subscription
129
+ const cleanupSubscription = useCallback(() => {
130
+ if (unsubscribeRef.current) {
131
+ unsubscribeRef.current();
132
+ unsubscribeRef.current = null;
133
+ }
134
+ }, []);
135
+
136
+ const fetchData = useCallback(() => {
137
+ cleanupSubscription();
138
+ setError(undefined);
139
+ setLoading(true);
140
+
141
+ // fixedFilter is already FilterValues — pass directly
142
+ const whereParams = fixedFilter && Object.keys(fixedFilter).length > 0 ? fixedFilter : undefined;
143
+
144
+ const onEntitiesUpdate = (res: { data: Entity<M>[], meta: { hasMore: boolean } }) => {
145
+ const newItems = res.data.map((e) => entityToRelationItem(e));
146
+ setItems(newItems);
147
+ setHasMore(res.meta.hasMore);
148
+ setLoading(false);
149
+ };
150
+
151
+ const onErrorUpdate = (fetchError: Error) => {
152
+ console.error("useRelationSelector: Error fetching data:", fetchError);
153
+ setError(fetchError);
154
+ setLoading(false);
155
+ };
156
+
157
+ const accessor = dataClient.collection(path);
158
+
159
+ let unsubscribe: (() => void) | undefined;
160
+
161
+ if (accessor.listen) {
162
+ unsubscribe = accessor.listen({
163
+ where: whereParams,
164
+ limit: limit,
165
+ orderBy: undefined,
166
+ searchString: currentSearch
167
+ }, (res) => onEntitiesUpdate({ data: res.data as Entity<M>[],
168
+ meta: res.meta }), onErrorUpdate);
169
+ } else {
170
+ accessor.find({
171
+ where: whereParams,
172
+ limit: limit,
173
+ offset: 0,
174
+ orderBy: undefined,
175
+ searchString: currentSearch
176
+ })
177
+ .then((res) => onEntitiesUpdate({ data: res.data as Entity<M>[],
178
+ meta: res.meta }))
179
+ .catch(onErrorUpdate);
180
+ unsubscribe = () => {};
181
+ }
182
+
183
+ unsubscribeRef.current = unsubscribe || null;
184
+ }, [dataClient, path, fixedFilter, limit, currentSearch, entityToRelationItem, cleanupSubscription, setLoading]);
185
+
186
+ // Search function with debouncing
187
+ const search = useCallback((searchString: string) => {
188
+ // Clear existing timeout
189
+ if (searchTimeoutRef.current) {
190
+ clearTimeout(searchTimeoutRef.current);
191
+ }
192
+
193
+ // Debounce search
194
+ searchTimeoutRef.current = setTimeout(() => {
195
+ setLimit(pageSize);
196
+ setCurrentSearch(searchString);
197
+ }, searchString.trim() ? 300 : 0);
198
+ }, [pageSize]);
199
+
200
+ // Load more function
201
+ const loadMore = useCallback(() => {
202
+ if (!isLoadingRef.current && hasMore && items.length > 0) {
203
+ setLoading(true);
204
+ setLimit(prev => prev + pageSize);
205
+ }
206
+ }, [hasMore, items.length, pageSize, setLoading]);
207
+
208
+ // Load initial data and update upon changes
209
+ useEffect(() => {
210
+ fetchData();
211
+
212
+ return () => {
213
+ cleanupSubscription();
214
+ };
215
+ }, [fetchData]);
216
+
217
+ useEffect(() => {
218
+ return () => {
219
+ if (searchTimeoutRef.current) {
220
+ clearTimeout(searchTimeoutRef.current);
221
+ }
222
+ };
223
+ }, []);
224
+
225
+ return useMemo(() => ({
226
+ items,
227
+ isLoading,
228
+ error,
229
+ search,
230
+ loadMore,
231
+ hasMore,
232
+ entityToRelationItem
233
+ }), [items, isLoading, error, search, loadMore, hasMore, entityToRelationItem]);
234
+ }
@@ -0,0 +1,45 @@
1
+ export * from "./data/useData";
2
+ export * from "./data/useCollection";
3
+ export * from "./data/useFetch";
4
+ export * from "./data/useRelationSelector";
5
+ export * from "./data/save";
6
+ export * from "./data/delete";
7
+
8
+
9
+ export * from "./useUnsavedChangesDialog";
10
+
11
+ export * from "./useStorageSource";
12
+ export * from "./useAuthController";
13
+ export * from "./useAuthSubscription";
14
+ export * from "./useDialogsController";
15
+ export * from "./useAdminModeController";
16
+ export * from "./useBuildAdminModeController";
17
+ export * from "./useEffectiveRoleController";
18
+ export * from "./useBuildEffectiveRoleController";
19
+ export * from "./useRebaseContext";
20
+ export * from "./useSnackbarController";
21
+ export * from "./useModeController";
22
+ export * from "./useClipboard";
23
+ export * from "./useLargeLayout";
24
+ export * from "./useCollapsedGroups";
25
+
26
+ export * from "./useBrowserTitleAndIcon";
27
+ export * from "./useSlot";
28
+ export * from "./useCustomizationController";
29
+
30
+ export * from "./useBuildLocalConfigurationPersistence";
31
+ export * from "./useBuildModeController";
32
+
33
+ export * from "./useRebaseRegistry";
34
+ export * from "./useBackendStorageSource";
35
+ export * from "./usePermissions";
36
+
37
+ export * from "./ApiConfigContext";
38
+
39
+ export * from "./useTranslation";
40
+ export * from "./useRebaseClient";
41
+
42
+ export * from "./useAnalyticsController";
43
+ export * from "./useUserConfigurationPersistence";
44
+ export * from "./useResolvedComponent";
45
+ export * from "./useComponentOverride";
@@ -0,0 +1,23 @@
1
+ import { AdminModeControllerContext } from "../contexts/AdminModeController";
2
+ import { useContext } from "react";
3
+
4
+ /**
5
+ * Use this controller to change the admin mode (developer vs editor)
6
+ * @group Hooks and utilities
7
+ */
8
+ export interface AdminModeController {
9
+ mode: "content" | "studio" | "settings";
10
+ setMode: (mode: "content" | "studio" | "settings") => void;
11
+ }
12
+
13
+ /**
14
+ * Hook to retrieve the current admin mode ("developer" | "editor"), and `setMode`
15
+ * to change it.
16
+ *
17
+ * Consider that in order to use this hook you need to have a parent
18
+ * `Rebase`
19
+ *
20
+ * @see AdminModeController
21
+ * @group Hooks and utilities
22
+ */
23
+ export const useAdminModeController = () => useContext(AdminModeControllerContext);
@@ -0,0 +1,8 @@
1
+ import { useContext } from "react";
2
+ import { AnalyticsController } from "@rebasepro/types";
3
+ import { AnalyticsContext } from "../contexts/AnalyticsContext";
4
+
5
+ /**
6
+ * @group Hooks and utilities
7
+ */
8
+ export const useAnalyticsController = (): AnalyticsController => useContext(AnalyticsContext);
@@ -0,0 +1,14 @@
1
+ import { AuthController, User } from "@rebasepro/types";
2
+ import { useContext } from "react";
3
+ import { AuthControllerContext } from "../contexts/AuthControllerContext";
4
+
5
+ /**
6
+ * Hook to retrieve the AuthContext.
7
+ *
8
+ * Consider that in order to use this hook you need to have a parent
9
+ * `Rebase`
10
+ *
11
+ * @see AuthController
12
+ * @group Hooks and utilities
13
+ */
14
+ export const useAuthController = <USER extends User = User, AuthControllerType extends AuthController<USER> = AuthController<USER>>(): AuthControllerType => useContext(AuthControllerContext) as AuthControllerType;