@antelopejs/dms 0.9.7 → 0.10.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 +114 -0
- package/README.md +1 -1
- package/data/module-store/catalog.json +2 -2
- package/dist/attachments/table-view.js +2 -2
- package/dist/attachments/table-view.js.map +1 -1
- package/dist/automation/actions.d.ts +2 -0
- package/dist/automation/actions.js +116 -71
- package/dist/automation/actions.js.map +1 -1
- package/dist/automation/i18n.d.ts +4 -0
- package/dist/automation/i18n.js +19 -0
- package/dist/automation/i18n.js.map +1 -0
- package/dist/automation/send-notification.d.ts +21 -0
- package/dist/automation/send-notification.js +163 -0
- package/dist/automation/send-notification.js.map +1 -0
- package/dist/automation/triggers.js +19 -25
- package/dist/automation/triggers.js.map +1 -1
- package/dist/client-origin-cors.d.ts +1 -0
- package/dist/client-origin-cors.js +19 -0
- package/dist/client-origin-cors.js.map +1 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +5 -3
- package/dist/config.js.map +1 -1
- package/dist/frontend-module.d.ts +4 -0
- package/dist/frontend-module.js +37 -0
- package/dist/frontend-module.js.map +1 -0
- package/dist/implementations/dms/client-ip.js +1 -1
- package/dist/implementations/dms/client-ip.js.map +1 -1
- package/dist/implementations/dms/dev-reload.d.ts +5 -0
- package/dist/implementations/dms/dev-reload.js +23 -3
- package/dist/implementations/dms/dev-reload.js.map +1 -1
- package/dist/implementations/dms/frontend-modules.d.ts +2 -4
- package/dist/implementations/dms/frontend-modules.js +22 -89
- package/dist/implementations/dms/frontend-modules.js.map +1 -1
- package/dist/implementations/dms/frontend-ready.d.ts +12 -2
- package/dist/implementations/dms/frontend-ready.js +34 -4
- package/dist/implementations/dms/frontend-ready.js.map +1 -1
- package/dist/implementations/dms/layout-banners.d.ts +2 -1
- package/dist/implementations/dms/layout-banners.js +9 -0
- package/dist/implementations/dms/layout-banners.js.map +1 -1
- package/dist/implementations/dms/manifest.d.ts +1 -2
- package/dist/implementations/dms/manifest.js +2 -0
- package/dist/implementations/dms/manifest.js.map +1 -1
- package/dist/implementations/dms/page.d.ts +4 -3
- package/dist/implementations/dms/page.js +49 -18
- package/dist/implementations/dms/page.js.map +1 -1
- package/dist/implementations/dms/permissions-resolver.d.ts +2 -2
- package/dist/implementations/dms/permissions-resolver.js +2 -2
- package/dist/implementations/dms/permissions-resolver.js.map +1 -1
- package/dist/implementations/dms/realtime.js +1 -0
- package/dist/implementations/dms/realtime.js.map +1 -1
- package/dist/implementations/dms-base/bulk-selection.d.ts +1 -1
- package/dist/implementations/dms-base/bulk-selection.js +23 -1
- package/dist/implementations/dms-base/bulk-selection.js.map +1 -1
- package/dist/implementations/dms-base/filter-tokens.js +5 -3
- package/dist/implementations/dms-base/filter-tokens.js.map +1 -1
- package/dist/implementations/dms-base/search-route.d.ts +1 -0
- package/dist/implementations/dms-base/search-route.js +35 -13
- package/dist/implementations/dms-base/search-route.js.map +1 -1
- package/dist/implementations/dms-base/table-view.js +11 -6
- package/dist/implementations/dms-base/table-view.js.map +1 -1
- package/dist/implementations/dms-notifications/index.d.ts +2 -2
- package/dist/implementations/dms-notifications/index.js +5 -5
- package/dist/implementations/dms-notifications/index.js.map +1 -1
- package/dist/index.js +28 -45
- package/dist/index.js.map +1 -1
- package/dist/pages/modules/index.d.ts +1 -0
- package/dist/pages/modules/index.js +1 -0
- package/dist/pages/modules/index.js.map +1 -1
- package/dist/pages/modules/modules-store.d.ts +9 -0
- package/dist/pages/modules/modules-store.js +30 -0
- package/dist/pages/modules/modules-store.js.map +1 -0
- package/dist/pages/settings/users/account-credentials.js +2 -2
- package/dist/pages/settings/users/account-credentials.js.map +1 -1
- package/dist/pages/settings/users/account-data-store.js +25 -8
- package/dist/pages/settings/users/account-data-store.js.map +1 -1
- package/dist/pages/settings/users/invite-extension-routes.js +9 -3
- package/dist/pages/settings/users/invite-extension-routes.js.map +1 -1
- package/dist/pages/settings/users/member-management.js +13 -14
- package/dist/pages/settings/users/member-management.js.map +1 -1
- package/dist/pages/settings/users/profile.d.ts +1 -4
- package/dist/pages/settings/users/profile.js +0 -35
- package/dist/pages/settings/users/profile.js.map +1 -1
- package/dist/pages/settings/users/role-editor-store.js +26 -18
- package/dist/pages/settings/users/role-editor-store.js.map +1 -1
- package/dist/pages/settings/users/role-editor.d.ts +0 -1
- package/dist/pages/settings/users/role-editor.js +1 -2
- package/dist/pages/settings/users/role-editor.js.map +1 -1
- package/dist/pages/settings/users/role-preview.js +1 -2
- package/dist/pages/settings/users/role-preview.js.map +1 -1
- package/dist/pages/settings/users/roles.js +2 -2
- package/dist/pages/settings/users/roles.js.map +1 -1
- package/dist/pages/settings/users/two-factor-operations.js.map +1 -1
- package/dist/realtime/broker.d.ts +6 -0
- package/dist/realtime/broker.js +8 -1
- package/dist/realtime/broker.js.map +1 -1
- package/dist/realtime/presence.d.ts +8 -3
- package/dist/realtime/presence.js +8 -4
- package/dist/realtime/presence.js.map +1 -1
- package/dist/realtime/sessions.d.ts +11 -1
- package/dist/realtime/sessions.js +21 -3
- package/dist/realtime/sessions.js.map +1 -1
- package/dist/realtime/table-view.d.ts +2 -2
- package/dist/realtime/table-view.js +4 -3
- package/dist/realtime/table-view.js.map +1 -1
- package/dist/routes/auth/client-ip.d.ts +1 -0
- package/dist/routes/auth/client-ip.js +18 -0
- package/dist/routes/auth/client-ip.js.map +1 -1
- package/dist/routes/auth/oauth/config.d.ts +2 -1
- package/dist/routes/auth/oauth/config.js +5 -1
- package/dist/routes/auth/oauth/config.js.map +1 -1
- package/dist/routes/auth/session-handoff.d.ts +0 -1
- package/dist/routes/auth/session-handoff.js +0 -2
- package/dist/routes/auth/session-handoff.js.map +1 -1
- package/dist/routes/auth.js +3 -2
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/realtime.d.ts +13 -1
- package/dist/routes/realtime.js +114 -29
- package/dist/routes/realtime.js.map +1 -1
- package/dist/startup-config.d.ts +2 -0
- package/dist/startup-config.js +62 -0
- package/dist/startup-config.js.map +1 -0
- package/dist/store/interfaces.d.ts +3 -2
- package/dist/store/interfaces.js +16 -6
- package/dist/store/interfaces.js.map +1 -1
- package/dist/store/listing.js +24 -13
- package/dist/store/listing.js.map +1 -1
- package/dist/store/operations.js +4 -0
- package/dist/store/operations.js.map +1 -1
- package/dist/store/requirements.js +3 -2
- package/dist/store/requirements.js.map +1 -1
- package/dist/store/runtime.d.ts +10 -1
- package/dist/store/runtime.js +33 -6
- package/dist/store/runtime.js.map +1 -1
- package/dist/store/types.d.ts +10 -0
- package/dist/validation/auth.schema.d.ts +2 -2
- package/dist/validation/member-invite.schema.d.ts +2 -10
- package/dist/validation/member-invite.schema.js +2 -15
- package/dist/validation/member-invite.schema.js.map +1 -1
- package/frontend-vue/dms.frontend.ts +24 -5
- package/frontend-vue/dms.server.ts +17 -0
- package/frontend-vue/layers/dms-auth/server/accounts.ts +3 -4
- package/frontend-vue/layers/dms-core/app/build/composables/renderNow.ts +34 -0
- package/frontend-vue/layers/dms-core/app/build/composables/user/tablePreferences.ts +5 -1
- package/frontend-vue/layers/dms-core/app/build/utils/crossTab.ts +199 -0
- package/frontend-vue/layers/dms-core/app/build/utils/sharedRealtimeConnection.ts +410 -0
- package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +77 -12
- package/frontend-vue/layers/dms-core/app/composables/data-types/useDataType.ts +26 -0
- package/frontend-vue/layers/dms-core/app/composables/hydration/isHydratingComponent.ts +29 -0
- package/frontend-vue/layers/dms-core/app/composables/live/livePauseSource.ts +29 -0
- package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +150 -9
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +6 -4
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +27 -1
- package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +256 -119
- package/frontend-vue/layers/dms-core/app/composables/translation/useComposedText.ts +21 -8
- package/frontend-vue/layers/dms-core/app/composables/translation/useTranslation.ts +22 -5
- package/frontend-vue/layers/dms-core/app/plugins/dev-error-log.ts +16 -0
- package/frontend-vue/layers/dms-core/app/types/user.ts +6 -0
- package/frontend-vue/layers/dms-core/app/utils/composedText.ts +7 -2
- package/frontend-vue/layers/dms-core/app/utils/formatter.ts +166 -35
- package/frontend-vue/layers/dms-core/app/utils/url-interpolation.ts +18 -1
- package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +134 -2
- package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +134 -2
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardBanners.vue +3 -2
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +41 -21
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardPageBody.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +4 -19
- package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +11 -3
- package/frontend-vue/layers/dms-layout/app/build/components/layout/GithubStarPrompt.vue +18 -7
- package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionBar.vue +8 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionsOutlet.vue +14 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/RowSidePanel.vue +43 -82
- package/frontend-vue/layers/dms-layout/app/build/components/layout/SidebarUserMenu.vue +44 -3
- package/frontend-vue/layers/dms-layout/app/build/components/layout/UserIdentity.vue +11 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleCatalogTile.vue +132 -35
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleManageMenu.vue +59 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleSourceLabel.vue +25 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreBanners.vue +73 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreOperation.vue +116 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +8 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +85 -207
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +10 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTileSkeleton.vue +68 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +24 -8
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxItem.vue +8 -5
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/region/RegionTimeZoneInput.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-permissions.ts +19 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/useRoleEditor.ts +2 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/layout/useSidePanelWidth.ts +31 -70
- package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationGroupsOpen.ts +11 -1
- package/frontend-vue/layers/dms-layout/app/build/utils/dev-reload-stream.ts +81 -0
- package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +232 -36
- package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +122 -14
- package/frontend-vue/layers/dms-layout/app/composables/layout/useLayoutBanners.ts +32 -3
- package/frontend-vue/layers/dms-layout/app/composables/layout/usePageRecord.ts +50 -0
- package/frontend-vue/layers/dms-layout/app/composables/layout/useServedHeaderActions.ts +25 -7
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleHistory.ts +3 -2
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +51 -4
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStoreActions.ts +194 -0
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +23 -3
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageLayout.ts +4 -2
- package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +6 -1
- package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/custom-layouts/SettingsLayout.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules-store.vue +291 -0
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +87 -474
- package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +3 -0
- package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +6 -22
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-navigation.client.ts +2 -1
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-personal-pages.client.ts +14 -44
- package/frontend-vue/layers/dms-layout/app/plugins/dms-dev-reload.client.ts +14 -9
- package/frontend-vue/layers/dms-layout/app/theme/form-controls.ts +9 -4
- package/frontend-vue/layers/dms-layout/app/theme/surfaces.ts +22 -6
- package/frontend-vue/layers/dms-layout/app/types/module-store.ts +9 -0
- package/frontend-vue/layers/dms-layout/app/types/page.ts +15 -1
- package/frontend-vue/layers/dms-layout/app/utils/account-menu.ts +11 -0
- package/frontend-vue/layers/dms-layout/app/utils/breadcrumb.ts +57 -0
- package/frontend-vue/layers/dms-layout/app/utils/pageQuery.ts +26 -0
- package/frontend-vue/layers/dms-layout/app/utils/pageRecord.ts +71 -0
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +39 -14
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +38 -13
- package/frontend-vue/layers/dms-ui/app/build/components/containers/TargetComponentTree.vue +31 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/constants.ts +25 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +244 -15
- package/frontend-vue/layers/dms-ui/app/build/components/containers/modal/DynamicModal.vue +42 -25
- package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowHeader.vue +153 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowNotFound.vue +44 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/row/drawerRowHeader.ts +35 -0
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormEntries.vue +44 -9
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormErrorSummary.vue +11 -4
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldControl.vue +38 -5
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldNotes.vue +29 -5
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormSectionNav.vue +3 -1
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormSections.vue +106 -27
- package/frontend-vue/layers/dms-ui/app/build/components/table/Actions.vue +12 -4
- package/frontend-vue/layers/dms-ui/app/build/components/table/Empty.vue +4 -2
- package/frontend-vue/layers/dms-ui/app/build/components/table/FiltersRow.vue +38 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/Pagination.vue +55 -54
- package/frontend-vue/layers/dms-ui/app/build/components/table/RowSelection.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +396 -288
- package/frontend-vue/layers/dms-ui/app/build/components/table/Tabs.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/Views.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardAvatar.vue +59 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardGridSkeleton.vue +19 -2
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardRowActions.vue +38 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardsDisplay.vue +148 -104
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/LiveHoldBanner.vue +17 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/bulkSelection.ts +43 -9
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowComponentOptions.ts +76 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowNavigation.ts +172 -27
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +270 -46
- package/frontend-vue/layers/dms-ui/app/build/composables/confirm/confirmDialogTexts.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/closingLayers.ts +186 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/context.ts +14 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/drawerSize.ts +229 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/useResizableSize.ts +175 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/metricCells.ts +18 -35
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/numberSuffix.ts +47 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +99 -24
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerFieldTypes.ts +2 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationFilter.ts +75 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationRows.ts +71 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationValue.ts +39 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/twoLineCell.ts +3 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/useColumnValueRenderer.ts +6 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/form/createFormFields.ts +15 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldSaved.ts +40 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldValueRule.ts +62 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formEntryContext.ts +5 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +42 -7
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formLayout.ts +106 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formSections.ts +32 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/readonlyValue.ts +19 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/registerFormFunctions.ts +27 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/repeaterTable.ts +65 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/serverFieldErrors.ts +49 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/toasterClearance.ts +118 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/useInstantForm.ts +12 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/live/currentPageLive.ts +41 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/live/livePoller.ts +10 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/live/pageReadClock.ts +28 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/live/shortcuts.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageLiveHost.ts +17 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/live/useUpdatedLabel.ts +38 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/pageToolbarContext.ts +75 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/toolbarControlHeight.ts +29 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodComparison.ts +52 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +21 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodShortLabels.ts +28 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/usePeriodDraft.ts +1 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useRelationFilterLabels.ts +73 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useRowScrollAnchor.ts +106 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useTable.ts +9 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useTableColumns.ts +1 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/archivedCount.ts +31 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/filterEditorValue.ts +38 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/formatFilterValue.ts +33 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/scrollEdges.ts +31 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useCardFields.ts +33 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useMobileDisplay.ts +48 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useScrolledPastTop.ts +41 -18
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +3 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLinkedVisit.ts +78 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLiveHold.ts +135 -22
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +56 -25
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +77 -32
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +86 -10
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/bulkActions.ts +14 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/card.ts +127 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/compareValues.ts +13 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/expandedRows.ts +28 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/groupedRows.ts +114 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/linkedArrival.ts +308 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/locateRow.ts +106 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pageArrival.ts +34 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pinnedRows.ts +17 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/quickFilters.ts +41 -33
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/recordLink.ts +24 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableCounts.ts +113 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableQuery.ts +27 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/views.ts +22 -6
- package/frontend-vue/layers/dms-ui/app/build/utils/addressSuggestions.ts +49 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/eyebrow.ts +12 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/fieldOptionsI18n.ts +51 -11
- package/frontend-vue/layers/dms-ui/app/build/utils/inputWidth.ts +53 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/numberAdjustment.ts +6 -3
- package/frontend-vue/layers/dms-ui/app/build/utils/relativeDate.ts +19 -5
- package/frontend-vue/layers/dms-ui/app/build/utils/scrollWithin.ts +99 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +36 -14
- package/frontend-vue/layers/dms-ui/app/components/PasswordStrength.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/RecursiveComponent.vue +9 -1
- package/frontend-vue/layers/dms-ui/app/components/blocks/LiveToggleBlock.vue +80 -32
- package/frontend-vue/layers/dms-ui/app/components/blocks/PageToolbarBlock.vue +137 -0
- package/frontend-vue/layers/dms-ui/app/components/blocks/StatGroupBlock.vue +5 -0
- package/frontend-vue/layers/dms-ui/app/components/chart/ApexChartHost.vue +16 -2
- package/frontend-vue/layers/dms-ui/app/components/chart/Chart.vue +24 -5
- package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +41 -23
- package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/container-footer/ContainerFooter.vue +34 -0
- package/frontend-vue/layers/dms-ui/app/components/field-row/FieldRow.vue +25 -2
- package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +2 -2
- package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +118 -36
- package/frontend-vue/layers/dms-ui/app/components/form/components/DatePicker.vue +20 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Display.vue +13 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +8 -3
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +53 -5
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputCode.vue +29 -5
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputNumber.vue +45 -8
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputPercentage.vue +28 -5
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputSecret.vue +5 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputText.vue +19 -2
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputTime.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +205 -65
- package/frontend-vue/layers/dms-ui/app/components/form/components/Repeater.vue +80 -30
- package/frontend-vue/layers/dms-ui/app/components/form/components/RepeaterTable.vue +203 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/Select.vue +4 -2
- package/frontend-vue/layers/dms-ui/app/components/grid/GridRow.vue +11 -2
- package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +79 -70
- package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +117 -21
- package/frontend-vue/layers/dms-ui/app/components/meter/Meter.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +113 -34
- package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +14 -2
- package/frontend-vue/layers/dms-ui/app/components/section-header/Eyebrow.vue +6 -1
- package/frontend-vue/layers/dms-ui/app/components/segmented/Segmented.vue +13 -0
- package/frontend-vue/layers/dms-ui/app/components/stat-group/StatGroup.vue +52 -3
- package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +45 -35
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanDisplay.vue +4 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +410 -96
- package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +33 -5
- package/frontend-vue/layers/dms-ui/app/composables/chart/apexCategoryLabels.ts +68 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/apexDatetimeBuckets.ts +306 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/formatValue.ts +4 -3
- package/frontend-vue/layers/dms-ui/app/composables/chart/heatmapColors.ts +93 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/localizeSeries.ts +62 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.ts +116 -8
- package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.types.ts +7 -0
- package/frontend-vue/layers/dms-ui/app/composables/containers/types.ts +84 -1
- package/frontend-vue/layers/dms-ui/app/composables/containers/useDrawer.ts +69 -8
- package/frontend-vue/layers/dms-ui/app/composables/form/types/field-saved.ts +11 -0
- package/frontend-vue/layers/dms-ui/app/composables/form/types/field.ts +4 -0
- package/frontend-vue/layers/dms-ui/app/composables/form/types/functions.ts +1 -0
- package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +142 -21
- package/frontend-vue/layers/dms-ui/app/composables/live/usePageLive.ts +11 -4
- package/frontend-vue/layers/dms-ui/app/composables/page-toolbar/usePageToolbarSize.ts +30 -0
- package/frontend-vue/layers/dms-ui/app/composables/table-view/kanban.ts +4 -2
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +22 -3
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +4 -0
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/display.ts +24 -1
- package/frontend-vue/layers/dms-ui/app/plugins/closing-layers.client.ts +10 -0
- package/frontend-vue/layers/dms-ui/app/plugins/live-pause-source.ts +15 -0
- package/frontend-vue/layers/dms-ui/app/plugins/register.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/types/input-number.ts +3 -2
- package/frontend-vue/layers/dms-ui/app/types/page-toolbar.ts +7 -0
- package/frontend-vue/layers/dms-ui/app/types/row-action.ts +0 -2
- package/frontend-vue/layers/dms-ui/app/types/row-navigation.ts +17 -0
- package/frontend-vue/layers/dms-ui/app/types/table/query.ts +2 -1
- package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +3 -1
- package/frontend-vue/layers/dms-ui/app/utils/componentTree.ts +24 -0
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +16 -4
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +16 -4
- package/frontend-vue/package.json +2 -2
- package/package.json +7 -6
- package/skills/dms/SKILL.md +1 -1
- package/skills/dms-auth/SKILL.md +5 -3
- package/skills/dms-best-practices/REFERENCE.md +2 -0
- package/skills/dms-best-practices/SKILL.md +4 -0
- package/skills/dms-module/SKILL.md +3 -2
- package/skills/dms-pages/REFERENCE.md +125 -16
- package/skills/dms-pages/SKILL.md +1 -1
- package/skills/dms-project/DEV-SERVERS.md +23 -1
- package/skills/dms-project/REFERENCE.md +1 -1
- package/skills/dms-project/SKILL.md +4 -1
- package/skills/dms-project/scripts/dms-dev.sh +205 -17
- package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +0 -42
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts" generic="T extends Data">
|
|
2
|
+
import { get } from "@nuxt/ui/runtime/utils/index.js";
|
|
2
3
|
import type {
|
|
3
4
|
ColumnSizingState,
|
|
4
5
|
ExpandedState,
|
|
@@ -58,9 +59,8 @@ import {
|
|
|
58
59
|
} from "../../build/composables/table-view/utils/tableQuery";
|
|
59
60
|
import { buildTableViewShortcuts } from "../../composables/table-view/shortcuts";
|
|
60
61
|
import {
|
|
61
|
-
|
|
62
|
+
expandedRowsOnListing,
|
|
62
63
|
type ExpandedRowMap,
|
|
63
|
-
keepListedRows,
|
|
64
64
|
nextExpandedRows,
|
|
65
65
|
} from "../../build/composables/table-view/utils/expandedRows";
|
|
66
66
|
import { resolveTableChrome } from "../../build/composables/table-view/utils/chrome";
|
|
@@ -128,16 +128,32 @@ import {
|
|
|
128
128
|
type WatchSource,
|
|
129
129
|
} from "vue";
|
|
130
130
|
import { useTableViewConfig } from "../../build/composables/table-view/useTableViewConfig";
|
|
131
|
-
import {
|
|
131
|
+
import {
|
|
132
|
+
keptViewId,
|
|
133
|
+
useTableViews,
|
|
134
|
+
} from "../../build/composables/table-view/useTableViews";
|
|
132
135
|
import { useGroupedRows } from "../../build/composables/table-view/useGroupedRows";
|
|
133
136
|
import { useTableFooter } from "../../build/composables/table-view/useTableFooter";
|
|
134
137
|
import { useAccumulatedPages } from "../../build/composables/table-view/useAccumulatedPages";
|
|
138
|
+
import type { RowStep } from "../../build/composables/actions/rowNavigation";
|
|
139
|
+
import { rowLocator } from "../../build/composables/table-view/utils/locateRow";
|
|
135
140
|
import { usePersistedPagination } from "../../build/composables/table-view/usePersistedPagination";
|
|
136
141
|
import { useTablePreferences } from "../../build/composables/table-view/useTablePreferences";
|
|
142
|
+
import {
|
|
143
|
+
isLinkedArrival,
|
|
144
|
+
linkedArrivalPreferences,
|
|
145
|
+
linkFollowingPreferences,
|
|
146
|
+
linkKeyScope,
|
|
147
|
+
urlFilterScope,
|
|
148
|
+
} from "../../build/composables/table-view/utils/linkedArrival";
|
|
149
|
+
import { pageArrivalKind } from "../../build/composables/table-view/utils/pageArrival";
|
|
150
|
+
import { useTableLinkedVisit } from "../../build/composables/table-view/useTableLinkedVisit";
|
|
151
|
+
import { useMobileDisplay } from "../../build/composables/table-view/useMobileDisplay";
|
|
137
152
|
import type { Ref } from "vue";
|
|
138
153
|
import type { TableField } from "#dms-core/app/build/composables/user/tablePreferences";
|
|
139
154
|
import { useTableRows } from "../../build/composables/table-view/useTableRows";
|
|
140
155
|
import { useRefreshTriggers } from "../../build/composables/blocks/useRefreshTriggers";
|
|
156
|
+
import { refreshPageBlocks } from "../../utils/blockRefresh";
|
|
141
157
|
import { useTableLiveHold } from "../../build/composables/table-view/useTableLiveHold";
|
|
142
158
|
import type { TableViewLiveHoldOptions } from "../../types/live";
|
|
143
159
|
import { usePageLive } from "../../composables/live/usePageLive";
|
|
@@ -145,13 +161,24 @@ import LiveHoldBanner from "../../build/components/table-view/LiveHoldBanner.vue
|
|
|
145
161
|
import { useTableReorder } from "../../build/composables/table-view/useTableReorder";
|
|
146
162
|
import {
|
|
147
163
|
groupedOptionsFor,
|
|
148
|
-
|
|
164
|
+
listTableSorting,
|
|
165
|
+
orderGroupedRows,
|
|
166
|
+
pickedTableSorting,
|
|
167
|
+
type TableSortingInput,
|
|
149
168
|
isGroupableColumn,
|
|
150
169
|
} from "../../build/composables/table-view/utils/groupedRows";
|
|
151
170
|
import {
|
|
152
171
|
readTableUrlTab,
|
|
153
172
|
useTableUrlTab,
|
|
154
173
|
} from "../../build/composables/table-view/useTableUrlTab";
|
|
174
|
+
import {
|
|
175
|
+
buildTableCountQueries,
|
|
176
|
+
isCountNarrowed,
|
|
177
|
+
navBadgeCount,
|
|
178
|
+
VIEW_COUNT_ID_PREFIX,
|
|
179
|
+
type TableCountState,
|
|
180
|
+
} from "../../build/composables/table-view/utils/tableCounts";
|
|
181
|
+
import { viewFilters } from "../../build/composables/table-view/utils/views";
|
|
155
182
|
import { useTableRecordLink } from "../../build/composables/table-view/useTableRecordLink";
|
|
156
183
|
import TableViews, {
|
|
157
184
|
type TableViewItem,
|
|
@@ -187,6 +214,7 @@ interface RealtimePresenceActor {
|
|
|
187
214
|
type RealtimePresenceMap = Record<string, RealtimePresenceActor[]>;
|
|
188
215
|
|
|
189
216
|
/** A kept field of the table, its state, and its default. */
|
|
217
|
+
// The default may move once mounted (a phone's display): read when written.
|
|
190
218
|
type TableStatePreference = [TableField, Ref<unknown>, unknown];
|
|
191
219
|
|
|
192
220
|
// The backend options of a table view, declared here as well so the
|
|
@@ -194,6 +222,8 @@ type TableStatePreference = [TableField, Ref<unknown>, unknown];
|
|
|
194
222
|
interface TableViewProps<T extends Data> extends TableViewConfig<T> {
|
|
195
223
|
/** Expandable rows: caret column + detail band (backend `expandable`). */
|
|
196
224
|
expandable?: TableViewExpandableConfig;
|
|
225
|
+
/** Caption count while the archived rows show (backend `archivedCountLabel`). */
|
|
226
|
+
archivedCountLabel?: string;
|
|
197
227
|
/** Placeholder of the search field. */
|
|
198
228
|
searchPlaceholder?: string;
|
|
199
229
|
/** Key opening the search (`defineShortcuts` notation); `shift_f` else. */
|
|
@@ -254,7 +284,51 @@ const {
|
|
|
254
284
|
defaultDisplay,
|
|
255
285
|
} = useTableViewConfig<T>(props);
|
|
256
286
|
|
|
257
|
-
const
|
|
287
|
+
const route = useDmsRoute();
|
|
288
|
+
const urlScope = {
|
|
289
|
+
tableId: props.tableId,
|
|
290
|
+
isSoleTableView: props.isSoleTableView,
|
|
291
|
+
};
|
|
292
|
+
const ALL_TAB_ID = "all";
|
|
293
|
+
|
|
294
|
+
const keptPreferences = useTablePreferences(componentId, pageId);
|
|
295
|
+
// A link naming a view, a tab, a record or a URL filter opens on the table's
|
|
296
|
+
// defaults plus what it names, never over the state kept from the last visit
|
|
297
|
+
// (which stays kept for the next plain visit), until the user makes the
|
|
298
|
+
// state shown their own: a view, a tab, a filter or a sort they pick. Only a
|
|
299
|
+
// reload (or Back) of the URL the table wrote for that state restores it.
|
|
300
|
+
const isLinked = isLinkedArrival({
|
|
301
|
+
query: route.query,
|
|
302
|
+
urlScope,
|
|
303
|
+
queryParamFilters,
|
|
304
|
+
isVisit: pageArrivalKind() === "visit",
|
|
305
|
+
rememberedTab: keptPreferences.get<string>("activeTab", ALL_TAB_ID),
|
|
306
|
+
keptLinkScope: keptPreferences.get<string | undefined>(
|
|
307
|
+
"linkScope",
|
|
308
|
+
undefined,
|
|
309
|
+
),
|
|
310
|
+
shownLink: keptPreferences.get<string | undefined>("shownLink", undefined),
|
|
311
|
+
});
|
|
312
|
+
const tablePreferences = linkFollowingPreferences(
|
|
313
|
+
isLinked ? linkedArrivalPreferences(keptPreferences) : keptPreferences,
|
|
314
|
+
{
|
|
315
|
+
isLinkedArrival: isLinked,
|
|
316
|
+
urlScope,
|
|
317
|
+
linkScope: () => urlFilterScope(route.query, queryParamFilters),
|
|
318
|
+
linkKeys: () => linkKeyScope(route.query, urlScope, queryParamFilters),
|
|
319
|
+
// Read once the table is mounted, past the declarations below. A page
|
|
320
|
+
// kept without its scope is restored as it is (`usePersistedPagination`).
|
|
321
|
+
listingState: () => [
|
|
322
|
+
["activeView", keptViewId(activeViewId.value), undefined],
|
|
323
|
+
["activeTab", activeTabId.value, ALL_TAB_ID],
|
|
324
|
+
["columnFilters", columnFilters.value, []],
|
|
325
|
+
["sorting", sorting.value, defaultSortState],
|
|
326
|
+
["pagination", paginationState.value, DEFAULT_PAGINATION],
|
|
327
|
+
["paginationScope", undefined, undefined],
|
|
328
|
+
],
|
|
329
|
+
},
|
|
330
|
+
);
|
|
331
|
+
onMounted(() => void tablePreferences.settleArrival());
|
|
258
332
|
const { sendComponentEvent } = useComponentEvent(componentId);
|
|
259
333
|
const { processI18n } = useTranslation();
|
|
260
334
|
|
|
@@ -394,8 +468,6 @@ const resolvedChrome = resolveTableChrome(props.layout, {
|
|
|
394
468
|
});
|
|
395
469
|
const GLOBAL_FILTER_DEBOUNCE_MS = 400;
|
|
396
470
|
|
|
397
|
-
const route = useDmsRoute();
|
|
398
|
-
|
|
399
471
|
const DEFAULT_FILTER_MODE = "is";
|
|
400
472
|
|
|
401
473
|
const queryParamHiddenFilters = computed<TableFilter[]>(() => {
|
|
@@ -511,12 +583,16 @@ const defaultSortKind = tableDefaultSort
|
|
|
511
583
|
?.type?.id,
|
|
512
584
|
)
|
|
513
585
|
: undefined;
|
|
586
|
+
// A hand-ordered table has no sort to pick: a sort saved before it was
|
|
587
|
+
// ordered by hand is not read back.
|
|
514
588
|
const sorting = ref<SortingState>(
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
589
|
+
reorderConfig
|
|
590
|
+
? defaultSortState
|
|
591
|
+
: sortingOrDefault(
|
|
592
|
+
tablePreferences.get<SortingState>("sorting", defaultSortState),
|
|
593
|
+
declaredSortableIds,
|
|
594
|
+
defaultSortState,
|
|
595
|
+
),
|
|
520
596
|
);
|
|
521
597
|
|
|
522
598
|
const savedFilters = tablePreferences.get<TableFilter[]>("columnFilters", []);
|
|
@@ -538,6 +614,16 @@ const manualFilters = savedFilters
|
|
|
538
614
|
.filter((f) => !pinnedKeys.has(f.accessorKey))
|
|
539
615
|
.map((f) => ({ ...f, pinned: false }));
|
|
540
616
|
const initialFilters = [...buildPinnedFilters(), ...manualFilters];
|
|
617
|
+
// The pinned filters at their declared value: what a view leaves out of them.
|
|
618
|
+
const pinnedDefaultFilters: TableFilter[] = (defaultFilters || []).map(
|
|
619
|
+
(filter) => ({
|
|
620
|
+
accessorKey: filter.accessorKey,
|
|
621
|
+
mode: filter.mode,
|
|
622
|
+
value: filter.value,
|
|
623
|
+
pinned: true,
|
|
624
|
+
initialValue: filter.value,
|
|
625
|
+
}),
|
|
626
|
+
);
|
|
541
627
|
|
|
542
628
|
const columnFilters = ref<TableFilter[]>(initialFilters);
|
|
543
629
|
const columnVisibility = ref<VisibilityState>(
|
|
@@ -559,18 +645,21 @@ const globalFilterDebounced = refDebounced(
|
|
|
559
645
|
);
|
|
560
646
|
const showArchived = ref(false);
|
|
561
647
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
648
|
+
// The display the table counts as its default: the phone's `mobileDisplay`
|
|
649
|
+
// once applied (see `useMobileDisplay` below).
|
|
650
|
+
const displayDefault = ref(
|
|
651
|
+
offeredDisplayIds.has(defaultDisplay) ? defaultDisplay : fallbackDisplayId,
|
|
652
|
+
);
|
|
566
653
|
|
|
567
654
|
// Views: named states of the whole table, the module's and the user's own.
|
|
568
655
|
const {
|
|
569
656
|
items: tableViewItems,
|
|
570
657
|
activeViewId,
|
|
658
|
+
activeView,
|
|
571
659
|
isModified: isViewModified,
|
|
572
660
|
canSaveViews,
|
|
573
|
-
|
|
661
|
+
selectView,
|
|
662
|
+
openLinkedView,
|
|
574
663
|
resetView,
|
|
575
664
|
saveView,
|
|
576
665
|
saveAsNewView,
|
|
@@ -578,21 +667,16 @@ const {
|
|
|
578
667
|
} = useTableViews({
|
|
579
668
|
views: props.views,
|
|
580
669
|
defaults: {
|
|
581
|
-
pinnedFilters:
|
|
582
|
-
accessorKey: filter.accessorKey,
|
|
583
|
-
mode: filter.mode,
|
|
584
|
-
value: filter.value,
|
|
585
|
-
pinned: true,
|
|
586
|
-
initialValue: filter.value,
|
|
587
|
-
})),
|
|
670
|
+
pinnedFilters: pinnedDefaultFilters,
|
|
588
671
|
sorting: defaultSortState,
|
|
589
672
|
visibility: initialVisibility,
|
|
590
673
|
columnOrder: listableColumns.map(
|
|
591
674
|
(column) => column.id ?? column.accessorKey,
|
|
592
675
|
),
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
676
|
+
// Read when a view opens: a phone's `mobileDisplay` once applied.
|
|
677
|
+
get display() {
|
|
678
|
+
return displayDefault.value;
|
|
679
|
+
},
|
|
596
680
|
density: props.density ?? DEFAULT_DENSITY,
|
|
597
681
|
displays: offeredDisplayIds,
|
|
598
682
|
},
|
|
@@ -610,9 +694,22 @@ const {
|
|
|
610
694
|
urlScope,
|
|
611
695
|
});
|
|
612
696
|
|
|
613
|
-
|
|
697
|
+
// A phone opens on the table's `mobileDisplay` (cards rather than a grid cut
|
|
698
|
+
// after its first columns) unless a display was chosen: saved, fixed by the
|
|
699
|
+
// open view, or the only way in.
|
|
700
|
+
useMobileDisplay({
|
|
701
|
+
mobileDisplay: props.mobileDisplay,
|
|
702
|
+
defaultDisplay: displayDefault,
|
|
703
|
+
offered: offeredDisplayIds,
|
|
704
|
+
display: activeDisplayId,
|
|
705
|
+
activeView,
|
|
706
|
+
isDisplayChosen: () =>
|
|
707
|
+
isDefaultDisplayStandalone ||
|
|
708
|
+
tablePreferences.get<string | undefined>("viewMode", undefined) !==
|
|
709
|
+
undefined,
|
|
710
|
+
});
|
|
614
711
|
|
|
615
|
-
const { t, te } = useI18n();
|
|
712
|
+
const { t, te, locale } = useI18n();
|
|
616
713
|
|
|
617
714
|
// A tab named by the URL wins over the one kept from the last visit.
|
|
618
715
|
const activeTabId = ref<string>(
|
|
@@ -668,12 +765,33 @@ const activeTabFilters = computed<TableFilter[]>(() => {
|
|
|
668
765
|
return tab?.filters || [];
|
|
669
766
|
});
|
|
670
767
|
|
|
768
|
+
const isOpenableTab = (tabId: string) =>
|
|
769
|
+
resolvedTabs.value.some((tab) => !tab.to && tab.id === tabId);
|
|
770
|
+
|
|
671
771
|
useTableUrlTab({
|
|
672
772
|
route,
|
|
673
773
|
urlScope,
|
|
674
774
|
activeTabId,
|
|
675
|
-
isOpenable:
|
|
676
|
-
|
|
775
|
+
isOpenable: isOpenableTab,
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
// A link to this page while it is shown opens like a linked arrival: on the
|
|
779
|
+
// table's defaults plus what it names, leaving the kept state as it was.
|
|
780
|
+
useTableLinkedVisit({
|
|
781
|
+
route,
|
|
782
|
+
urlScope,
|
|
783
|
+
queryParamFilters,
|
|
784
|
+
activeView: () => activeViewId.value,
|
|
785
|
+
activeTab: () => activeTabId.value,
|
|
786
|
+
canOpenView: (id) => tableViewItems.value.some((view) => view.id === id),
|
|
787
|
+
canOpenTab: isOpenableTab,
|
|
788
|
+
follow: (query) => {
|
|
789
|
+
void tablePreferences.followLink(() => {
|
|
790
|
+
openLinkedView(query);
|
|
791
|
+
const tab = readTableUrlTab(query, urlScope);
|
|
792
|
+
activeTabId.value = tab && isOpenableTab(tab) ? tab : ALL_TAB_ID;
|
|
793
|
+
});
|
|
794
|
+
},
|
|
677
795
|
});
|
|
678
796
|
|
|
679
797
|
watch(resolvedTabs, (next) => {
|
|
@@ -700,6 +818,7 @@ const tableRows = useTableRows<T>({
|
|
|
700
818
|
// Quick filters: dropdowns of the toolbar over a column's values (a select's
|
|
701
819
|
// items, a boolean, the rows a relation points to), writing that column's
|
|
702
820
|
// filter like the filters row does.
|
|
821
|
+
const { getDataType } = useDataTypes();
|
|
703
822
|
const relationQuickFilterValues = ref<Record<string, QuickFilterItem[]>>({});
|
|
704
823
|
// Relation values load after mount: until then their buttons hold their place.
|
|
705
824
|
const relationQuickFiltersLoaded = ref(false);
|
|
@@ -712,7 +831,7 @@ const quickFilterDefinitions = (props.quickFilters ?? []).map((filter) => {
|
|
|
712
831
|
column,
|
|
713
832
|
mode: quickFilterMode(column, filter.mode),
|
|
714
833
|
items: staticQuickFilterItems(column, processI18n),
|
|
715
|
-
source: relationQuickFilterSource(column),
|
|
834
|
+
source: relationQuickFilterSource(column, getDataType),
|
|
716
835
|
};
|
|
717
836
|
});
|
|
718
837
|
|
|
@@ -726,7 +845,9 @@ const loadRelationQuickFilters = async () => {
|
|
|
726
845
|
try {
|
|
727
846
|
const response = await $authFetch<
|
|
728
847
|
TableViewListResponse<Record<string, unknown>>
|
|
729
|
-
>(source!.
|
|
848
|
+
>(source!.searchUrl, {
|
|
849
|
+
query: { limit: RELATION_QUICK_FILTER_LIMIT },
|
|
850
|
+
});
|
|
730
851
|
relationQuickFilterValues.value = {
|
|
731
852
|
...relationQuickFilterValues.value,
|
|
732
853
|
[filter.field]: relationQuickFilterItems(response.results, source!),
|
|
@@ -800,17 +921,22 @@ const effectiveGlobalFilter = computed(() =>
|
|
|
800
921
|
const isGroupedDisplay = computed(
|
|
801
922
|
() => activeDisplayId.value === GROUPED_DISPLAY_ID && !!groupedOptions,
|
|
802
923
|
);
|
|
924
|
+
const sortingInput = computed<TableSortingInput>(() => ({
|
|
925
|
+
reorderField: reorderConfig?.field,
|
|
926
|
+
grouped: isGroupedDisplay.value ? activeGroupedOptions.value : undefined,
|
|
927
|
+
canSort: activeCapabilities.value.sorting,
|
|
928
|
+
sorting: sorting.value,
|
|
929
|
+
defaultSorting: defaultSortState,
|
|
930
|
+
}));
|
|
931
|
+
// The order the rows are wanted in; the grouped display lists them on the
|
|
932
|
+
// grouped column and orders each group by it (`shownResults`).
|
|
933
|
+
const pickedSorting = computed(() => pickedTableSorting(sortingInput.value));
|
|
803
934
|
const effectiveSorting = computed<SortingState>(() => {
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
isGroupedDisplay.value && activeGroupedOptions.value
|
|
810
|
-
? groupedSorting(picked, activeGroupedOptions.value)
|
|
811
|
-
: picked,
|
|
812
|
-
sortableIds.value,
|
|
813
|
-
);
|
|
935
|
+
const listed = listTableSorting(sortingInput.value);
|
|
936
|
+
// The hand order is kept whole: its field need not be a sortable column.
|
|
937
|
+
return reorderConfig && !sortingInput.value.grouped
|
|
938
|
+
? listed
|
|
939
|
+
: sanitizeSorting(listed, sortableIds.value);
|
|
814
940
|
});
|
|
815
941
|
|
|
816
942
|
// A feed grows by pages (`loadMore`, `infinite`) rather than paging.
|
|
@@ -852,6 +978,7 @@ const openRowId = computed(() =>
|
|
|
852
978
|
|
|
853
979
|
// The gate is set up with the other refresh triggers below; the hold only
|
|
854
980
|
// reads it once the table is on screen.
|
|
981
|
+
const NEW_ROWS_COUNT_ID = "liveHold:new";
|
|
855
982
|
const tableRoot = useTemplateRef<ComponentPublicInstance>("tableRoot");
|
|
856
983
|
const liveHold = useTableLiveHold({
|
|
857
984
|
liveHold: props.liveHold,
|
|
@@ -863,6 +990,32 @@ const liveHold = useTableLiveHold({
|
|
|
863
990
|
isSorted: () => !isSameSorting(effectiveSorting.value, defaultSortState),
|
|
864
991
|
tableElement: () => tableRoot.value?.$el ?? null,
|
|
865
992
|
},
|
|
993
|
+
// The pill counts the rows the table would list, on its own count route:
|
|
994
|
+
// the rows its tab, search and filters match now, less those it holds.
|
|
995
|
+
newRows: {
|
|
996
|
+
heldList: () => data.value,
|
|
997
|
+
countNow: async (signal) => {
|
|
998
|
+
const counts = await tableRows.countBatch(
|
|
999
|
+
[
|
|
1000
|
+
{
|
|
1001
|
+
id: NEW_ROWS_COUNT_ID,
|
|
1002
|
+
query: {
|
|
1003
|
+
...buildTableQuery({
|
|
1004
|
+
pagination: { pageIndex: 0, pageSize: 0 },
|
|
1005
|
+
sorting: [],
|
|
1006
|
+
columnFilters: effectiveColumnFilters.value,
|
|
1007
|
+
hiddenFilters: hiddenFilters.value,
|
|
1008
|
+
globalFilter: effectiveGlobalFilter.value,
|
|
1009
|
+
}),
|
|
1010
|
+
...archiveQuery.value,
|
|
1011
|
+
},
|
|
1012
|
+
},
|
|
1013
|
+
],
|
|
1014
|
+
signal,
|
|
1015
|
+
);
|
|
1016
|
+
return counts[NEW_ROWS_COUNT_ID];
|
|
1017
|
+
},
|
|
1018
|
+
},
|
|
866
1019
|
});
|
|
867
1020
|
|
|
868
1021
|
// The list, its counters and its footer figures load in parallel: a server
|
|
@@ -919,7 +1072,13 @@ const listLoadError = computed(() => {
|
|
|
919
1072
|
const shownData = computed(() =>
|
|
920
1073
|
listLoadError.value ? undefined : data.value,
|
|
921
1074
|
);
|
|
922
|
-
const shownResults = computed(() =>
|
|
1075
|
+
const shownResults = computed(() => {
|
|
1076
|
+
const results = shownData.value?.results;
|
|
1077
|
+
const grouped = activeGroupedOptions.value;
|
|
1078
|
+
return results && isGroupedDisplay.value && grouped
|
|
1079
|
+
? orderGroupedRows(results, grouped, pickedSorting.value, locale.value)
|
|
1080
|
+
: results;
|
|
1081
|
+
});
|
|
923
1082
|
|
|
924
1083
|
// Rows selected before the failure can no longer be seen or verified, yet the
|
|
925
1084
|
// selection toolbar would keep offering delete/archive/export on them.
|
|
@@ -1003,28 +1162,46 @@ watch(
|
|
|
1003
1162
|
},
|
|
1004
1163
|
);
|
|
1005
1164
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
buildTableQuery({
|
|
1010
|
-
pagination: { pageIndex: 0, pageSize: 0 },
|
|
1011
|
-
sorting: [],
|
|
1012
|
-
columnFilters: filters,
|
|
1013
|
-
hiddenFilters: queryParamHiddenFilters.value,
|
|
1014
|
-
globalFilter: search,
|
|
1015
|
-
});
|
|
1016
|
-
|
|
1017
|
-
const tabCountsQuery = computed(() => [
|
|
1018
|
-
...resolvedTabs.value
|
|
1019
|
-
.filter((tab) => !tab.to)
|
|
1020
|
-
.map((tab) => ({ id: tab.id, query: countQuery(tab.filters) })),
|
|
1021
|
-
...tableViewItems.value
|
|
1022
|
-
.filter((view) => view.count)
|
|
1023
|
-
.map((view) => ({
|
|
1024
|
-
id: `${VIEW_COUNT_ID_PREFIX}${view.id}`,
|
|
1025
|
-
query: countQuery(view.filters ?? [], view.search),
|
|
1026
|
-
})),
|
|
1165
|
+
const scopeFilters = computed<TableFilter[]>(() => [
|
|
1166
|
+
...queryParamHiddenFilters.value,
|
|
1167
|
+
...routeParamHiddenFilters.value,
|
|
1027
1168
|
]);
|
|
1169
|
+
const countState = computed<TableCountState>(() => ({
|
|
1170
|
+
scopeFilters: scopeFilters.value,
|
|
1171
|
+
tabFilters: activeTabFilters.value,
|
|
1172
|
+
columnFilters: effectiveColumnFilters.value,
|
|
1173
|
+
search: effectiveGlobalFilter.value,
|
|
1174
|
+
}));
|
|
1175
|
+
const isCountStateNarrowed = computed(() => isCountNarrowed(countState.value));
|
|
1176
|
+
|
|
1177
|
+
// Each counter counts what a click on it would list, so the counters follow
|
|
1178
|
+
// the filters, the search, the view and the tab: picks made in a row (quick
|
|
1179
|
+
// filters toggled one after the other) are counted once, when they settle.
|
|
1180
|
+
const COUNTS_DEBOUNCE_MS = 200;
|
|
1181
|
+
const debouncedCountsQuery = refDebounced(
|
|
1182
|
+
computed(() =>
|
|
1183
|
+
buildTableCountQueries({
|
|
1184
|
+
...countState.value,
|
|
1185
|
+
tabs: resolvedTabs.value
|
|
1186
|
+
.filter((tab) => !tab.to)
|
|
1187
|
+
.map((tab) => ({
|
|
1188
|
+
id: tab.id,
|
|
1189
|
+
filters: tab.filters,
|
|
1190
|
+
hasNavBadge: !!tab.navBadge,
|
|
1191
|
+
})),
|
|
1192
|
+
views: tableViewItems.value
|
|
1193
|
+
.filter((view) => view.count)
|
|
1194
|
+
.map((view) => ({
|
|
1195
|
+
id: view.id,
|
|
1196
|
+
filters: viewFilters(view, pinnedDefaultFilters),
|
|
1197
|
+
search: view.search,
|
|
1198
|
+
})),
|
|
1199
|
+
activeViewId: activeViewId.value,
|
|
1200
|
+
}),
|
|
1201
|
+
),
|
|
1202
|
+
COUNTS_DEBOUNCE_MS,
|
|
1203
|
+
);
|
|
1204
|
+
const tabCountsQuery = computed(() => debouncedCountsQuery.value);
|
|
1028
1205
|
|
|
1029
1206
|
// No default: `null` until the counts arrive, so the tabs draw placeholders
|
|
1030
1207
|
// instead of a count of nothing.
|
|
@@ -1095,6 +1272,83 @@ const hasMoreRows = computed(
|
|
|
1095
1272
|
const loadMoreRows = () => {
|
|
1096
1273
|
if (hasMoreRows.value && !isListLoading.value) loadNextPage();
|
|
1097
1274
|
};
|
|
1275
|
+
// A row's drawer or side panel steps through every row the table lists: past
|
|
1276
|
+
// the rows loaded, it turns the table's page (or grows its feed) and steps
|
|
1277
|
+
// onto the rows that arrive. A display reading its own rows keeps to them.
|
|
1278
|
+
const ADJACENT_ROWS_TIMEOUT_MS = 15_000;
|
|
1279
|
+
const listsServerPages = computed(() => !isActiveDisplaySelfManaged.value);
|
|
1280
|
+
// What a display reading its own rows shows (`setShownRows`), in its order:
|
|
1281
|
+
// the rows its row's container steps through. Another display, another set.
|
|
1282
|
+
const selfManagedRows = shallowRef<Data[]>([]);
|
|
1283
|
+
const setSelfManagedRows = (rows: T[]) => {
|
|
1284
|
+
selfManagedRows.value = rows as Data[];
|
|
1285
|
+
};
|
|
1286
|
+
watch(activeDisplayId, () => {
|
|
1287
|
+
selfManagedRows.value = [];
|
|
1288
|
+
});
|
|
1289
|
+
const navigableRows = (): Data[] =>
|
|
1290
|
+
listsServerPages.value
|
|
1291
|
+
? ((shownResults.value ?? []) as Data[])
|
|
1292
|
+
: selfManagedRows.value;
|
|
1293
|
+
const loadedRowsOffset = () => {
|
|
1294
|
+
if (isAccumulating || !listsServerPages.value) return 0;
|
|
1295
|
+
const { pageIndex, pageSize } = paginationState.value;
|
|
1296
|
+
return pageSize > 0 ? pageIndex * pageSize : 0;
|
|
1297
|
+
};
|
|
1298
|
+
const turnPage = (step: RowStep): boolean => {
|
|
1299
|
+
if (isAccumulating) {
|
|
1300
|
+
if (step < 0 || !hasMoreRows.value) return false;
|
|
1301
|
+
loadNextPage();
|
|
1302
|
+
return true;
|
|
1303
|
+
}
|
|
1304
|
+
const { pageIndex, pageSize } = paginationState.value;
|
|
1305
|
+
const next = pageIndex + step;
|
|
1306
|
+
const total = data.value?.total ?? 0;
|
|
1307
|
+
if (pageSize <= 0 || next < 0 || next * pageSize >= total) return false;
|
|
1308
|
+
paginationState.value = { ...paginationState.value, pageIndex: next };
|
|
1309
|
+
return true;
|
|
1310
|
+
};
|
|
1311
|
+
let adjacentRowsLoad: Promise<boolean> | undefined;
|
|
1312
|
+
const loadAdjacentRows = (step: RowStep): Promise<boolean> => {
|
|
1313
|
+
// A second J while the page loads waits for the same page.
|
|
1314
|
+
if (adjacentRowsLoad) return adjacentRowsLoad;
|
|
1315
|
+
if (!listsServerPages.value || isListLoading.value || !turnPage(step)) {
|
|
1316
|
+
return Promise.resolve(false);
|
|
1317
|
+
}
|
|
1318
|
+
const before = shownResults.value;
|
|
1319
|
+
adjacentRowsLoad = until(
|
|
1320
|
+
() => shownResults.value !== before && !isListLoading.value,
|
|
1321
|
+
)
|
|
1322
|
+
.toBe(true, { timeout: ADJACENT_ROWS_TIMEOUT_MS })
|
|
1323
|
+
.then(() => !listLoadError.value && shownResults.value !== before)
|
|
1324
|
+
.finally(() => {
|
|
1325
|
+
adjacentRowsLoad = undefined;
|
|
1326
|
+
});
|
|
1327
|
+
return adjacentRowsLoad;
|
|
1328
|
+
};
|
|
1329
|
+
// A row past the rows loaded (a deep link, a row live rows pushed out) is
|
|
1330
|
+
// found in the table's whole result, under the query the list reads: its
|
|
1331
|
+
// position, and its neighbours for the steps. Its rows read in the order
|
|
1332
|
+
// the table shows them: pinned rows lead each page it reads, not the result.
|
|
1333
|
+
const locateRow = rowLocator({
|
|
1334
|
+
list: async (offset, limit) => {
|
|
1335
|
+
if (!listsServerPages.value) return null;
|
|
1336
|
+
const page = await tableRows.listAsShown(
|
|
1337
|
+
{
|
|
1338
|
+
...queryRequest.value,
|
|
1339
|
+
...archiveQuery.value,
|
|
1340
|
+
...liveHold.heldQuery(),
|
|
1341
|
+
offset,
|
|
1342
|
+
limit,
|
|
1343
|
+
},
|
|
1344
|
+
requestedPagination.value.pageSize,
|
|
1345
|
+
);
|
|
1346
|
+
return page && { results: page.results as Data[], total: page.total };
|
|
1347
|
+
},
|
|
1348
|
+
rowIdKey: props.rowIdKey ?? ROW_ID_DEFAULT_KEY,
|
|
1349
|
+
isWholeRead: () => !!props.source && !props.source.capabilities.paginate,
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1098
1352
|
const accumulation = computed<TableAccumulation | undefined>(() =>
|
|
1099
1353
|
isAccumulating
|
|
1100
1354
|
? {
|
|
@@ -1160,6 +1414,7 @@ provide(KANBAN_DISPLAY_BRIDGE_KEY, {
|
|
|
1160
1414
|
groupByField: kanbanGroupBy,
|
|
1161
1415
|
editAction: tableProps.value.rowActions?.edit,
|
|
1162
1416
|
deleteAction: tableProps.value.rowActions?.delete,
|
|
1417
|
+
onCardMoved: () => void refreshCounts(),
|
|
1163
1418
|
});
|
|
1164
1419
|
|
|
1165
1420
|
// A link tab counts the rows of the list it opens; a list the caller may not
|
|
@@ -1198,14 +1453,19 @@ const refreshLinkTabCounts = async () => {
|
|
|
1198
1453
|
// event) or a refresh: what summarises them elsewhere (a navigation count)
|
|
1199
1454
|
// is told to read its figure again.
|
|
1200
1455
|
const { notifyTableDataChanged } = useTableDataChanges();
|
|
1201
|
-
const
|
|
1456
|
+
const refreshCounts = async () => {
|
|
1202
1457
|
notifyTableDataChanged(location);
|
|
1203
1458
|
await Promise.all([
|
|
1204
|
-
refresh(),
|
|
1205
1459
|
refreshTabCounts(),
|
|
1206
1460
|
refreshLinkTabCounts(),
|
|
1207
1461
|
refreshGroupCounts(),
|
|
1208
1462
|
refreshSummaries(),
|
|
1463
|
+
]);
|
|
1464
|
+
};
|
|
1465
|
+
const refreshAll = async () => {
|
|
1466
|
+
await Promise.all([
|
|
1467
|
+
refresh(),
|
|
1468
|
+
refreshCounts(),
|
|
1209
1469
|
activeDisplayRef.value?.refresh?.(),
|
|
1210
1470
|
]);
|
|
1211
1471
|
};
|
|
@@ -1222,6 +1482,11 @@ const refreshByUser = () => {
|
|
|
1222
1482
|
liveGate.acknowledge();
|
|
1223
1483
|
return refreshAll();
|
|
1224
1484
|
};
|
|
1485
|
+
// A write of the user's (a row or bulk action, a form saved) can move what
|
|
1486
|
+
// the rest of the page shows of the same records — a KPI, a tab's badge,
|
|
1487
|
+
// another list: every block of the page reads its data again, this table
|
|
1488
|
+
// among them, at once even on a paused live page.
|
|
1489
|
+
const refreshAfterWrite = () => refreshPageBlocks();
|
|
1225
1490
|
|
|
1226
1491
|
// "Preview as role": a link tab to a page the previewed role could not open
|
|
1227
1492
|
// is drawn locked, one to a page it opens partially is drawn partially
|
|
@@ -1259,19 +1524,27 @@ const tabsWithCount = computed(() =>
|
|
|
1259
1524
|
|
|
1260
1525
|
// A tab declared with `navBadge` keeps the navigation badge the server
|
|
1261
1526
|
// counted for the page it stands for (the linked page, or this one) up to
|
|
1262
|
-
// date with
|
|
1527
|
+
// date: with the tab's own counter while nothing narrows it, else with a
|
|
1528
|
+
// count of the tab alone, since the badge stands for the whole page. An
|
|
1529
|
+
// empty badge stands for zero.
|
|
1263
1530
|
const { setNavBadge } = useNavBadges();
|
|
1531
|
+
const tabNavBadgeCount = (tab: ResolvedTab, count: number | undefined) =>
|
|
1532
|
+
tab.to
|
|
1533
|
+
? count
|
|
1534
|
+
: navBadgeCount(tabCountsData.value, tab.id, isCountStateNarrowed.value);
|
|
1264
1535
|
watch(
|
|
1265
1536
|
tabsWithCount,
|
|
1266
1537
|
(next) => {
|
|
1267
1538
|
for (const tab of next) {
|
|
1268
1539
|
const source = resolvedTabs.value.find((entry) => entry.id === tab.id);
|
|
1269
|
-
if (!source?.navBadge
|
|
1540
|
+
if (!source?.navBadge) continue;
|
|
1541
|
+
const count = tabNavBadgeCount(source, tab.count);
|
|
1542
|
+
if (count === undefined) continue;
|
|
1270
1543
|
const fullId = source.to ? source.toPage : pageId;
|
|
1271
1544
|
const tone =
|
|
1272
1545
|
typeof source.navBadge === "object" ? source.navBadge.tone : undefined;
|
|
1273
1546
|
if (fullId) {
|
|
1274
|
-
setNavBadge(fullId,
|
|
1547
|
+
setNavBadge(fullId, count > 0 ? String(count) : "", tone);
|
|
1275
1548
|
}
|
|
1276
1549
|
}
|
|
1277
1550
|
},
|
|
@@ -1299,7 +1572,7 @@ const {
|
|
|
1299
1572
|
caption,
|
|
1300
1573
|
labelKey,
|
|
1301
1574
|
rowIdKey: props.rowIdKey,
|
|
1302
|
-
refreshCallback:
|
|
1575
|
+
refreshCallback: refreshAfterWrite,
|
|
1303
1576
|
formComponents,
|
|
1304
1577
|
formContainer,
|
|
1305
1578
|
formPages,
|
|
@@ -1308,9 +1581,16 @@ const {
|
|
|
1308
1581
|
pageId: pageId!,
|
|
1309
1582
|
queryParamFilters,
|
|
1310
1583
|
rowNavigation: {
|
|
1311
|
-
rows:
|
|
1584
|
+
rows: navigableRows,
|
|
1312
1585
|
rowIdKey: props.rowIdKey ?? ROW_ID_DEFAULT_KEY,
|
|
1313
1586
|
getRow: (id) => tableRows.getRow(id) as Promise<Data | undefined>,
|
|
1587
|
+
total: () =>
|
|
1588
|
+
listsServerPages.value
|
|
1589
|
+
? (shownData.value?.total ?? undefined)
|
|
1590
|
+
: undefined,
|
|
1591
|
+
offset: loadedRowsOffset,
|
|
1592
|
+
loadAdjacent: loadAdjacentRows,
|
|
1593
|
+
locate: locateRow,
|
|
1314
1594
|
},
|
|
1315
1595
|
recordScope: urlScope,
|
|
1316
1596
|
onRecordShown: (id) => recordLink?.shown(id),
|
|
@@ -1382,12 +1662,14 @@ const deselectRows = (ids: string[]) => {
|
|
|
1382
1662
|
};
|
|
1383
1663
|
|
|
1384
1664
|
// From the archive, a delete is a permanent one: its confirmation says so.
|
|
1385
|
-
|
|
1665
|
+
// A display holding the rows (a kanban card) hands them for its wording.
|
|
1666
|
+
const deleteRows = async (ids: string[], rows?: T[]) => {
|
|
1386
1667
|
const done = await deleteRowsAction(
|
|
1387
1668
|
ids,
|
|
1388
1669
|
tableProps.value.rowActions?.delete,
|
|
1389
1670
|
{
|
|
1390
1671
|
permanently: !!archiveMode && showArchived.value,
|
|
1672
|
+
rows,
|
|
1391
1673
|
},
|
|
1392
1674
|
);
|
|
1393
1675
|
if (done) deselectRows(ids);
|
|
@@ -1414,12 +1696,18 @@ const rowActionConfig = (key: "details" | "duplicate" | "add" | "edit") =>
|
|
|
1414
1696
|
tableProps.value.rowActions?.[key];
|
|
1415
1697
|
const handleRowClick = (item: T) =>
|
|
1416
1698
|
runConfirmedBuiltIn(rowActionConfig("details"), item, () => openRow(item));
|
|
1417
|
-
|
|
1418
|
-
|
|
1699
|
+
// Its dialog words the row shown (`{name}`), else only its id.
|
|
1700
|
+
const handleRowDuplicate = (itemId: string) => {
|
|
1701
|
+
const idKey = props.rowIdKey ?? ROW_ID_DEFAULT_KEY;
|
|
1702
|
+
const shown = shownResults.value?.find(
|
|
1703
|
+
(row) => String(get(row, idKey)) === itemId,
|
|
1704
|
+
);
|
|
1705
|
+
return runConfirmedBuiltIn(
|
|
1419
1706
|
rowActionConfig("duplicate"),
|
|
1420
|
-
{ [
|
|
1707
|
+
shown ?? { [idKey]: itemId },
|
|
1421
1708
|
() => duplicateRow(itemId),
|
|
1422
1709
|
);
|
|
1710
|
+
};
|
|
1423
1711
|
const handleRowAdd = () =>
|
|
1424
1712
|
runConfirmedBuiltIn(rowActionConfig("add"), undefined, () =>
|
|
1425
1713
|
newRow(queryParamDefaults.value),
|
|
@@ -1484,12 +1772,14 @@ const realtimeEventHandlers: Record<
|
|
|
1484
1772
|
return refreshAll();
|
|
1485
1773
|
},
|
|
1486
1774
|
[REALTIME_EVENT_TYPE.CREATED]: () => refreshAll(),
|
|
1775
|
+
// An edited row can change tab, group or column: the counts are read again
|
|
1776
|
+
// with it.
|
|
1487
1777
|
[REALTIME_EVENT_TYPE.UPDATED]: async (ids) => {
|
|
1488
1778
|
if (isActiveDisplaySelfManaged.value) {
|
|
1489
|
-
await activeDisplayRef.value?.refresh?.();
|
|
1779
|
+
await Promise.all([activeDisplayRef.value?.refresh?.(), refreshCounts()]);
|
|
1490
1780
|
return;
|
|
1491
1781
|
}
|
|
1492
|
-
await Promise.all(ids.map((id) => patchRowLocal(id)));
|
|
1782
|
+
await Promise.all([...ids.map((id) => patchRowLocal(id)), refreshCounts()]);
|
|
1493
1783
|
},
|
|
1494
1784
|
};
|
|
1495
1785
|
|
|
@@ -1636,22 +1926,20 @@ const expandedModel = computed<ExpandedState>({
|
|
|
1636
1926
|
},
|
|
1637
1927
|
});
|
|
1638
1928
|
|
|
1639
|
-
let
|
|
1929
|
+
let expandedListedKey: string | undefined;
|
|
1640
1930
|
if (expandableConfig) {
|
|
1641
1931
|
watch(
|
|
1642
1932
|
shownResults,
|
|
1643
1933
|
(rows) => {
|
|
1644
|
-
const
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
}
|
|
1649
|
-
expandedQueryKey = queryKey;
|
|
1650
|
-
expanded.value = defaultExpandedRows(
|
|
1651
|
-
rowIdsOf(rows),
|
|
1934
|
+
const next = expandedRowsOnListing(
|
|
1935
|
+
{ expanded: expanded.value, listedKey: expandedListedKey },
|
|
1936
|
+
rows ? rowIdsOf(rows) : undefined,
|
|
1937
|
+
JSON.stringify([queryRequest.value, archiveQuery.value]),
|
|
1652
1938
|
expandableConfig.defaultExpanded,
|
|
1653
1939
|
expandableConfig.single,
|
|
1654
1940
|
);
|
|
1941
|
+
expandedListedKey = next.listedKey;
|
|
1942
|
+
expanded.value = next.expanded;
|
|
1655
1943
|
},
|
|
1656
1944
|
{ immediate: true },
|
|
1657
1945
|
);
|
|
@@ -1896,6 +2184,7 @@ const displayContext = computed(
|
|
|
1896
2184
|
pageId,
|
|
1897
2185
|
options: optionsForDisplay(activeDisplayId.value),
|
|
1898
2186
|
refresh: refreshByUser,
|
|
2187
|
+
setShownRows: setSelfManagedRows,
|
|
1899
2188
|
reorder: reorderState.value,
|
|
1900
2189
|
table: displayTable.value,
|
|
1901
2190
|
}),
|
|
@@ -1993,13 +2282,16 @@ defineShortcuts(
|
|
|
1993
2282
|
);
|
|
1994
2283
|
|
|
1995
2284
|
// Each kept field with its default: a value back to it is forgotten.
|
|
2285
|
+
// A hand-ordered table keeps no sort.
|
|
1996
2286
|
const tableStatePreferences: TableStatePreference[] = [
|
|
1997
|
-
|
|
2287
|
+
...(reorderConfig
|
|
2288
|
+
? []
|
|
2289
|
+
: [["sorting", sorting, defaultSortState] as TableStatePreference]),
|
|
1998
2290
|
["columnFilters", columnFilters, []],
|
|
1999
2291
|
["columnVisibility", columnVisibility, initialVisibility],
|
|
2000
2292
|
["filtersOpen", filtersRowOpen, hasEmptyDefaultFilter],
|
|
2001
2293
|
["activeTab", activeTabId, ALL_TAB_ID],
|
|
2002
|
-
["viewMode", activeDisplayId,
|
|
2294
|
+
["viewMode", activeDisplayId, displayDefault],
|
|
2003
2295
|
["columnOrder", columnOrder, []],
|
|
2004
2296
|
["kanbanGroupBy", kanbanGroupBy, defaultKanbanGroupBy],
|
|
2005
2297
|
["groupedGroupBy", groupedGroupBy, defaultGroupedGroupBy],
|
|
@@ -2010,7 +2302,7 @@ tableStatePreferences.forEach(([field, state, defaultValue]) => {
|
|
|
2010
2302
|
watch(
|
|
2011
2303
|
state,
|
|
2012
2304
|
(newValue) => {
|
|
2013
|
-
tablePreferences.set(field, newValue, defaultValue);
|
|
2305
|
+
tablePreferences.set(field, newValue, unref(defaultValue));
|
|
2014
2306
|
},
|
|
2015
2307
|
{ deep: true },
|
|
2016
2308
|
);
|
|
@@ -2132,6 +2424,7 @@ onMounted(() => {
|
|
|
2132
2424
|
:url-filters="shownUrlFilters"
|
|
2133
2425
|
:default-page-size="props.pageSize"
|
|
2134
2426
|
:archive-toggle="canToggleArchived"
|
|
2427
|
+
:archived-count-label="props.archivedCountLabel"
|
|
2135
2428
|
:displays="availableDisplays"
|
|
2136
2429
|
:active-capabilities="activeCapabilities"
|
|
2137
2430
|
:kanban-group-by-options="kanbanGroupByOptions"
|
|
@@ -2143,7 +2436,10 @@ onMounted(() => {
|
|
|
2143
2436
|
:load-error="listLoadError"
|
|
2144
2437
|
:data="shownResults || []"
|
|
2145
2438
|
:pagination-options="{ manualPagination: true, rowCount: data?.total }"
|
|
2146
|
-
:sorting-options="{
|
|
2439
|
+
:sorting-options="{
|
|
2440
|
+
manualSorting: true,
|
|
2441
|
+
enableSorting: activeCapabilities.sorting,
|
|
2442
|
+
}"
|
|
2147
2443
|
:default-sort="tableDefaultSort"
|
|
2148
2444
|
:default-sort-label="defaultSortLabel"
|
|
2149
2445
|
:default-sort-kind="defaultSortKind"
|
|
@@ -2171,10 +2467,7 @@ onMounted(() => {
|
|
|
2171
2467
|
@remove-url-filter="removeUrlFilter"
|
|
2172
2468
|
@edit="handleRowEdit"
|
|
2173
2469
|
>
|
|
2174
|
-
<template
|
|
2175
|
-
v-if="liveHold.newCount.value > 0 || liveHold.reason.value"
|
|
2176
|
-
#notice
|
|
2177
|
-
>
|
|
2470
|
+
<template v-if="liveHold.reason.value" #notice>
|
|
2178
2471
|
<LiveHoldBanner
|
|
2179
2472
|
:count="liveHold.newCount.value"
|
|
2180
2473
|
:reason="liveHold.reason.value"
|
|
@@ -2183,6 +2476,27 @@ onMounted(() => {
|
|
|
2183
2476
|
@show="liveHold.showNew"
|
|
2184
2477
|
/>
|
|
2185
2478
|
</template>
|
|
2479
|
+
<!-- What came in while the rows were read or the page paused: a pill
|
|
2480
|
+
over the first rows, under the column header band (the table sets
|
|
2481
|
+
its height; a sticky one stays put above a scrolled area, so the pill
|
|
2482
|
+
stays under it), taking no room, so the rows stay where they are.
|
|
2483
|
+
Above the pinned columns and the presence rail it overlaps. -->
|
|
2484
|
+
<template v-else-if="liveHold.newCount.value > 0" #notice>
|
|
2485
|
+
<div class="relative z-30 h-0">
|
|
2486
|
+
<div
|
|
2487
|
+
class="pointer-events-none absolute inset-x-0 top-[calc(var(--dms-table-head-height,0px)+0.5rem)] flex justify-center px-4"
|
|
2488
|
+
>
|
|
2489
|
+
<LiveHoldBanner
|
|
2490
|
+
floating
|
|
2491
|
+
class="pointer-events-auto"
|
|
2492
|
+
:count="liveHold.newCount.value"
|
|
2493
|
+
:missed-label="liveHold.missedLabel"
|
|
2494
|
+
:detail="liveHold.missedDetail.value"
|
|
2495
|
+
@show="liveHold.showNew"
|
|
2496
|
+
/>
|
|
2497
|
+
</div>
|
|
2498
|
+
</div>
|
|
2499
|
+
</template>
|
|
2186
2500
|
<template v-if="hasViews" #views>
|
|
2187
2501
|
<TableViews
|
|
2188
2502
|
:items="viewItems"
|
|
@@ -2191,7 +2505,7 @@ onMounted(() => {
|
|
|
2191
2505
|
:modified="isViewModified"
|
|
2192
2506
|
:can-save-as="canSaveViews"
|
|
2193
2507
|
:divided="tabsWithCount.length === 0"
|
|
2194
|
-
@open="
|
|
2508
|
+
@open="selectView"
|
|
2195
2509
|
@reset="resetView"
|
|
2196
2510
|
@save="saveView"
|
|
2197
2511
|
@save-as="saveAsNewView"
|