@rebasepro/cms 0.17.3 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/{CollectionEditorDialog-B-akreOU.js → CollectionEditorDialog-BeX4b92r.js} +13 -7
- package/dist/{CollectionEditorDialog-B-akreOU.js.map → CollectionEditorDialog-BeX4b92r.js.map} +1 -1
- package/dist/{PropertyEditView-CiY84wkR.js → PropertyEditView-C7thfj4L.js} +3 -3
- package/dist/{PropertyEditView-CiY84wkR.js.map → PropertyEditView-C7thfj4L.js.map} +1 -1
- package/dist/{RouterCollectionsStudioView-DkAWda4N.js → RouterCollectionsStudioView-BtjzvB6E.js} +4 -4
- package/dist/{RouterCollectionsStudioView-DkAWda4N.js.map → RouterCollectionsStudioView-BtjzvB6E.js.map} +1 -1
- package/dist/{SchemaChangeDialog-DceT8QFX.js → SchemaChangeDialog-CkqK6KOC.js} +4 -2
- package/dist/{SchemaChangeDialog-DceT8QFX.js.map → SchemaChangeDialog-CkqK6KOC.js.map} +1 -1
- package/dist/collection_editor/api/generateCollectionApi.d.ts +1 -2
- package/dist/collection_editor_ui.d.ts +2 -0
- package/dist/collection_editor_ui.js +6 -5
- package/dist/collection_editor_ui.js.map +1 -1
- package/dist/components/DeleteEntityDialog.d.ts +2 -3
- package/dist/components/RebaseLayout.d.ts +11 -4
- package/dist/components/field_configs.d.ts +1 -1
- package/dist/editor/sanitize-html.d.ts +15 -0
- package/dist/editor.js +195 -10
- package/dist/editor.js.map +1 -1
- package/dist/{export-Ben8E355.js → export--arni_VY.js} +2 -2
- package/dist/{export-Ben8E355.js.map → export--arni_VY.js.map} +1 -1
- package/dist/form/components/LabelWithIcon.d.ts +10 -0
- package/dist/{history-DIEVd26f.js → history-C2TpoQwi.js} +2 -2
- package/dist/{history-DIEVd26f.js.map → history-C2TpoQwi.js.map} +1 -1
- package/dist/hooks/navigation/useBuildNavigationStateController.d.ts +1 -1
- package/dist/hooks/navigation/useResolvedViews.d.ts +1 -1
- package/dist/hooks/navigation/useTopLevelNavigation.d.ts +1 -1
- package/dist/{import-D25x7ko-.js → import-DTkXNoBg.js} +2 -2
- package/dist/{import-D25x7ko-.js.map → import-DTkXNoBg.js.map} +1 -1
- package/dist/index.js +78 -33
- package/dist/index.js.map +1 -1
- package/dist/util/propertyConfigMapper.d.ts +1 -1
- package/dist/util-CfLFggP7.js +57 -0
- package/dist/util-CfLFggP7.js.map +1 -0
- package/dist/{util-DVqnqlLn.js → util-DE1WQW6h.js} +134 -112
- package/dist/util-DE1WQW6h.js.map +1 -0
- package/package.json +42 -34
- package/dist/util-DVqnqlLn.js.map +0 -1
- package/src/collection_editor/CollectionEditorDialogsContext.tsx +0 -25
- package/src/collection_editor/ConfigControllerProvider.tsx +0 -395
- package/src/collection_editor/_cms_internals.ts +0 -41
- package/src/collection_editor/api/generateCollectionApi.ts +0 -126
- package/src/collection_editor/api/index.ts +0 -1
- package/src/collection_editor/extensibility_types.ts +0 -168
- package/src/collection_editor/index.ts +0 -108
- package/src/collection_editor/liveSchemaClient.ts +0 -285
- package/src/collection_editor/relation_kinds.ts +0 -44
- package/src/collection_editor/serializable_types.ts +0 -535
- package/src/collection_editor/serializable_utils.ts +0 -729
- package/src/collection_editor/types/collection_editor_controller.tsx +0 -62
- package/src/collection_editor/types/collection_inference.ts +0 -18
- package/src/collection_editor/types/config_controller.tsx +0 -121
- package/src/collection_editor/ui/AddKanbanColumnAction.tsx +0 -199
- package/src/collection_editor/ui/CollectionViewHeaderAction.tsx +0 -51
- package/src/collection_editor/ui/EditorCollectionAction.tsx +0 -54
- package/src/collection_editor/ui/EditorCollectionActionStart.tsx +0 -90
- package/src/collection_editor/ui/EditorEntityAction.tsx +0 -50
- package/src/collection_editor/ui/HomePageEditorCollectionAction.tsx +0 -114
- package/src/collection_editor/ui/KanbanSetupAction.tsx +0 -43
- package/src/collection_editor/ui/MissingReferenceWidget.tsx +0 -35
- package/src/collection_editor/ui/NewCollectionButton.tsx +0 -27
- package/src/collection_editor/ui/NewCollectionCard.tsx +0 -48
- package/src/collection_editor/ui/PropertyAddColumnComponent.tsx +0 -49
- package/src/collection_editor/ui/collection_editor/AICollectionGeneratorPopover.tsx +0 -260
- package/src/collection_editor/ui/collection_editor/AIModifiedPathsContext.tsx +0 -90
- package/src/collection_editor/ui/collection_editor/CollectionDetailsForm.tsx +0 -388
- package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +0 -1098
- package/src/collection_editor/ui/collection_editor/CollectionEditorWelcomeView.tsx +0 -301
- package/src/collection_editor/ui/collection_editor/CollectionJsonImportDialog.tsx +0 -172
- package/src/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.tsx +0 -620
- package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +0 -530
- package/src/collection_editor/ui/collection_editor/CollectionRelationsTab.tsx +0 -462
- package/src/collection_editor/ui/collection_editor/CollectionStudioView.tsx +0 -104
- package/src/collection_editor/ui/collection_editor/CollectionYupValidation.tsx +0 -9
- package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +0 -199
- package/src/collection_editor/ui/collection_editor/DisplaySettingsForm.tsx +0 -315
- package/src/collection_editor/ui/collection_editor/EntityActionsEditTab.tsx +0 -164
- package/src/collection_editor/ui/collection_editor/EntityActionsSelectDialog.tsx +0 -40
- package/src/collection_editor/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +0 -39
- package/src/collection_editor/ui/collection_editor/EnumForm.tsx +0 -373
- package/src/collection_editor/ui/collection_editor/GeneralSettingsForm.tsx +0 -288
- package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +0 -141
- package/src/collection_editor/ui/collection_editor/KanbanConfigSection.tsx +0 -194
- package/src/collection_editor/ui/collection_editor/LayoutModeSwitch.tsx +0 -53
- package/src/collection_editor/ui/collection_editor/PropertyEditView.tsx +0 -980
- package/src/collection_editor/ui/collection_editor/PropertyFieldPreview.tsx +0 -164
- package/src/collection_editor/ui/collection_editor/PropertyTree.tsx +0 -364
- package/src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx +0 -44
- package/src/collection_editor/ui/collection_editor/RouterCollectionsStudioView.tsx +0 -41
- package/src/collection_editor/ui/collection_editor/SchemaChangeDialog.tsx +0 -470
- package/src/collection_editor/ui/collection_editor/SubcollectionsEditTab.tsx +0 -256
- package/src/collection_editor/ui/collection_editor/SwitchControl.tsx +0 -39
- package/src/collection_editor/ui/collection_editor/ViewModeSwitch.tsx +0 -68
- package/src/collection_editor/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +0 -67
- package/src/collection_editor/ui/collection_editor/import/CollectionEditorImportMapping.tsx +0 -296
- package/src/collection_editor/ui/collection_editor/import/clean_import_data.ts +0 -57
- package/src/collection_editor/ui/collection_editor/properties/BlockPropertyField.tsx +0 -147
- package/src/collection_editor/ui/collection_editor/properties/BooleanPropertyField.tsx +0 -41
- package/src/collection_editor/ui/collection_editor/properties/CommonPropertyFields.tsx +0 -113
- package/src/collection_editor/ui/collection_editor/properties/DateTimePropertyField.tsx +0 -250
- package/src/collection_editor/ui/collection_editor/properties/EnumPropertyField.tsx +0 -126
- package/src/collection_editor/ui/collection_editor/properties/KeyValuePropertyField.tsx +0 -20
- package/src/collection_editor/ui/collection_editor/properties/MapPropertyField.tsx +0 -153
- package/src/collection_editor/ui/collection_editor/properties/MarkdownPropertyField.tsx +0 -217
- package/src/collection_editor/ui/collection_editor/properties/NumberPropertyField.tsx +0 -117
- package/src/collection_editor/ui/collection_editor/properties/ReferencePropertyField.tsx +0 -129
- package/src/collection_editor/ui/collection_editor/properties/RelationPropertyField.tsx +0 -335
- package/src/collection_editor/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -107
- package/src/collection_editor/ui/collection_editor/properties/StoragePropertyField.tsx +0 -403
- package/src/collection_editor/ui/collection_editor/properties/StringPropertyField.tsx +0 -148
- package/src/collection_editor/ui/collection_editor/properties/UrlPropertyField.tsx +0 -91
- package/src/collection_editor/ui/collection_editor/properties/VectorPropertyField.tsx +0 -34
- package/src/collection_editor/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +0 -49
- package/src/collection_editor/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +0 -925
- package/src/collection_editor/ui/collection_editor/properties/conditions/ConditionsPanel.tsx +0 -28
- package/src/collection_editor/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +0 -626
- package/src/collection_editor/ui/collection_editor/properties/conditions/index.ts +0 -6
- package/src/collection_editor/ui/collection_editor/properties/conditions/property_paths.ts +0 -93
- package/src/collection_editor/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +0 -52
- package/src/collection_editor/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +0 -63
- package/src/collection_editor/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +0 -114
- package/src/collection_editor/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +0 -154
- package/src/collection_editor/ui/collection_editor/properties/validation/ValidationPanel.tsx +0 -28
- package/src/collection_editor/ui/collection_editor/templates/blog_template.ts +0 -112
- package/src/collection_editor/ui/collection_editor/templates/pages_template.ts +0 -183
- package/src/collection_editor/ui/collection_editor/templates/products_template.ts +0 -88
- package/src/collection_editor/ui/collection_editor/templates/users_template.ts +0 -42
- package/src/collection_editor/ui/collection_editor/util.ts +0 -28
- package/src/collection_editor/ui/collection_editor/utils/strings.ts +0 -1
- package/src/collection_editor/ui/collection_editor/utils/supported_fields.tsx +0 -39
- package/src/collection_editor/ui/collection_editor/utils/update_property_for_widget.ts +0 -276
- package/src/collection_editor/useCollectionEditorController.tsx +0 -9
- package/src/collection_editor/useCollectionsConfigController.tsx +0 -9
- package/src/collection_editor/useJsonCollectionsConfigController.tsx +0 -297
- package/src/collection_editor/useLiveSchemaEditing.tsx +0 -315
- package/src/collection_editor/useLocalCollectionsConfigController.tsx +0 -341
- package/src/collection_editor/useSafeSnackbarController.ts +0 -9
- package/src/collection_editor/validateCollectionJson.ts +0 -440
- package/src/collection_editor_ui.ts +0 -21
- package/src/components/AdditionalFieldValue.tsx +0 -68
- package/src/components/AdminModeSyncer.tsx +0 -56
- package/src/components/ArrayContainer.tsx +0 -542
- package/src/components/ClearFilterSortButton.tsx +0 -56
- package/src/components/CollectionEditorDialogs.tsx +0 -69
- package/src/components/CollectionPanel.tsx +0 -179
- package/src/components/CollectionTableBinding/CollectionRowActions.tsx +0 -244
- package/src/components/CollectionTableBinding/CollectionTableBinding.tsx +0 -394
- package/src/components/CollectionTableBinding/CollectionTableBindingProps.tsx +0 -172
- package/src/components/CollectionTableBinding/PropertyTableCell.tsx +0 -350
- package/src/components/CollectionTableBinding/column_utils.tsx +0 -155
- package/src/components/CollectionTableBinding/fields/TableMultipleRelationField.tsx +0 -131
- package/src/components/CollectionTableBinding/fields/TableReferenceField.tsx +0 -193
- package/src/components/CollectionTableBinding/fields/TableRelationField.tsx +0 -192
- package/src/components/CollectionTableBinding/fields/TableRelationSelectorField.tsx +0 -42
- package/src/components/CollectionTableBinding/fields/TableStorageUpload.tsx +0 -324
- package/src/components/CollectionTableBinding/fields/VirtualTableSelect.tsx +0 -132
- package/src/components/CollectionTableBinding/fields/VirtualTableUserSelect.tsx +0 -41
- package/src/components/CollectionTableBinding/index.tsx +0 -14
- package/src/components/CollectionTableBinding/internal/CollectionTableToolbar.tsx +0 -96
- package/src/components/CollectionTableBinding/internal/EntityTableCell.tsx +0 -309
- package/src/components/CollectionTableBinding/internal/EntityTableCellActions.tsx +0 -80
- package/src/components/CollectionTableBinding/internal/common.tsx +0 -74
- package/src/components/CollectionTableBinding/internal/popup_field/PopupFormField.tsx +0 -422
- package/src/components/CollectionTableBinding/internal/popup_field/useDraggable.tsx +0 -99
- package/src/components/CollectionTableBinding/internal/popup_field/useWindowSize.tsx +0 -22
- package/src/components/CollectionTableBinding/table_bindings.tsx +0 -350
- package/src/components/CollectionViewBinding/BoardCardBinding.tsx +0 -263
- package/src/components/CollectionViewBinding/CollectionBoardViewBinding.tsx +0 -662
- package/src/components/CollectionViewBinding/CollectionCardViewBinding.tsx +0 -159
- package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +0 -1430
- package/src/components/CollectionViewBinding/CollectionViewActions.tsx +0 -227
- package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +0 -1603
- package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +0 -240
- package/src/components/CollectionViewBinding/EntityCardBinding.tsx +0 -239
- package/src/components/CollectionViewBinding/FilterPresetsButton.tsx +0 -298
- package/src/components/CollectionViewBinding/FiltersDialog.tsx +0 -218
- package/src/components/CollectionViewBinding/SearchExplanation.tsx +0 -106
- package/src/components/CollectionViewBinding/SearchHighlight.tsx +0 -311
- package/src/components/CollectionViewBinding/SlotValue.tsx +0 -156
- package/src/components/CollectionViewBinding/SortButton.tsx +0 -354
- package/src/components/CollectionViewBinding/SplitListCloseButton.tsx +0 -49
- package/src/components/CollectionViewBinding/SplitListShowButton.tsx +0 -28
- package/src/components/CollectionViewBinding/SplitListView.tsx +0 -488
- package/src/components/CollectionViewBinding/SplitViewContext.tsx +0 -34
- package/src/components/CollectionViewBinding/ViewModeToggle.tsx +0 -250
- package/src/components/CollectionViewBinding/hooks/useCollectionInlineEditor.ts +0 -81
- package/src/components/CollectionViewBinding/hooks/useKanbanDragAndDrop.ts +0 -181
- package/src/components/CollectionViewBinding/useBoardDataController.tsx +0 -802
- package/src/components/CollectionViewBinding/usePreviewSlots.ts +0 -703
- package/src/components/CollectionViewBinding/useSelectionController.tsx +0 -40
- package/src/components/CollectionViewBinding/utils.ts +0 -22
- package/src/components/DefaultAppBar.tsx +0 -264
- package/src/components/DefaultDrawer.tsx +0 -593
- package/src/components/DeleteEntityDialog.tsx +0 -205
- package/src/components/DetailViewBinding.tsx +0 -731
- package/src/components/DrawerNavigationGroup.tsx +0 -141
- package/src/components/DrawerNavigationItem.tsx +0 -115
- package/src/components/EditFormActions.tsx +0 -303
- package/src/components/EditViewBinding.tsx +0 -1069
- package/src/components/EntityDisplayHeader.tsx +0 -170
- package/src/components/EntityIdentityBar.tsx +0 -428
- package/src/components/EntityInspector.tsx +0 -156
- package/src/components/EntityPreviewBinding.tsx +0 -432
- package/src/components/EntityPreviewNesting.tsx +0 -50
- package/src/components/EntityViewBinding.tsx +0 -374
- package/src/components/FieldCaption.tsx +0 -14
- package/src/components/HomePage/ContentHomePage.tsx +0 -586
- package/src/components/HomePage/FavouritesView.tsx +0 -60
- package/src/components/HomePage/HomePageDnD.tsx +0 -704
- package/src/components/HomePage/NavigationCard.tsx +0 -81
- package/src/components/HomePage/NavigationCardBinding.tsx +0 -107
- package/src/components/HomePage/NavigationGroup.tsx +0 -156
- package/src/components/HomePage/RenameGroupDialog.tsx +0 -123
- package/src/components/HomePage/SmallNavigationCard.tsx +0 -41
- package/src/components/HomePage/index.tsx +0 -5
- package/src/components/InlineEntityPreview.tsx +0 -149
- package/src/components/JsonPreviewBinding.tsx +0 -66
- package/src/components/PropertyCollectionView.tsx +0 -336
- package/src/components/PropertyConfigBadge.tsx +0 -28
- package/src/components/PropertyIdCopyTooltip.tsx +0 -47
- package/src/components/RebaseAuthGate.tsx +0 -44
- package/src/components/RebaseCMS.tsx +0 -30
- package/src/components/RebaseLayout.tsx +0 -80
- package/src/components/RebaseNavigation.tsx +0 -369
- package/src/components/RebaseRouteDefs.tsx +0 -127
- package/src/components/RebaseShell.tsx +0 -83
- package/src/components/ReferenceTable/SelectionTableBinding.tsx +0 -389
- package/src/components/ReferenceWidget.tsx +0 -156
- package/src/components/RelationSelector.tsx +0 -926
- package/src/components/SearchIconsView.tsx +0 -78
- package/src/components/SelectableTable/SelectableTable.tsx +0 -309
- package/src/components/SelectableTable/SelectableTableContext.tsx +0 -7
- package/src/components/SelectableTable/SelectionStore.ts +0 -43
- package/src/components/SelectableTable/filters/BooleanFilterField.tsx +0 -59
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +0 -146
- package/src/components/SelectableTable/filters/FilterFieldBinding.tsx +0 -190
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +0 -250
- package/src/components/SelectableTable/filters/RelationFilterField.tsx +0 -238
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +0 -282
- package/src/components/SelectableTable/filters/null_filter.ts +0 -63
- package/src/components/SideDialogs.tsx +0 -238
- package/src/components/SidePanelBinding.tsx +0 -361
- package/src/components/SidePanelProvider.tsx +0 -80
- package/src/components/UserSelector.tsx +0 -513
- package/src/components/admin/CreationResultDialog.tsx +0 -138
- package/src/components/admin/index.ts +0 -1
- package/src/components/app/AppBar.tsx +0 -17
- package/src/components/app/Drawer.tsx +0 -38
- package/src/components/app/Scaffold.tsx +0 -366
- package/src/components/app/index.ts +0 -4
- package/src/components/app/useApp.tsx +0 -36
- package/src/components/common/default_entity_actions.tsx +0 -440
- package/src/components/common/index.ts +0 -1
- package/src/components/field_configs.tsx +0 -506
- package/src/components/history/EntityHistoryEntry.tsx +0 -203
- package/src/components/history/EntityHistoryView.tsx +0 -206
- package/src/components/history/LastEditedByFormAction.tsx +0 -24
- package/src/components/history/LastEditedByIndicator.tsx +0 -95
- package/src/components/history/UserChip.tsx +0 -15
- package/src/components/history/index.tsx +0 -5
- package/src/components/index.ts +0 -63
- package/src/contexts/BreacrumbsContext.tsx +0 -45
- package/src/contexts/SideDialogsControllerContext.tsx +0 -5
- package/src/contexts/SidePanelControllerContext.tsx +0 -5
- package/src/data_export/export/BasicExportAction.tsx +0 -142
- package/src/data_export/export/ExportCollectionAction.tsx +0 -321
- package/src/data_export/export/export.ts +0 -295
- package/src/data_export/export/fetch_export_data.ts +0 -90
- package/src/data_export/export/index.ts +0 -4
- package/src/data_export/index.ts +0 -1
- package/src/data_import/components/DataNewPropertiesMapping.tsx +0 -261
- package/src/data_import/components/ImportFileUpload.tsx +0 -42
- package/src/data_import/components/ImportNewPropertyFieldPreview.tsx +0 -58
- package/src/data_import/components/ImportSaveInProgress.tsx +0 -121
- package/src/data_import/components/index.ts +0 -4
- package/src/data_import/hooks/index.ts +0 -1
- package/src/data_import/hooks/useImportConfig.tsx +0 -34
- package/src/data_import/import/ImportCollectionAction.tsx +0 -461
- package/src/data_import/import/index.ts +0 -1
- package/src/data_import/index.ts +0 -5
- package/src/data_import/types/column_mapping.ts +0 -32
- package/src/data_import/types/index.ts +0 -1
- package/src/data_import/utils/csv.ts +0 -153
- package/src/data_import/utils/data.ts +0 -181
- package/src/data_import/utils/file_headers.ts +0 -14
- package/src/data_import/utils/file_to_json.ts +0 -149
- package/src/data_import/utils/get_import_inference_type.ts +0 -33
- package/src/data_import/utils/get_properties_mapping.ts +0 -67
- package/src/data_import/utils/index.ts +0 -5
- package/src/data_import/utils/save_entities.ts +0 -138
- package/src/data_import/utils/transforms.ts +0 -70
- package/src/editor/components/SlashCommandMenu.tsx +0 -532
- package/src/editor/components/editor-bubble-item.tsx +0 -32
- package/src/editor/components/editor-bubble.tsx +0 -143
- package/src/editor/components/image-bubble.tsx +0 -183
- package/src/editor/components/index.ts +0 -14
- package/src/editor/components/table-bubble.tsx +0 -233
- package/src/editor/editor.tsx +0 -529
- package/src/editor/extensions/HighlightDecorationExtension.ts +0 -115
- package/src/editor/extensions/Image.ts +0 -159
- package/src/editor/extensions/TextLoadingDecorationExtension.tsx +0 -107
- package/src/editor/extensions/clipboard.ts +0 -72
- package/src/editor/extensions/custom-keymap.ts +0 -25
- package/src/editor/extensions/drag-and-drop.tsx +0 -508
- package/src/editor/hooks/useProseMirror.ts +0 -126
- package/src/editor/hooks/useProseMirrorContext.ts +0 -15
- package/src/editor/index.ts +0 -2
- package/src/editor/markdown-it-plugins.d.ts +0 -24
- package/src/editor/markdown.ts +0 -204
- package/src/editor/nodeViews/ImageComponent.tsx +0 -20
- package/src/editor/nodeViews/ReactNodeView.tsx +0 -89
- package/src/editor/nodeViews/TaskItemComponent.tsx +0 -30
- package/src/editor/nodeViews/index.ts +0 -35
- package/src/editor/plugins/index.ts +0 -58
- package/src/editor/plugins/inputrules.ts +0 -82
- package/src/editor/plugins/placeholderPlugin.ts +0 -55
- package/src/editor/plugins/slashCommandPlugin.ts +0 -65
- package/src/editor/schema.ts +0 -339
- package/src/editor/selectors/ai-selector.tsx +0 -110
- package/src/editor/selectors/color-selector.tsx +0 -199
- package/src/editor/selectors/link-selector.tsx +0 -122
- package/src/editor/selectors/node-selector.tsx +0 -159
- package/src/editor/selectors/text-buttons.tsx +0 -79
- package/src/editor/types.ts +0 -6
- package/src/editor/useProseMirror.ts +0 -125
- package/src/editor/utils/prosemirror-utils.ts +0 -113
- package/src/editor/utils/remove_classes.ts +0 -18
- package/src/editor/utils/useDebouncedCallback.ts +0 -25
- package/src/form/EntityForm.tsx +0 -761
- package/src/form/EntityFormActions.tsx +0 -160
- package/src/form/EntityFormBinding.tsx +0 -342
- package/src/form/PropertyFieldBinding.tsx +0 -348
- package/src/form/components/ErrorFocus.tsx +0 -44
- package/src/form/components/FieldBlock.tsx +0 -189
- package/src/form/components/FieldHelperText.tsx +0 -84
- package/src/form/components/FormRail.tsx +0 -153
- package/src/form/components/FormSections.tsx +0 -145
- package/src/form/components/LabelWithIcon.tsx +0 -43
- package/src/form/components/LabelWithIconAndTooltip.tsx +0 -28
- package/src/form/components/LocalChangesMenu.tsx +0 -147
- package/src/form/components/StorageItemPreview.tsx +0 -81
- package/src/form/components/StorageUploadProgress.tsx +0 -118
- package/src/form/components/index.tsx +0 -6
- package/src/form/components/useRailVisible.tsx +0 -47
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +0 -109
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +0 -172
- package/src/form/field_bindings/BinaryFieldBinding.tsx +0 -177
- package/src/form/field_bindings/BlockFieldBinding.tsx +0 -282
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +0 -78
- package/src/form/field_bindings/GeopointFieldBinding.tsx +0 -200
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +0 -574
- package/src/form/field_bindings/MapFieldBinding.tsx +0 -120
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +0 -259
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +0 -130
- package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +0 -168
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +0 -94
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +0 -146
- package/src/form/field_bindings/RelationFieldBinding.tsx +0 -202
- package/src/form/field_bindings/RepeatFieldBinding.tsx +0 -143
- package/src/form/field_bindings/SelectFieldBinding.tsx +0 -111
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +0 -521
- package/src/form/field_bindings/SwitchFieldBinding.tsx +0 -70
- package/src/form/field_bindings/TextFieldBinding.tsx +0 -189
- package/src/form/field_bindings/UserSelectFieldBinding.tsx +0 -71
- package/src/form/field_bindings/VectorFieldBinding.tsx +0 -209
- package/src/form/form_utils.ts +0 -243
- package/src/form/index.tsx +0 -36
- package/src/form/useClearRestoreValue.tsx +0 -36
- package/src/form/useUndoableDiscard.ts +0 -39
- package/src/form/validation.ts +0 -583
- package/src/hooks/index.ts +0 -16
- package/src/hooks/navigation/contexts/CollectionRegistryContext.tsx +0 -22
- package/src/hooks/navigation/contexts/NavigationStateContext.tsx +0 -16
- package/src/hooks/navigation/contexts/UrlContext.tsx +0 -24
- package/src/hooks/navigation/contexts/index.ts +0 -14
- package/src/hooks/navigation/useAsyncResolver.ts +0 -100
- package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +0 -218
- package/src/hooks/navigation/useBuildNavigationStateController.tsx +0 -134
- package/src/hooks/navigation/useBuildUrlController.tsx +0 -66
- package/src/hooks/navigation/useNavigationResolution.ts +0 -271
- package/src/hooks/navigation/useResolvedCollections.ts +0 -102
- package/src/hooks/navigation/useResolvedViews.tsx +0 -139
- package/src/hooks/navigation/useTopLevelNavigation.ts +0 -273
- package/src/hooks/navigation/utils.ts +0 -200
- package/src/hooks/useAdminContext.tsx +0 -83
- package/src/hooks/useBreadcrumbsController.tsx +0 -17
- package/src/hooks/useBuildSideDialogsController.tsx +0 -145
- package/src/hooks/useBuildSidePanel.tsx +0 -315
- package/src/hooks/useChildViewSource.ts +0 -46
- package/src/hooks/useEntityDisplay.tsx +0 -262
- package/src/hooks/useEntityDisplayTitle.tsx +0 -49
- package/src/hooks/useHistory.ts +0 -178
- package/src/hooks/useRecordActions.tsx +0 -69
- package/src/hooks/useResolvedNavigationFrom.tsx +0 -167
- package/src/hooks/useResolvedUsers.ts +0 -148
- package/src/hooks/useSelectionDialog.tsx +0 -58
- package/src/hooks/useSideDialogsController.tsx +0 -25
- package/src/hooks/useSidePanel.tsx +0 -13
- package/src/index.ts +0 -124
- package/src/preview/PropertyPreview.tsx +0 -331
- package/src/preview/compact_text.ts +0 -68
- package/src/preview/components/ArrayEnumPreview.tsx +0 -39
- package/src/preview/components/BooleanPreview.tsx +0 -28
- package/src/preview/components/DatePreview.tsx +0 -106
- package/src/preview/components/EmptyValue.tsx +0 -10
- package/src/preview/components/EnumValuesChip.tsx +0 -39
- package/src/preview/components/ImagePreview.tsx +0 -144
- package/src/preview/components/InlineEntityListPreview.tsx +0 -42
- package/src/preview/components/ReferencePreview.tsx +0 -235
- package/src/preview/components/RelationPreview.tsx +0 -271
- package/src/preview/components/StorageThumbnail.tsx +0 -147
- package/src/preview/components/UrlComponentPreview.tsx +0 -116
- package/src/preview/components/UserPreview.tsx +0 -30
- package/src/preview/components/date_fns_locales.ts +0 -125
- package/src/preview/index.ts +0 -26
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -73
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +0 -67
- package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +0 -77
- package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -49
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -70
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +0 -79
- package/src/preview/property_previews/ArrayPropertyEnumPreview.tsx +0 -35
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +0 -79
- package/src/preview/property_previews/MapPropertyPreview.tsx +0 -198
- package/src/preview/property_previews/NumberPropertyPreview.tsx +0 -39
- package/src/preview/property_previews/SkeletonPropertyComponent.tsx +0 -276
- package/src/preview/property_previews/StringPropertyPreview.tsx +0 -70
- package/src/preview/util.ts +0 -39
- package/src/routes/CustomViewRoute.tsx +0 -33
- package/src/routes/RebaseRoute.tsx +0 -467
- package/src/routes/index.ts +0 -2
- package/src/types/components/EntityFormActionsProps.tsx +0 -20
- package/src/types/components/EntityFormProps.tsx +0 -119
- package/src/types/components/PropertyPreviewProps.tsx +0 -85
- package/src/types/components/formex.tsx +0 -2
- package/src/types/components/index.ts +0 -3
- package/src/types/fields.tsx +0 -340
- package/src/types/index.ts +0 -4
- package/src/util/dataControllerAdapter.ts +0 -88
- package/src/util/entity_actions.ts +0 -28
- package/src/util/index.ts +0 -5
- package/src/util/navigation_utils.ts +0 -89
- package/src/util/number_format.ts +0 -46
- package/src/util/previews.ts +0 -131
- package/src/util/propertyConfigMapper.ts +0 -134
- package/src/util/property_utils.tsx +0 -236
- package/src/util/resolutions.ts +0 -96
- package/src/util/view_constants.ts +0 -33
- package/src/util/view_mode.ts +0 -176
|
@@ -1,802 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { Entity, FilterValues, MAX_LIST_LIMIT } from "@rebasepro/types";
|
|
3
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
-
import { getRelationIncludeParams, useData, useRebaseContext } from "@rebasepro/app";
|
|
5
|
-
import type { AdminCollection } from "@rebasepro/cms-types";
|
|
6
|
-
|
|
7
|
-
const DEFAULT_PAGE_SIZE = 20;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* How long a column waits for its subscription's first payload before reading
|
|
11
|
-
* once over HTTP instead. Long enough that a healthy socket always wins the
|
|
12
|
-
* race, short enough that a dead one does not hold a blank board.
|
|
13
|
-
*/
|
|
14
|
-
const LIVE_FIRST_PAINT_TIMEOUT_MS = 2500;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Shallow equality for entity value records.
|
|
18
|
-
* Handles primitives and reference equality for nested values.
|
|
19
|
-
* Avoids JSON.stringify allocation on every comparison.
|
|
20
|
-
*/
|
|
21
|
-
function shallowEqualValues(
|
|
22
|
-
a: Record<string, unknown> | undefined,
|
|
23
|
-
b: Record<string, unknown> | undefined
|
|
24
|
-
): boolean {
|
|
25
|
-
if (a === b) return true;
|
|
26
|
-
if (!a || !b) return false;
|
|
27
|
-
const keysA = Object.keys(a);
|
|
28
|
-
const keysB = Object.keys(b);
|
|
29
|
-
if (keysA.length !== keysB.length) return false;
|
|
30
|
-
for (const key of keysA) {
|
|
31
|
-
if (a[key] !== b[key]) return false;
|
|
32
|
-
}
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Data state for a single board column
|
|
38
|
-
*/
|
|
39
|
-
export interface BoardColumnData<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
40
|
-
/** Entities loaded for this column */
|
|
41
|
-
entities: Entity<M>[];
|
|
42
|
-
/** Whether the column is currently loading data */
|
|
43
|
-
loading: boolean;
|
|
44
|
-
/** Whether there are more items to load */
|
|
45
|
-
hasMore: boolean;
|
|
46
|
-
/** Error if loading failed */
|
|
47
|
-
error?: Error;
|
|
48
|
-
/** Total count of entities in this column */
|
|
49
|
-
totalCount?: number;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Controller for managing per-column data in a Kanban board
|
|
54
|
-
*/
|
|
55
|
-
export interface BoardDataController<M extends Record<string, unknown> = any, COLUMN extends string = string> {
|
|
56
|
-
/** Data state for each column */
|
|
57
|
-
columnData: Record<COLUMN, BoardColumnData<M>>;
|
|
58
|
-
/** Load more items for a specific column */
|
|
59
|
-
loadMoreColumn: (column: COLUMN) => void;
|
|
60
|
-
/** Refresh data for a specific column */
|
|
61
|
-
refreshColumn: (column: COLUMN) => void;
|
|
62
|
-
/** Refresh all columns */
|
|
63
|
-
refreshAll: () => void;
|
|
64
|
-
/** Update column counts and optionally move item optimistically */
|
|
65
|
-
moveItemOptimistically: (itemId: string, sourceColumn: COLUMN, targetColumn: COLUMN, newValues?: Record<string, any>, newIndex?: number) => void;
|
|
66
|
-
/** Decrement column counts (for optimistic updates when deleting items) */
|
|
67
|
-
decrementColumnCounts: (columnDeltas: Record<COLUMN, number>) => void;
|
|
68
|
-
/** Whether any column is loading */
|
|
69
|
-
loading: boolean;
|
|
70
|
-
/** Any error from any column */
|
|
71
|
-
error?: Error;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface UseBoardDataControllerProps<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
75
|
-
/** Full path to the collection */
|
|
76
|
-
fullPath: string;
|
|
77
|
-
/** The entity collection configuration */
|
|
78
|
-
collection: AdminCollection<M>;
|
|
79
|
-
/** Property key used for column assignment */
|
|
80
|
-
columnProperty: string;
|
|
81
|
-
/** Array of column values (enum values from columnProperty) */
|
|
82
|
-
columns: string[];
|
|
83
|
-
/** Property key used for ordering within columns */
|
|
84
|
-
orderProperty?: string;
|
|
85
|
-
/** Number of items to load per page per column */
|
|
86
|
-
pageSize?: number;
|
|
87
|
-
/** Text search string to filter entities */
|
|
88
|
-
searchString?: string;
|
|
89
|
-
/** Additional filter values */
|
|
90
|
-
filterValues?: FilterValues<string>;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Hook that manages per-column data loading for the Kanban board.
|
|
95
|
-
* Each column gets its own independent query to the data source.
|
|
96
|
-
*/
|
|
97
|
-
export function useBoardDataController<M extends Record<string, unknown> = any, COLUMN extends string = string>({
|
|
98
|
-
fullPath,
|
|
99
|
-
collection,
|
|
100
|
-
columnProperty,
|
|
101
|
-
columns,
|
|
102
|
-
orderProperty,
|
|
103
|
-
pageSize = DEFAULT_PAGE_SIZE,
|
|
104
|
-
searchString,
|
|
105
|
-
filterValues
|
|
106
|
-
}: UseBoardDataControllerProps<M>): BoardDataController<M, COLUMN> {
|
|
107
|
-
|
|
108
|
-
const context = useRebaseContext();
|
|
109
|
-
const dataClient = useData();
|
|
110
|
-
// v4: use fullPath directly instead of resolveIdsFrom
|
|
111
|
-
const resolvedPath = fullPath;
|
|
112
|
-
|
|
113
|
-
// Stable refs for objects that shouldn't trigger re-subscriptions
|
|
114
|
-
const dataClientRef = useRef(dataClient);
|
|
115
|
-
const collectionRef = useRef(collection);
|
|
116
|
-
const contextRef = useRef(context);
|
|
117
|
-
dataClientRef.current = dataClient;
|
|
118
|
-
collectionRef.current = collection;
|
|
119
|
-
contextRef.current = context;
|
|
120
|
-
|
|
121
|
-
// Store filter/order params in refs so they're accessible without causing re-subscriptions
|
|
122
|
-
const filterValuesRef = useRef(filterValues);
|
|
123
|
-
const columnPropertyRef = useRef(columnProperty);
|
|
124
|
-
const orderPropertyRef = useRef(orderProperty);
|
|
125
|
-
const searchStringRef = useRef(searchString);
|
|
126
|
-
const resolvedPathRef = useRef(resolvedPath);
|
|
127
|
-
filterValuesRef.current = filterValues;
|
|
128
|
-
columnPropertyRef.current = columnProperty;
|
|
129
|
-
orderPropertyRef.current = orderProperty;
|
|
130
|
-
searchStringRef.current = searchString;
|
|
131
|
-
resolvedPathRef.current = resolvedPath;
|
|
132
|
-
|
|
133
|
-
// Track item count per column for pagination
|
|
134
|
-
const [columnItemCounts, setColumnItemCounts] = useState<Record<string, number>>(() => {
|
|
135
|
-
const initial: Record<string, number> = {};
|
|
136
|
-
columns.forEach(col => {
|
|
137
|
-
initial[col] = pageSize;
|
|
138
|
-
});
|
|
139
|
-
return initial;
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// Per-column data state
|
|
143
|
-
const [columnData, setColumnData] = useState<Record<string, BoardColumnData<M>>>(() => {
|
|
144
|
-
const initial: Record<string, BoardColumnData<M>> = {};
|
|
145
|
-
columns.forEach(col => {
|
|
146
|
-
initial[col] = {
|
|
147
|
-
entities: [],
|
|
148
|
-
loading: true,
|
|
149
|
-
hasMore: true,
|
|
150
|
-
error: undefined,
|
|
151
|
-
totalCount: undefined
|
|
152
|
-
};
|
|
153
|
-
});
|
|
154
|
-
return initial;
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// Track cleanup functions for subscriptions
|
|
158
|
-
const unsubscribersRef = useRef<Record<string, () => void>>({});
|
|
159
|
-
|
|
160
|
-
// Flag to prevent race conditions during cleanup
|
|
161
|
-
const isCleaningUpRef = useRef(false);
|
|
162
|
-
|
|
163
|
-
// Track items that are currently in an optimistic state (awaiting DB sync)
|
|
164
|
-
const pendingItemsRef = useRef<Record<string, { entity: Entity<M>, expectedValues: Record<string, any> }>>({});
|
|
165
|
-
|
|
166
|
-
// Stable keys for dependency comparison
|
|
167
|
-
const columnsKey = useMemo(() => [...columns].sort().join(","), [columns]);
|
|
168
|
-
const filterKey = useMemo(() => JSON.stringify(filterValues), [filterValues]);
|
|
169
|
-
|
|
170
|
-
// Track previous column item counts to detect which column changed
|
|
171
|
-
const prevColumnItemCountsRef = useRef<Record<string, number>>(columnItemCounts);
|
|
172
|
-
|
|
173
|
-
// Version counter to trigger full re-subscription when params change (not just load-more)
|
|
174
|
-
const [subscriptionVersion, setSubscriptionVersion] = useState(0);
|
|
175
|
-
|
|
176
|
-
// Trigger full re-subscription when key params change
|
|
177
|
-
useEffect(() => {
|
|
178
|
-
setSubscriptionVersion(v => v + 1);
|
|
179
|
-
}, [columnsKey, resolvedPath, columnProperty, orderProperty, searchString, filterKey, pageSize]);
|
|
180
|
-
|
|
181
|
-
// Cleanup subscriptions on unmount
|
|
182
|
-
useEffect(() => {
|
|
183
|
-
return () => {
|
|
184
|
-
isCleaningUpRef.current = true;
|
|
185
|
-
Object.values(unsubscribersRef.current).forEach(unsub => unsub?.());
|
|
186
|
-
unsubscribersRef.current = {};
|
|
187
|
-
};
|
|
188
|
-
}, []);
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Check if a column value is excluded by the active filter on the columnProperty.
|
|
192
|
-
* E.g. if the user sets "Status IN (Open, In Progress)", this returns true
|
|
193
|
-
* for "Waiting on Customer" since that column should show as empty.
|
|
194
|
-
*/
|
|
195
|
-
const isColumnExcludedByFilter = useCallback((column: string, filterValues?: FilterValues<string>, colProperty?: string): boolean => {
|
|
196
|
-
if (!filterValues || !colProperty) return false;
|
|
197
|
-
const filterForColumn = filterValues[colProperty as keyof typeof filterValues];
|
|
198
|
-
if (!filterForColumn || !Array.isArray(filterForColumn)) return false;
|
|
199
|
-
|
|
200
|
-
const [op, val] = filterForColumn as [string, unknown];
|
|
201
|
-
|
|
202
|
-
if (op === "==" && typeof val === "string") {
|
|
203
|
-
return column !== val;
|
|
204
|
-
}
|
|
205
|
-
if (op === "!=" && typeof val === "string") {
|
|
206
|
-
return column === val;
|
|
207
|
-
}
|
|
208
|
-
if (op === "in" && Array.isArray(val)) {
|
|
209
|
-
return !val.map(String).includes(column);
|
|
210
|
-
}
|
|
211
|
-
if (op === "not-in" && Array.isArray(val)) {
|
|
212
|
-
return val.map(String).includes(column);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return false;
|
|
216
|
-
}, []);
|
|
217
|
-
|
|
218
|
-
// Helper function to subscribe to a single column - uses refs to avoid dependency issues
|
|
219
|
-
const subscribeToColumn = useCallback((column: string, itemCount: number) => {
|
|
220
|
-
// Skip if we're in the middle of cleanup
|
|
221
|
-
if (isCleaningUpRef.current) return;
|
|
222
|
-
|
|
223
|
-
// `itemCount` grows by one page per "load more" click with nothing
|
|
224
|
-
// stopping it, and a read above `MAX_LIST_LIMIT` is refused rather than
|
|
225
|
-
// trimmed — a column would stop loading with an error instead of
|
|
226
|
-
// stopping at the ceiling. Ask for what the platform serves; `hasMore`
|
|
227
|
-
// below still compares against the count the board asked for, so the
|
|
228
|
-
// column reports itself exhausted exactly where it did before.
|
|
229
|
-
const requestedCount = Math.min(itemCount, MAX_LIST_LIMIT);
|
|
230
|
-
|
|
231
|
-
const currentDataClient = dataClientRef.current;
|
|
232
|
-
const currentCollection = collectionRef.current;
|
|
233
|
-
const currentContext = contextRef.current;
|
|
234
|
-
const currentFilterValues = filterValuesRef.current;
|
|
235
|
-
const currentColumnProperty = columnPropertyRef.current;
|
|
236
|
-
const currentOrderProperty = orderPropertyRef.current;
|
|
237
|
-
const currentSearchString = searchStringRef.current;
|
|
238
|
-
const currentResolvedPath = resolvedPathRef.current;
|
|
239
|
-
|
|
240
|
-
// If the column is excluded by the active filter on the column property,
|
|
241
|
-
// set it as empty immediately without querying.
|
|
242
|
-
const excluded = isColumnExcludedByFilter(column, currentFilterValues, currentColumnProperty);
|
|
243
|
-
if (excluded) {
|
|
244
|
-
setColumnData(prev => ({
|
|
245
|
-
...prev,
|
|
246
|
-
[column]: {
|
|
247
|
-
entities: [],
|
|
248
|
-
loading: false,
|
|
249
|
-
hasMore: false,
|
|
250
|
-
error: undefined,
|
|
251
|
-
totalCount: 0
|
|
252
|
-
}
|
|
253
|
-
}));
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Build filter map for this column — pass FilterValues directly
|
|
258
|
-
const whereFilter: FilterValues<string> = {
|
|
259
|
-
...(currentFilterValues ?? {}),
|
|
260
|
-
[currentColumnProperty]: ["==", column]
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
const orderByParam: [string, "asc" | "desc"] | undefined = currentOrderProperty ? [currentOrderProperty, "asc"] : undefined;
|
|
264
|
-
|
|
265
|
-
// Mark column as loading
|
|
266
|
-
setColumnData(prev => ({
|
|
267
|
-
...prev,
|
|
268
|
-
[column]: {
|
|
269
|
-
...prev[column],
|
|
270
|
-
loading: true,
|
|
271
|
-
error: undefined
|
|
272
|
-
}
|
|
273
|
-
}));
|
|
274
|
-
|
|
275
|
-
// onUpdate callback
|
|
276
|
-
const onUpdate = async (entities: Entity<M>[]) => {
|
|
277
|
-
// Skip updates if we're cleaning up
|
|
278
|
-
if (isCleaningUpRef.current) return;
|
|
279
|
-
|
|
280
|
-
const pendingMap = pendingItemsRef.current;
|
|
281
|
-
|
|
282
|
-
// Apply pending updates to incoming entities
|
|
283
|
-
const mergedEntities = entities.map(e => {
|
|
284
|
-
const pending = pendingMap[String(e.id)];
|
|
285
|
-
if (pending) {
|
|
286
|
-
// Check if DB entity has caught up to the expected column and order
|
|
287
|
-
const expectedCol = pending.expectedValues[currentColumnProperty];
|
|
288
|
-
const expectedOrder = currentOrderProperty ? pending.expectedValues[currentOrderProperty] : undefined;
|
|
289
|
-
|
|
290
|
-
let caughtUp = true;
|
|
291
|
-
if (e.values?.[currentColumnProperty] !== expectedCol) {
|
|
292
|
-
caughtUp = false;
|
|
293
|
-
}
|
|
294
|
-
if (currentOrderProperty && e.values?.[currentOrderProperty] !== expectedOrder) {
|
|
295
|
-
caughtUp = false;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
if (caughtUp) {
|
|
299
|
-
// DB has caught up, clear pending state
|
|
300
|
-
delete pendingMap[String(e.id)];
|
|
301
|
-
return e; // Use the real DB entity
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// DB hasn't caught up, overlay expected values
|
|
305
|
-
return { ...e,
|
|
306
|
-
values: { ...e.values,
|
|
307
|
-
...pending.expectedValues } };
|
|
308
|
-
}
|
|
309
|
-
return e;
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
// Add any pending items that belong to this column but aren't in the incoming entities yet
|
|
313
|
-
// (e.g. backend hasn't moved them to this column yet)
|
|
314
|
-
Object.values(pendingMap).forEach(pending => {
|
|
315
|
-
if (pending.expectedValues[currentColumnProperty] === column) {
|
|
316
|
-
if (!mergedEntities.some(e => String(e.id) === String(pending.entity.id))) {
|
|
317
|
-
mergedEntities.push(pending.entity);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
// Always filter in memory to only show entities that belong to this specific column.
|
|
323
|
-
// This is required because text search returns all matches, and collection_entity_patch
|
|
324
|
-
// may contain entities that have just been moved out of this column.
|
|
325
|
-
let processed = mergedEntities.filter(e => e.values?.[currentColumnProperty] === column);
|
|
326
|
-
|
|
327
|
-
// Sort locally if orderProperty is defined. This ensures that collection_entity_patch
|
|
328
|
-
// insertions (which prepend by default in websocket.ts) are correctly placed.
|
|
329
|
-
if (currentOrderProperty) {
|
|
330
|
-
processed = [...processed].sort((a, b) => {
|
|
331
|
-
const valA = a.values?.[currentOrderProperty] as string | undefined | null;
|
|
332
|
-
const valB = b.values?.[currentOrderProperty] as string | undefined | null;
|
|
333
|
-
|
|
334
|
-
const isAEmpty = valA === undefined || valA === null || valA === "";
|
|
335
|
-
const isBEmpty = valB === undefined || valB === null || valB === "";
|
|
336
|
-
|
|
337
|
-
if (isAEmpty && isBEmpty) return 0;
|
|
338
|
-
if (isAEmpty) return 1;
|
|
339
|
-
if (isBEmpty) return -1;
|
|
340
|
-
|
|
341
|
-
return valA < valB ? -1 : valA > valB ? 1 : 0;
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Apply afterRead callbacks if any.
|
|
346
|
-
// afterRead operates on flat rows; unwrap the Entity view-model
|
|
347
|
-
// before invoking and re-wrap the processed row after.
|
|
348
|
-
if (currentCollection.callbacks?.afterRead) {
|
|
349
|
-
try {
|
|
350
|
-
processed = await Promise.all(
|
|
351
|
-
processed.map(async (entity) => {
|
|
352
|
-
const processedRow = await currentCollection.callbacks!.afterRead!({
|
|
353
|
-
collection: currentCollection,
|
|
354
|
-
path: currentResolvedPath,
|
|
355
|
-
row: { id: entity.id, ...entity.values },
|
|
356
|
-
context: currentContext
|
|
357
|
-
});
|
|
358
|
-
return {
|
|
359
|
-
...entity,
|
|
360
|
-
values: processedRow as M
|
|
361
|
-
};
|
|
362
|
-
})
|
|
363
|
-
);
|
|
364
|
-
} catch (e) {
|
|
365
|
-
console.error("Error in afterRead callback:", e);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
const newHasMore = entities.length >= itemCount;
|
|
370
|
-
|
|
371
|
-
// Compare with current state — skip update if identical to avoid UI flash
|
|
372
|
-
setColumnData(prev => {
|
|
373
|
-
const existing = prev[column];
|
|
374
|
-
if (existing && !existing.loading && existing.entities.length === processed.length) {
|
|
375
|
-
// Quick structural equality check: same IDs in same order with same values
|
|
376
|
-
let identical = true;
|
|
377
|
-
for (let i = 0; i < processed.length; i++) {
|
|
378
|
-
const a = existing.entities[i];
|
|
379
|
-
const b = processed[i];
|
|
380
|
-
if (a.id !== b.id) {
|
|
381
|
-
identical = false;
|
|
382
|
-
break;
|
|
383
|
-
}
|
|
384
|
-
// Shallow-compare values to avoid JSON.stringify allocations
|
|
385
|
-
// entity.values are flat records from the DB, so shallow suffices
|
|
386
|
-
if (!shallowEqualValues(a.values, b.values)) {
|
|
387
|
-
identical = false;
|
|
388
|
-
break;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
if (identical && existing.hasMore === newHasMore) {
|
|
392
|
-
// Data is the same — return previous reference to prevent re-render
|
|
393
|
-
return prev;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
return {
|
|
398
|
-
...prev,
|
|
399
|
-
[column]: {
|
|
400
|
-
entities: processed,
|
|
401
|
-
loading: false,
|
|
402
|
-
hasMore: newHasMore,
|
|
403
|
-
error: undefined,
|
|
404
|
-
totalCount: prev[column]?.totalCount // Keep existing count
|
|
405
|
-
}
|
|
406
|
-
};
|
|
407
|
-
});
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
const accessor = currentDataClient.collection(currentResolvedPath);
|
|
411
|
-
|
|
412
|
-
// Eagerly include relations to avoid N+1 fetches.
|
|
413
|
-
const includeParams = getRelationIncludeParams(currentCollection);
|
|
414
|
-
|
|
415
|
-
const fetchOnce = () => accessor.find({
|
|
416
|
-
where: whereFilter,
|
|
417
|
-
limit: requestedCount,
|
|
418
|
-
orderBy: orderByParam,
|
|
419
|
-
include: includeParams,
|
|
420
|
-
searchString: currentSearchString
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
const onError = (error: Error) => {
|
|
424
|
-
// Skip error handling if we're cleaning up
|
|
425
|
-
if (isCleaningUpRef.current) return;
|
|
426
|
-
|
|
427
|
-
console.error(`Error loading column ${column}:`, error);
|
|
428
|
-
|
|
429
|
-
// A live subscription that fails is not the same thing as a column
|
|
430
|
-
// with nothing in it, but that is exactly what this used to render:
|
|
431
|
-
// entities cleared, no error surfaced, and a column reading "No
|
|
432
|
-
// items" above a header still counting eleven of them. Read once
|
|
433
|
-
// over HTTP instead — the board loses its live updates, not its
|
|
434
|
-
// contents — and only report the failure if that fails too.
|
|
435
|
-
fetchOnce()
|
|
436
|
-
.then(res => onUpdate(res.data as Entity<M>[]))
|
|
437
|
-
.catch(() => {
|
|
438
|
-
if (isCleaningUpRef.current) return;
|
|
439
|
-
setColumnData(prev => ({
|
|
440
|
-
...prev,
|
|
441
|
-
[column]: {
|
|
442
|
-
...prev[column],
|
|
443
|
-
entities: prev[column]?.entities ?? [],
|
|
444
|
-
loading: false,
|
|
445
|
-
hasMore: false,
|
|
446
|
-
error
|
|
447
|
-
}
|
|
448
|
-
}));
|
|
449
|
-
});
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
if (accessor.listen) {
|
|
453
|
-
let liveDataReceived = false;
|
|
454
|
-
const unsubscribe = accessor.listen({
|
|
455
|
-
where: whereFilter,
|
|
456
|
-
limit: requestedCount,
|
|
457
|
-
orderBy: orderByParam,
|
|
458
|
-
include: includeParams,
|
|
459
|
-
// Read into a local at the top of this function and then used
|
|
460
|
-
// nowhere, in both places it was read. The board took a search
|
|
461
|
-
// term, re-subscribed every column when it changed, and
|
|
462
|
-
// returned the same rows.
|
|
463
|
-
searchString: currentSearchString
|
|
464
|
-
}, res => {
|
|
465
|
-
liveDataReceived = true;
|
|
466
|
-
onUpdate(res.data as Entity<M>[]);
|
|
467
|
-
}, onError);
|
|
468
|
-
|
|
469
|
-
// A subscription that is never going to answer takes the client's
|
|
470
|
-
// full 30s watchdog to say so, and the board spins the whole time.
|
|
471
|
-
// If the socket has not delivered shortly after mount, read once
|
|
472
|
-
// over HTTP and paint that; the live data still wins whenever it
|
|
473
|
-
// arrives. Costs nothing when realtime is healthy.
|
|
474
|
-
const firstPaintFallback = setTimeout(() => {
|
|
475
|
-
if (liveDataReceived || isCleaningUpRef.current) return;
|
|
476
|
-
fetchOnce()
|
|
477
|
-
.then(res => {
|
|
478
|
-
if (liveDataReceived || isCleaningUpRef.current) return;
|
|
479
|
-
onUpdate(res.data as Entity<M>[]);
|
|
480
|
-
})
|
|
481
|
-
.catch(() => undefined);
|
|
482
|
-
}, LIVE_FIRST_PAINT_TIMEOUT_MS);
|
|
483
|
-
|
|
484
|
-
unsubscribersRef.current[column] = () => {
|
|
485
|
-
clearTimeout(firstPaintFallback);
|
|
486
|
-
unsubscribe?.();
|
|
487
|
-
};
|
|
488
|
-
} else {
|
|
489
|
-
fetchOnce()
|
|
490
|
-
.then(res => onUpdate(res.data as Entity<M>[]))
|
|
491
|
-
.catch((error: Error) => {
|
|
492
|
-
if (isCleaningUpRef.current) return;
|
|
493
|
-
setColumnData(prev => ({
|
|
494
|
-
...prev,
|
|
495
|
-
[column]: {
|
|
496
|
-
...prev[column],
|
|
497
|
-
entities: prev[column]?.entities ?? [],
|
|
498
|
-
loading: false,
|
|
499
|
-
hasMore: false,
|
|
500
|
-
error
|
|
501
|
-
}
|
|
502
|
-
}));
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
}, []); // No dependencies - uses refs for all values
|
|
506
|
-
|
|
507
|
-
// Main effect for all column subscriptions - runs when subscriptionVersion changes (i.e., key params change)
|
|
508
|
-
useEffect(() => {
|
|
509
|
-
// Mark that we're setting up new subscriptions
|
|
510
|
-
isCleaningUpRef.current = false;
|
|
511
|
-
|
|
512
|
-
// Clean up all existing subscriptions synchronously
|
|
513
|
-
const existingUnsubscribers = { ...unsubscribersRef.current };
|
|
514
|
-
unsubscribersRef.current = {};
|
|
515
|
-
Object.values(existingUnsubscribers).forEach(unsub => {
|
|
516
|
-
try {
|
|
517
|
-
unsub?.();
|
|
518
|
-
} catch (e) {
|
|
519
|
-
// Ignore cleanup errors
|
|
520
|
-
}
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
const currentDataClient = dataClientRef.current;
|
|
524
|
-
const currentCollection = collectionRef.current;
|
|
525
|
-
const currentFilterValues = filterValuesRef.current;
|
|
526
|
-
const currentColumnProperty = columnPropertyRef.current;
|
|
527
|
-
const currentSearchString = searchStringRef.current;
|
|
528
|
-
const currentResolvedPath = resolvedPathRef.current;
|
|
529
|
-
const currentColumns = columns;
|
|
530
|
-
const currentColumnItemCounts = columnItemCounts;
|
|
531
|
-
|
|
532
|
-
// Small delay to ensure Firestore has cleaned up previous listeners
|
|
533
|
-
const timeoutId = setTimeout(() => {
|
|
534
|
-
if (isCleaningUpRef.current) return;
|
|
535
|
-
|
|
536
|
-
currentColumns.forEach(column => {
|
|
537
|
-
const itemCount = currentColumnItemCounts[column] ?? pageSize;
|
|
538
|
-
subscribeToColumn(column, itemCount);
|
|
539
|
-
|
|
540
|
-
// Skip count query for columns excluded by the filter — subscribeToColumn
|
|
541
|
-
// already set them to totalCount: 0.
|
|
542
|
-
if (isColumnExcludedByFilter(column, currentFilterValues, currentColumnProperty)) {
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
// Count query for column (for display in column header)
|
|
547
|
-
const accessor = currentDataClient.collection(currentResolvedPath);
|
|
548
|
-
if (accessor.count) {
|
|
549
|
-
|
|
550
|
-
const whereFilter: FilterValues<string> = {
|
|
551
|
-
...(currentFilterValues ?? {}),
|
|
552
|
-
[currentColumnProperty]: ["==", column]
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
accessor.count({
|
|
556
|
-
where: whereFilter,
|
|
557
|
-
// Or the column header counts the unsearched collection
|
|
558
|
-
// while the column beneath it shows searched rows.
|
|
559
|
-
searchString: currentSearchString
|
|
560
|
-
}).then(count => {
|
|
561
|
-
if (isCleaningUpRef.current) return;
|
|
562
|
-
setColumnData(prev => ({
|
|
563
|
-
...prev,
|
|
564
|
-
[column]: {
|
|
565
|
-
...prev[column],
|
|
566
|
-
totalCount: count
|
|
567
|
-
}
|
|
568
|
-
}));
|
|
569
|
-
}).catch(e => {
|
|
570
|
-
console.warn(`Failed to get count for column ${column}:`, e);
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
// Update the ref after subscribing all
|
|
576
|
-
prevColumnItemCountsRef.current = { ...currentColumnItemCounts };
|
|
577
|
-
}, 0);
|
|
578
|
-
|
|
579
|
-
return () => {
|
|
580
|
-
clearTimeout(timeoutId);
|
|
581
|
-
isCleaningUpRef.current = true;
|
|
582
|
-
const unsubscribers = { ...unsubscribersRef.current };
|
|
583
|
-
unsubscribersRef.current = {};
|
|
584
|
-
Object.values(unsubscribers).forEach(unsub => {
|
|
585
|
-
try {
|
|
586
|
-
unsub?.();
|
|
587
|
-
} catch (e) {
|
|
588
|
-
// Ignore cleanup errors
|
|
589
|
-
}
|
|
590
|
-
});
|
|
591
|
-
};
|
|
592
|
-
|
|
593
|
-
}, [subscriptionVersion, subscribeToColumn, pageSize]);
|
|
594
|
-
|
|
595
|
-
// Track which subscription version last updated the counts
|
|
596
|
-
const lastProcessedVersionRef = useRef(subscriptionVersion);
|
|
597
|
-
|
|
598
|
-
// Separate effect to handle individual column load-more WITHOUT triggering full re-subscription
|
|
599
|
-
useEffect(() => {
|
|
600
|
-
// If subscriptionVersion changed, the main effect will handle everything
|
|
601
|
-
// Skip this effect to avoid race conditions
|
|
602
|
-
if (subscriptionVersion !== lastProcessedVersionRef.current) {
|
|
603
|
-
lastProcessedVersionRef.current = subscriptionVersion;
|
|
604
|
-
prevColumnItemCountsRef.current = { ...columnItemCounts };
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
const prevCounts = prevColumnItemCountsRef.current;
|
|
609
|
-
|
|
610
|
-
columns.forEach(column => {
|
|
611
|
-
const prevCount = prevCounts[column] ?? pageSize;
|
|
612
|
-
const newCount = columnItemCounts[column] ?? pageSize;
|
|
613
|
-
|
|
614
|
-
// Only re-subscribe if this specific column's count increased (load more)
|
|
615
|
-
if (newCount > prevCount && !isCleaningUpRef.current) {
|
|
616
|
-
// Unsubscribe only this column
|
|
617
|
-
if (unsubscribersRef.current[column]) {
|
|
618
|
-
try {
|
|
619
|
-
unsubscribersRef.current[column]();
|
|
620
|
-
} catch (e) {
|
|
621
|
-
// Ignore cleanup errors
|
|
622
|
-
}
|
|
623
|
-
delete unsubscribersRef.current[column];
|
|
624
|
-
}
|
|
625
|
-
// Re-subscribe with new limit after a small delay
|
|
626
|
-
setTimeout(() => {
|
|
627
|
-
if (!isCleaningUpRef.current) {
|
|
628
|
-
subscribeToColumn(column, newCount);
|
|
629
|
-
}
|
|
630
|
-
}, 0);
|
|
631
|
-
}
|
|
632
|
-
});
|
|
633
|
-
|
|
634
|
-
// Update the ref
|
|
635
|
-
prevColumnItemCountsRef.current = { ...columnItemCounts };
|
|
636
|
-
}, [columnItemCounts, columns, pageSize, subscribeToColumn, subscriptionVersion]);
|
|
637
|
-
|
|
638
|
-
const loadMoreColumn = useCallback((column: COLUMN) => {
|
|
639
|
-
setColumnItemCounts(prev => ({
|
|
640
|
-
...prev,
|
|
641
|
-
[column]: (prev[column] ?? pageSize) + pageSize
|
|
642
|
-
}));
|
|
643
|
-
}, [pageSize]);
|
|
644
|
-
|
|
645
|
-
const refreshColumn = useCallback((column: COLUMN) => {
|
|
646
|
-
// Force re-subscribe by resetting to initial count
|
|
647
|
-
setColumnItemCounts(prev => ({
|
|
648
|
-
...prev,
|
|
649
|
-
[column]: pageSize
|
|
650
|
-
}));
|
|
651
|
-
}, [pageSize]);
|
|
652
|
-
|
|
653
|
-
const refreshAll = useCallback(() => {
|
|
654
|
-
const reset: Record<string, number> = {};
|
|
655
|
-
columns.forEach(col => {
|
|
656
|
-
reset[col] = pageSize;
|
|
657
|
-
});
|
|
658
|
-
setColumnItemCounts(reset);
|
|
659
|
-
}, [columns, pageSize]);
|
|
660
|
-
|
|
661
|
-
// Optimistic update for when moving an item
|
|
662
|
-
const moveItemOptimistically = useCallback((itemId: string, sourceColumn: COLUMN, targetColumn: COLUMN, newValues?: Record<string, any>, newIndex?: number) => {
|
|
663
|
-
setColumnData(prev => {
|
|
664
|
-
const updated = { ...prev };
|
|
665
|
-
let itemToMove: Entity<M> | undefined;
|
|
666
|
-
|
|
667
|
-
const sourceEntities = [...(updated[sourceColumn]?.entities || [])];
|
|
668
|
-
const itemIndex = sourceEntities.findIndex(e => String(e.id) === itemId);
|
|
669
|
-
|
|
670
|
-
if (itemIndex !== -1) {
|
|
671
|
-
itemToMove = sourceEntities[itemIndex];
|
|
672
|
-
sourceEntities.splice(itemIndex, 1);
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
if (itemToMove) {
|
|
676
|
-
const newValuesMerged = {
|
|
677
|
-
...itemToMove.values,
|
|
678
|
-
...(newValues || {})
|
|
679
|
-
};
|
|
680
|
-
|
|
681
|
-
const updatedEntity = {
|
|
682
|
-
...itemToMove,
|
|
683
|
-
values: newValuesMerged
|
|
684
|
-
};
|
|
685
|
-
|
|
686
|
-
// Store in pending items ref
|
|
687
|
-
pendingItemsRef.current[String(updatedEntity.id)] = {
|
|
688
|
-
entity: updatedEntity,
|
|
689
|
-
expectedValues: newValuesMerged
|
|
690
|
-
};
|
|
691
|
-
|
|
692
|
-
const targetEntities = sourceColumn === targetColumn ? sourceEntities : [...(updated[targetColumn]?.entities || [])];
|
|
693
|
-
|
|
694
|
-
if (newIndex !== undefined && newIndex >= 0 && newIndex <= targetEntities.length) {
|
|
695
|
-
targetEntities.splice(newIndex, 0, updatedEntity);
|
|
696
|
-
} else {
|
|
697
|
-
targetEntities.push(updatedEntity);
|
|
698
|
-
if (orderPropertyRef.current) {
|
|
699
|
-
const orderProp = orderPropertyRef.current;
|
|
700
|
-
targetEntities.sort((a, b) => {
|
|
701
|
-
const valA = a.values?.[orderProp] as string | undefined | null;
|
|
702
|
-
const valB = b.values?.[orderProp] as string | undefined | null;
|
|
703
|
-
|
|
704
|
-
// Handle nulls/empty strings to match Postgres NULLS LAST (ASC) behavior
|
|
705
|
-
const isAEmpty = valA === undefined || valA === null || valA === "";
|
|
706
|
-
const isBEmpty = valB === undefined || valB === null || valB === "";
|
|
707
|
-
|
|
708
|
-
if (isAEmpty && isBEmpty) return 0;
|
|
709
|
-
if (isAEmpty) return 1; // A is null, B is not -> A goes after B
|
|
710
|
-
if (isBEmpty) return -1; // B is null, A is not -> A goes before B
|
|
711
|
-
|
|
712
|
-
return valA < valB ? -1 : valA > valB ? 1 : 0;
|
|
713
|
-
});
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
updated[sourceColumn] = {
|
|
718
|
-
...updated[sourceColumn],
|
|
719
|
-
entities: sourceColumn === targetColumn ? targetEntities : sourceEntities,
|
|
720
|
-
totalCount: sourceColumn === targetColumn
|
|
721
|
-
? updated[sourceColumn].totalCount
|
|
722
|
-
: Math.max(0, (updated[sourceColumn].totalCount ?? 0) - 1)
|
|
723
|
-
};
|
|
724
|
-
|
|
725
|
-
if (sourceColumn !== targetColumn) {
|
|
726
|
-
updated[targetColumn] = {
|
|
727
|
-
...updated[targetColumn],
|
|
728
|
-
entities: targetEntities,
|
|
729
|
-
totalCount: (updated[targetColumn].totalCount ?? 0) + 1
|
|
730
|
-
};
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
} else if (sourceColumn !== targetColumn) {
|
|
734
|
-
// If item not found locally but counts need update
|
|
735
|
-
if (updated[sourceColumn]?.totalCount !== undefined) {
|
|
736
|
-
updated[sourceColumn] = {
|
|
737
|
-
...updated[sourceColumn],
|
|
738
|
-
totalCount: Math.max(0, (updated[sourceColumn].totalCount ?? 0) - 1)
|
|
739
|
-
};
|
|
740
|
-
}
|
|
741
|
-
if (updated[targetColumn]?.totalCount !== undefined) {
|
|
742
|
-
updated[targetColumn] = {
|
|
743
|
-
...updated[targetColumn],
|
|
744
|
-
totalCount: (updated[targetColumn].totalCount ?? 0) + 1
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
return updated;
|
|
750
|
-
});
|
|
751
|
-
}, []);
|
|
752
|
-
|
|
753
|
-
// Optimistic update for column counts when deleting items
|
|
754
|
-
const decrementColumnCounts = useCallback((columnDeltas: Record<COLUMN, number>) => {
|
|
755
|
-
setColumnData(prev => {
|
|
756
|
-
const updated = { ...prev };
|
|
757
|
-
|
|
758
|
-
for (const [column, delta] of Object.entries(columnDeltas) as [COLUMN, number][]) {
|
|
759
|
-
if (updated[column]?.totalCount !== undefined) {
|
|
760
|
-
updated[column] = {
|
|
761
|
-
...updated[column],
|
|
762
|
-
totalCount: Math.max(0, (updated[column].totalCount ?? 0) - delta)
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
return updated;
|
|
768
|
-
});
|
|
769
|
-
}, []);
|
|
770
|
-
|
|
771
|
-
// Aggregate loading and error state
|
|
772
|
-
const loading = useMemo(() => {
|
|
773
|
-
return Object.values(columnData).some((col) => col.loading);
|
|
774
|
-
}, [columnData]);
|
|
775
|
-
|
|
776
|
-
const error = useMemo(() => {
|
|
777
|
-
const errors = Object.values(columnData)
|
|
778
|
-
.map((col) => col.error)
|
|
779
|
-
.filter(Boolean);
|
|
780
|
-
return errors[0];
|
|
781
|
-
}, [columnData]);
|
|
782
|
-
|
|
783
|
-
return useMemo(() => ({
|
|
784
|
-
columnData: columnData as Record<COLUMN, BoardColumnData<M>>,
|
|
785
|
-
loadMoreColumn,
|
|
786
|
-
refreshColumn,
|
|
787
|
-
refreshAll,
|
|
788
|
-
moveItemOptimistically,
|
|
789
|
-
decrementColumnCounts,
|
|
790
|
-
loading,
|
|
791
|
-
error
|
|
792
|
-
}), [
|
|
793
|
-
columnData,
|
|
794
|
-
loadMoreColumn,
|
|
795
|
-
refreshColumn,
|
|
796
|
-
refreshAll,
|
|
797
|
-
moveItemOptimistically,
|
|
798
|
-
decrementColumnCounts,
|
|
799
|
-
loading,
|
|
800
|
-
error
|
|
801
|
-
]);
|
|
802
|
-
}
|