@antelopejs/dms 0.8.2 → 0.9.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 +36 -0
- 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 +4 -2
- package/dist/implementations/dms/frontend-modules.js +8 -7
- package/dist/implementations/dms/frontend-modules.js.map +1 -1
- package/dist/implementations/dms/manifest.d.ts +6 -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 +11 -5
- package/dist/implementations/dms/page.js +56 -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/realtime.d.ts +5 -1
- package/dist/routes/realtime.js +16 -9
- package/dist/routes/realtime.js.map +1 -1
- 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/interfaces/dms-base/table-and-card-catalog.test.js +25 -0
- package/dist/test/unit/interfaces/dms-base/table-and-card-catalog.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/validation/regional-preferences.schema.d.ts +2 -2
- 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/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/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/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 +6 -28
- 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/page.ts +13 -0
- 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 +2 -2
- package/frontend-vue/pnpm-lock.yaml +5 -5
- 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/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.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
|
@@ -1,105 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
interface FileMetadataUrlResponse {
|
|
4
|
-
url: string;
|
|
5
|
-
expiresAt?: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
type CachedFileReadUrl = FileMetadataUrlResponse;
|
|
9
|
-
|
|
10
|
-
const URL_REFRESH_LEAD_MS = 5000;
|
|
11
|
-
const RETRY_DELAY_MS = 5000;
|
|
1
|
+
import { useFileMetadata } from "../build/composables/files/useFileMetadata";
|
|
12
2
|
|
|
13
3
|
/**
|
|
14
4
|
* Resolves stored file resource keys to read URLs through
|
|
15
|
-
* `/api/files/metadata`.
|
|
16
|
-
*
|
|
5
|
+
* `/api/files/metadata`. A key resolved during the setup (an immediate
|
|
6
|
+
* watcher) is read with the page, the server render included; expiring URLs
|
|
7
|
+
* refresh while visible, and failed lookups that may heal are retried
|
|
8
|
+
* instead of permanently hiding the file.
|
|
17
9
|
*/
|
|
18
10
|
export function useFileReadUrls(storage?: string) {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
function isFresh(metadata: CachedFileReadUrl): boolean {
|
|
27
|
-
return (
|
|
28
|
-
metadata.expiresAt === undefined ||
|
|
29
|
-
metadata.expiresAt - URL_REFRESH_LEAD_MS > Date.now()
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function scheduleRefresh(key: string, expiresAt?: number): void {
|
|
34
|
-
if (isDisposed || import.meta.env.SSR) return;
|
|
35
|
-
const currentTimer = refreshTimers.get(key);
|
|
36
|
-
if (currentTimer) clearTimeout(currentTimer);
|
|
37
|
-
if (expiresAt === undefined) return;
|
|
38
|
-
const remaining = Math.max(expiresAt - Date.now(), 0);
|
|
39
|
-
const refreshLead = Math.min(URL_REFRESH_LEAD_MS, remaining / 2);
|
|
40
|
-
const delay = Math.max(remaining - refreshLead, 0);
|
|
41
|
-
refreshTimers.set(
|
|
42
|
-
key,
|
|
43
|
-
setTimeout(() => resolve(key, true), delay),
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function resolve(key: string, force = false): Promise<void> {
|
|
48
|
-
if (isDisposed) return;
|
|
49
|
-
const cached = urls.value.get(key);
|
|
50
|
-
if (!force && cached && isFresh(cached)) return;
|
|
51
|
-
if (pendingKeys.has(key)) return;
|
|
52
|
-
|
|
53
|
-
pendingKeys.add(key);
|
|
54
|
-
try {
|
|
55
|
-
const metadata = await $authFetch<FileMetadataUrlResponse>(
|
|
56
|
-
"/api/files/metadata",
|
|
57
|
-
{
|
|
58
|
-
query: { resourceKey: key, storage },
|
|
59
|
-
headers: contentLanguage
|
|
60
|
-
? { [CONTENT_LANGUAGE_HEADER]: contentLanguage }
|
|
61
|
-
: undefined,
|
|
62
|
-
},
|
|
63
|
-
);
|
|
64
|
-
if (isDisposed) return;
|
|
65
|
-
urls.value.set(key, metadata);
|
|
66
|
-
scheduleRefresh(key, metadata.expiresAt);
|
|
67
|
-
} catch {
|
|
68
|
-
if (isDisposed) return;
|
|
69
|
-
if (cached?.expiresAt !== undefined && cached.expiresAt <= Date.now()) {
|
|
70
|
-
urls.value.delete(key);
|
|
71
|
-
} else if (cached?.expiresAt !== undefined) {
|
|
72
|
-
scheduleRefresh(key, cached.expiresAt);
|
|
73
|
-
}
|
|
74
|
-
if (
|
|
75
|
-
!import.meta.env.SSR &&
|
|
76
|
-
(!cached ||
|
|
77
|
-
(cached.expiresAt !== undefined && cached.expiresAt <= Date.now()))
|
|
78
|
-
) {
|
|
79
|
-
refreshTimers.set(
|
|
80
|
-
key,
|
|
81
|
-
setTimeout(() => resolve(key, true), RETRY_DELAY_MS),
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
pendingKeys.delete(key);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function getUrl(key: string): string | undefined {
|
|
89
|
-
const metadata = urls.value.get(key);
|
|
90
|
-
if (
|
|
91
|
-
!metadata ||
|
|
92
|
-
(metadata.expiresAt !== undefined && metadata.expiresAt <= Date.now())
|
|
93
|
-
) {
|
|
94
|
-
return undefined;
|
|
95
|
-
}
|
|
96
|
-
return metadata.url;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
onScopeDispose(() => {
|
|
100
|
-
isDisposed = true;
|
|
101
|
-
refreshTimers.forEach(clearTimeout);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
return { getUrl, resolve };
|
|
11
|
+
const files = useFileMetadata(storage);
|
|
12
|
+
return {
|
|
13
|
+
/** The URL to show `key` with, until it expires; undefined until read. */
|
|
14
|
+
getUrl: (key: string): string | undefined => files.metadata(key)?.url,
|
|
15
|
+
resolve: (key: string): Promise<void> => files.resolve(key),
|
|
16
|
+
};
|
|
105
17
|
}
|
|
@@ -11,6 +11,15 @@ export const BLOCK_REFRESH_EVENT = "DmsComponent.Block.Refresh";
|
|
|
11
11
|
*/
|
|
12
12
|
export const LIVE_BLOCK_REFRESH_EVENT = "DmsComponent.Block.LiveRefresh";
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* The detail of a `refreshPageBlocks()` event: a listener whose refresh
|
|
16
|
+
* takes time hands its promise to `waitUntil`, and the call resolves once
|
|
17
|
+
* every one settled.
|
|
18
|
+
*/
|
|
19
|
+
export interface PageBlocksRefreshDetail {
|
|
20
|
+
waitUntil(work: Promise<unknown>): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
/** The watch functions every block can name (see interface-dms). */
|
|
15
24
|
export const BlockFunctions = {
|
|
16
25
|
REFRESH_PAGE: "DmsComponent.Block.RefreshPage",
|
|
@@ -23,10 +32,30 @@ export const BlockFunctions = {
|
|
|
23
32
|
* through `BlockFunctions.REFRESH_PAGE` once it saved. A user action always
|
|
24
33
|
* refreshes: on a page declared with `live` it reads even while paused, and
|
|
25
34
|
* the blocks it reads no longer count what they had missed.
|
|
35
|
+
*
|
|
36
|
+
* @returns Resolves once the page's data refresh (its `useApiData` reads)
|
|
37
|
+
* is done, failed reads included: they keep their `error`
|
|
26
38
|
*/
|
|
27
|
-
export function refreshPageBlocks(): void {
|
|
28
|
-
if (typeof window === "undefined") return;
|
|
29
|
-
|
|
39
|
+
export function refreshPageBlocks(): Promise<void> {
|
|
40
|
+
if (typeof window === "undefined") return Promise.resolve();
|
|
41
|
+
const pending: Array<Promise<unknown>> = [];
|
|
42
|
+
const detail: PageBlocksRefreshDetail = {
|
|
43
|
+
waitUntil: (work) => void pending.push(work),
|
|
44
|
+
};
|
|
45
|
+
window.dispatchEvent(new CustomEvent(BLOCK_REFRESH_EVENT, { detail }));
|
|
46
|
+
return Promise.allSettled(pending).then(() => undefined);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Has `refreshPageBlocks()` wait for `work` too, when `event` is one of its
|
|
51
|
+
* events.
|
|
52
|
+
*/
|
|
53
|
+
export function waitForPageBlocksRefresh(
|
|
54
|
+
event: Event,
|
|
55
|
+
work: Promise<unknown>,
|
|
56
|
+
): void {
|
|
57
|
+
const detail = (event as CustomEvent<PageBlocksRefreshDetail | null>).detail;
|
|
58
|
+
detail?.waitUntil(work);
|
|
30
59
|
}
|
|
31
60
|
|
|
32
61
|
/**
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"license": "Apache-2.0",
|
|
12
12
|
"packageManager": "pnpm@10.6.5",
|
|
13
13
|
"engines": {
|
|
14
|
-
"@antelopejs/dms-frontend": ">=0.
|
|
14
|
+
"@antelopejs/dms-frontend": ">=0.6.0 <0.7.0"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
17
|
"typecheck": "ajs dms verify-source --layer \"$PWD\" --local-package @antelopejs/dms=.. --local-package @antelopejs/interface-dms=../../interface-dms",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@antelopejs/core": ">=1.13.4 <2",
|
|
77
|
-
"@antelopejs/dms-frontend": ">=0.
|
|
77
|
+
"@antelopejs/dms-frontend": ">=0.6.0 <0.7.0",
|
|
78
78
|
"@eslint/js": "^8.57.0",
|
|
79
79
|
"@inertiajs/vue3": "^3.7.0",
|
|
80
80
|
"@nuxt/ui": "4.10.0",
|
|
@@ -160,8 +160,8 @@ importers:
|
|
|
160
160
|
specifier: '>=1.13.4 <2'
|
|
161
161
|
version: 1.13.5
|
|
162
162
|
'@antelopejs/dms-frontend':
|
|
163
|
-
specifier: '>=0.
|
|
164
|
-
version: 0.
|
|
163
|
+
specifier: '>=0.6.0 <0.7.0'
|
|
164
|
+
version: 0.6.0(@antelopejs/core@1.13.5)
|
|
165
165
|
'@eslint/js':
|
|
166
166
|
specifier: ^8.57.0
|
|
167
167
|
version: 8.57.0
|
|
@@ -237,8 +237,8 @@ packages:
|
|
|
237
237
|
engines: {node: '>=20.19'}
|
|
238
238
|
hasBin: true
|
|
239
239
|
|
|
240
|
-
'@antelopejs/dms-frontend@0.
|
|
241
|
-
resolution: {integrity: sha512-
|
|
240
|
+
'@antelopejs/dms-frontend@0.6.0':
|
|
241
|
+
resolution: {integrity: sha512-1514XxAK7qPRdOrGDd4/lJ8Xo9Fq9nMUY+7HZe1isRTbuwkJoFiUUHsssICm5hWeTWtuTEtLe+Pwe6wHokYSaA==}
|
|
242
242
|
engines: {node: '>=20.12.0'}
|
|
243
243
|
hasBin: true
|
|
244
244
|
peerDependencies:
|
|
@@ -4208,7 +4208,7 @@ snapshots:
|
|
|
4208
4208
|
semver: 7.8.5
|
|
4209
4209
|
typescript: 5.9.3
|
|
4210
4210
|
|
|
4211
|
-
'@antelopejs/dms-frontend@0.
|
|
4211
|
+
'@antelopejs/dms-frontend@0.6.0(@antelopejs/core@1.13.5)':
|
|
4212
4212
|
dependencies:
|
|
4213
4213
|
'@antelopejs/core': 1.13.5
|
|
4214
4214
|
chokidar: 4.0.3
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { DmsServerEvent } from "#dms/server";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A secret of the DMS's private options, as the frontend server hands them
|
|
5
|
+
* to its routes. The manifest's value wins; the environment variable of the
|
|
6
|
+
* same secret is the fallback, read only when the manifest holds none. A
|
|
7
|
+
* frontend built without the bootstrap credential gets no private options at
|
|
8
|
+
* all, so the environment variable alone configures it there.
|
|
9
|
+
*
|
|
10
|
+
* @param event The route's event
|
|
11
|
+
* @param section The private options' section (`htmlRender`, `oauth`)
|
|
12
|
+
* @param key The secret's key in it
|
|
13
|
+
* @param variable The environment variable that configures it
|
|
14
|
+
*/
|
|
15
|
+
export function privateSecret(
|
|
16
|
+
event: DmsServerEvent,
|
|
17
|
+
section: string,
|
|
18
|
+
key: string,
|
|
19
|
+
variable: string,
|
|
20
|
+
): string | undefined {
|
|
21
|
+
const options = event.module.privateOptions[section] as
|
|
22
|
+
| Record<string, unknown>
|
|
23
|
+
| undefined;
|
|
24
|
+
const value = options?.[key];
|
|
25
|
+
if (typeof value === "string" && value) return value;
|
|
26
|
+
return process.env[variable] || undefined;
|
|
27
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
/**
|
|
3
|
+
* Every key of the async data lives in the scope of the session's account and
|
|
4
|
+
* workspace: the browser finds what the server transferred under the same
|
|
5
|
+
* scope, and a switch of account or workspace drops the previous session's
|
|
6
|
+
* data instead of showing it under the new one.
|
|
7
|
+
*/
|
|
8
|
+
import {
|
|
9
|
+
type App,
|
|
10
|
+
createApp,
|
|
11
|
+
createSSRApp,
|
|
12
|
+
defineComponent,
|
|
13
|
+
h,
|
|
14
|
+
nextTick,
|
|
15
|
+
Suspense,
|
|
16
|
+
} from "vue";
|
|
17
|
+
import { renderToString } from "vue/server-renderer";
|
|
18
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
19
|
+
import type { DmsFrontendRuntime } from "#dms/frontend-module";
|
|
20
|
+
import { asyncDataScopeOf } from "../layers/dms-core/app/build/utils/asyncDataScope";
|
|
21
|
+
|
|
22
|
+
const engine = await import("#dms/frontend-module");
|
|
23
|
+
const { default: asyncDataScopePlugin } = await import(
|
|
24
|
+
"../layers/dms-core/app/plugins/async-data-scope"
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const ACCOUNT_A = { accountId: "account-a", activeTenantId: "tenant-1" };
|
|
28
|
+
const ACCOUNT_B = { accountId: "account-b", activeTenantId: "tenant-1" };
|
|
29
|
+
|
|
30
|
+
const handler = vi.fn(async () => "orders of account A");
|
|
31
|
+
|
|
32
|
+
const Page = defineComponent({
|
|
33
|
+
async setup() {
|
|
34
|
+
const { data } = await engine.useDmsAsyncData("orders", handler);
|
|
35
|
+
return () => h("p", data.value ?? "none");
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const Root = defineComponent({
|
|
40
|
+
setup: () => () => h(Suspense, null, { default: () => h(Page) }),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const mounted: App[] = [];
|
|
44
|
+
|
|
45
|
+
function start(app: App, runtime: DmsFrontendRuntime, session: object) {
|
|
46
|
+
engine.provideDmsFrontendRuntime(app, runtime);
|
|
47
|
+
app.runWithContext(() => {
|
|
48
|
+
engine.useDmsState("dms-session", () => null).value = session;
|
|
49
|
+
void asyncDataScopePlugin({} as never);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function settle(): Promise<void> {
|
|
54
|
+
await nextTick();
|
|
55
|
+
await new Promise((done) => setTimeout(done, 0));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
beforeEach(async () => {
|
|
59
|
+
handler.mockClear();
|
|
60
|
+
await engine.setupFrontendModules([]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
mounted.splice(0).forEach((app) => app.unmount());
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("the async data scope", () => {
|
|
68
|
+
it("is a digest of the account and the workspace, none when signed out", () => {
|
|
69
|
+
const scope = asyncDataScopeOf(ACCOUNT_A);
|
|
70
|
+
expect(scope).toMatch(/^[0-9a-z]+$/);
|
|
71
|
+
expect(scope).not.toContain("account-a");
|
|
72
|
+
expect(asyncDataScopeOf({ ...ACCOUNT_A })).toBe(scope);
|
|
73
|
+
expect(asyncDataScopeOf(ACCOUNT_B)).not.toBe(scope);
|
|
74
|
+
expect(
|
|
75
|
+
asyncDataScopeOf({ ...ACCOUNT_A, activeTenantId: "tenant-2" }),
|
|
76
|
+
).not.toBe(scope);
|
|
77
|
+
expect(asyncDataScopeOf(null)).toBeUndefined();
|
|
78
|
+
expect(asyncDataScopeOf({})).toBeUndefined();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("hands the server's data to the browser under the same session", async () => {
|
|
82
|
+
const server = engine.createDmsFrontendRuntime(undefined, {}, true);
|
|
83
|
+
const serverApp = createSSRApp(Root);
|
|
84
|
+
start(serverApp, server, ACCOUNT_A);
|
|
85
|
+
expect(await renderToString(serverApp)).toBe("<p>orders of account A</p>");
|
|
86
|
+
const payload = structuredClone(engine.serializeDmsPayload(server));
|
|
87
|
+
expect(JSON.stringify(payload)).not.toContain("account-a");
|
|
88
|
+
|
|
89
|
+
handler.mockClear();
|
|
90
|
+
const browser = engine.createDmsFrontendRuntime(undefined, {});
|
|
91
|
+
const browserApp = createApp(Root);
|
|
92
|
+
start(browserApp, browser, ACCOUNT_A);
|
|
93
|
+
engine.restoreDmsPayload(browser, payload);
|
|
94
|
+
const host = document.createElement("div");
|
|
95
|
+
browserApp.mount(host);
|
|
96
|
+
mounted.push(browserApp);
|
|
97
|
+
await settle();
|
|
98
|
+
|
|
99
|
+
expect(handler).not.toHaveBeenCalled();
|
|
100
|
+
expect(host.textContent).toBe("orders of account A");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("drops the previous account's data once the session switches", async () => {
|
|
104
|
+
const browser = engine.createDmsFrontendRuntime(undefined, {});
|
|
105
|
+
const browserApp = createApp(Root);
|
|
106
|
+
start(browserApp, browser, ACCOUNT_A);
|
|
107
|
+
const host = document.createElement("div");
|
|
108
|
+
browserApp.mount(host);
|
|
109
|
+
mounted.push(browserApp);
|
|
110
|
+
await settle();
|
|
111
|
+
expect(host.textContent).toBe("orders of account A");
|
|
112
|
+
|
|
113
|
+
browserApp.runWithContext(() => {
|
|
114
|
+
engine.useDmsState("dms-session", () => null).value = ACCOUNT_B;
|
|
115
|
+
});
|
|
116
|
+
await settle();
|
|
117
|
+
|
|
118
|
+
expect(browser.asyncData.size).toBe(0);
|
|
119
|
+
expect(host.textContent).toBe("none");
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -8,14 +8,14 @@ const navigateDms = vi.fn();
|
|
|
8
8
|
beforeEach(() => {
|
|
9
9
|
vi.resetModules();
|
|
10
10
|
vi.stubGlobal("defineDmsMiddleware", (handler: Middleware) => handler);
|
|
11
|
-
vi.stubGlobal("
|
|
12
|
-
vi.stubGlobal("
|
|
11
|
+
vi.stubGlobal("useDmsError", () => pageError);
|
|
12
|
+
vi.stubGlobal("abortDmsNavigation", () => false);
|
|
13
13
|
vi.stubGlobal("navigateDms", navigateDms);
|
|
14
14
|
vi.stubGlobal("useSiteLayout", () => ({
|
|
15
15
|
siteLayout: { value: {} },
|
|
16
16
|
findMatchingRoute: () => ({ metadata: {} }),
|
|
17
17
|
}));
|
|
18
|
-
vi.stubGlobal("
|
|
18
|
+
vi.stubGlobal("useDmsUserSession", () => ({ loggedIn: { value: false } }));
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
afterEach(() => {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { createApp, defineComponent, h, reactive, ref } from "vue";
|
|
9
9
|
import { afterEach, beforeEach, expect, it, vi } from "vitest";
|
|
10
|
+
import { useRealDataEngine } from "./fixtures/dataEngine";
|
|
10
11
|
import { refreshPageBlocks } from "../layers/dms-ui/app/utils/blockRefresh";
|
|
11
12
|
|
|
12
13
|
const requests: string[] = [];
|
|
@@ -93,6 +94,8 @@ function mountBanner(props: Record<string, unknown>) {
|
|
|
93
94
|
};
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
beforeEach(() => useRealDataEngine());
|
|
98
|
+
|
|
96
99
|
beforeEach(() => {
|
|
97
100
|
requests.length = 0;
|
|
98
101
|
pressed.length = 0;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { type Component, createSSRApp, defineComponent, h, ref } from "vue";
|
|
7
7
|
import { renderToString } from "vue/server-renderer";
|
|
8
8
|
import { afterEach, beforeEach, expect, it, vi } from "vitest";
|
|
9
|
+
import { useRealDataEngine } from "./fixtures/dataEngine";
|
|
9
10
|
import type { UseChartFetchOptions } from "../layers/dms-ui/app/composables/chart/useChartFetch";
|
|
10
11
|
|
|
11
12
|
const fetchOptions: Array<UseChartFetchOptions<unknown>> = [];
|
|
@@ -78,6 +79,8 @@ async function render(block: Component): Promise<void> {
|
|
|
78
79
|
await renderToString(app);
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
beforeEach(() => useRealDataEngine());
|
|
83
|
+
|
|
81
84
|
beforeEach(() => {
|
|
82
85
|
fetchOptions.length = 0;
|
|
83
86
|
vi.stubGlobal("useI18n", () => ({ locale: ref("en-GB"), t: String }));
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { createApp, defineComponent, h, nextTick, reactive, ref } from "vue";
|
|
9
9
|
import { afterEach, beforeEach, expect, it, vi } from "vitest";
|
|
10
|
+
import { useRealDataEngine } from "./fixtures/dataEngine";
|
|
10
11
|
import type { RealtimeTopicHandler } from "../layers/dms-core/app/composables/realtime/useUserRealtime";
|
|
11
12
|
|
|
12
13
|
const requests: string[] = [];
|
|
@@ -106,6 +107,8 @@ function mountRealtimeBlock(probe: ItemsProbe) {
|
|
|
106
107
|
mounted.push(app);
|
|
107
108
|
}
|
|
108
109
|
|
|
110
|
+
beforeEach(() => useRealDataEngine());
|
|
111
|
+
|
|
109
112
|
beforeEach(() => {
|
|
110
113
|
requests.length = 0;
|
|
111
114
|
revision = 1;
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
ref,
|
|
17
17
|
} from "vue";
|
|
18
18
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
19
|
+
import { useRealDataEngine } from "./fixtures/dataEngine";
|
|
19
20
|
import { readFetchNotFound } from "../layers/dms-ui/app/build/utils/fetchNotFound";
|
|
20
21
|
|
|
21
22
|
interface FetchFailure extends Error {
|
|
@@ -92,6 +93,8 @@ function mount(component: Component): HTMLElement {
|
|
|
92
93
|
return host;
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
beforeEach(() => useRealDataEngine());
|
|
97
|
+
|
|
95
98
|
beforeEach(() => {
|
|
96
99
|
answers.clear();
|
|
97
100
|
route.query = { workspace: "web" };
|
|
@@ -157,12 +160,23 @@ describe("useChartFetch on a 404", () => {
|
|
|
157
160
|
);
|
|
158
161
|
});
|
|
159
162
|
|
|
160
|
-
it("keeps them
|
|
163
|
+
it("keeps them when a read of the same request fails otherwise", async () => {
|
|
161
164
|
const state = mountFetch();
|
|
162
165
|
await settle();
|
|
163
|
-
|
|
166
|
+
answers.set("/api/kpis?workspace=web", failure(500));
|
|
167
|
+
await state.refresh?.();
|
|
168
|
+
await settle();
|
|
164
169
|
expect(state.data?.value).toEqual({ items: [{ label: "web" }] });
|
|
165
170
|
});
|
|
171
|
+
|
|
172
|
+
it("never shows a record's values for another request that failed", async () => {
|
|
173
|
+
// They belong to another workspace: the card shows its failure instead.
|
|
174
|
+
const state = mountFetch();
|
|
175
|
+
await settle();
|
|
176
|
+
await switchWorkspace("down");
|
|
177
|
+
expect(state.data?.value).toBeNull();
|
|
178
|
+
expect(state.error?.value).toBeTruthy();
|
|
179
|
+
});
|
|
166
180
|
});
|
|
167
181
|
|
|
168
182
|
describe("useBlockItems on a 404", () => {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { createApp, defineComponent, h, ref } from "vue";
|
|
9
9
|
import { afterEach, beforeEach, expect, it, vi } from "vitest";
|
|
10
|
+
import { useRealDataEngine } from "./fixtures/dataEngine";
|
|
10
11
|
import {
|
|
11
12
|
BlockFunctions,
|
|
12
13
|
refreshPageBlocks,
|
|
@@ -64,6 +65,8 @@ function mountBlock(fetchUrl: string) {
|
|
|
64
65
|
|
|
65
66
|
const apps: Array<() => void> = [];
|
|
66
67
|
|
|
68
|
+
beforeEach(() => useRealDataEngine());
|
|
69
|
+
|
|
67
70
|
beforeEach(() => {
|
|
68
71
|
requests.length = 0;
|
|
69
72
|
answer = 1;
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
ref,
|
|
16
16
|
} from "vue";
|
|
17
17
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
18
|
+
import { useRealDataEngine } from "./fixtures/dataEngine";
|
|
18
19
|
import {
|
|
19
20
|
replaceUrlVariables,
|
|
20
21
|
resolveTextVariables,
|
|
@@ -59,6 +60,8 @@ function mount(component: Component) {
|
|
|
59
60
|
mounted.push(app);
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
beforeEach(() => useRealDataEngine());
|
|
64
|
+
|
|
62
65
|
beforeEach(() => {
|
|
63
66
|
requests.length = 0;
|
|
64
67
|
route.query = {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defu } from "defu";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import appConfig from "../layers/dms-layout/app/app.config";
|
|
4
|
+
|
|
5
|
+
const ANTELOPE_ICON = "/images/antelope-logo/icon.svg";
|
|
6
|
+
|
|
7
|
+
describe("branding defaults", () => {
|
|
8
|
+
it("hold the palettes and logos the layout ships", () => {
|
|
9
|
+
expect(appConfig.branding.defaults).toEqual({
|
|
10
|
+
colors: appConfig.ui.colors,
|
|
11
|
+
logo: appConfig.branding.logo,
|
|
12
|
+
});
|
|
13
|
+
expect(appConfig.branding.defaults.colors.primary).toBe("dms");
|
|
14
|
+
expect(appConfig.branding.logo.favicon).toEqual({
|
|
15
|
+
light: ANTELOPE_ICON,
|
|
16
|
+
dark: ANTELOPE_ICON,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("survive a live edit of the merged palette and logos", () => {
|
|
21
|
+
// A clone keeps any object shared between the two places, like the
|
|
22
|
+
// loader's merge keeps the objects no layer overrides.
|
|
23
|
+
const merged = defu(
|
|
24
|
+
{ branding: { footer: { githubStar: false } } },
|
|
25
|
+
structuredClone(appConfig),
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
merged.ui.colors.primary = "violet";
|
|
29
|
+
merged.branding.logo.favicon!.light = "/branding/icon.png";
|
|
30
|
+
|
|
31
|
+
expect(merged.branding.defaults.colors.primary).toBe("dms");
|
|
32
|
+
expect(merged.branding.defaults.logo.favicon?.light).toBe(ANTELOPE_ICON);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -4,6 +4,7 @@ import { renderToString } from "vue/server-renderer";
|
|
|
4
4
|
import { createI18n } from "vue-i18n";
|
|
5
5
|
import en from "../layers/dms-layout/i18n/locales/layout-en-GB.json";
|
|
6
6
|
import fr from "../layers/dms-layout/i18n/locales/layout-fr-FR.json";
|
|
7
|
+
import type { DmsBrandingLogo } from "../layers/dms-core/shared/types/app-config";
|
|
7
8
|
|
|
8
9
|
interface EmailModule {
|
|
9
10
|
default: Component;
|
|
@@ -17,6 +18,10 @@ const emailModules = import.meta.glob<EmailModule>(
|
|
|
17
18
|
const PUBLIC_BASE = "https://admin.example.com";
|
|
18
19
|
const PUBLIC_PNG_LOGO = "/images/logo.png";
|
|
19
20
|
const SVG_LOGO = "/images/logo.svg";
|
|
21
|
+
const EMAIL_LOGOS: Partial<DmsBrandingLogo> = {
|
|
22
|
+
default: { light: SVG_LOGO, dark: SVG_LOGO },
|
|
23
|
+
email: { light: "/branding/email.png", dark: "/branding/email-dark.png" },
|
|
24
|
+
};
|
|
20
25
|
const ELEMENTS: Record<string, string> = {
|
|
21
26
|
EHtml: "html",
|
|
22
27
|
EHead: "head",
|
|
@@ -41,6 +46,11 @@ function element(tag: string) {
|
|
|
41
46
|
});
|
|
42
47
|
}
|
|
43
48
|
|
|
49
|
+
/** A path stands for `branding.logo.default.light`. */
|
|
50
|
+
function brandingLogo(logo: string | Partial<DmsBrandingLogo>) {
|
|
51
|
+
return typeof logo === "string" ? { default: { light: logo } } : logo;
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
function templateName(path: string): string {
|
|
45
55
|
return path.split("/").pop()?.replace(".vue", "") ?? path;
|
|
46
56
|
}
|
|
@@ -56,11 +66,11 @@ const EMAIL_PROPS = {
|
|
|
56
66
|
|
|
57
67
|
async function renderEmail(
|
|
58
68
|
component: Component,
|
|
59
|
-
logo: string
|
|
69
|
+
logo: string | Partial<DmsBrandingLogo>,
|
|
60
70
|
locale = "en",
|
|
61
71
|
): Promise<string> {
|
|
62
72
|
vi.stubGlobal("useDmsAppConfig", () => ({
|
|
63
|
-
branding: { logo:
|
|
73
|
+
branding: { logo: brandingLogo(logo) },
|
|
64
74
|
}));
|
|
65
75
|
vi.stubGlobal("useDmsRuntimeConfig", () => ({
|
|
66
76
|
public: { dms: { clientBaseUrl: PUBLIC_BASE } },
|
|
@@ -100,6 +110,17 @@ describe("built-in emails", () => {
|
|
|
100
110
|
expect(html).toContain(`<img src="${PUBLIC_BASE}${PUBLIC_PNG_LOGO}"`);
|
|
101
111
|
});
|
|
102
112
|
|
|
113
|
+
it.each(templates)(
|
|
114
|
+
"%s draws the email logo, with its dark variant",
|
|
115
|
+
async (_name, component) => {
|
|
116
|
+
const html = await renderEmail(component, EMAIL_LOGOS);
|
|
117
|
+
expect(html).toContain(
|
|
118
|
+
`<source media="(prefers-color-scheme: dark)" srcset="${PUBLIC_BASE}/branding/email-dark.png">`,
|
|
119
|
+
);
|
|
120
|
+
expect(html).toContain(`<img src="${PUBLIC_BASE}/branding/email.png"`);
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
|
|
103
124
|
it.each(templates)("%s is written in French", async (_name, component) => {
|
|
104
125
|
const html = await renderEmail(component, PUBLIC_PNG_LOGO, "fr");
|
|
105
126
|
expect(html).toContain("Bonjour");
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* refresh, and reads once when the page resumes.
|
|
5
5
|
*/
|
|
6
6
|
import { createApp, defineComponent, h, ref } from "vue";
|
|
7
|
-
import { afterEach, expect, it, vi } from "vitest";
|
|
7
|
+
import { beforeEach, afterEach, expect, it, vi } from "vitest";
|
|
8
|
+
import { useRealDataEngine } from "./fixtures/dataEngine";
|
|
8
9
|
import {
|
|
9
10
|
refreshLivePageBlocks,
|
|
10
11
|
refreshPageBlocks,
|
|
@@ -39,6 +40,8 @@ afterEach(() => {
|
|
|
39
40
|
window.localStorage.clear();
|
|
40
41
|
});
|
|
41
42
|
|
|
43
|
+
beforeEach(() => useRealDataEngine());
|
|
44
|
+
|
|
42
45
|
it("requests nothing while paused and reads once on resume", async () => {
|
|
43
46
|
const live = createPageLiveController("api.overview", {});
|
|
44
47
|
setCurrentPageLive(live);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createSSRApp, defineComponent, h, ref, type PropType } from "vue";
|
|
2
2
|
import { renderToString } from "vue/server-renderer";
|
|
3
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { beforeEach, afterEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { useRealDataEngine } from "./fixtures/dataEngine";
|
|
4
5
|
import ChartCard from "../layers/dms-ui/app/components/chart/ChartCard.vue";
|
|
5
6
|
import Chart from "../layers/dms-ui/app/components/chart/Chart.vue";
|
|
6
7
|
import KpiCard from "../layers/dms-ui/app/components/kpi/KpiCard.vue";
|
|
@@ -119,6 +120,8 @@ async function renderCards(
|
|
|
119
120
|
|
|
120
121
|
afterEach(() => vi.unstubAllGlobals());
|
|
121
122
|
|
|
123
|
+
beforeEach(() => useRealDataEngine());
|
|
124
|
+
|
|
122
125
|
describe("card → nested chart → Apex format propagation", () => {
|
|
123
126
|
it.each(["EUR", "JPY", "KWD"])(
|
|
124
127
|
"propagates native %s formatting to headline, comparison, axis, tooltip and list",
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type Component,
|
|
14
14
|
} from "vue";
|
|
15
15
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
16
|
+
import { useRealDataEngine } from "./fixtures/dataEngine";
|
|
16
17
|
import { createI18n } from "vue-i18n";
|
|
17
18
|
import { setRegionalPreferencesSource } from "../layers/dms-core/app/utils/regional";
|
|
18
19
|
|
|
@@ -118,6 +119,8 @@ const seats = (value: number) => ({
|
|
|
118
119
|
params: { count: { type: "count", value } },
|
|
119
120
|
});
|
|
120
121
|
|
|
122
|
+
beforeEach(() => useRealDataEngine());
|
|
123
|
+
|
|
121
124
|
beforeEach(() => {
|
|
122
125
|
i18n.global.locale.value = "en";
|
|
123
126
|
fetchedItems = [];
|