@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.
Files changed (172) hide show
  1. package/README.md +1 -1
  2. package/dist/{CollectionEditorDialog-Dpc75wIZ.js → CollectionEditorDialog-B-iVE9y3.js} +75 -62
  3. package/dist/{CollectionEditorDialog-Dpc75wIZ.js.map → CollectionEditorDialog-B-iVE9y3.js.map} +1 -1
  4. package/dist/{PropertyEditView-DH3XZC2x.js → PropertyEditView-DoLEH_bQ.js} +36 -16
  5. package/dist/{PropertyEditView-DH3XZC2x.js.map → PropertyEditView-DoLEH_bQ.js.map} +1 -1
  6. package/dist/{RouterCollectionsStudioView-BZLq4MGp.js → RouterCollectionsStudioView-C3669Ezm.js} +5 -5
  7. package/dist/{RouterCollectionsStudioView-BZLq4MGp.js.map → RouterCollectionsStudioView-C3669Ezm.js.map} +1 -1
  8. package/dist/collection_editor/serializable_types.d.ts +4 -1
  9. package/dist/collection_editor/ui/collection_editor/CollectionRelationsTab.d.ts +1 -0
  10. package/dist/collection_editor/useLocalCollectionsConfigController.d.ts +8 -0
  11. package/dist/collection_editor_ui.js +5 -5
  12. package/dist/collection_editor_ui.js.map +1 -1
  13. package/dist/components/CollectionViewBinding/SplitListCloseButton.d.ts +10 -0
  14. package/dist/components/CollectionViewBinding/SplitViewContext.d.ts +23 -0
  15. package/dist/components/CollectionViewBinding/hooks/useKanbanDragAndDrop.d.ts +26 -0
  16. package/dist/components/DetailViewBinding.d.ts +5 -0
  17. package/dist/components/EditFormActions.d.ts +12 -1
  18. package/dist/components/EditViewBinding.d.ts +6 -1
  19. package/dist/components/EntityIdentityBar.d.ts +53 -0
  20. package/dist/components/EntityInspector.d.ts +29 -0
  21. package/dist/components/EntityPreviewNesting.d.ts +25 -0
  22. package/dist/components/EntityViewBinding.d.ts +9 -0
  23. package/dist/components/InlineEntityPreview.d.ts +47 -0
  24. package/dist/components/index.d.ts +2 -0
  25. package/dist/editor.js +18 -10
  26. package/dist/editor.js.map +1 -1
  27. package/dist/{export-BzIpUdSK.js → export-B8DIn-u8.js} +2 -2
  28. package/dist/{export-BzIpUdSK.js.map → export-B8DIn-u8.js.map} +1 -1
  29. package/dist/form/EntityForm.d.ts +1 -1
  30. package/dist/form/EntityFormActions.d.ts +11 -1
  31. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  32. package/dist/form/components/FieldBlock.d.ts +30 -0
  33. package/dist/form/components/FormRail.d.ts +18 -0
  34. package/dist/form/components/FormSections.d.ts +16 -0
  35. package/dist/form/components/index.d.ts +3 -2
  36. package/dist/form/components/useRailVisible.d.ts +24 -0
  37. package/dist/form/field_bindings/BinaryFieldBinding.d.ts +1 -1
  38. package/dist/form/field_bindings/DateTimeFieldBinding.d.ts +1 -1
  39. package/dist/form/field_bindings/GeopointFieldBinding.d.ts +1 -1
  40. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
  41. package/dist/form/field_bindings/MultiSelectFieldBinding.d.ts +1 -1
  42. package/dist/form/field_bindings/ReadOnlyFieldBinding.d.ts +1 -1
  43. package/dist/form/field_bindings/SelectFieldBinding.d.ts +1 -1
  44. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +1 -1
  45. package/dist/form/field_bindings/SwitchFieldBinding.d.ts +1 -1
  46. package/dist/form/field_bindings/TextFieldBinding.d.ts +1 -1
  47. package/dist/form/field_bindings/UserSelectFieldBinding.d.ts +1 -1
  48. package/dist/form/field_bindings/VectorFieldBinding.d.ts +1 -1
  49. package/dist/{history-g9688IaK.js → history-ClwDEtqN.js} +32 -31
  50. package/dist/history-ClwDEtqN.js.map +1 -0
  51. package/dist/hooks/navigation/utils.d.ts +16 -0
  52. package/dist/hooks/useBuildSidePanel.d.ts +14 -0
  53. package/dist/hooks/useEntityDisplayTitle.d.ts +18 -0
  54. package/dist/hooks/useRecordActions.d.ts +17 -0
  55. package/dist/{import-BjUG-cIY.js → import-Cwzf4RW5.js} +2 -2
  56. package/dist/{import-BjUG-cIY.js.map → import-Cwzf4RW5.js.map} +1 -1
  57. package/dist/index.js +106 -35
  58. package/dist/index.js.map +1 -1
  59. package/dist/markdown-CklalUUk.js.map +1 -1
  60. package/dist/preview/components/InlineEntityListPreview.d.ts +15 -0
  61. package/dist/preview/index.d.ts +1 -0
  62. package/dist/preview/property_previews/ArrayOfReferencesPreview.d.ts +1 -1
  63. package/dist/preview/property_previews/ArrayOfRelationsPreview.d.ts +1 -1
  64. package/dist/types/components/EntityFormActionsProps.d.ts +0 -1
  65. package/dist/types/components/EntityFormProps.d.ts +0 -5
  66. package/dist/types/fields.d.ts +24 -0
  67. package/dist/{util-B5fJm1FA.js → util-DSh3HRzK.js} +1974 -1130
  68. package/dist/util-DSh3HRzK.js.map +1 -0
  69. package/package.json +32 -34
  70. package/src/collection_editor/ConfigControllerProvider.tsx +1 -1
  71. package/src/collection_editor/serializable_types.ts +4 -3
  72. package/src/collection_editor/serializable_utils.ts +51 -17
  73. package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +65 -22
  74. package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +38 -30
  75. package/src/collection_editor/ui/collection_editor/CollectionRelationsTab.tsx +3 -1
  76. package/src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx +1 -1
  77. package/src/collection_editor/ui/collection_editor/RouterCollectionsStudioView.tsx +1 -1
  78. package/src/collection_editor/ui/collection_editor/properties/RelationPropertyField.tsx +16 -0
  79. package/src/collection_editor/useLocalCollectionsConfigController.tsx +127 -15
  80. package/src/collection_editor/validateCollectionJson.ts +3 -3
  81. package/src/components/AdminModeSyncer.tsx +1 -1
  82. package/src/components/CollectionTableBinding/PropertyTableCell.tsx +10 -2
  83. package/src/components/CollectionTableBinding/column_utils.tsx +26 -2
  84. package/src/components/CollectionTableBinding/table_bindings.tsx +2 -1
  85. package/src/components/CollectionViewBinding/CollectionBoardViewBinding.tsx +23 -16
  86. package/src/components/CollectionViewBinding/CollectionViewActions.tsx +7 -1
  87. package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +8 -2
  88. package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +22 -11
  89. package/src/components/CollectionViewBinding/SplitListCloseButton.tsx +33 -0
  90. package/src/components/CollectionViewBinding/SplitListView.tsx +18 -3
  91. package/src/components/CollectionViewBinding/SplitViewContext.tsx +34 -0
  92. package/src/components/CollectionViewBinding/hooks/useKanbanDragAndDrop.ts +48 -9
  93. package/src/components/CollectionViewBinding/useBoardDataController.tsx +80 -25
  94. package/src/components/CollectionViewBinding/usePreviewSlots.ts +18 -6
  95. package/src/components/DefaultAppBar.tsx +1 -1
  96. package/src/components/DefaultDrawer.tsx +1 -1
  97. package/src/components/DetailViewBinding.tsx +100 -143
  98. package/src/components/DrawerNavigationItem.tsx +1 -1
  99. package/src/components/EditFormActions.tsx +54 -129
  100. package/src/components/EditViewBinding.tsx +182 -93
  101. package/src/components/EntityIdentityBar.tsx +288 -0
  102. package/src/components/EntityInspector.tsx +132 -0
  103. package/src/components/EntityPreviewBinding.tsx +6 -2
  104. package/src/components/EntityPreviewNesting.tsx +50 -0
  105. package/src/components/EntityViewBinding.tsx +35 -32
  106. package/src/components/HomePage/FavouritesView.tsx +1 -1
  107. package/src/components/HomePage/NavigationCardBinding.tsx +1 -1
  108. package/src/components/HomePage/SmallNavigationCard.tsx +1 -1
  109. package/src/components/InlineEntityPreview.tsx +149 -0
  110. package/src/components/RebaseLayout.tsx +1 -1
  111. package/src/components/RebaseNavigation.tsx +45 -5
  112. package/src/components/RebaseRouteDefs.tsx +1 -1
  113. package/src/components/SelectableTable/SelectableTable.tsx +2 -2
  114. package/src/components/SidePanelBinding.tsx +19 -13
  115. package/src/components/UserSelector.tsx +12 -6
  116. package/src/components/common/default_entity_actions.tsx +4 -3
  117. package/src/components/history/EntityHistoryEntry.tsx +28 -7
  118. package/src/components/history/EntityHistoryView.tsx +20 -20
  119. package/src/components/history/LastEditedByIndicator.tsx +4 -3
  120. package/src/components/index.ts +2 -0
  121. package/src/data_import/utils/get_import_inference_type.ts +6 -0
  122. package/src/form/EntityForm.tsx +267 -179
  123. package/src/form/EntityFormActions.tsx +50 -99
  124. package/src/form/EntityFormBinding.tsx +4 -2
  125. package/src/form/PropertyFieldBinding.tsx +4 -0
  126. package/src/form/components/FieldBlock.tsx +119 -0
  127. package/src/form/components/FormRail.tsx +125 -0
  128. package/src/form/components/FormSections.tsx +118 -0
  129. package/src/form/components/index.tsx +3 -2
  130. package/src/form/components/useRailVisible.tsx +46 -0
  131. package/src/form/field_bindings/BinaryFieldBinding.tsx +3 -2
  132. package/src/form/field_bindings/DateTimeFieldBinding.tsx +3 -2
  133. package/src/form/field_bindings/GeopointFieldBinding.tsx +3 -2
  134. package/src/form/field_bindings/MapFieldBinding.tsx +1 -2
  135. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +3 -2
  136. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +2 -1
  137. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -1
  138. package/src/form/field_bindings/ReferenceFieldBinding.tsx +2 -1
  139. package/src/form/field_bindings/RelationFieldBinding.tsx +6 -4
  140. package/src/form/field_bindings/RepeatFieldBinding.tsx +4 -1
  141. package/src/form/field_bindings/SelectFieldBinding.tsx +3 -2
  142. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +31 -8
  143. package/src/form/field_bindings/SwitchFieldBinding.tsx +7 -3
  144. package/src/form/field_bindings/TextFieldBinding.tsx +18 -16
  145. package/src/form/field_bindings/UserSelectFieldBinding.tsx +3 -2
  146. package/src/form/field_bindings/VectorFieldBinding.tsx +2 -1
  147. package/src/hooks/navigation/useBuildUrlController.tsx +1 -1
  148. package/src/hooks/navigation/useTopLevelNavigation.ts +18 -7
  149. package/src/hooks/navigation/utils.ts +20 -0
  150. package/src/hooks/useBuildSideDialogsController.tsx +2 -2
  151. package/src/hooks/useBuildSidePanel.tsx +22 -61
  152. package/src/hooks/useEntityDisplayTitle.tsx +60 -0
  153. package/src/hooks/useHistory.ts +6 -5
  154. package/src/hooks/useRecordActions.tsx +69 -0
  155. package/src/hooks/useResolvedUsers.ts +10 -5
  156. package/src/preview/PropertyPreview.tsx +48 -22
  157. package/src/preview/components/InlineEntityListPreview.tsx +42 -0
  158. package/src/preview/components/ReferencePreview.tsx +34 -14
  159. package/src/preview/components/RelationPreview.tsx +34 -14
  160. package/src/preview/index.ts +1 -0
  161. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +24 -1
  162. package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +25 -1
  163. package/src/routes/RebaseRoute.tsx +2 -2
  164. package/src/types/components/EntityFormActionsProps.tsx +0 -1
  165. package/src/types/components/EntityFormProps.tsx +0 -6
  166. package/src/types/fields.tsx +27 -0
  167. package/dist/form/components/FormEntry.d.ts +0 -6
  168. package/dist/form/components/FormLayout.d.ts +0 -5
  169. package/dist/history-g9688IaK.js.map +0 -1
  170. package/dist/util-B5fJm1FA.js.map +0 -1
  171. package/src/form/components/FormEntry.tsx +0 -22
  172. 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":";;;;;;;;AAGA,SAAgB,SAAS,EAAE,QAAwB;CAC/C,OACI,oBAAC,SAAD;EAAS,OAAO,KAAK,SAAS,KAAK;YAC/B,qBAAC,MAAD;GAAM,MAAM;GAAS,WAAW;aAAhC,CACK,KAAK,YAAY,oBAAC,OAAD;IACd,WAAW;IACX,KAAK,KAAK;IAAU,KAAK,KAAK,eAAe;GAAgB,CAAA,GACjE,oBAAC,QAAD,EAAA,UAAO,KAAK,eAAe,KAAK,SAAS,KAAK,IAAU,CAAA,CACtD;;CACD,CAAA;AAEjB;;;;;;ACIA,SAAS,UAAU,GAAY,GAAqB;CAChD,IAAI,MAAM,GAAG,OAAO;CACpB,IAAI,KAAK,QAAQ,KAAK,MAAM,OAAO,MAAM;CACzC,IAAI,OAAO,MAAM,OAAO,GAAG,OAAO;CAClC,IAAI,OAAO,MAAM,UAAU,OAAO;CAElC,IAAI,MAAM,QAAQ,CAAC,GAAG;EAClB,IAAI,CAAC,MAAM,QAAQ,CAAC,KAAK,EAAE,WAAY,EAAgB,QAAQ,OAAO;EACtE,OAAO,EAAE,OAAO,MAAM,MAAM,UAAU,MAAO,EAAgB,EAAE,CAAC;CACpE;CAEA,IAAI,MAAM,QAAQ,CAAC,GAAG,OAAO;CAE7B,MAAM,OAAO;CACb,MAAM,OAAO;CACb,MAAM,QAAQ,OAAO,KAAK,IAAI;CAC9B,MAAM,QAAQ,OAAO,KAAK,IAAI;CAC9B,IAAI,MAAM,WAAW,MAAM,QAAQ,OAAO;CAC1C,OAAO,MAAM,OAAM,QAAO,OAAO,QAAQ,UAAU,KAAK,MAAM,KAAK,IAAI,CAAC;AAC5E;AAWA,SAAS,kBAAkB,EACvB,qBACA,eACA,eAKD;CACC,IAAI,OAAO,wBAAwB,YAAY,OAAO,wBAAwB,UAC1E,OAAO,oBAAC,YAAD;EAAY,SAAS;EAAW,OAAO;EAAa,WAAU;YAChE;CACO,CAAA;MACT,IAAI,OAAO,wBAAwB,WACtC,OAAO,oBAAC,YAAD;EAAY,SAAS;EAAW,OAAO;EAAa,WAAU;YAChE,sBAAsB,SAAS;CACxB,CAAA;MAEZ,OAAO,oBAAC,SAAD;EACH,MAAM;EACN,OAAO,qBAAC,OAAD;GAAK,WAAW;aAAhB,CACH,oBAAC,YAAD;IAAY,SAAS;IAAW,OAAO;cAAa;GAExC,CAAA,GACZ,oBAAC,iBAAD;IACiB;IACb,OAAO;IACP,UAAU;IACV,MAAM;GAAS,CAAA,CAClB;;YACL,oBAAC,eAAD;GAAe,MAAM,SAAS;GAAU,OAAO;GAAY,WAAW;EAAQ,CAAA;CACzE,CAAA;AAEjB;;;;;AAMA,SAAgB,mBAAmB,EAC/B,SACA,OACA,YACA,MACA,SACwB;CAExB,MAAM,iBAAiB,kBAAkB;CAOzC,MAAM,gBAAgB,QAAM,cAAc;EACtC,MAAM,MAAM,MAAM;EAClB,IAAI,CAAC,OAAO,CAAC,YAAY,OAAO;EAChC,OAAO,IAAI,QAAO,QAAO;GACrB,MAAM,WAAW,kBAAkB,WAAW,YAAY,GAAG;GAC7D,IAAI,CAAC,UAAU,OAAO;GACtB,IAAI,UAAU,YAAY,SAAS,MAAM,OAAO;GAChD,OAAO,EAAE,SAAS,SAAS,UAAU,eAAe,YAAY,SAAS;EAC7E,CAAC;CACL,GAAG,CAAC,MAAM,gBAAgB,UAAU,CAAC;CAErC,MAAM,iBAAiB,MAAM;CAC7B,MAAM,YAAY,IAAI,KAAK,MAAM,UAAU;CAC3C,MAAM,YAAY,MAAM;CAGxB,MAAM,cAAc,eAAe;CACnC,MAAM,cAAc,cAAc,aAAa,MACzC,cACA,KAAA;CAEN,OAAO,qBAAC,OAAD;EAAK,WAAW;YAAhB,CACH,qBAAC,OAAD;GAAK,WAAW;aAAhB;IACI,oBAAC,YAAD;KAAY,SAAS;KAAS,OAAO;eAChC,UAAU,eAAe;IAClB,CAAA;IACZ,oBAAC,MAAD;KAAM,MAAM;eACP,MAAM;IACL,CAAA;IACL,CAAC,eAAe,aAAa,oBAAC,MAAD;KAAM,MAAM;eAAU;IAAgB,CAAA;IACnE,eAAe,oBAAC,UAAD,EAAU,MAAM,YAAa,CAAA;GAC5C;MACL,qBAAC,OAAD;GACI,WAAW,IACP,gCACA,gBACA,UACA,gBACA,QAAQ,yDAAyD,IACjE,SAAS,UAAU,QAAQ,aAC3B,0BACA,kBACJ;aAVJ,CAYK,SAED,qBAAC,OAAD;IAAK,WAAW;cAAhB,CAEK,iBAAiB,cAAc,cAAc,KAAK,QAAgB;KAC/D,MAAM,gBAAgB,kBAAkB,WAAW,YAAY,GAAG;KAClE,MAAM,cAAc,MAAM,SAAS,eAAe,MAAM,QAAQ,GAAG,IAAI,KAAA;KACvE,MAAM,sBAAsB,iBAAiB,eAAe,gBAAgB,GAAG,IAAI,KAAA;KAEnF,MAAM,UAAU,gBACV,oBAAC,iBAAD;MACE,aAAa;MACb,OAAO;MACP,UAAU;MACV,MAAM;KAAS,CAAA,IACjB,oBAAC,YAAD;MAAY,SAAS;gBAClB,OAAO,gBAAgB,WAAW,cAAc,KAAK,UAAU,WAAW;KACnE,CAAA;KAQhB,OACI,qBAAC,OAAD;MACI,WAAU;gBADd,CAEI,oBAAC,YAAD;OAAY,SAAS;OACjB,OAAO;OACP,WAAU;iBACT;MACO,CAAA,GACZ,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACK,wBAAwB,KAAA,KAAa,CAAC,UAAU,qBAAqB,WAAW,KAC7E,oBAAC,mBAAD;QAAwC;QACrB;QACf,aAAa;OAAK,CAAA,GAEzB,OACA;QACJ;QAfK,cAAc,GAenB;IAEb,CAAC,IAEC,CAAC,iBAAiB,cAAc,WAAW,MACzC,oBAAC,YAAD;KAAY,SAAS;KAAW,OAAO;eAClC,MAAM,WAAW,WAAW,mBACzB,MAAM,WAAW,WAAW,mBAAmB;IAC3C,CAAA,CAGf;KAEJ;IACJ;;AACT;;;;;;;AC3LA,SAAgB,kBAAqD,EACjE,QACA,YACA,eAC0B;CAE1B,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,qBAAqB,sBAAsB;CAC1B,kBAAkB;CACzC,MAAM,QAAQ,aAAa,OAAO;CAElC,MAAM,OAAO,WAAW;CACxB,MAAM,WAAW,QAAQ;CAEzB,MAAM,EACF,SACA,WACA,SACA,UACA,WACA,WAAW;EACX;EACA;EACA,SAAS,CAAC,CAAC;EACX,UAAU;CACd,CAAC;CAED,MAAM,CAAC,iBAAiB,sBAAsB,SAA6B;CAC3E,MAAM,CAAC,aAAa,kBAAkB,SAAS,KAAK;CAEpD,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,cAAc,OAAuB,IAAI;CAG/C,gBAAgB;EACZ,MAAM,mBAAmB,aAAa;EACtC,MAAM,kBAAkB,YAAY;EAEpC,IAAI,CAAC,oBAAoB,CAAC,mBAAmB,CAAC,WAAW,WAAW;EAEpE,MAAM,WAAW,IAAI,sBAChB,oBAAoB;GACjB,IAAI,gBAAgB,EAAE,CAAC,kBAAkB,WAAW,CAAC,WACjD,SAAS;EAEjB,GACA;GACI,MAAM;GACN,YAAY;GACZ,WAAW;EACf,CACJ;EAEA,SAAS,QAAQ,eAAe;EAEhC,aAAa,SAAS,WAAW;CACrC,GAAG;EAAC;EAAS;EAAW,QAAQ;EAAQ;CAAQ,CAAC;CAEjD,IAAI,CAAC,QACD,OAAO,oBAAC,OAAD;EAAK,WAAU;YAClB,oBAAC,OAAD,EAAA,UAAQ,EAAE,oBAAoB,KAAK,iDAAwD,CAAA;CAC1F,CAAA;CAGT,eAAe,SAAS,WAAmB;EACvC,eAAe,IAAI;EACnB,IAAI;GACA,MAAM,iBAAiB,MAAM,OAAO,SAAS;GAC7C,mBAAmB,KAAA,CAAS;GAI5B,IAAI,aAAa,QAAQ,aAAa,gBAClC,YAAY,OAAO,UAAU;IACzB,QAAQ;IACR,aAAa;IACb,SAAS,CAAC;GACd,CAAC;GAGL,mBAAmB,KAAK;IACpB,SAAS,EAAE,kBAAkB,KAAK;IAClC,MAAM;GACV,CAAC;EACL,SAAS,OAAO;GACZ,QAAQ,MAAM,2BAA2B,KAAK;GAC9C,mBAAmB,KAAK;IACpB,SAAS,EAAE,sBAAsB,KAAK;IACtC,MAAM;GACV,CAAC;EACL,UAAU;GACN,eAAe,KAAK;EACxB;CACJ;CAEA,MAAM,cAAc,kBACd,QAAQ,MAAK,MAAK,EAAE,OAAO,eAAe,IAC1C,KAAA;CAKN,OAAO,qBAAC,OAAD;EACH,KAAK;EACL,WAAW,IAAI,4EAA4E;YAFxF,CAGH,qBAAC,OAAD;GAAK,WAAU;aAAf;IAEK,aAAa,QAAQ,WAAW,KAC7B,oBAAC,OAAD;KAAK,WAAU;eACV;MAAC;MAAG;MAAG;KAAC,CAAC,CAAC,KAAI,MACX,qBAAC,OAAD;MAAa,WAAU;gBAAvB,CACI,oBAAC,OAAD,EAAK,WAAU,sDAAsD,CAAA,GACrE,oBAAC,OAAD,EAAK,WAAU,8GAA8G,CAAA,CAC5H;QAHK,CAGL,CACR;IACA,CAAA;IAGR,CAAC,aAAa,QAAQ,WAAW,KAAK,qBAAA,UAAA,EAAA,UAAA,CACnC,oBAAC,OAAD,EAAA,UACK,EAAE,eAAe,KAAK,uBACpB,CAAA,GACP,oBAAC,YAAD;KAAY,SAAS;eAChB,EAAE,2BAA2B,KAAK;IAC3B,CAAA,CACd,EAAA,CAAA;IAED,QAAQ,KAAK,UACV,oBAAC,OAAD;KAAoB,WAAU;eAC1B,oBAAC,oBAAD;MACW;MACK;MACZ,MAAM;MACN,SACI,oBAAC,SAAD;OAAS,OAAO,EAAE,gBAAgB,KAAK;OACnC,WAAW;iBACX,oBAAC,YAAD;QACI,eAAe;SACX,IAAI,OACA,mBAAmB,KAAK;UACpB,SAAS,EAAE,sBAAsB,KAAK;UACtC,MAAM;SACV,CAAC;cAED,mBAAmB,MAAM,EAAE;QAEnC;kBACA,oBAAC,aAAD,CAAa,CAAA;OACL,CAAA;MACP,CAAA;KAEhB,CAAA;IACA,GAxBK,MAAM,EAwBX,CACR;IAGA,QAAQ,SAAS,KACd,qBAAC,OAAD;KAAK,KAAK;KAAa,WAAU;eAAjC,CACK,aAAa,oBAAC,OAAD,EAAA,UAAQ,EAAE,SAAS,EAAS,CAAA,GACzC,CAAC,WAAW,QAAQ,SAAS,MAAM,oBAAC,OAAD,EAAA,UAAQ,EAAE,iBAAiB,KAAK,kBAAyB,CAAA,CAC5F;;GAER;MAEL,oBAAC,eAAD,EAAA,UACI,oBAAC,oBAAD;GACI,MAAM,QAAQ,eAAe;GAC7B,gBAAgB;IACZ,IAAI,iBAAiB,SAAS,eAAe;GACjD;GACA,gBAAgB,mBAAmB,KAAA,CAAS;GAC5C,OAAO,oBAAC,YAAD;IAAY,SAAS;cAAc,EAAE,sBAAsB,KAAK;GAA2C,CAAA;GAClH,MAAM,cACA,oBAAC,OAAD;IAAK,WAAU;cACb,qBAAC,YAAD;KAAY,SAAS;KAAW,OAAO;eAAvC;MAAoD;MACD;MAC9C,IAAI,KAAK,YAAY,UAAU,CAAC,CAAC,eAAe;MAAE;KAE3C;;GACX,CAAA,IACH;EAET,CAAA,EACU,CAAA,CACd;;AACT"}
@@ -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, Gn as getPropertyInPath, In as useCollectionRegistryController, Nt as PropertyConfigBadge, Ot as ImportSaveInProgress, St as DataNewPropertiesMapping, Wn as getPropertiesWithPropertiesOrder, Zt as useSelectionController, k as getFieldConfig, kt as ImportFileUpload } from "./util-B5fJm1FA.js";
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-BjUG-cIY.js.map
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 StorageThumbnailInternal, $t as CollectionTableBinding, A as getFieldId, An as ArrayOfReferencesPreview, At as convertFileToJson, B as MapFieldBinding, Bn as getBracketNotation, Bt as getCollectionBySlugWithin, 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 CollectionRegistryContext, Ft as FieldCaption, G as useSelectionDialog, Gn as getPropertyInPath, Gt as removeTrailingSlash, H as DateTimeFieldBinding, Hn as getIconForProperty, Ht as getLastSegment, I as RepeatFieldBinding, In as useCollectionRegistryController, It as useBreadcrumbsController, J as useBuildUrlController, Jn as isRelationProperty, Jt as resolveViewMode, K as SelectionTableBinding, Kn as getResolvedPropertyInPath, Kt as resolveCollectionPathIds, L as ReferenceFieldBinding, Ln as getEntityPreviewKeys, Lt as BreadcrumbsProvider, M as TextFieldBinding, Mn as EntityPreviewBinding, Mt as ArrayContainer, N as SwitchFieldBinding, Nn as SidePanelControllerContext, Nt as PropertyConfigBadge, O as getDefaultFieldId, On as ArrayOfStorageComponentsPreview, Ot as ImportSaveInProgress, P as StorageUploadFieldBinding, Pn as useSidePanel, Pt as SearchIconsView, Q as useResolvedCollections, Qn as StorageThumbnail, Qt as VirtualTableInput, R as MultiSelectFieldBinding, Rn as getEntityTitlePropertyKey, Rt as mergeEntityActions, S as getInitialEntityValues, Sn as KeyValuePreview, St as DataNewPropertiesMapping, T as PropertyFieldBinding, Tn as ArrayOfStringsPreview, Tt as processValueMapping, U as BlockFieldBinding, Un as getIconForWidget, Ut as removeInitialAndTrailingSlashes, V as KeyValueFieldBinding, Vn as getDefaultPropertiesOrder, Vt as getCollectionPathsCombinations, W as ArrayOfReferencesFieldBinding, Wn as getPropertiesWithPropertiesOrder, Wt as removeInitialSlash, X as useTopLevelNavigation, Xn as StringPropertyPreview, Xt as resolveEntityView, Y as useBuildNavigationStateController, Yn as ArrayPropertyPreview, Yt as resolveEntityAction, Z as useResolvedViews, Zn as EnumValuesChip, Zt as useSelectionController, _ as useCollectionsConfigController, _n as getUserLabel, _t as CollectionViewActions, a as namespaceToPropertiesPath, an as NavigationStateContext, ar as UrlComponentPreview, at as useBuildSidePanel, b as extractTouchedValues, bn as BooleanPreview, bt as useCollectionEditorDialogsState, c as buildCollectionGenerationCallback, cn as SideDialogsControllerContext, cr as EmptyValue, ct as copyEntityAction, d as fromSerializableCollectionConfigs, dn as useClearRestoreValue, dr as LabelWithIcon, dt as resetPasswordAction, en as SelectableTable, er as SkeletonPropertyComponent, et as useHistory, f as fromSerializableProperties, fn as ReadOnlyFieldBinding, fr as FormLayout, ft as CreationResultDialog, g as toSerializableProperty, gn as UserPreview, gt as EntityCardBinding, h as toSerializableProperties, hn as PropertyPreview, ht as CollectionCardViewBinding, i as namespaceToPropertiesOrderPath, in as useUrlController, ir as renderSkeletonText, it as getEntityViewWidth, j as VectorFieldBinding, jn as ReferencePreview, jt as unflattenObject, k as getFieldConfig, kn as RelationPreview, kt as ImportFileUpload, l as validateCollectionJson, ln as SelectableTableContext, lr as LabelWithIconAndTooltip, lt as deleteEntityAction, m as toSerializableCollectionConfig, mn as ArrayOfMapsPreview, mt as EntityViewBinding, n as getFullIdPath, nn as useAdminContext, nr as renderSkeletonIcon, nt as useResolvedNavigationFrom, o as CollectionGenerationApiError, on as useNavigationStateController, or as ImagePreview, ot as EditViewBinding, p as fromSerializableProperty, pn as FieldHelperText, pr as FormEntry, pt as DetailViewBinding, q as SideDialogs, qn as isReferenceProperty, qt as resolveOpenEntityMode, r as idToPropertiesPath, rn as UrlContext, rr as renderSkeletonImageThumbnail, rt as buildSidePanelsFromUrl, s as DEFAULT_COLLECTION_GENERATION_ENDPOINT, sn as useSideDialogsController, sr as sanitizeUrl, st as CollectionViewBinding, t as getFullId, tn as CollectionRowActions, tr as renderSkeletonCaptionText, tt as resolveNavigationFrom, u as fromSerializableCollectionConfig, un as ArrayCustomShapedFieldBinding, ur as PropertyIdCopyTooltip, 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 getEntityTitlePropertyKeyForEntity, zt as addInitialSlash } from "./util-B5fJm1FA.js";
2
- import { i as PropertySelectEntry, n as ImportCollectionAction, r as ImportDataPreview } from "./import-BjUG-cIY.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-BzIpUdSK.js";
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-dom";
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
- navigate(urlController.buildUrlCollectionPath(`${collectionPath}/${entityId}`) + "/edit" + hash);
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
- navigate(urlController.buildUrlCollectionPath(`${collectionPath}/${entityId}`) + hash);
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: filteredEntries.filter((e) => e.group === group),
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
- let token = optionsRef.current?.getAuthToken ? await optionsRef.current.getAuthToken() : null;
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(`${baseUrl.replace(/\/$/, "")}/api/schema-editor${endpoint}`, {
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: options?.readOnly ?? false,
1439
- readOnlyReason: "Local collection editing is only available in development mode.",
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: collection.id || fullPath.split("/").pop(),
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
- options?.readOnly,
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-BZLq4MGp.js").then((n) => n.n).then((m) => ({ default: m.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 ?? process.env.NODE_ENV === "production",
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(CollectionsStudioView, { configController: internalConfigController })
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
- onNavigationEntriesUpdate(adminGroupData ? [...draggable, {
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-Dpc75wIZ.js").then((n) => n.r).then((m) => ({ default: m.CollectionEditorDialog })));
2723
- var PropertyFormDialog = lazy(() => import("./PropertyEditView-DH3XZC2x.js").then((n) => n.t).then((m) => ({ default: m.PropertyFormDialog })));
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, FormEntry, FormLayout, ImagePreview, ImportCollectionAction, ImportDataPreview, ImportFileUpload, ImportNewPropertyFieldPreview, ImportSaveInProgress, 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, 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 };
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