@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
|
@@ -12,9 +12,11 @@ import { readFetchNotFound } from "../../build/utils/fetchNotFound";
|
|
|
12
12
|
import DmsBlockInlineStatus from "../../build/components/blocks/BlockInlineStatus.vue";
|
|
13
13
|
import {
|
|
14
14
|
ABSENT_VALUE_PARTS,
|
|
15
|
+
formatValue,
|
|
15
16
|
formatValueParts,
|
|
16
17
|
} from "../../composables/chart/formatValue";
|
|
17
18
|
import { resolveSparklineAccent } from "../../composables/chart/resolveSparklineAccent";
|
|
19
|
+
import { usePeriodComparisonDisplay } from "../../build/composables/period/periodComparison";
|
|
18
20
|
import type {
|
|
19
21
|
KpiCardResponse,
|
|
20
22
|
ValueFormat,
|
|
@@ -34,16 +36,21 @@ interface Props extends DefaultComponentProps {
|
|
|
34
36
|
fetchUrl?: string;
|
|
35
37
|
fetchUrlMethod?: string;
|
|
36
38
|
periodScope?: string;
|
|
39
|
+
/** Topics whose events make the card read `fetchUrl` again. */
|
|
40
|
+
realtimeTopic?: string | string[];
|
|
37
41
|
valueFormat?: ValueFormat;
|
|
38
42
|
currencyCode?: string;
|
|
39
43
|
valuePrecision?: ValuePrecision;
|
|
40
44
|
showDelta?: boolean;
|
|
45
|
+
/** "vs 1 234" next to the change, when the data carries `previousValue`. */
|
|
46
|
+
showPreviousValue?: boolean;
|
|
41
47
|
showSparkline?: boolean;
|
|
42
48
|
sparklineAccent?: string;
|
|
43
49
|
invert?: boolean;
|
|
44
50
|
compareLabel?: string;
|
|
45
51
|
staticValue?: number;
|
|
46
52
|
staticDelta?: number;
|
|
53
|
+
staticPreviousValue?: number;
|
|
47
54
|
staticSparkline?: number[];
|
|
48
55
|
}
|
|
49
56
|
|
|
@@ -54,6 +61,7 @@ const POSITIVE_SPARKLINE_ACCENT = "primary";
|
|
|
54
61
|
const props = withDefaults(defineProps<Props>(), {
|
|
55
62
|
variant: "default",
|
|
56
63
|
showDelta: true,
|
|
64
|
+
showPreviousValue: true,
|
|
57
65
|
showSparkline: false,
|
|
58
66
|
valueFormat: "compact",
|
|
59
67
|
sparklineAccent: "auto",
|
|
@@ -74,6 +82,7 @@ const staticData = computed<KpiCardResponse | null>(() => {
|
|
|
74
82
|
return {
|
|
75
83
|
value: props.staticValue,
|
|
76
84
|
delta: props.staticDelta,
|
|
85
|
+
previousValue: props.staticPreviousValue,
|
|
77
86
|
sparkline: props.staticSparkline,
|
|
78
87
|
};
|
|
79
88
|
});
|
|
@@ -83,12 +92,21 @@ const { data, isLoading, error, refresh } = useChartFetch<KpiCardResponse>({
|
|
|
83
92
|
fetchUrlMethod: props.fetchUrlMethod,
|
|
84
93
|
routeParams: () => props.routeParams,
|
|
85
94
|
periodScope: props.periodScope,
|
|
95
|
+
realtimeTopic: props.realtimeTopic,
|
|
86
96
|
staticData: () => staticData.value,
|
|
87
97
|
watchSource: () => watchKey.value,
|
|
88
98
|
});
|
|
89
99
|
|
|
100
|
+
// With no comparison selected on the scope, a change the data still carries
|
|
101
|
+
// (a static one, a route ignoring `comparison`) is not shown either.
|
|
102
|
+
const comparison = usePeriodComparisonDisplay(props.periodScope);
|
|
90
103
|
const value = computed(() => data.value?.value);
|
|
91
|
-
const delta = computed(() =>
|
|
104
|
+
const delta = computed(() =>
|
|
105
|
+
comparison.isOff.value ? null : (data.value?.delta ?? null),
|
|
106
|
+
);
|
|
107
|
+
const previousValue = computed(() =>
|
|
108
|
+
comparison.isOff.value ? undefined : data.value?.previousValue,
|
|
109
|
+
);
|
|
92
110
|
const sparkline = computed(() => data.value?.sparkline ?? []);
|
|
93
111
|
|
|
94
112
|
const formattedParts = computed(() =>
|
|
@@ -103,6 +121,19 @@ const formattedParts = computed(() =>
|
|
|
103
121
|
),
|
|
104
122
|
);
|
|
105
123
|
|
|
124
|
+
// Formatted as the value is, unit and currency included: "vs €42,900".
|
|
125
|
+
const previousText = computed(() => {
|
|
126
|
+
if (!props.showPreviousValue || previousValue.value === undefined) return "";
|
|
127
|
+
const formatted = formatValue(
|
|
128
|
+
previousValue.value,
|
|
129
|
+
props.valueFormat,
|
|
130
|
+
locale.value,
|
|
131
|
+
props.currencyCode,
|
|
132
|
+
props.valuePrecision,
|
|
133
|
+
);
|
|
134
|
+
return t("dms.chart.versus_value", { value: formatted });
|
|
135
|
+
});
|
|
136
|
+
|
|
106
137
|
const isFirstLoad = computed(() => isLoading.value && data.value === null);
|
|
107
138
|
// v2 error state: a failed first load shows a message and a retry button in
|
|
108
139
|
// place of the value; a failed refetch keeps the last value.
|
|
@@ -116,13 +147,37 @@ const notFound = computed(() =>
|
|
|
116
147
|
// A refetch keeps the stale value on screen, faded, instead of a skeleton.
|
|
117
148
|
const isRefreshing = computed(() => isLoading.value && data.value !== null);
|
|
118
149
|
const showTrend = computed(() => props.showDelta && delta.value !== null);
|
|
150
|
+
// The trend's line is held while the change is still to come, so toggling a
|
|
151
|
+
// comparison never moves the footnote, and dropped with no comparison at all.
|
|
152
|
+
const showTrendLine = computed(
|
|
153
|
+
() => props.showDelta && !comparison.isOff.value,
|
|
154
|
+
);
|
|
119
155
|
const hasSparkline = computed(
|
|
120
156
|
() => props.showSparkline && sparkline.value.length > 0,
|
|
121
157
|
);
|
|
158
|
+
// The comparison label names the period the change is measured against, so
|
|
159
|
+
// it shows with the change only: an explicit `compareLabel` wins, then the
|
|
160
|
+
// description, then the label of the comparison the scope selects. With no
|
|
161
|
+
// change (no comparison selected, or none in the data) only the description.
|
|
122
162
|
const footnote = computed(() => {
|
|
123
|
-
const
|
|
124
|
-
|
|
163
|
+
const isComparing = delta.value !== null;
|
|
164
|
+
const text = (isComparing && props.compareLabel) || props.description;
|
|
165
|
+
if (text) return processI18n(text);
|
|
166
|
+
return isComparing ? (comparison.label.value ?? "") : "";
|
|
125
167
|
});
|
|
168
|
+
// Known before any data: the skeleton draws it, and the loaded card holds it
|
|
169
|
+
// empty while the change is still to come, so toggling a comparison moves
|
|
170
|
+
// nothing. With no comparison selected, the trend's line goes and only a
|
|
171
|
+
// description keeps this one.
|
|
172
|
+
const hasFootnoteLine = computed(
|
|
173
|
+
() =>
|
|
174
|
+
!!props.description ||
|
|
175
|
+
(!comparison.isOff.value &&
|
|
176
|
+
!!(props.compareLabel || comparison.label.value)),
|
|
177
|
+
);
|
|
178
|
+
const holdsFootnoteLine = computed(
|
|
179
|
+
() => !footnote.value && hasFootnoteLine.value,
|
|
180
|
+
);
|
|
126
181
|
|
|
127
182
|
const resolvedSparklineAccent = computed(() =>
|
|
128
183
|
resolveSparklineAccent(
|
|
@@ -160,23 +215,42 @@ const isStat = computed(() => props.variant === "stat");
|
|
|
160
215
|
{{ formattedParts.unit }}
|
|
161
216
|
</span>
|
|
162
217
|
</template>
|
|
163
|
-
<!--
|
|
164
|
-
|
|
165
|
-
|
|
218
|
+
<!-- While the change is still to come, the trend's line keeps its
|
|
219
|
+
place: toggling a comparison never resizes the card. With no
|
|
220
|
+
comparison selected it goes. So does the sparkline's box keep its
|
|
221
|
+
place, from the first load on: the aside is taller than the value
|
|
222
|
+
column with it, and the card would grow. -->
|
|
223
|
+
<template v-if="showTrendLine || previousText || showSparkline" #aside>
|
|
166
224
|
<div class="ml-auto grid shrink-0 justify-items-end gap-1">
|
|
167
|
-
<
|
|
168
|
-
v-if="
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
225
|
+
<div
|
|
226
|
+
v-if="showTrendLine || previousText"
|
|
227
|
+
class="flex h-4 items-center gap-1.5"
|
|
228
|
+
>
|
|
229
|
+
<DmsTrendBadge
|
|
230
|
+
v-if="showTrend"
|
|
231
|
+
:delta="delta"
|
|
232
|
+
:invert="invert"
|
|
233
|
+
variant="text"
|
|
234
|
+
/>
|
|
235
|
+
<span
|
|
236
|
+
v-if="previousText"
|
|
237
|
+
class="text-dimmed text-xs whitespace-nowrap tabular-nums"
|
|
238
|
+
>
|
|
239
|
+
{{ previousText }}
|
|
240
|
+
</span>
|
|
241
|
+
</div>
|
|
242
|
+
<div v-if="showSparkline" class="h-6 w-16">
|
|
175
243
|
<DmsSparkline
|
|
244
|
+
v-if="hasSparkline"
|
|
176
245
|
:values="sparkline"
|
|
177
246
|
:accent="resolvedSparklineAccent"
|
|
178
247
|
:aria-label="processI18n(title)"
|
|
179
248
|
/>
|
|
249
|
+
<USkeleton
|
|
250
|
+
v-else-if="isFirstLoad"
|
|
251
|
+
:aria-label="t('dms.a11y.loading')"
|
|
252
|
+
class="size-full"
|
|
253
|
+
/>
|
|
180
254
|
</div>
|
|
181
255
|
</div>
|
|
182
256
|
</template>
|
|
@@ -236,14 +310,14 @@ const isStat = computed(() => props.variant === "stat");
|
|
|
236
310
|
<!-- The trend (a 24px line) and the footnote (16px) as the loaded card
|
|
237
311
|
lays them out; the footnote is a prop, so it is known up front. -->
|
|
238
312
|
<div
|
|
239
|
-
v-if="isFirstLoad && (
|
|
313
|
+
v-if="isFirstLoad && (showTrendLine || hasFootnoteLine || showSparkline)"
|
|
240
314
|
class="mt-auto flex items-end gap-2"
|
|
241
315
|
>
|
|
242
316
|
<div class="grid">
|
|
243
|
-
<div v-if="
|
|
317
|
+
<div v-if="showTrendLine" class="flex h-6 items-center">
|
|
244
318
|
<USkeleton :aria-label="t('dms.a11y.loading')" class="h-3 w-[58px]" />
|
|
245
319
|
</div>
|
|
246
|
-
<div v-if="
|
|
320
|
+
<div v-if="hasFootnoteLine" class="flex h-4 items-center">
|
|
247
321
|
<USkeleton :aria-label="t('dms.a11y.loading')" class="h-2.5 w-28" />
|
|
248
322
|
</div>
|
|
249
323
|
</div>
|
|
@@ -254,20 +328,37 @@ const isStat = computed(() => props.variant === "stat");
|
|
|
254
328
|
/>
|
|
255
329
|
</div>
|
|
256
330
|
<!-- The trend's line holds its place, as in the skeleton, while there is
|
|
257
|
-
no change to show
|
|
258
|
-
|
|
331
|
+
no change to show yet: no "0%" stand-in, and toggling a comparison
|
|
332
|
+
never moves the footnote. With no comparison selected on the scope,
|
|
333
|
+
the line goes, and the comparison label with it. -->
|
|
259
334
|
<div
|
|
260
|
-
v-else-if="
|
|
335
|
+
v-else-if="
|
|
336
|
+
!hasError &&
|
|
337
|
+
(showTrendLine ||
|
|
338
|
+
previousText ||
|
|
339
|
+
footnote ||
|
|
340
|
+
holdsFootnoteLine ||
|
|
341
|
+
hasSparkline)
|
|
342
|
+
"
|
|
261
343
|
class="mt-auto flex items-end gap-2"
|
|
262
344
|
>
|
|
263
345
|
<div class="min-w-0">
|
|
264
|
-
<div
|
|
346
|
+
<div
|
|
347
|
+
v-if="showTrendLine || previousText"
|
|
348
|
+
class="flex h-6 min-w-0 items-center gap-2"
|
|
349
|
+
>
|
|
265
350
|
<DmsTrendBadge
|
|
266
351
|
v-if="showTrend"
|
|
267
352
|
:delta="delta"
|
|
268
353
|
:invert="invert"
|
|
269
354
|
variant="text"
|
|
270
355
|
/>
|
|
356
|
+
<span
|
|
357
|
+
v-if="previousText"
|
|
358
|
+
class="text-dimmed truncate text-xs tabular-nums"
|
|
359
|
+
>
|
|
360
|
+
{{ previousText }}
|
|
361
|
+
</span>
|
|
271
362
|
</div>
|
|
272
363
|
<p
|
|
273
364
|
v-if="footnote"
|
|
@@ -275,6 +366,11 @@ const isStat = computed(() => props.variant === "stat");
|
|
|
275
366
|
>
|
|
276
367
|
{{ footnote }}
|
|
277
368
|
</p>
|
|
369
|
+
<span
|
|
370
|
+
v-else-if="holdsFootnoteLine"
|
|
371
|
+
class="block h-4"
|
|
372
|
+
aria-hidden="true"
|
|
373
|
+
/>
|
|
278
374
|
</div>
|
|
279
375
|
<div
|
|
280
376
|
v-if="hasSparkline"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { readableNumberFormat } from "#dms-core/app/utils/formatter";
|
|
2
3
|
import { computed } from "vue";
|
|
3
4
|
import { tv } from "tailwind-variants";
|
|
4
5
|
|
|
@@ -176,7 +177,7 @@ const legendEntries = computed(() =>
|
|
|
176
177
|
);
|
|
177
178
|
|
|
178
179
|
const formatNumber = (amount: number): string =>
|
|
179
|
-
|
|
180
|
+
readableNumberFormat(locale.value).format(amount);
|
|
180
181
|
|
|
181
182
|
const valueText = computed(() => {
|
|
182
183
|
if (props.valueLabel) return props.valueLabel;
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
type CalendarRange,
|
|
26
26
|
type CalendarRangeAnchor,
|
|
27
27
|
calendarToRange,
|
|
28
|
+
formatPeriodDays,
|
|
28
29
|
formatPeriodRange,
|
|
29
30
|
formatRelativeInstant,
|
|
30
31
|
limitCalendarRange,
|
|
@@ -35,6 +36,9 @@ import PeriodRangeCalendar from "../../build/components/period/PeriodRangeCalend
|
|
|
35
36
|
import { useUserRegionalPreferences } from "#dms-core/app/composables/user/useUserRegionalPreferences";
|
|
36
37
|
import { usePageLive } from "../../composables/live/usePageLive";
|
|
37
38
|
import { useLivePeriodSpan } from "../../build/composables/live/useLivePeriodSpan";
|
|
39
|
+
import { usePageToolbar } from "../../build/composables/page-toolbar/pageToolbarContext";
|
|
40
|
+
import { toolbarControlHeightClass } from "../../build/composables/page-toolbar/toolbarControlHeight";
|
|
41
|
+
import { PRESET_SHORT_LABELS } from "../../build/composables/period/periodShortLabels";
|
|
38
42
|
|
|
39
43
|
interface Props extends DefaultComponentProps {
|
|
40
44
|
id: string;
|
|
@@ -64,9 +68,12 @@ const RELATIVE_RANGE_LABEL: NonNullable<Props["rangeLabel"]> = "relative";
|
|
|
64
68
|
// Two months side by side from this width; one below (design @container 760px).
|
|
65
69
|
const TWO_MONTHS_QUERY = "(min-width: 768px)";
|
|
66
70
|
|
|
71
|
+
type PeriodSelectorSize = NonNullable<Props["size"]>;
|
|
72
|
+
const DEFAULT_SIZE: PeriodSelectorSize = "sm";
|
|
73
|
+
|
|
67
74
|
const props = withDefaults(defineProps<Props>(), {
|
|
68
75
|
align: "right",
|
|
69
|
-
size:
|
|
76
|
+
size: undefined,
|
|
70
77
|
showRangeLabel: true,
|
|
71
78
|
rangeLabel: "absolute",
|
|
72
79
|
variant: "default",
|
|
@@ -78,16 +85,14 @@ const ALIGN_CLASSES: Record<NonNullable<Props["align"]>, string> = {
|
|
|
78
85
|
right: "justify-end",
|
|
79
86
|
};
|
|
80
87
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
"this-quarter": "QTD",
|
|
90
|
-
ytd: "YTD",
|
|
88
|
+
// The window beside the controls, at each size of a toolbar holding the
|
|
89
|
+
// selector; outside one it keeps its size whatever the controls'.
|
|
90
|
+
const RANGE_LABEL_SIZE_CLASSES: Record<PeriodSelectorSize, string> = {
|
|
91
|
+
xs: "text-[11px]",
|
|
92
|
+
sm: "text-[11.5px]",
|
|
93
|
+
md: "text-[12.5px]",
|
|
94
|
+
lg: "text-[13px]",
|
|
95
|
+
xl: "text-sm",
|
|
91
96
|
};
|
|
92
97
|
|
|
93
98
|
/** Rolling windows first, then calendar periods (design preset groups). */
|
|
@@ -113,6 +118,19 @@ useWatch(props.watchActions || [], props.componentId);
|
|
|
113
118
|
|
|
114
119
|
const { t, locale } = useI18n();
|
|
115
120
|
|
|
121
|
+
// In a toolbar, the selector takes its size, and its window scales with it.
|
|
122
|
+
const toolbar = usePageToolbar();
|
|
123
|
+
const size = computed<PeriodSelectorSize>(
|
|
124
|
+
() => props.size ?? toolbar?.size.value ?? DEFAULT_SIZE,
|
|
125
|
+
);
|
|
126
|
+
const rangeLabelSizeClass = computed(
|
|
127
|
+
() => RANGE_LABEL_SIZE_CLASSES[toolbar ? size.value : DEFAULT_SIZE],
|
|
128
|
+
);
|
|
129
|
+
// In a toolbar, its buttons stand as tall as the segments and the live pill.
|
|
130
|
+
const controlHeightClass = computed(() =>
|
|
131
|
+
toolbar ? toolbarControlHeightClass(size.value) : "",
|
|
132
|
+
);
|
|
133
|
+
|
|
116
134
|
// A paused live page keeps its period where it was: a relative preset
|
|
117
135
|
// sliding every minute would refetch every block reading it.
|
|
118
136
|
const { isPaused: isPagePaused } = usePageLive();
|
|
@@ -171,6 +189,12 @@ function presetLabel(preset: PeriodPreset): string {
|
|
|
171
189
|
);
|
|
172
190
|
}
|
|
173
191
|
|
|
192
|
+
/** The preset's short code in the user's language; custom has none. */
|
|
193
|
+
function presetShortLabel(preset: PeriodPreset): string | undefined {
|
|
194
|
+
const fallback = PRESET_SHORT_LABELS[preset];
|
|
195
|
+
return fallback && t(`dms.period.short_presets.${preset}`, fallback);
|
|
196
|
+
}
|
|
197
|
+
|
|
174
198
|
function comparisonLabel(comparison: PeriodComparison): string {
|
|
175
199
|
return (
|
|
176
200
|
props.comparisonLabels?.[comparison] ??
|
|
@@ -201,7 +225,7 @@ interface PresetEntry {
|
|
|
201
225
|
|
|
202
226
|
function presetEntry(preset: PeriodPreset): PresetEntry {
|
|
203
227
|
const label = presetLabel(preset);
|
|
204
|
-
const short =
|
|
228
|
+
const short = presetShortLabel(preset);
|
|
205
229
|
return { preset, label, short: short === label ? undefined : short };
|
|
206
230
|
}
|
|
207
231
|
|
|
@@ -414,11 +438,15 @@ function comparisonKeyClass(active: boolean): string {
|
|
|
414
438
|
return active ? COMPARISON_KEY_ACTIVE : COMPARISON_KEY_NONE;
|
|
415
439
|
}
|
|
416
440
|
|
|
417
|
-
|
|
418
|
-
|
|
441
|
+
// The year of a comparison range is noise when it is the main one's, in the
|
|
442
|
+
// reader's calendar, on the server as in the browser.
|
|
443
|
+
function isSameYear(compare: PeriodRange, main: PeriodRange): boolean {
|
|
419
444
|
const clock = readerClock();
|
|
420
|
-
|
|
421
|
-
|
|
445
|
+
return clock.wall(compare.to).year === clock.wall(main.to).year;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function compareRangeText(compare: PeriodRange, main: PeriodRange): string {
|
|
449
|
+
return formatPeriodRange(compare, locale.value, !isSameYear(compare, main));
|
|
422
450
|
}
|
|
423
451
|
|
|
424
452
|
const compareRangeLabel = computed(() => {
|
|
@@ -426,40 +454,82 @@ const compareRangeLabel = computed(() => {
|
|
|
426
454
|
return compareRange && range ? compareRangeText(compareRange, range) : null;
|
|
427
455
|
});
|
|
428
456
|
|
|
457
|
+
// A menu names the compared window itself ("vs 16 – 17 Oct"), so the range
|
|
458
|
+
// label beside it keeps the current window only: said twice, the comparison
|
|
459
|
+
// wrapped the toolbar. The option's name moves to its tooltip and its menu.
|
|
460
|
+
const rangeLabelCompare = computed(() =>
|
|
461
|
+
isComparisonMenu.value ? null : compareRangeLabel.value,
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
const menuCompareDays = computed(() => {
|
|
465
|
+
const { compareRange, range } = period.state.value;
|
|
466
|
+
if (!compareRange || !range) return null;
|
|
467
|
+
return formatPeriodDays(
|
|
468
|
+
compareRange,
|
|
469
|
+
locale.value,
|
|
470
|
+
!isSameYear(compareRange, range),
|
|
471
|
+
);
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
const comparisonMenuLabel = computed(() => {
|
|
475
|
+
const days = menuCompareDays.value;
|
|
476
|
+
if (!hasComparison.value || !days) {
|
|
477
|
+
return comparisonLabel(period.comparison.value);
|
|
478
|
+
}
|
|
479
|
+
return `${t("dms.period.popover.vs", "vs")} ${days}`;
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
const comparisonMenuAria = computed(() => {
|
|
483
|
+
const name = `${t("dms.period.comparison_aria", "Comparison")}: ${comparisonLabel(period.comparison.value)}`;
|
|
484
|
+
const days = hasComparison.value ? menuCompareDays.value : null;
|
|
485
|
+
return days ? `${name}, ${days}` : name;
|
|
486
|
+
});
|
|
487
|
+
|
|
429
488
|
// ── Segmented variant ──────────────────────────────────────────────────
|
|
430
489
|
function segmentLabel(preset: PeriodPreset): string {
|
|
431
490
|
return (
|
|
432
491
|
props.presetLabels?.[preset] ??
|
|
433
|
-
|
|
492
|
+
presetShortLabel(preset) ??
|
|
434
493
|
presetLabel(preset)
|
|
435
494
|
);
|
|
436
495
|
}
|
|
437
496
|
|
|
438
497
|
interface SegmentItem {
|
|
439
498
|
label: string;
|
|
499
|
+
ariaLabel: string;
|
|
440
500
|
value: string;
|
|
441
501
|
icon?: string;
|
|
442
502
|
}
|
|
443
503
|
|
|
444
|
-
function
|
|
504
|
+
function customRangeText(): string | null {
|
|
445
505
|
const { range } = period.state.value;
|
|
446
506
|
return isCustomActive.value && range
|
|
447
507
|
? formatPeriodRange(range, locale.value, false)
|
|
448
|
-
:
|
|
508
|
+
: null;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// In a toolbar the custom segment is its calendar icon alone, like the short
|
|
512
|
+
// codes beside it: the window beside them writes the range once chosen.
|
|
513
|
+
// Named in full either way.
|
|
514
|
+
function customSegment(): SegmentItem {
|
|
515
|
+
const range = customRangeText();
|
|
516
|
+
const name = presetLabel(CUSTOM_PRESET);
|
|
517
|
+
const visible = toolbar ? "" : (range ?? name);
|
|
518
|
+
return {
|
|
519
|
+
label: visible,
|
|
520
|
+
ariaLabel: range ? `${name}: ${range}` : name,
|
|
521
|
+
value: CUSTOM_PRESET,
|
|
522
|
+
icon: CALENDAR_ICON,
|
|
523
|
+
};
|
|
449
524
|
}
|
|
450
525
|
|
|
451
526
|
const segmentedItems = computed<SegmentItem[]>(() => {
|
|
452
527
|
const items: SegmentItem[] = listedPresets.value.map((preset) => ({
|
|
453
528
|
label: segmentLabel(preset),
|
|
529
|
+
ariaLabel: presetLabel(preset),
|
|
454
530
|
value: preset,
|
|
455
531
|
}));
|
|
456
|
-
if (isCustomEnabled.value)
|
|
457
|
-
items.push({
|
|
458
|
-
label: customSegmentLabel(),
|
|
459
|
-
value: CUSTOM_PRESET,
|
|
460
|
-
icon: CALENDAR_ICON,
|
|
461
|
-
});
|
|
462
|
-
}
|
|
532
|
+
if (isCustomEnabled.value) items.push(customSegment());
|
|
463
533
|
return items;
|
|
464
534
|
});
|
|
465
535
|
|
|
@@ -509,7 +579,11 @@ function onSegmentedClick(event: MouseEvent) {
|
|
|
509
579
|
:icon="CALENDAR_ICON"
|
|
510
580
|
:trailing-icon="isOpen ? CARET_UP_ICON : CARET_DOWN_ICON"
|
|
511
581
|
:aria-label="$t('dms.period.preset_aria', 'Preset')"
|
|
512
|
-
:class="[
|
|
582
|
+
:class="[
|
|
583
|
+
triggerOrder,
|
|
584
|
+
controlHeightClass,
|
|
585
|
+
isOpen && OPEN_TRIGGER_CLASS,
|
|
586
|
+
]"
|
|
513
587
|
:ui="{ trailingIcon: 'text-dimmed -me-0.5' }"
|
|
514
588
|
>
|
|
515
589
|
<span
|
|
@@ -729,7 +803,10 @@ function onSegmentedClick(event: MouseEvent) {
|
|
|
729
803
|
:aria-pressed="hasComparison"
|
|
730
804
|
:disabled="isComparisonDisabled"
|
|
731
805
|
class="order-2"
|
|
732
|
-
:class="
|
|
806
|
+
:class="[
|
|
807
|
+
controlHeightClass,
|
|
808
|
+
hasComparison ? 'text-toned' : 'text-dimmed',
|
|
809
|
+
]"
|
|
733
810
|
@click="toggleComparison"
|
|
734
811
|
>
|
|
735
812
|
<template #leading>
|
|
@@ -754,8 +831,10 @@ function onSegmentedClick(event: MouseEvent) {
|
|
|
754
831
|
:size="size"
|
|
755
832
|
:disabled="isComparisonDisabled"
|
|
756
833
|
:trailing-icon="CARET_DOWN_ICON"
|
|
757
|
-
:aria-label="
|
|
834
|
+
:aria-label="comparisonMenuAria"
|
|
835
|
+
:title="comparisonLabel(period.comparison.value)"
|
|
758
836
|
class="text-muted order-2"
|
|
837
|
+
:class="controlHeightClass"
|
|
759
838
|
:ui="{ trailingIcon: 'text-dimmed' }"
|
|
760
839
|
>
|
|
761
840
|
<template #leading>
|
|
@@ -765,7 +844,7 @@ function onSegmentedClick(event: MouseEvent) {
|
|
|
765
844
|
aria-hidden="true"
|
|
766
845
|
/>
|
|
767
846
|
</template>
|
|
768
|
-
{{
|
|
847
|
+
{{ comparisonMenuLabel }}
|
|
769
848
|
</UButton>
|
|
770
849
|
</UDropdownMenu>
|
|
771
850
|
|
|
@@ -775,16 +854,16 @@ function onSegmentedClick(event: MouseEvent) {
|
|
|
775
854
|
<span
|
|
776
855
|
v-if="showRangeLabel && rangeLabelText"
|
|
777
856
|
data-allow-mismatch="text"
|
|
778
|
-
class="text-dimmed hidden font-mono
|
|
779
|
-
:class="rangeLabelOrder"
|
|
857
|
+
class="dms-period-selector__range text-dimmed hidden font-mono font-medium whitespace-nowrap tabular-nums sm:inline"
|
|
858
|
+
:class="[rangeLabelOrder, rangeLabelSizeClass]"
|
|
780
859
|
>
|
|
781
860
|
<b class="text-toned font-[550]">{{ rangeLabelText }}</b>
|
|
782
|
-
<template v-if="
|
|
861
|
+
<template v-if="rangeLabelCompare">
|
|
783
862
|
·
|
|
784
863
|
<span class="text-(--dms-chart-2)">
|
|
785
864
|
{{ $t("dms.period.popover.vs", "vs") }}
|
|
786
865
|
</span>
|
|
787
|
-
{{
|
|
866
|
+
{{ rangeLabelCompare }}
|
|
788
867
|
</template>
|
|
789
868
|
</span>
|
|
790
869
|
</div>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed } from "vue";
|
|
2
|
+
import { computed, useTemplateRef } from "vue";
|
|
3
3
|
import { tv } from "tailwind-variants";
|
|
4
4
|
import {
|
|
5
5
|
type FormKind,
|
|
6
6
|
saveBarState,
|
|
7
7
|
} from "../../build/composables/form/formFooter";
|
|
8
8
|
import { trackFloatingSaveBar } from "../../build/composables/form/floatingSaveBars";
|
|
9
|
+
import { trackToasterClearance } from "../../build/composables/form/toasterClearance";
|
|
9
10
|
import DmsUnsavedStatus from "../../build/components/form/UnsavedStatus.vue";
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -64,6 +65,7 @@ const emit = defineEmits<{
|
|
|
64
65
|
|
|
65
66
|
const SECONDARY_LABELS: Record<FormKind, string> = {
|
|
66
67
|
record: "dms.save_bar.discard",
|
|
68
|
+
create: "dms.save_bar.discard",
|
|
67
69
|
action: "dms.button.reset",
|
|
68
70
|
};
|
|
69
71
|
|
|
@@ -136,6 +138,15 @@ const buttons = computed(() => BUTTONS[props.variant]);
|
|
|
136
138
|
trackFloatingSaveBar(
|
|
137
139
|
() => props.variant === "floating" && !state.value.isHidden,
|
|
138
140
|
);
|
|
141
|
+
// Toasts stack in the bottom corner the floating bar and a drawer's footer
|
|
142
|
+
// band reach: they rise above the bar rather than cover its buttons. The
|
|
143
|
+
// band's surface is its root, the floating card its inner bar.
|
|
144
|
+
const rootElement = useTemplateRef<HTMLElement>("root");
|
|
145
|
+
const barElement = useTemplateRef<HTMLElement>("bar");
|
|
146
|
+
trackToasterClearance(
|
|
147
|
+
() => (props.variant === "floating" ? barElement.value : rootElement.value),
|
|
148
|
+
() => !state.value.isHidden,
|
|
149
|
+
);
|
|
139
150
|
|
|
140
151
|
// Empty while there is nothing to save: the hidden bar keeps its one line.
|
|
141
152
|
const statusText = computed(() => {
|
|
@@ -154,11 +165,12 @@ const saveText = computed(() => {
|
|
|
154
165
|
|
|
155
166
|
<template>
|
|
156
167
|
<div
|
|
168
|
+
ref="root"
|
|
157
169
|
:class="ui.root()"
|
|
158
170
|
:inert="state.isHidden || undefined"
|
|
159
171
|
:aria-hidden="state.isHidden || undefined"
|
|
160
172
|
>
|
|
161
|
-
<div :class="ui.bar()">
|
|
173
|
+
<div ref="bar" :class="ui.bar()">
|
|
162
174
|
<DmsUnsavedStatus
|
|
163
175
|
v-if="state.showsStatus"
|
|
164
176
|
:text="statusText"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from "vue";
|
|
3
3
|
import { tv } from "tailwind-variants";
|
|
4
|
+
import { keepHyphenatedWords } from "../../build/utils/eyebrow";
|
|
4
5
|
|
|
5
6
|
export type EyebrowTone = "dimmed" | "muted" | "primary" | "error";
|
|
6
7
|
export type EyebrowSize = "xs" | "sm";
|
|
@@ -56,6 +57,10 @@ const theme = tv({
|
|
|
56
57
|
},
|
|
57
58
|
});
|
|
58
59
|
|
|
60
|
+
const text = computed(() =>
|
|
61
|
+
props.label === undefined ? undefined : keepHyphenatedWords(props.label),
|
|
62
|
+
);
|
|
63
|
+
|
|
59
64
|
const classes = computed(() =>
|
|
60
65
|
theme({ tone: props.tone, size: props.size, truncate: props.truncate }),
|
|
61
66
|
);
|
|
@@ -63,6 +68,6 @@ const classes = computed(() =>
|
|
|
63
68
|
|
|
64
69
|
<template>
|
|
65
70
|
<component :is="props.as" :class="classes">
|
|
66
|
-
<slot>{{
|
|
71
|
+
<slot>{{ text }}</slot>
|
|
67
72
|
</component>
|
|
68
73
|
</template>
|
|
@@ -7,7 +7,13 @@ import { computed, ref } from "vue";
|
|
|
7
7
|
// Single-select, v-model'd. Implements the radiogroup keyboard pattern
|
|
8
8
|
// (roving tabindex + arrow/Home/End, skipping disabled segments).
|
|
9
9
|
interface SegItem {
|
|
10
|
+
/** The visible text; empty for an icon-only segment. */
|
|
10
11
|
label: string;
|
|
12
|
+
/**
|
|
13
|
+
* The segment's accessible name when the visible label does not say it
|
|
14
|
+
* all: a short code ("24H" for "Last 24 hours") or an icon alone.
|
|
15
|
+
*/
|
|
16
|
+
ariaLabel?: string;
|
|
11
17
|
value: string | number;
|
|
12
18
|
icon?: string;
|
|
13
19
|
/** Small mono counter after the label. */
|
|
@@ -149,6 +155,12 @@ function onKeydown(event: KeyboardEvent, index: number) {
|
|
|
149
155
|
}
|
|
150
156
|
|
|
151
157
|
// A track wider than its container (a phone, a narrow column) scrolls
|
|
158
|
+
// Every segment is named: its `ariaLabel`, else its label, else its value
|
|
159
|
+
// (an icon-only segment given neither still reads as something).
|
|
160
|
+
function itemAriaLabel(item: SegItem): string {
|
|
161
|
+
return item.ariaLabel || item.label || String(item.value);
|
|
162
|
+
}
|
|
163
|
+
|
|
152
164
|
// sideways inside its pill, or wraps with `overflow="wrap"`; the focus ring
|
|
153
165
|
// sits inside the segment so the scroll box never clips it. (No root-level
|
|
154
166
|
// template comment: it would break the class fallthrough.)
|
|
@@ -184,6 +196,7 @@ function itemClass(item: SegItem, index: number): (string | false)[] {
|
|
|
184
196
|
type="button"
|
|
185
197
|
role="radio"
|
|
186
198
|
:aria-checked="model === item.value"
|
|
199
|
+
:aria-label="itemAriaLabel(item)"
|
|
187
200
|
:disabled="isDisabled(index)"
|
|
188
201
|
:tabindex="index === focusIndex ? 0 : -1"
|
|
189
202
|
class="focus-visible:outline-primary inline-flex shrink-0 items-center gap-1.5 rounded-[6px] whitespace-nowrap transition-colors focus-visible:outline-2 focus-visible:-outline-offset-2"
|