@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,123 +0,0 @@
1
-
2
- type EntityCustomView<M extends Record<string, unknown> = Record<string, unknown>> = { key: string; [key: string]: unknown };
3
- import { getCollectionPathsCombinations, removeInitialAndTrailingSlashes } from "./navigation_utils";
4
- import { getSubcollections } from "@rebasepro/common";
5
- import type { AdminCollection } from "@rebasepro/cms-types";
6
-
7
- export type NavigationViewInternal<M extends Record<string, unknown> = Record<string, unknown>> =
8
- | NavigationViewEntityInternal<M>
9
- | NavigationViewCollectionInternal<M>
10
- | NavigationViewEntityCustomInternal<M>;
11
-
12
- export interface NavigationViewEntityInternal<M extends Record<string, unknown>> {
13
- type: "entity";
14
- entityId: string | number;
15
- slug: string;
16
- path: string;
17
- parentCollection: AdminCollection<M>;
18
- }
19
-
20
- export interface NavigationViewCollectionInternal<M extends Record<string, unknown>> {
21
- type: "collection";
22
- id: string;
23
- slug: string;
24
- path: string;
25
- collection: AdminCollection<M>;
26
- }
27
-
28
- export interface NavigationViewEntityCustomInternal<M extends Record<string, unknown>> {
29
- type: "custom_view";
30
- slug: string;
31
- path: string;
32
- entityId: string | number;
33
- view: EntityCustomView<M>;
34
- }
35
-
36
- export function getNavigationEntriesFromPath(props: {
37
- path: string,
38
- collections: AdminCollection[] | undefined,
39
- currentFullPath?: string,
40
- contextEntityViews?: EntityCustomView[]
41
- }): NavigationViewInternal[] {
42
-
43
- const {
44
- path,
45
- collections = [],
46
- currentFullPath
47
- } = props;
48
-
49
- const subpaths = removeInitialAndTrailingSlashes(path).split("/");
50
- const subpathCombinations = getCollectionPathsCombinations(subpaths);
51
-
52
- const result: NavigationViewInternal[] = [];
53
- for (let i = 0; i < subpathCombinations.length; i++) {
54
- const subpathCombination = subpathCombinations[i];
55
-
56
- const collection = collections && collections.find((entry) => entry.slug === subpathCombination);
57
-
58
- if (collection) {
59
- const collectionPath = currentFullPath && currentFullPath.length > 0
60
- ? (currentFullPath + "/" + collection.slug)
61
- : collection.slug;
62
- result.push({
63
- type: "collection",
64
- id: collection.slug,
65
- slug: collectionPath,
66
- path: collectionPath,
67
- collection
68
- });
69
- const restOfThePath = removeInitialAndTrailingSlashes(removeInitialAndTrailingSlashes(path).replace(subpathCombination, ""));
70
- const nextSegments = restOfThePath.length > 0 ? restOfThePath.split("/") : [];
71
- if (nextSegments.length > 0) {
72
- const entityId = nextSegments[0];
73
- const path = collectionPath + "/" + entityId;
74
- result.push({
75
- type: "entity",
76
- entityId,
77
- slug: collectionPath,
78
- path,
79
- parentCollection: collection
80
- });
81
- if (nextSegments.length > 1) {
82
- const newPath = nextSegments.slice(1).join("/");
83
- if (!collection) {
84
- throw Error("collection not found resolving path: " + collection);
85
- }
86
- const entityViews = collection.entityViews;
87
- const customView = entityViews && entityViews
88
- .map((entry) => resolveEntityView(entry, props.contextEntityViews))
89
- .filter((v): v is EntityCustomView => v != null)
90
- .find((entry) => entry.key === newPath);
91
- const subcollections = getSubcollections(collection);
92
- if (customView) {
93
- result.push({
94
- type: "custom_view",
95
- slug: collectionPath,
96
- entityId: entityId,
97
- path: path + "/" + customView.key,
98
- view: customView
99
- });
100
- } else if (subcollections) {
101
- result.push(...getNavigationEntriesFromPath({
102
- path: newPath,
103
- collections: subcollections,
104
- currentFullPath: path,
105
- contextEntityViews: props.contextEntityViews
106
- }));
107
- }
108
- }
109
- }
110
- break;
111
- }
112
-
113
- }
114
- return result;
115
- }
116
-
117
- function resolveEntityView(entityView: string | EntityCustomView, contextEntityViews?: EntityCustomView[]): EntityCustomView | undefined {
118
- if (typeof entityView === "string") {
119
- return contextEntityViews?.find((entry) => entry.key === entityView);
120
- } else {
121
- return entityView;
122
- }
123
- }
@@ -1,171 +0,0 @@
1
-
2
-
3
- import { getSubcollections } from "@rebasepro/common";
4
- import type { AdminCollection } from "@rebasepro/cms-types";
5
-
6
- export function removeInitialAndTrailingSlashes(s: string): string {
7
- return removeInitialSlash(removeTrailingSlash(s));
8
- }
9
-
10
- export function removeInitialSlash(s: string) {
11
- if (s.startsWith("/"))
12
- return s.slice(1);
13
- else return s;
14
- }
15
-
16
- export function removeTrailingSlash(s: string) {
17
- if (s.endsWith("/"))
18
- return s.slice(0, -1);
19
- else return s;
20
- }
21
-
22
- export function addInitialSlash(s: string) {
23
- if (s.startsWith("/"))
24
- return s;
25
- else return `/${s}`;
26
- }
27
-
28
- export function getLastSegment(path: string) {
29
- const cleanPath = removeInitialAndTrailingSlashes(path);
30
- if (cleanPath.includes("/")) {
31
- const segments = cleanPath.split("/");
32
- return segments[segments.length - 1];
33
- }
34
- return cleanPath;
35
- }
36
-
37
- export function resolveCollectionPathIds(path: string, allCollections: AdminCollection[]): string {
38
- let remainingPath = removeInitialAndTrailingSlashes(path);
39
- if (!remainingPath) {
40
- return "";
41
- }
42
-
43
- let currentCollections: AdminCollection[] | undefined = allCollections;
44
- const resolvedPathParts: string[] = [];
45
-
46
- while (remainingPath.length > 0) {
47
- if (!currentCollections || currentCollections.length === 0) {
48
- // We have remaining path segments but no more collections to match against
49
- console.warn(`resolveCollectionPathIds: Path structure implies subcollections, but none found before segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
50
- resolvedPathParts.push(remainingPath);
51
- remainingPath = ""; // Stop processing
52
- break;
53
- }
54
-
55
- let foundMatch = false;
56
- // Sort potential matches by length descending to prioritize longer matches (e.g., "a/b" over "a")
57
- const potentialMatches: { col: AdminCollection; match: string; }[] = currentCollections
58
- .flatMap(col => [{
59
- col,
60
- match: col.slug
61
- }])
62
- .filter(p => p.match && remainingPath.startsWith(p.match))
63
- .sort((a, b) => b.match.length - a.match.length);
64
-
65
- if (potentialMatches.length > 0) {
66
- const {
67
- col: foundCollection,
68
- match: matchString
69
- } = potentialMatches[0];
70
-
71
- resolvedPathParts.push(foundCollection.slug); // Use the defined path
72
- remainingPath = removeInitialSlash(remainingPath.substring(matchString.length));
73
-
74
- // Check if we are at the end of the path
75
- if (remainingPath.length === 0) {
76
- foundMatch = true;
77
- break; // Path ends with a collection segment
78
- }
79
-
80
- // The next segment must be a entity ID
81
- const idSeparatorIndex = remainingPath.indexOf("/");
82
- let entityId: string | number;
83
- if (idSeparatorIndex > -1) {
84
- entityId = remainingPath.substring(0, idSeparatorIndex);
85
- remainingPath = remainingPath.substring(idSeparatorIndex + 1);
86
- } else {
87
- // This should not happen if the original path is valid (odd segments)
88
- // but handle it defensively: assume the rest is the ID
89
- entityId = remainingPath;
90
- remainingPath = "";
91
- console.warn(`resolveCollectionPathIds: Path seems to end with a entity ID "${entityId}" instead of a collection segment in original path "${path}". This might indicate an invalid input path.`);
92
- // Even if it ends here, we still need to push the ID
93
- }
94
-
95
- resolvedPathParts.push(entityId); // Append entity ID
96
- currentCollections = getSubcollections(foundCollection); // Move to subcollections
97
- foundMatch = true;
98
-
99
- if (!currentCollections && remainingPath.length > 0) {
100
- // Warn if the path continues but no subcollections were defined
101
- console.warn(`resolveCollectionPathIds: Path continues after entity ID "${entityId}", but no subcollections are defined for the preceding collection "${foundCollection.slug}" in path "${path}". Appending remaining original path.`);
102
- resolvedPathParts.push(remainingPath); // Append the rest
103
- remainingPath = ""; // Stop processing
104
- break;
105
- }
106
-
107
- }
108
-
109
- if (!foundMatch) {
110
- // Collection definition not found for the start of the remaining path
111
- console.warn(`resolveCollectionPathIds: Collection definition not found for segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
112
- resolvedPathParts.push(remainingPath); // Append the rest
113
- remainingPath = ""; // Stop processing
114
- break;
115
- }
116
- }
117
-
118
- return resolvedPathParts.join("/");
119
- }
120
-
121
- /**
122
- * Find the corresponding view at any depth for a given path.
123
- * Note that path or segments of the paths can be collection aliases.
124
- * @param slugOrPath
125
- * @param collections
126
- */
127
- export function getCollectionBySlugWithin(slugOrPath: string, collections: AdminCollection[]): AdminCollection | undefined {
128
-
129
- const subpaths = removeInitialAndTrailingSlashes(slugOrPath).split("/");
130
- if (subpaths.length % 2 === 0) {
131
- throw Error(`getCollectionBySlug: Collection paths must have an odd number of segments: ${slugOrPath}`);
132
- }
133
-
134
- const subpathCombinations = getCollectionPathsCombinations(subpaths);
135
- let result: AdminCollection | undefined;
136
- for (let i = 0; i < subpathCombinations.length; i++) {
137
- const subpathCombination = subpathCombinations[i];
138
- const navigationEntry = collections && collections
139
- .sort((a, b) => (a.slug ?? "").localeCompare(b.slug ?? ""))
140
- .find((entry) => entry.slug === subpathCombination);
141
-
142
- if (navigationEntry) {
143
-
144
- if (subpathCombination === slugOrPath) {
145
- result = navigationEntry;
146
- } else if (getSubcollections(navigationEntry).length > 0) {
147
- const newPath = slugOrPath.replace(subpathCombination, "").split("/").slice(2).join("/");
148
- if (newPath.length > 0)
149
- result = getCollectionBySlugWithin(newPath, getSubcollections(navigationEntry));
150
- }
151
- }
152
- if (result) break;
153
- }
154
- return result;
155
- }
156
-
157
- /**
158
- * Get the subcollection combinations from a path:
159
- * "sites/es/locales" => ["sites/es/locales", "sites"]
160
- * @param subpaths
161
- */
162
- export function getCollectionPathsCombinations(subpaths: string[]): string[] {
163
- const entries = subpaths.length > 0 && subpaths.length % 2 === 0 ? subpaths.splice(0, subpaths.length - 1) : subpaths;
164
-
165
- const length = entries.length;
166
- const result: string[] = [];
167
- for (let i = length; i > 0; i = i - 2) {
168
- result.push(entries.slice(0, i).join("/"));
169
- }
170
- return result;
171
- }
@@ -1,59 +0,0 @@
1
- import { EntityReference } from "@rebasepro/types";
2
- import { getCollectionPathsCombinations, removeInitialAndTrailingSlashes } from "./navigation_utils";
3
- import { getSubcollections } from "@rebasepro/common";
4
- import type { AdminCollection } from "@rebasepro/cms-types";
5
-
6
- export function getParentReferencesFromPath(props: {
7
- path: string,
8
- collections: AdminCollection[] | undefined,
9
- currentFullPath?: string,
10
- }): EntityReference[] {
11
-
12
- const {
13
- path,
14
- collections = [],
15
- currentFullPath
16
- } = props;
17
-
18
- const subpaths = removeInitialAndTrailingSlashes(path).split("/");
19
- const subpathCombinations = getCollectionPathsCombinations(subpaths);
20
-
21
- const result: EntityReference[] = [];
22
- for (let i = 0; i < subpathCombinations.length; i++) {
23
- const subpathCombination = subpathCombinations[i];
24
-
25
- const collection: AdminCollection | undefined = collections && collections.find((entry) => entry.slug === subpathCombination);
26
-
27
- // If we find a collection, we add the reference and continue
28
- if (collection) {
29
- const collectionPath = currentFullPath && currentFullPath.length > 0
30
- ? (currentFullPath + "/" + collection.slug) // Use the current full path if provided
31
- : collection.slug;
32
-
33
- const restOfThePath = removeInitialAndTrailingSlashes(removeInitialAndTrailingSlashes(path).replace(subpathCombination, ""));
34
- const nextSegments = restOfThePath.length > 0 ? restOfThePath.split("/") : [];
35
- if (nextSegments.length > 0) {
36
- const entityId = nextSegments[0];
37
- const path = collectionPath + "/" + entityId;
38
- result.push(new EntityReference({ id: entityId,
39
- path: collectionPath }));
40
- if (nextSegments.length > 1) {
41
- const newPath = nextSegments.slice(1).join("/");
42
- if (!collection) {
43
- throw Error("collection not found resolving path: " + collection);
44
- }
45
- if (getSubcollections(collection).length > 0) {
46
- result.push(...getParentReferencesFromPath({
47
- path: newPath,
48
- collections: getSubcollections(collection),
49
- currentFullPath: path
50
- }));
51
- }
52
- }
53
- }
54
- break;
55
- }
56
-
57
- }
58
- return result;
59
- }
@@ -1,30 +0,0 @@
1
- import type { Properties, Property } from "@rebasepro/types";
2
-
3
- /**
4
- * The property at a dotted path, walking `map` children — `address.street`.
5
- *
6
- * The value counterpart is `getValueInPath` in `@rebasepro/utils`; this is the
7
- * schema half, and the two have to be used together. Reading a dotted path off
8
- * an entity while looking its property up with a flat `properties[path]` gives
9
- * the value and `undefined` for how to render it, which is how a declared title
10
- * on a nested field silently fell back to a derived one.
11
- *
12
- * There were three copies of this: one private to `useColumnsIds`, one exported
13
- * from the admin layer, and the flat lookup in the title resolver that was not
14
- * this function at all. This is the one, in the lowest layer that needs it —
15
- * admin re-exports it under the name it already published.
16
- */
17
- export function getPropertyInPath(properties: Properties, path: string): Property | undefined {
18
- if (typeof properties !== "object" || !properties) return undefined;
19
- if (path in properties) {
20
- return (properties as Record<string, Property>)[path];
21
- }
22
- if (path.includes(".")) {
23
- const pathSegments = path.split(".");
24
- const childProperty = (properties as Record<string, Property>)[pathSegments[0]];
25
- if (typeof childProperty === "object" && childProperty?.type === "map" && childProperty.properties) {
26
- return getPropertyInPath(childProperty.properties, pathSegments.slice(1).join("."));
27
- }
28
- }
29
- return undefined;
30
- }
@@ -1,264 +0,0 @@
1
- /**
2
- * Property presentation helpers.
3
- *
4
- * These read a property's admin block — `readOnly`, `disabled.hidden`, the
5
- * declarative `conditions` — and lived in `@rebasepro/common`, which is on the
6
- * backend's dependency path. Nothing in core ever called them: `isReadOnly` and
7
- * `isHidden` are used only by the panel, and `applyPropertyConditions` has no
8
- * production caller at all.
9
- *
10
- * That last one is worth knowing about rather than assuming: the collection editor
11
- * has a whole Conditions UI, `serializable_utils` persists what it writes, and
12
- * `BaseProperty.conditions` documents itself as "evaluated at runtime like property
13
- * builders" — but the evaluator below is reached only from its own tests. It lives
14
- * here because here is where it would be called from once it is wired up.
15
- *
16
- * The one part of `conditions` that *is* applied is the literal case:
17
- * `hidden`/`readOnly`/`disabled` stated as a plain boolean rather than as a rule.
18
- * A literal needs no context, so `isHidden`/`isReadOnly`/`isDisabled` can answer
19
- * it directly, and those three gates are consulted everywhere a field is laid
20
- * out. A *rule* still is not evaluated anywhere in production — the split is
21
- * deliberate, not an oversight: it is the difference between a condition that
22
- * needs an entity to be evaluated against and one that does not.
23
- */
24
- import type { ConditionContext, ConditionRule, EnumValueConfig, Property, PropertyConditions, ReferenceProperty } from "@rebasepro/types";
25
- import type { AdminArrayOptions, AdminReferenceOptions } from "@rebasepro/cms-types";
26
- import { evaluateCondition } from "@rebasepro/common";
27
-
28
- /**
29
- * A condition stated as a literal rather than as a rule.
30
- *
31
- * `PropertyConditions` accepts either, and the two are answered in different
32
- * places: a rule needs a context and so can only be evaluated while rendering a
33
- * particular entity, but a literal is already the answer. Reading it here is
34
- * what makes `hidden: true` work without every gate below having to build a
35
- * condition context first — and without the caller reaching for
36
- * `{ "==": [1, 1] }` to say something it can say with a boolean.
37
- */
38
- function literalCondition(condition: ConditionRule | undefined): boolean {
39
- return condition === true;
40
- }
41
-
42
- export function isReadOnly(property: Property): boolean {
43
- if (property.admin?.readOnly)
44
- return true;
45
- if (literalCondition(property.conditions?.readOnly))
46
- return true;
47
- if (property.type === "date") {
48
- if (property.autoValue)
49
- return true;
50
- }
51
- if (property.type === "reference") {
52
- return !property.path && !("Field" in (property.admin || {}) && property.admin?.Field);
53
- }
54
- return false;
55
- }
56
-
57
- export function isHidden(property: Property): boolean {
58
- if (literalCondition(property.conditions?.hidden)) return true;
59
- return typeof property.admin?.disabled === "object" && Boolean(property.admin?.disabled.hidden);
60
- }
61
-
62
- /**
63
- * Whether the field is disabled by its own declaration, ignoring form state.
64
- *
65
- * The `admin.disabled` block and `conditions.disabled: true` say the same thing
66
- * two ways, so every caller that gated on the first now asks here instead of
67
- * growing a second check of its own.
68
- */
69
- export function isDisabled(property: Property): boolean {
70
- return Boolean(property.admin?.disabled) || literalCondition(property.conditions?.disabled);
71
- }
72
-
73
- export function applyPropertyConditions(
74
- property: Property,
75
- context: ConditionContext
76
- ): Property {
77
- const { conditions } = property;
78
- if (!conditions) return property;
79
-
80
- const result = { ...property };
81
-
82
- // ═══════════════════════════════════════════════════════════════════════
83
- // FIELD STATE CONDITIONS
84
- // ═══════════════════════════════════════════════════════════════════════
85
-
86
- // Evaluate disabled condition
87
- if (conditions.disabled) {
88
- const isDisabled = evaluateCondition(conditions.disabled, context);
89
- if (isDisabled) {
90
- result.admin = result.admin || {};
91
- result.admin.disabled = {
92
- clearOnDisabled: conditions.clearOnDisabled ?? false,
93
- disabledMessage: conditions.disabledMessage,
94
- hidden: false
95
- };
96
- }
97
- }
98
-
99
- // Evaluate hidden condition
100
- if (conditions.hidden) {
101
- const isHidden = evaluateCondition(conditions.hidden, context);
102
- if (isHidden) {
103
- result.admin = result.admin || {};
104
- result.admin.disabled = {
105
- ...(typeof result.admin?.disabled === "object" ? result.admin.disabled : {}),
106
- hidden: true,
107
- clearOnDisabled: conditions.clearOnDisabled ?? false
108
- };
109
- }
110
- }
111
-
112
- // Evaluate readOnly condition
113
- if (conditions.readOnly) {
114
- const isReadOnly = evaluateCondition(conditions.readOnly, context);
115
- if (isReadOnly) {
116
- result.admin = result.admin || {};
117
- result.admin.readOnly = true;
118
- }
119
- }
120
-
121
- // ═══════════════════════════════════════════════════════════════════════
122
- // VALIDATION CONDITIONS
123
- // ═══════════════════════════════════════════════════════════════════════
124
-
125
- // Evaluate required condition
126
- if (conditions.required !== undefined) {
127
- const isRequired = evaluateCondition(conditions.required, context) as boolean;
128
- result.validation = {
129
- ...result.validation,
130
- required: isRequired as boolean | undefined,
131
- requiredMessage: conditions.requiredMessage
132
- };
133
- }
134
-
135
- // ═══════════════════════════════════════════════════════════════════════
136
- // VALUE CONDITIONS
137
- // ═══════════════════════════════════════════════════════════════════════
138
-
139
- // Apply default value for new entities
140
- if (context.isNew && conditions.defaultValue !== undefined) {
141
- result.defaultValue = evaluateCondition(conditions.defaultValue, context) as Property["defaultValue"];
142
- }
143
-
144
- // ═══════════════════════════════════════════════════════════════════════
145
- // ENUM CONDITIONS
146
- // ═══════════════════════════════════════════════════════════════════════
147
-
148
- if ("enum" in result && result.enum && (conditions.enumConditions || conditions.allowedEnumValues || conditions.excludedEnumValues)) {
149
- (result as Record<string, unknown>).enum = applyEnumConditions(
150
- result.enum as EnumValueConfig[],
151
- conditions,
152
- context
153
- );
154
- }
155
-
156
- // ═══════════════════════════════════════════════════════════════════════
157
- // REFERENCE CONDITIONS
158
- // ═══════════════════════════════════════════════════════════════════════
159
-
160
- if (result.type === "reference") {
161
- if (conditions.referencePath) {
162
- (result as ReferenceProperty).path = evaluateCondition(conditions.referencePath, context) as string;
163
- }
164
- if (conditions.referenceFilter) {
165
- result.admin = result.admin || {};
166
- (result.admin as AdminReferenceOptions).fixedFilter =
167
- evaluateCondition(conditions.referenceFilter, context) as AdminReferenceOptions["fixedFilter"];
168
- }
169
- }
170
-
171
- // ═══════════════════════════════════════════════════════════════════════
172
- // ARRAY CONDITIONS
173
- // ═══════════════════════════════════════════════════════════════════════
174
-
175
- if (result.type === "array") {
176
- if (conditions.canAddElements !== undefined) {
177
- result.admin = result.admin || {};
178
- (result.admin as AdminArrayOptions).canAddElements = evaluateCondition(conditions.canAddElements, context) as boolean;
179
- }
180
- if (conditions.sortable !== undefined) {
181
- result.admin = result.admin || {};
182
- (result.admin as AdminArrayOptions).sortable = evaluateCondition(conditions.sortable, context) as boolean;
183
- }
184
- }
185
-
186
- return result;
187
- }
188
-
189
- /**
190
- * Convert an object with numeric keys back to an array.
191
- * Firestore stores arrays as {"0": "a", "1": "b"} to avoid nested arrays.
192
- */
193
- function objectToArray(obj: unknown): string[] {
194
- if (Array.isArray(obj)) return obj.map(String);
195
- if (obj && typeof obj === "object") {
196
- const keys = Object.keys(obj);
197
- if (keys.length > 0 && keys.every(k => !isNaN(Number(k)))) {
198
- return keys
199
- .sort((a, b) => Number(a) - Number(b))
200
- .map(k => (obj as Record<string, unknown>)[k])
201
- .filter((v): v is string => typeof v === "string" || typeof v === "number")
202
- .map(String);
203
- }
204
- }
205
- return [];
206
- }
207
-
208
- /**
209
- * Apply enum-specific conditions to filter and modify enum values.
210
- */
211
- function applyEnumConditions(
212
- enumValues: EnumValueConfig[],
213
- conditions: PropertyConditions,
214
- context: ConditionContext
215
- ): EnumValueConfig[] {
216
- let result = [...enumValues];
217
-
218
- // Apply allowedEnumValues filter
219
- if (conditions.allowedEnumValues) {
220
- const allowed = evaluateCondition(conditions.allowedEnumValues, context);
221
- // Handle both array format and object-with-numeric-keys format (Firestore workaround)
222
- const allowedArray = objectToArray(allowed);
223
- if (allowedArray.length > 0) {
224
- result = result.filter(ev => allowedArray.includes(String(ev.id)));
225
- }
226
- }
227
-
228
- // Apply excludedEnumValues filter
229
- if (conditions.excludedEnumValues) {
230
- const excluded = evaluateCondition(conditions.excludedEnumValues, context);
231
- // Handle both array format and object-with-numeric-keys format
232
- const excludedArray = objectToArray(excluded);
233
- if (excludedArray.length > 0) {
234
- result = result.filter(ev => !excludedArray.includes(String(ev.id)));
235
- }
236
- }
237
-
238
- // Apply individual enum conditions
239
- if (conditions.enumConditions) {
240
- result = result
241
- .map(ev => {
242
- const evConditions = conditions.enumConditions?.[ev.id];
243
- if (!evConditions) return ev;
244
-
245
- // Check hidden condition first
246
- if (evConditions.hidden && evaluateCondition(evConditions.hidden, context)) {
247
- return null; // Will be filtered out
248
- }
249
-
250
- // Check disabled condition
251
- if (evConditions.disabled && evaluateCondition(evConditions.disabled, context)) {
252
- return {
253
- ...ev,
254
- disabled: true
255
- };
256
- }
257
-
258
- return ev;
259
- })
260
- .filter((ev): ev is EnumValueConfig => ev !== null);
261
- }
262
-
263
- return result;
264
- }