@antelopejs/dms 0.8.3 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/data/module-store/catalog.json +71 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/implementations/dms/frontend-backend-proxies.d.ts +3 -0
- package/dist/implementations/dms/frontend-backend-proxies.js +39 -0
- package/dist/implementations/dms/frontend-backend-proxies.js.map +1 -0
- package/dist/implementations/dms/frontend-bootstrap.d.ts +1 -0
- package/dist/implementations/dms/frontend-bootstrap.js +13 -0
- package/dist/implementations/dms/frontend-bootstrap.js.map +1 -1
- package/dist/implementations/dms/frontend-modules.d.ts +6 -2
- package/dist/implementations/dms/frontend-modules.js +37 -8
- package/dist/implementations/dms/frontend-modules.js.map +1 -1
- package/dist/implementations/dms/frontend-ready.d.ts +8 -0
- package/dist/implementations/dms/frontend-ready.js +34 -0
- package/dist/implementations/dms/frontend-ready.js.map +1 -0
- package/dist/implementations/dms/manifest.d.ts +7 -0
- package/dist/implementations/dms/manifest.js +13 -0
- package/dist/implementations/dms/manifest.js.map +1 -1
- package/dist/implementations/dms/menu-categories.d.ts +16 -0
- package/dist/implementations/dms/menu-categories.js +30 -0
- package/dist/implementations/dms/menu-categories.js.map +1 -0
- package/dist/implementations/dms/nav-badges.d.ts +0 -8
- package/dist/implementations/dms/nav-badges.js +0 -16
- package/dist/implementations/dms/nav-badges.js.map +1 -1
- package/dist/implementations/dms/page.d.ts +13 -5
- package/dist/implementations/dms/page.js +72 -41
- package/dist/implementations/dms/page.js.map +1 -1
- package/dist/implementations/dms-html-render/index.js +5 -2
- package/dist/implementations/dms-html-render/index.js.map +1 -1
- package/dist/implementations/dms-notifications/index.d.ts +4 -3
- package/dist/implementations/dms-notifications/index.js +14 -6
- package/dist/implementations/dms-notifications/index.js.map +1 -1
- package/dist/pages/settings/settings-index.js +3 -2
- package/dist/pages/settings/settings-index.js.map +1 -1
- package/dist/pages/settings/users/invites.js +2 -2
- package/dist/pages/settings/users/invites.js.map +1 -1
- package/dist/pages/settings/users/member-change-notifications.js +17 -6
- package/dist/pages/settings/users/member-change-notifications.js.map +1 -1
- package/dist/pages/settings/users/member-invite-form.d.ts +3 -2
- package/dist/pages/settings/users/member-invite-form.js +7 -4
- package/dist/pages/settings/users/member-invite-form.js.map +1 -1
- package/dist/pages/settings/users/member-roles-field.js +2 -1
- package/dist/pages/settings/users/member-roles-field.js.map +1 -1
- package/dist/pages/settings/users/members.js +4 -1
- package/dist/pages/settings/users/members.js.map +1 -1
- package/dist/pages/settings/users/notifications.d.ts +6 -0
- package/dist/pages/settings/users/notifications.js +23 -15
- package/dist/pages/settings/users/notifications.js.map +1 -1
- package/dist/pages/settings/users/profile.js +1 -1
- package/dist/pages/settings/users/profile.js.map +1 -1
- package/dist/pages/settings/users/region-form.d.ts +1 -1
- package/dist/pages/settings/users/region-form.js +2 -1
- package/dist/pages/settings/users/region-form.js.map +1 -1
- package/dist/pages/settings/users/role-editor-store.d.ts +3 -1
- package/dist/pages/settings/users/role-editor-store.js +17 -4
- package/dist/pages/settings/users/role-editor-store.js.map +1 -1
- package/dist/pages/settings/users/roles.js +1 -0
- package/dist/pages/settings/users/roles.js.map +1 -1
- package/dist/realtime/registry.d.ts +1 -0
- package/dist/realtime/registry.js +5 -0
- package/dist/realtime/registry.js.map +1 -1
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.js +1 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/realtime.d.ts +5 -1
- package/dist/routes/realtime.js +16 -9
- package/dist/routes/realtime.js.map +1 -1
- package/dist/routes/store.d.ts +21 -0
- package/dist/routes/store.js +130 -0
- package/dist/routes/store.js.map +1 -0
- package/dist/store/activity.d.ts +2 -0
- package/dist/store/activity.js +12 -0
- package/dist/store/activity.js.map +1 -0
- package/dist/store/cache.d.ts +8 -0
- package/dist/store/cache.js +29 -0
- package/dist/store/cache.js.map +1 -0
- package/dist/store/catalog.d.ts +4 -0
- package/dist/store/catalog.js +69 -0
- package/dist/store/catalog.js.map +1 -0
- package/dist/store/compatibility.d.ts +3 -0
- package/dist/store/compatibility.js +45 -0
- package/dist/store/compatibility.js.map +1 -0
- package/dist/store/declarations.d.ts +18 -0
- package/dist/store/declarations.js +116 -0
- package/dist/store/declarations.js.map +1 -0
- package/dist/store/frontend-compatibility.d.ts +18 -0
- package/dist/store/frontend-compatibility.js +93 -0
- package/dist/store/frontend-compatibility.js.map +1 -0
- package/dist/store/interfaces.d.ts +6 -0
- package/dist/store/interfaces.js +126 -0
- package/dist/store/interfaces.js.map +1 -0
- package/dist/store/listing.d.ts +2 -0
- package/dist/store/listing.js +180 -0
- package/dist/store/listing.js.map +1 -0
- package/dist/store/operations.d.ts +8 -0
- package/dist/store/operations.js +254 -0
- package/dist/store/operations.js.map +1 -0
- package/dist/store/registry.d.ts +4 -0
- package/dist/store/registry.js +61 -0
- package/dist/store/registry.js.map +1 -0
- package/dist/store/requirements.d.ts +14 -0
- package/dist/store/requirements.js +81 -0
- package/dist/store/requirements.js.map +1 -0
- package/dist/store/runtime.d.ts +11 -0
- package/dist/store/runtime.js +78 -0
- package/dist/store/runtime.js.map +1 -0
- package/dist/store/tarball.d.ts +2 -0
- package/dist/store/tarball.js +80 -0
- package/dist/store/tarball.js.map +1 -0
- package/dist/store/types.d.ts +98 -0
- package/dist/store/types.js +3 -0
- package/dist/store/types.js.map +1 -0
- package/dist/test/integration/account-deletion.test.js +2 -2
- package/dist/test/integration/account-deletion.test.js.map +1 -1
- package/dist/test/integration/auth/auth-key-rotation.test.js +2 -1
- package/dist/test/integration/auth/auth-key-rotation.test.js.map +1 -1
- package/dist/test/integration/auth/current-password.test.js +2 -1
- package/dist/test/integration/auth/current-password.test.js.map +1 -1
- package/dist/test/integration/auth/session-revocation.test.js +2 -1
- package/dist/test/integration/auth/session-revocation.test.js.map +1 -1
- package/dist/test/integration/auth/sign-in-country.test.js +2 -1
- package/dist/test/integration/auth/sign-in-country.test.js.map +1 -1
- package/dist/test/integration/auth/two-factor-codes.test.js +2 -1
- package/dist/test/integration/auth/two-factor-codes.test.js.map +1 -1
- package/dist/test/integration/auth/two-factor-password.test.js +3 -2
- package/dist/test/integration/auth/two-factor-password.test.js.map +1 -1
- package/dist/test/integration/email-change.test.js +2 -1
- package/dist/test/integration/email-change.test.js.map +1 -1
- package/dist/test/integration/files/component-files.test.js +2 -2
- package/dist/test/integration/files/component-files.test.js.map +1 -1
- package/dist/test/integration/member-settings-access.test.js +8 -7
- package/dist/test/integration/member-settings-access.test.js.map +1 -1
- package/dist/test/integration/menu-access-events.test.d.ts +1 -0
- package/dist/test/integration/menu-access-events.test.js +148 -0
- package/dist/test/integration/menu-access-events.test.js.map +1 -0
- package/dist/test/integration/notification-inbox.test.js +4 -1
- package/dist/test/integration/notification-inbox.test.js.map +1 -1
- package/dist/test/integration/notification-locked-subjects.test.js +3 -2
- package/dist/test/integration/notification-locked-subjects.test.js.map +1 -1
- package/dist/test/integration/notification-unread-badge.test.js +51 -5
- package/dist/test/integration/notification-unread-badge.test.js.map +1 -1
- package/dist/test/integration/page-layout-route-guard.test.d.ts +1 -0
- package/dist/test/integration/page-layout-route-guard.test.js +56 -0
- package/dist/test/integration/page-layout-route-guard.test.js.map +1 -0
- package/dist/test/integration/permission-warnings.test.js +3 -2
- package/dist/test/integration/permission-warnings.test.js.map +1 -1
- package/dist/test/integration/removed-member-notifications.test.js +3 -2
- package/dist/test/integration/removed-member-notifications.test.js.map +1 -1
- package/dist/test/integration/role-delete-reassign.test.js +2 -1
- package/dist/test/integration/role-delete-reassign.test.js.map +1 -1
- package/dist/test/integration/role-permission-ancestors.test.js +2 -1
- package/dist/test/integration/role-permission-ancestors.test.js.map +1 -1
- package/dist/test/unit/frontend-bootstrap.test.js +13 -0
- package/dist/test/unit/frontend-bootstrap.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/category-slug-registry.test.js +14 -5
- package/dist/test/unit/implementations/dms/category-slug-registry.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.d.ts +1 -0
- package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.js +66 -0
- package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.js.map +1 -0
- package/dist/test/unit/implementations/dms/manifest-strip.test.js +26 -0
- package/dist/test/unit/implementations/dms/manifest-strip.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/nav-badges.test.js +23 -9
- package/dist/test/unit/implementations/dms/nav-badges.test.js.map +1 -1
- package/dist/test/unit/implementations/dms-html-render/generate-html.test.js +22 -4
- package/dist/test/unit/implementations/dms-html-render/generate-html.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms/page-layout-query.test.js +12 -0
- package/dist/test/unit/interfaces/dms/page-layout-query.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms/page-teardown.test.js +1 -1
- package/dist/test/unit/interfaces/dms/page-teardown.test.js.map +1 -1
- package/dist/test/unit/pages/settings/member-roles-field.test.js +1 -1
- package/dist/test/unit/pages/settings/member-roles-field.test.js.map +1 -1
- package/dist/test/unit/pages/settings/role-delete-confirm.test.js.map +1 -1
- package/dist/test/unit/realtime/menu-topics.test.d.ts +1 -0
- package/dist/test/unit/realtime/menu-topics.test.js +26 -0
- package/dist/test/unit/realtime/menu-topics.test.js.map +1 -0
- package/dist/test/unit/store/store.test.d.ts +1 -0
- package/dist/test/unit/store/store.test.js +127 -0
- package/dist/test/unit/store/store.test.js.map +1 -0
- package/dist/validation/store.schema.d.ts +39 -0
- package/dist/validation/store.schema.js +27 -0
- package/dist/validation/store.schema.js.map +1 -0
- package/frontend-vue/deferred-plugins.ts +3 -4
- package/frontend-vue/dms.email.ts +5 -1
- package/frontend-vue/dms.frontend.ts +15 -5
- package/frontend-vue/dms.server.ts +74 -0
- package/frontend-vue/layers/dms-auth/app/composables/useLogout.ts +1 -1
- package/frontend-vue/layers/dms-auth/app/composables/usePostLoginRedirect.ts +16 -3
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/2fa.vue +3 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/backup.vue +3 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/forgot-validation.vue +1 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/oauth-complete.vue +26 -3
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/recover.vue +1 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/signup.vue +22 -25
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/validate.vue +1 -1
- package/frontend-vue/layers/dms-auth/app/middleware/auth.ts +5 -7
- package/frontend-vue/layers/dms-auth/app/plugins/permissions-preload.ts +17 -0
- package/frontend-vue/layers/dms-auth/server/accounts.ts +135 -0
- package/frontend-vue/layers/dms-auth/server/actions.ts +251 -0
- package/frontend-vue/layers/dms-auth/server/oauth.ts +260 -0
- package/frontend-vue/layers/dms-auth/server/session.ts +158 -0
- package/frontend-vue/layers/dms-core/app/build/utils/asyncDataScope.ts +47 -0
- package/frontend-vue/layers/dms-core/app/build/utils/permission-preview.ts +6 -2
- package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +148 -0
- package/frontend-vue/layers/dms-core/app/composables/auth/useAuthFetch.ts +7 -20
- package/frontend-vue/layers/dms-core/app/composables/auth/useCurrentUser.ts +2 -2
- package/frontend-vue/layers/dms-core/app/composables/auth/useMultiAccount.ts +18 -5
- package/frontend-vue/layers/dms-core/app/composables/auth/usePermissions.ts +35 -6
- package/frontend-vue/layers/dms-core/app/composables/auth/useSessionRecovery.ts +1 -1
- package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +20 -14
- package/frontend-vue/layers/dms-core/app/composables/period/periodClock.ts +121 -0
- package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +42 -0
- package/frontend-vue/layers/dms-core/app/composables/period/periodTimeZone.ts +31 -0
- package/frontend-vue/layers/dms-core/app/composables/period/presetResolvers.ts +48 -19
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +33 -4
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +78 -25
- package/frontend-vue/layers/dms-core/app/composables/realtime/usePageRealtime.ts +3 -0
- package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +36 -6
- package/frontend-vue/layers/dms-core/app/composables/user/useUserRegionalPreferences.ts +1 -1
- package/frontend-vue/layers/dms-core/app/plugins/async-data-scope.ts +26 -0
- package/frontend-vue/layers/dms-core/app/utils/pageRoutes.ts +22 -0
- package/frontend-vue/layers/dms-core/app/utils/regional.ts +36 -3
- package/frontend-vue/layers/dms-core/app/utils/trailingDeduper.ts +10 -1
- package/frontend-vue/layers/dms-core/shared/types/app-config.ts +48 -4
- package/frontend-vue/layers/dms-layout/app/app.config.ts +42 -23
- package/frontend-vue/layers/dms-layout/app/assets/css/main.css +72 -20
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardFrame.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +0 -3
- package/frontend-vue/layers/dms-layout/app/build/components/notification/NotificationPopover.vue +9 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreDependencyPicker.vue +136 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +98 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +247 -29
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +19 -2
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/members/MemberRolePicker.vue +13 -12
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +17 -7
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationPreferencesForm.vue +0 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfilePersonalInfo.vue +36 -33
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfileSecuritySummary.vue +1 -2
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-types.ts +5 -4
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecuritySessions.vue +35 -28
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityStatus.vue +0 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityTwoFactor.vue +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/dev-reload/useDevReloadHolder.ts +7 -6
- package/frontend-vue/layers/dms-layout/app/build/composables/layout/sidePanelState.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/navigation/useVisitNavBadges.ts +26 -0
- package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationCatalog.ts +25 -23
- package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationPreferences.ts +44 -23
- package/frontend-vue/layers/dms-layout/app/build/composables/security/useSessionHandoff.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/preferencesSummary.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useAccountData.ts +3 -2
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useProfileAvatar.ts +1 -18
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useProfilePreferences.ts +19 -23
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityFormat.ts +6 -3
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityOverview.ts +13 -18
- package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +186 -120
- package/frontend-vue/layers/dms-layout/app/build/utils/module-store-install.ts +146 -0
- package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +8 -6
- package/frontend-vue/layers/dms-layout/app/build/utils/publicErrorMessage.ts +19 -0
- package/frontend-vue/layers/dms-layout/app/components/navigation/CollapsedMenuItem.vue +0 -11
- package/frontend-vue/layers/dms-layout/app/components/navigation/NavigationMenu.vue +3 -13
- package/frontend-vue/layers/dms-layout/app/composables/general/useSystemState.ts +7 -11
- package/frontend-vue/layers/dms-layout/app/composables/notification/useNotifications.ts +103 -22
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +109 -0
- package/frontend-vue/layers/dms-layout/app/composables/page/useModulesListing.ts +25 -102
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +0 -10
- package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +177 -96
- package/frontend-vue/layers/dms-layout/app/composables/profile/useUserLanguage.ts +3 -2
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +341 -62
- package/frontend-vue/layers/dms-layout/app/emails/components/EmailLayout.vue +31 -11
- package/frontend-vue/layers/dms-layout/app/error.vue +5 -2
- package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/middleware/page-leave-guard.global.ts +2 -2
- package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +12 -9
- package/frontend-vue/layers/dms-layout/app/plugins/dms-menu-sync.client.ts +31 -3
- package/frontend-vue/layers/dms-layout/app/plugins/favicon.ts +58 -0
- package/frontend-vue/layers/dms-layout/app/plugins/notification-stream.client.ts +15 -7
- package/frontend-vue/layers/dms-layout/app/plugins/profile-sync.client.ts +5 -1
- package/frontend-vue/layers/dms-layout/app/plugins/regional-preferences.ts +10 -4
- package/frontend-vue/layers/dms-layout/app/plugins/time-zone-cookie.client.ts +12 -0
- package/frontend-vue/layers/dms-layout/app/types/module-store.ts +134 -0
- package/frontend-vue/layers/dms-layout/app/types/page.ts +13 -0
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +107 -9
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +107 -9
- package/frontend-vue/layers/dms-layout/server/html-render.ts +58 -0
- package/frontend-vue/layers/dms-layout/server/render-token.ts +95 -0
- package/frontend-vue/layers/dms-onboarding/app/composables/onboarding/useOnboarding.ts +25 -21
- package/frontend-vue/layers/dms-onboarding/app/middleware/onboarding.global.ts +1 -1
- package/frontend-vue/layers/dms-onboarding/app/plugins/onboarding-preload.ts +9 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +4 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/files/useFileMetadata.ts +162 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formRecordLoad.ts +23 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageDataRefresh.ts +101 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/navigation/useNavBadges.ts +33 -6
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +19 -16
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodQuery.ts +40 -20
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +6 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableListData.ts +42 -14
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +19 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +5 -2
- package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +9 -9
- package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +20 -18
- package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/File.vue +26 -132
- package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +40 -105
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +2 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +4 -4
- package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +9 -13
- package/frontend-vue/layers/dms-ui/app/components/table-view/CascaderPath.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/FilePreview.vue +12 -24
- package/frontend-vue/layers/dms-ui/app/components/table-view/ImagePreview.vue +17 -29
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +7 -3
- package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +20 -8
- package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +8 -11
- package/frontend-vue/layers/dms-ui/app/composables/chart/useChartFetch.ts +146 -182
- package/frontend-vue/layers/dms-ui/app/composables/live/usePageLiveRefresh.ts +8 -2
- package/frontend-vue/layers/dms-ui/app/composables/useFileReadUrls.ts +11 -99
- package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +32 -3
- package/frontend-vue/package.json +3 -3
- package/frontend-vue/pnpm-lock.yaml +16 -22
- package/frontend-vue/server/private-options.ts +27 -0
- package/frontend-vue/tests/async-data-scope.test.ts +121 -0
- package/frontend-vue/tests/auth-middleware-error.test.ts +3 -3
- package/frontend-vue/tests/banner-block-fetch.test.ts +3 -0
- package/frontend-vue/tests/block-fetch-options-forwarding.test.ts +3 -0
- package/frontend-vue/tests/block-items-period-realtime.test.ts +3 -0
- package/frontend-vue/tests/block-not-found.test.ts +16 -2
- package/frontend-vue/tests/block-page-refresh.test.ts +3 -0
- package/frontend-vue/tests/block-route-params.test.ts +3 -0
- package/frontend-vue/tests/branding-defaults.test.ts +34 -0
- package/frontend-vue/tests/builtin-emails.test.ts +23 -2
- package/frontend-vue/tests/chart-live-pause.test.ts +4 -1
- package/frontend-vue/tests/chart-value-propagation.test.ts +4 -1
- package/frontend-vue/tests/composed-text-blocks.test.ts +3 -0
- package/frontend-vue/tests/email-layout.test.ts +66 -4
- package/frontend-vue/tests/error-page-message.test.ts +25 -2
- package/frontend-vue/tests/favicon.test.ts +131 -0
- package/frontend-vue/tests/file-fields-ssr.test.ts +145 -0
- package/frontend-vue/tests/file-read-urls.test.ts +145 -34
- package/frontend-vue/tests/file-unavailable.test.ts +5 -1
- package/frontend-vue/tests/fixtures/dataEngine.ts +44 -0
- package/frontend-vue/tests/form-load-error.test.ts +55 -1
- package/frontend-vue/tests/helpers/api-data-runtime.ts +77 -0
- package/frontend-vue/tests/image-hydration-ids.test.ts +21 -16
- package/frontend-vue/tests/image-reference-preservation.test.ts +59 -53
- package/frontend-vue/tests/input-number-adjust.test.ts +14 -0
- package/frontend-vue/tests/key-value-list-copy.test.ts +3 -0
- package/frontend-vue/tests/member-role-picker.test.ts +4 -2
- package/frontend-vue/tests/menu-sync-reconnect.test.ts +47 -0
- package/frontend-vue/tests/metadata-retry.test.ts +5 -1
- package/frontend-vue/tests/module-routing.test.ts +2 -5
- package/frontend-vue/tests/module-store-catalog.test.ts +270 -51
- package/frontend-vue/tests/modules-catalog.test.ts +17 -4
- package/frontend-vue/tests/modules-listing-transfer.test.ts +95 -0
- package/frontend-vue/tests/multi-account-validate.test.ts +23 -1
- package/frontend-vue/tests/nav-badge-tones.test.ts +66 -3
- package/frontend-vue/tests/notification-counts-from-writes.test.ts +223 -0
- package/frontend-vue/tests/notification-counts-transfer.test.ts +88 -0
- package/frontend-vue/tests/notification-inbox-display.test.ts +57 -5
- package/frontend-vue/tests/notification-popover-errors.test.ts +21 -1
- package/frontend-vue/tests/oauth-complete-handoff.test.ts +209 -0
- package/frontend-vue/tests/onboarding-middleware.test.ts +1 -1
- package/frontend-vue/tests/onboarding-transfer.test.ts +106 -0
- package/frontend-vue/tests/page-data-refresh.test.ts +141 -0
- package/frontend-vue/tests/page-fill-height.test.ts +16 -3
- package/frontend-vue/tests/page-no-visible-blocks.test.ts +12 -2
- package/frontend-vue/tests/page-realtime-remount.test.ts +1 -1
- package/frontend-vue/tests/page-record-label.test.ts +0 -7
- package/frontend-vue/tests/page-validation-redirect.test.ts +12 -2
- package/frontend-vue/tests/period-hydration-now.test.ts +185 -0
- package/frontend-vue/tests/period-live-pause.test.ts +3 -0
- package/frontend-vue/tests/period-reader-zone.test.ts +137 -0
- package/frontend-vue/tests/period-scope-first-fetch.test.ts +17 -1
- package/frontend-vue/tests/period-ssr.test.ts +195 -0
- package/frontend-vue/tests/permission-preview-partial.test.ts +0 -2
- package/frontend-vue/tests/permissions-transfer.test.ts +194 -0
- package/frontend-vue/tests/post-login-redirect.test.ts +10 -5
- package/frontend-vue/tests/profile-personal-info-ssr.test.ts +150 -0
- package/frontend-vue/tests/profile-preferences-summary.test.ts +15 -11
- package/frontend-vue/tests/realtime-reconnect.test.ts +22 -3
- package/frontend-vue/tests/roles-name-errors.test.ts +1 -1
- package/frontend-vue/tests/row-side-panel.test.ts +1 -1
- package/frontend-vue/tests/security-sessions-load-error.test.ts +3 -1
- package/frontend-vue/tests/server-routes-vs-pages.test.ts +26 -0
- package/frontend-vue/tests/server-routes.test.ts +582 -0
- package/frontend-vue/tests/session-handoff.test.ts +1 -1
- package/frontend-vue/tests/settings-reads-ssr.test.ts +232 -0
- package/frontend-vue/tests/side-panels.test.ts +8 -1
- package/frontend-vue/tests/sign-in-exit-history.test.ts +219 -0
- package/frontend-vue/tests/signup-invitation-link.test.ts +56 -8
- package/frontend-vue/tests/site-layout-categories.test.ts +42 -0
- package/frontend-vue/tests/site-layout-hydration.test.ts +3 -3
- package/frontend-vue/tests/site-layout-rendered.test.ts +103 -0
- package/frontend-vue/tests/site-layout-resync.test.ts +87 -0
- package/frontend-vue/tests/ssr-guard-preload.test.ts +188 -0
- package/frontend-vue/tests/table-cell-tones.test.ts +36 -1
- package/frontend-vue/tests/table-data-ssr.test.ts +195 -0
- package/frontend-vue/tests/table-list-data-requests.test.ts +154 -0
- package/frontend-vue/tests/table-retry-loading.test.ts +37 -0
- package/frontend-vue/tests/table-source-navigation.test.ts +0 -1
- package/frontend-vue/tests/table-source-period-scope.test.ts +0 -1
- package/frontend-vue/tests/table-view-views.test.ts +1 -1
- package/frontend-vue/tests/top-list-card-actions.test.ts +3 -0
- package/frontend-vue/tests/top-list-card-period-badge-ssr.test.ts +212 -0
- package/frontend-vue/tests/top-list-card-period-badge.test.ts +79 -0
- package/frontend-vue/tests/top-list-card-value-suffix.test.ts +3 -0
- package/frontend-vue/tests/trailing-deduper.test.ts +22 -0
- package/frontend-vue/tests/use-api-data-update.test.ts +119 -0
- package/frontend-vue/tests/use-api-data.test.ts +410 -0
- package/frontend-vue/tests/user-language.test.ts +8 -5
- package/frontend-vue/tests/widget-label-i18n.test.ts +4 -1
- package/frontend-vue/vitest.config.ts +6 -0
- package/package.json +2 -2
- package/skills/dms/SKILL.md +2 -1
- package/skills/dms-module/SKILL.md +1 -1
- package/skills/dms-pages/SKILL.md +4 -1
- package/frontend-vue/layers/dms-core/app/build/composables/page/usePageRecordLabel.ts +0 -9
- package/frontend-vue/layers/dms-layout/app/composables/page/usePrefetch.ts +0 -21
- package/frontend-vue/layers/dms-ui/app/build/composables/form/useControlError.ts +0 -10
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useServerRenderedAsyncData.ts +0 -23
- package/frontend-vue/tests/control-error-path.test.ts +0 -11
- package/frontend-vue/tests/modules-listing-per-request.test.ts +0 -104
- package/frontend-vue/tests/page-layout-background-refresh.test.ts +0 -321
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { onScopeDispose, type Ref, watch } from "vue";
|
|
2
|
+
import { refreshDmsPageData } from "#dms/frontend-module";
|
|
3
|
+
import type { RealtimeTopicHandler } from "../../../../../dms-core/app/composables/realtime/useUserRealtime";
|
|
4
|
+
import {
|
|
5
|
+
onPageBlocksRefresh,
|
|
6
|
+
waitForPageBlocksRefresh,
|
|
7
|
+
} from "../../../utils/blockRefresh";
|
|
8
|
+
import { useRefreshTriggers } from "../blocks/useRefreshTriggers";
|
|
9
|
+
|
|
10
|
+
// One frame: the events of one burst (a bulk edit) read the data once.
|
|
11
|
+
const BURST_WINDOW_MS = 16;
|
|
12
|
+
|
|
13
|
+
/** What the page's data refresh listens to. */
|
|
14
|
+
export interface PageDataRefreshSource {
|
|
15
|
+
/** The page's realtime topics, once its session is subscribed to it. */
|
|
16
|
+
pageTopics: Ref<string[]>;
|
|
17
|
+
subscribe(topic: string, handler: RealtimeTopicHandler): () => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Reads again the data the page's components show through `useApiData` (and
|
|
22
|
+
* `useDmsAsyncData`), with `refreshDmsPageData()`: on each
|
|
23
|
+
* `refreshPageBlocks()`, an action of the user, at once; on each event of one
|
|
24
|
+
* of the page's realtime topics and each `refreshLivePageBlocks()`, through
|
|
25
|
+
* the page's live gate, like a built-in block. A read opts out with
|
|
26
|
+
* `refreshOnPageRefresh: false`, as the built-in blocks that refresh on their
|
|
27
|
+
* own triggers do. The page renderer calls it once, from its setup;
|
|
28
|
+
* `refreshPageBlocks()` resolves once the refresh it asked for is done.
|
|
29
|
+
*/
|
|
30
|
+
export function usePageDataRefresh(source: PageDataRefreshSource): void {
|
|
31
|
+
const { refresh, cancel } = burstRefresh();
|
|
32
|
+
const gate = useRefreshTriggers(undefined, () => void refresh());
|
|
33
|
+
// The gate's own listener reads at once on the same event: this call joins
|
|
34
|
+
// the same burst, and hands its promise to the caller.
|
|
35
|
+
const stopWaiting = onPageBlocksRefresh((event) =>
|
|
36
|
+
waitForPageBlocksRefresh(event, refresh()),
|
|
37
|
+
);
|
|
38
|
+
// Presence snapshots ride the same topics: they carry no `type`.
|
|
39
|
+
const onEvent: RealtimeTopicHandler = (event) => {
|
|
40
|
+
if ("type" in event) gate.trigger(event);
|
|
41
|
+
};
|
|
42
|
+
let unsubscribes: Array<() => void> = [];
|
|
43
|
+
const unsubscribeAll = () => {
|
|
44
|
+
for (const unsubscribe of unsubscribes) unsubscribe();
|
|
45
|
+
unsubscribes = [];
|
|
46
|
+
};
|
|
47
|
+
watch(
|
|
48
|
+
source.pageTopics,
|
|
49
|
+
(topics) => {
|
|
50
|
+
unsubscribeAll();
|
|
51
|
+
unsubscribes = topics.map((topic) => source.subscribe(topic, onEvent));
|
|
52
|
+
},
|
|
53
|
+
{ immediate: true },
|
|
54
|
+
);
|
|
55
|
+
onScopeDispose(() => {
|
|
56
|
+
unsubscribeAll();
|
|
57
|
+
stopWaiting();
|
|
58
|
+
cancel();
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface BurstRefresh {
|
|
63
|
+
/** Joins the burst under way, or starts one; resolves once it is read. */
|
|
64
|
+
refresh(): Promise<void>;
|
|
65
|
+
cancel(): void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface Burst {
|
|
69
|
+
promise: Promise<void>;
|
|
70
|
+
done(): void;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function startBurst(): Burst {
|
|
74
|
+
let done!: () => void;
|
|
75
|
+
const promise = new Promise<void>((resolve) => {
|
|
76
|
+
done = resolve;
|
|
77
|
+
});
|
|
78
|
+
return { promise, done };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** `refreshDmsPageData()`, once for the calls of one burst. */
|
|
82
|
+
function burstRefresh(): BurstRefresh {
|
|
83
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
84
|
+
let burst: Burst | undefined;
|
|
85
|
+
return {
|
|
86
|
+
refresh() {
|
|
87
|
+
clearTimeout(timer);
|
|
88
|
+
const current = (burst ??= startBurst());
|
|
89
|
+
timer = setTimeout(() => {
|
|
90
|
+
burst = undefined;
|
|
91
|
+
refreshDmsPageData().then(current.done, current.done);
|
|
92
|
+
}, BURST_WINDOW_MS);
|
|
93
|
+
return current.promise;
|
|
94
|
+
},
|
|
95
|
+
cancel() {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
burst?.done();
|
|
98
|
+
burst = undefined;
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -10,17 +10,25 @@ export interface NavBadge {
|
|
|
10
10
|
/** A menu entry as the server serves it, as far as its badge goes. */
|
|
11
11
|
export interface ServedNavBadge {
|
|
12
12
|
fullId?: string;
|
|
13
|
+
/** A static badge the page declares. */
|
|
13
14
|
badge?: string;
|
|
14
|
-
/** The tone of a counted badge; neutral when absent. */
|
|
15
15
|
badgeTone?: Tone;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/** A badge a page visit carries (`navBadges`), counted for this visit. */
|
|
19
|
+
export interface CountedNavBadge {
|
|
20
|
+
badge: string;
|
|
21
|
+
/** Neutral when absent. */
|
|
22
|
+
tone?: Tone;
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
/**
|
|
19
|
-
*
|
|
26
|
+
* The counts of the navigation entries, by page full id
|
|
20
27
|
* (`settings.workspace.members` → `{ label: "7" }`, `""` for none), with
|
|
21
|
-
* their tone.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
28
|
+
* their tone. Each page visit brings the ones the server counted for it
|
|
29
|
+
* (`navBadges`, a page prop: the site layout a browser keeps for minutes
|
|
30
|
+
* holds none), and a table view tab declared with `navBadge` publishes its
|
|
31
|
+
* own counter once shown; navigation surfaces draw them over a static badge.
|
|
24
32
|
*
|
|
25
33
|
* Shared state, so a count published on one page stays shown on the others.
|
|
26
34
|
*/
|
|
@@ -29,6 +37,7 @@ export const useNavBadges = () => {
|
|
|
29
37
|
"dms-nav-badges",
|
|
30
38
|
() => ({}),
|
|
31
39
|
);
|
|
40
|
+
const servedIds = useDmsState<string[]>("dms-served-nav-badges", () => []);
|
|
32
41
|
|
|
33
42
|
/** Sets, or with `undefined` clears, the badge of a page. */
|
|
34
43
|
function setNavBadge(
|
|
@@ -61,5 +70,23 @@ export const useNavBadges = () => {
|
|
|
61
70
|
return badge?.label ? badge : undefined;
|
|
62
71
|
}
|
|
63
72
|
|
|
64
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Takes the counts a page visit carries: each one replaces the entry's
|
|
75
|
+
* badge, and an entry the previous visit counted that this one does not
|
|
76
|
+
* (its count fell to zero) loses its badge.
|
|
77
|
+
*/
|
|
78
|
+
function applyCountedNavBadges(
|
|
79
|
+
counted: Record<string, CountedNavBadge> | undefined,
|
|
80
|
+
): void {
|
|
81
|
+
if (!counted) return;
|
|
82
|
+
for (const fullId of servedIds.value) {
|
|
83
|
+
if (!(fullId in counted)) setNavBadge(fullId, "");
|
|
84
|
+
}
|
|
85
|
+
for (const [fullId, { badge, tone }] of Object.entries(counted)) {
|
|
86
|
+
setNavBadge(fullId, badge, tone);
|
|
87
|
+
}
|
|
88
|
+
servedIds.value = Object.keys(counted);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { badges, setNavBadge, navBadgeOf, applyCountedNavBadges };
|
|
65
92
|
};
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type PeriodRange,
|
|
15
15
|
} from "#dms-core/app/composables/period/types";
|
|
16
16
|
import { regionalDateTimeFormat } from "#dms-core/app/utils/regional";
|
|
17
|
+
import { readerClock } from "#dms-core/app/composables/period/periodClock";
|
|
17
18
|
|
|
18
19
|
/** A day range as the range calendar reads and writes it. */
|
|
19
20
|
export interface CalendarRange {
|
|
@@ -102,20 +103,16 @@ export function isBeyondMaxDays(
|
|
|
102
103
|
return Math.abs(date.compare(pickedDay)) >= maxDays;
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
// A period's days are the reader's calendar days (`readerClock`), on the
|
|
107
|
+
// server as in the browser.
|
|
105
108
|
function isStartOfDay(date: Date): boolean {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
date.getMinutes() === 0 &&
|
|
109
|
-
date.getSeconds() === 0 &&
|
|
110
|
-
date.getMilliseconds() === 0
|
|
111
|
-
);
|
|
109
|
+
const { hours, minutes, seconds, milliseconds } = readerClock().wall(date);
|
|
110
|
+
return hours === 0 && minutes === 0 && seconds === 0 && milliseconds === 0;
|
|
112
111
|
}
|
|
113
112
|
|
|
114
113
|
function isEndOfDay(date: Date): boolean {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
date.getMinutes() === END_OF_DAY_MINUTES
|
|
118
|
-
);
|
|
114
|
+
const { hours, minutes } = readerClock().wall(date);
|
|
115
|
+
return hours === END_OF_DAY_HOURS && minutes === END_OF_DAY_MINUTES;
|
|
119
116
|
}
|
|
120
117
|
|
|
121
118
|
// Up to a day and an hour: `last-24h` plus slack for a DST shift.
|
|
@@ -207,11 +204,16 @@ const YESTERDAY_OFFSET = -1;
|
|
|
207
204
|
const WEEKDAY_FORMAT: FormatOptions = { weekday: "short" };
|
|
208
205
|
|
|
209
206
|
function localDayStart(date: Date): number {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
207
|
+
const clock = readerClock();
|
|
208
|
+
return clock
|
|
209
|
+
.instant({
|
|
210
|
+
...clock.wall(date),
|
|
211
|
+
hours: 0,
|
|
212
|
+
minutes: 0,
|
|
213
|
+
seconds: 0,
|
|
214
|
+
milliseconds: 0,
|
|
215
|
+
})
|
|
216
|
+
.getTime();
|
|
215
217
|
}
|
|
216
218
|
|
|
217
219
|
function daysBefore(date: Date, now: Date): number {
|
|
@@ -235,7 +237,8 @@ function relativeDayPrefix(date: Date, now: Date, locale: string): string {
|
|
|
235
237
|
if (days < DAYS_PER_WEEK) {
|
|
236
238
|
return `${formatWith(date, locale, WEEKDAY_FORMAT)} `;
|
|
237
239
|
}
|
|
238
|
-
const
|
|
240
|
+
const clock = readerClock();
|
|
241
|
+
const sameYear = clock.wall(date).year === clock.wall(now).year;
|
|
239
242
|
const day: FormatOptions = sameYear
|
|
240
243
|
? DAY_FORMAT
|
|
241
244
|
: { ...DAY_FORMAT, year: "numeric" };
|
|
@@ -8,6 +8,10 @@ import {
|
|
|
8
8
|
type PeriodRange,
|
|
9
9
|
} from "#dms-core/app/composables/period/types";
|
|
10
10
|
import type { PeriodQueryKeys } from "#dms-core/app/composables/period/periodLink";
|
|
11
|
+
import {
|
|
12
|
+
readerClock,
|
|
13
|
+
type WallTime,
|
|
14
|
+
} from "#dms-core/app/composables/period/periodClock";
|
|
11
15
|
|
|
12
16
|
export {
|
|
13
17
|
type PeriodQueryKeys,
|
|
@@ -67,27 +71,41 @@ function readText(query: PeriodQuery, key: string): string | undefined {
|
|
|
67
71
|
|
|
68
72
|
type DayBoundary = "start" | "end";
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
const DAY_BOUNDARY_TIMES: Record<
|
|
75
|
+
DayBoundary,
|
|
76
|
+
Pick<WallTime, "hours" | "minutes" | "seconds" | "milliseconds">
|
|
77
|
+
> = {
|
|
78
|
+
start: { hours: 0, minutes: 0, seconds: 0, milliseconds: 0 },
|
|
79
|
+
end: {
|
|
80
|
+
hours: END_OF_DAY_HOURS,
|
|
81
|
+
minutes: END_OF_DAY_MINUTES,
|
|
82
|
+
seconds: END_OF_DAY_SECONDS,
|
|
83
|
+
milliseconds: END_OF_DAY_MS,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Days are the reader's calendar days (`readerClock`): the browser's zone,
|
|
88
|
+
// on the server as in the browser.
|
|
89
|
+
function dayBoundary(
|
|
90
|
+
wall: Pick<WallTime, "year" | "month" | "day">,
|
|
91
|
+
boundary: DayBoundary,
|
|
92
|
+
): Date {
|
|
93
|
+
return readerClock().instant({ ...wall, ...DAY_BOUNDARY_TIMES[boundary] });
|
|
80
94
|
}
|
|
81
95
|
|
|
82
96
|
/** A local calendar day, refused when it does not exist (`2026-02-30`). */
|
|
83
97
|
function parseIsoDay(match: RegExpExecArray, boundary: DayBoundary) {
|
|
84
98
|
const [year, month, day] = match.slice(1).map(Number);
|
|
85
|
-
const date =
|
|
99
|
+
const date = dayBoundary(
|
|
100
|
+
{ year: year!, month: month! - MONTH_OFFSET, day: day! },
|
|
101
|
+
boundary,
|
|
102
|
+
);
|
|
103
|
+
const wall = readerClock().wall(date);
|
|
86
104
|
const exists =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return exists ?
|
|
105
|
+
wall.year === year &&
|
|
106
|
+
wall.month === month! - MONTH_OFFSET &&
|
|
107
|
+
wall.day === day;
|
|
108
|
+
return exists ? date : null;
|
|
91
109
|
}
|
|
92
110
|
|
|
93
111
|
/**
|
|
@@ -112,8 +130,9 @@ function pad(value: number, length = DATE_PART_LENGTH): string {
|
|
|
112
130
|
}
|
|
113
131
|
|
|
114
132
|
function isDayBoundary(date: Date, boundary: DayBoundary): boolean {
|
|
115
|
-
|
|
116
|
-
|
|
133
|
+
return (
|
|
134
|
+
dayBoundary(readerClock().wall(date), boundary).getTime() === date.getTime()
|
|
135
|
+
);
|
|
117
136
|
}
|
|
118
137
|
|
|
119
138
|
/**
|
|
@@ -122,10 +141,11 @@ function isDayBoundary(date: Date, boundary: DayBoundary): boolean {
|
|
|
122
141
|
*/
|
|
123
142
|
export function formatPeriodDate(date: Date, boundary: DayBoundary): string {
|
|
124
143
|
if (!isDayBoundary(date, boundary)) return date.toISOString();
|
|
144
|
+
const { year, month, day } = readerClock().wall(date);
|
|
125
145
|
return [
|
|
126
|
-
pad(
|
|
127
|
-
pad(
|
|
128
|
-
pad(
|
|
146
|
+
pad(year, YEAR_PART_LENGTH),
|
|
147
|
+
pad(month + MONTH_OFFSET),
|
|
148
|
+
pad(day),
|
|
129
149
|
].join("-");
|
|
130
150
|
}
|
|
131
151
|
|
|
@@ -4,7 +4,6 @@ import type {
|
|
|
4
4
|
TableViewFooter,
|
|
5
5
|
TableViewFooterSummaryConfig,
|
|
6
6
|
} from "../../../composables/table-view/types";
|
|
7
|
-
import { useServerRenderedAsyncData } from "./useServerRenderedAsyncData";
|
|
8
7
|
import type {
|
|
9
8
|
TableFooterLegendItem,
|
|
10
9
|
TableFooterTexts,
|
|
@@ -35,10 +34,9 @@ const UNSORTED_KEYS = new Set(["sortKey", "sortDirection"]);
|
|
|
35
34
|
* route computes over every listed row — a sum read like its column's cells
|
|
36
35
|
* unless a `format` is given — and the legend of a select column.
|
|
37
36
|
*/
|
|
38
|
-
export
|
|
37
|
+
export function useTableFooter(options: TableFooterOptions) {
|
|
39
38
|
const { footer, columns, location } = options;
|
|
40
39
|
const summaries = footer?.summary ?? [];
|
|
41
|
-
const { $authFetch } = useAuthFetch();
|
|
42
40
|
const { locale } = useI18n();
|
|
43
41
|
const { processI18n } = useTranslation();
|
|
44
42
|
const { renderValue } = useColumnValueRenderer();
|
|
@@ -52,17 +50,17 @@ export async function useTableFooter(options: TableFooterOptions) {
|
|
|
52
50
|
ids: summaries.map((summary) => summary.id),
|
|
53
51
|
}));
|
|
54
52
|
|
|
55
|
-
const { data: values, refresh } =
|
|
56
|
-
Record<string, number>
|
|
57
|
-
>(
|
|
53
|
+
const { data: values, refresh } = useApiData<Record<string, number>>(
|
|
58
54
|
`${options.dataKey}-summary`,
|
|
59
|
-
() =>
|
|
55
|
+
({ $authFetch, signal }) =>
|
|
60
56
|
summaries.length === 0
|
|
61
57
|
? Promise.resolve({})
|
|
62
58
|
: $authFetch<Record<string, number>>(`${location}/summary`, {
|
|
63
59
|
query: summaryQuery.value,
|
|
60
|
+
signal,
|
|
64
61
|
}),
|
|
65
|
-
|
|
62
|
+
// Read again with the table's rows, on the table's own triggers.
|
|
63
|
+
{ lazy: true, watch: [summaryQuery], refreshOnPageRefresh: false },
|
|
66
64
|
);
|
|
67
65
|
|
|
68
66
|
const formatSummary = (
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { computed, type ComputedRef } from "vue";
|
|
2
2
|
import type { TableViewListResponse } from "../../../composables/table-view/types";
|
|
3
|
-
import { useServerRenderedAsyncData } from "./useServerRenderedAsyncData";
|
|
4
3
|
import type { useTableRows } from "./useTableRows";
|
|
5
4
|
import { buildTableDataKey } from "./utils/tableQuery";
|
|
6
5
|
|
|
@@ -43,6 +42,14 @@ export interface TableCountsDataOptions<T extends Record<string, unknown>> {
|
|
|
43
42
|
|
|
44
43
|
const NO_HELD_QUERY = (): Query => ({});
|
|
45
44
|
|
|
45
|
+
/**
|
|
46
|
+
* A watch source following `source` by value: a query rebuilt equal (its
|
|
47
|
+
* inputs recomputed to the same values, a period re-resolved to the same
|
|
48
|
+
* bounds) must not read the rows again.
|
|
49
|
+
*/
|
|
50
|
+
const byValue = (source: ComputedRef<unknown>): ComputedRef<string> =>
|
|
51
|
+
computed(() => JSON.stringify(source.value));
|
|
52
|
+
|
|
46
53
|
const EMPTY_LIST_RESULT: TableViewListResponse<never> = {
|
|
47
54
|
results: [],
|
|
48
55
|
total: 0,
|
|
@@ -51,7 +58,10 @@ const EMPTY_LIST_RESULT: TableViewListResponse<never> = {
|
|
|
51
58
|
};
|
|
52
59
|
|
|
53
60
|
/**
|
|
54
|
-
* The page of rows a table view shows
|
|
61
|
+
* The page of rows a table view shows: awaited by a server render, which
|
|
62
|
+
* transfers it, but not by a client navigation, which paints the list chrome
|
|
63
|
+
* and its skeleton at once (`lazy`). Read the loading state from `data`
|
|
64
|
+
* (`null` until the first result). The key names the source URL: a
|
|
55
65
|
* navigation that remounts the page while the previous one is still on screen
|
|
56
66
|
* binds to the cached entry of its key, so another URL needs another key to be
|
|
57
67
|
* requested at all, and the watch covers a page kept mounted.
|
|
@@ -69,14 +79,27 @@ export function useTableListData<T extends Record<string, unknown>>(
|
|
|
69
79
|
isSelfManaged: isSelfManaged.value,
|
|
70
80
|
sourceUrl: rows.sourceUrl.value,
|
|
71
81
|
});
|
|
72
|
-
return
|
|
82
|
+
return useDmsAsyncData(
|
|
73
83
|
key,
|
|
74
84
|
// `null` while a source waits on its period scope: the table's skeleton.
|
|
75
|
-
(): Promise<TableViewListResponse<T> | null> =>
|
|
85
|
+
({ signal }): Promise<TableViewListResponse<T> | null> =>
|
|
76
86
|
isSelfManaged.value
|
|
77
87
|
? Promise.resolve(EMPTY_LIST_RESULT)
|
|
78
|
-
: rows.list(
|
|
79
|
-
|
|
88
|
+
: rows.list(
|
|
89
|
+
{ ...query.value, ...archiveQuery.value, ...heldQuery() },
|
|
90
|
+
signal,
|
|
91
|
+
),
|
|
92
|
+
{
|
|
93
|
+
watch: [
|
|
94
|
+
byValue(query),
|
|
95
|
+
byValue(archiveQuery),
|
|
96
|
+
isSelfManaged,
|
|
97
|
+
rows.sourceUrl,
|
|
98
|
+
],
|
|
99
|
+
lazy: true,
|
|
100
|
+
// The table reads again on its own triggers, through its live hold.
|
|
101
|
+
refreshOnPageRefresh: false,
|
|
102
|
+
},
|
|
80
103
|
);
|
|
81
104
|
}
|
|
82
105
|
|
|
@@ -84,10 +107,10 @@ export function useTableListData<T extends Record<string, unknown>>(
|
|
|
84
107
|
* The counters of a table view's tabs and views, keyed by id; `null` until
|
|
85
108
|
* they arrive. Keyed by source URL like the list (see useTableListData).
|
|
86
109
|
*
|
|
87
|
-
* A source waiting on its period scope answers `null` too, like its list:
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
110
|
+
* A source waiting on its period scope answers `null` too, like its list: a
|
|
111
|
+
* server render that cannot resolve the period as the browser will (see
|
|
112
|
+
* `usePeriodScope`) draws the counters' skeleton, as the browser does until
|
|
113
|
+
* its own request answers, and hydration matches.
|
|
91
114
|
*/
|
|
92
115
|
export function useTableCountsData<T extends Record<string, unknown>>(
|
|
93
116
|
options: TableCountsDataOptions<T>,
|
|
@@ -95,9 +118,9 @@ export function useTableCountsData<T extends Record<string, unknown>>(
|
|
|
95
118
|
const { rows, queries, archiveQuery } = options;
|
|
96
119
|
const heldQuery = options.heldQuery ?? NO_HELD_QUERY;
|
|
97
120
|
const sourceSuffix = rows.sourceUrl.value ? `-${rows.sourceUrl.value}` : "";
|
|
98
|
-
return
|
|
121
|
+
return useDmsAsyncData<Record<string, number> | null>(
|
|
99
122
|
`table-view-${options.componentId}-${options.pageId}-tab-counts${sourceSuffix}`,
|
|
100
|
-
async () => {
|
|
123
|
+
async ({ signal }) => {
|
|
101
124
|
if (rows.isPeriodPending.value) return null;
|
|
102
125
|
if (queries.value.length === 0) return {};
|
|
103
126
|
return await rows.countBatch(
|
|
@@ -105,8 +128,13 @@ export function useTableCountsData<T extends Record<string, unknown>>(
|
|
|
105
128
|
id,
|
|
106
129
|
query: { ...query, ...archiveQuery.value, ...heldQuery() },
|
|
107
130
|
})),
|
|
131
|
+
signal,
|
|
108
132
|
);
|
|
109
133
|
},
|
|
110
|
-
{
|
|
134
|
+
{
|
|
135
|
+
watch: [byValue(queries), byValue(archiveQuery), rows.sourceUrl],
|
|
136
|
+
lazy: true,
|
|
137
|
+
refreshOnPageRefresh: false,
|
|
138
|
+
},
|
|
111
139
|
);
|
|
112
140
|
}
|
|
@@ -185,6 +185,7 @@ export function useTableRows<T extends Record<string, unknown>>(
|
|
|
185
185
|
const listFromSource = async (
|
|
186
186
|
sourceConfig: TableViewSourceConfig,
|
|
187
187
|
query: Query,
|
|
188
|
+
signal?: AbortSignal,
|
|
188
189
|
): Promise<TableViewListResponse<T> | null> => {
|
|
189
190
|
if (isPeriodPending.value) return null;
|
|
190
191
|
const url = sourceUrl.value;
|
|
@@ -192,6 +193,7 @@ export function useTableRows<T extends Record<string, unknown>>(
|
|
|
192
193
|
const { capabilities } = sourceConfig;
|
|
193
194
|
const response = await api<{ results: T[]; total: number }>(url, {
|
|
194
195
|
query: sourceRouteQuery(query, capabilities),
|
|
196
|
+
signal,
|
|
195
197
|
});
|
|
196
198
|
if (capabilities.paginate) {
|
|
197
199
|
return {
|
|
@@ -214,30 +216,39 @@ export function useTableRows<T extends Record<string, unknown>>(
|
|
|
214
216
|
): TableViewListResponse<T> | null =>
|
|
215
217
|
page && { ...page, results: pinRows(page.results, options.pinnedRows?.()) };
|
|
216
218
|
|
|
217
|
-
|
|
219
|
+
// `signal` aborts the request once its answer is no longer wanted.
|
|
220
|
+
const list = async (
|
|
221
|
+
query: Query,
|
|
222
|
+
signal?: AbortSignal,
|
|
223
|
+
): Promise<TableViewListResponse<T> | null> =>
|
|
218
224
|
withPinnedRows(
|
|
219
225
|
source
|
|
220
|
-
? await listFromSource(source, query)
|
|
221
|
-
: await api<TableViewListResponse<T>>(`${location}/list`, {
|
|
226
|
+
? await listFromSource(source, query, signal)
|
|
227
|
+
: await api<TableViewListResponse<T>>(`${location}/list`, {
|
|
228
|
+
query,
|
|
229
|
+
signal,
|
|
230
|
+
}),
|
|
222
231
|
);
|
|
223
232
|
|
|
224
233
|
// A source answers its totals one list at a time.
|
|
225
234
|
const countBatch = async (
|
|
226
235
|
queries: TableCountQuery[],
|
|
236
|
+
signal?: AbortSignal,
|
|
227
237
|
): Promise<Record<string, number>> => {
|
|
228
238
|
if (!source) {
|
|
229
239
|
return api<Record<string, number>>(`${location}/count/batch`, {
|
|
230
240
|
method: "POST",
|
|
231
241
|
body: { queries },
|
|
242
|
+
signal,
|
|
232
243
|
});
|
|
233
244
|
}
|
|
234
245
|
const totals = await Promise.all(
|
|
235
246
|
queries.map(async ({ id, query }) => {
|
|
236
|
-
const page = await listFromSource(
|
|
237
|
-
|
|
238
|
-
offset: 0,
|
|
239
|
-
|
|
240
|
-
|
|
247
|
+
const page = await listFromSource(
|
|
248
|
+
source,
|
|
249
|
+
{ ...query, offset: 0, limit: 1 },
|
|
250
|
+
signal,
|
|
251
|
+
);
|
|
241
252
|
return page ? [[id, page.total] as const] : [];
|
|
242
253
|
}),
|
|
243
254
|
);
|
|
@@ -64,7 +64,7 @@ const VIEW_NAME_FIELD = "label";
|
|
|
64
64
|
*/
|
|
65
65
|
export function useTableViews(options: TableViewsOptions) {
|
|
66
66
|
const { views, defaults, state, preferences, urlScope } = options;
|
|
67
|
-
const { user } =
|
|
67
|
+
const { user } = useDmsUserSession();
|
|
68
68
|
const { t } = useI18n();
|
|
69
69
|
const { processI18n } = useTranslation();
|
|
70
70
|
const toast = useToast();
|
|
@@ -42,7 +42,8 @@ interface Props extends DefaultComponentProps {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// The comparison series takes palette step 2 (v2 violet), re-resolved on
|
|
45
|
-
// theme changes via useThemeRevision so it tracks light/dark mode.
|
|
45
|
+
// theme changes via useThemeRevision so it tracks light/dark mode. Its legend
|
|
46
|
+
// key reads the same token in CSS.
|
|
46
47
|
const COMPARISON_COLOR_TOKEN = "--dms-chart-2";
|
|
47
48
|
const themeRevision = useThemeRevision();
|
|
48
49
|
const comparisonColor = computed(() => {
|
|
@@ -236,9 +237,11 @@ const hasMeta = computed(
|
|
|
236
237
|
{{ primaryLegendLabel }}
|
|
237
238
|
</span>
|
|
238
239
|
<span class="flex items-center gap-1.5">
|
|
240
|
+
<!-- The token itself, not its resolved value: a server render
|
|
241
|
+
cannot read the theme, and must draw what the browser draws. -->
|
|
239
242
|
<span
|
|
240
243
|
:class="LEGEND_KEY_CLASS"
|
|
241
|
-
|
|
244
|
+
class="bg-(--dms-chart-2)"
|
|
242
245
|
aria-hidden="true"
|
|
243
246
|
/>
|
|
244
247
|
{{ comparisonLegendLabel }}
|
|
@@ -367,41 +367,41 @@ defineExpose(flow);
|
|
|
367
367
|
halo (which only targets the built-in node types), keeping node and edge
|
|
368
368
|
selection states visually consistent. */
|
|
369
369
|
.dms-flow-canvas .vue-flow__node.selected {
|
|
370
|
-
box-shadow: 0 0 0 1.5px var(--color-
|
|
370
|
+
box-shadow: 0 0 0 1.5px var(--ui-color-primary-500);
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
-
/* Handles:
|
|
373
|
+
/* Handles: primary accent, ringed with the card surface for contrast. */
|
|
374
374
|
.dms-flow-canvas .vue-flow__handle {
|
|
375
375
|
width: 9px;
|
|
376
376
|
height: 9px;
|
|
377
|
-
background: var(--color-
|
|
377
|
+
background: var(--ui-color-primary-500);
|
|
378
378
|
border: 2px solid var(--dms-surface-card);
|
|
379
379
|
border-radius: 9999px;
|
|
380
380
|
}
|
|
381
381
|
|
|
382
382
|
.dms-flow-canvas .vue-flow__handle:hover {
|
|
383
|
-
background: var(--color-
|
|
383
|
+
background: var(--ui-color-primary-400);
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
-
/* Edges: hairline by default,
|
|
386
|
+
/* Edges: hairline by default, primary accent on selection / animation. */
|
|
387
387
|
.dms-flow-canvas .vue-flow__edge-path {
|
|
388
388
|
stroke: var(--ui-border-accented);
|
|
389
389
|
stroke-width: 1.5;
|
|
390
390
|
}
|
|
391
391
|
|
|
392
392
|
.dms-flow-canvas .vue-flow__edge.animated .vue-flow__edge-path {
|
|
393
|
-
stroke: var(--color-
|
|
393
|
+
stroke: var(--ui-color-primary-500);
|
|
394
394
|
}
|
|
395
395
|
|
|
396
396
|
.dms-flow-canvas .vue-flow__edge.selected .vue-flow__edge-path,
|
|
397
397
|
.dms-flow-canvas .vue-flow__edge:focus .vue-flow__edge-path,
|
|
398
398
|
.dms-flow-canvas .vue-flow__edge:focus-visible .vue-flow__edge-path {
|
|
399
|
-
stroke: var(--color-
|
|
399
|
+
stroke: var(--ui-color-primary-500);
|
|
400
400
|
stroke-width: 2;
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
.dms-flow-canvas .vue-flow__connection-path {
|
|
404
|
-
stroke: var(--color-
|
|
404
|
+
stroke: var(--ui-color-primary-500);
|
|
405
405
|
stroke-width: 2;
|
|
406
406
|
}
|
|
407
407
|
|
|
@@ -514,7 +514,7 @@ defineExpose(flow);
|
|
|
514
514
|
}
|
|
515
515
|
|
|
516
516
|
.dms-flow-canvas__delete-button:focus-visible {
|
|
517
|
-
outline: 2px solid var(--color-
|
|
517
|
+
outline: 2px solid var(--ui-color-primary-500);
|
|
518
518
|
outline-offset: 1px;
|
|
519
519
|
}
|
|
520
520
|
</style>
|