@antelopejs/dms 0.9.8 → 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 +102 -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 -48
- 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/index.d.ts +0 -1
- package/dist/routes/index.js +0 -1
- package/dist/routes/index.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/dist/routes/startup-gate.d.ts +0 -7
- package/dist/routes/startup-gate.js +0 -39
- package/dist/routes/startup-gate.js.map +0 -1
- package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +0 -42
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
computed,
|
|
3
|
+
getCurrentScope,
|
|
4
|
+
onMounted,
|
|
5
|
+
onScopeDispose,
|
|
6
|
+
ref,
|
|
7
|
+
watch,
|
|
8
|
+
type ComputedRef,
|
|
9
|
+
} from "vue";
|
|
2
10
|
import { usePageLive } from "../../../composables/live/usePageLive";
|
|
3
11
|
import type {
|
|
4
12
|
PageLiveNote,
|
|
5
13
|
TableViewLiveHoldOptions,
|
|
6
14
|
} from "../../../types/live";
|
|
7
15
|
import type { LiveGate } from "../live/currentPageLive";
|
|
8
|
-
import { useScrolledPastTop } from "./useScrolledPastTop";
|
|
16
|
+
import { useScrolledPastTop, useTableReached } from "./useScrolledPastTop";
|
|
9
17
|
import { LIVE_HELD_AT_QUERY_KEY } from "./useTableRows";
|
|
10
18
|
|
|
11
19
|
type Query = Record<string, unknown>;
|
|
@@ -18,7 +26,10 @@ export interface TableLiveHoldSignals {
|
|
|
18
26
|
isSearching?: () => boolean;
|
|
19
27
|
/** The rows are sorted otherwise than by the table's default. */
|
|
20
28
|
isSorted?: () => boolean;
|
|
21
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* The table's element: whether it is on screen, and where its first row
|
|
31
|
+
* stands for `whileScrolled`.
|
|
32
|
+
*/
|
|
22
33
|
tableElement?: () => unknown;
|
|
23
34
|
}
|
|
24
35
|
|
|
@@ -29,6 +40,29 @@ export type TableLiveHoldReason =
|
|
|
29
40
|
| "sorted"
|
|
30
41
|
| "scrolled";
|
|
31
42
|
|
|
43
|
+
/**
|
|
44
|
+
* A table holds by itself for a reason it names, or because the user has its
|
|
45
|
+
* rows on screen (`reading`): new rows would push them down. The latter goes
|
|
46
|
+
* unsaid; the new rows wait behind the "N new · Show" pill.
|
|
47
|
+
*/
|
|
48
|
+
type LocalHoldReason = TableLiveHoldReason | "reading";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* How a held table counts the new rows it would show: the rows its current
|
|
52
|
+
* query (tab, search, filters) matches now, less the rows it holds. Without
|
|
53
|
+
* it the pill counts every event of the table's topic, whatever row each
|
|
54
|
+
* one is about.
|
|
55
|
+
*/
|
|
56
|
+
export interface TableNewRowsCounter {
|
|
57
|
+
/**
|
|
58
|
+
* The list on screen, `null` before it arrives; read again whenever it
|
|
59
|
+
* changes (a new read, another query), which counts again.
|
|
60
|
+
*/
|
|
61
|
+
heldList: () => { total: number } | null | undefined;
|
|
62
|
+
/** How many rows the table's current query matches now, unbounded. */
|
|
63
|
+
countNow: (signal: AbortSignal) => Promise<number | undefined>;
|
|
64
|
+
}
|
|
65
|
+
|
|
32
66
|
export interface TableLiveHoldOptions {
|
|
33
67
|
/** The table's `liveHold`: `true` or an object enables it. */
|
|
34
68
|
liveHold: boolean | TableViewLiveHoldOptions | undefined;
|
|
@@ -40,12 +74,16 @@ export interface TableLiveHoldOptions {
|
|
|
40
74
|
*/
|
|
41
75
|
gate: () => LiveGate;
|
|
42
76
|
signals?: TableLiveHoldSignals;
|
|
77
|
+
newRows?: TableNewRowsCounter;
|
|
43
78
|
}
|
|
44
79
|
|
|
45
80
|
export interface TableLiveHold {
|
|
46
81
|
/** The rows are held: the page is paused, or the table holds by itself. */
|
|
47
82
|
isHolding: ComputedRef<boolean>;
|
|
48
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* The rows the table would add since its rows were held or last shown
|
|
85
|
+
* (`newRows`), or else the events it received meanwhile.
|
|
86
|
+
*/
|
|
49
87
|
newCount: ComputedRef<number>;
|
|
50
88
|
/**
|
|
51
89
|
* Why the table holds by itself while its page is live, `null` otherwise:
|
|
@@ -77,22 +115,26 @@ function holdOptionsOf(
|
|
|
77
115
|
}
|
|
78
116
|
|
|
79
117
|
// The table alone holds while the page is live: the user reads a row in the
|
|
80
|
-
// side panel, narrows or reorders the rows,
|
|
81
|
-
//
|
|
118
|
+
// side panel, narrows or reorders the rows, reads further down than the rows
|
|
119
|
+
// that would move, or has them on screen at all — a new row at the top would
|
|
120
|
+
// push them down. The first reason in this order is the one shown.
|
|
82
121
|
function useLocalHold(
|
|
83
122
|
holdOptions: TableViewLiveHoldOptions | null,
|
|
84
123
|
signals: TableLiveHoldSignals,
|
|
85
|
-
): ComputedRef<
|
|
124
|
+
): ComputedRef<LocalHoldReason | null> {
|
|
86
125
|
const tableElement = signals.tableElement ?? NO_ELEMENT;
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
return
|
|
90
|
-
}
|
|
91
|
-
const
|
|
126
|
+
const element = () => {
|
|
127
|
+
const target = tableElement();
|
|
128
|
+
return target instanceof HTMLElement ? target : null;
|
|
129
|
+
};
|
|
130
|
+
const isScrolled = useScrolledPastTop(element, !!holdOptions?.whileScrolled);
|
|
131
|
+
const isReached = useTableReached(element, !!holdOptions);
|
|
132
|
+
const reasons: Array<[LocalHoldReason, boolean, () => boolean]> = [
|
|
92
133
|
["rowOpen", !!holdOptions?.whileRowOpen, signals.isRowOpen ?? NEVER],
|
|
93
134
|
["searching", !!holdOptions?.whileSearching, signals.isSearching ?? NEVER],
|
|
94
135
|
["sorted", !!holdOptions?.whileSorted, signals.isSorted ?? NEVER],
|
|
95
136
|
["scrolled", !!holdOptions?.whileScrolled, () => isScrolled.value],
|
|
137
|
+
["reading", !!holdOptions, () => isReached.value],
|
|
96
138
|
];
|
|
97
139
|
return computed(
|
|
98
140
|
() =>
|
|
@@ -102,10 +144,11 @@ function useLocalHold(
|
|
|
102
144
|
}
|
|
103
145
|
|
|
104
146
|
/**
|
|
105
|
-
* The `liveHold` of a table view: while its live page is paused
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
147
|
+
* The `liveHold` of a table view: while its live page is paused, while its
|
|
148
|
+
* rows are on screen or scrolled past (rows landing at the top would move
|
|
149
|
+
* them) — or, with `whileRowOpen` / `whileSearching` / `whileSorted` /
|
|
150
|
+
* `whileScrolled`, saying so, while the user reads a row, searches, sorts or
|
|
151
|
+
* scrolled down — the table keeps its rows: the gate requests nothing on its events and counts them, and a
|
|
109
152
|
* source route is told the moment they were frozen, so the user's own
|
|
110
153
|
* search, sort and pagination list the rows as they stood.
|
|
111
154
|
* Show reads them once more without leaving the hold; the end of a hold of
|
|
@@ -132,16 +175,19 @@ export function useTableLiveHold(options: TableLiveHoldOptions): TableLiveHold {
|
|
|
132
175
|
},
|
|
133
176
|
{ flush: "sync" },
|
|
134
177
|
);
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
178
|
+
const missed = () => (isHolding.value ? options.gate().missed.value : 0);
|
|
179
|
+
const newCount = options.newRows
|
|
180
|
+
? useCountedNewRows(missed, options.newRows)
|
|
181
|
+
: computed(missed);
|
|
138
182
|
|
|
139
183
|
return {
|
|
140
184
|
isHolding,
|
|
141
185
|
newCount,
|
|
142
|
-
reason: computed(() =>
|
|
143
|
-
isHolding.value
|
|
144
|
-
|
|
186
|
+
reason: computed(() => {
|
|
187
|
+
if (!isHolding.value || isPaused.value) return null;
|
|
188
|
+
const reason = localReason.value;
|
|
189
|
+
return reason === "reading" ? null : reason;
|
|
190
|
+
}),
|
|
145
191
|
missedLabel: holdOptions?.missedLabel,
|
|
146
192
|
missedDetail: computed(() =>
|
|
147
193
|
isPaused.value && newCount.value > 0 ? missedDetail.value : null,
|
|
@@ -151,6 +197,73 @@ export function useTableLiveHold(options: TableLiveHoldOptions): TableLiveHold {
|
|
|
151
197
|
};
|
|
152
198
|
}
|
|
153
199
|
|
|
200
|
+
/** Lets a burst of events through before counting the new rows once. */
|
|
201
|
+
const NEW_ROWS_COUNT_DELAY_MS = 500;
|
|
202
|
+
|
|
203
|
+
// The new rows the table would show, counted on its source while it holds
|
|
204
|
+
// some events: a topic publishes on every row, so a "4xx" tab, a search or a
|
|
205
|
+
// filter would otherwise announce rows it never lists. Counted again on each
|
|
206
|
+
// new event or held list, a burst at a time. Rows removed meanwhile lower
|
|
207
|
+
// the count: the table answers what it lists, not what came in. A count that
|
|
208
|
+
// fails falls back to the events. Only in the browser, once mounted: the
|
|
209
|
+
// table sets up its gate and its list after its hold.
|
|
210
|
+
function useCountedNewRows(
|
|
211
|
+
missed: () => number,
|
|
212
|
+
counter: TableNewRowsCounter,
|
|
213
|
+
): ComputedRef<number> {
|
|
214
|
+
const counted = ref(0);
|
|
215
|
+
const hasFailed = ref(false);
|
|
216
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
217
|
+
let inFlight: AbortController | undefined;
|
|
218
|
+
|
|
219
|
+
const cancel = () => {
|
|
220
|
+
clearTimeout(timer);
|
|
221
|
+
timer = undefined;
|
|
222
|
+
inFlight?.abort();
|
|
223
|
+
inFlight = undefined;
|
|
224
|
+
};
|
|
225
|
+
const count = async () => {
|
|
226
|
+
timer = undefined;
|
|
227
|
+
const held = counter.heldList()?.total;
|
|
228
|
+
if (held === undefined) return;
|
|
229
|
+
inFlight?.abort();
|
|
230
|
+
const controller = new AbortController();
|
|
231
|
+
inFlight = controller;
|
|
232
|
+
try {
|
|
233
|
+
const now = await counter.countNow(controller.signal);
|
|
234
|
+
if (controller.signal.aborted) return;
|
|
235
|
+
hasFailed.value = now === undefined;
|
|
236
|
+
counted.value = Math.max(0, (now ?? held) - held);
|
|
237
|
+
} catch {
|
|
238
|
+
if (!controller.signal.aborted) hasFailed.value = true;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const scope = getCurrentScope();
|
|
243
|
+
const follow = () =>
|
|
244
|
+
watch(
|
|
245
|
+
[missed, counter.heldList],
|
|
246
|
+
([events]) => {
|
|
247
|
+
if (events === 0) {
|
|
248
|
+
cancel();
|
|
249
|
+
counted.value = 0;
|
|
250
|
+
hasFailed.value = false;
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
timer ??= setTimeout(count, NEW_ROWS_COUNT_DELAY_MS);
|
|
254
|
+
},
|
|
255
|
+
{ immediate: true },
|
|
256
|
+
);
|
|
257
|
+
onMounted(() => scope?.run(follow));
|
|
258
|
+
onScopeDispose(cancel);
|
|
259
|
+
|
|
260
|
+
return computed(() => {
|
|
261
|
+
const events = missed();
|
|
262
|
+
if (events === 0) return 0;
|
|
263
|
+
return hasFailed.value ? events : counted.value;
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
154
267
|
// The bound a source route receives while the rows are held: they are as
|
|
155
268
|
// fresh as the hold, or as the last read the user asked for since (Show, an
|
|
156
269
|
// action of theirs).
|
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
resolveUrlVariables,
|
|
12
12
|
} from "../../utils/urlVariables";
|
|
13
13
|
import type { RowActionRule } from "#dms-core/app/types/row-action";
|
|
14
|
-
import {
|
|
14
|
+
import { compareValues, textOf } from "./utils/compareValues";
|
|
15
|
+
import { pinRows, pinRowsBySpan } from "./utils/pinnedRows";
|
|
15
16
|
import {
|
|
16
17
|
appendPeriodToUrl,
|
|
17
18
|
usePeriodScope,
|
|
@@ -86,18 +87,6 @@ export function sourceRouteQuery(
|
|
|
86
87
|
);
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
const textOf = (value: unknown): string =>
|
|
90
|
-
value === null || value === undefined
|
|
91
|
-
? ""
|
|
92
|
-
: typeof value === "object"
|
|
93
|
-
? JSON.stringify(value)
|
|
94
|
-
: String(value);
|
|
95
|
-
|
|
96
|
-
const compareValues = (a: unknown, b: unknown): number => {
|
|
97
|
-
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
98
|
-
return textOf(a).localeCompare(textOf(b), undefined, { numeric: true });
|
|
99
|
-
};
|
|
100
|
-
|
|
101
90
|
/**
|
|
102
91
|
* The rows the browser narrows, sorts and pages itself, for what the source
|
|
103
92
|
* route left to it: a search over the listed columns' text, the sort on one
|
|
@@ -155,7 +144,10 @@ interface SharedRead<T> {
|
|
|
155
144
|
* order its keys were built in.
|
|
156
145
|
*/
|
|
157
146
|
const sourceReadKey = (url: string, query: Query): string =>
|
|
158
|
-
JSON.stringify([
|
|
147
|
+
JSON.stringify([
|
|
148
|
+
url,
|
|
149
|
+
Object.entries(query).sort(([a], [b]) => a.localeCompare(b)),
|
|
150
|
+
]);
|
|
159
151
|
|
|
160
152
|
/**
|
|
161
153
|
* Settles with `answer`, or rejects as soon as `signal` aborts, calling
|
|
@@ -173,9 +165,9 @@ function awaitUnlessAborted<R>(
|
|
|
173
165
|
};
|
|
174
166
|
if (signal.aborted) return abort();
|
|
175
167
|
signal.addEventListener("abort", abort, { once: true });
|
|
176
|
-
answer
|
|
177
|
-
|
|
178
|
-
|
|
168
|
+
answer
|
|
169
|
+
.then(resolve, reject)
|
|
170
|
+
.finally(() => signal.removeEventListener("abort", abort));
|
|
179
171
|
});
|
|
180
172
|
}
|
|
181
173
|
|
|
@@ -294,19 +286,57 @@ export function useTableRows<T extends Record<string, unknown>>(
|
|
|
294
286
|
): TableViewListResponse<T> | null =>
|
|
295
287
|
page && { ...page, results: pinRows(page.results, options.pinnedRows?.()) };
|
|
296
288
|
|
|
289
|
+
// The list the route answers, before the pinned rows lead it.
|
|
290
|
+
const listAnswered = (
|
|
291
|
+
query: Query,
|
|
292
|
+
signal?: AbortSignal,
|
|
293
|
+
): Promise<TableViewListResponse<T> | null> =>
|
|
294
|
+
source
|
|
295
|
+
? listFromSource(source, query, signal)
|
|
296
|
+
: api<TableViewListResponse<T>>(`${location}/list`, { query, signal });
|
|
297
|
+
|
|
297
298
|
// `signal` aborts the request once its answer is no longer wanted.
|
|
298
299
|
const list = async (
|
|
299
300
|
query: Query,
|
|
300
301
|
signal?: AbortSignal,
|
|
301
302
|
): Promise<TableViewListResponse<T> | null> =>
|
|
302
|
-
withPinnedRows(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
303
|
+
withPinnedRows(await listAnswered(query, signal));
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* The rows `query.offset` and `query.limit` name, in the order a table
|
|
307
|
+
* reading `span` rows at a time from the first shows them: the pinned
|
|
308
|
+
* rows lead each of its reads (a page), not the rows read here. The reads
|
|
309
|
+
* they fall in are read whole, then each pinned on its own. A source
|
|
310
|
+
* answering every row pins them all before its pages are cut: as `list`.
|
|
311
|
+
*/
|
|
312
|
+
const listAsShown = async (
|
|
313
|
+
query: Query,
|
|
314
|
+
span: number,
|
|
315
|
+
): Promise<TableViewListResponse<T> | null> => {
|
|
316
|
+
const pinnedRows = options.pinnedRows?.();
|
|
317
|
+
const offset = Number(query.offset) || 0;
|
|
318
|
+
const limit = Number(query.limit) || 0;
|
|
319
|
+
const isWholeSource = !!source && !source.capabilities.paginate;
|
|
320
|
+
if (!pinnedRows || span <= 0 || limit <= 0 || isWholeSource) {
|
|
321
|
+
return list(query);
|
|
322
|
+
}
|
|
323
|
+
const start = Math.floor(offset / span) * span;
|
|
324
|
+
const end = Math.ceil((offset + limit) / span) * span;
|
|
325
|
+
const page = await listAnswered({
|
|
326
|
+
...query,
|
|
327
|
+
offset: start,
|
|
328
|
+
limit: end - start,
|
|
329
|
+
});
|
|
330
|
+
if (!page) return null;
|
|
331
|
+
const shown = pinRowsBySpan(page.results, span, pinnedRows);
|
|
332
|
+
const from = offset - start;
|
|
333
|
+
return {
|
|
334
|
+
...page,
|
|
335
|
+
results: shown.slice(from, from + limit),
|
|
336
|
+
offset,
|
|
337
|
+
limit,
|
|
338
|
+
};
|
|
339
|
+
};
|
|
310
340
|
|
|
311
341
|
// A source answers its totals one list at a time.
|
|
312
342
|
const countBatch = async (
|
|
@@ -358,6 +388,7 @@ export function useTableRows<T extends Record<string, unknown>>(
|
|
|
358
388
|
sourceUrl,
|
|
359
389
|
isPeriodPending,
|
|
360
390
|
list,
|
|
391
|
+
listAsShown,
|
|
361
392
|
countBatch,
|
|
362
393
|
getRow,
|
|
363
394
|
};
|
package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts
CHANGED
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
resolveFormContainerTexts,
|
|
45
45
|
type TableViewFormKind,
|
|
46
46
|
} from "./utils/formTexts";
|
|
47
|
+
import { dmsOwnDrawer } from "../containers/context";
|
|
47
48
|
|
|
48
49
|
const DEFAULT_ROW_ID_KEY = "_id";
|
|
49
50
|
const DEFAULT_MODAL_SIZE = "xl";
|
|
@@ -95,6 +96,12 @@ type ConfirmedRun = NonNullable<ConfirmOptions["onConfirm"]>;
|
|
|
95
96
|
/** Options of a delete: from the archive, it is a permanent one. */
|
|
96
97
|
interface DeleteRowsOptions {
|
|
97
98
|
permanently?: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* The rows deleted, when the caller holds them: a dialog naming the row
|
|
101
|
+
* (`{name}`) reads its fields here first. A kanban card is not among the
|
|
102
|
+
* rows the table shows.
|
|
103
|
+
*/
|
|
104
|
+
rows?: Data[];
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
export const useTableRowActions = <T extends Data>(
|
|
@@ -249,24 +256,26 @@ export const useTableRowActions = <T extends Data>(
|
|
|
249
256
|
fetchUrl?: string,
|
|
250
257
|
submitDefaults?: Record<string, unknown>,
|
|
251
258
|
) => {
|
|
252
|
-
const drawer = openDrawer(
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
component: DmsForm,
|
|
257
|
-
componentOptions: {
|
|
258
|
-
...processedOptions,
|
|
259
|
-
...(fetchUrl && { fetchUrl }),
|
|
260
|
-
...(submitDefaults && { submitDefaults }),
|
|
261
|
-
componentId: `${config.componentId}-${componentIdSuffix}`,
|
|
262
|
-
pageId: pageIdSuffix,
|
|
259
|
+
const drawer = openDrawer(
|
|
260
|
+
dmsOwnDrawer({
|
|
261
|
+
title,
|
|
262
|
+
description,
|
|
263
263
|
containerId,
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
264
|
+
component: DmsForm,
|
|
265
|
+
componentOptions: {
|
|
266
|
+
...processedOptions,
|
|
267
|
+
...(fetchUrl && { fetchUrl }),
|
|
268
|
+
...(submitDefaults && { submitDefaults }),
|
|
269
|
+
componentId: `${config.componentId}-${componentIdSuffix}`,
|
|
270
|
+
pageId: pageIdSuffix,
|
|
271
|
+
containerId,
|
|
272
|
+
onSuccessCallback: () => {
|
|
273
|
+
drawer.close();
|
|
274
|
+
config.refreshCallback?.();
|
|
275
|
+
},
|
|
267
276
|
},
|
|
268
|
-
},
|
|
269
|
-
|
|
277
|
+
}),
|
|
278
|
+
);
|
|
270
279
|
return drawer;
|
|
271
280
|
};
|
|
272
281
|
|
|
@@ -654,38 +663,65 @@ export const useTableRowActions = <T extends Data>(
|
|
|
654
663
|
};
|
|
655
664
|
};
|
|
656
665
|
|
|
657
|
-
const
|
|
658
|
-
config.
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
666
|
+
const rowIdOf = (row: Data): string =>
|
|
667
|
+
String(getItemId(row, config.rowIdKey ?? DEFAULT_ROW_ID_KEY));
|
|
668
|
+
|
|
669
|
+
const findRowIn = (rows: Data[] | undefined, id: string) =>
|
|
670
|
+
rows?.find((row) => rowIdOf(row) === id);
|
|
671
|
+
|
|
672
|
+
// A row the table does not show (a kanban card, a row deleted from a
|
|
673
|
+
// custom card) is read by id: a `from` dialog asks the server instead.
|
|
674
|
+
const readRow = async (
|
|
675
|
+
id: string,
|
|
676
|
+
declared: ActionConfirm,
|
|
677
|
+
): Promise<Data | undefined> => {
|
|
678
|
+
const getRow = config.rowNavigation?.getRow;
|
|
679
|
+
if (!getRow || isConfirmFrom(declared)) return undefined;
|
|
680
|
+
return getRow(id).catch(() => undefined);
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
/** The one row a dialog words: the caller's, the table's, else read. */
|
|
684
|
+
const findConfirmRow = async (
|
|
685
|
+
id: string,
|
|
686
|
+
declared: ActionConfirm,
|
|
687
|
+
rows?: Data[],
|
|
688
|
+
): Promise<Data | undefined> =>
|
|
689
|
+
findRowIn(rows, id) ??
|
|
690
|
+
findRowIn(config.rowNavigation?.rows(), id) ??
|
|
691
|
+
(await readRow(id, declared));
|
|
664
692
|
|
|
665
693
|
/**
|
|
666
|
-
* What a fixed dialog words: the rows' count, and for one row
|
|
667
|
-
*
|
|
668
|
-
*
|
|
694
|
+
* What a fixed dialog words: the rows' count, and for one row its own
|
|
695
|
+
* fields too (`{name}`). The count wins a clash, it is what every bulk
|
|
696
|
+
* dialog may name.
|
|
669
697
|
*/
|
|
670
|
-
const confirmRowFields = (
|
|
698
|
+
const confirmRowFields = async (
|
|
699
|
+
ids: string[],
|
|
700
|
+
declared: ActionConfirm,
|
|
701
|
+
rows?: Data[],
|
|
702
|
+
): Promise<Data> => {
|
|
671
703
|
const count = { count: ids.length };
|
|
672
|
-
const row =
|
|
704
|
+
const row =
|
|
705
|
+
ids.length === 1
|
|
706
|
+
? await findConfirmRow(ids[0]!, declared, rows)
|
|
707
|
+
: undefined;
|
|
673
708
|
return row ? { ...row, ...count } : count;
|
|
674
709
|
};
|
|
675
710
|
|
|
676
711
|
/** Asks the declared confirmation of a bulk action, running it inside. */
|
|
677
|
-
const askDeclaredBulkConfirm = (
|
|
712
|
+
const askDeclaredBulkConfirm = async (
|
|
678
713
|
declared: ActionConfirm,
|
|
679
714
|
ids: string[],
|
|
680
715
|
fallback: Partial<ConfirmOptions>,
|
|
681
716
|
run: ConfirmedRun,
|
|
717
|
+
rows?: Data[],
|
|
682
718
|
): Promise<boolean> =>
|
|
683
719
|
confirmAction(declared, {
|
|
684
720
|
urlParams: {
|
|
685
721
|
id: ids[0],
|
|
686
722
|
[config.rowIdKey ?? DEFAULT_ROW_ID_KEY]: ids[0],
|
|
687
723
|
},
|
|
688
|
-
row: confirmRowFields(ids),
|
|
724
|
+
row: await confirmRowFields(ids, declared, rows),
|
|
689
725
|
fallback,
|
|
690
726
|
run,
|
|
691
727
|
});
|
|
@@ -703,11 +739,14 @@ export const useTableRowActions = <T extends Data>(
|
|
|
703
739
|
actionConfig: boolean | RowActionConfig | undefined,
|
|
704
740
|
action: ConfirmedBulkAction,
|
|
705
741
|
bulkConfig: BulkActionConfig,
|
|
742
|
+
rows?: Data[],
|
|
706
743
|
): Promise<boolean> => {
|
|
707
744
|
const fallback = bulkActionConfirm(action, ids.length, t);
|
|
708
745
|
const run = bulkActionRun(ids, actionConfig, bulkConfig);
|
|
709
746
|
const declared = declaredBulkConfirm(actionConfig, ids);
|
|
710
|
-
if (declared)
|
|
747
|
+
if (declared) {
|
|
748
|
+
return askDeclaredBulkConfirm(declared, ids, fallback, run, rows);
|
|
749
|
+
}
|
|
711
750
|
return confirm({ ...fallback, onConfirm: run });
|
|
712
751
|
};
|
|
713
752
|
|
|
@@ -746,7 +785,13 @@ export const useTableRowActions = <T extends Data>(
|
|
|
746
785
|
): Promise<boolean> => {
|
|
747
786
|
if (!normalizeActionConfig(deleteConfig).isEnabled) return false;
|
|
748
787
|
const action = options.permanently ? "deletePermanently" : "delete";
|
|
749
|
-
return confirmBulkAction(
|
|
788
|
+
return confirmBulkAction(
|
|
789
|
+
ids,
|
|
790
|
+
deleteConfig,
|
|
791
|
+
action,
|
|
792
|
+
DELETE_CONFIG,
|
|
793
|
+
options.rows,
|
|
794
|
+
);
|
|
750
795
|
};
|
|
751
796
|
|
|
752
797
|
const archiveRows = async (
|
|
@@ -54,6 +54,12 @@ export interface ResolvedTableView extends TableViewViewConfig {
|
|
|
54
54
|
|
|
55
55
|
export const VIEW_URL_KEY = "view";
|
|
56
56
|
const NO_VIEW = "";
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The open view as the preferences keep it: no view open is kept too, so the
|
|
60
|
+
* next visit does not fall back to the default view.
|
|
61
|
+
*/
|
|
62
|
+
export const keptViewId = (id: string | undefined): string => id ?? NO_VIEW;
|
|
57
63
|
const VIEW_NAME_FIELD = "label";
|
|
58
64
|
|
|
59
65
|
/**
|
|
@@ -128,11 +134,58 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
128
134
|
applySnapshot(viewSnapshot(view, defaults));
|
|
129
135
|
};
|
|
130
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Opens a view, or, for the one already open, closes it: back to the
|
|
139
|
+
* table's own default state without a view.
|
|
140
|
+
*/
|
|
141
|
+
const selectView = (id: string) =>
|
|
142
|
+
openView(id === activeViewId.value ? undefined : id);
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Back on the view kept from the last visit. The table's state is kept
|
|
146
|
+
* apart and restored as it was left, modified or not, but the search is
|
|
147
|
+
* not kept: the view's own comes back with it.
|
|
148
|
+
*/
|
|
149
|
+
const restoreKeptView = (id: string) => {
|
|
150
|
+
const view = findView(id);
|
|
151
|
+
activeViewId.value = view?.id;
|
|
152
|
+
if (view) state.globalFilter.value = view.search || undefined;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Opens a link followed on the table already shown as a linked arrival
|
|
157
|
+
* reads it: the view it names, else the default view, else the table's
|
|
158
|
+
* default listing state under the reader's own presentation.
|
|
159
|
+
*/
|
|
160
|
+
const openLinkedView = (query: Record<string, unknown>) => {
|
|
161
|
+
const named = readTableUrlKey(query, urlScope, VIEW_URL_KEY);
|
|
162
|
+
const view = named.isCleared
|
|
163
|
+
? undefined
|
|
164
|
+
: (findView(named.value) ?? findView(views?.defaultView));
|
|
165
|
+
if (view) return openView(view.id);
|
|
166
|
+
openDefaultListing();
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* No view open, the table's default listing state (filters, search, sort)
|
|
171
|
+
* under the reader's own presentation: what a link to no view opens.
|
|
172
|
+
*/
|
|
173
|
+
const openDefaultListing = () => {
|
|
174
|
+
const snapshot = viewSnapshot(undefined, defaults);
|
|
175
|
+
activeViewId.value = undefined;
|
|
176
|
+
state.columnFilters.value = snapshot.filters.map((filter) => ({
|
|
177
|
+
...filter,
|
|
178
|
+
}));
|
|
179
|
+
state.globalFilter.value = undefined;
|
|
180
|
+
state.sorting.value = snapshot.sorting;
|
|
181
|
+
state.pagination.value = { ...state.pagination.value, pageIndex: 0 };
|
|
182
|
+
};
|
|
183
|
+
|
|
131
184
|
// Read once, on arrival: a view named by the URL wins over the state kept
|
|
132
185
|
// from the last visit; a view the caller may not open falls back to the
|
|
133
186
|
// default one, with a word (on the client, once mounted). The user's views
|
|
134
187
|
// are kept in the browser, so a view that is not the module's is resolved
|
|
135
|
-
// once mounted.
|
|
188
|
+
// once mounted. An empty one (`?view=`) names no view.
|
|
136
189
|
const urlView = readTableUrlKey(route.query, urlScope, VIEW_URL_KEY);
|
|
137
190
|
const persistedViewId = preferences.get<string | undefined>(
|
|
138
191
|
"activeView",
|
|
@@ -141,7 +194,9 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
141
194
|
let isUrlViewUnavailable = false;
|
|
142
195
|
let isUrlViewPending = false;
|
|
143
196
|
let isOpenedOnArrival = false;
|
|
144
|
-
if (urlView.
|
|
197
|
+
if (urlView.isCleared) {
|
|
198
|
+
openDefaultListing();
|
|
199
|
+
} else if (urlView.value && findView(urlView.value)) {
|
|
145
200
|
openView(urlView.value);
|
|
146
201
|
isOpenedOnArrival = true;
|
|
147
202
|
} else if (urlView.value) {
|
|
@@ -150,7 +205,7 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
150
205
|
if (views?.defaultView) openView(views.defaultView);
|
|
151
206
|
isOpenedOnArrival = !!views?.defaultView;
|
|
152
207
|
} else {
|
|
153
|
-
|
|
208
|
+
restoreKeptView(persistedViewId);
|
|
154
209
|
}
|
|
155
210
|
|
|
156
211
|
// The browser-only state (order, density) is restored over the view opened
|
|
@@ -168,7 +223,7 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
168
223
|
} else if (isOpenedOnArrival) {
|
|
169
224
|
reapplyBrowserState();
|
|
170
225
|
} else if (persistedViewId && !activeViewId.value) {
|
|
171
|
-
|
|
226
|
+
restoreKeptView(persistedViewId);
|
|
172
227
|
}
|
|
173
228
|
};
|
|
174
229
|
|
|
@@ -189,7 +244,11 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
189
244
|
});
|
|
190
245
|
|
|
191
246
|
watch(activeViewId, (id) => {
|
|
192
|
-
preferences.set<string | undefined>(
|
|
247
|
+
preferences.set<string | undefined>(
|
|
248
|
+
"activeView",
|
|
249
|
+
keptViewId(id),
|
|
250
|
+
undefined,
|
|
251
|
+
);
|
|
193
252
|
});
|
|
194
253
|
|
|
195
254
|
const isModified = computed(() => {
|
|
@@ -265,11 +324,26 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
265
324
|
activeViewId.value = view.id;
|
|
266
325
|
};
|
|
267
326
|
|
|
268
|
-
/**
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
327
|
+
/**
|
|
328
|
+
* Deletes one of the user's views, once confirmed. Deleting the open one
|
|
329
|
+
* goes back to the table's own default state: what it set (its search
|
|
330
|
+
* above all) would otherwise stay applied with no view to show it.
|
|
331
|
+
*/
|
|
332
|
+
const deleteView = async (id: string) => {
|
|
333
|
+
const view = findView(id);
|
|
334
|
+
if (!view?.isUserView) return;
|
|
335
|
+
const isConfirmed = await confirm({
|
|
336
|
+
title: t("dms.table.views.delete_title"),
|
|
337
|
+
description: t("dms.table.views.delete_description", {
|
|
338
|
+
view: view.label,
|
|
339
|
+
}),
|
|
340
|
+
icon: "i-ph-trash",
|
|
341
|
+
color: "error",
|
|
342
|
+
confirmLabel: t("dms.table.views.delete"),
|
|
343
|
+
});
|
|
344
|
+
if (!isConfirmed) return;
|
|
345
|
+
writeUserViews(storedUserViews.value.filter((stored) => stored.id !== id));
|
|
346
|
+
if (activeViewId.value === id) openView(undefined);
|
|
273
347
|
};
|
|
274
348
|
|
|
275
349
|
return {
|
|
@@ -279,6 +353,8 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
279
353
|
isModified,
|
|
280
354
|
canSaveViews: !!views?.userViews,
|
|
281
355
|
openView,
|
|
356
|
+
selectView,
|
|
357
|
+
openLinkedView,
|
|
282
358
|
resetView,
|
|
283
359
|
saveView,
|
|
284
360
|
saveAsNewView,
|