@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.
- package/README.md +4 -1
- package/dist/components/index.d.ts +0 -3
- package/dist/contexts/AuthControllerContext.d.ts +2 -1
- package/dist/contexts/CustomizationControllerContext.d.ts +2 -1
- package/dist/contexts/DialogsProvider.d.ts +2 -1
- package/dist/contexts/RebaseDataContext.d.ts +10 -1
- package/dist/contexts/StorageSourceContext.d.ts +11 -1
- package/dist/core/RebaseProps.d.ts +2 -12
- package/dist/debug/index.d.ts +19 -0
- package/dist/debug.js +3631 -0
- package/dist/hooks/data/delete.d.ts +16 -7
- package/dist/hooks/data/save.d.ts +18 -4
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useAdminModeController.d.ts +5 -4
- package/dist/hooks/useNavigationGroupLabel.d.ts +17 -0
- package/dist/hooks/useStudioBridge.d.ts +33 -0
- package/dist/index.es.js +2599 -6980
- package/dist/index.es.js.map +1 -1
- package/dist/vitePlugin.d.ts +22 -0
- package/dist/vitePlugin.js +40 -1
- package/package.json +42 -31
- package/src/auth/api.ts +0 -134
- package/src/auth/index.ts +0 -26
- package/src/auth/types.ts +0 -115
- package/src/auth/useRebaseAuthController.ts +0 -345
- package/src/collections/collection_view_config.ts +0 -76
- package/src/collections/entity-display-cache.ts +0 -195
- package/src/collections/entity-display.ts +0 -77
- package/src/collections/entity_image_preview.ts +0 -61
- package/src/collections/filter-operator-resolution.ts +0 -119
- package/src/collections/form-layout.ts +0 -394
- package/src/collections/index.ts +0 -13
- package/src/collections/navigation_from_path.ts +0 -123
- package/src/collections/navigation_utils.ts +0 -171
- package/src/collections/parent_references_from_path.ts +0 -59
- package/src/collections/property-path.ts +0 -30
- package/src/collections/property_presentation.ts +0 -264
- package/src/collections/summary-property.ts +0 -114
- package/src/collections/title-property.ts +0 -312
- package/src/components/AIIcon.tsx +0 -47
- package/src/components/AuthSimulationMenu.tsx +0 -0
- package/src/components/ConfirmationDialog.tsx +0 -48
- package/src/components/Debug/UIReferenceView.tsx +0 -1053
- package/src/components/Debug/UIStyleGuide.tsx +0 -159
- package/src/components/Debug/collection-views/CardViewDemo.tsx +0 -87
- package/src/components/Debug/collection-views/CollectionTableDemo.tsx +0 -107
- package/src/components/Debug/collection-views/KanbanBoardDemo.tsx +0 -89
- package/src/components/Debug/collection-views/index.ts +0 -3
- package/src/components/Debug/collection-views/sample_data.ts +0 -44
- package/src/components/Debug/crm-dashboard/CalendarWidget.tsx +0 -387
- package/src/components/Debug/crm-dashboard/CrmDashboardDemo.tsx +0 -1100
- package/src/components/Debug/crm-dashboard/DashboardMetrics.tsx +0 -131
- package/src/components/Debug/crm-dashboard/PipelineOverview.tsx +0 -288
- package/src/components/Debug/crm-dashboard/RecentActivity.tsx +0 -213
- package/src/components/Debug/crm-dashboard/TaskFilters.tsx +0 -182
- package/src/components/Debug/crm-dashboard/TaskTable.tsx +0 -425
- package/src/components/Debug/crm-dashboard/TasksView.tsx +0 -234
- package/src/components/ErrorTooltip.tsx +0 -12
- package/src/components/ErrorView.tsx +0 -94
- package/src/components/LanguageToggle.tsx +0 -59
- package/src/components/LoginView/LoginView.tsx +0 -1235
- package/src/components/LoginView/index.ts +0 -2
- package/src/components/LoginView/oauth-redirect-flow.ts +0 -184
- package/src/components/NotFoundPage.tsx +0 -27
- package/src/components/RebaseAuth.tsx +0 -27
- package/src/components/RebaseLogo.tsx +0 -88
- package/src/components/SchemaDriftBanner.tsx +0 -102
- package/src/components/UnsavedChangesDialog.tsx +0 -46
- package/src/components/UserDisplay.tsx +0 -54
- package/src/components/UserSelectPopover.tsx +0 -399
- package/src/components/UserSettingsView.tsx +0 -318
- package/src/components/common/index.ts +0 -6
- package/src/components/common/table_height.tsx +0 -21
- package/src/components/common/types.tsx +0 -66
- package/src/components/common/useColumnsIds.tsx +0 -261
- package/src/components/common/useDataTableController.tsx +0 -571
- package/src/components/common/useDebouncedData.ts +0 -49
- package/src/components/common/useScrollRestoration.tsx +0 -68
- package/src/components/index.tsx +0 -26
- package/src/contexts/AdminModeController.tsx +0 -11
- package/src/contexts/AnalyticsContext.tsx +0 -4
- package/src/contexts/AuthControllerContext.tsx +0 -4
- package/src/contexts/CollectionResolverContext.tsx +0 -24
- package/src/contexts/CollectionScopeContext.tsx +0 -68
- package/src/contexts/ComponentOverrideContext.tsx +0 -81
- package/src/contexts/CustomizationControllerContext.tsx +0 -4
- package/src/contexts/DataDriverContext.tsx +0 -4
- package/src/contexts/DataSourcesContext.tsx +0 -36
- package/src/contexts/DatabaseAdminContext.tsx +0 -4
- package/src/contexts/DialogsProvider.tsx +0 -56
- package/src/contexts/EffectiveRoleController.tsx +0 -12
- package/src/contexts/ModeController.tsx +0 -11
- package/src/contexts/RebaseClientInstanceContext.tsx +0 -7
- package/src/contexts/RebaseDataContext.tsx +0 -4
- package/src/contexts/SnackbarProvider.tsx +0 -15
- package/src/contexts/StorageSourceContext.tsx +0 -4
- package/src/contexts/StorageSourcesContext.tsx +0 -30
- package/src/contexts/UserConfigurationPersistenceContext.tsx +0 -4
- package/src/contexts/index.ts +0 -18
- package/src/core/PluginLifecycleManager.tsx +0 -96
- package/src/core/PluginProviderStack.tsx +0 -40
- package/src/core/Rebase.tsx +0 -488
- package/src/core/RebaseProps.tsx +0 -333
- package/src/core/RebaseRouter.tsx +0 -18
- package/src/core/RebaseRoutes.tsx +0 -24
- package/src/core/index.tsx +0 -5
- package/src/hooks/ApiConfigContext.tsx +0 -83
- package/src/hooks/data/collectionQuery.ts +0 -55
- package/src/hooks/data/delete.ts +0 -56
- package/src/hooks/data/save.ts +0 -78
- package/src/hooks/data/useCollection.tsx +0 -183
- package/src/hooks/data/useData.tsx +0 -18
- package/src/hooks/data/useDataOrder.ts +0 -26
- package/src/hooks/data/useFetch.tsx +0 -156
- package/src/hooks/data/useRelationSelector.tsx +0 -280
- package/src/hooks/index.tsx +0 -45
- package/src/hooks/useAdminModeController.tsx +0 -23
- package/src/hooks/useAnalyticsController.tsx +0 -8
- package/src/hooks/useAuthController.tsx +0 -15
- package/src/hooks/useAuthSubscription.ts +0 -130
- package/src/hooks/useBackendStorageSource.ts +0 -295
- package/src/hooks/useBridgeRegistration.tsx +0 -32
- package/src/hooks/useBrowserTitleAndIcon.tsx +0 -23
- package/src/hooks/useBuildAdminModeController.tsx +0 -29
- package/src/hooks/useBuildEffectiveRoleController.tsx +0 -30
- package/src/hooks/useBuildLocalConfigurationPersistence.tsx +0 -83
- package/src/hooks/useBuildModeController.tsx +0 -73
- package/src/hooks/useClipboard.tsx +0 -158
- package/src/hooks/useCollapsedGroups.ts +0 -116
- package/src/hooks/useComponentOverride.tsx +0 -59
- package/src/hooks/useCustomizationController.tsx +0 -14
- package/src/hooks/useDialogsController.tsx +0 -15
- package/src/hooks/useEffectiveRoleController.tsx +0 -10
- package/src/hooks/useLargeLayout.tsx +0 -65
- package/src/hooks/useModeController.tsx +0 -23
- package/src/hooks/useNavigationBlocker.tsx +0 -108
- package/src/hooks/usePermissions.ts +0 -45
- package/src/hooks/useRebaseClient.tsx +0 -10
- package/src/hooks/useRebaseContext.tsx +0 -84
- package/src/hooks/useRebaseRegistry.tsx +0 -91
- package/src/hooks/useResolvedComponent.tsx +0 -195
- package/src/hooks/useSlot.tsx +0 -64
- package/src/hooks/useSnackbarController.tsx +0 -80
- package/src/hooks/useStorageSource.tsx +0 -11
- package/src/hooks/useStudioBridge.tsx +0 -258
- package/src/hooks/useTranslation.ts +0 -38
- package/src/hooks/useUnsavedChangesDialog.tsx +0 -67
- package/src/hooks/useUserConfigurationPersistence.tsx +0 -11
- package/src/i18n/RebaseI18nProvider.tsx +0 -159
- package/src/index.ts +0 -32
- package/src/internal/common.tsx +0 -28
- package/src/internal/useRestoreScroll.tsx +0 -69
- package/src/locales/de.ts +0 -941
- package/src/locales/en.ts +0 -1021
- package/src/locales/es.ts +0 -962
- package/src/locales/fr.ts +0 -941
- package/src/locales/hi.ts +0 -941
- package/src/locales/it.ts +0 -941
- package/src/locales/pt.ts +0 -949
- package/src/util/constants.ts +0 -7
- package/src/util/createFormexStub.tsx +0 -66
- package/src/util/entity_cache.ts +0 -242
- package/src/util/enums.ts +0 -34
- package/src/util/icon_list.ts +0 -22
- package/src/util/icons.tsx +0 -129
- package/src/util/index.ts +0 -13
- package/src/util/local_storage.ts +0 -66
- package/src/util/previews.ts +0 -127
- package/src/util/useStorageUploadController.tsx +0 -354
- package/src/vitePlugin.ts +0 -215
- /package/dist/{components/Debug → debug}/UIReferenceView.d.ts +0 -0
- /package/dist/{components/Debug → debug}/UIStyleGuide.d.ts +0 -0
- /package/dist/{components/Debug → debug}/collection-views/CardViewDemo.d.ts +0 -0
- /package/dist/{components/Debug → debug}/collection-views/CollectionTableDemo.d.ts +0 -0
- /package/dist/{components/Debug → debug}/collection-views/KanbanBoardDemo.d.ts +0 -0
- /package/dist/{components/Debug → debug}/collection-views/index.d.ts +0 -0
- /package/dist/{components/Debug → debug}/collection-views/sample_data.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/CalendarWidget.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/CrmDashboardDemo.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/DashboardMetrics.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/PipelineOverview.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/RecentActivity.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/TaskFilters.d.ts +0 -0
- /package/dist/{components/Debug → debug}/crm-dashboard/TaskTable.d.ts +0 -0
- /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
|
-
}
|