@rebasepro/admin 0.6.0 → 0.7.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 +7 -3
- package/dist/{CollectionEditorDialog-jA-PLh6N.js → CollectionEditorDialog-iGgS4rLX.js} +233 -152
- package/dist/CollectionEditorDialog-iGgS4rLX.js.map +1 -0
- package/dist/{PropertyEditView-DEUddmg_.js → PropertyEditView-C9yhLyja.js} +129 -15
- package/dist/PropertyEditView-C9yhLyja.js.map +1 -0
- package/dist/{CollectionsStudioView-UKouBPOm.js → RouterCollectionsStudioView-Dqx27GD8.js} +61 -39
- package/dist/RouterCollectionsStudioView-Dqx27GD8.js.map +1 -0
- package/dist/collection_editor/extensibility_types.d.ts +135 -0
- package/dist/collection_editor/index.d.ts +6 -0
- package/dist/collection_editor/serializable_types.d.ts +374 -0
- package/dist/collection_editor/serializable_utils.d.ts +50 -0
- package/dist/collection_editor/ui/collection_editor/CollectionEditorDialog.d.ts +2 -1
- package/dist/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +12 -1
- package/dist/collection_editor/ui/collection_editor/CollectionStudioView.d.ts +13 -1
- package/dist/collection_editor/ui/collection_editor/CollectionsStudioView.d.ts +24 -2
- package/dist/collection_editor/ui/collection_editor/DisplaySettingsForm.d.ts +2 -1
- package/dist/collection_editor/ui/collection_editor/GeneralSettingsForm.d.ts +4 -1
- package/dist/collection_editor/ui/collection_editor/PropertyEditView.d.ts +12 -1
- package/dist/collection_editor/ui/collection_editor/RouterCollectionStudioView.d.ts +15 -0
- package/dist/collection_editor/ui/collection_editor/RouterCollectionsStudioView.d.ts +14 -0
- package/dist/collection_editor/useJsonCollectionsConfigController.d.ts +37 -0
- package/dist/collection_editor/useSafeSnackbarController.d.ts +2 -0
- package/dist/collection_editor_ui.d.ts +4 -0
- package/dist/collection_editor_ui.js +33 -4
- package/dist/collection_editor_ui.js.map +1 -0
- package/dist/components/EntityCollectionView/EntityBoardCard.d.ts +3 -3
- package/dist/components/EntityCollectionView/hooks/useKanbanDragAndDrop.d.ts +3 -3
- package/dist/components/EntityEditView.d.ts +3 -2
- package/dist/components/RebaseCMS.d.ts +1 -1
- package/dist/editor/editor.d.ts +10 -4
- package/dist/editor.js +6 -4
- package/dist/editor.js.map +1 -1
- package/dist/form/EntityForm.d.ts +10 -9
- package/dist/form/EntityFormBinding.d.ts +27 -0
- package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +2 -2
- package/dist/form/form_utils.d.ts +8 -0
- package/dist/form/index.d.ts +5 -1
- package/dist/{history-BL49TIDb.js → history-ChHgyyvM.js} +2 -2
- package/dist/{history-BL49TIDb.js.map → history-ChHgyyvM.js.map} +1 -1
- package/dist/hooks/navigation/contexts/CollectionRegistryContext.d.ts +1 -1
- package/dist/hooks/navigation/useAsyncResolver.d.ts +26 -0
- package/dist/hooks/navigation/useBuildCollectionRegistryController.d.ts +2 -0
- package/dist/hooks/navigation/useTopLevelNavigation.d.ts +1 -1
- package/dist/hooks/navigation/utils.d.ts +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -2
- package/dist/routes/CustomViewRoute.d.ts +2 -1
- package/dist/{src-BJ8fkM8y.js → src-DOX2nOyh.js} +1634 -1330
- package/dist/src-DOX2nOyh.js.map +1 -0
- package/dist/types/components/EntityFormActionsProps.d.ts +1 -1
- package/dist/types/components/EntityFormProps.d.ts +59 -0
- package/dist/types/fields.d.ts +6 -1
- package/dist/util/dataControllerAdapter.d.ts +30 -0
- package/dist/util/navigation_utils.d.ts +1 -1
- package/dist/util/propertyConfigMapper.d.ts +17 -0
- package/package.json +18 -18
- package/src/collection_editor/extensibility_types.ts +168 -0
- package/src/collection_editor/index.ts +46 -0
- package/src/collection_editor/serializable_types.ts +487 -0
- package/src/collection_editor/serializable_utils.ts +588 -0
- package/src/collection_editor/ui/AddKanbanColumnAction.tsx +2 -0
- package/src/collection_editor/ui/collection_editor/AICollectionGeneratorPopover.tsx +4 -4
- package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +134 -70
- package/src/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.tsx +95 -78
- package/src/collection_editor/ui/collection_editor/CollectionStudioView.tsx +23 -20
- package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +79 -27
- package/src/collection_editor/ui/collection_editor/DisplaySettingsForm.tsx +9 -7
- package/src/collection_editor/ui/collection_editor/GeneralSettingsForm.tsx +22 -5
- package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +3 -3
- package/src/collection_editor/ui/collection_editor/PropertyEditView.tsx +155 -10
- package/src/collection_editor/ui/collection_editor/PropertyFieldPreview.tsx +6 -6
- package/src/collection_editor/ui/collection_editor/PropertyTree.tsx +1 -0
- package/src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx +44 -0
- package/src/collection_editor/ui/collection_editor/RouterCollectionsStudioView.tsx +41 -0
- package/src/collection_editor/ui/collection_editor/properties/EnumPropertyField.tsx +4 -3
- package/src/collection_editor/useJsonCollectionsConfigController.tsx +286 -0
- package/src/collection_editor/useSafeSnackbarController.ts +9 -0
- package/src/collection_editor_ui.ts +6 -0
- package/src/components/ArrayContainer.tsx +5 -5
- package/src/components/DefaultDrawer.tsx +13 -3
- package/src/components/DrawerNavigationGroup.tsx +2 -2
- package/src/components/DrawerNavigationItem.tsx +4 -4
- package/src/components/EntityCollectionView/EntityBoardCard.tsx +6 -6
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +14 -11
- package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +40 -190
- package/src/components/EntityCollectionView/EntityCollectionListView.tsx +54 -183
- package/src/components/EntityCollectionView/hooks/useKanbanDragAndDrop.ts +6 -6
- package/src/components/EntityEditView.tsx +5 -4
- package/src/components/EntityEditViewFormActions.tsx +32 -26
- package/src/components/HomePage/ContentHomePage.tsx +13 -4
- package/src/components/HomePage/NavigationCard.tsx +8 -14
- package/src/components/HomePage/NavigationGroup.tsx +1 -1
- package/src/components/HomePage/SmallNavigationCard.tsx +1 -5
- package/src/components/RebaseCMS.tsx +3 -2
- package/src/components/RebaseNavigation.tsx +71 -7
- package/src/components/SelectableTable/SelectableTable.tsx +2 -2
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +28 -21
- package/src/components/SideDialogs.tsx +0 -7
- package/src/components/common/default_entity_actions.tsx +18 -10
- package/src/contexts/BreacrumbsContext.tsx +15 -6
- package/src/editor/editor.tsx +14 -8
- package/src/editor/selectors/ai-selector.tsx +0 -1
- package/src/form/EntityForm.tsx +191 -428
- package/src/form/EntityFormActions.tsx +33 -47
- package/src/form/EntityFormBinding.tsx +323 -0
- package/src/form/components/FormLayout.tsx +1 -1
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +4 -4
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +7 -2
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +1 -1
- package/src/form/form_utils.ts +163 -0
- package/src/form/index.tsx +14 -5
- package/src/hooks/navigation/contexts/CollectionRegistryContext.tsx +2 -2
- package/src/hooks/navigation/contexts/NavigationStateContext.tsx +2 -2
- package/src/hooks/navigation/contexts/UrlContext.tsx +1 -2
- package/src/hooks/navigation/useAsyncResolver.ts +100 -0
- package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +15 -8
- package/src/hooks/navigation/useBuildNavigationStateController.tsx +12 -8
- package/src/hooks/navigation/useNavigationResolution.ts +33 -0
- package/src/hooks/navigation/useResolvedCollections.ts +29 -71
- package/src/hooks/navigation/useResolvedViews.tsx +40 -70
- package/src/hooks/navigation/useTopLevelNavigation.ts +20 -16
- package/src/hooks/navigation/utils.ts +7 -4
- package/src/index.ts +7 -2
- package/src/routes/CustomViewRoute.tsx +14 -6
- package/src/types/components/EntityFormActionsProps.tsx +1 -1
- package/src/types/components/EntityFormProps.tsx +68 -0
- package/src/types/fields.tsx +7 -1
- package/src/util/dataControllerAdapter.ts +93 -0
- package/src/util/navigation_utils.ts +1 -1
- package/src/util/propertyConfigMapper.ts +134 -0
- package/LICENSE +0 -21
- package/dist/CollectionEditorDialog-jA-PLh6N.js.map +0 -1
- package/dist/CollectionsStudioView-UKouBPOm.js.map +0 -1
- package/dist/PropertyEditView-DEUddmg_.js.map +0 -1
- package/dist/components/EntityCollectionView/Board.d.ts +0 -3
- package/dist/components/EntityCollectionView/BoardColumn.d.ts +0 -42
- package/dist/components/EntityCollectionView/BoardColumnTitle.d.ts +0 -9
- package/dist/components/EntityCollectionView/BoardSortableList.d.ts +0 -14
- package/dist/components/EntityCollectionView/board_types.d.ts +0 -105
- package/dist/hooks/navigation/useNavigationRegistry.d.ts +0 -12
- package/dist/hooks/navigation/useNavigationURLs.d.ts +0 -11
- package/dist/src-BJ8fkM8y.js.map +0 -1
- package/src/components/EntityCollectionView/Board.tsx +0 -475
- package/src/components/EntityCollectionView/BoardColumn.tsx +0 -165
- package/src/components/EntityCollectionView/BoardColumnTitle.tsx +0 -46
- package/src/components/EntityCollectionView/BoardSortableList.tsx +0 -177
- package/src/components/EntityCollectionView/board_types.ts +0 -113
- package/src/hooks/navigation/useNavigationRegistry.ts +0 -164
- package/src/hooks/navigation/useNavigationURLs.ts +0 -56
|
@@ -3,9 +3,9 @@ import React, { useEffect, useMemo, useState } from "react";
|
|
|
3
3
|
|
|
4
4
|
import { Field, getIn, useFormex } from "@rebasepro/formex";
|
|
5
5
|
import {
|
|
6
|
-
useLargeLayout
|
|
7
|
-
useSnackbarController
|
|
6
|
+
useLargeLayout
|
|
8
7
|
} from "@rebasepro/core";
|
|
8
|
+
import { useSafeSnackbarController } from "../../useSafeSnackbarController";
|
|
9
9
|
import { ErrorBoundary } from "@rebasepro/ui";
|
|
10
10
|
import {
|
|
11
11
|
Button,
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
defaultBorderMixin,
|
|
17
17
|
FileSearchIcon,
|
|
18
18
|
IconButton,
|
|
19
|
+
iconSize,
|
|
19
20
|
PlusIcon,
|
|
20
21
|
Tooltip,
|
|
21
22
|
Typography
|
|
@@ -29,6 +30,8 @@ import { PropertyTree } from "./PropertyTree";
|
|
|
29
30
|
import { GetCodeDialog } from "./GetCodeDialog";
|
|
30
31
|
import { useAIModifiedPaths } from "./AIModifiedPathsContext";
|
|
31
32
|
import { useCollectionsConfigController } from "../../useCollectionsConfigController";
|
|
33
|
+
import type { PropertyTypePreset, PropertyType } from "../../extensibility_types";
|
|
34
|
+
import type { SerializableProperty, SerializableCollection } from "../../serializable_types";
|
|
32
35
|
|
|
33
36
|
type PropertyOrBuilder = Property | Record<string, unknown>;
|
|
34
37
|
|
|
@@ -43,7 +46,15 @@ type CollectionEditorFormProps = {
|
|
|
43
46
|
getData?: () => Promise<object[]>;
|
|
44
47
|
doCollectionInference?: (collection: EntityCollection) => Promise<Partial<EntityCollection> | null> | undefined;
|
|
45
48
|
propertyConfigs: Record<string, PropertyConfig>;
|
|
46
|
-
|
|
49
|
+
propertyTypePresets?: PropertyTypePreset[];
|
|
50
|
+
hiddenPropertyTypes?: PropertyType[];
|
|
51
|
+
renderExtraPropertyFields?: (params: {
|
|
52
|
+
metadata: Record<string, unknown>;
|
|
53
|
+
onMetadataChange: (key: string, value: unknown) => void;
|
|
54
|
+
property: SerializableProperty;
|
|
55
|
+
collection: SerializableCollection;
|
|
56
|
+
}) => React.ReactNode;
|
|
57
|
+
standalone?: boolean;
|
|
47
58
|
};
|
|
48
59
|
|
|
49
60
|
export function CollectionPropertiesEditorForm({
|
|
@@ -56,7 +67,11 @@ export function CollectionPropertiesEditorForm({
|
|
|
56
67
|
getUser,
|
|
57
68
|
getData,
|
|
58
69
|
doCollectionInference,
|
|
59
|
-
propertyConfigs
|
|
70
|
+
propertyConfigs,
|
|
71
|
+
propertyTypePresets,
|
|
72
|
+
hiddenPropertyTypes,
|
|
73
|
+
renderExtraPropertyFields,
|
|
74
|
+
standalone,
|
|
60
75
|
|
|
61
76
|
}: CollectionEditorFormProps) {
|
|
62
77
|
|
|
@@ -69,11 +84,12 @@ export function CollectionPropertiesEditorForm({
|
|
|
69
84
|
dirty
|
|
70
85
|
} = useFormex<EntityCollection>();
|
|
71
86
|
|
|
72
|
-
const snackbarController =
|
|
73
|
-
const
|
|
87
|
+
const snackbarController = useSafeSnackbarController();
|
|
88
|
+
const configControllerFromContext = useCollectionsConfigController();
|
|
89
|
+
const configController = standalone ? { readOnly: false } : configControllerFromContext;
|
|
74
90
|
|
|
75
91
|
const largeLayout = useLargeLayout();
|
|
76
|
-
const asDialog = !largeLayout
|
|
92
|
+
const asDialog = standalone ? false : !largeLayout
|
|
77
93
|
|
|
78
94
|
// index of the selected property within the namespace
|
|
79
95
|
const [selectedPropertyIndex, setSelectedPropertyIndex] = useState<number | undefined>();
|
|
@@ -115,7 +131,7 @@ export function CollectionPropertiesEditorForm({
|
|
|
115
131
|
promise.then((newCollection) => {
|
|
116
132
|
|
|
117
133
|
if (!newCollection) {
|
|
118
|
-
snackbarController
|
|
134
|
+
snackbarController?.open?.({
|
|
119
135
|
type: "error",
|
|
120
136
|
message: "Could not infer properties from data"
|
|
121
137
|
});
|
|
@@ -222,7 +238,7 @@ export function CollectionPropertiesEditorForm({
|
|
|
222
238
|
|
|
223
239
|
// Check if there are any changes (new properties or modified nested properties)
|
|
224
240
|
if (allNewPropertyKeys.length === 0) {
|
|
225
|
-
snackbarController
|
|
241
|
+
snackbarController?.open?.({
|
|
226
242
|
type: "info",
|
|
227
243
|
message: "No new properties found in existing data"
|
|
228
244
|
});
|
|
@@ -241,7 +257,7 @@ export function CollectionPropertiesEditorForm({
|
|
|
241
257
|
updatePropertiesOrder(updatedPropertiesOrder);
|
|
242
258
|
setInferredPropertyKeys(allNewPropertyKeys);
|
|
243
259
|
|
|
244
|
-
snackbarController
|
|
260
|
+
snackbarController?.open?.({
|
|
245
261
|
type: "success",
|
|
246
262
|
message: `Added ${allNewPropertyKeys.length} new ${allNewPropertyKeys.length === 1 ? "property" : "properties"}`
|
|
247
263
|
});
|
|
@@ -404,98 +420,87 @@ export function CollectionPropertiesEditorForm({
|
|
|
404
420
|
};
|
|
405
421
|
|
|
406
422
|
const body = (
|
|
407
|
-
<div className={"grid grid-cols-12
|
|
423
|
+
<div className={"grid grid-cols-12 h-full bg-surface-50 dark:bg-surface-800"}>
|
|
408
424
|
<div className={cls(
|
|
409
|
-
"bg-surface-50 dark:bg-surface-
|
|
410
|
-
"p-4 md:p-8",
|
|
411
|
-
"col-span-12 lg:col-span-5 h-full overflow-auto",
|
|
425
|
+
"col-span-12 lg:col-span-5 h-full flex flex-col bg-surface-50 dark:bg-surface-800",
|
|
412
426
|
!asDialog && "border-r " + defaultBorderMixin
|
|
413
427
|
)}>
|
|
414
|
-
|
|
415
|
-
<div className="flex
|
|
416
|
-
|
|
417
|
-
<div className="grow mb-4">
|
|
418
|
-
|
|
428
|
+
{/* Sidebar Header */}
|
|
429
|
+
<div className={cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px] shrink-0", defaultBorderMixin)}>
|
|
430
|
+
<div className="flex-grow min-w-0 pr-2">
|
|
419
431
|
<Field
|
|
420
432
|
name={"name"}
|
|
421
433
|
as={DebouncedTextField}
|
|
422
434
|
invisible={true}
|
|
423
435
|
className="-ml-1"
|
|
424
|
-
inputClassName="text-
|
|
436
|
+
inputClassName="text-sm font-semibold truncate bg-transparent border-0 outline-none focus:ring-0"
|
|
425
437
|
placeholder={"Collection name"}
|
|
426
438
|
size={"small"}
|
|
427
439
|
required
|
|
428
440
|
error={Boolean(errors?.name)}/>
|
|
429
|
-
|
|
430
|
-
{owner &&
|
|
431
|
-
<Typography variant={"body2"}
|
|
432
|
-
className={"ml-2"}
|
|
433
|
-
color={"secondary"}>
|
|
434
|
-
Created by {owner.displayName}
|
|
435
|
-
</Typography>}
|
|
436
441
|
</div>
|
|
437
442
|
|
|
438
|
-
{extraIcon && <div className="
|
|
439
|
-
{extraIcon}
|
|
440
|
-
</div>}
|
|
443
|
+
{extraIcon && <div className="flex-shrink-0">{extraIcon}</div>}
|
|
441
444
|
|
|
442
|
-
<div className="
|
|
443
|
-
{
|
|
444
|
-
asChild={true}>
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
445
|
+
<div className="flex items-center gap-1.5 flex-shrink-0 ml-2">
|
|
446
|
+
{inferPropertiesFromData && (
|
|
447
|
+
<Tooltip title={"Add new properties based on data"} asChild={true}>
|
|
448
|
+
<IconButton
|
|
449
|
+
size="small"
|
|
450
|
+
disabled={inferringProperties}
|
|
451
|
+
onClick={inferPropertiesFromData}
|
|
452
|
+
>
|
|
453
|
+
{inferringProperties ? <CircularProgress size={"smallest"}/> : <FileSearchIcon size={iconSize.smallest}/>}
|
|
454
|
+
</IconButton>
|
|
455
|
+
</Tooltip>
|
|
456
|
+
)}
|
|
457
|
+
<Tooltip title={"Add new property"} asChild={true}>
|
|
454
458
|
<IconButton
|
|
455
|
-
|
|
456
|
-
disabled={inferringProperties}
|
|
457
|
-
onClick={inferPropertiesFromData}>
|
|
458
|
-
{inferringProperties ? <CircularProgress size={"small"}/> : <FileSearchIcon/>}
|
|
459
|
-
</IconButton>
|
|
460
|
-
</Tooltip>}
|
|
461
|
-
<Tooltip title={"Add new property"}
|
|
462
|
-
asChild={true}>
|
|
463
|
-
<Button
|
|
459
|
+
size="small"
|
|
464
460
|
disabled={configController?.readOnly}
|
|
465
|
-
onClick={() => setNewPropertyDialogOpen(true)}
|
|
466
|
-
|
|
467
|
-
|
|
461
|
+
onClick={() => setNewPropertyDialogOpen(true)}
|
|
462
|
+
>
|
|
463
|
+
<PlusIcon size={iconSize.smallest}/>
|
|
464
|
+
</IconButton>
|
|
468
465
|
</Tooltip>
|
|
469
466
|
</div>
|
|
470
467
|
</div>
|
|
471
468
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
469
|
+
{/* Sidebar Content */}
|
|
470
|
+
<div className="flex-grow overflow-y-auto p-3 no-scrollbar space-y-3 bg-surface-50 dark:bg-surface-800">
|
|
471
|
+
{owner && (
|
|
472
|
+
<div className="px-1 py-0.5">
|
|
473
|
+
<Typography variant="body2" color="secondary">
|
|
474
|
+
Created by {owner.displayName}
|
|
475
|
+
</Typography>
|
|
476
|
+
</div>
|
|
477
|
+
)}
|
|
478
|
+
<ErrorBoundary>
|
|
479
|
+
<PropertyTree
|
|
480
|
+
inferredPropertyKeys={inferredPropertyKeys}
|
|
481
|
+
selectedPropertyKey={selectedPropertyKey ? getFullId(selectedPropertyKey, selectedPropertyNamespace) : undefined}
|
|
482
|
+
properties={values.properties}
|
|
483
|
+
additionalFields={values.additionalFields}
|
|
484
|
+
propertiesOrder={usedPropertiesOrder}
|
|
485
|
+
onPropertyClick={onPropertyClick}
|
|
486
|
+
onPropertyMove={onPropertyMove}
|
|
487
|
+
onPropertyRemove={(isNewCollection || (inferredPropertyKeys && inferredPropertyKeys.length > 0)) && !configController?.readOnly ? deleteProperty : undefined}
|
|
488
|
+
errors={errors}/>
|
|
489
|
+
</ErrorBoundary>
|
|
490
|
+
|
|
491
|
+
<Button className={"w-full"}
|
|
492
|
+
variant="outlined"
|
|
493
|
+
color="neutral"
|
|
494
|
+
disabled={configController?.readOnly}
|
|
495
|
+
onClick={() => setNewPropertyDialogOpen(true)}
|
|
496
|
+
startIcon={<PlusIcon/>}>
|
|
497
|
+
Add new property
|
|
498
|
+
</Button>
|
|
499
|
+
</div>
|
|
495
500
|
</div>
|
|
496
501
|
|
|
497
502
|
{!asDialog &&
|
|
498
|
-
<div className={"col-span-12 lg:col-span-7 p-4 md:py-8 md:px-4 h-full overflow-auto"}>
|
|
503
|
+
<div className={"col-span-12 lg:col-span-7 p-4 md:py-8 md:px-4 h-full overflow-auto bg-surface-50 dark:bg-surface-800"}>
|
|
499
504
|
<div
|
|
500
505
|
className="sticky top-8 min-h-full w-full flex flex-col justify-center">
|
|
501
506
|
|
|
@@ -519,6 +524,10 @@ export function CollectionPropertiesEditorForm({
|
|
|
519
524
|
initialErrors={initialErrors}
|
|
520
525
|
getData={getData}
|
|
521
526
|
propertyConfigs={propertyConfigs}
|
|
527
|
+
propertyTypePresets={propertyTypePresets}
|
|
528
|
+
hiddenPropertyTypes={hiddenPropertyTypes}
|
|
529
|
+
renderExtraPropertyFields={renderExtraPropertyFields}
|
|
530
|
+
collectionValues={values}
|
|
522
531
|
|
|
523
532
|
/>}
|
|
524
533
|
|
|
@@ -563,6 +572,10 @@ export function CollectionPropertiesEditorForm({
|
|
|
563
572
|
initialErrors={initialErrors}
|
|
564
573
|
getData={getData}
|
|
565
574
|
propertyConfigs={propertyConfigs}
|
|
575
|
+
propertyTypePresets={propertyTypePresets}
|
|
576
|
+
hiddenPropertyTypes={hiddenPropertyTypes}
|
|
577
|
+
renderExtraPropertyFields={renderExtraPropertyFields}
|
|
578
|
+
collectionValues={values}
|
|
566
579
|
|
|
567
580
|
onCancel={closePropertyDialog}
|
|
568
581
|
onOkClicked={asDialog
|
|
@@ -589,6 +602,10 @@ export function CollectionPropertiesEditorForm({
|
|
|
589
602
|
getData={getData}
|
|
590
603
|
allowDataInference={!isNewCollection}
|
|
591
604
|
propertyConfigs={propertyConfigs}
|
|
605
|
+
propertyTypePresets={propertyTypePresets}
|
|
606
|
+
hiddenPropertyTypes={hiddenPropertyTypes}
|
|
607
|
+
renderExtraPropertyFields={renderExtraPropertyFields}
|
|
608
|
+
collectionValues={values}
|
|
592
609
|
existingPropertyKeys={values.propertiesOrder as string[]}/>
|
|
593
610
|
|
|
594
611
|
<ErrorBoundary>
|
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
|
|
2
2
|
import { useUnsavedChangesDialog, UnsavedChangesDialog } from "@rebasepro/core";
|
|
3
|
-
import { useUrlController } from "../../_cms_internals";
|
|
4
3
|
import * as React from "react";
|
|
5
|
-
import { useState
|
|
4
|
+
import { useState } from "react";
|
|
6
5
|
import { Button, PlusIcon, Typography } from "@rebasepro/ui";
|
|
7
|
-
import { useSnackbarController } from "@rebasepro/core";
|
|
8
6
|
import { CollectionEditorDialogProps } from "./CollectionEditorDialog";
|
|
9
7
|
import { AIModifiedPathsProvider } from "./AIModifiedPathsContext";
|
|
10
8
|
import { CollectionEditor } from "./CollectionEditorDialog";
|
|
11
|
-
import {
|
|
9
|
+
import type { EntityCollection } from "@rebasepro/types";
|
|
12
10
|
|
|
13
11
|
export type CollectionStudioViewProps = Omit<CollectionEditorDialogProps, "open" | "isNewCollection" | "editedCollectionId" | "handleClose" | "handleCancel"> & {
|
|
14
12
|
collectionId?: string | "new";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Called after a successful save.
|
|
16
|
+
* Receives the saved collection (or undefined if dismissed without saving).
|
|
17
|
+
* When not provided, save completes silently.
|
|
18
|
+
*/
|
|
19
|
+
onSave?: (collection?: EntityCollection) => void;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Called when the user cancels editing.
|
|
23
|
+
* When not provided, cancel is a no-op.
|
|
24
|
+
*/
|
|
25
|
+
onCancel?: () => void;
|
|
15
26
|
};
|
|
16
27
|
|
|
17
|
-
export function CollectionStudioView({ collectionId, ...props }: CollectionStudioViewProps) {
|
|
18
|
-
const snackbarController = useSnackbarController();
|
|
19
|
-
const navigate = useNavigate();
|
|
20
|
-
const urlController = useUrlController();
|
|
28
|
+
export function CollectionStudioView({ collectionId, onSave, onCancel, ...props }: CollectionStudioViewProps) {
|
|
21
29
|
|
|
22
30
|
// Form state from the editor
|
|
23
31
|
const [formDirty, setFormDirty] = useState<boolean>(false);
|
|
@@ -28,12 +36,11 @@ export function CollectionStudioView({ collectionId, ...props }: CollectionStudi
|
|
|
28
36
|
() => setFormDirty(false)
|
|
29
37
|
);
|
|
30
38
|
|
|
31
|
-
// Map collectionId from URL params if missing? We can pass it directly.
|
|
32
39
|
const activeCollectionId = collectionId;
|
|
33
40
|
|
|
34
41
|
const handleCancelClick = () => {
|
|
35
42
|
if (!formDirty) {
|
|
36
|
-
|
|
43
|
+
onCancel?.();
|
|
37
44
|
} else {
|
|
38
45
|
setCancelRequested(true);
|
|
39
46
|
triggerDialog();
|
|
@@ -41,7 +48,7 @@ export function CollectionStudioView({ collectionId, ...props }: CollectionStudi
|
|
|
41
48
|
};
|
|
42
49
|
|
|
43
50
|
return (
|
|
44
|
-
<div className="flex-grow flex flex-col h-full w-full bg-
|
|
51
|
+
<div className="flex-grow flex flex-col h-full w-full bg-surface-50 dark:bg-surface-800">
|
|
45
52
|
<AIModifiedPathsProvider>
|
|
46
53
|
{activeCollectionId ? (
|
|
47
54
|
<CollectionEditor
|
|
@@ -55,13 +62,9 @@ export function CollectionStudioView({ collectionId, ...props }: CollectionStudi
|
|
|
55
62
|
handleClose={(savedCollection) => {
|
|
56
63
|
setFormDirty(false);
|
|
57
64
|
if (savedCollection) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
});
|
|
62
|
-
if (activeCollectionId === "new") {
|
|
63
|
-
navigate(urlController.buildAppUrlPath(`s/schema/${savedCollection.slug}`));
|
|
64
|
-
}
|
|
65
|
+
setTimeout(() => {
|
|
66
|
+
onSave?.(savedCollection);
|
|
67
|
+
}, 0);
|
|
65
68
|
}
|
|
66
69
|
}}
|
|
67
70
|
setFormDirty={setFormDirty}
|
|
@@ -73,7 +76,7 @@ export function CollectionStudioView({ collectionId, ...props }: CollectionStudi
|
|
|
73
76
|
</Typography>
|
|
74
77
|
<Button
|
|
75
78
|
disabled={props.configController?.readOnly}
|
|
76
|
-
onClick={() =>
|
|
79
|
+
onClick={() => onSave?.()}
|
|
77
80
|
>
|
|
78
81
|
<PlusIcon/>
|
|
79
82
|
Add new collection
|
|
@@ -86,7 +89,7 @@ export function CollectionStudioView({ collectionId, ...props }: CollectionStudi
|
|
|
86
89
|
handleOk={() => {
|
|
87
90
|
dialogProps.handleOk();
|
|
88
91
|
if (cancelRequested) {
|
|
89
|
-
|
|
92
|
+
onCancel?.();
|
|
90
93
|
setCancelRequested(false);
|
|
91
94
|
}
|
|
92
95
|
}}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { IconForView } from "@rebasepro/core";
|
|
3
|
-
;
|
|
4
|
-
import { useUrlController } from "../../_cms_internals";
|
|
5
3
|
import React, { useState, useEffect } from "react";
|
|
6
|
-
import { useLocation, useNavigate } from "react-router-dom";
|
|
7
4
|
import {
|
|
8
5
|
Button,
|
|
9
6
|
cls,
|
|
@@ -15,26 +12,70 @@ import {
|
|
|
15
12
|
Tooltip,
|
|
16
13
|
Typography
|
|
17
14
|
} from "@rebasepro/ui";
|
|
18
|
-
;
|
|
15
|
+
import type { EntityCollection } from "@rebasepro/types";
|
|
19
16
|
import { CollectionsConfigController } from "../../types/config_controller";
|
|
20
17
|
import { CollectionStudioView } from "./CollectionStudioView";
|
|
18
|
+
import type { CollectionEditorExtensionProps } from "../../extensibility_types";
|
|
21
19
|
|
|
22
|
-
export interface CollectionsStudioViewProps {
|
|
20
|
+
export interface CollectionsStudioViewProps extends CollectionEditorExtensionProps {
|
|
23
21
|
configController: CollectionsConfigController;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Collections to show in the sidebar.
|
|
25
|
+
* When provided, overrides the collections from `configController`.
|
|
26
|
+
* Use this to control exactly which collections the editor displays.
|
|
27
|
+
*/
|
|
28
|
+
collections?: EntityCollection[];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Controlled active collection ID.
|
|
32
|
+
* When provided together with `onActiveCollectionChange`,
|
|
33
|
+
* navigation is fully external — no internal state is used.
|
|
34
|
+
*/
|
|
35
|
+
activeCollectionId?: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Called when the user clicks a collection, "new", or navigates.
|
|
39
|
+
* When provided, the component is fully controlled (no internal state).
|
|
40
|
+
* When not provided, the component manages selection via internal state.
|
|
41
|
+
*
|
|
42
|
+
* Pass `"new"` to create a new collection. Pass `undefined` to deselect.
|
|
43
|
+
*/
|
|
44
|
+
onActiveCollectionChange?: (collectionId: string | undefined) => void;
|
|
24
45
|
}
|
|
25
46
|
|
|
26
|
-
export function CollectionsStudioView({
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
47
|
+
export function CollectionsStudioView({
|
|
48
|
+
configController,
|
|
49
|
+
collections: collectionsProp,
|
|
50
|
+
activeCollectionId: controlledActiveId,
|
|
51
|
+
onActiveCollectionChange,
|
|
52
|
+
propertyTypePresets,
|
|
53
|
+
hiddenPropertyTypes,
|
|
54
|
+
renderExtraPropertyFields,
|
|
55
|
+
renderExtraCollectionFields,
|
|
56
|
+
visibleTabs,
|
|
57
|
+
standalone,
|
|
58
|
+
}: CollectionsStudioViewProps) {
|
|
30
59
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
60
|
+
// ── Navigation state ────────────────────────────────────────────────
|
|
61
|
+
// If onActiveCollectionChange is provided, the component is controlled.
|
|
62
|
+
// Otherwise, use internal state.
|
|
63
|
+
const [internalActiveId, setInternalActiveId] = useState<string | undefined>(undefined);
|
|
64
|
+
const isControlled = onActiveCollectionChange !== undefined;
|
|
65
|
+
const activeCollectionId = isControlled ? controlledActiveId : internalActiveId;
|
|
34
66
|
|
|
35
|
-
|
|
36
|
-
|
|
67
|
+
const setActiveCollectionId = (id: string | undefined) => {
|
|
68
|
+
if (isControlled) {
|
|
69
|
+
onActiveCollectionChange?.(id);
|
|
70
|
+
} else {
|
|
71
|
+
setInternalActiveId(id);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// ── Collections list ────────────────────────────────────────────────
|
|
76
|
+
const collections = collectionsProp ?? configController.collections ?? [];
|
|
37
77
|
|
|
78
|
+
// ── Sidebar sizing ──────────────────────────────────────────────────
|
|
38
79
|
const [sidebarSize, setSidebarSize] = useState(() => {
|
|
39
80
|
try {
|
|
40
81
|
const saved = localStorage.getItem("rebase_collections_editor_sidebar_size");
|
|
@@ -52,19 +93,17 @@ export function CollectionsStudioView({ configController }: CollectionsStudioVie
|
|
|
52
93
|
}
|
|
53
94
|
}, [sidebarSize]);
|
|
54
95
|
|
|
55
|
-
const collections = configController.collections || [];
|
|
56
|
-
|
|
57
96
|
return (
|
|
58
|
-
<div className="flex h-full w-full bg-
|
|
97
|
+
<div className="flex h-full w-full bg-surface-50 dark:bg-surface-800 overflow-hidden text-text-primary dark:text-text-primary-dark">
|
|
59
98
|
<ResizablePanels
|
|
60
99
|
orientation="horizontal"
|
|
61
100
|
panelSizePercent={sidebarSize}
|
|
62
101
|
onPanelSizeChange={setSidebarSize}
|
|
63
102
|
minPanelSizePx={220}
|
|
64
103
|
firstPanel={
|
|
65
|
-
<div className={cls("flex flex-col h-full w-full bg-
|
|
104
|
+
<div className={cls("flex flex-col h-full w-full bg-surface-50 dark:bg-surface-800 border-r", defaultBorderMixin)}>
|
|
66
105
|
<div className={cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin)}>
|
|
67
|
-
<Typography variant="caption" className="font-
|
|
106
|
+
<Typography variant="caption" className="font-semibold text-[11px] uppercase tracking-wider text-surface-400 dark:text-surface-400">
|
|
68
107
|
Collections
|
|
69
108
|
</Typography>
|
|
70
109
|
<Tooltip title={configController.readOnly ? configController.readOnlyReason || "Read only" : "Add collection"}>
|
|
@@ -72,7 +111,7 @@ export function CollectionsStudioView({ configController }: CollectionsStudioVie
|
|
|
72
111
|
<IconButton
|
|
73
112
|
size="small"
|
|
74
113
|
disabled={configController.readOnly}
|
|
75
|
-
onClick={() =>
|
|
114
|
+
onClick={() => setActiveCollectionId("new")}
|
|
76
115
|
className={activeCollectionId === "new" ? "text-primary dark:text-primary-dark" : "text-text-secondary dark:text-text-secondary-dark"}
|
|
77
116
|
>
|
|
78
117
|
<PlusIcon size={iconSize.smallest}/>
|
|
@@ -95,18 +134,18 @@ export function CollectionsStudioView({ configController }: CollectionsStudioVie
|
|
|
95
134
|
return (
|
|
96
135
|
<div
|
|
97
136
|
key={collectionKey}
|
|
98
|
-
onClick={() =>
|
|
137
|
+
onClick={() => setActiveCollectionId(collectionKey)}
|
|
99
138
|
className={cls(
|
|
100
|
-
"flex items-center gap-
|
|
139
|
+
"flex items-center gap-2.5 px-3 h-[30px] cursor-pointer rounded-lg text-[13px] font-medium transition-colors",
|
|
101
140
|
isSelected
|
|
102
|
-
? "bg-primary/
|
|
103
|
-
: "hover:bg-
|
|
141
|
+
? "bg-primary/8 text-primary dark:bg-primary/10 dark:text-primary-light font-semibold"
|
|
142
|
+
: "hover:bg-primary/5 dark:hover:bg-primary/5 text-surface-700 dark:text-surface-300 hover:text-surface-900 dark:hover:text-white"
|
|
104
143
|
)}
|
|
105
144
|
>
|
|
106
|
-
<IconForView collectionOrView={collection} size={"
|
|
145
|
+
<IconForView collectionOrView={collection} size={"smallest"} className={cls(
|
|
107
146
|
isSelected
|
|
108
147
|
? "text-primary dark:text-primary-light"
|
|
109
|
-
: "text-
|
|
148
|
+
: "text-surface-500 dark:text-text-secondary-dark"
|
|
110
149
|
)}/>
|
|
111
150
|
<span className="truncate flex-1">
|
|
112
151
|
{collection.name || collection.slug}
|
|
@@ -125,6 +164,19 @@ export function CollectionsStudioView({ configController }: CollectionsStudioVie
|
|
|
125
164
|
key={activeCollectionId}
|
|
126
165
|
configController={configController}
|
|
127
166
|
collectionId={activeCollectionId}
|
|
167
|
+
onSave={(savedCollection) => {
|
|
168
|
+
// After creating a new collection, switch to it
|
|
169
|
+
if (activeCollectionId === "new" && savedCollection?.slug) {
|
|
170
|
+
setActiveCollectionId(savedCollection.slug);
|
|
171
|
+
}
|
|
172
|
+
}}
|
|
173
|
+
onCancel={() => setActiveCollectionId(undefined)}
|
|
174
|
+
propertyTypePresets={propertyTypePresets}
|
|
175
|
+
hiddenPropertyTypes={hiddenPropertyTypes}
|
|
176
|
+
renderExtraPropertyFields={renderExtraPropertyFields}
|
|
177
|
+
renderExtraCollectionFields={renderExtraCollectionFields}
|
|
178
|
+
visibleTabs={visibleTabs}
|
|
179
|
+
standalone={standalone}
|
|
128
180
|
/>
|
|
129
181
|
) : (
|
|
130
182
|
<div className="flex flex-col items-center justify-center h-full gap-4">
|
|
@@ -133,7 +185,7 @@ export function CollectionsStudioView({ configController }: CollectionsStudioVie
|
|
|
133
185
|
</Typography>
|
|
134
186
|
<Button
|
|
135
187
|
disabled={configController.readOnly}
|
|
136
|
-
onClick={() =>
|
|
188
|
+
onClick={() => setActiveCollectionId("new")}
|
|
137
189
|
>
|
|
138
190
|
<PlusIcon/>
|
|
139
191
|
Add new collection
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { FieldCaption } from "../../_cms_internals";
|
|
3
3
|
import React, { useMemo, useState } from "react";
|
|
4
|
-
import {
|
|
4
|
+
import { useCustomizationController } from "@rebasepro/core";
|
|
5
5
|
import { getFieldConfig } from "../../../components/field_configs";
|
|
6
6
|
import { PropertyConfigBadge } from "../../../components/PropertyConfigBadge";
|
|
7
7
|
import { EntityCollection, Property } from "@rebasepro/types";
|
|
@@ -27,9 +27,11 @@ import { useCollectionsConfigController } from "../../useCollectionsConfigContro
|
|
|
27
27
|
import { unslugify } from "@rebasepro/utils";
|
|
28
28
|
|
|
29
29
|
export function DisplaySettingsForm({
|
|
30
|
-
expandKanban
|
|
30
|
+
expandKanban,
|
|
31
|
+
standalone,
|
|
31
32
|
}: {
|
|
32
33
|
expandKanban?: boolean;
|
|
34
|
+
standalone?: boolean;
|
|
33
35
|
}) {
|
|
34
36
|
|
|
35
37
|
const {
|
|
@@ -41,9 +43,9 @@ export function DisplaySettingsForm({
|
|
|
41
43
|
|
|
42
44
|
const [orderPropertyDialogOpen, setOrderPropertyDialogOpen] = useState(false);
|
|
43
45
|
|
|
44
|
-
const authController = useAuthController();
|
|
45
46
|
const customizationController = useCustomizationController();
|
|
46
|
-
const
|
|
47
|
+
const configControllerFromContext = useCollectionsConfigController();
|
|
48
|
+
const configController = standalone ? { readOnly: false } : configControllerFromContext;
|
|
47
49
|
|
|
48
50
|
// Get text properties (for orderProperty - uses string fractional indexing keys)
|
|
49
51
|
const textProperties = useMemo(() => {
|
|
@@ -118,7 +120,7 @@ export function DisplaySettingsForm({
|
|
|
118
120
|
}
|
|
119
121
|
const prop = textProperties.find(p => p.key === value);
|
|
120
122
|
if (!prop) return "Select a property";
|
|
121
|
-
const fieldConfig = getFieldConfig(prop.property, customizationController
|
|
123
|
+
const fieldConfig = getFieldConfig(prop.property, customizationController?.propertyConfigs ?? {});
|
|
122
124
|
return (
|
|
123
125
|
<div className="flex items-center gap-2">
|
|
124
126
|
<PropertyConfigBadge propertyConfig={fieldConfig}/>
|
|
@@ -139,7 +141,7 @@ export function DisplaySettingsForm({
|
|
|
139
141
|
) : undefined}
|
|
140
142
|
>
|
|
141
143
|
{textProperties.map((prop) => {
|
|
142
|
-
const fieldConfig = getFieldConfig(prop.property, customizationController
|
|
144
|
+
const fieldConfig = getFieldConfig(prop.property, customizationController?.propertyConfigs ?? {});
|
|
143
145
|
return (
|
|
144
146
|
<SelectItem key={prop.key} value={prop.key}>
|
|
145
147
|
<div className="flex items-center gap-3">
|
|
@@ -205,7 +207,7 @@ hideFromCollection: true }
|
|
|
205
207
|
autoUpdateId={false}
|
|
206
208
|
inArray={false}
|
|
207
209
|
allowDataInference={false}
|
|
208
|
-
propertyConfigs={customizationController
|
|
210
|
+
propertyConfigs={customizationController?.propertyConfigs ?? {}}
|
|
209
211
|
|
|
210
212
|
existingPropertyKeys={Object.keys(values.properties ?? {})}
|
|
211
213
|
onPropertyChanged={({ id, property }) => {
|