@antelopejs/dms 0.8.3 → 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 +20 -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/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/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,4 +1,5 @@
|
|
|
1
|
-
import { computed, ref, type ComputedRef } from "vue";
|
|
1
|
+
import { computed, markRaw, ref, type ComputedRef, type Ref } from "vue";
|
|
2
|
+
import { resolvesPeriodsAsBrowser } from "./periodTimeZone";
|
|
2
3
|
import type { PeriodState } from "./types";
|
|
3
4
|
|
|
4
5
|
interface PeriodScopeRegistration {
|
|
@@ -6,65 +7,117 @@ interface PeriodScopeRegistration {
|
|
|
6
7
|
refCount: number;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
interface PeriodScopeRegistry {
|
|
11
|
+
scopes: Map<string, PeriodScopeRegistration>;
|
|
12
|
+
version: Ref<number>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const PERIOD_SCOPES_STATE_KEY = "dms-period-scopes";
|
|
11
16
|
|
|
12
|
-
function
|
|
13
|
-
|
|
17
|
+
function createRegistry(): PeriodScopeRegistry {
|
|
18
|
+
return markRaw({ scopes: new Map(), version: ref(0) });
|
|
14
19
|
}
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
// A browser tab runs one application, whose scopes every caller shares; a
|
|
22
|
+
// server renders many requests at once, each with its own scopes.
|
|
23
|
+
const browserRegistry = createRegistry();
|
|
24
|
+
|
|
25
|
+
function usePeriodScopeRegistry(): PeriodScopeRegistry {
|
|
26
|
+
if (typeof window !== "undefined") return browserRegistry;
|
|
27
|
+
return useDmsState(PERIOD_SCOPES_STATE_KEY, createRegistry).value;
|
|
28
|
+
}
|
|
17
29
|
|
|
18
30
|
export function registerPeriodScope(
|
|
19
31
|
id: string,
|
|
20
32
|
state: ComputedRef<PeriodState>,
|
|
21
33
|
): () => void {
|
|
22
|
-
|
|
23
|
-
const existing =
|
|
34
|
+
const registry = usePeriodScopeRegistry();
|
|
35
|
+
const existing = registry.scopes.get(id);
|
|
24
36
|
if (existing) {
|
|
25
37
|
existing.state = state;
|
|
26
38
|
existing.refCount += 1;
|
|
27
39
|
} else {
|
|
28
|
-
|
|
40
|
+
registry.scopes.set(id, { state, refCount: 1 });
|
|
29
41
|
}
|
|
30
|
-
|
|
42
|
+
registry.version.value += 1;
|
|
31
43
|
return () => {
|
|
32
|
-
const entry =
|
|
44
|
+
const entry = registry.scopes.get(id);
|
|
33
45
|
if (!entry) return;
|
|
34
46
|
entry.refCount -= 1;
|
|
35
|
-
if (entry.refCount <= 0)
|
|
36
|
-
|
|
47
|
+
if (entry.refCount <= 0) registry.scopes.delete(id);
|
|
48
|
+
registry.version.value += 1;
|
|
37
49
|
};
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
const warnedMissingScopes = new Set<string>();
|
|
41
53
|
const MISSING_SCOPE_WARN_DELAY_MS = 250;
|
|
42
54
|
|
|
43
|
-
function deferMissingScopeWarn(
|
|
55
|
+
function deferMissingScopeWarn(
|
|
56
|
+
registry: PeriodScopeRegistry,
|
|
57
|
+
id: string,
|
|
58
|
+
): void {
|
|
44
59
|
if (warnedMissingScopes.has(id)) return;
|
|
45
60
|
warnedMissingScopes.add(id);
|
|
46
61
|
if (typeof window === "undefined") return;
|
|
47
62
|
setTimeout(() => {
|
|
48
|
-
if (
|
|
63
|
+
if (registry.scopes.has(id)) return;
|
|
49
64
|
console.warn(
|
|
50
65
|
`[dms] periodScope "${id}" is not registered. The consumer will run without period filtering. Add a PeriodSelector with id="${id}" to bind it.`,
|
|
51
66
|
);
|
|
52
67
|
}, MISSING_SCOPE_WARN_DELAY_MS);
|
|
53
68
|
}
|
|
54
69
|
|
|
70
|
+
function readPeriodScope(
|
|
71
|
+
registry: PeriodScopeRegistry,
|
|
72
|
+
id: string,
|
|
73
|
+
): PeriodState | null {
|
|
74
|
+
void registry.version.value;
|
|
75
|
+
const entry = registry.scopes.get(id);
|
|
76
|
+
if (!entry) {
|
|
77
|
+
deferMissingScopeWarn(registry, id);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return entry.state.value;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The state of a period scope, `null` while no selector registers it.
|
|
85
|
+
*
|
|
86
|
+
* A server render reads it once, as the caller sets up: a selector that sets
|
|
87
|
+
* up later (further down the page) leaves the caller to the browser, whose
|
|
88
|
+
* first render must match the HTML. It also answers `null` for a period the
|
|
89
|
+
* browser could resolve to other bounds: before the browser's cookie names
|
|
90
|
+
* its time zone. A relative preset resolves from the instant the server
|
|
91
|
+
* hands the browser (`periodRenderNow`), so both read the same bounds.
|
|
92
|
+
*/
|
|
55
93
|
export function usePeriodScope(
|
|
56
94
|
id: string | undefined,
|
|
57
95
|
): ComputedRef<PeriodState | null> {
|
|
58
|
-
return computed(() =>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
96
|
+
if (!id) return computed(() => null);
|
|
97
|
+
const registry = usePeriodScopeRegistry();
|
|
98
|
+
if (typeof window !== "undefined")
|
|
99
|
+
return computed(() => readPeriodScope(registry, id));
|
|
100
|
+
const state = readPeriodScope(registry, id);
|
|
101
|
+
const resolvesAsBrowser = state !== null && resolvesPeriodsAsBrowser();
|
|
102
|
+
return computed(() => (resolvesAsBrowser ? state : null));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The preset of a period scope, `null` while no selector registers it: what
|
|
107
|
+
* a card's period badge names. A server render reads it once, as the caller
|
|
108
|
+
* sets up, like `usePeriodScope`, but whatever bounds the browser would
|
|
109
|
+
* resolve the period to: a preset is the same name on both sides, so the
|
|
110
|
+
* server prints the badge the browser hydrates.
|
|
111
|
+
*/
|
|
112
|
+
export function usePeriodScopePreset(
|
|
113
|
+
id: string | undefined,
|
|
114
|
+
): ComputedRef<PeriodState["preset"] | null> {
|
|
115
|
+
if (!id) return computed(() => null);
|
|
116
|
+
const registry = usePeriodScopeRegistry();
|
|
117
|
+
if (typeof window !== "undefined")
|
|
118
|
+
return computed(() => readPeriodScope(registry, id)?.preset ?? null);
|
|
119
|
+
const preset = readPeriodScope(registry, id)?.preset ?? null;
|
|
120
|
+
return computed(() => preset);
|
|
68
121
|
}
|
|
69
122
|
|
|
70
123
|
/**
|
|
@@ -10,6 +10,8 @@ export interface PageRealtimeApi {
|
|
|
10
10
|
subscribe(topic: string, handler: RealtimeTopicHandler): () => void;
|
|
11
11
|
sessionId: Ref<string | undefined>;
|
|
12
12
|
pageId: Ref<string | undefined>;
|
|
13
|
+
/** The page's realtime topics, once the session is subscribed to it. */
|
|
14
|
+
pageTopics: Ref<string[]>;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
interface PageClaim {
|
|
@@ -70,6 +72,7 @@ export function usePageRealtime(
|
|
|
70
72
|
subscribe: user.subscribe,
|
|
71
73
|
sessionId: user.sessionId,
|
|
72
74
|
pageId: resolvedPageId,
|
|
75
|
+
pageTopics: user.pageTopics,
|
|
73
76
|
};
|
|
74
77
|
}
|
|
75
78
|
|
|
@@ -51,12 +51,18 @@ export interface UserRealtimeApi {
|
|
|
51
51
|
* announced in the greeting.
|
|
52
52
|
*/
|
|
53
53
|
menuTopics: Ref<string[]>;
|
|
54
|
+
/**
|
|
55
|
+
* The realtime topics of the page this session is on, as its subscription
|
|
56
|
+
* answered them: an event on one means the page's data moved.
|
|
57
|
+
*/
|
|
58
|
+
pageTopics: Ref<string[]>;
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
interface UserRealtimeState {
|
|
57
62
|
sessionId: Ref<string | undefined>;
|
|
58
63
|
pageId: Ref<string | undefined>;
|
|
59
64
|
menuTopics: Ref<string[]>;
|
|
65
|
+
pageTopics: Ref<string[]>;
|
|
60
66
|
appliedPageId: Ref<string | null>;
|
|
61
67
|
handlers: Map<string, Set<RealtimeTopicHandler>>;
|
|
62
68
|
snapshots: Map<string, RealtimeSnapshotEvent>;
|
|
@@ -230,22 +236,42 @@ const connectWithBackoff = (
|
|
|
230
236
|
};
|
|
231
237
|
};
|
|
232
238
|
|
|
239
|
+
interface PageSubscriptionAnswer {
|
|
240
|
+
topics?: unknown;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** The topics a page subscription answered, none when it answered none. */
|
|
244
|
+
const readPageTopics = async (response: Response): Promise<string[]> => {
|
|
245
|
+
let answer: PageSubscriptionAnswer | null;
|
|
246
|
+
try {
|
|
247
|
+
answer = (await response.json()) as PageSubscriptionAnswer | null;
|
|
248
|
+
} catch {
|
|
249
|
+
return [];
|
|
250
|
+
}
|
|
251
|
+
const topics = answer?.topics;
|
|
252
|
+
return Array.isArray(topics)
|
|
253
|
+
? topics.filter((topic): topic is string => typeof topic === "string")
|
|
254
|
+
: [];
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/** Subscribes the session to `pageId`, or to no page; returns its topics. */
|
|
233
258
|
const callPageEndpoint = async (
|
|
234
259
|
state: UserRealtimeState,
|
|
235
260
|
pageId: string | null,
|
|
236
261
|
baseUrl: string,
|
|
237
|
-
): Promise<
|
|
262
|
+
): Promise<string[]> => {
|
|
238
263
|
const sessionId = state.sessionId.value;
|
|
239
|
-
if (!sessionId) return;
|
|
264
|
+
if (!sessionId) return [];
|
|
240
265
|
const headers: Record<string, string> = { [SESSION_HEADER]: sessionId };
|
|
241
266
|
const url = pageId
|
|
242
267
|
? `${baseUrl}${REALTIME_SUBSCRIBE_PATH_PREFIX}${pageId}`
|
|
243
268
|
: `${baseUrl}${REALTIME_UNSUBSCRIBE_PATH}`;
|
|
244
|
-
await fetch(url, {
|
|
269
|
+
const response = await fetch(url, {
|
|
245
270
|
method: "POST",
|
|
246
271
|
headers,
|
|
247
272
|
credentials: "include",
|
|
248
273
|
});
|
|
274
|
+
return pageId && response.ok ? readPageTopics(response) : [];
|
|
249
275
|
};
|
|
250
276
|
|
|
251
277
|
const applyDesiredPage = async (
|
|
@@ -256,11 +282,12 @@ const applyDesiredPage = async (
|
|
|
256
282
|
const sessionId = state.sessionId.value;
|
|
257
283
|
if (state.appliedPageId.value === desired) return;
|
|
258
284
|
if (!sessionId) return;
|
|
259
|
-
await callPageEndpoint(state, desired, baseUrl);
|
|
285
|
+
const topics = await callPageEndpoint(state, desired, baseUrl);
|
|
260
286
|
// A reconnect during the call replaced the session: the page is applied to
|
|
261
287
|
// the dead one, and the new one still needs it.
|
|
262
288
|
if (state.sessionId.value !== sessionId) return;
|
|
263
289
|
state.appliedPageId.value = desired;
|
|
290
|
+
state.pageTopics.value = topics;
|
|
264
291
|
};
|
|
265
292
|
|
|
266
293
|
/**
|
|
@@ -288,6 +315,7 @@ const reconcilePage = (
|
|
|
288
315
|
const resetSession = (state: UserRealtimeState): void => {
|
|
289
316
|
state.sessionId.value = undefined;
|
|
290
317
|
state.appliedPageId.value = null;
|
|
318
|
+
state.pageTopics.value = [];
|
|
291
319
|
state.snapshots.clear();
|
|
292
320
|
};
|
|
293
321
|
|
|
@@ -299,10 +327,12 @@ const buildApi = (): UserRealtimeApi => {
|
|
|
299
327
|
const menuTopics = useDmsState<string[]>(MENU_TOPICS_STATE_KEY, () => []);
|
|
300
328
|
const appliedPageId = ref<string | null>(null);
|
|
301
329
|
const pageId = ref<string | undefined>(undefined);
|
|
330
|
+
const pageTopics = ref<string[]>([]);
|
|
302
331
|
const state: UserRealtimeState = {
|
|
303
332
|
sessionId,
|
|
304
333
|
pageId,
|
|
305
334
|
menuTopics,
|
|
335
|
+
pageTopics,
|
|
306
336
|
appliedPageId,
|
|
307
337
|
handlers: new Map(),
|
|
308
338
|
snapshots: new Map(),
|
|
@@ -310,7 +340,7 @@ const buildApi = (): UserRealtimeApi => {
|
|
|
310
340
|
};
|
|
311
341
|
|
|
312
342
|
const config = useDmsRuntimeConfig();
|
|
313
|
-
const { loggedIn } =
|
|
343
|
+
const { loggedIn } = useDmsUserSession();
|
|
314
344
|
const { refreshSession, redirectToAuth } = useSessionRecovery();
|
|
315
345
|
const baseUrl = config.public.dms.baseURL as string;
|
|
316
346
|
|
|
@@ -366,7 +396,7 @@ const buildApi = (): UserRealtimeApi => {
|
|
|
366
396
|
await reconcilePage(state, baseUrl);
|
|
367
397
|
};
|
|
368
398
|
|
|
369
|
-
return { subscribe, setPage, sessionId, pageId, menuTopics };
|
|
399
|
+
return { subscribe, setPage, sessionId, pageId, menuTopics, pageTopics };
|
|
370
400
|
};
|
|
371
401
|
|
|
372
402
|
export function useUserRealtime(): UserRealtimeApi {
|
|
@@ -27,7 +27,7 @@ function localeTimeFormat(locale: string): TimeFormatPreference {
|
|
|
27
27
|
* the values themselves, such as a calendar's `weekStartsOn`.
|
|
28
28
|
*/
|
|
29
29
|
export function useUserRegionalPreferences() {
|
|
30
|
-
const { user } =
|
|
30
|
+
const { user } = useDmsUserSession<RegionalPreferenceFields>();
|
|
31
31
|
const { locale } = useI18n();
|
|
32
32
|
|
|
33
33
|
const preferences = computed<RegionalPreferences>(() =>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { watch } from "vue";
|
|
2
|
+
import {
|
|
3
|
+
defineDmsPlugin,
|
|
4
|
+
setDmsAsyncDataScope,
|
|
5
|
+
useDmsUserSession,
|
|
6
|
+
} from "#dms/frontend-module";
|
|
7
|
+
import {
|
|
8
|
+
asyncDataScopeOf,
|
|
9
|
+
type ScopedSession,
|
|
10
|
+
} from "../build/utils/asyncDataScope";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Keeps every `useApiData` / `useDmsAsyncData` key in the scope of the
|
|
14
|
+
* session's account and workspace, on both sides before the page renders:
|
|
15
|
+
* the browser finds the data the server transferred under the same scope,
|
|
16
|
+
* and a switch of account or workspace drops the data of the previous one
|
|
17
|
+
* instead of showing it under the new session.
|
|
18
|
+
*/
|
|
19
|
+
export default defineDmsPlugin(() => {
|
|
20
|
+
const { session } = useDmsUserSession<unknown, ScopedSession>();
|
|
21
|
+
const scope = () => asyncDataScopeOf(session.value);
|
|
22
|
+
setDmsAsyncDataScope(scope());
|
|
23
|
+
// One app per request on a server: its session never changes there.
|
|
24
|
+
if (typeof window === "undefined") return;
|
|
25
|
+
watch(scope, (next) => setDmsAsyncDataScope(next));
|
|
26
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Mirror of PAGE_ROUTE_PREFIX and pageRoutePath from
|
|
2
|
+
// @antelopejs/interface-dms/page.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Where the backend serves the routes a page controller declares, and its
|
|
6
|
+
* layout: under the DMS's own prefix, which the frontend server proxies. The
|
|
7
|
+
* page's own path is the page, never a backend route.
|
|
8
|
+
*/
|
|
9
|
+
export const PAGE_ROUTE_PREFIX = "/dms/pages";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The backend path of a page's route, as the browser calls it:
|
|
13
|
+
* `pageRoutePath("/settings/user/security", "/sessions")` is
|
|
14
|
+
* `/dms/pages/settings/user/security/sessions`.
|
|
15
|
+
*
|
|
16
|
+
* @param pageSlug The page's path, such as `/settings/user/security`
|
|
17
|
+
* @param route The route below the page; none for the page's own route
|
|
18
|
+
*/
|
|
19
|
+
export function pageRoutePath(pageSlug: string, route = ""): string {
|
|
20
|
+
const path = `${PAGE_ROUTE_PREFIX}/${pageSlug}/${route}`.replace(/\/+/g, "/");
|
|
21
|
+
return path.endsWith("/") ? path.slice(0, -1) : path;
|
|
22
|
+
}
|
|
@@ -37,6 +37,7 @@ export interface RegionalOptionsSettings {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
type PreferencesSource = () => RegionalPreferences;
|
|
40
|
+
type ReaderTimeZoneSource = () => string | null | undefined;
|
|
40
41
|
|
|
41
42
|
const WEEK_STARTS: readonly number[] = [0, 1, 6];
|
|
42
43
|
const TIME_FORMATS: readonly string[] = ["h23", "h12"];
|
|
@@ -73,6 +74,7 @@ const NUMERIC_DATE: Intl.DateTimeFormatOptions = {
|
|
|
73
74
|
|
|
74
75
|
const knownTimeZones = new Map<string, boolean>();
|
|
75
76
|
let preferencesSource: PreferencesSource | undefined;
|
|
77
|
+
let readerTimeZoneSource: ReaderTimeZoneSource | undefined;
|
|
76
78
|
|
|
77
79
|
/** Whether the runtime knows `timeZone` (an unknown one would throw in Intl). */
|
|
78
80
|
export function isKnownTimeZone(timeZone: string): boolean {
|
|
@@ -125,6 +127,35 @@ export function setRegionalPreferencesSource(source: PreferencesSource): void {
|
|
|
125
127
|
preferencesSource = source;
|
|
126
128
|
}
|
|
127
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Tells a server render where to read the zone of the browser it renders for
|
|
132
|
+
* (its `dms-time-zone` cookie). Called on every read, so it resolves the
|
|
133
|
+
* request being rendered rather than a value captured once.
|
|
134
|
+
*/
|
|
135
|
+
export function setReaderTimeZoneSource(source: ReaderTimeZoneSource): void {
|
|
136
|
+
readerTimeZoneSource = source;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The time zone of the browser the page is for, when it is not the zone the
|
|
141
|
+
* code runs in: `undefined` in the browser, where it is; on a server, the zone
|
|
142
|
+
* the browser's cookie names, if this runtime knows it. Dates computed or
|
|
143
|
+
* written for the browser use it, so the server renders what the browser
|
|
144
|
+
* renders.
|
|
145
|
+
*/
|
|
146
|
+
export function readerTimeZone(): string | undefined {
|
|
147
|
+
if (typeof window !== "undefined") return undefined;
|
|
148
|
+
let zone: string | null | undefined;
|
|
149
|
+
try {
|
|
150
|
+
zone = readerTimeZoneSource?.();
|
|
151
|
+
} catch {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
return zone && zone !== browserTimeZone() && isKnownTimeZone(zone)
|
|
155
|
+
? zone
|
|
156
|
+
: undefined;
|
|
157
|
+
}
|
|
158
|
+
|
|
128
159
|
/** The current user's preferences; none outside an application. */
|
|
129
160
|
export function currentRegionalPreferences(): RegionalPreferences {
|
|
130
161
|
try {
|
|
@@ -195,8 +226,10 @@ export function withRegionalOptions(
|
|
|
195
226
|
let result = preferences.dateFormat
|
|
196
227
|
? DATE_FORMAT_REWRITES[preferences.dateFormat](options)
|
|
197
228
|
: { ...options };
|
|
198
|
-
|
|
199
|
-
|
|
229
|
+
const timeZone =
|
|
230
|
+
(!settings.keepLocalZone && preferences.timeZone) || readerTimeZone();
|
|
231
|
+
if (timeZone && !result.timeZone) {
|
|
232
|
+
result = { ...result, timeZone };
|
|
200
233
|
}
|
|
201
234
|
const statesClock =
|
|
202
235
|
result.hour12 !== undefined || result.hourCycle !== undefined;
|
|
@@ -238,7 +271,7 @@ export function regionalDayNumber(
|
|
|
238
271
|
preferences: RegionalPreferences = currentRegionalPreferences(),
|
|
239
272
|
): number {
|
|
240
273
|
const parts = new Intl.DateTimeFormat("en-US", {
|
|
241
|
-
timeZone: preferences.timeZone,
|
|
274
|
+
timeZone: preferences.timeZone ?? readerTimeZone(),
|
|
242
275
|
year: "numeric",
|
|
243
276
|
month: "numeric",
|
|
244
277
|
day: "numeric",
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export interface TrailingDeduper {
|
|
2
2
|
run(task: () => Promise<void>): Promise<void>;
|
|
3
|
+
/**
|
|
4
|
+
* Joins the run in flight, or starts one: for a caller that needs a result
|
|
5
|
+
* rather than a fresh one, such as a first load, which a run started
|
|
6
|
+
* before it answers as well as its own would.
|
|
7
|
+
*/
|
|
8
|
+
join(task: () => Promise<void>): Promise<void>;
|
|
3
9
|
}
|
|
4
10
|
|
|
5
11
|
/**
|
|
@@ -38,5 +44,8 @@ export function createTrailingDeduper(): TrailingDeduper {
|
|
|
38
44
|
return trailing;
|
|
39
45
|
};
|
|
40
46
|
|
|
41
|
-
|
|
47
|
+
const join = (task: () => Promise<void>): Promise<void> =>
|
|
48
|
+
inFlight ?? run(task);
|
|
49
|
+
|
|
50
|
+
return { run, join };
|
|
42
51
|
}
|
|
@@ -12,6 +12,51 @@ export interface DmsLogoSources {
|
|
|
12
12
|
dark: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/** The logos of an instance, one per place it is drawn. */
|
|
16
|
+
export interface DmsBrandingLogo {
|
|
17
|
+
/** The expanded sidebar and the auth pages. */
|
|
18
|
+
default: DmsLogoSources;
|
|
19
|
+
/** The icon-only sidebar. */
|
|
20
|
+
collapsed: DmsLogoSources;
|
|
21
|
+
/**
|
|
22
|
+
* The header of the emails, in place of `default.light`. A PNG or a JPEG:
|
|
23
|
+
* Gmail and Outlook refuse SVG, and an email leaves out a logo they cannot
|
|
24
|
+
* load. `dark` goes to the mail clients that follow the reader's dark mode.
|
|
25
|
+
*/
|
|
26
|
+
email?: DmsLogoSources;
|
|
27
|
+
/**
|
|
28
|
+
* The browser tab icon. `light` and `dark` follow the operating system's
|
|
29
|
+
* color scheme, not the dashboard's color mode: the browser draws the tab
|
|
30
|
+
* outside the page.
|
|
31
|
+
*/
|
|
32
|
+
favicon?: DmsLogoSources;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The Tailwind palette each Nuxt UI color role resolves to (`ui.colors`): a
|
|
37
|
+
* stock palette name, or one whose `--color-<name>-*` shades a stylesheet
|
|
38
|
+
* declares.
|
|
39
|
+
*/
|
|
40
|
+
export interface DmsUiColors {
|
|
41
|
+
primary: string;
|
|
42
|
+
secondary: string;
|
|
43
|
+
neutral: string;
|
|
44
|
+
success: string;
|
|
45
|
+
warning: string;
|
|
46
|
+
error: string;
|
|
47
|
+
info: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The DMS's own logos and palettes, kept whatever a layer sets in their place,
|
|
52
|
+
* so a tool can preview a return to the DMS look (the builder's theme editor).
|
|
53
|
+
* A layer overrides `branding.logo` and `ui.colors`, never these.
|
|
54
|
+
*/
|
|
55
|
+
export interface DmsBrandingDefaults {
|
|
56
|
+
logo: DmsBrandingLogo;
|
|
57
|
+
colors: DmsUiColors;
|
|
58
|
+
}
|
|
59
|
+
|
|
15
60
|
/**
|
|
16
61
|
* What the footer of the public and auth pages shows besides its links
|
|
17
62
|
* (`registerFooterLink`).
|
|
@@ -33,11 +78,10 @@ export interface DmsBrandingConfig {
|
|
|
33
78
|
* Absent when an instance ships no logo of its own; the layer's app config
|
|
34
79
|
* supplies the AntelopeJS one by default.
|
|
35
80
|
*/
|
|
36
|
-
logo?:
|
|
37
|
-
default: DmsLogoSources;
|
|
38
|
-
collapsed: DmsLogoSources;
|
|
39
|
-
};
|
|
81
|
+
logo?: DmsBrandingLogo;
|
|
40
82
|
footer?: DmsFooterConfig;
|
|
83
|
+
/** Supplied by the DMS layout layer's app config. */
|
|
84
|
+
defaults?: DmsBrandingDefaults;
|
|
41
85
|
}
|
|
42
86
|
|
|
43
87
|
export interface DmsAppConfig {
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DmsBrandingLogo,
|
|
3
|
+
DmsUiColors,
|
|
4
|
+
} from "#dms-core/shared/types/app-config";
|
|
1
5
|
import { EYEBROW_CLASS } from "#dms-ui/app/build/utils/eyebrow";
|
|
2
6
|
import { actionsTheme } from "./theme/actions";
|
|
3
7
|
import { dataTheme } from "./theme/data";
|
|
@@ -8,21 +12,44 @@ import {
|
|
|
8
12
|
} from "./theme/navigation";
|
|
9
13
|
import { surfacesTheme } from "./theme/surfaces";
|
|
10
14
|
|
|
15
|
+
const ANTELOPE_ICON = "/images/antelope-logo/icon.svg";
|
|
16
|
+
|
|
17
|
+
// The palettes and logos are built afresh for each place that holds them: a
|
|
18
|
+
// merged app config keeps the very objects no layer overrides, so a live edit
|
|
19
|
+
// of `ui.colors` or `branding.logo` would otherwise rewrite
|
|
20
|
+
// `branding.defaults` too.
|
|
21
|
+
|
|
22
|
+
function dmsColors(): DmsUiColors {
|
|
23
|
+
return {
|
|
24
|
+
primary: "dms",
|
|
25
|
+
// The AI accent of the v2 design.
|
|
26
|
+
secondary: "violet",
|
|
27
|
+
neutral: "neutral",
|
|
28
|
+
// Design semantic hexes map exactly onto these Tailwind palettes:
|
|
29
|
+
// success #10b981 (emerald), warning #f59e0b (amber),
|
|
30
|
+
// error #ef4444 (red), info #06b6d4 (cyan).
|
|
31
|
+
success: "emerald",
|
|
32
|
+
warning: "amber",
|
|
33
|
+
error: "red",
|
|
34
|
+
info: "cyan",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// No email logo: the AntelopeJS logos are SVG, which mail clients refuse.
|
|
39
|
+
function dmsLogo(): DmsBrandingLogo {
|
|
40
|
+
return {
|
|
41
|
+
default: {
|
|
42
|
+
light: "/images/antelope-logo/light.svg",
|
|
43
|
+
dark: "/images/antelope-logo/dark.svg",
|
|
44
|
+
},
|
|
45
|
+
collapsed: { light: ANTELOPE_ICON, dark: ANTELOPE_ICON },
|
|
46
|
+
favicon: { light: ANTELOPE_ICON, dark: ANTELOPE_ICON },
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
11
50
|
export default {
|
|
12
51
|
ui: {
|
|
13
|
-
colors:
|
|
14
|
-
primary: "dms",
|
|
15
|
-
// The AI accent of the v2 design.
|
|
16
|
-
secondary: "violet",
|
|
17
|
-
neutral: "neutral",
|
|
18
|
-
// Design semantic hexes map exactly onto these Tailwind palettes:
|
|
19
|
-
// success #10b981 (emerald), warning #f59e0b (amber),
|
|
20
|
-
// error #ef4444 (red), info #06b6d4 (cyan).
|
|
21
|
-
success: "emerald",
|
|
22
|
-
warning: "amber",
|
|
23
|
-
error: "red",
|
|
24
|
-
info: "cyan",
|
|
25
|
-
},
|
|
52
|
+
colors: dmsColors(),
|
|
26
53
|
icons: {
|
|
27
54
|
arrowDown: "i-ph-arrow-down-light",
|
|
28
55
|
arrowLeft: "i-ph-arrow-left-light",
|
|
@@ -184,16 +211,8 @@ export default {
|
|
|
184
211
|
},
|
|
185
212
|
},
|
|
186
213
|
branding: {
|
|
187
|
-
logo:
|
|
188
|
-
|
|
189
|
-
light: "/images/antelope-logo/light.svg",
|
|
190
|
-
dark: "/images/antelope-logo/dark.svg",
|
|
191
|
-
},
|
|
192
|
-
collapsed: {
|
|
193
|
-
light: "/images/antelope-logo/icon.svg",
|
|
194
|
-
dark: "/images/antelope-logo/icon.svg",
|
|
195
|
-
},
|
|
196
|
-
},
|
|
214
|
+
logo: dmsLogo(),
|
|
215
|
+
defaults: { logo: dmsLogo(), colors: dmsColors() },
|
|
197
216
|
footer: {
|
|
198
217
|
copyright: "Antelope",
|
|
199
218
|
githubStar: true,
|