@rebasepro/admin 0.12.0 → 0.12.1-canary.g009ed95
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 +1 -1
- package/dist/{CollectionEditorDialog-Dpc75wIZ.js → CollectionEditorDialog-B-iVE9y3.js} +75 -62
- package/dist/{CollectionEditorDialog-Dpc75wIZ.js.map → CollectionEditorDialog-B-iVE9y3.js.map} +1 -1
- package/dist/{PropertyEditView-DH3XZC2x.js → PropertyEditView-DoLEH_bQ.js} +36 -16
- package/dist/{PropertyEditView-DH3XZC2x.js.map → PropertyEditView-DoLEH_bQ.js.map} +1 -1
- package/dist/{RouterCollectionsStudioView-BZLq4MGp.js → RouterCollectionsStudioView-C3669Ezm.js} +5 -5
- package/dist/{RouterCollectionsStudioView-BZLq4MGp.js.map → RouterCollectionsStudioView-C3669Ezm.js.map} +1 -1
- package/dist/collection_editor/serializable_types.d.ts +4 -1
- package/dist/collection_editor/ui/collection_editor/CollectionRelationsTab.d.ts +1 -0
- package/dist/collection_editor/useLocalCollectionsConfigController.d.ts +8 -0
- package/dist/collection_editor_ui.js +5 -5
- package/dist/collection_editor_ui.js.map +1 -1
- package/dist/components/CollectionViewBinding/SplitListCloseButton.d.ts +10 -0
- package/dist/components/CollectionViewBinding/SplitViewContext.d.ts +23 -0
- package/dist/components/CollectionViewBinding/hooks/useKanbanDragAndDrop.d.ts +26 -0
- package/dist/components/DetailViewBinding.d.ts +5 -0
- package/dist/components/EditFormActions.d.ts +12 -1
- package/dist/components/EditViewBinding.d.ts +6 -1
- package/dist/components/EntityIdentityBar.d.ts +53 -0
- package/dist/components/EntityInspector.d.ts +29 -0
- package/dist/components/EntityPreviewNesting.d.ts +25 -0
- package/dist/components/EntityViewBinding.d.ts +9 -0
- package/dist/components/InlineEntityPreview.d.ts +47 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/editor.js +18 -10
- package/dist/editor.js.map +1 -1
- package/dist/{export-BzIpUdSK.js → export-B8DIn-u8.js} +2 -2
- package/dist/{export-BzIpUdSK.js.map → export-B8DIn-u8.js.map} +1 -1
- package/dist/form/EntityForm.d.ts +1 -1
- package/dist/form/EntityFormActions.d.ts +11 -1
- package/dist/form/PropertyFieldBinding.d.ts +1 -1
- package/dist/form/components/FieldBlock.d.ts +30 -0
- package/dist/form/components/FormRail.d.ts +18 -0
- package/dist/form/components/FormSections.d.ts +16 -0
- package/dist/form/components/index.d.ts +3 -2
- package/dist/form/components/useRailVisible.d.ts +24 -0
- package/dist/form/field_bindings/BinaryFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/DateTimeFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/GeopointFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MultiSelectFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/ReadOnlyFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/SelectFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/SwitchFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/TextFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/UserSelectFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/VectorFieldBinding.d.ts +1 -1
- package/dist/{history-g9688IaK.js → history-ClwDEtqN.js} +32 -31
- package/dist/history-ClwDEtqN.js.map +1 -0
- package/dist/hooks/navigation/utils.d.ts +16 -0
- package/dist/hooks/useBuildSidePanel.d.ts +14 -0
- package/dist/hooks/useEntityDisplayTitle.d.ts +18 -0
- package/dist/hooks/useRecordActions.d.ts +17 -0
- package/dist/{import-BjUG-cIY.js → import-Cwzf4RW5.js} +2 -2
- package/dist/{import-BjUG-cIY.js.map → import-Cwzf4RW5.js.map} +1 -1
- package/dist/index.js +106 -35
- package/dist/index.js.map +1 -1
- package/dist/markdown-CklalUUk.js.map +1 -1
- package/dist/preview/components/InlineEntityListPreview.d.ts +15 -0
- package/dist/preview/index.d.ts +1 -0
- package/dist/preview/property_previews/ArrayOfReferencesPreview.d.ts +1 -1
- package/dist/preview/property_previews/ArrayOfRelationsPreview.d.ts +1 -1
- package/dist/types/components/EntityFormActionsProps.d.ts +0 -1
- package/dist/types/components/EntityFormProps.d.ts +0 -5
- package/dist/types/fields.d.ts +24 -0
- package/dist/{util-B5fJm1FA.js → util-DSh3HRzK.js} +1974 -1130
- package/dist/util-DSh3HRzK.js.map +1 -0
- package/package.json +32 -34
- package/src/collection_editor/ConfigControllerProvider.tsx +1 -1
- package/src/collection_editor/serializable_types.ts +4 -3
- package/src/collection_editor/serializable_utils.ts +51 -17
- package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +65 -22
- package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +38 -30
- package/src/collection_editor/ui/collection_editor/CollectionRelationsTab.tsx +3 -1
- package/src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx +1 -1
- package/src/collection_editor/ui/collection_editor/RouterCollectionsStudioView.tsx +1 -1
- package/src/collection_editor/ui/collection_editor/properties/RelationPropertyField.tsx +16 -0
- package/src/collection_editor/useLocalCollectionsConfigController.tsx +127 -15
- package/src/collection_editor/validateCollectionJson.ts +3 -3
- package/src/components/AdminModeSyncer.tsx +1 -1
- package/src/components/CollectionTableBinding/PropertyTableCell.tsx +10 -2
- package/src/components/CollectionTableBinding/column_utils.tsx +26 -2
- package/src/components/CollectionTableBinding/table_bindings.tsx +2 -1
- package/src/components/CollectionViewBinding/CollectionBoardViewBinding.tsx +23 -16
- package/src/components/CollectionViewBinding/CollectionViewActions.tsx +7 -1
- package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +8 -2
- package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +22 -11
- package/src/components/CollectionViewBinding/SplitListCloseButton.tsx +33 -0
- package/src/components/CollectionViewBinding/SplitListView.tsx +18 -3
- package/src/components/CollectionViewBinding/SplitViewContext.tsx +34 -0
- package/src/components/CollectionViewBinding/hooks/useKanbanDragAndDrop.ts +48 -9
- package/src/components/CollectionViewBinding/useBoardDataController.tsx +80 -25
- package/src/components/CollectionViewBinding/usePreviewSlots.ts +18 -6
- package/src/components/DefaultAppBar.tsx +1 -1
- package/src/components/DefaultDrawer.tsx +1 -1
- package/src/components/DetailViewBinding.tsx +100 -143
- package/src/components/DrawerNavigationItem.tsx +1 -1
- package/src/components/EditFormActions.tsx +54 -129
- package/src/components/EditViewBinding.tsx +182 -93
- package/src/components/EntityIdentityBar.tsx +288 -0
- package/src/components/EntityInspector.tsx +132 -0
- package/src/components/EntityPreviewBinding.tsx +6 -2
- package/src/components/EntityPreviewNesting.tsx +50 -0
- package/src/components/EntityViewBinding.tsx +35 -32
- package/src/components/HomePage/FavouritesView.tsx +1 -1
- package/src/components/HomePage/NavigationCardBinding.tsx +1 -1
- package/src/components/HomePage/SmallNavigationCard.tsx +1 -1
- package/src/components/InlineEntityPreview.tsx +149 -0
- package/src/components/RebaseLayout.tsx +1 -1
- package/src/components/RebaseNavigation.tsx +45 -5
- package/src/components/RebaseRouteDefs.tsx +1 -1
- package/src/components/SelectableTable/SelectableTable.tsx +2 -2
- package/src/components/SidePanelBinding.tsx +19 -13
- package/src/components/UserSelector.tsx +12 -6
- package/src/components/common/default_entity_actions.tsx +4 -3
- package/src/components/history/EntityHistoryEntry.tsx +28 -7
- package/src/components/history/EntityHistoryView.tsx +20 -20
- package/src/components/history/LastEditedByIndicator.tsx +4 -3
- package/src/components/index.ts +2 -0
- package/src/data_import/utils/get_import_inference_type.ts +6 -0
- package/src/form/EntityForm.tsx +267 -179
- package/src/form/EntityFormActions.tsx +50 -99
- package/src/form/EntityFormBinding.tsx +4 -2
- package/src/form/PropertyFieldBinding.tsx +4 -0
- package/src/form/components/FieldBlock.tsx +119 -0
- package/src/form/components/FormRail.tsx +125 -0
- package/src/form/components/FormSections.tsx +118 -0
- package/src/form/components/index.tsx +3 -2
- package/src/form/components/useRailVisible.tsx +46 -0
- package/src/form/field_bindings/BinaryFieldBinding.tsx +3 -2
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +3 -2
- package/src/form/field_bindings/GeopointFieldBinding.tsx +3 -2
- package/src/form/field_bindings/MapFieldBinding.tsx +1 -2
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +3 -2
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +2 -1
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -1
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +2 -1
- package/src/form/field_bindings/RelationFieldBinding.tsx +6 -4
- package/src/form/field_bindings/RepeatFieldBinding.tsx +4 -1
- package/src/form/field_bindings/SelectFieldBinding.tsx +3 -2
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +31 -8
- package/src/form/field_bindings/SwitchFieldBinding.tsx +7 -3
- package/src/form/field_bindings/TextFieldBinding.tsx +18 -16
- package/src/form/field_bindings/UserSelectFieldBinding.tsx +3 -2
- package/src/form/field_bindings/VectorFieldBinding.tsx +2 -1
- package/src/hooks/navigation/useBuildUrlController.tsx +1 -1
- package/src/hooks/navigation/useTopLevelNavigation.ts +18 -7
- package/src/hooks/navigation/utils.ts +20 -0
- package/src/hooks/useBuildSideDialogsController.tsx +2 -2
- package/src/hooks/useBuildSidePanel.tsx +22 -61
- package/src/hooks/useEntityDisplayTitle.tsx +60 -0
- package/src/hooks/useHistory.ts +6 -5
- package/src/hooks/useRecordActions.tsx +69 -0
- package/src/hooks/useResolvedUsers.ts +10 -5
- package/src/preview/PropertyPreview.tsx +48 -22
- package/src/preview/components/InlineEntityListPreview.tsx +42 -0
- package/src/preview/components/ReferencePreview.tsx +34 -14
- package/src/preview/components/RelationPreview.tsx +34 -14
- package/src/preview/index.ts +1 -0
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +24 -1
- package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +25 -1
- package/src/routes/RebaseRoute.tsx +2 -2
- package/src/types/components/EntityFormActionsProps.tsx +0 -1
- package/src/types/components/EntityFormProps.tsx +0 -6
- package/src/types/fields.tsx +27 -0
- package/dist/form/components/FormEntry.d.ts +0 -6
- package/dist/form/components/FormLayout.d.ts +0 -5
- package/dist/history-g9688IaK.js.map +0 -1
- package/dist/util-B5fJm1FA.js.map +0 -1
- package/src/form/components/FormEntry.tsx +0 -22
- package/src/form/components/FormLayout.tsx +0 -16
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-ClwDEtqN.js","names":[],"sources":["../src/components/history/UserChip.tsx","../src/components/history/EntityHistoryEntry.tsx","../src/components/history/EntityHistoryView.tsx"],"sourcesContent":["import { User } from \"@rebasepro/types\";\nimport { Chip, Tooltip } from \"@rebasepro/ui\";\n\nexport function UserChip({ user }: { user: User }) {\n return (\n <Tooltip title={user.email ?? user.uid}>\n <Chip size={\"small\"} className={\"flex items-center\"}>\n {user.photoURL && <img\n className={\"rounded-full w-6 h-6 mr-2\"}\n src={user.photoURL} alt={user.displayName ?? \"User picture\"}/>}\n <span>{user.displayName ?? user.email ?? user.uid}</span>\n </Chip>\n </Tooltip>\n );\n}\n","\nimport type { Property } from \"@rebasepro/types\";\nimport * as React from \"react\";\n\nimport { ArrowLeftIcon, Chip, cls, defaultBorderMixin, iconSize, Tooltip, Typography } from \"@rebasepro/ui\";\nimport { PreviewSize } from \"../../types/components/PropertyPreviewProps\";\nimport { getPropertyInPath } from \"../../util/property_utils\";\nimport { PropertyPreview } from \"../../preview/PropertyPreview\";\nimport { SkeletonPropertyComponent } from \"../../preview/property_previews/SkeletonPropertyComponent\";\nimport { useAuthController } from \"@rebasepro/app\";\nimport { UserChip } from \"./UserChip\";\nimport { HistoryEntryData } from \"../../hooks\";\nimport { getValueInPath } from \"@rebasepro/utils\";\nimport type { AdminCollection } from \"@rebasepro/admin-types\";\n\n/**\n * Recursive deep equality for primitives, arrays and plain objects.\n */\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a == null || b == null) return a === b;\n if (typeof a !== typeof b) return false;\n if (typeof a !== \"object\") return false;\n\n if (Array.isArray(a)) {\n if (!Array.isArray(b) || a.length !== (b as unknown[]).length) return false;\n return a.every((item, i) => deepEqual(item, (b as unknown[])[i]));\n }\n\n if (Array.isArray(b)) return false;\n\n const aObj = a as Record<string, unknown>;\n const bObj = b as Record<string, unknown>;\n const aKeys = Object.keys(aObj);\n const bKeys = Object.keys(bObj);\n if (aKeys.length !== bKeys.length) return false;\n return aKeys.every(key => key in bObj && deepEqual(aObj[key], bObj[key]));\n}\n\nexport type EntityHistoryEntryProps = {\n size: PreviewSize;\n actions?: React.ReactNode;\n collection?: AdminCollection;\n hover?: boolean;\n entry: HistoryEntryData;\n onClick?: (e: React.SyntheticEvent) => void;\n};\n\nfunction PreviousValueView({\n previousValueInPath,\n childProperty,\n propertyKey\n}: {\n previousValueInPath: unknown;\n childProperty: Property;\n propertyKey: string;\n}) {\n if (typeof previousValueInPath === \"string\" || typeof previousValueInPath === \"number\") {\n return <Typography variant={\"caption\"} color={\"secondary\"} className=\"line-through\">\n {previousValueInPath}\n </Typography>;\n } else if (typeof previousValueInPath === \"boolean\") {\n return <Typography variant={\"caption\"} color={\"secondary\"} className=\"line-through\">\n {previousValueInPath ? \"true\" : \"false\"}\n </Typography>;\n } else {\n return <Tooltip\n side={\"left\"}\n title={<div className={\"flex flex-col gap-2\"}>\n <Typography variant={\"caption\"} color={\"secondary\"}>\n Previous value\n </Typography>\n <PropertyPreview\n propertyKey={propertyKey as string}\n value={previousValueInPath as never}\n property={childProperty as Property}\n size={\"small\"}/>\n </div>}>\n <ArrowLeftIcon size={iconSize.smallest} color={\"disabled\"} className={\"mb-1\"}/>\n </Tooltip>\n }\n}\n\n/**\n * Displays a single entity history revision entry.\n * Adapted from the entity_history plugin — now reads from backend API data.\n */\nexport function EntityHistoryEntry({\n actions,\n hover,\n collection,\n size,\n entry\n}: EntityHistoryEntryProps) {\n\n const authController = useAuthController();\n\n // The backend reports the whole row as changed, which includes the columns\n // that cannot meaningfully change: a timestamp it stamps itself on every\n // write, and the primary key, which is what identifies the row being\n // revised. Every revision was listing `updated_at: <now>` and `id: <the id\n // in the title bar>` around the one field the user actually edited.\n const changedFields = React.useMemo(() => {\n const all = entry.changed_fields;\n if (!all || !collection) return all;\n return all.filter(key => {\n const property = getPropertyInPath(collection.properties, key);\n if (!property) return true;\n if (\"isId\" in property && property.isId) return false;\n return !(property.type === \"date\" && \"autoValue\" in property && property.autoValue);\n });\n }, [entry.changed_fields, collection]);\n\n const previousValues = entry.previous_values;\n const updatedOn = new Date(entry.updated_at);\n const updatedBy = entry.updated_by;\n\n // Resolve user display\n const currentUser = authController.user;\n const userDisplay = updatedBy === currentUser?.uid\n ? currentUser\n : undefined;\n\n return <div className={\"@container/histentry w-full flex flex-col gap-2\"}>\n <div className={\"flex items-center flex-wrap gap-2\"}>\n <Typography variant={\"body2\"} color={\"secondary\"}>\n {updatedOn.toLocaleString()}\n </Typography>\n <Chip size={\"small\"}>\n {entry.action}\n </Chip>\n {!userDisplay && updatedBy && <Chip size={\"small\"}>{updatedBy}</Chip>}\n {userDisplay && <UserChip user={userDisplay}/>}\n </div>\n <div\n className={cls(\n \"bg-white dark:bg-surface-900\",\n \"min-h-[44px]\",\n \"w-full\",\n \"items-center\",\n hover ? \"hover:bg-surface-accent-50 dark:hover:bg-surface-800\" : \"\",\n size === \"small\" ? \"p-1\" : \"px-2 py-1\",\n \"flex border rounded-lg\",\n defaultBorderMixin\n )}>\n\n {actions}\n\n <div className={\"flex flex-col grow w-full m-1 shrink min-w-0\"}>\n\n {changedFields && collection && changedFields.map((key: string) => {\n const childProperty = getPropertyInPath(collection.properties, key);\n const valueInPath = entry.values ? getValueInPath(entry.values, key) : undefined;\n const previousValueInPath = previousValues ? getValueInPath(previousValues, key) : undefined;\n\n const element = childProperty\n ? <PropertyPreview\n propertyKey={key}\n value={valueInPath as never}\n property={childProperty as Property}\n size={\"small\"}/>\n : <Typography variant={\"body2\"}>\n {typeof valueInPath === \"string\" ? valueInPath : JSON.stringify(valueInPath)}\n </Typography>;\n\n // The key column used to be `min-w-[200px] w-1/5` beside a\n // `w-4/5` value — 200px of label and 80% of the row, which\n // adds up to more than the row in anything narrower than a\n // full page. In the inspector that pushed every value off\n // the right edge and wrapped ids one character per line.\n // It stacks until the entry itself is wide enough.\n return (\n <div key={\"ref_prev_\" + key}\n className=\"flex flex-col @sm/histentry:flex-row @sm/histentry:items-baseline gap-x-4 gap-y-0.5 w-full my-1.5\">\n <Typography variant={\"caption\"}\n color={\"secondary\"}\n className=\"shrink-0 break-all @sm/histentry:w-40 @sm/histentry:text-right @sm/histentry:break-normal @sm/histentry:truncate\">\n {key}\n </Typography>\n <div className=\"min-w-0 flex-1 flex flex-col items-start gap-0.5\">\n {previousValueInPath !== undefined && !deepEqual(previousValueInPath, valueInPath) &&\n <PreviousValueView previousValueInPath={previousValueInPath}\n childProperty={childProperty as Property}\n propertyKey={key}/>\n }\n {element}\n </div>\n </div>\n );\n })}\n\n {(!changedFields || changedFields.length === 0) && (\n <Typography variant={\"caption\"} color={\"secondary\"}>\n {entry.action === \"create\" ? \"Entity created\" :\n entry.action === \"delete\" ? \"Entity deleted\" : \"No field changes recorded\"}\n </Typography>\n )}\n\n </div>\n\n </div>\n </div>;\n}\n","\nimport type { EntityCustomViewParams } from \"@rebasepro/admin-types\";\nimport { useRef, useEffect } from \"react\";\nimport { cls, IconButton, Label, Tooltip, Typography } from \"@rebasepro/ui\";\nimport { ErrorBoundary, HistoryIcon } from \"@rebasepro/ui\";\nimport { EntityHistoryEntry } from \"./EntityHistoryEntry\";\nimport { useSnackbarController, useAuthController, useTranslation } from \"@rebasepro/app\";\nimport { ConfirmationDialog } from \"@rebasepro/app\";\nimport { useState } from \"react\";\nimport { useHistory } from \"../../hooks/useHistory\";\n\n/**\n * Entity history tab view. Shows a paginated list of entity revisions\n * fetched from the backend API. Supports infinite scroll and revert.\n */\nexport function EntityHistoryView<M extends Record<string, unknown>>({\n entity,\n collection,\n formContext\n}: EntityCustomViewParams<M>) {\n\n const { t } = useTranslation();\n const snackbarController = useSnackbarController();\n const authController = useAuthController();\n const dirty = formContext?.formex.dirty;\n\n const slug = collection.slug;\n const entityId = entity?.id;\n\n const {\n entries,\n isLoading,\n hasMore,\n loadMore,\n revert\n } = useHistory({\n slug,\n entityId,\n enabled: !!entityId,\n pageSize: 10\n });\n\n const [revertHistoryId, setRevertHistoryId] = useState<string | undefined>();\n const [isReverting, setIsReverting] = useState(false);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const loadMoreRef = useRef<HTMLDivElement>(null);\n\n // Intersection observer for infinite scroll\n useEffect(() => {\n const currentContainer = containerRef.current;\n const currentLoadMore = loadMoreRef.current;\n\n if (!currentContainer || !currentLoadMore || !hasMore || isLoading) return;\n\n const observer = new IntersectionObserver(\n (observerEntries) => {\n if (observerEntries[0].isIntersecting && hasMore && !isLoading) {\n loadMore();\n }\n },\n {\n root: currentContainer,\n rootMargin: \"0px 0px 200px 0px\",\n threshold: 0.01\n }\n );\n\n observer.observe(currentLoadMore);\n\n return () => observer.disconnect();\n }, [hasMore, isLoading, entries.length, loadMore]);\n\n if (!entity) {\n return <div className=\"flex items-center justify-center h-full\">\n <Label>{t(\"history_new_entity\") ?? \"History is only available for existing records\"}</Label>\n </div>;\n }\n\n async function doRevert(historyId: string) {\n setIsReverting(true);\n try {\n const revertedValues = await revert(historyId);\n setRevertHistoryId(undefined);\n\n // Reset the form with the reverted values so the UI updates\n // immediately without requiring a page refresh.\n if (formContext?.formex?.resetForm && revertedValues) {\n formContext.formex.resetForm({\n values: revertedValues as M,\n submitCount: 0,\n touched: {}\n });\n }\n\n snackbarController.open({\n message: t(\"history_reverted\") ?? \"Reverted to selected version\",\n type: \"info\"\n });\n } catch (error) {\n console.error(\"Error reverting entity:\", error);\n snackbarController.open({\n message: t(\"history_revert_error\") ?? \"Error reverting entity\",\n type: \"error\"\n });\n } finally {\n setIsReverting(false);\n }\n }\n\n const revertEntry = revertHistoryId\n ? entries.find(e => e.id === revertHistoryId)\n : undefined;\n\n // `p-8` plus a `mt-24` heading were sized for a full-width tab that no\n // longer exists. In the inspector panel that was 96px of nothing above the\n // first revision, and 32px of gutter on each side of a 448px column.\n return <div\n ref={containerRef}\n className={cls(\"relative flex-1 h-full overflow-auto w-full flex flex-col gap-4 p-4 sm:p-5\")}>\n <div className=\"flex flex-col gap-2 max-w-6xl mx-auto w-full\">\n\n {isLoading && entries.length === 0 && (\n <div className=\"flex flex-col gap-4\">\n {[1, 2, 3].map(i => (\n <div key={i} className=\"flex flex-col gap-2 animate-pulse\">\n <div className=\"h-4 w-48 bg-surface-200 dark:bg-surface-700 rounded\"/>\n <div className=\"h-12 w-full bg-surface-100 dark:bg-surface-900 rounded-lg border border-surface-200 dark:border-surface-700\"/>\n </div>\n ))}\n </div>\n )}\n\n {!isLoading && entries.length === 0 && <>\n <Label>\n {t(\"history_empty\") ?? \"No history available\"}\n </Label>\n <Typography variant={\"caption\"}>\n {t(\"history_empty_description\") ?? \"When you save a record, a new version is created and stored in the history.\"}\n </Typography>\n </>}\n\n {entries.map((entry) => (\n <div key={entry.id} className=\"flex flex-col gap-2 w-full\">\n <EntityHistoryEntry\n entry={entry}\n collection={collection}\n size={\"medium\"}\n actions={\n <Tooltip title={t(\"history_revert\") ?? \"Revert to this version\"}\n className={\"m-2 grow-0 self-start\"}>\n <IconButton\n onClick={() => {\n if (dirty) {\n snackbarController.open({\n message: t(\"history_revert_dirty\") ?? \"Please save or discard your changes before reverting\",\n type: \"warning\"\n });\n } else {\n setRevertHistoryId(entry.id);\n }\n }}>\n <HistoryIcon/>\n </IconButton>\n </Tooltip>\n }\n />\n </div>\n ))}\n\n {/* Load more sentinel */}\n {entries.length > 0 && (\n <div ref={loadMoreRef} className=\"py-4 text-center\">\n {isLoading && <Label>{t(\"loading\")}</Label>}\n {!hasMore && entries.length > 10 && <Label>{t(\"history_no_more\") ?? \"No more history\"}</Label>}\n </div>\n )}\n </div>\n\n <ErrorBoundary>\n <ConfirmationDialog\n open={Boolean(revertHistoryId)}\n onAccept={() => {\n if (revertHistoryId) doRevert(revertHistoryId);\n }}\n onCancel={() => setRevertHistoryId(undefined)}\n title={<Typography variant={\"subtitle2\"}>{t(\"history_revert_title\") ?? \"Revert data to this version?\"}</Typography>}\n body={revertEntry\n ? <div className=\"p-4\">\n <Typography variant={\"caption\"} color={\"secondary\"}>\n This will save the entity with the values from{\" \"}\n {new Date(revertEntry.updated_at).toLocaleString()}.\n A new history entry will be created for the revert.\n </Typography>\n </div>\n : null\n }\n />\n </ErrorBoundary>\n </div>;\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import type { AppView, RebasePlugin, NavigationGroupMapping, AdminCollection } from "@rebasepro/admin-types";
|
|
2
2
|
export declare const NAVIGATION_DEFAULT_GROUP_NAME = "Views";
|
|
3
3
|
export declare const NAVIGATION_ADMIN_GROUP_NAME = "Admin";
|
|
4
|
+
/**
|
|
5
|
+
* Group names that sink to the bottom of the drawer on their name alone.
|
|
6
|
+
*
|
|
7
|
+
* A magic string, and it stays one for compatibility: apps have been relying on
|
|
8
|
+
* `group: "Settings"` landing last since before there was a flag, and taking it
|
|
9
|
+
* away would silently reshuffle their navigation.
|
|
10
|
+
*
|
|
11
|
+
* It is a bad way to ask for the behaviour, though — it is invisible from the
|
|
12
|
+
* `group?: string` type, and it is defeated by the most ordinary edit there is:
|
|
13
|
+
* translating the label. `group: "Ajustes"` is the same group to a reader and a
|
|
14
|
+
* different one to this comparison, so the ordering just quietly stops
|
|
15
|
+
* happening. Set {@link AppView.pinToBottom} instead, which says so.
|
|
16
|
+
*/
|
|
17
|
+
export declare const NAVIGATION_BOTTOM_GROUP_NAMES: readonly ["Admin", "Settings"];
|
|
18
|
+
/** Whether a group sinks to the bottom by virtue of its name. @see NAVIGATION_BOTTOM_GROUP_NAMES */
|
|
19
|
+
export declare function isBottomPinnedGroupName(groupName?: string): boolean;
|
|
4
20
|
export declare function getGroup(collectionOrView: AdminCollection | AppView): string;
|
|
5
21
|
export declare function computeNavigationGroups({ navigationGroupMappings, collections, views, plugins }: {
|
|
6
22
|
navigationGroupMappings?: NavigationGroupMapping[];
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import type { CustomizationController, SidePanelBindingProps, SideDialogsController, SidePanelController, UrlController, NavigationStateController, AdminCollection } from "@rebasepro/admin-types";
|
|
2
2
|
import { CollectionRegistryController } from "@rebasepro/types";
|
|
3
3
|
import { AuthController } from "@rebasepro/admin-types";
|
|
4
|
+
/**
|
|
5
|
+
* How wide the side panel opens.
|
|
6
|
+
*
|
|
7
|
+
* One width for the whole panel, whatever tab is showing. It used to depend on
|
|
8
|
+
* the selected tab — the form got one width and a subcollection or custom view
|
|
9
|
+
* got `calc(55vw + 768px)` — so the panel physically resized under the cursor
|
|
10
|
+
* every time you switched tabs. And the "form" width was derived from how
|
|
11
|
+
* deeply nested the collection's *properties* were, which is not a thing anyone
|
|
12
|
+
* can predict from looking at the panel.
|
|
13
|
+
*
|
|
14
|
+
* Now: `width` prop, then the collection's `sideDialogWidth`, then a default
|
|
15
|
+
* sized for the content the panel actually has to hold — the form column plus
|
|
16
|
+
* its metadata rail — capped so it never eats the whole window.
|
|
17
|
+
*/
|
|
4
18
|
export declare function getEntityViewWidth(props: SidePanelBindingProps<any>, small: boolean, customizationController: CustomizationController): string;
|
|
5
19
|
export declare const useBuildSidePanel: (collectionRegistryController: CollectionRegistryController, urlController: UrlController, navigationStateController: NavigationStateController, sideDialogsController: SideDialogsController, authController: AuthController) => SidePanelController;
|
|
6
20
|
export declare function buildSidePanelsFromUrl(path: string, collections: AdminCollection[], newFlag: boolean): SidePanelBindingProps<any>[];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
2
|
+
import type { EntityStatus } from "@rebasepro/types";
|
|
3
|
+
export interface UseEntityDisplayTitleParams<M extends Record<string, unknown>> {
|
|
4
|
+
collection: AdminCollection<M>;
|
|
5
|
+
values?: Record<string, unknown>;
|
|
6
|
+
entityId?: string | number;
|
|
7
|
+
status: EntityStatus;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The human name for a record, for the identity bar and the breadcrumb.
|
|
11
|
+
*
|
|
12
|
+
* The guard on `status` is the point. The title resolver walks candidate
|
|
13
|
+
* properties and returns the first non-empty one, and on a brand new entity the
|
|
14
|
+
* only populated values are the property defaults — so creating a blog post
|
|
15
|
+
* opened a page headed **draft**, the default of its `status` enum. A record
|
|
16
|
+
* that does not exist yet has no name; it has an intent.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useEntityDisplayTitle<M extends Record<string, unknown>>({ collection, values, entityId, status }: UseEntityDisplayTitleParams<M>): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { EntityAction, AdminCollection } from "@rebasepro/admin-types";
|
|
2
|
+
import type { Entity } from "@rebasepro/types";
|
|
3
|
+
export interface UseRecordActionsParams<M extends Record<string, unknown>> {
|
|
4
|
+
collection: AdminCollection<M>;
|
|
5
|
+
path: string;
|
|
6
|
+
entity?: Entity<M>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The actions you can perform *on* a record — copy, delete, and whatever the
|
|
10
|
+
* collection adds — resolved against the current user's permissions.
|
|
11
|
+
*
|
|
12
|
+
* Extracted from the form's footer so the identity bar can own them. They are
|
|
13
|
+
* not ways to leave the form, so they never belonged next to Save; putting them
|
|
14
|
+
* in the bar's overflow menu is what let the footer disappear instead of
|
|
15
|
+
* becoming a full-width strip holding two icon buttons.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useRecordActions<M extends Record<string, unknown>>({ collection, path, entity }: UseRecordActionsParams<M>): EntityAction[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { $t as CollectionTableBinding, Ct as convertDataToEntity, Dt as useImportConfig, Et as getInferenceType,
|
|
2
|
+
import { $t as CollectionTableBinding, Ct as convertDataToEntity, Dt as useImportConfig, Et as getInferenceType, Ln as useCollectionRegistryController, Nt as PropertyConfigBadge, Ot as ImportSaveInProgress, St as DataNewPropertiesMapping, Zt as useSelectionController, fr as getPropertiesWithPropertiesOrder, k as getFieldConfig, kt as ImportFileUpload, pr as getPropertyInPath } from "./util-DSh3HRzK.js";
|
|
3
3
|
import React, { useCallback, useEffect } from "react";
|
|
4
4
|
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Select, SelectItem, Tooltip, Typography, UploadIcon, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
|
|
5
5
|
import { useAuthController, useCustomizationController, useSnackbarController } from "@rebasepro/app";
|
|
@@ -325,4 +325,4 @@ var import_exports = /* @__PURE__ */ __exportAll({
|
|
|
325
325
|
//#endregion
|
|
326
326
|
export { PropertySelectEntry as i, ImportCollectionAction as n, ImportDataPreview as r, import_exports as t };
|
|
327
327
|
|
|
328
|
-
//# sourceMappingURL=import-
|
|
328
|
+
//# sourceMappingURL=import-Cwzf4RW5.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import-BjUG-cIY.js","names":[],"sources":["../src/data_import/import/ImportCollectionAction.tsx","../src/data_import/import/index.ts"],"sourcesContent":["import React, { useCallback, useEffect } from \"react\";\nimport {\n useAuthController,\n useCustomizationController,\n useSnackbarController\n} from \"@rebasepro/app\";\nimport { getPropertiesWithPropertiesOrder, getPropertyInPath } from \"../../util\";\nimport { Properties, Property, User } from \"@rebasepro/types\";\nimport { CollectionActionsProps, AdminCollection } from \"@rebasepro/admin-types\";\nimport { getFieldConfig } from \"../../components/field_configs\";\nimport { PropertyConfigBadge } from \"../../components/PropertyConfigBadge\";\nimport { useSelectionController } from \"../../components/CollectionViewBinding/useSelectionController\";\nimport { CollectionTableBinding } from \"../../components/CollectionTableBinding/CollectionTableBinding\";\nimport { useCollectionRegistryController } from \"../../hooks\";\nimport {\n Button,\n cls,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n IconButton,\n iconSize,\n Select,\n SelectItem,\n Tooltip,\n Typography,\n UploadIcon\n} from \"@rebasepro/ui\";\nimport { buildEntityPropertiesFromData } from \"@rebasepro/inference\";\nimport { useImportConfig } from \"../hooks\";\nimport { convertDataToEntity, getInferenceType } from \"../utils\";\nimport { DataNewPropertiesMapping } from \"../components/DataNewPropertiesMapping\";\nimport { ImportFileUpload } from \"../components/ImportFileUpload\";\nimport { ImportSaveInProgress } from \"../components/ImportSaveInProgress\";\nimport { ImportConfig } from \"../types\";\nimport { slugify } from \"@rebasepro/utils\";\n\ntype ImportState = \"initial\" | \"mapping\" | \"preview\" | \"import_data_saving\";\n\nexport function ImportCollectionAction<M extends Record<string, unknown>, USER extends User>({\n collection,\n path,\n onAnalyticsEvent\n}: CollectionActionsProps<M, USER> & {\n onAnalyticsEvent?: (event: string, params?: any) => void;\n}\n) {\n\n const snackbarController = useSnackbarController();\n\n const [open, setOpen] = React.useState(false);\n\n const [step, setStep] = React.useState<ImportState>(\"initial\");\n\n const importConfig = useImportConfig();\n\n const handleClickOpen = useCallback(() => {\n setOpen(true);\n onAnalyticsEvent?.(\"import_open\");\n setStep(\"initial\");\n }, [onAnalyticsEvent]);\n\n const handleClose = useCallback(() => {\n setOpen(false);\n }, [setOpen]);\n\n const onMappingComplete = useCallback(() => {\n onAnalyticsEvent?.(\"import_mapping_complete\");\n setStep(\"preview\");\n }, [onAnalyticsEvent]);\n\n const onPreviewComplete = useCallback(() => {\n onAnalyticsEvent?.(\"import_data_save\");\n setStep(\"import_data_saving\");\n }, [onAnalyticsEvent]);\n\n const onDataAdded = async (data: object[]) => {\n importConfig.setImportData(data);\n\n if (data.length > 0) {\n const originProperties = await buildEntityPropertiesFromData(data, getInferenceType);\n importConfig.setOriginProperties(originProperties as Properties);\n\n const headersMapping = buildHeadersMappingFromData(data, collection?.properties);\n importConfig.setHeadersMapping(headersMapping);\n const firstKey = Object.keys(headersMapping)?.[0];\n if (firstKey?.includes(\"id\") || firstKey?.includes(\"key\")) {\n importConfig.setIdColumn(firstKey);\n }\n }\n setTimeout(() => {\n onAnalyticsEvent?.(\"import_data_added\");\n setStep(\"mapping\");\n }, 100);\n };\n\n const properties = getPropertiesWithPropertiesOrder(collection.properties, collection.propertiesOrder as Extract<keyof M, string>[]);\n\n const propertiesAndLevel = Object.entries(properties)\n .flatMap(([key, property]) => getPropertiesAndLevel(key, property, 0));\n const propertiesOrder = (collection.propertiesOrder ?? Object.keys(collection.properties)) as Extract<keyof M, string>[];\n\n return <>\n\n <Tooltip title={\"Import\"}\n asChild={true}>\n <IconButton\n size={\"small\"}\n color={\"primary\"} onClick={handleClickOpen}>\n <UploadIcon size={iconSize.small}/>\n </IconButton>\n </Tooltip>\n\n <Dialog open={open}\n fullWidth={step !== \"initial\"}\n fullHeight={step !== \"initial\"}\n maxWidth={step === \"initial\" ? \"lg\" : \"7xl\"}>\n\n <DialogTitle variant={\"h6\"} hidden={step === \"preview\"}>Import data</DialogTitle>\n\n <DialogContent className={\"h-full flex flex-col gap-4 my-4\"} fullHeight={step === \"preview\"}>\n\n {step === \"initial\" && <>\n <Typography variant={\"body2\"}>Upload a CSV, Excel or JSON file and map it to your existing\n schema</Typography>\n <ImportFileUpload onDataAdded={onDataAdded}/>\n </>}\n\n {step === \"mapping\" && <>\n <DataNewPropertiesMapping importConfig={importConfig}\n destinationProperties={properties}\n buildPropertyView={({\n isIdColumn,\n property,\n propertyKey,\n importKey\n }) => {\n return <PropertyTreeSelect\n selectedPropertyKey={propertyKey ?? \"\"}\n properties={properties}\n propertiesAndLevel={propertiesAndLevel}\n isIdColumn={isIdColumn}\n onIdSelected={() => {\n importConfig.setIdColumn(importKey);\n }}\n onPropertySelected={(newPropertyKey) => {\n\n onAnalyticsEvent?.(\"import_mapping_field_updated\");\n const newHeadersMapping: Record<string, string | null> = Object.entries(importConfig.headersMapping)\n .map(([currentImportKey, currentPropertyKey]) => {\n if (currentPropertyKey === newPropertyKey) {\n return { [currentImportKey]: null };\n }\n if (currentImportKey === importKey) {\n return { [currentImportKey]: newPropertyKey };\n }\n return { [currentImportKey]: currentPropertyKey };\n })\n .reduce((acc, curr) => ({ ...acc,\n...curr }), {});\n importConfig.setHeadersMapping(newHeadersMapping as Record<string, string>);\n\n if (newPropertyKey === importConfig.idColumn) {\n importConfig.setIdColumn(undefined);\n }\n\n }}\n />;\n }}/>\n </>}\n\n {step === \"preview\" && <ImportDataPreview importConfig={importConfig}\n properties={properties}\n propertiesOrder={propertiesOrder}/>}\n\n {step === \"import_data_saving\" && importConfig &&\n <ImportSaveInProgress importConfig={importConfig}\n collection={collection as AdminCollection}\n path={path}\n onImportSuccess={(importedCollection) => {\n handleClose();\n snackbarController.open({\n type: \"info\",\n message: \"Data imported successfully\"\n });\n }}\n />}\n\n </DialogContent>\n <DialogActions>\n\n {step === \"mapping\" && <Button\n onClick={() => setStep(\"initial\")}\n variant={\"text\"}>\n Back\n </Button>}\n\n {step === \"preview\" && <Button\n onClick={() => setStep(\"mapping\")}\n variant={\"text\"}>\n Back\n </Button>}\n\n <Button onClick={handleClose}\n variant={\"text\"}>\n Cancel\n </Button>\n\n {step === \"mapping\" && <Button variant=\"filled\"\n color={\"primary\"}\n onClick={onMappingComplete}>\n Next\n </Button>}\n\n {step === \"preview\" && <Button variant=\"filled\"\n color={\"primary\"}\n onClick={onPreviewComplete}>\n Save data\n </Button>}\n\n </DialogActions>\n </Dialog>\n\n </>;\n}\n\nconst internalIDValue = \"__internal_id__\";\n\nfunction PropertyTreeSelect({\n selectedPropertyKey,\n properties,\n onPropertySelected,\n onIdSelected,\n propertiesAndLevel,\n isIdColumn\n}: {\n selectedPropertyKey: string | null;\n properties: Record<string, Property>;\n onPropertySelected: (propertyKey: string | null) => void;\n onIdSelected: () => void;\n propertiesAndLevel: PropertyAndLevel[];\n isIdColumn?: boolean;\n}) {\n\n const selectedProperty = selectedPropertyKey ? getPropertyInPath(properties, selectedPropertyKey) : null;\n\n const renderValue = useCallback((selectedPropertyKey: string) => {\n\n if (selectedPropertyKey === internalIDValue) {\n return <Typography variant={\"body2\"} className={\"p-4\"}>Use this column as ID</Typography>;\n }\n\n if (!selectedPropertyKey || !selectedProperty) {\n return <Typography variant={\"body2\"} color=\"disabled\" className={\"p-4\"}>Do not import this\n property</Typography>;\n }\n\n return <PropertySelectEntry propertyKey={selectedPropertyKey}\n property={selectedProperty as Property}/>;\n }, [selectedProperty]);\n\n const onSelectValueChange = (value: string) => {\n if (value === internalIDValue) {\n onIdSelected();\n onPropertySelected(null);\n } else if (value === \"__do_not_import\") {\n onPropertySelected(null);\n } else {\n onPropertySelected(value);\n }\n };\n\n return <Select value={isIdColumn ? internalIDValue : (selectedPropertyKey ?? undefined)}\n fullWidth={true}\n onValueChange={onSelectValueChange}\n renderValue={renderValue}>\n\n <SelectItem value={\"__do_not_import\"}>\n <Typography variant={\"body2\"} color={\"disabled\"} className={\"p-4\"}>Do not import this property</Typography>\n </SelectItem>\n\n <SelectItem value={internalIDValue}>\n <Typography variant={\"body2\"} className={\"p-4\"}>Use this column as ID</Typography>\n </SelectItem>\n\n {propertiesAndLevel.map(({\n property,\n level,\n propertyKey\n }) => {\n return <SelectItem value={propertyKey}\n key={propertyKey}\n disabled={property.type === \"map\"}>\n <PropertySelectEntry propertyKey={propertyKey}\n property={property}\n level={level}/>\n </SelectItem>;\n })}\n\n </Select>;\n}\n\ntype PropertyAndLevel = {\n property: Property,\n level: number,\n propertyKey: string\n};\n\nfunction getPropertiesAndLevel(key: string, property: Property, level: number): PropertyAndLevel[] {\n const properties: PropertyAndLevel[] = [];\n properties.push({\n property,\n level,\n propertyKey: key\n });\n if (property.type === \"map\" && property.properties) {\n Object.entries(property.properties).forEach(([childKey, value]) => {\n properties.push(...getPropertiesAndLevel(`${key}.${childKey}`, value as Property, level + 1));\n });\n }\n return properties;\n}\n\nexport function PropertySelectEntry({\n propertyKey,\n property,\n level = 0\n}: {\n propertyKey: string;\n property: Property;\n level?: number;\n}) {\n\n const { propertyConfigs } = useCustomizationController();\n const widget = getFieldConfig(property, propertyConfigs);\n\n return <div\n className=\"flex flex-row w-full text-start items-center h-full\">\n\n {new Array(level).fill(0).map((_, index) =>\n <div className={cls(defaultBorderMixin, \"ml-8 border-l h-12\")} key={index}/>)}\n\n <div className={\"m-4\"}>\n <Tooltip title={widget?.name}>\n <PropertyConfigBadge propertyConfig={widget}/>\n </Tooltip>\n </div>\n\n <div className={\"flex flex-col grow p-2 pl-2\"}>\n <Typography variant=\"body1\"\n component=\"span\"\n className=\"grow pr-2\">\n {property.name\n ? property.name\n : \"\\u00a0\"\n }\n </Typography>\n\n <Typography className=\" pr-2\"\n variant={\"body2\"}\n component=\"span\"\n color=\"secondary\">\n {propertyKey}\n </Typography>\n </div>\n\n </div>;\n\n}\n\nexport function ImportDataPreview<M extends Record<string, unknown>>({\n importConfig,\n properties,\n propertiesOrder\n}: {\n importConfig: ImportConfig,\n properties: Properties,\n propertiesOrder: Extract<keyof M, string>[],\n}) {\n const authController = useAuthController();\n const collectionRegistry = useCollectionRegistryController();\n useEffect(() => {\n const mappedData = importConfig.importData.map(d => convertDataToEntity(\n authController,\n collectionRegistry,\n d,\n importConfig.idColumn,\n importConfig.headersMapping,\n properties,\n \"TEMP_PATH\",\n importConfig.defaultValues\n ));\n importConfig.setEntities(mappedData);\n }, []);\n\n const selectionController = useSelectionController();\n\n return <CollectionTableBinding\n title={<div>\n <Typography variant={\"subtitle2\"}>Imported data preview</Typography>\n <Typography variant={\"caption\"}>Entities with the same id will be overwritten</Typography>\n </div>}\n tableController={{\n data: importConfig.entities,\n dataLoading: false,\n noMoreToLoad: false\n }}\n enablePopupIcon={false}\n endAdornment={<div className={\"h-12\"}/>}\n filterable={false}\n sortable={false}\n openEntityMode={\"full_screen\"}\n selectionController={selectionController}\n properties={properties}/>\n\n}\n\nfunction buildHeadersMappingFromData(objArr: object[], properties?: Properties) {\n const headersMapping: Record<string, string> = {};\n objArr.filter(Boolean).forEach((obj) => {\n Object.keys(obj).forEach((key) => {\n const child = (obj as Record<string, unknown>)[key];\n if (child != null && typeof child === \"object\" && !Array.isArray(child)) {\n const childProperty = properties?.[key];\n const childProperties = childProperty && \"properties\" in childProperty ? childProperty.properties : undefined;\n const childHeadersMapping = buildHeadersMappingFromData([child as object], childProperties);\n Object.entries(childHeadersMapping).forEach(([subKey, mapping]) => {\n headersMapping[`${key}.${subKey}`] = `${key}.${mapping}`;\n });\n }\n\n if (!properties) {\n headersMapping[key] = key;\n } else if (key in properties) {\n headersMapping[key] = key;\n } else {\n const slug = slugify(key);\n if (slug in properties) {\n headersMapping[key] = slug;\n } else {\n headersMapping[key] = key;\n }\n }\n\n });\n });\n return headersMapping;\n}\n","export * from \"./ImportCollectionAction\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"import-Cwzf4RW5.js","names":[],"sources":["../src/data_import/import/ImportCollectionAction.tsx","../src/data_import/import/index.ts"],"sourcesContent":["import React, { useCallback, useEffect } from \"react\";\nimport {\n useAuthController,\n useCustomizationController,\n useSnackbarController\n} from \"@rebasepro/app\";\nimport { getPropertiesWithPropertiesOrder, getPropertyInPath } from \"../../util\";\nimport { Properties, Property, User } from \"@rebasepro/types\";\nimport { CollectionActionsProps, AdminCollection } from \"@rebasepro/admin-types\";\nimport { getFieldConfig } from \"../../components/field_configs\";\nimport { PropertyConfigBadge } from \"../../components/PropertyConfigBadge\";\nimport { useSelectionController } from \"../../components/CollectionViewBinding/useSelectionController\";\nimport { CollectionTableBinding } from \"../../components/CollectionTableBinding/CollectionTableBinding\";\nimport { useCollectionRegistryController } from \"../../hooks\";\nimport {\n Button,\n cls,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n IconButton,\n iconSize,\n Select,\n SelectItem,\n Tooltip,\n Typography,\n UploadIcon\n} from \"@rebasepro/ui\";\nimport { buildEntityPropertiesFromData } from \"@rebasepro/inference\";\nimport { useImportConfig } from \"../hooks\";\nimport { convertDataToEntity, getInferenceType } from \"../utils\";\nimport { DataNewPropertiesMapping } from \"../components/DataNewPropertiesMapping\";\nimport { ImportFileUpload } from \"../components/ImportFileUpload\";\nimport { ImportSaveInProgress } from \"../components/ImportSaveInProgress\";\nimport { ImportConfig } from \"../types\";\nimport { slugify } from \"@rebasepro/utils\";\n\ntype ImportState = \"initial\" | \"mapping\" | \"preview\" | \"import_data_saving\";\n\nexport function ImportCollectionAction<M extends Record<string, unknown>, USER extends User>({\n collection,\n path,\n onAnalyticsEvent\n}: CollectionActionsProps<M, USER> & {\n onAnalyticsEvent?: (event: string, params?: any) => void;\n}\n) {\n\n const snackbarController = useSnackbarController();\n\n const [open, setOpen] = React.useState(false);\n\n const [step, setStep] = React.useState<ImportState>(\"initial\");\n\n const importConfig = useImportConfig();\n\n const handleClickOpen = useCallback(() => {\n setOpen(true);\n onAnalyticsEvent?.(\"import_open\");\n setStep(\"initial\");\n }, [onAnalyticsEvent]);\n\n const handleClose = useCallback(() => {\n setOpen(false);\n }, [setOpen]);\n\n const onMappingComplete = useCallback(() => {\n onAnalyticsEvent?.(\"import_mapping_complete\");\n setStep(\"preview\");\n }, [onAnalyticsEvent]);\n\n const onPreviewComplete = useCallback(() => {\n onAnalyticsEvent?.(\"import_data_save\");\n setStep(\"import_data_saving\");\n }, [onAnalyticsEvent]);\n\n const onDataAdded = async (data: object[]) => {\n importConfig.setImportData(data);\n\n if (data.length > 0) {\n const originProperties = await buildEntityPropertiesFromData(data, getInferenceType);\n importConfig.setOriginProperties(originProperties as Properties);\n\n const headersMapping = buildHeadersMappingFromData(data, collection?.properties);\n importConfig.setHeadersMapping(headersMapping);\n const firstKey = Object.keys(headersMapping)?.[0];\n if (firstKey?.includes(\"id\") || firstKey?.includes(\"key\")) {\n importConfig.setIdColumn(firstKey);\n }\n }\n setTimeout(() => {\n onAnalyticsEvent?.(\"import_data_added\");\n setStep(\"mapping\");\n }, 100);\n };\n\n const properties = getPropertiesWithPropertiesOrder(collection.properties, collection.propertiesOrder as Extract<keyof M, string>[]);\n\n const propertiesAndLevel = Object.entries(properties)\n .flatMap(([key, property]) => getPropertiesAndLevel(key, property, 0));\n const propertiesOrder = (collection.propertiesOrder ?? Object.keys(collection.properties)) as Extract<keyof M, string>[];\n\n return <>\n\n <Tooltip title={\"Import\"}\n asChild={true}>\n <IconButton\n size={\"small\"}\n color={\"primary\"} onClick={handleClickOpen}>\n <UploadIcon size={iconSize.small}/>\n </IconButton>\n </Tooltip>\n\n <Dialog open={open}\n fullWidth={step !== \"initial\"}\n fullHeight={step !== \"initial\"}\n maxWidth={step === \"initial\" ? \"lg\" : \"7xl\"}>\n\n <DialogTitle variant={\"h6\"} hidden={step === \"preview\"}>Import data</DialogTitle>\n\n <DialogContent className={\"h-full flex flex-col gap-4 my-4\"} fullHeight={step === \"preview\"}>\n\n {step === \"initial\" && <>\n <Typography variant={\"body2\"}>Upload a CSV, Excel or JSON file and map it to your existing\n schema</Typography>\n <ImportFileUpload onDataAdded={onDataAdded}/>\n </>}\n\n {step === \"mapping\" && <>\n <DataNewPropertiesMapping importConfig={importConfig}\n destinationProperties={properties}\n buildPropertyView={({\n isIdColumn,\n property,\n propertyKey,\n importKey\n }) => {\n return <PropertyTreeSelect\n selectedPropertyKey={propertyKey ?? \"\"}\n properties={properties}\n propertiesAndLevel={propertiesAndLevel}\n isIdColumn={isIdColumn}\n onIdSelected={() => {\n importConfig.setIdColumn(importKey);\n }}\n onPropertySelected={(newPropertyKey) => {\n\n onAnalyticsEvent?.(\"import_mapping_field_updated\");\n const newHeadersMapping: Record<string, string | null> = Object.entries(importConfig.headersMapping)\n .map(([currentImportKey, currentPropertyKey]) => {\n if (currentPropertyKey === newPropertyKey) {\n return { [currentImportKey]: null };\n }\n if (currentImportKey === importKey) {\n return { [currentImportKey]: newPropertyKey };\n }\n return { [currentImportKey]: currentPropertyKey };\n })\n .reduce((acc, curr) => ({ ...acc,\n...curr }), {});\n importConfig.setHeadersMapping(newHeadersMapping as Record<string, string>);\n\n if (newPropertyKey === importConfig.idColumn) {\n importConfig.setIdColumn(undefined);\n }\n\n }}\n />;\n }}/>\n </>}\n\n {step === \"preview\" && <ImportDataPreview importConfig={importConfig}\n properties={properties}\n propertiesOrder={propertiesOrder}/>}\n\n {step === \"import_data_saving\" && importConfig &&\n <ImportSaveInProgress importConfig={importConfig}\n collection={collection as AdminCollection}\n path={path}\n onImportSuccess={(importedCollection) => {\n handleClose();\n snackbarController.open({\n type: \"info\",\n message: \"Data imported successfully\"\n });\n }}\n />}\n\n </DialogContent>\n <DialogActions>\n\n {step === \"mapping\" && <Button\n onClick={() => setStep(\"initial\")}\n variant={\"text\"}>\n Back\n </Button>}\n\n {step === \"preview\" && <Button\n onClick={() => setStep(\"mapping\")}\n variant={\"text\"}>\n Back\n </Button>}\n\n <Button onClick={handleClose}\n variant={\"text\"}>\n Cancel\n </Button>\n\n {step === \"mapping\" && <Button variant=\"filled\"\n color={\"primary\"}\n onClick={onMappingComplete}>\n Next\n </Button>}\n\n {step === \"preview\" && <Button variant=\"filled\"\n color={\"primary\"}\n onClick={onPreviewComplete}>\n Save data\n </Button>}\n\n </DialogActions>\n </Dialog>\n\n </>;\n}\n\nconst internalIDValue = \"__internal_id__\";\n\nfunction PropertyTreeSelect({\n selectedPropertyKey,\n properties,\n onPropertySelected,\n onIdSelected,\n propertiesAndLevel,\n isIdColumn\n}: {\n selectedPropertyKey: string | null;\n properties: Record<string, Property>;\n onPropertySelected: (propertyKey: string | null) => void;\n onIdSelected: () => void;\n propertiesAndLevel: PropertyAndLevel[];\n isIdColumn?: boolean;\n}) {\n\n const selectedProperty = selectedPropertyKey ? getPropertyInPath(properties, selectedPropertyKey) : null;\n\n const renderValue = useCallback((selectedPropertyKey: string) => {\n\n if (selectedPropertyKey === internalIDValue) {\n return <Typography variant={\"body2\"} className={\"p-4\"}>Use this column as ID</Typography>;\n }\n\n if (!selectedPropertyKey || !selectedProperty) {\n return <Typography variant={\"body2\"} color=\"disabled\" className={\"p-4\"}>Do not import this\n property</Typography>;\n }\n\n return <PropertySelectEntry propertyKey={selectedPropertyKey}\n property={selectedProperty as Property}/>;\n }, [selectedProperty]);\n\n const onSelectValueChange = (value: string) => {\n if (value === internalIDValue) {\n onIdSelected();\n onPropertySelected(null);\n } else if (value === \"__do_not_import\") {\n onPropertySelected(null);\n } else {\n onPropertySelected(value);\n }\n };\n\n return <Select value={isIdColumn ? internalIDValue : (selectedPropertyKey ?? undefined)}\n fullWidth={true}\n onValueChange={onSelectValueChange}\n renderValue={renderValue}>\n\n <SelectItem value={\"__do_not_import\"}>\n <Typography variant={\"body2\"} color={\"disabled\"} className={\"p-4\"}>Do not import this property</Typography>\n </SelectItem>\n\n <SelectItem value={internalIDValue}>\n <Typography variant={\"body2\"} className={\"p-4\"}>Use this column as ID</Typography>\n </SelectItem>\n\n {propertiesAndLevel.map(({\n property,\n level,\n propertyKey\n }) => {\n return <SelectItem value={propertyKey}\n key={propertyKey}\n disabled={property.type === \"map\"}>\n <PropertySelectEntry propertyKey={propertyKey}\n property={property}\n level={level}/>\n </SelectItem>;\n })}\n\n </Select>;\n}\n\ntype PropertyAndLevel = {\n property: Property,\n level: number,\n propertyKey: string\n};\n\nfunction getPropertiesAndLevel(key: string, property: Property, level: number): PropertyAndLevel[] {\n const properties: PropertyAndLevel[] = [];\n properties.push({\n property,\n level,\n propertyKey: key\n });\n if (property.type === \"map\" && property.properties) {\n Object.entries(property.properties).forEach(([childKey, value]) => {\n properties.push(...getPropertiesAndLevel(`${key}.${childKey}`, value as Property, level + 1));\n });\n }\n return properties;\n}\n\nexport function PropertySelectEntry({\n propertyKey,\n property,\n level = 0\n}: {\n propertyKey: string;\n property: Property;\n level?: number;\n}) {\n\n const { propertyConfigs } = useCustomizationController();\n const widget = getFieldConfig(property, propertyConfigs);\n\n return <div\n className=\"flex flex-row w-full text-start items-center h-full\">\n\n {new Array(level).fill(0).map((_, index) =>\n <div className={cls(defaultBorderMixin, \"ml-8 border-l h-12\")} key={index}/>)}\n\n <div className={\"m-4\"}>\n <Tooltip title={widget?.name}>\n <PropertyConfigBadge propertyConfig={widget}/>\n </Tooltip>\n </div>\n\n <div className={\"flex flex-col grow p-2 pl-2\"}>\n <Typography variant=\"body1\"\n component=\"span\"\n className=\"grow pr-2\">\n {property.name\n ? property.name\n : \"\\u00a0\"\n }\n </Typography>\n\n <Typography className=\" pr-2\"\n variant={\"body2\"}\n component=\"span\"\n color=\"secondary\">\n {propertyKey}\n </Typography>\n </div>\n\n </div>;\n\n}\n\nexport function ImportDataPreview<M extends Record<string, unknown>>({\n importConfig,\n properties,\n propertiesOrder\n}: {\n importConfig: ImportConfig,\n properties: Properties,\n propertiesOrder: Extract<keyof M, string>[],\n}) {\n const authController = useAuthController();\n const collectionRegistry = useCollectionRegistryController();\n useEffect(() => {\n const mappedData = importConfig.importData.map(d => convertDataToEntity(\n authController,\n collectionRegistry,\n d,\n importConfig.idColumn,\n importConfig.headersMapping,\n properties,\n \"TEMP_PATH\",\n importConfig.defaultValues\n ));\n importConfig.setEntities(mappedData);\n }, []);\n\n const selectionController = useSelectionController();\n\n return <CollectionTableBinding\n title={<div>\n <Typography variant={\"subtitle2\"}>Imported data preview</Typography>\n <Typography variant={\"caption\"}>Entities with the same id will be overwritten</Typography>\n </div>}\n tableController={{\n data: importConfig.entities,\n dataLoading: false,\n noMoreToLoad: false\n }}\n enablePopupIcon={false}\n endAdornment={<div className={\"h-12\"}/>}\n filterable={false}\n sortable={false}\n openEntityMode={\"full_screen\"}\n selectionController={selectionController}\n properties={properties}/>\n\n}\n\nfunction buildHeadersMappingFromData(objArr: object[], properties?: Properties) {\n const headersMapping: Record<string, string> = {};\n objArr.filter(Boolean).forEach((obj) => {\n Object.keys(obj).forEach((key) => {\n const child = (obj as Record<string, unknown>)[key];\n if (child != null && typeof child === \"object\" && !Array.isArray(child)) {\n const childProperty = properties?.[key];\n const childProperties = childProperty && \"properties\" in childProperty ? childProperty.properties : undefined;\n const childHeadersMapping = buildHeadersMappingFromData([child as object], childProperties);\n Object.entries(childHeadersMapping).forEach(([subKey, mapping]) => {\n headersMapping[`${key}.${subKey}`] = `${key}.${mapping}`;\n });\n }\n\n if (!properties) {\n headersMapping[key] = key;\n } else if (key in properties) {\n headersMapping[key] = key;\n } else {\n const slug = slugify(key);\n if (slug in properties) {\n headersMapping[key] = slug;\n } else {\n headersMapping[key] = key;\n }\n }\n\n });\n });\n return headersMapping;\n}\n","export * from \"./ImportCollectionAction\";\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { $ as useBuildCollectionRegistryController, $n as
|
|
2
|
-
import { i as PropertySelectEntry, n as ImportCollectionAction, r as ImportDataPreview } from "./import-
|
|
3
|
-
import { a as downloadDataAsCsv, c as getEntityJsonExportableData, i as downloadBlob, n as ExportCollectionAction, o as downloadEntitiesExport, r as BasicExportAction, s as getEntityCSVExportableData } from "./export-
|
|
1
|
+
import { $ as useBuildCollectionRegistryController, $n as sanitizeUrl, $t as CollectionTableBinding, A as getFieldId, An as ArrayOfReferencesPreview, At as convertFileToJson, B as MapFieldBinding, Bn as getEntityTitlePropertyKeyForEntity, Bt as mergeEntityActions, C as removeEmptyContainers, Cn as MapPropertyPreview, Ct as convertDataToEntity, D as getDefaultFieldConfig, Dn as ArrayEnumPreview, Dt as useImportConfig, E as DEFAULT_FIELD_CONFIGS, En as ArrayPropertyEnumPreview, Et as getInferenceType, F as SelectFieldBinding, Fn as useSidePanel, Ft as FieldCaption, G as useSelectionDialog, Gn as StorageThumbnailInternal, Gt as removeInitialAndTrailingSlashes, H as DateTimeFieldBinding, Hn as StringPropertyPreview, Ht as getCollectionBySlugWithin, I as RepeatFieldBinding, In as CollectionRegistryContext, It as useBreadcrumbsController, J as useBuildUrlController, Jn as renderSkeletonIcon, Jt as resolveCollectionPathIds, K as SelectionTableBinding, Kn as SkeletonPropertyComponent, Kt as removeInitialSlash, L as ReferenceFieldBinding, Ln as useCollectionRegistryController, Lt as BreadcrumbsProvider, M as TextFieldBinding, Mn as ReferencePreview, Mt as ArrayContainer, N as SwitchFieldBinding, Nn as EntityPreviewBinding, Nt as PropertyConfigBadge, O as getDefaultFieldId, On as ArrayOfStorageComponentsPreview, Ot as ImportSaveInProgress, P as StorageUploadFieldBinding, Pn as SidePanelControllerContext, Pt as SearchIconsView, Q as useResolvedCollections, Qn as ImagePreview, Qt as VirtualTableInput, R as MultiSelectFieldBinding, Rn as getEntityPreviewKeys, Rt as resolveEntityAction, S as getInitialEntityValues, Sn as KeyValuePreview, St as DataNewPropertiesMapping, T as PropertyFieldBinding, Tn as ArrayOfStringsPreview, Tt as processValueMapping, U as BlockFieldBinding, Un as EnumValuesChip, Ut as getCollectionPathsCombinations, V as KeyValueFieldBinding, Vn as ArrayPropertyPreview, Vt as addInitialSlash, W as ArrayOfReferencesFieldBinding, Wn as StorageThumbnail, Wt as getLastSegment, X as useTopLevelNavigation, Xn as renderSkeletonText, Xt as resolveViewMode, Y as useBuildNavigationStateController, Yn as renderSkeletonImageThumbnail, Yt as resolveOpenEntityMode, Z as useResolvedViews, Zn as UrlComponentPreview, Zt as useSelectionController, _ as useCollectionsConfigController, _n as getUserLabel, _t as CollectionViewActions, a as namespaceToPropertiesPath, an as NavigationStateContext, ar as FieldBlock, at as useBuildSidePanel, b as extractTouchedValues, bn as BooleanPreview, bt as useCollectionEditorDialogsState, c as buildCollectionGenerationCallback, cn as SideDialogsControllerContext, cr as getBracketNotation, ct as copyEntityAction, d as fromSerializableCollectionConfigs, dn as useClearRestoreValue, dr as getIconForWidget, dt as resetPasswordAction, en as SelectableTable, er as EmptyValue, et as useHistory, f as fromSerializableProperties, fn as ReadOnlyFieldBinding, fr as getPropertiesWithPropertiesOrder, ft as CreationResultDialog, g as toSerializableProperty, gn as UserPreview, gr as isRelationProperty, gt as EntityCardBinding, h as toSerializableProperties, hn as PropertyPreview, hr as isReferenceProperty, ht as CollectionCardViewBinding, i as namespaceToPropertiesOrderPath, in as useUrlController, ir as FormRail, it as getEntityViewWidth, j as VectorFieldBinding, jn as InlineEntityListPreview, jt as unflattenObject, k as getFieldConfig, kn as RelationPreview, kt as ImportFileUpload, l as validateCollectionJson, ln as SelectableTableContext, lr as getDefaultPropertiesOrder, lt as deleteEntityAction, m as toSerializableCollectionConfig, mn as ArrayOfMapsPreview, mr as getResolvedPropertyInPath, mt as EntityViewBinding, n as getFullIdPath, nn as useAdminContext, nr as LabelWithIcon, nt as useResolvedNavigationFrom, o as CollectionGenerationApiError, on as useNavigationStateController, or as isSelfLabellingProperty, ot as EditViewBinding, p as fromSerializableProperty, pn as FieldHelperText, pr as getPropertyInPath, pt as DetailViewBinding, q as SideDialogs, qn as renderSkeletonCaptionText, qt as removeTrailingSlash, r as idToPropertiesPath, rn as UrlContext, rr as FormSections, rt as buildSidePanelsFromUrl, s as DEFAULT_COLLECTION_GENERATION_ENDPOINT, sn as useSideDialogsController, sr as PropertyIdCopyTooltip, st as CollectionViewBinding, t as getFullId, tn as CollectionRowActions, tr as LabelWithIconAndTooltip, tt as resolveNavigationFrom, u as fromSerializableCollectionConfig, un as ArrayCustomShapedFieldBinding, ur as getIconForProperty, ut as editEntityAction, v as EntityFormBinding, vn as useResolvedUser, vt as useCollectionEditorController, w as zodToFormErrors, wn as ArrayOneOfPreview, wt as flattenEntry, x as getChanges, xn as DatePreview, xt as ImportNewPropertyFieldPreview, y as EntityForm, yn as NumberPropertyPreview, yt as ConfigControllerProvider, z as MarkdownEditorFieldBinding, zn as getEntityTitlePropertyKey, zt as resolveEntityView } from "./util-DSh3HRzK.js";
|
|
2
|
+
import { i as PropertySelectEntry, n as ImportCollectionAction, r as ImportDataPreview } from "./import-Cwzf4RW5.js";
|
|
3
|
+
import { a as downloadDataAsCsv, c as getEntityJsonExportableData, i as downloadBlob, n as ExportCollectionAction, o as downloadEntitiesExport, r as BasicExportAction, s as getEntityCSVExportableData } from "./export-B8DIn-u8.js";
|
|
4
4
|
import React, { Suspense, lazy, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
5
5
|
import { deepEqual } from "fast-equals";
|
|
6
6
|
import { Avatar, Button, Card, ChevronDownIcon, ChevronsLeftIcon, ChevronsRightIcon, Chip, CircularProgressCenter, Collapse, Container, Dialog, DialogActions, DialogContent, DialogTitle, ErrorBoundary, ExpandablePanel, IconButton, LogOutIcon, Markdown, Menu, MenuIcon, MenuItem, MoonIcon, PencilIcon, SearchBar, SettingsIcon, Sheet, Skeleton, StarIcon, SunIcon, SunMoonIcon, TextField, Tooltip, Typography, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
|
|
7
7
|
import { buildRoutedRebaseData, getReferenceFrom, resolveDataSource } from "@rebasepro/common";
|
|
8
|
-
import { CollectionResolverRegistrationContext, CollectionScopeProvider, CustomizationControllerContext, ErrorView, IconForView, LanguageToggle, LoginView, NavigationBlockerProvider, NotFoundPage, RebaseDataContext, RebaseLogo, RebaseRoutes, SchemaDriftBanner, StudioBridgeRegistryProvider, UIReferenceView, UnsavedChangesDialog, UserSettingsView, buildCollapsedDefaults, getIcon, getLastSegment as getLastSegment$1, getNavigationEntriesFromPath, useAdminModeController, useAnalyticsController, useAuthController, useBridgeRegistration, useBuildLocalConfigurationPersistence, useCollapsedGroups, useComponentOverride, useCustomizationController, useData, useDataSources, useLargeLayout, useModeController, useNavigationBlocker, useRebaseClient, useRebaseContext, useRebaseRegistry, useRebaseRegistryDispatch, useRestoreScroll, useSlot, useTranslation, useUserConfigurationPersistence } from "@rebasepro/app";
|
|
8
|
+
import { CollectionResolverRegistrationContext, CollectionScopeProvider, CustomizationControllerContext, DEFAULT_API_PATH, ErrorView, IconForView, LanguageToggle, LoginView, NavigationBlockerProvider, NotFoundPage, RebaseDataContext, RebaseLogo, RebaseRoutes, SchemaDriftBanner, StudioBridgeRegistryProvider, UIReferenceView, UnsavedChangesDialog, UserSettingsView, buildCollapsedDefaults, getIcon, getLastSegment as getLastSegment$1, getNavigationEntriesFromPath, useAdminModeController, useAnalyticsController, useAuthController, useBridgeRegistration, useBuildLocalConfigurationPersistence, useCollapsedGroups, useComponentOverride, useCustomizationController, useData, useDataSources, useLargeLayout, useModeController, useNavigationBlocker, useRebaseClient, useRebaseContext, useRebaseRegistry, useRebaseRegistryDispatch, useRestoreScroll, useSlot, useTranslation, useUserConfigurationPersistence } from "@rebasepro/app";
|
|
9
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
import { prettifyIdentifier, toArray } from "@rebasepro/utils";
|
|
11
|
-
import { Link, NavLink, Outlet, Route, useLocation, useNavigate } from "react-router
|
|
11
|
+
import { Link, NavLink, Outlet, Route, useLocation, useNavigate } from "react-router";
|
|
12
12
|
import { DndContext, DragOverlay, KeyboardSensor, MeasuringStrategy, MouseSensor, TouchSensor, closestCenter, closestCorners, getFirstCollision, pointerWithin, rectIntersection, useDndMonitor, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
|
|
13
13
|
import { restrictToVerticalAxis, restrictToWindowEdges } from "@dnd-kit/modifiers";
|
|
14
14
|
import { SortableContext, arrayMove, defaultAnimateLayoutChanges, rectSortingStrategy, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
@@ -232,7 +232,8 @@ function EntityFullScreenRoute({ pathname, navigationEntries, isNew, isCopy }) {
|
|
|
232
232
|
path: collectionPath,
|
|
233
233
|
selectedTab: selectedTab ?? void 0,
|
|
234
234
|
onEditClick: () => {
|
|
235
|
-
|
|
235
|
+
const editUrl = urlController.buildUrlCollectionPath(`${collectionPath}/${entityId}`) + "/edit";
|
|
236
|
+
navigate(editUrl + hash);
|
|
236
237
|
},
|
|
237
238
|
onTabChange: (params) => {
|
|
238
239
|
setSelectedTab(params.selectedTab);
|
|
@@ -253,7 +254,8 @@ function EntityFullScreenRoute({ pathname, navigationEntries, isNew, isCopy }) {
|
|
|
253
254
|
defaultValues: isNew ? defaultValues : void 0,
|
|
254
255
|
onValuesModified: (modified) => blocked.current = modified,
|
|
255
256
|
navigateBack: () => {
|
|
256
|
-
|
|
257
|
+
const detailUrl = urlController.buildUrlCollectionPath(`${collectionPath}/${entityId}`);
|
|
258
|
+
navigate(detailUrl + hash);
|
|
257
259
|
},
|
|
258
260
|
onSaved: (params) => {
|
|
259
261
|
const newSelectedTab = params.selectedTab;
|
|
@@ -932,9 +934,10 @@ function AdminNavigationContent() {
|
|
|
932
934
|
}),
|
|
933
935
|
headerSlot,
|
|
934
936
|
groupsToRender.map((group) => {
|
|
937
|
+
const entriesInGroup = filteredEntries.filter((e) => e.group === group);
|
|
935
938
|
return /* @__PURE__ */ jsx(ResolvedDrawerNavigationGroup, {
|
|
936
939
|
group,
|
|
937
|
-
entries:
|
|
940
|
+
entries: entriesInGroup,
|
|
938
941
|
collapsed: isGroupCollapsed(group),
|
|
939
942
|
onToggleCollapsed: () => toggleGroupCollapsed(group),
|
|
940
943
|
drawerOpen: drawerVisuallyOpen,
|
|
@@ -1397,21 +1400,37 @@ function RebaseAuthGate({ children }) {
|
|
|
1397
1400
|
}
|
|
1398
1401
|
//#endregion
|
|
1399
1402
|
//#region src/collection_editor/useLocalCollectionsConfigController.tsx
|
|
1403
|
+
/**
|
|
1404
|
+
* The guess this hook used to make on its own.
|
|
1405
|
+
*
|
|
1406
|
+
* `process.env.NODE_ENV` here is the *frontend bundle's* build mode. Whether
|
|
1407
|
+
* the schema editor exists is decided by a different process entirely — the
|
|
1408
|
+
* server's own `NODE_ENV`, its `collectionsDir`, whether its collections were
|
|
1409
|
+
* introspected, whether `ts-morph` is installed. The two disagree in every
|
|
1410
|
+
* ordinary setup: a dev frontend against a deployed API, a `baas` project, a
|
|
1411
|
+
* hosted console. When they did, the editor offered itself and every save came
|
|
1412
|
+
* back `404 Not Found`. It is kept only as the answer for a backend too old to
|
|
1413
|
+
* be asked.
|
|
1414
|
+
*/
|
|
1415
|
+
var buildModeGuess = () => process.env.NODE_ENV === "production";
|
|
1400
1416
|
function useLocalCollectionsConfigController(clientOrUrl, baseCollections = [], options) {
|
|
1401
1417
|
const parsedCollections = baseCollections;
|
|
1402
1418
|
const optionsRef = useRef(options);
|
|
1403
1419
|
optionsRef.current = options;
|
|
1420
|
+
const clientBaseUrl = typeof clientOrUrl === "string" ? clientOrUrl : clientOrUrl?.baseUrl ?? "";
|
|
1421
|
+
const clientApiPath = typeof clientOrUrl === "object" && clientOrUrl !== null && clientOrUrl.apiPath || DEFAULT_API_PATH;
|
|
1422
|
+
const editorUrl = `${String(clientBaseUrl).replace(/\/$/, "")}${clientApiPath}/schema-editor`;
|
|
1423
|
+
const resolveToken = async () => {
|
|
1424
|
+
let token = optionsRef.current?.getAuthToken ? await optionsRef.current.getAuthToken() : null;
|
|
1425
|
+
if (!token && typeof clientOrUrl === "object" && clientOrUrl !== null && clientOrUrl.resolveToken) token = await clientOrUrl.resolveToken();
|
|
1426
|
+
return token ?? null;
|
|
1427
|
+
};
|
|
1404
1428
|
const request = async (endpoint, payload) => {
|
|
1405
1429
|
try {
|
|
1406
|
-
|
|
1407
|
-
let baseUrl = typeof clientOrUrl === "string" ? clientOrUrl : "";
|
|
1408
|
-
if (typeof clientOrUrl === "object" && clientOrUrl !== null) {
|
|
1409
|
-
baseUrl = clientOrUrl.baseUrl || baseUrl;
|
|
1410
|
-
if (!token && clientOrUrl.resolveToken) token = await clientOrUrl.resolveToken();
|
|
1411
|
-
}
|
|
1430
|
+
const token = await resolveToken();
|
|
1412
1431
|
const headers = { "Content-Type": "application/json" };
|
|
1413
1432
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
1414
|
-
const response = await fetch(`${
|
|
1433
|
+
const response = await fetch(`${editorUrl}${endpoint}`, {
|
|
1415
1434
|
method: "POST",
|
|
1416
1435
|
headers,
|
|
1417
1436
|
body: JSON.stringify(payload)
|
|
@@ -1433,10 +1452,51 @@ function useLocalCollectionsConfigController(clientOrUrl, baseCollections = [],
|
|
|
1433
1452
|
throw e;
|
|
1434
1453
|
}
|
|
1435
1454
|
};
|
|
1455
|
+
const forcedReadOnly = options?.readOnly;
|
|
1456
|
+
const authKey = options?.authKey ?? null;
|
|
1457
|
+
const [availability, setAvailability] = useState({ state: "unknown" });
|
|
1458
|
+
useEffect(() => {
|
|
1459
|
+
if (forcedReadOnly === true) return;
|
|
1460
|
+
let cancelled = false;
|
|
1461
|
+
(async () => {
|
|
1462
|
+
try {
|
|
1463
|
+
const token = await resolveToken();
|
|
1464
|
+
const response = await fetch(`${editorUrl}/status`, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
|
|
1465
|
+
if (cancelled) return;
|
|
1466
|
+
if (response.status === 404) {
|
|
1467
|
+
setAvailability({ state: "unknown" });
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
const body = await response.json().catch(() => ({}));
|
|
1471
|
+
if (cancelled) return;
|
|
1472
|
+
if (response.ok) setAvailability({
|
|
1473
|
+
state: "known",
|
|
1474
|
+
enabled: body.enabled === true,
|
|
1475
|
+
reason: body.reason
|
|
1476
|
+
});
|
|
1477
|
+
else setAvailability({
|
|
1478
|
+
state: "known",
|
|
1479
|
+
enabled: false,
|
|
1480
|
+
reason: body.error?.message ?? `The backend refused to say whether collections are editable (HTTP ${response.status}).`
|
|
1481
|
+
});
|
|
1482
|
+
} catch {
|
|
1483
|
+
if (!cancelled) setAvailability({ state: "unknown" });
|
|
1484
|
+
}
|
|
1485
|
+
})();
|
|
1486
|
+
return () => {
|
|
1487
|
+
cancelled = true;
|
|
1488
|
+
};
|
|
1489
|
+
}, [
|
|
1490
|
+
editorUrl,
|
|
1491
|
+
forcedReadOnly,
|
|
1492
|
+
authKey
|
|
1493
|
+
]);
|
|
1494
|
+
const readOnly = forcedReadOnly ?? (availability.state === "known" ? !availability.enabled : buildModeGuess());
|
|
1495
|
+
const readOnlyReason = availability.state === "known" && availability.reason ? availability.reason : "Collections can only be edited against a backend running the schema editor, which is off in production.";
|
|
1436
1496
|
return useMemo(() => ({
|
|
1437
1497
|
loading: false,
|
|
1438
|
-
readOnly
|
|
1439
|
-
readOnlyReason
|
|
1498
|
+
readOnly,
|
|
1499
|
+
readOnlyReason,
|
|
1440
1500
|
collections: parsedCollections,
|
|
1441
1501
|
getCollection: (id) => {
|
|
1442
1502
|
const found = parsedCollections.find((c) => c.id === id || c.slug === id);
|
|
@@ -1480,8 +1540,9 @@ function useLocalCollectionsConfigController(clientOrUrl, baseCollections = [],
|
|
|
1480
1540
|
});
|
|
1481
1541
|
},
|
|
1482
1542
|
updatePropertiesOrder: async ({ collection, fullPath, newPropertiesOrder }) => {
|
|
1543
|
+
const collectionId = collection.id || fullPath.split("/").pop();
|
|
1483
1544
|
await request("/collection/save", {
|
|
1484
|
-
collectionId
|
|
1545
|
+
collectionId,
|
|
1485
1546
|
collectionData: { propertiesOrder: newPropertiesOrder }
|
|
1486
1547
|
});
|
|
1487
1548
|
},
|
|
@@ -1491,7 +1552,8 @@ function useLocalCollectionsConfigController(clientOrUrl, baseCollections = [],
|
|
|
1491
1552
|
}), [
|
|
1492
1553
|
clientOrUrl,
|
|
1493
1554
|
parsedCollections,
|
|
1494
|
-
|
|
1555
|
+
readOnly,
|
|
1556
|
+
readOnlyReason,
|
|
1495
1557
|
options?.getAuthToken
|
|
1496
1558
|
]);
|
|
1497
1559
|
}
|
|
@@ -1499,7 +1561,7 @@ function useLocalCollectionsConfigController(clientOrUrl, baseCollections = [],
|
|
|
1499
1561
|
//#region src/components/RebaseNavigation.tsx
|
|
1500
1562
|
var EMPTY_PLUGINS = [];
|
|
1501
1563
|
var EMPTY_COLLECTIONS = [];
|
|
1502
|
-
var CollectionsStudioView = lazy(() => import("./RouterCollectionsStudioView-
|
|
1564
|
+
var CollectionsStudioView = lazy(() => import("./RouterCollectionsStudioView-C3669Ezm.js").then((n) => n.n).then((m) => ({ default: m.RouterCollectionsStudioView })));
|
|
1503
1565
|
/**
|
|
1504
1566
|
* Navigation layer — builds and provides all admin navigation controllers:
|
|
1505
1567
|
* collection registry, URL controller, navigation state, side entity,
|
|
@@ -1566,8 +1628,9 @@ function RebaseNavigation({ children }) {
|
|
|
1566
1628
|
if (missing.length > 0) console.warn(`[Rebase] These data source(s) declare a direct/custom transport but have no client-side driver and will fall back to the default data source: ${missing.map((k) => `"${k}"`).join(", ")}. Provide a \`driver\` for them in \`dataSources\` on <Rebase>.`);
|
|
1567
1629
|
}, [dataSources]);
|
|
1568
1630
|
const internalConfigController = useLocalCollectionsConfigController(rebaseClient, resolvedCollections, collectionEditorEnabled ? {
|
|
1569
|
-
readOnly: collectionEditorOptions?.readOnly
|
|
1570
|
-
getAuthToken: collectionEditorOptions?.getAuthToken ?? authController?.getAuthToken
|
|
1631
|
+
readOnly: collectionEditorOptions?.readOnly,
|
|
1632
|
+
getAuthToken: collectionEditorOptions?.getAuthToken ?? authController?.getAuthToken,
|
|
1633
|
+
authKey: authController?.user?.uid ?? null
|
|
1571
1634
|
} : { readOnly: true });
|
|
1572
1635
|
const schemaView = useMemo(() => {
|
|
1573
1636
|
if (!collectionEditorEnabled) return void 0;
|
|
@@ -1587,14 +1650,10 @@ function RebaseNavigation({ children }) {
|
|
|
1587
1650
|
nestedRoutes: true,
|
|
1588
1651
|
view: /* @__PURE__ */ jsx(Suspense, {
|
|
1589
1652
|
fallback: /* @__PURE__ */ jsx(CircularProgressCenter, {}),
|
|
1590
|
-
children: /* @__PURE__ */ jsx(
|
|
1653
|
+
children: /* @__PURE__ */ jsx(CollectionsStudioViewFromContext, {})
|
|
1591
1654
|
})
|
|
1592
1655
|
};
|
|
1593
|
-
}, [
|
|
1594
|
-
collectionEditorEnabled,
|
|
1595
|
-
registry.studioConfig,
|
|
1596
|
-
internalConfigController
|
|
1597
|
-
]);
|
|
1656
|
+
}, [collectionEditorEnabled, registry.studioConfig]);
|
|
1598
1657
|
const devViews = useMemo(() => {
|
|
1599
1658
|
const base = registry.studioConfig?.devViews ?? [];
|
|
1600
1659
|
if (schemaView) return [...base, schemaView];
|
|
@@ -1648,7 +1707,8 @@ function RebaseNavigation({ children }) {
|
|
|
1648
1707
|
children: /* @__PURE__ */ jsxs(SidePanelProvider, { children: [/* @__PURE__ */ jsx(BridgeAutoRegistrar, {
|
|
1649
1708
|
collectionRegistryController,
|
|
1650
1709
|
urlController,
|
|
1651
|
-
navigationStateController
|
|
1710
|
+
navigationStateController,
|
|
1711
|
+
canWriteToCodebase: !internalConfigController.readOnly
|
|
1652
1712
|
}), children] })
|
|
1653
1713
|
})
|
|
1654
1714
|
})
|
|
@@ -1663,14 +1723,24 @@ function RebaseNavigation({ children }) {
|
|
|
1663
1723
|
return navigationContent;
|
|
1664
1724
|
}
|
|
1665
1725
|
/**
|
|
1726
|
+
* The injected "Edit collections" view, bound to the live config controller.
|
|
1727
|
+
*
|
|
1728
|
+
* Exists so the element stored in the navigation state does not close over a
|
|
1729
|
+
* controller instance — see the comment at its construction site.
|
|
1730
|
+
*/
|
|
1731
|
+
function CollectionsStudioViewFromContext() {
|
|
1732
|
+
return /* @__PURE__ */ jsx(CollectionsStudioView, { configController: useCollectionsConfigController() });
|
|
1733
|
+
}
|
|
1734
|
+
/**
|
|
1666
1735
|
* Internal component that auto-registers admin controllers into the
|
|
1667
1736
|
* self-assembling Studio bridge. Must be rendered inside both the
|
|
1668
1737
|
* navigation contexts and the StudioBridgeRegistryProvider.
|
|
1669
1738
|
*/
|
|
1670
|
-
function BridgeAutoRegistrar({ collectionRegistryController, urlController, navigationStateController }) {
|
|
1739
|
+
function BridgeAutoRegistrar({ collectionRegistryController, urlController, navigationStateController, canWriteToCodebase }) {
|
|
1671
1740
|
useBridgeRegistration("collectionRegistry", collectionRegistryController);
|
|
1672
1741
|
useBridgeRegistration("urlController", urlController);
|
|
1673
1742
|
useBridgeRegistration("navigationState", navigationStateController);
|
|
1743
|
+
useBridgeRegistration("capabilities", useMemo(() => ({ codebase: canWriteToCodebase }), [canWriteToCodebase]));
|
|
1674
1744
|
return null;
|
|
1675
1745
|
}
|
|
1676
1746
|
//#endregion
|
|
@@ -2538,10 +2608,11 @@ function ContentHomePage({ additionalActions, additionalChildrenStart, additiona
|
|
|
2538
2608
|
name: g.name,
|
|
2539
2609
|
entries: g.entries.map((e) => e.slug)
|
|
2540
2610
|
}));
|
|
2541
|
-
|
|
2611
|
+
const all = adminGroupData ? [...draggable, {
|
|
2542
2612
|
name: adminGroupData.name,
|
|
2543
2613
|
entries: adminGroupData.entries.map((e) => e.slug)
|
|
2544
|
-
}] : draggable
|
|
2614
|
+
}] : draggable;
|
|
2615
|
+
onNavigationEntriesUpdate(all);
|
|
2545
2616
|
};
|
|
2546
2617
|
const { isGroupCollapsed, toggleGroupCollapsed } = useCollapsedGroups(useMemo(() => [...items.map((item) => item.name), ...adminGroupData ? [adminGroupData.name] : []], [items, adminGroupData]), "home", useMemo(() => buildCollapsedDefaults(registry.cmsConfig?.navigationGroupMappings, "home"), [registry.cmsConfig?.navigationGroupMappings]));
|
|
2547
2618
|
const { sensors, collisionDetection, onDragStart, onDragOver, onDragEnd, dropAnimation, activeItemForOverlay, activeGroupData, draggingGroupId, containers, dndKitActiveNode, onDragCancel, isDraggingCardOnly, dialogOpenForGroup, setDialogOpenForGroup, handleRenameGroup, handleDialogClose, isHoveringNewGroupDropZone, setIsHoveringNewGroupDropZone } = useHomePageDnd({
|
|
@@ -2719,8 +2790,8 @@ function ContentHomePage({ additionalActions, additionalChildrenStart, additiona
|
|
|
2719
2790
|
}
|
|
2720
2791
|
//#endregion
|
|
2721
2792
|
//#region src/components/CollectionEditorDialogs.tsx
|
|
2722
|
-
var CollectionEditorDialog = lazy(() => import("./CollectionEditorDialog-
|
|
2723
|
-
var PropertyFormDialog = lazy(() => import("./PropertyEditView-
|
|
2793
|
+
var CollectionEditorDialog = lazy(() => import("./CollectionEditorDialog-B-iVE9y3.js").then((n) => n.r).then((m) => ({ default: m.CollectionEditorDialog })));
|
|
2794
|
+
var PropertyFormDialog = lazy(() => import("./PropertyEditView-DoLEH_bQ.js").then((n) => n.t).then((m) => ({ default: m.PropertyFormDialog })));
|
|
2724
2795
|
/**
|
|
2725
2796
|
* Renders the CollectionEditorDialog and PropertyFormDialog inside
|
|
2726
2797
|
* the RebaseShell tree where admin-internal contexts
|
|
@@ -3318,6 +3389,6 @@ function MissingReferenceWidget({ path: pathProp }) {
|
|
|
3318
3389
|
});
|
|
3319
3390
|
}
|
|
3320
3391
|
//#endregion
|
|
3321
|
-
export { AdminModeSyncer, AppBar, ArrayContainer, ArrayCustomShapedFieldBinding, ArrayEnumPreview, ArrayOfMapsPreview, ArrayOfReferencesFieldBinding, ArrayOfReferencesPreview, ArrayOfStorageComponentsPreview, ArrayOfStringsPreview, ArrayOneOfPreview, ArrayPropertyEnumPreview, ArrayPropertyPreview, BasicExportAction, BlockFieldBinding, BooleanPreview, CollectionCardViewBinding, CollectionGenerationApiError, CollectionPanel, CollectionRegistryContext, CollectionRowActions, CollectionTableBinding, CollectionViewActions, CollectionViewBinding, CreationResultDialog, CustomViewRoute, DEFAULT_COLLECTION_GENERATION_ENDPOINT, DEFAULT_FIELD_CONFIGS, DataNewPropertiesMapping, DatePreview, DateTimeFieldBinding, DefaultDrawer, Drawer, DrawerFooterActions, EmptyValue, EntityCardBinding, EntityForm, EntityFormBinding, EntityPreviewBinding, EntityViewBinding, EnumValuesChip, ExportCollectionAction, FieldCaption, FieldHelperText,
|
|
3392
|
+
export { AdminModeSyncer, AppBar, ArrayContainer, ArrayCustomShapedFieldBinding, ArrayEnumPreview, ArrayOfMapsPreview, ArrayOfReferencesFieldBinding, ArrayOfReferencesPreview, ArrayOfStorageComponentsPreview, ArrayOfStringsPreview, ArrayOneOfPreview, ArrayPropertyEnumPreview, ArrayPropertyPreview, BasicExportAction, BlockFieldBinding, BooleanPreview, CollectionCardViewBinding, CollectionGenerationApiError, CollectionPanel, CollectionRegistryContext, CollectionRowActions, CollectionTableBinding, CollectionViewActions, CollectionViewBinding, CreationResultDialog, CustomViewRoute, DEFAULT_COLLECTION_GENERATION_ENDPOINT, DEFAULT_FIELD_CONFIGS, DataNewPropertiesMapping, DatePreview, DateTimeFieldBinding, DefaultDrawer, Drawer, DrawerFooterActions, EmptyValue, EntityCardBinding, EntityForm, EntityFormBinding, EntityPreviewBinding, EntityViewBinding, EnumValuesChip, ExportCollectionAction, FieldBlock, FieldCaption, FieldHelperText, FormRail, FormSections, ImagePreview, ImportCollectionAction, ImportDataPreview, ImportFileUpload, ImportNewPropertyFieldPreview, ImportSaveInProgress, InlineEntityListPreview, KeyValueFieldBinding, KeyValuePreview, LabelWithIcon, LabelWithIconAndTooltip, MapFieldBinding, MapPropertyPreview, MarkdownEditorFieldBinding, MissingReferenceWidget, MultiSelectFieldBinding, NavigationStateContext, NumberPropertyPreview, PropertyConfigBadge, PropertyFieldBinding, PropertyIdCopyTooltip, PropertyPreview, PropertySelectEntry, ReadOnlyFieldBinding, RebaseAdmin, RebaseAuthGate, RebaseLayout, RebaseNavigation, RebaseRoute, RebaseRouteDefs, RebaseShell, ReferenceFieldBinding, ReferencePreview, ReferenceWidget, RelationPreview, RepeatFieldBinding, Scaffold, SearchIconsView, SelectFieldBinding, SelectableTable, SelectableTableContext, SelectionTableBinding, SideDialogs, SidePanelControllerContext, SidePanelProvider, SkeletonPropertyComponent, StorageThumbnail, StorageThumbnailInternal, StorageUploadFieldBinding, StringPropertyPreview, SwitchFieldBinding, TextFieldBinding, UrlComponentPreview, UrlContext, UserPreview, VectorFieldBinding, VirtualTableInput, addInitialSlash, buildCollectionGenerationCallback, buildSidePanelsFromUrl, convertDataToEntity, convertFileToJson, copyEntityAction, createStaticDataController, deleteEntityAction, downloadBlob, downloadDataAsCsv, downloadEntitiesExport, editEntityAction, extractTouchedValues, flattenEntry, fromSerializableCollectionConfig, fromSerializableCollectionConfigs, fromSerializableProperties, fromSerializableProperty, getBracketNotation, getChanges, getCollectionBySlugWithin, getCollectionPathsCombinations, getDefaultFieldConfig, getDefaultFieldId, getDefaultPropertiesOrder, getEntityCSVExportableData, getEntityJsonExportableData, getEntityPreviewKeys, getEntityTitlePropertyKey, getEntityTitlePropertyKeyForEntity, getEntityViewWidth, getFieldConfig, getFieldId, getFullId, getFullIdPath, getIconForProperty, getIconForWidget, getInferenceType, getInitialEntityValues, getLastSegment, getPropertiesWithPropertiesOrder, getPropertyInPath, getResolvedPropertyInPath, getUserLabel, idToPropertiesPath, isReferenceProperty, isRelationProperty, isSelfLabellingProperty, mapPropertiesToConfigs, mapPropertyToConfig, mergeEntityActions, namespaceToPropertiesOrderPath, namespaceToPropertiesPath, processValueMapping, removeEmptyContainers, removeInitialAndTrailingSlashes, removeInitialSlash, removeTrailingSlash, renderSkeletonCaptionText, renderSkeletonIcon, renderSkeletonImageThumbnail, renderSkeletonText, resetPasswordAction, resolveCollectionPathIds, resolveEntityAction, resolveEntityView, resolveNavigationFrom, sanitizeUrl, toSerializableCollectionConfig, toSerializableProperties, toSerializableProperty, unflattenObject, useAdminContext, useApp, useBreadcrumbsController, useBuildCollectionRegistryController, useBuildNavigationStateController, useBuildSidePanel, useBuildUrlController, useClearRestoreValue, useCollectionDataController, useCollectionEditorController, useCollectionRegistryController, useCollectionsConfigController, useHistory, useImportConfig, useJsonCollectionsConfigController, useLocalCollectionsConfigController, useNavigationStateController, useResolvedCollections, useResolvedNavigationFrom, useResolvedUser, useResolvedViews, useSelectionController, useSelectionDialog, useSideDialogsController, useSidePanel, useTopLevelNavigation, useUrlController, validateCollectionJson, zodToFormErrors };
|
|
3322
3393
|
|
|
3323
3394
|
//# sourceMappingURL=index.js.map
|