@object-ui/app-shell 6.2.3 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1229 -0
- package/README.md +292 -0
- package/dist/assistant/assistantBus.d.ts +72 -0
- package/dist/assistant/assistantBus.js +133 -0
- package/dist/chrome/CommandPalette.d.ts +1 -1
- package/dist/chrome/CommandPalette.js +26 -22
- package/dist/chrome/ConditionalAuthWrapper.d.ts +1 -1
- package/dist/chrome/ConsoleToaster.d.ts +1 -1
- package/dist/chrome/ConsoleToaster.js +3 -1
- package/dist/chrome/ErrorBoundary.d.ts +1 -1
- package/dist/chrome/KeyboardShortcutsDialog.d.ts +1 -1
- package/dist/chrome/KeyboardShortcutsDialog.js +16 -5
- package/dist/chrome/LoadingScreen.d.ts +1 -1
- package/dist/chrome/LoadingScreen.js +22 -26
- package/dist/chrome/RouteFader.d.ts +1 -1
- package/dist/chrome/ThemeProvider.d.ts +1 -1
- package/dist/components/ManagedByBadge.d.ts +1 -1
- package/dist/console/AppContent.d.ts +1 -1
- package/dist/console/AppContent.js +184 -39
- package/dist/console/ConsoleShell.d.ts +7 -7
- package/dist/console/ConsoleShell.js +32 -3
- package/dist/console/ai/AiChatPage.d.ts +88 -1
- package/dist/console/ai/AiChatPage.js +747 -66
- package/dist/console/ai/ConversationsSidebar.d.ts +26 -1
- package/dist/console/ai/ConversationsSidebar.js +149 -34
- package/dist/console/ai/LiveCanvas.d.ts +28 -0
- package/dist/console/ai/LiveCanvas.js +80 -0
- package/dist/console/ai/reconcileTurn.d.ts +8 -0
- package/dist/console/ai/reconcileTurn.js +20 -0
- package/dist/console/auth/AuthPageLayout.d.ts +1 -1
- package/dist/console/auth/ForgotPasswordPage.d.ts +1 -1
- package/dist/console/auth/LoginPage.d.ts +1 -1
- package/dist/console/auth/RegisterPage.d.ts +1 -1
- package/dist/console/auth/RegisterPage.js +23 -3
- package/dist/console/cloud-connection/CloudConnectionPanel.d.ts +1 -0
- package/dist/console/cloud-connection/CloudConnectionPanel.js +169 -0
- package/dist/console/home/AppCard.d.ts +1 -1
- package/dist/console/home/AppCard.js +6 -12
- package/dist/console/home/HomeAppsStrip.d.ts +8 -0
- package/dist/console/home/HomeAppsStrip.js +61 -0
- package/dist/console/home/HomeLayout.d.ts +1 -1
- package/dist/console/home/HomeLayout.js +3 -1
- package/dist/console/home/HomePage.d.ts +1 -2
- package/dist/console/home/HomePage.js +149 -21
- package/dist/console/home/HomeRail.d.ts +22 -0
- package/dist/console/home/HomeRail.js +62 -0
- package/dist/console/home/QuickActions.d.ts +1 -1
- package/dist/console/home/QuickActions.js +3 -11
- package/dist/console/home/RecentApps.d.ts +1 -1
- package/dist/console/home/RecentApps.js +2 -2
- package/dist/console/home/StarredApps.d.ts +1 -1
- package/dist/console/home/StarredApps.js +2 -2
- package/dist/console/marketplace/InstalledListWidget.d.ts +1 -0
- package/dist/console/marketplace/InstalledListWidget.js +93 -0
- package/dist/console/marketplace/MarkdownText.d.ts +1 -1
- package/dist/console/marketplace/MarketplaceAccessDenied.d.ts +1 -1
- package/dist/console/marketplace/MarketplaceInstalledPage.d.ts +8 -14
- package/dist/console/marketplace/MarketplaceInstalledPage.js +14 -66
- package/dist/console/marketplace/MarketplacePackagePage.d.ts +1 -1
- package/dist/console/marketplace/MarketplacePackagePage.js +249 -8
- package/dist/console/marketplace/MarketplacePage.d.ts +1 -1
- package/dist/console/marketplace/MarketplacePage.js +60 -3
- package/dist/console/marketplace/PackageIcon.d.ts +1 -1
- package/dist/console/marketplace/PluginDisclosure.d.ts +14 -0
- package/dist/console/marketplace/PluginDisclosure.js +38 -0
- package/dist/console/marketplace/marketplaceApi.d.ts +123 -0
- package/dist/console/marketplace/marketplaceApi.js +254 -1
- package/dist/console/organizations/CreateWorkspaceDialog.d.ts +1 -1
- package/dist/console/organizations/OrganizationsLayout.d.ts +1 -1
- package/dist/console/organizations/OrganizationsPage.d.ts +1 -1
- package/dist/console/organizations/manage/AcceptInvitationPage.d.ts +1 -1
- package/dist/console/organizations/manage/InvitationsPage.d.ts +1 -1
- package/dist/console/organizations/manage/InviteMemberDialog.d.ts +1 -1
- package/dist/console/organizations/manage/MembersPage.d.ts +1 -1
- package/dist/console/organizations/manage/OrganizationLayout.d.ts +1 -1
- package/dist/console/organizations/manage/SettingsPage.d.ts +1 -1
- package/dist/context/CommandPaletteProvider.d.ts +44 -0
- package/dist/context/CommandPaletteProvider.js +71 -0
- package/dist/context/FavoritesProvider.d.ts +1 -1
- package/dist/context/NavigationContext.d.ts +1 -1
- package/dist/context/RecentItemsProvider.d.ts +2 -2
- package/dist/context/UserStateAdapters.d.ts +1 -1
- package/dist/context/index.d.ts +2 -0
- package/dist/context/index.js +1 -0
- package/dist/hooks/index.d.ts +5 -2
- package/dist/hooks/index.js +4 -1
- package/dist/hooks/useActionModal.d.ts +53 -0
- package/dist/hooks/useActionModal.js +111 -0
- package/dist/hooks/useChatConversation.d.ts +137 -4
- package/dist/hooks/useChatConversation.js +316 -25
- package/dist/hooks/useConsoleActionRuntime.d.ts +70 -0
- package/dist/hooks/useConsoleActionRuntime.js +564 -0
- package/dist/hooks/useConversationList.js +61 -3
- package/dist/hooks/useHomeInbox.d.ts +13 -0
- package/dist/hooks/useHomeInbox.js +142 -0
- package/dist/hooks/useNavPins.js +17 -23
- package/dist/hooks/useNavigationSync.d.ts +33 -0
- package/dist/hooks/useNavigationSync.js +98 -12
- package/dist/hooks/useReconcileOnError.d.ts +40 -0
- package/dist/hooks/useReconcileOnError.js +37 -0
- package/dist/hooks/useRecordApprovals.d.ts +18 -19
- package/dist/hooks/useRecordApprovals.js +24 -40
- package/dist/hooks/useResponsiveSidebar.js +14 -5
- package/dist/hooks/useSettleSignal.d.ts +19 -0
- package/dist/hooks/useSettleSignal.js +20 -0
- package/dist/hooks/useTrackRouteAsRecent.js +35 -0
- package/dist/hooks/useUrlOverlay.d.ts +62 -0
- package/dist/hooks/useUrlOverlay.js +88 -0
- package/dist/index.d.ts +18 -8
- package/dist/index.js +17 -5
- package/dist/layout/ActivityFeed.d.ts +1 -1
- package/dist/layout/AppHeader.d.ts +3 -2
- package/dist/layout/AppHeader.js +237 -72
- package/dist/layout/AppSidebar.d.ts +2 -1
- package/dist/layout/AppSidebar.js +26 -46
- package/dist/layout/AppSwitcher.d.ts +2 -1
- package/dist/layout/AppSwitcher.js +9 -5
- package/dist/layout/AuthPageLayout.d.ts +1 -1
- package/dist/layout/ConnectionStatus.d.ts +1 -1
- package/dist/layout/ConnectionStatus.js +9 -6
- package/dist/layout/ConsoleChatbotFab.d.ts +19 -1
- package/dist/layout/ConsoleChatbotFab.js +16 -2
- package/dist/layout/ConsoleFloatingChatbot.d.ts +34 -2
- package/dist/layout/ConsoleFloatingChatbot.js +391 -41
- package/dist/layout/ConsoleLayout.d.ts +1 -1
- package/dist/layout/ConsoleLayout.js +27 -11
- package/dist/layout/ContextSelectors.d.ts +44 -0
- package/dist/layout/ContextSelectors.js +242 -0
- package/dist/layout/InboxPopover.d.ts +6 -1
- package/dist/layout/InboxPopover.js +25 -6
- package/dist/layout/LocaleSwitcher.d.ts +1 -1
- package/dist/layout/LocalizedSidebarTrigger.d.ts +2 -0
- package/dist/layout/LocalizedSidebarTrigger.js +15 -0
- package/dist/layout/MobileViewSwitcherContext.d.ts +1 -1
- package/dist/layout/ModeToggle.d.ts +1 -1
- package/dist/layout/PageHeader.d.ts +1 -1
- package/dist/layout/UnifiedSidebar.d.ts +2 -1
- package/dist/layout/UnifiedSidebar.js +116 -15
- package/dist/layout/agentPicker.d.ts +56 -0
- package/dist/layout/agentPicker.js +40 -0
- package/dist/observability/index.d.ts +1 -0
- package/dist/observability/index.js +1 -0
- package/dist/observability/settleSignal.d.ts +64 -0
- package/dist/observability/settleSignal.js +131 -0
- package/dist/preview/CommitTimeline.d.ts +15 -0
- package/dist/preview/CommitTimeline.js +82 -0
- package/dist/preview/DraftChangesPanel.d.ts +19 -0
- package/dist/preview/DraftChangesPanel.js +114 -0
- package/dist/preview/DraftPreviewBar.d.ts +8 -0
- package/dist/preview/DraftPreviewBar.js +86 -0
- package/dist/preview/PreviewDraftEmptyState.d.ts +16 -0
- package/dist/preview/PreviewDraftEmptyState.js +47 -0
- package/dist/preview/PreviewModeContext.d.ts +57 -0
- package/dist/preview/PreviewModeContext.js +99 -0
- package/dist/preview/UnpublishedAppBar.d.ts +8 -0
- package/dist/preview/UnpublishedAppBar.js +83 -0
- package/dist/preview/commitHistory.d.ts +28 -0
- package/dist/preview/commitHistory.js +48 -0
- package/dist/preview/draftStatus.d.ts +20 -0
- package/dist/preview/draftStatus.js +27 -0
- package/dist/preview/usePublishAllDrafts.d.ts +18 -0
- package/dist/preview/usePublishAllDrafts.js +106 -0
- package/dist/providers/AdapterProvider.d.ts +1 -1
- package/dist/providers/AdapterProvider.js +6 -1
- package/dist/providers/ExpressionProvider.d.ts +1 -1
- package/dist/providers/MetadataProvider.d.ts +17 -2
- package/dist/providers/MetadataProvider.js +192 -12
- package/dist/runtime-config.d.ts +46 -2
- package/dist/runtime-config.js +39 -2
- package/dist/services/builtinComponents.js +68 -59
- package/dist/skeletons/SkeletonDashboard.d.ts +1 -1
- package/dist/skeletons/SkeletonDetail.d.ts +1 -1
- package/dist/skeletons/SkeletonGrid.d.ts +1 -1
- package/dist/utils/appRoute.d.ts +21 -0
- package/dist/utils/appRoute.js +25 -0
- package/dist/utils/deriveRelatedLists.d.ts +54 -0
- package/dist/utils/deriveRelatedLists.js +91 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/managedByEmptyState.d.ts +8 -1
- package/dist/utils/managedByEmptyState.js +13 -7
- package/dist/utils/preferLocal.d.ts +18 -0
- package/dist/utils/preferLocal.js +24 -0
- package/dist/views/ActionConfirmDialog.d.ts +1 -1
- package/dist/views/ActionConfirmDialog.js +3 -1
- package/dist/views/ActionParamDialog.d.ts +6 -1
- package/dist/views/ActionParamDialog.js +9 -3
- package/dist/views/ActionResultDialog.d.ts +13 -0
- package/dist/views/ActionResultDialog.js +134 -0
- package/dist/views/ComponentNavView.d.ts +14 -1
- package/dist/views/CreateViewDialog.d.ts +1 -1
- package/dist/views/DashboardConfigPanel.d.ts +28 -0
- package/dist/views/DashboardConfigPanel.js +81 -0
- package/dist/views/DashboardView.d.ts +4 -3
- package/dist/views/DashboardView.js +38 -239
- package/dist/views/FlowRunner.d.ts +31 -0
- package/dist/views/FlowRunner.js +121 -0
- package/dist/views/InterfaceListPage.d.ts +49 -0
- package/dist/views/InterfaceListPage.js +347 -0
- package/dist/views/MetadataInspector.d.ts +2 -2
- package/dist/views/ObjectView.d.ts +1 -1
- package/dist/views/ObjectView.js +209 -532
- package/dist/views/PageView.d.ts +8 -3
- package/dist/views/PageView.js +45 -32
- package/dist/views/RecordDetailView.d.ts +1 -1
- package/dist/views/RecordDetailView.js +363 -148
- package/dist/views/RecordFormPage.d.ts +1 -1
- package/dist/views/RecordFormPage.js +26 -1
- package/dist/views/ReportConfigPanel.d.ts +37 -0
- package/dist/views/ReportConfigPanel.js +85 -0
- package/dist/views/ReportView.d.ts +1 -1
- package/dist/views/ReportView.js +116 -7
- package/dist/views/RuntimeDraftBar.d.ts +30 -0
- package/dist/views/RuntimeDraftBar.js +112 -0
- package/dist/views/ScreenView.d.ts +70 -0
- package/dist/views/ScreenView.js +73 -0
- package/dist/views/SearchResultsPage.d.ts +1 -1
- package/dist/views/SearchResultsPage.js +8 -18
- package/dist/views/ViewConfigPanel.d.ts +24 -17
- package/dist/views/ViewConfigPanel.js +121 -77
- package/dist/views/index.d.ts +1 -1
- package/dist/views/index.js +1 -1
- package/dist/views/metadata-admin/AuditPanel.d.ts +28 -0
- package/dist/views/metadata-admin/AuditPanel.js +79 -0
- package/dist/views/metadata-admin/DiagnosticsPage.d.ts +20 -0
- package/dist/views/metadata-admin/DiagnosticsPage.js +69 -0
- package/dist/views/metadata-admin/DirectoryPage.d.ts +16 -1
- package/dist/views/metadata-admin/DirectoryPage.js +101 -24
- package/dist/views/metadata-admin/DraftReviewPanel.d.ts +33 -0
- package/dist/views/metadata-admin/DraftReviewPanel.js +77 -0
- package/dist/views/metadata-admin/EmbeddedItemEditor.d.ts +17 -1
- package/dist/views/metadata-admin/EmbeddedItemEditor.js +15 -8
- package/dist/views/metadata-admin/JsonSourceEditor.d.ts +39 -0
- package/dist/views/metadata-admin/JsonSourceEditor.js +196 -0
- package/dist/views/metadata-admin/LayeredDiff.d.ts +39 -1
- package/dist/views/metadata-admin/LayeredDiff.js +171 -5
- package/dist/views/metadata-admin/MetadataDetailDrawer.d.ts +15 -1
- package/dist/views/metadata-admin/MetadataTypeActions.d.ts +48 -0
- package/dist/views/metadata-admin/MetadataTypeActions.js +165 -0
- package/dist/views/metadata-admin/PackagesPage.d.ts +18 -0
- package/dist/views/metadata-admin/PackagesPage.js +403 -0
- package/dist/views/metadata-admin/PageShell.d.ts +1 -1
- package/dist/views/metadata-admin/PageShell.js +9 -4
- package/dist/views/metadata-admin/PermissionMatrixEditor.d.ts +35 -1
- package/dist/views/metadata-admin/QuickFind.d.ts +21 -1
- package/dist/views/metadata-admin/QuickFind.js +6 -3
- package/dist/views/metadata-admin/RelatedPanel.d.ts +24 -1
- package/dist/views/metadata-admin/RelatedPanel.js +20 -18
- package/dist/views/metadata-admin/ResourceEditPage.d.ts +40 -1
- package/dist/views/metadata-admin/ResourceEditPage.js +1250 -60
- package/dist/views/metadata-admin/ResourceHistoryPage.d.ts +39 -1
- package/dist/views/metadata-admin/ResourceHistoryPage.js +66 -16
- package/dist/views/metadata-admin/ResourceListPage.d.ts +13 -1
- package/dist/views/metadata-admin/ResourceListPage.js +258 -30
- package/dist/views/metadata-admin/ResourceRouter.d.ts +23 -1
- package/dist/views/metadata-admin/SchemaForm.d.ts +34 -1
- package/dist/views/metadata-admin/SchemaForm.js +559 -49
- package/dist/views/metadata-admin/StudioHomePage.d.ts +22 -0
- package/dist/views/metadata-admin/StudioHomePage.js +205 -0
- package/dist/views/metadata-admin/anchors.js +255 -24
- package/dist/views/metadata-admin/clientValidation.d.ts +50 -0
- package/dist/views/metadata-admin/clientValidation.js +169 -0
- package/dist/views/metadata-admin/color-variant-field.d.ts +30 -0
- package/dist/views/metadata-admin/color-variant-field.js +38 -0
- package/dist/views/metadata-admin/createDerive.d.ts +75 -0
- package/dist/views/metadata-admin/createDerive.js +179 -0
- package/dist/views/metadata-admin/dashboard-schema.d.ts +12 -0
- package/dist/views/metadata-admin/dashboard-schema.js +80 -0
- package/dist/views/metadata-admin/datasource/DatasourceResourcePage.d.ts +35 -0
- package/dist/views/metadata-admin/datasource/DatasourceResourcePage.js +327 -0
- package/dist/views/metadata-admin/datasource/register.d.ts +1 -0
- package/dist/views/metadata-admin/datasource/register.js +24 -0
- package/dist/views/metadata-admin/default-inspector-registry.d.ts +49 -0
- package/dist/views/metadata-admin/default-inspector-registry.js +8 -0
- package/dist/views/metadata-admin/default-schemas.js +115 -10
- package/dist/views/metadata-admin/external/ExternalDatasourcePanel.d.ts +27 -0
- package/dist/views/metadata-admin/external/ExternalDatasourcePanel.js +69 -0
- package/dist/views/metadata-admin/external/ImportObjectDialog.d.ts +27 -0
- package/dist/views/metadata-admin/external/ImportObjectDialog.js +77 -0
- package/dist/views/metadata-admin/external/SchemaBrowser.d.ts +16 -0
- package/dist/views/metadata-admin/external/SchemaBrowser.js +74 -0
- package/dist/views/metadata-admin/external/ValidationPanel.d.ts +16 -0
- package/dist/views/metadata-admin/external/ValidationPanel.js +68 -0
- package/dist/views/metadata-admin/external/api.d.ts +100 -0
- package/dist/views/metadata-admin/external/api.js +124 -0
- package/dist/views/metadata-admin/i18n.d.ts +1 -0
- package/dist/views/metadata-admin/i18n.js +1252 -2
- package/dist/views/metadata-admin/index.d.ts +8 -5
- package/dist/views/metadata-admin/index.js +12 -2
- package/dist/views/metadata-admin/inspector-registry.d.ts +51 -0
- package/dist/views/metadata-admin/inspector-registry.js +11 -0
- package/dist/views/metadata-admin/inspectors/ActionDefaultInspector.d.ts +30 -0
- package/dist/views/metadata-admin/inspectors/ActionDefaultInspector.js +180 -0
- package/dist/views/metadata-admin/inspectors/AppNavInspector.d.ts +16 -0
- package/dist/views/metadata-admin/inspectors/AppNavInspector.js +110 -0
- package/dist/views/metadata-admin/inspectors/ConditionBuilder.d.ts +29 -0
- package/dist/views/metadata-admin/inspectors/ConditionBuilder.js +154 -0
- package/dist/views/metadata-admin/inspectors/DashboardDefaultInspector.d.ts +28 -0
- package/dist/views/metadata-admin/inspectors/DashboardDefaultInspector.js +110 -0
- package/dist/views/metadata-admin/inspectors/DashboardWidgetInspector.d.ts +18 -0
- package/dist/views/metadata-admin/inspectors/DashboardWidgetInspector.js +139 -0
- package/dist/views/metadata-admin/inspectors/DatasetDefaultInspector.d.ts +21 -0
- package/dist/views/metadata-admin/inspectors/DatasetDefaultInspector.js +221 -0
- package/dist/views/metadata-admin/inspectors/FlowEdgeInspector.d.ts +16 -0
- package/dist/views/metadata-admin/inspectors/FlowEdgeInspector.js +126 -0
- package/dist/views/metadata-admin/inspectors/FlowInspector.d.ts +12 -0
- package/dist/views/metadata-admin/inspectors/FlowInspector.js +9 -0
- package/dist/views/metadata-admin/inspectors/FlowKeyValueField.d.ts +30 -0
- package/dist/views/metadata-admin/inspectors/FlowKeyValueField.js +125 -0
- package/dist/views/metadata-admin/inspectors/FlowNodeConfigField.d.ts +18 -0
- package/dist/views/metadata-admin/inspectors/FlowNodeConfigField.js +40 -0
- package/dist/views/metadata-admin/inspectors/FlowNodeInspector.d.ts +14 -0
- package/dist/views/metadata-admin/inspectors/FlowNodeInspector.js +205 -0
- package/dist/views/metadata-admin/inspectors/FlowObjectListField.d.ts +26 -0
- package/dist/views/metadata-admin/inspectors/FlowObjectListField.js +105 -0
- package/dist/views/metadata-admin/inspectors/FlowReferenceField.d.ts +83 -0
- package/dist/views/metadata-admin/inspectors/FlowReferenceField.js +181 -0
- package/dist/views/metadata-admin/inspectors/FlowStringListField.d.ts +21 -0
- package/dist/views/metadata-admin/inspectors/FlowStringListField.js +60 -0
- package/dist/views/metadata-admin/inspectors/InspectorComboField.d.ts +40 -0
- package/dist/views/metadata-admin/inspectors/InspectorComboField.js +61 -0
- package/dist/views/metadata-admin/inspectors/ObjectDefaultInspector.d.ts +21 -0
- package/dist/views/metadata-admin/inspectors/ObjectDefaultInspector.js +55 -0
- package/dist/views/metadata-admin/inspectors/ObjectFieldInspector.d.ts +23 -0
- package/dist/views/metadata-admin/inspectors/ObjectFieldInspector.js +365 -0
- package/dist/views/metadata-admin/inspectors/PageBlockInspector.d.ts +48 -0
- package/dist/views/metadata-admin/inspectors/PageBlockInspector.js +332 -0
- package/dist/views/metadata-admin/inspectors/ReportDefaultInspector.d.ts +58 -0
- package/dist/views/metadata-admin/inspectors/ReportDefaultInspector.js +218 -0
- package/dist/views/metadata-admin/inspectors/ViewColumnInspector.d.ts +19 -0
- package/dist/views/metadata-admin/inspectors/ViewColumnInspector.js +144 -0
- package/dist/views/metadata-admin/inspectors/ViewInspector.d.ts +19 -0
- package/dist/views/metadata-admin/inspectors/ViewInspector.js +21 -0
- package/dist/views/metadata-admin/inspectors/ViewVariantInspector.d.ts +54 -0
- package/dist/views/metadata-admin/inspectors/ViewVariantInspector.js +191 -0
- package/dist/views/metadata-admin/inspectors/_shared.d.ts +128 -0
- package/dist/views/metadata-admin/inspectors/_shared.js +113 -0
- package/dist/views/metadata-admin/inspectors/datasetFilterCondition.d.ts +24 -0
- package/dist/views/metadata-admin/inspectors/datasetFilterCondition.js +97 -0
- package/dist/views/metadata-admin/inspectors/expression-validate.d.ts +26 -0
- package/dist/views/metadata-admin/inspectors/expression-validate.js +66 -0
- package/dist/views/metadata-admin/inspectors/flow-node-config.d.ts +143 -0
- package/dist/views/metadata-admin/inspectors/flow-node-config.js +506 -0
- package/dist/views/metadata-admin/inspectors/index.d.ts +1 -0
- package/dist/views/metadata-admin/inspectors/index.js +45 -0
- package/dist/views/metadata-admin/inspectors/json-schema-to-fields.d.ts +40 -0
- package/dist/views/metadata-admin/inspectors/json-schema-to-fields.js +227 -0
- package/dist/views/metadata-admin/inspectors/useDatasetFields.d.ts +72 -0
- package/dist/views/metadata-admin/inspectors/useDatasetFields.js +0 -0
- package/dist/views/metadata-admin/issuePath.d.ts +22 -0
- package/dist/views/metadata-admin/issuePath.js +65 -0
- package/dist/views/metadata-admin/mergeServerFields.d.ts +65 -0
- package/dist/views/metadata-admin/mergeServerFields.js +56 -0
- package/dist/views/metadata-admin/package-scope.d.ts +26 -0
- package/dist/views/metadata-admin/package-scope.js +43 -0
- package/dist/views/metadata-admin/preview-registry.d.ts +55 -0
- package/dist/views/metadata-admin/previews/ActionPreview.d.ts +25 -0
- package/dist/views/metadata-admin/previews/ActionPreview.js +238 -0
- package/dist/views/metadata-admin/previews/AddWidgetPicker.d.ts +12 -0
- package/dist/views/metadata-admin/previews/AddWidgetPicker.js +56 -0
- package/dist/views/metadata-admin/previews/AgentPreview.d.ts +24 -0
- package/dist/views/metadata-admin/previews/AgentPreview.js +100 -0
- package/dist/views/metadata-admin/previews/AppNavCanvas.d.ts +31 -0
- package/dist/views/metadata-admin/previews/AppNavCanvas.js +260 -0
- package/dist/views/metadata-admin/previews/AppPreview.d.ts +16 -1
- package/dist/views/metadata-admin/previews/AppPreview.js +23 -14
- package/dist/views/metadata-admin/previews/BookPreview.d.ts +20 -0
- package/dist/views/metadata-admin/previews/BookPreview.js +132 -0
- package/dist/views/metadata-admin/previews/DashboardPreview.d.ts +16 -1
- package/dist/views/metadata-admin/previews/DashboardPreview.js +110 -8
- package/dist/views/metadata-admin/previews/DatasetPreview.d.ts +18 -0
- package/dist/views/metadata-admin/previews/DatasetPreview.js +105 -0
- package/dist/views/metadata-admin/previews/DatasourcePreview.d.ts +23 -0
- package/dist/views/metadata-admin/previews/DatasourcePreview.js +68 -0
- package/dist/views/metadata-admin/previews/EmailTemplatePreview.d.ts +14 -1
- package/dist/views/metadata-admin/previews/FieldStub.d.ts +30 -0
- package/dist/views/metadata-admin/previews/FieldStub.js +104 -0
- package/dist/views/metadata-admin/previews/FieldsListEditor.d.ts +50 -0
- package/dist/views/metadata-admin/previews/FieldsListEditor.js +97 -0
- package/dist/views/metadata-admin/previews/FlowCanvas.d.ts +49 -0
- package/dist/views/metadata-admin/previews/FlowCanvas.js +416 -0
- package/dist/views/metadata-admin/previews/FlowPreview.d.ts +20 -0
- package/dist/views/metadata-admin/previews/FlowPreview.js +120 -0
- package/dist/views/metadata-admin/previews/FlowRunsPanel.d.ts +46 -0
- package/dist/views/metadata-admin/previews/FlowRunsPanel.js +97 -0
- package/dist/views/metadata-admin/previews/FlowSimulatorPanel.d.ts +25 -0
- package/dist/views/metadata-admin/previews/FlowSimulatorPanel.js +204 -0
- package/dist/views/metadata-admin/previews/JobPreview.d.ts +28 -0
- package/dist/views/metadata-admin/previews/JobPreview.js +290 -0
- package/dist/views/metadata-admin/previews/ObjectFormCanvas.d.ts +30 -0
- package/dist/views/metadata-admin/previews/ObjectFormCanvas.js +547 -0
- package/dist/views/metadata-admin/previews/ObjectPreview.d.ts +14 -1
- package/dist/views/metadata-admin/previews/ObjectPreview.js +5 -30
- package/dist/views/metadata-admin/previews/OutlineStrip.d.ts +32 -0
- package/dist/views/metadata-admin/previews/OutlineStrip.js +8 -0
- package/dist/views/metadata-admin/previews/PageBlockCanvas.d.ts +49 -0
- package/dist/views/metadata-admin/previews/PageBlockCanvas.js +510 -0
- package/dist/views/metadata-admin/previews/PagePreview.d.ts +10 -1
- package/dist/views/metadata-admin/previews/PagePreview.js +200 -5
- package/dist/views/metadata-admin/previews/PermissionPreview.d.ts +27 -0
- package/dist/views/metadata-admin/previews/PermissionPreview.js +115 -0
- package/dist/views/metadata-admin/previews/PreviewShell.d.ts +29 -6
- package/dist/views/metadata-admin/previews/PreviewShell.js +16 -3
- package/dist/views/metadata-admin/previews/ReportPreview.d.ts +18 -1
- package/dist/views/metadata-admin/previews/ReportPreview.js +23 -15
- package/dist/views/metadata-admin/previews/RolePreview.d.ts +19 -0
- package/dist/views/metadata-admin/previews/RolePreview.js +14 -0
- package/dist/views/metadata-admin/previews/ScreenPreview.d.ts +38 -0
- package/dist/views/metadata-admin/previews/ScreenPreview.js +61 -0
- package/dist/views/metadata-admin/previews/SkillPreview.d.ts +22 -0
- package/dist/views/metadata-admin/previews/SkillPreview.js +34 -0
- package/dist/views/metadata-admin/previews/ToolPreview.d.ts +25 -0
- package/dist/views/metadata-admin/previews/ToolPreview.js +122 -0
- package/dist/views/metadata-admin/previews/TranslationPreview.d.ts +25 -0
- package/dist/views/metadata-admin/previews/TranslationPreview.js +52 -0
- package/dist/views/metadata-admin/previews/ValidationPreview.d.ts +27 -0
- package/dist/views/metadata-admin/previews/ValidationPreview.js +110 -0
- package/dist/views/metadata-admin/previews/ViewColumnPanes.d.ts +62 -0
- package/dist/views/metadata-admin/previews/ViewColumnPanes.js +140 -0
- package/dist/views/metadata-admin/previews/ViewPreview.d.ts +23 -1
- package/dist/views/metadata-admin/previews/ViewPreview.js +101 -73
- package/dist/views/metadata-admin/previews/block-config.d.ts +82 -0
- package/dist/views/metadata-admin/previews/block-config.js +324 -0
- package/dist/views/metadata-admin/previews/block-types.d.ts +40 -0
- package/dist/views/metadata-admin/previews/block-types.js +110 -0
- package/dist/views/metadata-admin/previews/field-types.d.ts +53 -0
- package/dist/views/metadata-admin/previews/field-types.js +97 -0
- package/dist/views/metadata-admin/previews/flow-canvas-layout.d.ts +102 -0
- package/dist/views/metadata-admin/previews/flow-canvas-layout.js +227 -0
- package/dist/views/metadata-admin/previews/flow-canvas-parts.d.ts +96 -0
- package/dist/views/metadata-admin/previews/flow-canvas-parts.js +373 -0
- package/dist/views/metadata-admin/previews/form-preview.d.ts +24 -0
- package/dist/views/metadata-admin/previews/form-preview.js +29 -0
- package/dist/views/metadata-admin/previews/index.js +43 -0
- package/dist/views/metadata-admin/previews/object-fields-bridge.d.ts +66 -0
- package/dist/views/metadata-admin/previews/object-fields-bridge.js +171 -0
- package/dist/views/metadata-admin/previews/object-fields-io.d.ts +130 -0
- package/dist/views/metadata-admin/previews/object-fields-io.js +243 -0
- package/dist/views/metadata-admin/previews/screen-spec.d.ts +43 -0
- package/dist/views/metadata-admin/previews/screen-spec.js +108 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-types.d.ts +102 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-types.js +2 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-validate.d.ts +15 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-validate.js +185 -0
- package/dist/views/metadata-admin/previews/simulator/flow-simulator.d.ts +73 -0
- package/dist/views/metadata-admin/previews/simulator/flow-simulator.js +426 -0
- package/dist/views/metadata-admin/previews/useDatasetCatalog.d.ts +47 -0
- package/dist/views/metadata-admin/previews/useDatasetCatalog.js +133 -0
- package/dist/views/metadata-admin/previews/useFlowNodePalette.d.ts +44 -0
- package/dist/views/metadata-admin/previews/useFlowNodePalette.js +124 -0
- package/dist/views/metadata-admin/previews/useMetaOptions.d.ts +8 -0
- package/dist/views/metadata-admin/previews/useMetaOptions.js +50 -0
- package/dist/views/metadata-admin/previews/useObjectFields.d.ts +23 -0
- package/dist/views/metadata-admin/previews/useObjectFields.js +79 -0
- package/dist/views/metadata-admin/previews/useObjectOptions.d.ts +8 -0
- package/dist/views/metadata-admin/previews/useObjectOptions.js +43 -0
- package/dist/views/metadata-admin/previews/view-column-io.d.ts +42 -0
- package/dist/views/metadata-admin/previews/view-column-io.js +73 -0
- package/dist/views/metadata-admin/previews/widget-types.d.ts +24 -0
- package/dist/views/metadata-admin/previews/widget-types.js +40 -0
- package/dist/views/metadata-admin/registry.d.ts +140 -19
- package/dist/views/metadata-admin/report-schema.d.ts +26 -0
- package/dist/views/metadata-admin/report-schema.js +121 -0
- package/dist/views/metadata-admin/useMetadata.d.ts +100 -2
- package/dist/views/metadata-admin/useMetadata.js +155 -4
- package/dist/views/metadata-admin/view-item-normalize.d.ts +20 -0
- package/dist/views/metadata-admin/view-item-normalize.js +68 -0
- package/dist/views/metadata-admin/view-schema.d.ts +16 -0
- package/dist/views/metadata-admin/view-schema.js +107 -0
- package/dist/views/metadata-admin/view-variant-model.d.ts +23 -0
- package/dist/views/metadata-admin/view-variant-model.js +64 -0
- package/dist/views/metadata-admin/widgets.d.ts +89 -1
- package/dist/views/metadata-admin/widgets.js +491 -17
- package/dist/views/runtime-metadata-persistence.d.ts +78 -0
- package/dist/views/runtime-metadata-persistence.js +89 -0
- package/dist/views/useOpenRecordList.d.ts +18 -0
- package/dist/views/useOpenRecordList.js +36 -0
- package/dist/views/userFilterUrlState.d.ts +15 -0
- package/dist/views/userFilterUrlState.js +53 -0
- package/dist/views/view-config-adapter.d.ts +38 -0
- package/dist/views/view-config-adapter.js +80 -0
- package/package.json +52 -34
- package/dist/views/DesignDrawer.d.ts +0 -28
- package/dist/views/DesignDrawer.js +0 -51
- package/dist/views/metadata-admin/DesignerEditorWrapper.d.ts +0 -68
- package/dist/views/metadata-admin/DesignerEditorWrapper.js +0 -158
package/dist/views/ObjectView.js
CHANGED
|
@@ -11,6 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
11
11
|
*/
|
|
12
12
|
import { useMemo, useState, useCallback, useEffect, useRef, lazy, Suspense } from 'react';
|
|
13
13
|
import { useParams, useSearchParams, useNavigate, useLocation } from 'react-router-dom';
|
|
14
|
+
import { parseUserFilterParams, applyUserFilterParams } from './userFilterUrlState';
|
|
14
15
|
const ObjectChart = lazy(() => import('@object-ui/plugin-charts').then((m) => ({ default: m.ObjectChart })));
|
|
15
16
|
const ImportWizard = lazy(() => import('@object-ui/plugin-grid').then((m) => ({ default: m.ImportWizard })));
|
|
16
17
|
import { ListView } from '@object-ui/plugin-list';
|
|
@@ -24,6 +25,8 @@ import { useFavorites } from '../hooks/useFavorites';
|
|
|
24
25
|
import { getIcon } from '../utils/getIcon';
|
|
25
26
|
import { MetadataPanel, useMetadataInspector } from './MetadataInspector';
|
|
26
27
|
import { ViewConfigPanel } from './ViewConfigPanel';
|
|
28
|
+
import { useMetadataClient } from './metadata-admin/useMetadata';
|
|
29
|
+
import { persistRuntimeMetadata, createRuntimeMetadata } from './runtime-metadata-persistence';
|
|
27
30
|
import { CreateViewDialog } from './CreateViewDialog';
|
|
28
31
|
import { PageHeader } from '../layout/PageHeader';
|
|
29
32
|
import { useMobileViewSwitcherRegistration } from '../layout/MobileViewSwitcherContext';
|
|
@@ -34,13 +37,11 @@ import { resolveManagedByEmptyState } from '../utils/managedByEmptyState';
|
|
|
34
37
|
import { useObjectActions } from '../hooks/useObjectActions';
|
|
35
38
|
import { useObjectTranslation, useObjectLabel } from '@object-ui/i18n';
|
|
36
39
|
import { usePermissions } from '@object-ui/permissions';
|
|
37
|
-
import { useAuth
|
|
40
|
+
import { useAuth } from '@object-ui/auth';
|
|
38
41
|
import { useRealtimeSubscription, useConflictResolution } from '@object-ui/collaboration';
|
|
39
42
|
import { ActionProvider, useNavigationOverlay, SchemaRenderer } from '@object-ui/react';
|
|
40
43
|
import { toast } from 'sonner';
|
|
41
|
-
import {
|
|
42
|
-
import { ActionParamDialog } from './ActionParamDialog';
|
|
43
|
-
import { resolveActionParams } from '../utils/resolveActionParams';
|
|
44
|
+
import { useConsoleActionRuntime } from '../hooks/useConsoleActionRuntime';
|
|
44
45
|
/** Map view types to Lucide icons (Airtable-style) */
|
|
45
46
|
const VIEW_TYPE_ICONS = {
|
|
46
47
|
grid: TableIcon,
|
|
@@ -87,121 +88,39 @@ function substituteFilterTokens(filter, currentUserId) {
|
|
|
87
88
|
};
|
|
88
89
|
return filter.map(sub);
|
|
89
90
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const VIEW_TYPE_KEYS = [
|
|
104
|
-
'kanban', 'calendar', 'timeline', 'gantt',
|
|
105
|
-
'gallery', 'map', 'chart', 'grid',
|
|
106
|
-
];
|
|
107
|
-
// Fold view-type-specific sub-blocks plus any non-storage NamedListView
|
|
108
|
-
// fields into a single `config_json` blob so the round-trip preserves
|
|
109
|
-
// everything the renderer might need (bulkActions, rowActions, pagination,
|
|
110
|
-
// navigation, emptyState, exportOptions, rowHeight, isPinned, isDefault,
|
|
111
|
-
// visibility, sortOrder, …).
|
|
112
|
-
const subConfig = {};
|
|
113
|
-
for (const k of VIEW_TYPE_KEYS) {
|
|
114
|
-
if (config[k] && typeof config[k] === 'object') {
|
|
115
|
-
Object.assign(subConfig, config[k]);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
const EXTRA_CONFIG_KEYS = [
|
|
119
|
-
'bulkActions', 'bulkActionDefs', 'rowActions', 'pagination', 'navigation',
|
|
120
|
-
'emptyState', 'exportOptions', 'rowHeight',
|
|
121
|
-
'isPinned', 'isDefault', 'visibility', 'sortOrder',
|
|
122
|
-
'showSort',
|
|
123
|
-
];
|
|
124
|
-
for (const k of EXTRA_CONFIG_KEYS) {
|
|
125
|
-
if (config[k] !== undefined)
|
|
126
|
-
subConfig[k] = config[k];
|
|
91
|
+
export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey }) {
|
|
92
|
+
const { objectName } = useParams();
|
|
93
|
+
const { t } = useObjectTranslation();
|
|
94
|
+
// Resolve the object definition up front. When it's missing we render the
|
|
95
|
+
// "object not found" empty state *here*, before the inner component mounts.
|
|
96
|
+
// This is the key to keeping the Rules of Hooks satisfied: ObjectViewInner
|
|
97
|
+
// holds ~50 hooks and must call them unconditionally, so the missing-object
|
|
98
|
+
// branch lives in this thin wrapper instead of as a mid-component early
|
|
99
|
+
// return. The inner subtree then mounts/unmounts as a whole (object exists
|
|
100
|
+
// ↔ doesn't) rather than toggling the number of hooks executed per render.
|
|
101
|
+
const objectDef = objects.find((o) => o.name === objectName);
|
|
102
|
+
if (!objectDef) {
|
|
103
|
+
return (_jsx("div", { className: "h-full p-4 flex items-center justify-center", children: _jsxs(Empty, { children: [_jsx("div", { className: "mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-muted", children: _jsx(TableIcon, { className: "h-6 w-6 text-muted-foreground" }) }), _jsx(EmptyTitle, { children: t('console.objectView.objectNotFound') }), _jsxs(EmptyDescription, { children: [t('console.objectView.objectNotFoundDescription', { objectName }), ' ', t('console.objectView.objectNotFoundHint')] })] }) }));
|
|
127
104
|
}
|
|
128
|
-
|
|
129
|
-
? config.columns
|
|
130
|
-
: (opts.defaultColumns ?? []);
|
|
131
|
-
const viewType = config.type || 'grid';
|
|
132
|
-
const baseLabel = config.label || config.name || 'Untitled View';
|
|
133
|
-
const slug = baseLabel
|
|
134
|
-
.toLowerCase()
|
|
135
|
-
.replace(/[^a-z0-9]+/g, '_')
|
|
136
|
-
.replace(/^_+|_+$/g, '')
|
|
137
|
-
.slice(0, 60) || 'view';
|
|
138
|
-
return {
|
|
139
|
-
name: `${slug}_${Date.now().toString(36)}`,
|
|
140
|
-
label: baseLabel,
|
|
141
|
-
object_name: objectName,
|
|
142
|
-
view_type: viewType,
|
|
143
|
-
columns_json: JSON.stringify(incomingColumns),
|
|
144
|
-
filters_json: config.filter ? JSON.stringify(config.filter) : null,
|
|
145
|
-
sort_json: config.sort ? JSON.stringify(config.sort) : null,
|
|
146
|
-
config_json: Object.keys(subConfig).length > 0
|
|
147
|
-
? JSON.stringify(subConfig)
|
|
148
|
-
: null,
|
|
149
|
-
page_size: config.pageSize ?? 25,
|
|
150
|
-
show_search: config.showSearch !== false,
|
|
151
|
-
show_filters: config.showFilters !== false,
|
|
152
|
-
managed_by: 'user',
|
|
153
|
-
};
|
|
105
|
+
return (_jsx(ObjectViewInner, { dataSource: dataSource, objects: objects, onEdit: onEdit, externalRefreshKey: externalRefreshKey }));
|
|
154
106
|
}
|
|
155
107
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* inside `config_json`) so the rest of the UI — ViewTabBar, ListView, the
|
|
160
|
-
* grid renderer — can consume it without caring about the storage layer.
|
|
161
|
-
*
|
|
162
|
-
* Robust to fixtures/mocks that already store the spec shape directly
|
|
163
|
-
* (older tests, the in-memory dev adapter): if `*_json` is missing we fall
|
|
164
|
-
* back to `sv.columns` / `sv.filter` / `sv.sort` as-is.
|
|
108
|
+
* Inner ObjectView body. Only mounted by {@link ObjectView} once the object
|
|
109
|
+
* definition is known to exist, so every hook below runs unconditionally on
|
|
110
|
+
* every render of this component — no early return sits between hook calls.
|
|
165
111
|
*/
|
|
166
|
-
function
|
|
167
|
-
const parse = (raw) => {
|
|
168
|
-
if (raw == null)
|
|
169
|
-
return undefined;
|
|
170
|
-
if (typeof raw !== 'string')
|
|
171
|
-
return raw;
|
|
172
|
-
try {
|
|
173
|
-
return JSON.parse(raw);
|
|
174
|
-
}
|
|
175
|
-
catch {
|
|
176
|
-
return undefined;
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
const columns = parse(sv.columns_json) ?? sv.columns;
|
|
180
|
-
const filter = parse(sv.filters_json) ?? sv.filter;
|
|
181
|
-
const sort = parse(sv.sort_json) ?? sv.sort;
|
|
182
|
-
const extra = parse(sv.config_json) ?? {};
|
|
183
|
-
return {
|
|
184
|
-
...sv,
|
|
185
|
-
...extra,
|
|
186
|
-
type: sv.view_type ?? sv.type ?? 'grid',
|
|
187
|
-
objectName: sv.object_name ?? sv.objectName,
|
|
188
|
-
columns,
|
|
189
|
-
filter,
|
|
190
|
-
sort,
|
|
191
|
-
showSearch: sv.show_search ?? sv.showSearch,
|
|
192
|
-
showFilters: sv.show_filters ?? sv.showFilters,
|
|
193
|
-
pageSize: sv.page_size ?? sv.pageSize,
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey }) {
|
|
112
|
+
function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }) {
|
|
197
113
|
const navigate = useNavigate();
|
|
198
114
|
const { appName, objectName, viewId } = useParams();
|
|
199
115
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
200
116
|
const location = useLocation();
|
|
201
117
|
const { showDebug } = useMetadataInspector();
|
|
202
118
|
const { t } = useObjectTranslation();
|
|
203
|
-
const { objectLabel, objectDescription: objectDesc, viewLabel, viewEmptyState, actionLabel, actionConfirm, actionSuccess, fieldLabel, fieldOptionLabel } = useObjectLabel();
|
|
119
|
+
const { objectLabel, objectDescription: objectDesc, viewLabel, viewEmptyState, actionLabel, actionConfirm, actionSuccess, actionParamText, fieldLabel, fieldOptionLabel } = useObjectLabel();
|
|
204
120
|
const { isFavorite, toggleFavorite } = useFavorites();
|
|
121
|
+
// ADR-0034: runtime view edits persist via the metadata draft/publish
|
|
122
|
+
// model (the `sys_view` table is retired).
|
|
123
|
+
const metadataClient = useMetadataClient();
|
|
205
124
|
// Inline view config panel state (Airtable-style right sidebar)
|
|
206
125
|
const [showViewConfigPanel, setShowViewConfigPanel] = useState(false);
|
|
207
126
|
const [viewConfigPanelMode, setViewConfigPanelMode] = useState('edit');
|
|
@@ -243,28 +162,23 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
243
162
|
const handleViewConfigSave = useCallback((draft) => {
|
|
244
163
|
setViewDraft(draft);
|
|
245
164
|
setRefreshKey(k => k + 1);
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
else {
|
|
256
|
-
console.warn('[ViewConfigPanel] Cannot persist view config: missing objectName or viewId.');
|
|
257
|
-
}
|
|
165
|
+
// ADR-0034: stage a per-item draft via the metadata seam; an explicit
|
|
166
|
+
// Publish (RuntimeDraftBar) promotes it + records a version.
|
|
167
|
+
const vid = draft.id;
|
|
168
|
+
if (metadataClient && vid) {
|
|
169
|
+
persistRuntimeMetadata('view', vid, draft, { metadataClient }).catch((err) => {
|
|
170
|
+
console.error('[ViewConfigPanel] Failed to persist view config:', err);
|
|
171
|
+
});
|
|
258
172
|
}
|
|
259
173
|
else {
|
|
260
|
-
console.warn('[ViewConfigPanel]
|
|
174
|
+
console.warn('[ViewConfigPanel] Cannot persist view config: missing metadataClient or viewId.');
|
|
261
175
|
}
|
|
262
|
-
}, [
|
|
176
|
+
}, [metadataClient]);
|
|
263
177
|
/** Create a new view via the config panel */
|
|
264
178
|
const handleViewCreate = useCallback(async (config) => {
|
|
265
179
|
try {
|
|
266
180
|
let createdId;
|
|
267
|
-
if (
|
|
181
|
+
if (metadataClient) {
|
|
268
182
|
// Prefill sensible defaults so the saved view renders rows
|
|
269
183
|
// immediately even if the user didn't pick columns yet.
|
|
270
184
|
const objectDef = objects?.find?.((o) => o.name === objectName);
|
|
@@ -305,16 +219,14 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
305
219
|
spec.gallery = { ...existing, visibleFields: incomingColumns };
|
|
306
220
|
}
|
|
307
221
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
createdId = created?.id ?? created?._id;
|
|
317
|
-
}
|
|
222
|
+
// ADR-0034: a new view is created as an invisible per-item
|
|
223
|
+
// draft via the metadata seam; an explicit Publish promotes it.
|
|
224
|
+
// UI-layer concerns (default columns, kanban/gallery massaging
|
|
225
|
+
// above, and the auto-activation below) stay here.
|
|
226
|
+
const draftName = String(config?.name ?? config?.id ?? spec?.id ?? '');
|
|
227
|
+
createdId = await createRuntimeMetadata('view', draftName, spec, {
|
|
228
|
+
metadataClient,
|
|
229
|
+
});
|
|
318
230
|
}
|
|
319
231
|
setShowViewConfigPanel(false);
|
|
320
232
|
setViewConfigPanelMode('edit');
|
|
@@ -334,20 +246,32 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
334
246
|
catch (err) {
|
|
335
247
|
console.error('[ViewConfigPanel] Failed to create view:', err);
|
|
336
248
|
}
|
|
337
|
-
}, [dataSource, objectName, objects, navigate, viewId]);
|
|
249
|
+
}, [dataSource, objectName, objects, navigate, viewId, metadataClient]);
|
|
338
250
|
// Record count tracking for footer
|
|
339
251
|
const [recordCount, setRecordCount] = useState(undefined);
|
|
340
252
|
// Admin users automatically get design tools (no toggle needed)
|
|
341
253
|
const { user, activeOrganization } = useAuth();
|
|
342
254
|
const isAdmin = user?.role === 'admin';
|
|
343
255
|
const { can } = usePermissions();
|
|
344
|
-
// Get Object Definition
|
|
256
|
+
// Get Object Definition. The outer ObjectView wrapper already guards the
|
|
257
|
+
// missing-object case, so this always resolves while this component is
|
|
258
|
+
// mounted — every hook below can therefore run unconditionally.
|
|
345
259
|
const objectDef = objects.find((o) => o.name === objectName);
|
|
346
|
-
if (!objectDef) {
|
|
347
|
-
return (_jsx("div", { className: "h-full p-4 flex items-center justify-center", children: _jsxs(Empty, { children: [_jsx("div", { className: "mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-muted", children: _jsx(TableIcon, { className: "h-6 w-6 text-muted-foreground" }) }), _jsx(EmptyTitle, { children: t('console.objectView.objectNotFound') }), _jsxs(EmptyDescription, { children: [t('console.objectView.objectNotFoundDescription', { objectName }), ' ', t('console.objectView.objectNotFoundHint')] })] }) }));
|
|
348
|
-
}
|
|
349
260
|
// Refresh trigger — bumped after view CRUD or external data mutations.
|
|
350
261
|
const [refreshKey, setRefreshKey] = useState(0);
|
|
262
|
+
// Shared console action runtime: confirm/param/result dialogs, the
|
|
263
|
+
// authenticated api/flow/server-action handlers, SPA navigation, and the
|
|
264
|
+
// paused screen-flow runner. The same runtime PageView mounts (#1605).
|
|
265
|
+
// ObjectView additionally feeds its confirm/toast handlers into
|
|
266
|
+
// useObjectActions below, so it consumes the hook directly (rather than the
|
|
267
|
+
// ConsoleActionRuntimeProvider wrapper).
|
|
268
|
+
const actionRuntime = useConsoleActionRuntime({
|
|
269
|
+
dataSource,
|
|
270
|
+
objects,
|
|
271
|
+
objectName: objectDef.name,
|
|
272
|
+
onRefresh: () => setRefreshKey((k) => k + 1),
|
|
273
|
+
});
|
|
274
|
+
const { confirmHandler, toastHandler } = actionRuntime;
|
|
351
275
|
// Resolve which generic CRUD affordances belong in the toolbar for
|
|
352
276
|
// this object's lifecycle bucket (`managedBy`). config tables show
|
|
353
277
|
// New/Edit/Delete but no CSV Import; system / append-only / better-auth
|
|
@@ -364,10 +288,11 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
364
288
|
}, [externalRefreshKey]);
|
|
365
289
|
// Import wizard open/close state — toolbar entry triggers it.
|
|
366
290
|
const [showImport, setShowImport] = useState(false);
|
|
367
|
-
// ─── User-defined views (
|
|
368
|
-
// Saved views created via the ViewConfigPanel ("Add View")
|
|
369
|
-
//
|
|
370
|
-
// the ViewTabBar
|
|
291
|
+
// ─── User-defined views (metadata overlay) ──────────────────────────
|
|
292
|
+
// Saved views created via the ViewConfigPanel ("Add View") live in the
|
|
293
|
+
// metadata overlay (`/meta/view`). We fetch them via `listViews` and merge
|
|
294
|
+
// into `views` so the ViewTabBar renders them alongside metadata-defined
|
|
295
|
+
// listViews.
|
|
371
296
|
const [savedViews, setSavedViews] = useState([]);
|
|
372
297
|
useEffect(() => {
|
|
373
298
|
let cancelled = false;
|
|
@@ -375,10 +300,8 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
375
300
|
setSavedViews([]);
|
|
376
301
|
return;
|
|
377
302
|
}
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
// Falls back to the legacy `find('sys_view', ...)` path only when
|
|
381
|
-
// the adapter doesn't expose `listViews` (e.g. test mocks).
|
|
303
|
+
// Read saved views from the metadata overlay (`/meta/view`) via the
|
|
304
|
+
// adapter's `listViews`. Adapters without it surface no saved views.
|
|
382
305
|
if (typeof dataSource?.listViews === 'function') {
|
|
383
306
|
dataSource.listViews(objectName)
|
|
384
307
|
.then((rows) => {
|
|
@@ -405,41 +328,9 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
405
328
|
});
|
|
406
329
|
return () => { cancelled = true; };
|
|
407
330
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}
|
|
412
|
-
dataSource
|
|
413
|
-
.find('sys_view', {
|
|
414
|
-
$filter: ['object_name', '=', objectName],
|
|
415
|
-
$orderby: [{ field: 'created_at', order: 'asc' }],
|
|
416
|
-
$top: 200,
|
|
417
|
-
})
|
|
418
|
-
.then((res) => {
|
|
419
|
-
if (cancelled)
|
|
420
|
-
return;
|
|
421
|
-
const rows = Array.isArray(res)
|
|
422
|
-
? res
|
|
423
|
-
: Array.isArray(res?.data) ? res.data
|
|
424
|
-
: Array.isArray(res?.records) ? res.records
|
|
425
|
-
: Array.isArray(res?.value) ? res.value
|
|
426
|
-
: [];
|
|
427
|
-
// Defensive client-side filter: only keep rows that look like
|
|
428
|
-
// sys_view records for *this* object. Adapters that don't
|
|
429
|
-
// honour $filter (or test mocks that ignore it) won't pollute
|
|
430
|
-
// the view list with arbitrary records. Match either casing —
|
|
431
|
-
// the storage column is `object_name`, but older mock fixtures
|
|
432
|
-
// and tests may still emit `objectName`.
|
|
433
|
-
const filtered = rows
|
|
434
|
-
.filter(r => r && (r.object_name === objectName || r.objectName === objectName))
|
|
435
|
-
.map(fromSysViewRecord);
|
|
436
|
-
setSavedViews(filtered);
|
|
437
|
-
})
|
|
438
|
-
.catch((err) => {
|
|
439
|
-
console.error('[ObjectView] Failed to load sys_view records:', err);
|
|
440
|
-
if (!cancelled)
|
|
441
|
-
setSavedViews([]);
|
|
442
|
-
});
|
|
331
|
+
// No overlay API available (e.g. a minimal adapter / test mock) → no
|
|
332
|
+
// saved views. The retired `sys_view` table is no longer read.
|
|
333
|
+
setSavedViews([]);
|
|
443
334
|
return () => { cancelled = true; };
|
|
444
335
|
}, [dataSource, objectName, refreshKey]);
|
|
445
336
|
// Persisted per-view config overrides (e.g. density toggle). Saved
|
|
@@ -766,13 +657,10 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
766
657
|
return;
|
|
767
658
|
}
|
|
768
659
|
try {
|
|
769
|
-
//
|
|
660
|
+
// Metadata overlay path — `vid` is the view's `name` field.
|
|
770
661
|
if (typeof dataSource?.updateView === 'function') {
|
|
771
662
|
await dataSource.updateView(objectName, vid, { label: newName });
|
|
772
663
|
}
|
|
773
|
-
else if (dataSource?.update) {
|
|
774
|
-
await dataSource.update('sys_view', vid, { label: newName });
|
|
775
|
-
}
|
|
776
664
|
setRefreshKey(k => k + 1);
|
|
777
665
|
}
|
|
778
666
|
catch (err) {
|
|
@@ -780,34 +668,6 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
780
668
|
toast.error(t('objectViewActions.renameFailed'));
|
|
781
669
|
}
|
|
782
670
|
}, [dataSource, objectName, isSavedView, t]);
|
|
783
|
-
// Promise-based confirm/param dialogs — declared early so destructive
|
|
784
|
-
// handlers (delete, etc.) can `await confirmHandler(...)` for a proper
|
|
785
|
-
// Airtable-style confirmation flow.
|
|
786
|
-
const [confirmState, setConfirmState] = useState({ open: false, message: '' });
|
|
787
|
-
const [paramState, setParamState] = useState({ open: false, params: [] });
|
|
788
|
-
const confirmHandler = useCallback((message, options) => {
|
|
789
|
-
return new Promise((resolve) => {
|
|
790
|
-
setConfirmState({ open: true, message, options, resolve });
|
|
791
|
-
});
|
|
792
|
-
}, []);
|
|
793
|
-
const paramCollectionHandler = useCallback((params, action) => {
|
|
794
|
-
return new Promise((resolve) => {
|
|
795
|
-
// List_item actions stash the row record under params._rowRecord
|
|
796
|
-
// (see ObjectGrid → onRowAction). Pull it out so resolveActionParams
|
|
797
|
-
// can pre-fill `defaultFromRow` params from the row's current values.
|
|
798
|
-
const row = action?.params && !Array.isArray(action.params)
|
|
799
|
-
? action.params._rowRecord
|
|
800
|
-
: undefined;
|
|
801
|
-
const resolved = resolveActionParams(params, {
|
|
802
|
-
objectName: objectName || objectDef?.name || '',
|
|
803
|
-
objects: objects || [],
|
|
804
|
-
fieldLabel,
|
|
805
|
-
fieldOptionLabel,
|
|
806
|
-
row,
|
|
807
|
-
});
|
|
808
|
-
setParamState({ open: true, params: resolved, resolve });
|
|
809
|
-
});
|
|
810
|
-
}, [objectName, objectDef, objects, fieldLabel, fieldOptionLabel]);
|
|
811
671
|
const handleDeleteView = useCallback(async (vid) => {
|
|
812
672
|
if (!dataSource)
|
|
813
673
|
return;
|
|
@@ -829,9 +689,6 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
829
689
|
if (typeof dataSource?.deleteView === 'function') {
|
|
830
690
|
await dataSource.deleteView(objectName, vid);
|
|
831
691
|
}
|
|
832
|
-
else if (dataSource?.delete) {
|
|
833
|
-
await dataSource.delete('sys_view', vid);
|
|
834
|
-
}
|
|
835
692
|
// If we deleted the active view, fall back to the first remaining view.
|
|
836
693
|
if (vid === activeViewId) {
|
|
837
694
|
const fallback = views.find((v) => v.id !== vid);
|
|
@@ -845,58 +702,6 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
845
702
|
toast.error(t('objectViewActions.deleteFailed'));
|
|
846
703
|
}
|
|
847
704
|
}, [dataSource, isSavedView, activeViewId, views, viewId, navigate, t, confirmHandler]);
|
|
848
|
-
const handleDuplicateView = useCallback(async (vid) => {
|
|
849
|
-
if (!dataSource)
|
|
850
|
-
return;
|
|
851
|
-
const source = views.find((v) => v.id === vid);
|
|
852
|
-
if (!source)
|
|
853
|
-
return;
|
|
854
|
-
try {
|
|
855
|
-
// ADR-0005 overlay path — store the full NamedListView spec
|
|
856
|
-
// shape directly under a unique `name`. No legacy sys_view
|
|
857
|
-
// column-flattening required.
|
|
858
|
-
const baseName = String(source.name || vid).toLowerCase()
|
|
859
|
-
.replace(/[^a-z0-9_]+/g, '_').replace(/^_+|_+$/g, '') || 'view';
|
|
860
|
-
const newName = `${baseName}_copy_${Date.now().toString(36)}`;
|
|
861
|
-
// Drop `id` so the overlay row is keyed purely by `name`; copying
|
|
862
|
-
// the source's `id` would collide with the source view in the
|
|
863
|
-
// tab-bar dedup logic and silently shadow it.
|
|
864
|
-
const { id: _omitId, ...rest } = source;
|
|
865
|
-
const spec = {
|
|
866
|
-
...rest,
|
|
867
|
-
name: newName,
|
|
868
|
-
label: `${source.label || vid} (Copy)`,
|
|
869
|
-
isDefault: false,
|
|
870
|
-
isPinned: false,
|
|
871
|
-
data: source.data || { provider: 'object', object: objectName },
|
|
872
|
-
object: source.object || objectName,
|
|
873
|
-
};
|
|
874
|
-
let newId;
|
|
875
|
-
if (typeof dataSource?.createView === 'function') {
|
|
876
|
-
const created = await dataSource.createView(objectName, spec);
|
|
877
|
-
newId = created?.name || newName;
|
|
878
|
-
}
|
|
879
|
-
else if (dataSource?.create) {
|
|
880
|
-
const payload = toSysViewPayload(spec, objectName);
|
|
881
|
-
const created = await dataSource.create('sys_view', payload);
|
|
882
|
-
newId = created?.id ?? created?._id;
|
|
883
|
-
}
|
|
884
|
-
setRefreshKey(k => k + 1);
|
|
885
|
-
// Auto-activate the duplicate (Airtable parity).
|
|
886
|
-
if (newId) {
|
|
887
|
-
if (viewId) {
|
|
888
|
-
navigate(`../${newId}`, { relative: 'path' });
|
|
889
|
-
}
|
|
890
|
-
else {
|
|
891
|
-
navigate(`view/${newId}`);
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
catch (err) {
|
|
896
|
-
console.error('[ViewTabBar] Failed to duplicate view:', err);
|
|
897
|
-
toast.error('Failed to duplicate view');
|
|
898
|
-
}
|
|
899
|
-
}, [dataSource, views, objectName, navigate, viewId]);
|
|
900
705
|
const handlePinView = useCallback(async (vid, pinned) => {
|
|
901
706
|
if (!dataSource)
|
|
902
707
|
return;
|
|
@@ -908,9 +713,6 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
908
713
|
if (typeof dataSource?.updateView === 'function') {
|
|
909
714
|
await dataSource.updateView(objectName, vid, { isPinned: pinned });
|
|
910
715
|
}
|
|
911
|
-
else if (dataSource?.update) {
|
|
912
|
-
await dataSource.update('sys_view', vid, { isPinned: pinned });
|
|
913
|
-
}
|
|
914
716
|
setRefreshKey(k => k + 1);
|
|
915
717
|
}
|
|
916
718
|
catch (err) {
|
|
@@ -922,23 +724,18 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
922
724
|
return;
|
|
923
725
|
if (!isSavedView(vid)) {
|
|
924
726
|
toast.error(t('console.objectView.cannotEditMetaView')
|
|
925
|
-
|| 'System view —
|
|
727
|
+
|| 'System view — it cannot be set as a default.');
|
|
926
728
|
return;
|
|
927
729
|
}
|
|
928
730
|
try {
|
|
929
731
|
// Clear `isDefault` on all other saved views, then set this one.
|
|
930
|
-
|
|
732
|
+
if (typeof dataSource?.updateView !== 'function')
|
|
733
|
+
return;
|
|
734
|
+
const updateView = dataSource.updateView;
|
|
931
735
|
const updates = savedViews
|
|
932
736
|
.filter((sv) => (sv.id || sv._id) !== vid && sv.isDefault)
|
|
933
|
-
.map((sv) => {
|
|
934
|
-
|
|
935
|
-
return hasOverlay
|
|
936
|
-
? dataSource.updateView(objectName, id, { isDefault: false })
|
|
937
|
-
: dataSource.update('sys_view', id, { isDefault: false });
|
|
938
|
-
});
|
|
939
|
-
updates.push(hasOverlay
|
|
940
|
-
? dataSource.updateView(objectName, vid, { isDefault: true })
|
|
941
|
-
: dataSource.update('sys_view', vid, { isDefault: true }));
|
|
737
|
+
.map((sv) => updateView(objectName, sv.id || sv._id, { isDefault: false }));
|
|
738
|
+
updates.push(updateView(objectName, vid, { isDefault: true }));
|
|
942
739
|
await Promise.all(updates);
|
|
943
740
|
setRefreshKey(k => k + 1);
|
|
944
741
|
}
|
|
@@ -959,15 +756,12 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
959
756
|
catch { /* ignore */ }
|
|
960
757
|
// Best-effort: also persist `sortOrder` on each saved view so other
|
|
961
758
|
// sessions / users can pick up the order from the backend.
|
|
962
|
-
if (dataSource) {
|
|
963
|
-
const
|
|
759
|
+
if (typeof dataSource?.updateView === 'function') {
|
|
760
|
+
const updateView = dataSource.updateView;
|
|
964
761
|
const savedIdSet = new Set(savedViews.map((sv) => sv.id || sv._id));
|
|
965
762
|
const updates = orderedIds
|
|
966
763
|
.filter(id => savedIdSet.has(id))
|
|
967
|
-
.map((id, idx) =>
|
|
968
|
-
? dataSource.updateView(objectName, id, { sortOrder: idx })
|
|
969
|
-
: dataSource.update?.('sys_view', id, { sortOrder: idx }))
|
|
970
|
-
.filter(Boolean);
|
|
764
|
+
.map((id, idx) => updateView(objectName, id, { sortOrder: idx }));
|
|
971
765
|
try {
|
|
972
766
|
await Promise.all(updates);
|
|
973
767
|
}
|
|
@@ -980,10 +774,10 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
980
774
|
const handleConfigView = useCallback((vid) => {
|
|
981
775
|
// System (metadata-defined) views are read-only — opening the
|
|
982
776
|
// ViewConfigPanel against one would let the user save changes that
|
|
983
|
-
// never persist.
|
|
777
|
+
// never persist.
|
|
984
778
|
if (!isSavedView(vid)) {
|
|
985
779
|
toast.error(t('console.objectView.cannotEditMetaView')
|
|
986
|
-
|| 'System view —
|
|
780
|
+
|| 'System view — it cannot be edited.');
|
|
987
781
|
return;
|
|
988
782
|
}
|
|
989
783
|
if (vid !== activeViewId)
|
|
@@ -994,16 +788,12 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
994
788
|
const handleAddView = useCallback(() => {
|
|
995
789
|
setShowCreateViewDialog(true);
|
|
996
790
|
}, []);
|
|
997
|
-
//
|
|
791
|
+
// Current user — also used below for `{current_user_id}` filter-token
|
|
792
|
+
// substitution. The schema-action handlers (toast/navigate/api/flow/server)
|
|
793
|
+
// now live in the shared `useConsoleActionRuntime` hook (declared earlier).
|
|
998
794
|
const currentUser = user
|
|
999
795
|
? { id: user.id, name: user.name, avatar: user.image }
|
|
1000
796
|
: FALLBACK_USER;
|
|
1001
|
-
const toastHandler = useCallback((message, options) => {
|
|
1002
|
-
if (options?.type === 'error')
|
|
1003
|
-
toast.error(message);
|
|
1004
|
-
else
|
|
1005
|
-
toast.success(message);
|
|
1006
|
-
}, []);
|
|
1007
797
|
// Action system for toolbar operations — refreshKey moved up (declared earlier).
|
|
1008
798
|
// Wired to confirmHandler/toastHandler so deletes use the Shadcn AlertDialog
|
|
1009
799
|
// and Sonner toast instead of native window.confirm.
|
|
@@ -1014,200 +804,10 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
1014
804
|
onEdit,
|
|
1015
805
|
onRefresh: () => setRefreshKey(k => k + 1),
|
|
1016
806
|
onConfirm: confirmHandler,
|
|
807
|
+
// ToastHandler's `type` is a string-literal union — a subset of the
|
|
808
|
+
// looser `{ type?: string }` useObjectActions declares; cast is sound.
|
|
1017
809
|
onToast: toastHandler,
|
|
1018
810
|
});
|
|
1019
|
-
const navigateHandler = useCallback((url, options) => {
|
|
1020
|
-
if (options?.external || options?.newTab) {
|
|
1021
|
-
window.open(url, '_blank', 'noopener,noreferrer');
|
|
1022
|
-
}
|
|
1023
|
-
else {
|
|
1024
|
-
navigate(url);
|
|
1025
|
-
}
|
|
1026
|
-
}, [navigate]);
|
|
1027
|
-
// Authenticated fetch for direct backend calls (e.g. flow trigger,
|
|
1028
|
-
// schema actions targeting absolute API paths). Declared before
|
|
1029
|
-
// apiHandler so the latter can close over it.
|
|
1030
|
-
const authFetch = useMemo(() => createAuthenticatedFetch(), []);
|
|
1031
|
-
const apiHandler = useCallback(async (action) => {
|
|
1032
|
-
try {
|
|
1033
|
-
const target = action.target || action.name;
|
|
1034
|
-
const params = action.params || {};
|
|
1035
|
-
// Absolute HTTP target (e.g. /api/v1/auth/organization/invite-member,
|
|
1036
|
-
// http://..., https://...) — bypass dataSource and call the API
|
|
1037
|
-
// directly through the authenticated fetch wrapper so:
|
|
1038
|
-
// - Authorization: Bearer <token> is injected
|
|
1039
|
-
// - X-Tenant-ID is injected (multi-tenant routing)
|
|
1040
|
-
// - Same-origin cookies (better-auth.session_token) ride along
|
|
1041
|
-
//
|
|
1042
|
-
// This is the canonical path for schema actions on managed-by
|
|
1043
|
-
// tables (sys_user/invite_user, sys_session/revoke, …) where
|
|
1044
|
-
// generic CRUD does not apply.
|
|
1045
|
-
const targetStr = typeof target === 'string' ? target : '';
|
|
1046
|
-
const isAbsolute = targetStr.startsWith('/') || /^https?:\/\//i.test(targetStr);
|
|
1047
|
-
if (isAbsolute) {
|
|
1048
|
-
const baseUrl = import.meta.env.VITE_SERVER_URL || '';
|
|
1049
|
-
// Row context is stashed on params under `_rowRecord` by the
|
|
1050
|
-
// row-action dispatcher (see ObjectGrid → onRowAction). Pull
|
|
1051
|
-
// it out before assembling the request body.
|
|
1052
|
-
const rawParams = { ...params };
|
|
1053
|
-
const rowRecord = rawParams._rowRecord;
|
|
1054
|
-
delete rawParams._rowRecord;
|
|
1055
|
-
// Interpolate `{field}` tokens in the target URL from the
|
|
1056
|
-
// row record. Lets actions hit data-API endpoints like
|
|
1057
|
-
// `PATCH /api/v1/sys_api_key/{id}` without bespoke wiring.
|
|
1058
|
-
let resolvedTarget = targetStr;
|
|
1059
|
-
if (rowRecord && /\{[a-z_][a-z0-9_]*\}/i.test(resolvedTarget)) {
|
|
1060
|
-
resolvedTarget = resolvedTarget.replace(/\{([a-z_][a-z0-9_]*)\}/gi, (_, k) => {
|
|
1061
|
-
const v = rowRecord[k];
|
|
1062
|
-
return v == null ? '' : encodeURIComponent(String(v));
|
|
1063
|
-
});
|
|
1064
|
-
}
|
|
1065
|
-
const url = resolvedTarget.startsWith('http') ? resolvedTarget : `${baseUrl}${resolvedTarget}`;
|
|
1066
|
-
// Apply bodyShape: 'flat' (default) keeps user params at top
|
|
1067
|
-
// level; { wrap: 'data' } nests them under that key while
|
|
1068
|
-
// `recordIdParam` / `organizationId` stay flat (better-auth
|
|
1069
|
-
// organization/update semantics).
|
|
1070
|
-
const wrap = action.bodyShape && typeof action.bodyShape === 'object' && action.bodyShape.wrap
|
|
1071
|
-
? action.bodyShape.wrap
|
|
1072
|
-
: undefined;
|
|
1073
|
-
const body = wrap
|
|
1074
|
-
? { [wrap]: rawParams }
|
|
1075
|
-
: { ...rawParams };
|
|
1076
|
-
// Inject row id (or chosen row field) for list_item actions.
|
|
1077
|
-
if (rowRecord && action.recordIdParam) {
|
|
1078
|
-
const rowField = action.recordIdField || 'id';
|
|
1079
|
-
const rowValue = rowRecord[rowField];
|
|
1080
|
-
if (rowValue != null)
|
|
1081
|
-
body[action.recordIdParam] = rowValue;
|
|
1082
|
-
}
|
|
1083
|
-
// Auto-inject organizationId only for better-auth org-scoped
|
|
1084
|
-
// endpoints. Data-API endpoints (/api/v1/{object}/{id}) must
|
|
1085
|
-
// NOT receive a stray organizationId field — that would try
|
|
1086
|
-
// to overwrite the row's tenant column.
|
|
1087
|
-
const isAuthOrgEndpoint = /\/api\/v1\/auth\//.test(resolvedTarget);
|
|
1088
|
-
if (isAuthOrgEndpoint && !body.organizationId && activeOrganization?.id) {
|
|
1089
|
-
body.organizationId = activeOrganization.id;
|
|
1090
|
-
}
|
|
1091
|
-
// Merge static body fragment last so constants override any
|
|
1092
|
-
// user-collected values (e.g. resend:true on resend-invite,
|
|
1093
|
-
// revoked:true on revoke-api-key).
|
|
1094
|
-
if (action.bodyExtra && typeof action.bodyExtra === 'object') {
|
|
1095
|
-
Object.assign(body, action.bodyExtra);
|
|
1096
|
-
}
|
|
1097
|
-
const method = (action.method || 'POST').toUpperCase();
|
|
1098
|
-
const init = {
|
|
1099
|
-
method,
|
|
1100
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1101
|
-
credentials: 'include',
|
|
1102
|
-
};
|
|
1103
|
-
// GET/DELETE conventionally have no body; for everything else
|
|
1104
|
-
// we serialize. (We don't expect GET here today, but keep the
|
|
1105
|
-
// guard for safety.)
|
|
1106
|
-
if (method !== 'GET' && method !== 'DELETE') {
|
|
1107
|
-
init.body = JSON.stringify(body);
|
|
1108
|
-
}
|
|
1109
|
-
const res = await authFetch(url, init);
|
|
1110
|
-
if (!res.ok) {
|
|
1111
|
-
let detail = `HTTP ${res.status}`;
|
|
1112
|
-
try {
|
|
1113
|
-
const j = await res.json();
|
|
1114
|
-
detail = j?.error || j?.message || detail;
|
|
1115
|
-
}
|
|
1116
|
-
catch { /* response body not JSON */ }
|
|
1117
|
-
return { success: false, error: detail };
|
|
1118
|
-
}
|
|
1119
|
-
const data = await res.json().catch(() => ({}));
|
|
1120
|
-
if (action.refreshAfter !== false)
|
|
1121
|
-
setRefreshKey(k => k + 1);
|
|
1122
|
-
return { success: true, data, reload: action.refreshAfter !== false };
|
|
1123
|
-
}
|
|
1124
|
-
// Generic list-level API handler: update/execute via dataSource
|
|
1125
|
-
if (typeof dataSource.execute === 'function') {
|
|
1126
|
-
await dataSource.execute(objectDef.name, target, params);
|
|
1127
|
-
}
|
|
1128
|
-
else if (params.recordId && Object.keys(params).length > 1 && typeof dataSource.update === 'function') {
|
|
1129
|
-
await dataSource.update(objectDef.name, params.recordId, params);
|
|
1130
|
-
}
|
|
1131
|
-
const shouldRefresh = action.refreshAfter !== false;
|
|
1132
|
-
if (shouldRefresh) {
|
|
1133
|
-
setRefreshKey(k => k + 1);
|
|
1134
|
-
}
|
|
1135
|
-
return { success: true, reload: shouldRefresh };
|
|
1136
|
-
}
|
|
1137
|
-
catch (error) {
|
|
1138
|
-
return { success: false, error: error.message };
|
|
1139
|
-
}
|
|
1140
|
-
}, [dataSource, objectDef.name, authFetch, activeOrganization]);
|
|
1141
|
-
// Flow action handler — POST to /api/v1/automation/{name}/trigger.
|
|
1142
|
-
// Triggered when an Action with `type: 'flow'` is invoked from list-level
|
|
1143
|
-
// locations (list_toolbar, list_item). For list_item the row's recordId is
|
|
1144
|
-
// expected in `action.params.recordId`.
|
|
1145
|
-
const flowHandler = useCallback(async (action) => {
|
|
1146
|
-
const flowName = action.target || action.name;
|
|
1147
|
-
if (!flowName) {
|
|
1148
|
-
return { success: false, error: 'No flow target provided for flow action' };
|
|
1149
|
-
}
|
|
1150
|
-
try {
|
|
1151
|
-
const baseUrl = import.meta.env.VITE_SERVER_URL || '';
|
|
1152
|
-
const params = action.params || {};
|
|
1153
|
-
const res = await authFetch(`${baseUrl}/api/v1/automation/${encodeURIComponent(flowName)}/trigger`, {
|
|
1154
|
-
method: 'POST',
|
|
1155
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1156
|
-
body: JSON.stringify({
|
|
1157
|
-
recordId: params.recordId,
|
|
1158
|
-
objectName: objectDef.name,
|
|
1159
|
-
params,
|
|
1160
|
-
}),
|
|
1161
|
-
});
|
|
1162
|
-
const json = await res.json().catch(() => null);
|
|
1163
|
-
if (!res.ok || (json && json.success === false)) {
|
|
1164
|
-
const errMsg = json?.error || `Flow "${flowName}" failed (HTTP ${res.status})`;
|
|
1165
|
-
return { success: false, error: errMsg };
|
|
1166
|
-
}
|
|
1167
|
-
const shouldRefresh = action.refreshAfter !== false;
|
|
1168
|
-
if (shouldRefresh) {
|
|
1169
|
-
setRefreshKey(k => k + 1);
|
|
1170
|
-
}
|
|
1171
|
-
return { success: true, data: json?.data, reload: shouldRefresh };
|
|
1172
|
-
}
|
|
1173
|
-
catch (error) {
|
|
1174
|
-
return { success: false, error: error.message };
|
|
1175
|
-
}
|
|
1176
|
-
}, [authFetch, objectDef.name]);
|
|
1177
|
-
// Server-side action handler — POST to /api/v1/actions/{object}/{action}.
|
|
1178
|
-
// For list-toolbar/list-item `script` and `modal` actions whose `target`
|
|
1179
|
-
// matches a server-registered handler. selectedIds (from action.params)
|
|
1180
|
-
// is forwarded so bulk handlers like massUpdateStage / addToCampaign work.
|
|
1181
|
-
const serverActionHandler = useCallback(async (action) => {
|
|
1182
|
-
const targetName = action.target || action.name;
|
|
1183
|
-
if (!targetName) {
|
|
1184
|
-
return { success: false, error: 'No action target provided' };
|
|
1185
|
-
}
|
|
1186
|
-
const params = (action.params && !Array.isArray(action.params))
|
|
1187
|
-
? action.params
|
|
1188
|
-
: {};
|
|
1189
|
-
try {
|
|
1190
|
-
const baseUrl = import.meta.env.VITE_SERVER_URL || '';
|
|
1191
|
-
const obj = action.objectName || objectDef.name || 'global';
|
|
1192
|
-
const res = await authFetch(`${baseUrl}/api/v1/actions/${encodeURIComponent(obj)}/${encodeURIComponent(targetName)}`, {
|
|
1193
|
-
method: 'POST',
|
|
1194
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1195
|
-
body: JSON.stringify({ recordId: params.recordId, params }),
|
|
1196
|
-
});
|
|
1197
|
-
const json = await res.json().catch(() => null);
|
|
1198
|
-
if (!res.ok || (json && json.success === false)) {
|
|
1199
|
-
const errMsg = json?.error || `Action "${targetName}" failed (HTTP ${res.status})`;
|
|
1200
|
-
return { success: false, error: errMsg };
|
|
1201
|
-
}
|
|
1202
|
-
const shouldRefresh = action.refreshAfter !== false;
|
|
1203
|
-
if (shouldRefresh)
|
|
1204
|
-
setRefreshKey(k => k + 1);
|
|
1205
|
-
return { success: true, data: json?.data, reload: shouldRefresh };
|
|
1206
|
-
}
|
|
1207
|
-
catch (error) {
|
|
1208
|
-
return { success: false, error: error.message };
|
|
1209
|
-
}
|
|
1210
|
-
}, [authFetch, objectDef.name]);
|
|
1211
811
|
// Real-time: auto-refresh when server reports data changes
|
|
1212
812
|
const { lastMessage: realtimeMessage } = useRealtimeSubscription({
|
|
1213
813
|
channel: `object:${objectDef.name}`,
|
|
@@ -1261,17 +861,33 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
1261
861
|
* which matches the shape consumed by the list view's data fetcher when
|
|
1262
862
|
* merging base filters.
|
|
1263
863
|
*/
|
|
864
|
+
// Dep on the serialized `filter[...]` entries only — `uf_*` user-filter
|
|
865
|
+
// params also live in the URL and must not invalidate this memo (a new
|
|
866
|
+
// array identity here rebuilds the whole list schema and refetches).
|
|
867
|
+
const filterParamsKey = Array.from(searchParams.entries())
|
|
868
|
+
.filter(([k]) => k.startsWith('filter['))
|
|
869
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
870
|
+
.join('&');
|
|
1264
871
|
const urlFilters = useMemo(() => {
|
|
1265
872
|
const out = [];
|
|
1266
|
-
|
|
873
|
+
new URLSearchParams(filterParamsKey).forEach((value, key) => {
|
|
1267
874
|
const m = /^filter\[(.+)\]$/.exec(key);
|
|
1268
875
|
if (m && m[1] && value !== '') {
|
|
1269
876
|
out.push([m[1], '=', value]);
|
|
1270
877
|
}
|
|
1271
878
|
});
|
|
1272
879
|
return out;
|
|
1273
|
-
|
|
1274
|
-
|
|
880
|
+
}, [filterParamsKey]);
|
|
881
|
+
/**
|
|
882
|
+
* End-user filter selections restored from `uf_*` URL params (ADR-0047
|
|
883
|
+
* persistence). Captured once per ObjectView mount — UserFilters only
|
|
884
|
+
* reads them at its own mount, and later URL writes must not churn the
|
|
885
|
+
* schema memo.
|
|
886
|
+
*/
|
|
887
|
+
const [initialUfSelections] = useState(() => parseUserFilterParams(new URLSearchParams(window.location.search)));
|
|
888
|
+
const handleUserFilterSelectionsChange = useCallback((selections) => {
|
|
889
|
+
setSearchParams(prev => applyUserFilterParams(prev, selections), { replace: true });
|
|
890
|
+
}, [setSearchParams]);
|
|
1275
891
|
// Memoize onNavigate to prevent stale closure in useNavigationOverlay's handleClick
|
|
1276
892
|
const handleNavOverlayNavigate = useCallback((recordId, action) => {
|
|
1277
893
|
if (action === 'new_window') {
|
|
@@ -1383,16 +999,48 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
1383
999
|
}
|
|
1384
1000
|
if (viewDef.type === 'chart') {
|
|
1385
1001
|
const chartConfig = viewDef.chart || {};
|
|
1002
|
+
// ADR-0021 (#1890): dataset-bound chart — the single author-facing
|
|
1003
|
+
// shape. Selects dimensions/measures BY NAME and runs through the
|
|
1004
|
+
// governed queryDataset path (numbers consistent across surfaces).
|
|
1005
|
+
if (chartConfig.dataset) {
|
|
1006
|
+
const dims = Array.isArray(chartConfig.dimensions) ? chartConfig.dimensions : [];
|
|
1007
|
+
const vals = Array.isArray(chartConfig.values) ? chartConfig.values : [];
|
|
1008
|
+
return (_jsx(Suspense, { fallback: _jsx("div", { className: "p-4 text-sm text-muted-foreground", children: "Loading chart\u2026" }), children: _jsx(ObjectChart, { dataSource: ds, schema: {
|
|
1009
|
+
type: 'object-chart',
|
|
1010
|
+
dataset: chartConfig.dataset,
|
|
1011
|
+
dimensions: dims,
|
|
1012
|
+
values: vals,
|
|
1013
|
+
chartType: chartConfig.chartType || 'bar',
|
|
1014
|
+
xAxisKey: dims[0],
|
|
1015
|
+
series: vals.map((v) => ({ dataKey: v, label: v })),
|
|
1016
|
+
config: chartConfig.config,
|
|
1017
|
+
className: 'h-[400px] w-full',
|
|
1018
|
+
} }) }, key));
|
|
1019
|
+
}
|
|
1020
|
+
// ObjectChart consumes a structured `aggregate` ({ field, function,
|
|
1021
|
+
// groupBy }) + `xAxisKey` + `series`, NOT the flat spec-level
|
|
1022
|
+
// `xAxisField`/`yAxisFields`/`aggregation` keys. Translate here so the
|
|
1023
|
+
// chart actually runs its aggregate query (otherwise it renders empty).
|
|
1024
|
+
const categoryField = chartConfig.xAxisField || 'name';
|
|
1025
|
+
const valueField = (Array.isArray(chartConfig.yAxisFields) && chartConfig.yAxisFields[0]) || 'value';
|
|
1026
|
+
const aggFn = chartConfig.aggregation || 'count';
|
|
1027
|
+
const series = chartConfig.series && chartConfig.series.length > 0
|
|
1028
|
+
? chartConfig.series
|
|
1029
|
+
: [{ dataKey: valueField, label: valueField }];
|
|
1386
1030
|
return (_jsx(Suspense, { fallback: _jsx("div", { className: "p-4 text-sm text-muted-foreground", children: "Loading chart\u2026" }), children: _jsx(ObjectChart, { dataSource: ds, schema: {
|
|
1387
1031
|
type: 'object-chart',
|
|
1388
1032
|
objectName: objectDef.name,
|
|
1389
|
-
chartType: chartConfig.chartType,
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1033
|
+
chartType: chartConfig.chartType || 'bar',
|
|
1034
|
+
aggregate: {
|
|
1035
|
+
field: valueField,
|
|
1036
|
+
function: aggFn,
|
|
1037
|
+
groupBy: categoryField,
|
|
1038
|
+
},
|
|
1039
|
+
xAxisKey: categoryField,
|
|
1040
|
+
series,
|
|
1394
1041
|
config: chartConfig.config,
|
|
1395
1042
|
filter: chartConfig.filter,
|
|
1043
|
+
className: 'h-[400px] w-full',
|
|
1396
1044
|
} }) }, key));
|
|
1397
1045
|
}
|
|
1398
1046
|
const fullSchema = {
|
|
@@ -1431,9 +1079,20 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
1431
1079
|
persistViewPatch(viewDef.id, viewDef, { columnState: state });
|
|
1432
1080
|
},
|
|
1433
1081
|
inlineEdit: viewDef.inlineEdit ?? viewDef.editRecordsInline ?? listSchema.inlineEdit,
|
|
1434
|
-
appearance
|
|
1435
|
-
|
|
1436
|
-
|
|
1082
|
+
// ADR-0047 — spec `appearance` (incl. allowedVisualizations, the
|
|
1083
|
+
// runtime visualization whitelist) flows from the view metadata;
|
|
1084
|
+
// the legacy bare `showDescription` flag is folded in on top.
|
|
1085
|
+
appearance: viewDef.appearance
|
|
1086
|
+
? (viewDef.showDescription != null
|
|
1087
|
+
? { ...viewDef.appearance, showDescription: viewDef.showDescription }
|
|
1088
|
+
: viewDef.appearance)
|
|
1089
|
+
: (viewDef.showDescription != null
|
|
1090
|
+
? { showDescription: viewDef.showDescription }
|
|
1091
|
+
: listSchema.appearance),
|
|
1092
|
+
// Offer the visualization switcher only when the author
|
|
1093
|
+
// whitelisted more than one type; ListView intersects the
|
|
1094
|
+
// whitelist with capability-resolvable types.
|
|
1095
|
+
showViewSwitcher: ((viewDef.appearance ?? listSchema.appearance)?.allowedVisualizations?.length ?? 0) > 1,
|
|
1437
1096
|
// Propagate toolbar/display flags for all view types
|
|
1438
1097
|
showSearch: viewDef.showSearch ?? listSchema.showSearch,
|
|
1439
1098
|
showSort: viewDef.showSort ?? listSchema.showSort,
|
|
@@ -1473,23 +1132,42 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
1473
1132
|
* remains for back-compat where the action lives elsewhere.
|
|
1474
1133
|
*/
|
|
1475
1134
|
rowActionDefs: (Array.isArray(objectDef?.actions)
|
|
1476
|
-
? objectDef.actions
|
|
1135
|
+
? objectDef.actions
|
|
1136
|
+
.filter((a) => Array.isArray(a?.locations) && a.locations.includes('list_item'))
|
|
1137
|
+
// Localize label / confirm / success the same way the
|
|
1138
|
+
// record_header and list_toolbar paths do — the row kebab
|
|
1139
|
+
// previously rendered raw English `a.label`. The `visible`
|
|
1140
|
+
// CEL is forwarded untouched (spread) and evaluated per-row
|
|
1141
|
+
// at render time inside RowActionMenu.
|
|
1142
|
+
.map((a) => ({
|
|
1143
|
+
...a,
|
|
1144
|
+
label: actionLabel(objectDef.name, a.name, a.label || a.name),
|
|
1145
|
+
...(a.confirmText !== undefined && {
|
|
1146
|
+
confirmText: actionConfirm(objectDef.name, a.name, a.confirmText),
|
|
1147
|
+
}),
|
|
1148
|
+
...(a.successMessage !== undefined && {
|
|
1149
|
+
successMessage: actionSuccess(objectDef.name, a.name, a.successMessage),
|
|
1150
|
+
}),
|
|
1151
|
+
}))
|
|
1477
1152
|
: []),
|
|
1478
1153
|
bulkActions: viewDef.bulkActions ?? listSchema.bulkActions,
|
|
1479
1154
|
bulkActionDefs: viewDef.bulkActionDefs ?? listSchema.bulkActionDefs,
|
|
1480
1155
|
sharing: viewDef.sharing ?? listSchema.sharing,
|
|
1481
1156
|
addRecord: viewDef.addRecord ?? listSchema.addRecord,
|
|
1482
1157
|
conditionalFormatting: viewDef.conditionalFormatting ?? listSchema.conditionalFormatting,
|
|
1483
|
-
|
|
1484
|
-
|
|
1158
|
+
// ADR-0053: this is the object default list = "views" mode; the
|
|
1159
|
+
// ViewTabBar above is the only nav control. The in-list Airtable-
|
|
1160
|
+
// style filter rows (quickFilters / userFilters / tabs) belong to
|
|
1161
|
+
// page "filters" mode (InterfaceListPage), so suppress them here.
|
|
1162
|
+
quickFilters: undefined,
|
|
1163
|
+
userFilters: undefined,
|
|
1485
1164
|
showRecordCount: viewDef.showRecordCount ?? listSchema.showRecordCount,
|
|
1486
1165
|
allowPrinting: viewDef.allowPrinting ?? listSchema.allowPrinting,
|
|
1487
1166
|
virtualScroll: viewDef.virtualScroll ?? listSchema.virtualScroll,
|
|
1488
1167
|
emptyState: viewEmptyState(objectDef.name, viewDef.name || viewDef.id || '', viewDef.emptyState
|
|
1489
1168
|
?? listSchema.emptyState
|
|
1490
|
-
?? resolveManagedByEmptyState(objectDef?.managedBy)),
|
|
1169
|
+
?? resolveManagedByEmptyState(objectDef?.managedBy, t)),
|
|
1491
1170
|
aria: viewDef.aria ?? listSchema.aria,
|
|
1492
|
-
tabs: listSchema.tabs,
|
|
1493
1171
|
// Propagate filter/sort as default filters/sort for data flow
|
|
1494
1172
|
...((() => {
|
|
1495
1173
|
const combined = [
|
|
@@ -1540,12 +1218,26 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
1540
1218
|
subtitleField: viewDef.gallery?.subtitleField,
|
|
1541
1219
|
},
|
|
1542
1220
|
gantt: {
|
|
1221
|
+
// Spread the full view-defined gantt config first so the
|
|
1222
|
+
// renderer's extended fields (parentField/typeField for the
|
|
1223
|
+
// summary→step hierarchy, baseline*, groupByField,
|
|
1224
|
+
// resourceView/assignee*, tooltipFields, quickFilters, …)
|
|
1225
|
+
// survive; then layer the three required defaults last so an
|
|
1226
|
+
// omitted source value still falls back. (Mirrors the gallery
|
|
1227
|
+
// branch above — a bare whitelist here was dropping every
|
|
1228
|
+
// field past colorField and flattening the chart.)
|
|
1229
|
+
...(viewDef.gantt || {}),
|
|
1543
1230
|
startDateField: viewDef.gantt?.startDateField || 'start_date',
|
|
1544
1231
|
endDateField: viewDef.gantt?.endDateField || 'end_date',
|
|
1545
1232
|
titleField: viewDef.gantt?.titleField || 'name',
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1233
|
+
},
|
|
1234
|
+
tree: {
|
|
1235
|
+
// Self-referencing tree-grid config (plugin-tree). Spread the
|
|
1236
|
+
// full view-defined tree first so parentField/fields/
|
|
1237
|
+
// defaultExpandedDepth survive; labelField falls back to the
|
|
1238
|
+
// object title. parentField auto-detects when omitted.
|
|
1239
|
+
...(viewDef.tree || {}),
|
|
1240
|
+
labelField: viewDef.tree?.labelField || viewDef.tree?.titleField || objectDef.titleField || 'name',
|
|
1549
1241
|
},
|
|
1550
1242
|
chart: {
|
|
1551
1243
|
chartType: viewDef.chart?.chartType,
|
|
@@ -1589,8 +1281,8 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
1589
1281
|
persistViewPatch(viewDef.id, viewDef, { hiddenFields: hidden });
|
|
1590
1282
|
}, onColumnStateChange: (state) => {
|
|
1591
1283
|
persistViewPatch(viewDef.id, viewDef, { columnState: state });
|
|
1592
|
-
}, dataSource: ds }, key));
|
|
1593
|
-
}, [activeView, objectDef, objectName, refreshKey, navOverlay, actions, persistViewPatch, urlFilters]);
|
|
1284
|
+
}, userFilterSelections: initialUfSelections, onUserFilterSelectionsChange: handleUserFilterSelectionsChange, dataSource: ds }, key));
|
|
1285
|
+
}, [activeView, objectDef, objectName, refreshKey, navOverlay, actions, persistViewPatch, urlFilters, initialUfSelections, handleUserFilterSelectionsChange]);
|
|
1594
1286
|
// Memoize the merged views array so PluginObjectView doesn't get a new
|
|
1595
1287
|
// reference on every render (which would trigger unnecessary data refetches).
|
|
1596
1288
|
const mergedViews = useMemo(() => views.map((v) => v.id === activeViewId && viewDraft && viewDraft.id === v.id
|
|
@@ -1610,7 +1302,6 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
1610
1302
|
viewActions: isAdmin ? [
|
|
1611
1303
|
{ type: 'settings' },
|
|
1612
1304
|
{ type: 'share' },
|
|
1613
|
-
{ type: 'duplicate' },
|
|
1614
1305
|
{ type: 'delete' },
|
|
1615
1306
|
] : [],
|
|
1616
1307
|
onNavigate: (recordId, mode) => {
|
|
@@ -1633,15 +1324,7 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
1633
1324
|
}
|
|
1634
1325
|
},
|
|
1635
1326
|
}), [objectDef, onEdit, activeView?.showSearch, activeView?.showFilters, activeView?.showSort, activeView?.name, activeView?.label, navigate, viewId, isAdmin, location.pathname, location.search, viewLabel, objectLabel]);
|
|
1636
|
-
return (_jsxs(ActionProvider, {
|
|
1637
|
-
objectName: objectDef.name,
|
|
1638
|
-
user: currentUser,
|
|
1639
|
-
// Expose active org so `type: 'url'` actions can template
|
|
1640
|
-
// `/organizations/${activeOrganization.slug}/...` etc.
|
|
1641
|
-
activeOrganization: activeOrganization
|
|
1642
|
-
? { id: activeOrganization.id, slug: activeOrganization.slug, name: activeOrganization.name }
|
|
1643
|
-
: null,
|
|
1644
|
-
}, onConfirm: confirmHandler, onToast: toastHandler, onNavigate: navigateHandler, onParamCollection: paramCollectionHandler, handlers: { api: apiHandler, flow: flowHandler, script: serverActionHandler, modal: serverActionHandler }, children: [_jsxs("div", { className: "h-full flex flex-col bg-background min-w-0 overflow-hidden", children: [_jsx("div", { className: "hidden sm:block", children: _jsx(PageHeader, { title: _jsxs("span", { className: "inline-flex items-center gap-2", children: [_jsx("span", { className: "truncate", children: objectLabel(objectDef) }), _jsx(ManagedByBadge, { managedBy: objectDef?.managedBy })] }), description: objectDef.description ? objectDesc(objectDef) : undefined, icon: (() => { const I = getIcon(objectDef?.icon); return _jsx(I, { className: "h-4 w-4" }); })(), actions: _jsxs(_Fragment, { children: [objectName && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => toggleFavorite({
|
|
1327
|
+
return (_jsxs(ActionProvider, { ...actionRuntime.actionProviderProps, children: [_jsxs("div", { className: "h-full flex flex-col bg-background min-w-0 overflow-hidden", children: [_jsx("div", { className: "hidden sm:block", children: _jsx(PageHeader, { title: _jsxs("span", { className: "inline-flex items-center gap-2", children: [_jsx("span", { className: "truncate", children: objectLabel(objectDef) }), _jsx(ManagedByBadge, { managedBy: objectDef?.managedBy })] }), description: objectDef.description ? objectDesc(objectDef) : undefined, icon: (() => { const I = getIcon(objectDef?.icon); return _jsx(I, { className: "h-4 w-4" }); })(), actions: _jsxs(_Fragment, { children: [objectName && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => toggleFavorite({
|
|
1645
1328
|
id: `object:${objectName}`,
|
|
1646
1329
|
label: objectLabel(objectDef),
|
|
1647
1330
|
href: `/apps/${appName}/${objectName}`,
|
|
@@ -1704,7 +1387,7 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
1704
1387
|
readonly: isSystem,
|
|
1705
1388
|
readonlyReason: isSystem
|
|
1706
1389
|
? (t('console.objectView.systemViewReadonly')
|
|
1707
|
-
|| 'System view defined in code —
|
|
1390
|
+
|| 'System view defined in code — read-only.')
|
|
1708
1391
|
: undefined,
|
|
1709
1392
|
};
|
|
1710
1393
|
});
|
|
@@ -1713,27 +1396,21 @@ export function ObjectView({ dataSource, objects, onEdit, externalRefreshKey })
|
|
|
1713
1396
|
showAddButton: isAdmin,
|
|
1714
1397
|
showPinnedSection: true,
|
|
1715
1398
|
showVisibilityGroups: true,
|
|
1716
|
-
}, onAddView: isAdmin ? handleAddView : undefined, onRenameView: isAdmin ? handleRenameView : undefined, onDeleteView: isAdmin ? handleDeleteView : undefined,
|
|
1399
|
+
}, onAddView: isAdmin ? handleAddView : undefined, onRenameView: isAdmin ? handleRenameView : undefined, onDeleteView: isAdmin ? handleDeleteView : undefined, onPinView: isAdmin ? handlePinView : undefined, onSetDefaultView: isAdmin ? handleSetDefaultView : undefined, onConfigView: isAdmin ? handleConfigView : undefined, onManageViews: isAdmin ? () => setManageViewsOpen(true) : undefined }), isAdmin && (_jsx(ManageViewsDialog, { open: manageViewsOpen, onOpenChange: setManageViewsOpen, views: viewTabItems, activeViewId: activeViewId, viewTypeIcons: VIEW_TYPE_ICONS, onRename: handleRenameView, onDelete: handleDeleteView, onSetDefault: handleSetDefaultView, onSetPinned: handlePinView, onReorder: handleReorderViews, onAddView: handleAddView, onConfigView: handleConfigView }))] }) }));
|
|
1717
1400
|
})(), _jsxs("div", { className: "flex-1 overflow-hidden relative flex flex-row", children: [navOverlay.mode === 'split' && navOverlay.isOpen ? (_jsx(NavigationOverlay, { ...navOverlay, setIsOpen: (open) => { if (!open)
|
|
1718
1401
|
handleDrawerClose(); }, title: objectLabel(objectDef), onExpand: handleExpandDrawer, expandLabel: t('console.objectView.expandToPage', { defaultValue: 'Open as full page' }), storageKey: `drawer-width:${objectDef.name}`, mainContent: _jsxs("div", { className: "flex-1 min-w-0 relative h-full flex flex-col", children: [_jsx("div", { className: "flex-1 relative overflow-hidden", children: _jsx("div", { className: "h-full overflow-auto", children: _jsx(PluginObjectView, { schema: objectViewSchema, dataSource: dataSource, views: mergedViews, activeViewId: activeViewId, onViewChange: handleViewChange, onEdit: (record) => onEdit?.(record), onRowClick: (record, event) => {
|
|
1719
1402
|
handleRowClick(record, event);
|
|
1720
|
-
}, renderListView: renderListView, onCreateView: handleCreateView, onViewAction: handleViewAction }) }) }), typeof recordCount === 'number' && (_jsx("div", { "data-testid": "record-count-footer", className: "border-t px-3 sm:px-4 py-1.5 text-xs text-muted-foreground bg-muted/5 shrink-0", children: t('console.objectView.recordCount', { count: recordCount }) }))] }), children: (record) => {
|
|
1403
|
+
}, renderListView: renderListView, onCreateView: handleCreateView, hideNamedViewTabs: true, onViewAction: handleViewAction }) }) }), typeof recordCount === 'number' && (_jsx("div", { "data-testid": "record-count-footer", className: "border-t px-3 sm:px-4 py-1.5 text-xs text-muted-foreground bg-muted/5 shrink-0", children: t('console.objectView.recordCount', { count: recordCount }) }))] }), children: (record) => {
|
|
1721
1404
|
const recordId = (record.id || record._id);
|
|
1722
1405
|
return (_jsx(RecordDetailView, { dataSource: dataSource, objects: objects, onEdit: onEdit, objectNameOverride: objectDef.name, recordIdOverride: recordId, embedded: true }));
|
|
1723
1406
|
} })) : (_jsx("div", { className: "flex-1 min-w-0 relative h-full flex flex-col", children: _jsx("div", { className: "flex-1 relative overflow-hidden", children: _jsx("div", { className: "h-full overflow-auto", children: _jsx(PluginObjectView, { schema: objectViewSchema, dataSource: dataSource, views: mergedViews, activeViewId: activeViewId, onViewChange: handleViewChange, onEdit: (record) => onEdit?.(record), onRowClick: (record, event) => {
|
|
1724
1407
|
handleRowClick(record, event);
|
|
1725
|
-
}, renderListView: renderListView, onCreateView: handleCreateView, onViewAction: handleViewAction }) }) }) })), _jsx(MetadataPanel, { open: showDebug && isAdmin, sections: [
|
|
1408
|
+
}, renderListView: renderListView, onCreateView: handleCreateView, hideNamedViewTabs: true, onViewAction: handleViewAction }) }) }) })), _jsx(MetadataPanel, { open: showDebug && isAdmin, sections: [
|
|
1726
1409
|
{ title: 'View Configuration', data: activeView },
|
|
1727
1410
|
{ title: 'Object Definition', data: objectDef },
|
|
1728
|
-
] }), _jsx("div", { "data-testid": "view-config-panel-wrapper", className: `transition-[max-width,opacity] duration-300 ease-in-out overflow-hidden ${showViewConfigPanel && isAdmin ? 'max-w-[280px] opacity-100' : 'max-w-0 opacity-0'}`, children: _jsx(ViewConfigPanel, { open: showViewConfigPanel && isAdmin, onClose: () => { setShowViewConfigPanel(false); setViewConfigPanelMode('edit'); }, mode: viewConfigPanelMode, activeView: activeView, objectDef: objectDef, recordCount: recordCount, onSave: handleViewConfigSave, onViewUpdate: handleViewUpdate, onCreate: handleViewCreate }) }), _jsx(CreateViewDialog, { open: showCreateViewDialog && isAdmin, onOpenChange: setShowCreateViewDialog, existingLabels: views.map((v) => v.label).filter(Boolean), objectDef: objectDef, onCreate: (cfg) => handleViewCreate(cfg) })] }), navOverlay.mode !== 'split' && (_jsx(NavigationOverlay, { ...navOverlay, setIsOpen: (open) => { if (!open)
|
|
1411
|
+
] }), _jsx("div", { "data-testid": "view-config-panel-wrapper", className: `transition-[max-width,opacity] duration-300 ease-in-out overflow-hidden ${showViewConfigPanel && isAdmin ? 'max-w-[280px] opacity-100' : 'max-w-0 opacity-0'}`, children: _jsx(ViewConfigPanel, { open: showViewConfigPanel && isAdmin, onClose: () => { setShowViewConfigPanel(false); setViewConfigPanelMode('edit'); }, mode: viewConfigPanelMode, activeView: activeView, objectDef: objectDef, recordCount: recordCount, onSave: handleViewConfigSave, onViewUpdate: handleViewUpdate, onCreate: handleViewCreate, metadataClient: metadataClient, onAfterChange: () => setRefreshKey(k => k + 1) }) }), _jsx(CreateViewDialog, { open: showCreateViewDialog && isAdmin, onOpenChange: setShowCreateViewDialog, existingLabels: views.map((v) => v.label).filter(Boolean), objectDef: objectDef, onCreate: (cfg) => handleViewCreate(cfg) })] }), navOverlay.mode !== 'split' && (_jsx(NavigationOverlay, { ...navOverlay, setIsOpen: (open) => { if (!open)
|
|
1729
1412
|
handleDrawerClose(); }, title: objectLabel(objectDef), onExpand: handleExpandDrawer, expandLabel: t('console.objectView.expandToPage', { defaultValue: 'Open as full page' }), storageKey: `drawer-width:${objectDef.name}`, children: (record) => {
|
|
1730
1413
|
const recordId = (record.id || record._id);
|
|
1731
1414
|
return (_jsx(RecordDetailView, { dataSource: dataSource, objects: objects, onEdit: onEdit, objectNameOverride: objectDef.name, recordIdOverride: recordId, embedded: true }));
|
|
1732
|
-
} }))] }),
|
|
1733
|
-
if (!open)
|
|
1734
|
-
setConfirmState({ open: false, message: '' });
|
|
1735
|
-
} }), _jsx(ActionParamDialog, { state: paramState, onOpenChange: (open) => {
|
|
1736
|
-
if (!open)
|
|
1737
|
-
setParamState({ open: false, params: [] });
|
|
1738
|
-
} })] }));
|
|
1415
|
+
} }))] }), actionRuntime.dialogs] }));
|
|
1739
1416
|
}
|