@antelopejs/dms 0.8.3 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/data/module-store/catalog.json +71 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/implementations/dms/frontend-backend-proxies.d.ts +3 -0
- package/dist/implementations/dms/frontend-backend-proxies.js +39 -0
- package/dist/implementations/dms/frontend-backend-proxies.js.map +1 -0
- package/dist/implementations/dms/frontend-bootstrap.d.ts +1 -0
- package/dist/implementations/dms/frontend-bootstrap.js +13 -0
- package/dist/implementations/dms/frontend-bootstrap.js.map +1 -1
- package/dist/implementations/dms/frontend-modules.d.ts +6 -2
- package/dist/implementations/dms/frontend-modules.js +37 -8
- package/dist/implementations/dms/frontend-modules.js.map +1 -1
- package/dist/implementations/dms/frontend-ready.d.ts +8 -0
- package/dist/implementations/dms/frontend-ready.js +34 -0
- package/dist/implementations/dms/frontend-ready.js.map +1 -0
- package/dist/implementations/dms/manifest.d.ts +7 -0
- package/dist/implementations/dms/manifest.js +13 -0
- package/dist/implementations/dms/manifest.js.map +1 -1
- package/dist/implementations/dms/menu-categories.d.ts +16 -0
- package/dist/implementations/dms/menu-categories.js +30 -0
- package/dist/implementations/dms/menu-categories.js.map +1 -0
- package/dist/implementations/dms/nav-badges.d.ts +0 -8
- package/dist/implementations/dms/nav-badges.js +0 -16
- package/dist/implementations/dms/nav-badges.js.map +1 -1
- package/dist/implementations/dms/page.d.ts +13 -5
- package/dist/implementations/dms/page.js +72 -41
- package/dist/implementations/dms/page.js.map +1 -1
- package/dist/implementations/dms-html-render/index.js +5 -2
- package/dist/implementations/dms-html-render/index.js.map +1 -1
- package/dist/implementations/dms-notifications/index.d.ts +4 -3
- package/dist/implementations/dms-notifications/index.js +14 -6
- package/dist/implementations/dms-notifications/index.js.map +1 -1
- package/dist/pages/settings/settings-index.js +3 -2
- package/dist/pages/settings/settings-index.js.map +1 -1
- package/dist/pages/settings/users/invites.js +2 -2
- package/dist/pages/settings/users/invites.js.map +1 -1
- package/dist/pages/settings/users/member-change-notifications.js +17 -6
- package/dist/pages/settings/users/member-change-notifications.js.map +1 -1
- package/dist/pages/settings/users/member-invite-form.d.ts +3 -2
- package/dist/pages/settings/users/member-invite-form.js +7 -4
- package/dist/pages/settings/users/member-invite-form.js.map +1 -1
- package/dist/pages/settings/users/member-roles-field.js +2 -1
- package/dist/pages/settings/users/member-roles-field.js.map +1 -1
- package/dist/pages/settings/users/members.js +4 -1
- package/dist/pages/settings/users/members.js.map +1 -1
- package/dist/pages/settings/users/notifications.d.ts +6 -0
- package/dist/pages/settings/users/notifications.js +23 -15
- package/dist/pages/settings/users/notifications.js.map +1 -1
- package/dist/pages/settings/users/profile.js +1 -1
- package/dist/pages/settings/users/profile.js.map +1 -1
- package/dist/pages/settings/users/region-form.d.ts +1 -1
- package/dist/pages/settings/users/region-form.js +2 -1
- package/dist/pages/settings/users/region-form.js.map +1 -1
- package/dist/pages/settings/users/role-editor-store.d.ts +3 -1
- package/dist/pages/settings/users/role-editor-store.js +17 -4
- package/dist/pages/settings/users/role-editor-store.js.map +1 -1
- package/dist/pages/settings/users/roles.js +1 -0
- package/dist/pages/settings/users/roles.js.map +1 -1
- package/dist/realtime/registry.d.ts +1 -0
- package/dist/realtime/registry.js +5 -0
- package/dist/realtime/registry.js.map +1 -1
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.js +1 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/realtime.d.ts +5 -1
- package/dist/routes/realtime.js +16 -9
- package/dist/routes/realtime.js.map +1 -1
- package/dist/routes/store.d.ts +21 -0
- package/dist/routes/store.js +130 -0
- package/dist/routes/store.js.map +1 -0
- package/dist/store/activity.d.ts +2 -0
- package/dist/store/activity.js +12 -0
- package/dist/store/activity.js.map +1 -0
- package/dist/store/cache.d.ts +8 -0
- package/dist/store/cache.js +29 -0
- package/dist/store/cache.js.map +1 -0
- package/dist/store/catalog.d.ts +4 -0
- package/dist/store/catalog.js +69 -0
- package/dist/store/catalog.js.map +1 -0
- package/dist/store/compatibility.d.ts +3 -0
- package/dist/store/compatibility.js +45 -0
- package/dist/store/compatibility.js.map +1 -0
- package/dist/store/declarations.d.ts +18 -0
- package/dist/store/declarations.js +116 -0
- package/dist/store/declarations.js.map +1 -0
- package/dist/store/frontend-compatibility.d.ts +18 -0
- package/dist/store/frontend-compatibility.js +93 -0
- package/dist/store/frontend-compatibility.js.map +1 -0
- package/dist/store/interfaces.d.ts +6 -0
- package/dist/store/interfaces.js +126 -0
- package/dist/store/interfaces.js.map +1 -0
- package/dist/store/listing.d.ts +2 -0
- package/dist/store/listing.js +180 -0
- package/dist/store/listing.js.map +1 -0
- package/dist/store/operations.d.ts +8 -0
- package/dist/store/operations.js +254 -0
- package/dist/store/operations.js.map +1 -0
- package/dist/store/registry.d.ts +4 -0
- package/dist/store/registry.js +61 -0
- package/dist/store/registry.js.map +1 -0
- package/dist/store/requirements.d.ts +14 -0
- package/dist/store/requirements.js +81 -0
- package/dist/store/requirements.js.map +1 -0
- package/dist/store/runtime.d.ts +11 -0
- package/dist/store/runtime.js +78 -0
- package/dist/store/runtime.js.map +1 -0
- package/dist/store/tarball.d.ts +2 -0
- package/dist/store/tarball.js +80 -0
- package/dist/store/tarball.js.map +1 -0
- package/dist/store/types.d.ts +98 -0
- package/dist/store/types.js +3 -0
- package/dist/store/types.js.map +1 -0
- package/dist/test/integration/account-deletion.test.js +2 -2
- package/dist/test/integration/account-deletion.test.js.map +1 -1
- package/dist/test/integration/auth/auth-key-rotation.test.js +2 -1
- package/dist/test/integration/auth/auth-key-rotation.test.js.map +1 -1
- package/dist/test/integration/auth/current-password.test.js +2 -1
- package/dist/test/integration/auth/current-password.test.js.map +1 -1
- package/dist/test/integration/auth/session-revocation.test.js +2 -1
- package/dist/test/integration/auth/session-revocation.test.js.map +1 -1
- package/dist/test/integration/auth/sign-in-country.test.js +2 -1
- package/dist/test/integration/auth/sign-in-country.test.js.map +1 -1
- package/dist/test/integration/auth/two-factor-codes.test.js +2 -1
- package/dist/test/integration/auth/two-factor-codes.test.js.map +1 -1
- package/dist/test/integration/auth/two-factor-password.test.js +3 -2
- package/dist/test/integration/auth/two-factor-password.test.js.map +1 -1
- package/dist/test/integration/email-change.test.js +2 -1
- package/dist/test/integration/email-change.test.js.map +1 -1
- package/dist/test/integration/files/component-files.test.js +2 -2
- package/dist/test/integration/files/component-files.test.js.map +1 -1
- package/dist/test/integration/member-settings-access.test.js +8 -7
- package/dist/test/integration/member-settings-access.test.js.map +1 -1
- package/dist/test/integration/menu-access-events.test.d.ts +1 -0
- package/dist/test/integration/menu-access-events.test.js +148 -0
- package/dist/test/integration/menu-access-events.test.js.map +1 -0
- package/dist/test/integration/notification-inbox.test.js +4 -1
- package/dist/test/integration/notification-inbox.test.js.map +1 -1
- package/dist/test/integration/notification-locked-subjects.test.js +3 -2
- package/dist/test/integration/notification-locked-subjects.test.js.map +1 -1
- package/dist/test/integration/notification-unread-badge.test.js +51 -5
- package/dist/test/integration/notification-unread-badge.test.js.map +1 -1
- package/dist/test/integration/page-layout-route-guard.test.d.ts +1 -0
- package/dist/test/integration/page-layout-route-guard.test.js +56 -0
- package/dist/test/integration/page-layout-route-guard.test.js.map +1 -0
- package/dist/test/integration/permission-warnings.test.js +3 -2
- package/dist/test/integration/permission-warnings.test.js.map +1 -1
- package/dist/test/integration/removed-member-notifications.test.js +3 -2
- package/dist/test/integration/removed-member-notifications.test.js.map +1 -1
- package/dist/test/integration/role-delete-reassign.test.js +2 -1
- package/dist/test/integration/role-delete-reassign.test.js.map +1 -1
- package/dist/test/integration/role-permission-ancestors.test.js +2 -1
- package/dist/test/integration/role-permission-ancestors.test.js.map +1 -1
- package/dist/test/unit/frontend-bootstrap.test.js +13 -0
- package/dist/test/unit/frontend-bootstrap.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/category-slug-registry.test.js +14 -5
- package/dist/test/unit/implementations/dms/category-slug-registry.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.d.ts +1 -0
- package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.js +66 -0
- package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.js.map +1 -0
- package/dist/test/unit/implementations/dms/manifest-strip.test.js +26 -0
- package/dist/test/unit/implementations/dms/manifest-strip.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/nav-badges.test.js +23 -9
- package/dist/test/unit/implementations/dms/nav-badges.test.js.map +1 -1
- package/dist/test/unit/implementations/dms-html-render/generate-html.test.js +22 -4
- package/dist/test/unit/implementations/dms-html-render/generate-html.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms/page-layout-query.test.js +12 -0
- package/dist/test/unit/interfaces/dms/page-layout-query.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms/page-teardown.test.js +1 -1
- package/dist/test/unit/interfaces/dms/page-teardown.test.js.map +1 -1
- package/dist/test/unit/pages/settings/member-roles-field.test.js +1 -1
- package/dist/test/unit/pages/settings/member-roles-field.test.js.map +1 -1
- package/dist/test/unit/pages/settings/role-delete-confirm.test.js.map +1 -1
- package/dist/test/unit/realtime/menu-topics.test.d.ts +1 -0
- package/dist/test/unit/realtime/menu-topics.test.js +26 -0
- package/dist/test/unit/realtime/menu-topics.test.js.map +1 -0
- package/dist/test/unit/store/store.test.d.ts +1 -0
- package/dist/test/unit/store/store.test.js +127 -0
- package/dist/test/unit/store/store.test.js.map +1 -0
- package/dist/validation/store.schema.d.ts +39 -0
- package/dist/validation/store.schema.js +27 -0
- package/dist/validation/store.schema.js.map +1 -0
- package/frontend-vue/deferred-plugins.ts +3 -4
- package/frontend-vue/dms.email.ts +5 -1
- package/frontend-vue/dms.frontend.ts +15 -5
- package/frontend-vue/dms.server.ts +74 -0
- package/frontend-vue/layers/dms-auth/app/composables/useLogout.ts +1 -1
- package/frontend-vue/layers/dms-auth/app/composables/usePostLoginRedirect.ts +16 -3
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/2fa.vue +3 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/backup.vue +3 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/forgot-validation.vue +1 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/oauth-complete.vue +26 -3
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/recover.vue +1 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/signup.vue +22 -25
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/validate.vue +1 -1
- package/frontend-vue/layers/dms-auth/app/middleware/auth.ts +5 -7
- package/frontend-vue/layers/dms-auth/app/plugins/permissions-preload.ts +17 -0
- package/frontend-vue/layers/dms-auth/server/accounts.ts +135 -0
- package/frontend-vue/layers/dms-auth/server/actions.ts +251 -0
- package/frontend-vue/layers/dms-auth/server/oauth.ts +260 -0
- package/frontend-vue/layers/dms-auth/server/session.ts +158 -0
- package/frontend-vue/layers/dms-core/app/build/utils/asyncDataScope.ts +47 -0
- package/frontend-vue/layers/dms-core/app/build/utils/permission-preview.ts +6 -2
- package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +148 -0
- package/frontend-vue/layers/dms-core/app/composables/auth/useAuthFetch.ts +7 -20
- package/frontend-vue/layers/dms-core/app/composables/auth/useCurrentUser.ts +2 -2
- package/frontend-vue/layers/dms-core/app/composables/auth/useMultiAccount.ts +18 -5
- package/frontend-vue/layers/dms-core/app/composables/auth/usePermissions.ts +35 -6
- package/frontend-vue/layers/dms-core/app/composables/auth/useSessionRecovery.ts +1 -1
- package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +20 -14
- package/frontend-vue/layers/dms-core/app/composables/period/periodClock.ts +121 -0
- package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +42 -0
- package/frontend-vue/layers/dms-core/app/composables/period/periodTimeZone.ts +31 -0
- package/frontend-vue/layers/dms-core/app/composables/period/presetResolvers.ts +48 -19
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +33 -4
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +78 -25
- package/frontend-vue/layers/dms-core/app/composables/realtime/usePageRealtime.ts +3 -0
- package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +36 -6
- package/frontend-vue/layers/dms-core/app/composables/user/useUserRegionalPreferences.ts +1 -1
- package/frontend-vue/layers/dms-core/app/plugins/async-data-scope.ts +26 -0
- package/frontend-vue/layers/dms-core/app/utils/pageRoutes.ts +22 -0
- package/frontend-vue/layers/dms-core/app/utils/regional.ts +36 -3
- package/frontend-vue/layers/dms-core/app/utils/trailingDeduper.ts +10 -1
- package/frontend-vue/layers/dms-core/shared/types/app-config.ts +48 -4
- package/frontend-vue/layers/dms-layout/app/app.config.ts +42 -23
- package/frontend-vue/layers/dms-layout/app/assets/css/main.css +72 -20
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardFrame.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +0 -3
- package/frontend-vue/layers/dms-layout/app/build/components/notification/NotificationPopover.vue +9 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreDependencyPicker.vue +136 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +98 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +247 -29
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +19 -2
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/members/MemberRolePicker.vue +13 -12
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +17 -7
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationPreferencesForm.vue +0 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfilePersonalInfo.vue +36 -33
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfileSecuritySummary.vue +1 -2
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-types.ts +5 -4
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecuritySessions.vue +35 -28
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityStatus.vue +0 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityTwoFactor.vue +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/dev-reload/useDevReloadHolder.ts +7 -6
- package/frontend-vue/layers/dms-layout/app/build/composables/layout/sidePanelState.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/navigation/useVisitNavBadges.ts +26 -0
- package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationCatalog.ts +25 -23
- package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationPreferences.ts +44 -23
- package/frontend-vue/layers/dms-layout/app/build/composables/security/useSessionHandoff.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/preferencesSummary.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useAccountData.ts +3 -2
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useProfileAvatar.ts +1 -18
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useProfilePreferences.ts +19 -23
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityFormat.ts +6 -3
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityOverview.ts +13 -18
- package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +186 -120
- package/frontend-vue/layers/dms-layout/app/build/utils/module-store-install.ts +146 -0
- package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +8 -6
- package/frontend-vue/layers/dms-layout/app/build/utils/publicErrorMessage.ts +19 -0
- package/frontend-vue/layers/dms-layout/app/components/navigation/CollapsedMenuItem.vue +0 -11
- package/frontend-vue/layers/dms-layout/app/components/navigation/NavigationMenu.vue +3 -13
- package/frontend-vue/layers/dms-layout/app/composables/general/useSystemState.ts +7 -11
- package/frontend-vue/layers/dms-layout/app/composables/notification/useNotifications.ts +103 -22
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +109 -0
- package/frontend-vue/layers/dms-layout/app/composables/page/useModulesListing.ts +25 -102
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +0 -10
- package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +177 -96
- package/frontend-vue/layers/dms-layout/app/composables/profile/useUserLanguage.ts +3 -2
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +341 -62
- package/frontend-vue/layers/dms-layout/app/emails/components/EmailLayout.vue +31 -11
- package/frontend-vue/layers/dms-layout/app/error.vue +5 -2
- package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/middleware/page-leave-guard.global.ts +2 -2
- package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +12 -9
- package/frontend-vue/layers/dms-layout/app/plugins/dms-menu-sync.client.ts +31 -3
- package/frontend-vue/layers/dms-layout/app/plugins/favicon.ts +58 -0
- package/frontend-vue/layers/dms-layout/app/plugins/notification-stream.client.ts +15 -7
- package/frontend-vue/layers/dms-layout/app/plugins/profile-sync.client.ts +5 -1
- package/frontend-vue/layers/dms-layout/app/plugins/regional-preferences.ts +10 -4
- package/frontend-vue/layers/dms-layout/app/plugins/time-zone-cookie.client.ts +12 -0
- package/frontend-vue/layers/dms-layout/app/types/module-store.ts +134 -0
- package/frontend-vue/layers/dms-layout/app/types/page.ts +13 -0
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +107 -9
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +107 -9
- package/frontend-vue/layers/dms-layout/server/html-render.ts +58 -0
- package/frontend-vue/layers/dms-layout/server/render-token.ts +95 -0
- package/frontend-vue/layers/dms-onboarding/app/composables/onboarding/useOnboarding.ts +25 -21
- package/frontend-vue/layers/dms-onboarding/app/middleware/onboarding.global.ts +1 -1
- package/frontend-vue/layers/dms-onboarding/app/plugins/onboarding-preload.ts +9 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +4 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/files/useFileMetadata.ts +162 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formRecordLoad.ts +23 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageDataRefresh.ts +101 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/navigation/useNavBadges.ts +33 -6
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +19 -16
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodQuery.ts +40 -20
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +6 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableListData.ts +42 -14
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +19 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +5 -2
- package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +9 -9
- package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +20 -18
- package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/File.vue +26 -132
- package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +40 -105
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +2 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +4 -4
- package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +9 -13
- package/frontend-vue/layers/dms-ui/app/components/table-view/CascaderPath.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/FilePreview.vue +12 -24
- package/frontend-vue/layers/dms-ui/app/components/table-view/ImagePreview.vue +17 -29
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +7 -3
- package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +20 -8
- package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +8 -11
- package/frontend-vue/layers/dms-ui/app/composables/chart/useChartFetch.ts +146 -182
- package/frontend-vue/layers/dms-ui/app/composables/live/usePageLiveRefresh.ts +8 -2
- package/frontend-vue/layers/dms-ui/app/composables/useFileReadUrls.ts +11 -99
- package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +32 -3
- package/frontend-vue/package.json +3 -3
- package/frontend-vue/pnpm-lock.yaml +16 -22
- package/frontend-vue/server/private-options.ts +27 -0
- package/frontend-vue/tests/async-data-scope.test.ts +121 -0
- package/frontend-vue/tests/auth-middleware-error.test.ts +3 -3
- package/frontend-vue/tests/banner-block-fetch.test.ts +3 -0
- package/frontend-vue/tests/block-fetch-options-forwarding.test.ts +3 -0
- package/frontend-vue/tests/block-items-period-realtime.test.ts +3 -0
- package/frontend-vue/tests/block-not-found.test.ts +16 -2
- package/frontend-vue/tests/block-page-refresh.test.ts +3 -0
- package/frontend-vue/tests/block-route-params.test.ts +3 -0
- package/frontend-vue/tests/branding-defaults.test.ts +34 -0
- package/frontend-vue/tests/builtin-emails.test.ts +23 -2
- package/frontend-vue/tests/chart-live-pause.test.ts +4 -1
- package/frontend-vue/tests/chart-value-propagation.test.ts +4 -1
- package/frontend-vue/tests/composed-text-blocks.test.ts +3 -0
- package/frontend-vue/tests/email-layout.test.ts +66 -4
- package/frontend-vue/tests/error-page-message.test.ts +25 -2
- package/frontend-vue/tests/favicon.test.ts +131 -0
- package/frontend-vue/tests/file-fields-ssr.test.ts +145 -0
- package/frontend-vue/tests/file-read-urls.test.ts +145 -34
- package/frontend-vue/tests/file-unavailable.test.ts +5 -1
- package/frontend-vue/tests/fixtures/dataEngine.ts +44 -0
- package/frontend-vue/tests/form-load-error.test.ts +55 -1
- package/frontend-vue/tests/helpers/api-data-runtime.ts +77 -0
- package/frontend-vue/tests/image-hydration-ids.test.ts +21 -16
- package/frontend-vue/tests/image-reference-preservation.test.ts +59 -53
- package/frontend-vue/tests/input-number-adjust.test.ts +14 -0
- package/frontend-vue/tests/key-value-list-copy.test.ts +3 -0
- package/frontend-vue/tests/member-role-picker.test.ts +4 -2
- package/frontend-vue/tests/menu-sync-reconnect.test.ts +47 -0
- package/frontend-vue/tests/metadata-retry.test.ts +5 -1
- package/frontend-vue/tests/module-routing.test.ts +2 -5
- package/frontend-vue/tests/module-store-catalog.test.ts +270 -51
- package/frontend-vue/tests/modules-catalog.test.ts +17 -4
- package/frontend-vue/tests/modules-listing-transfer.test.ts +95 -0
- package/frontend-vue/tests/multi-account-validate.test.ts +23 -1
- package/frontend-vue/tests/nav-badge-tones.test.ts +66 -3
- package/frontend-vue/tests/notification-counts-from-writes.test.ts +223 -0
- package/frontend-vue/tests/notification-counts-transfer.test.ts +88 -0
- package/frontend-vue/tests/notification-inbox-display.test.ts +57 -5
- package/frontend-vue/tests/notification-popover-errors.test.ts +21 -1
- package/frontend-vue/tests/oauth-complete-handoff.test.ts +209 -0
- package/frontend-vue/tests/onboarding-middleware.test.ts +1 -1
- package/frontend-vue/tests/onboarding-transfer.test.ts +106 -0
- package/frontend-vue/tests/page-data-refresh.test.ts +141 -0
- package/frontend-vue/tests/page-fill-height.test.ts +16 -3
- package/frontend-vue/tests/page-no-visible-blocks.test.ts +12 -2
- package/frontend-vue/tests/page-realtime-remount.test.ts +1 -1
- package/frontend-vue/tests/page-record-label.test.ts +0 -7
- package/frontend-vue/tests/page-validation-redirect.test.ts +12 -2
- package/frontend-vue/tests/period-hydration-now.test.ts +185 -0
- package/frontend-vue/tests/period-live-pause.test.ts +3 -0
- package/frontend-vue/tests/period-reader-zone.test.ts +137 -0
- package/frontend-vue/tests/period-scope-first-fetch.test.ts +17 -1
- package/frontend-vue/tests/period-ssr.test.ts +195 -0
- package/frontend-vue/tests/permission-preview-partial.test.ts +0 -2
- package/frontend-vue/tests/permissions-transfer.test.ts +194 -0
- package/frontend-vue/tests/post-login-redirect.test.ts +10 -5
- package/frontend-vue/tests/profile-personal-info-ssr.test.ts +150 -0
- package/frontend-vue/tests/profile-preferences-summary.test.ts +15 -11
- package/frontend-vue/tests/realtime-reconnect.test.ts +22 -3
- package/frontend-vue/tests/roles-name-errors.test.ts +1 -1
- package/frontend-vue/tests/row-side-panel.test.ts +1 -1
- package/frontend-vue/tests/security-sessions-load-error.test.ts +3 -1
- package/frontend-vue/tests/server-routes-vs-pages.test.ts +26 -0
- package/frontend-vue/tests/server-routes.test.ts +582 -0
- package/frontend-vue/tests/session-handoff.test.ts +1 -1
- package/frontend-vue/tests/settings-reads-ssr.test.ts +232 -0
- package/frontend-vue/tests/side-panels.test.ts +8 -1
- package/frontend-vue/tests/sign-in-exit-history.test.ts +219 -0
- package/frontend-vue/tests/signup-invitation-link.test.ts +56 -8
- package/frontend-vue/tests/site-layout-categories.test.ts +42 -0
- package/frontend-vue/tests/site-layout-hydration.test.ts +3 -3
- package/frontend-vue/tests/site-layout-rendered.test.ts +103 -0
- package/frontend-vue/tests/site-layout-resync.test.ts +87 -0
- package/frontend-vue/tests/ssr-guard-preload.test.ts +188 -0
- package/frontend-vue/tests/table-cell-tones.test.ts +36 -1
- package/frontend-vue/tests/table-data-ssr.test.ts +195 -0
- package/frontend-vue/tests/table-list-data-requests.test.ts +154 -0
- package/frontend-vue/tests/table-retry-loading.test.ts +37 -0
- package/frontend-vue/tests/table-source-navigation.test.ts +0 -1
- package/frontend-vue/tests/table-source-period-scope.test.ts +0 -1
- package/frontend-vue/tests/table-view-views.test.ts +1 -1
- package/frontend-vue/tests/top-list-card-actions.test.ts +3 -0
- package/frontend-vue/tests/top-list-card-period-badge-ssr.test.ts +212 -0
- package/frontend-vue/tests/top-list-card-period-badge.test.ts +79 -0
- package/frontend-vue/tests/top-list-card-value-suffix.test.ts +3 -0
- package/frontend-vue/tests/trailing-deduper.test.ts +22 -0
- package/frontend-vue/tests/use-api-data-update.test.ts +119 -0
- package/frontend-vue/tests/use-api-data.test.ts +410 -0
- package/frontend-vue/tests/user-language.test.ts +8 -5
- package/frontend-vue/tests/widget-label-i18n.test.ts +4 -1
- package/frontend-vue/vitest.config.ts +6 -0
- package/package.json +2 -2
- package/skills/dms/SKILL.md +2 -1
- package/skills/dms-module/SKILL.md +1 -1
- package/skills/dms-pages/SKILL.md +4 -1
- package/frontend-vue/layers/dms-core/app/build/composables/page/usePageRecordLabel.ts +0 -9
- package/frontend-vue/layers/dms-layout/app/composables/page/usePrefetch.ts +0 -21
- package/frontend-vue/layers/dms-ui/app/build/composables/form/useControlError.ts +0 -10
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useServerRenderedAsyncData.ts +0 -23
- package/frontend-vue/tests/control-error-path.test.ts +0 -11
- package/frontend-vue/tests/modules-listing-per-request.test.ts +0 -104
- package/frontend-vue/tests/page-layout-background-refresh.test.ts +0 -321
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { pageRoutePath } from "#dms-core/app/utils/pageRoutes";
|
|
2
|
+
import { onBeforeMount } from "vue";
|
|
1
3
|
import { NotificationEvents } from "./types/events";
|
|
2
4
|
import { unreadToneWith } from "../../build/components/pages/settings/notification/notificationDisplay";
|
|
3
5
|
|
|
@@ -30,13 +32,22 @@ export interface UserNotification {
|
|
|
30
32
|
* bell, the Notifications entry): the most important unread notification's,
|
|
31
33
|
* which the server weighs as the inbox draws it.
|
|
32
34
|
*/
|
|
33
|
-
interface FeedCounts {
|
|
35
|
+
export interface FeedCounts {
|
|
34
36
|
all: number;
|
|
35
37
|
unread: number;
|
|
36
38
|
unreadTone?: NotificationTone;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
/**
|
|
42
|
+
* What a write on the feed answers (marking read or unread, deleting), and
|
|
43
|
+
* what its realtime event carries: the whole feed's counts after it.
|
|
44
|
+
*/
|
|
45
|
+
interface FeedWriteResult {
|
|
46
|
+
counts?: FeedCounts;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const API_BASE = pageRoutePath("/settings/user/notifications");
|
|
50
|
+
const BELL_COUNTS_DATA_KEY = "dms-notification-bell-counts";
|
|
40
51
|
const NOTIFICATION_COMPONENT_ID = "global-notifications";
|
|
41
52
|
const NOTIFICATIONS_PAGE_SIZE = 20;
|
|
42
53
|
|
|
@@ -130,16 +141,35 @@ export const useNotifications = () => {
|
|
|
130
141
|
unreadTone.value = counts.unread > 0 ? counts.unreadTone : undefined;
|
|
131
142
|
};
|
|
132
143
|
|
|
144
|
+
/** Reads the whole feed's unread count and tone, the bell's badge. */
|
|
145
|
+
const readBellCounts = (signal?: AbortSignal) =>
|
|
146
|
+
$authFetch<FeedCounts>(`${API_BASE}/counts`, { signal });
|
|
147
|
+
|
|
148
|
+
/** Takes counts as the bell's: from now on, they are known. */
|
|
149
|
+
const applyBellCounts = (counts: FeedCounts | undefined) => {
|
|
150
|
+
if (counts) applyFeedCounts(counts);
|
|
151
|
+
areCountsLoaded.value = true;
|
|
152
|
+
};
|
|
153
|
+
|
|
133
154
|
/** Loads the whole feed's unread count and tone, the bell's badge. */
|
|
134
155
|
const fetchBellCounts = async () => {
|
|
135
156
|
try {
|
|
136
|
-
|
|
137
|
-
if (counts) applyFeedCounts(counts);
|
|
157
|
+
applyBellCounts(await readBellCounts());
|
|
138
158
|
} finally {
|
|
139
159
|
areCountsLoaded.value = true;
|
|
140
160
|
}
|
|
141
161
|
};
|
|
142
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Takes the counts a write answered or its realtime event carried. A
|
|
165
|
+
* write's own event brings the same counts again, which changes nothing;
|
|
166
|
+
* without counts (a server before they were sent) they are read.
|
|
167
|
+
*/
|
|
168
|
+
const takeWriteCounts = async (counts: FeedCounts | undefined) => {
|
|
169
|
+
if (counts) applyBellCounts(counts);
|
|
170
|
+
else await fetchBellCounts();
|
|
171
|
+
};
|
|
172
|
+
|
|
143
173
|
const fetchUnreadPreview = async () => {
|
|
144
174
|
const data = await $authFetch<UserNotification[]>(
|
|
145
175
|
`${API_BASE}/unread-preview`,
|
|
@@ -168,30 +198,33 @@ export const useNotifications = () => {
|
|
|
168
198
|
};
|
|
169
199
|
|
|
170
200
|
const markAsRead = async (notificationId: string) => {
|
|
171
|
-
await $authFetch(
|
|
172
|
-
|
|
173
|
-
|
|
201
|
+
const { counts } = await $authFetch<FeedWriteResult>(
|
|
202
|
+
`${API_BASE}/mark-read/${notificationId}`,
|
|
203
|
+
{ method: "PUT" },
|
|
204
|
+
);
|
|
174
205
|
setReadState([notificationId], true);
|
|
175
206
|
unreadPreview.value = unreadPreview.value.filter((n) => !n.isRead);
|
|
176
|
-
await
|
|
207
|
+
await takeWriteCounts(counts);
|
|
177
208
|
emitRead([notificationId]);
|
|
178
209
|
};
|
|
179
210
|
|
|
180
211
|
const markAsUnread = async (notificationId: string) => {
|
|
181
|
-
await $authFetch(
|
|
182
|
-
|
|
183
|
-
|
|
212
|
+
const { counts } = await $authFetch<FeedWriteResult>(
|
|
213
|
+
`${API_BASE}/mark-unread/${notificationId}`,
|
|
214
|
+
{ method: "PUT" },
|
|
215
|
+
);
|
|
184
216
|
setReadState([notificationId], false);
|
|
185
|
-
await
|
|
217
|
+
await takeWriteCounts(counts);
|
|
186
218
|
emitCount();
|
|
187
219
|
};
|
|
188
220
|
|
|
189
221
|
const deleteNotification = async (notificationId: string) => {
|
|
190
|
-
await $authFetch(
|
|
191
|
-
|
|
192
|
-
|
|
222
|
+
const { counts } = await $authFetch<FeedWriteResult>(
|
|
223
|
+
`${API_BASE}/delete/${notificationId}`,
|
|
224
|
+
{ method: "DELETE" },
|
|
225
|
+
);
|
|
193
226
|
removeFromLists([notificationId]);
|
|
194
|
-
await
|
|
227
|
+
await takeWriteCounts(counts);
|
|
195
228
|
|
|
196
229
|
sendComponentEvent(
|
|
197
230
|
NotificationEvents.NOTIFICATION_DELETED,
|
|
@@ -200,24 +233,26 @@ export const useNotifications = () => {
|
|
|
200
233
|
);
|
|
201
234
|
};
|
|
202
235
|
|
|
203
|
-
|
|
236
|
+
// The realtime events of the user's feed, this tab's own writes included:
|
|
237
|
+
// each carries the feed's counts after it.
|
|
238
|
+
const handleRemoteRead = async (ids: string[], counts?: FeedCounts) => {
|
|
204
239
|
if (ids.length === 0) return;
|
|
205
240
|
setReadState(ids, true);
|
|
206
241
|
unreadPreview.value = unreadPreview.value.filter((n) => !n.isRead);
|
|
207
|
-
await
|
|
242
|
+
await takeWriteCounts(counts);
|
|
208
243
|
emitRead(ids);
|
|
209
244
|
};
|
|
210
245
|
|
|
211
|
-
const handleRemoteUnread = async (ids: string[]) => {
|
|
246
|
+
const handleRemoteUnread = async (ids: string[], counts?: FeedCounts) => {
|
|
212
247
|
if (ids.length === 0) return;
|
|
213
248
|
setReadState(ids, false);
|
|
214
|
-
await
|
|
249
|
+
await takeWriteCounts(counts);
|
|
215
250
|
emitCount();
|
|
216
251
|
};
|
|
217
252
|
|
|
218
|
-
const handleRemoteAllRead = async () => {
|
|
253
|
+
const handleRemoteAllRead = async (counts?: FeedCounts) => {
|
|
219
254
|
setAllRead();
|
|
220
|
-
await
|
|
255
|
+
await takeWriteCounts(counts);
|
|
221
256
|
emitCount();
|
|
222
257
|
};
|
|
223
258
|
|
|
@@ -251,6 +286,8 @@ export const useNotifications = () => {
|
|
|
251
286
|
notifications,
|
|
252
287
|
hasMore,
|
|
253
288
|
fetchBellCounts,
|
|
289
|
+
readBellCounts,
|
|
290
|
+
applyBellCounts,
|
|
254
291
|
fetchUnreadPreview,
|
|
255
292
|
fetchNotifications,
|
|
256
293
|
markAsRead,
|
|
@@ -262,3 +299,47 @@ export const useNotifications = () => {
|
|
|
262
299
|
handleRemoteAllRead,
|
|
263
300
|
};
|
|
264
301
|
};
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* The header bell's counts (the unread total and its tone) for the chrome
|
|
305
|
+
* drawing the bell, called in its setup: read with the page for a signed-in
|
|
306
|
+
* user, so a server render shows the badge, and the bell's state starts from
|
|
307
|
+
* the same counts in the browser before anything renders them. The browser
|
|
308
|
+
* neither asks again nor sees them move as it hydrates (which the inbox would
|
|
309
|
+
* take for a new notification and list again). The bell then keeps them
|
|
310
|
+
* current (realtime, its own refreshes).
|
|
311
|
+
*/
|
|
312
|
+
export function useRenderedBellCounts(): void {
|
|
313
|
+
const notifications = useNotifications();
|
|
314
|
+
if (
|
|
315
|
+
!useDmsUserSession().loggedIn.value ||
|
|
316
|
+
notifications.areCountsLoaded.value
|
|
317
|
+
)
|
|
318
|
+
return;
|
|
319
|
+
const read = useApiData<FeedCounts>(
|
|
320
|
+
BELL_COUNTS_DATA_KEY,
|
|
321
|
+
async ({ signal }) => {
|
|
322
|
+
const counts = await notifications.readBellCounts(signal);
|
|
323
|
+
notifications.applyBellCounts(counts);
|
|
324
|
+
return counts;
|
|
325
|
+
},
|
|
326
|
+
);
|
|
327
|
+
// A client navigation into the dashboard mounts the bell before this read
|
|
328
|
+
// answers: the bell waits for it instead of reading the counts again.
|
|
329
|
+
if (!import.meta.env.SSR)
|
|
330
|
+
renderedBellCountsRead = Promise.resolve(read).then(
|
|
331
|
+
() => undefined,
|
|
332
|
+
() => undefined,
|
|
333
|
+
);
|
|
334
|
+
onBeforeMount(() => {
|
|
335
|
+
if (read.data.value) notifications.applyBellCounts(read.data.value);
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** The browser's last `useRenderedBellCounts` read, settled or not. */
|
|
340
|
+
let renderedBellCountsRead: Promise<void> | undefined;
|
|
341
|
+
|
|
342
|
+
/** Waits for the counts `useRenderedBellCounts` reads, if it reads any. */
|
|
343
|
+
export function settledRenderedBellCounts(): Promise<void> | undefined {
|
|
344
|
+
return renderedBellCountsRead;
|
|
345
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
StoreActionBody,
|
|
3
|
+
StoreListing,
|
|
4
|
+
StoreListingEntry,
|
|
5
|
+
StoreOperationKind,
|
|
6
|
+
} from "../../types/module-store";
|
|
7
|
+
import { isAwaitingFrontend } from "../../build/utils/module-store-catalog";
|
|
8
|
+
|
|
9
|
+
export const ENDPOINT_STORE_LISTING = "/dms/store/listing";
|
|
10
|
+
const ENDPOINT_STORE_ACTION = "/dms/store";
|
|
11
|
+
/** How often the listing is read while an operation or the frontend runs. */
|
|
12
|
+
const FOLLOW_INTERVAL_MS = 1000;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The module store of the Modules page: the listing, the operations, and the
|
|
16
|
+
* follow-up that reads the listing again while something moves. An operation
|
|
17
|
+
* the owner started here reloads the page once the frontend serves its
|
|
18
|
+
* result, so a new module's pages appear without a manual refresh.
|
|
19
|
+
*/
|
|
20
|
+
export const useModuleStore = () => {
|
|
21
|
+
// Taken once, in setup: the follow-up reads the listing from a timer,
|
|
22
|
+
// outside any component context.
|
|
23
|
+
const { $authFetch } = useAuthFetch();
|
|
24
|
+
const listing = ref<StoreListing | undefined>(undefined);
|
|
25
|
+
const loadingError = ref<unknown>(null);
|
|
26
|
+
/** Modules an operation from this page changed, until the frontend follows. */
|
|
27
|
+
const awaitingReload = ref(new Set<string>());
|
|
28
|
+
let followTimer: ReturnType<typeof setTimeout> | undefined;
|
|
29
|
+
|
|
30
|
+
const isFollowing = computed(() => {
|
|
31
|
+
const modules = listing.value?.modules ?? [];
|
|
32
|
+
return (
|
|
33
|
+
listing.value?.busy === true ||
|
|
34
|
+
modules.some((entry) => isAwaitingFrontend(entry))
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
async function load(): Promise<void> {
|
|
39
|
+
try {
|
|
40
|
+
listing.value = await $authFetch<StoreListing>(ENDPOINT_STORE_LISTING);
|
|
41
|
+
loadingError.value = null;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
loadingError.value = error;
|
|
44
|
+
}
|
|
45
|
+
reloadWhenServed();
|
|
46
|
+
scheduleFollow();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Reloads the page once every module an operation from here changed is
|
|
51
|
+
* served by the frontend (or has no interface at all). A frontend that
|
|
52
|
+
* never reported stays put: the page says what to run instead.
|
|
53
|
+
*/
|
|
54
|
+
function reloadWhenServed(): void {
|
|
55
|
+
if (import.meta.env.SSR || awaitingReload.value.size === 0) return;
|
|
56
|
+
const changed = (listing.value?.modules ?? []).filter((entry) =>
|
|
57
|
+
awaitingReload.value.has(entry.id),
|
|
58
|
+
);
|
|
59
|
+
const isSettled = changed.every(
|
|
60
|
+
(entry) =>
|
|
61
|
+
entry.state !== "busy" &&
|
|
62
|
+
(entry.frontend === "synced" || entry.frontend === "none"),
|
|
63
|
+
);
|
|
64
|
+
const needsReload = changed.some((entry) => entry.frontend === "synced");
|
|
65
|
+
if (!isSettled) return;
|
|
66
|
+
awaitingReload.value = new Set();
|
|
67
|
+
if (needsReload) window.location.reload();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function scheduleFollow(): void {
|
|
71
|
+
if (import.meta.env.SSR) return;
|
|
72
|
+
if (followTimer) clearTimeout(followTimer);
|
|
73
|
+
followTimer = undefined;
|
|
74
|
+
if (!isFollowing.value) return;
|
|
75
|
+
followTimer = setTimeout(() => void load(), FOLLOW_INTERVAL_MS);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Starts `kind` on `entry`, with `body` when it carries more than the id;
|
|
80
|
+
* rejects with the server's answer when refused.
|
|
81
|
+
*/
|
|
82
|
+
async function run(
|
|
83
|
+
kind: StoreOperationKind,
|
|
84
|
+
entry: StoreListingEntry,
|
|
85
|
+
body: StoreActionBody = { id: entry.id },
|
|
86
|
+
): Promise<void> {
|
|
87
|
+
listing.value = await $authFetch<StoreListing>(
|
|
88
|
+
`${ENDPOINT_STORE_ACTION}/${kind}`,
|
|
89
|
+
{ method: "POST", body },
|
|
90
|
+
);
|
|
91
|
+
awaitingReload.value = new Set([...awaitingReload.value, entry.id]);
|
|
92
|
+
scheduleFollow();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function stop(): void {
|
|
96
|
+
if (followTimer) clearTimeout(followTimer);
|
|
97
|
+
followTimer = undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
listing,
|
|
102
|
+
loadingError,
|
|
103
|
+
isFollowing,
|
|
104
|
+
awaitingReload,
|
|
105
|
+
load,
|
|
106
|
+
run,
|
|
107
|
+
stop,
|
|
108
|
+
};
|
|
109
|
+
};
|
|
@@ -10,15 +10,7 @@ export interface ModulesListingError {
|
|
|
10
10
|
status?: number;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const STATE_KEY_MODULES = "dms-modulesListing";
|
|
14
|
-
const STATE_KEY_LOADING = "dms-modulesListingLoading";
|
|
15
|
-
const STATE_KEY_ERROR = "dms-modulesListingError";
|
|
16
|
-
const STATE_KEY_FETCHED_AT = "dms-modulesListingFetchedAt";
|
|
17
|
-
// A listing younger than this is fresh enough to show without refetching:
|
|
18
|
-
// it also covers the hydration right after a server render.
|
|
19
|
-
const REVALIDATE_AFTER_MS = 30_000;
|
|
20
13
|
export const ENDPOINT_MODULES_LISTING = "/dms/modules-listing";
|
|
21
|
-
const IN_FLIGHT_MODULES_LISTING_KEY = "__dmsInFlightModulesListing";
|
|
22
14
|
|
|
23
15
|
function readStatus(error: unknown): number | undefined {
|
|
24
16
|
const details = error as
|
|
@@ -33,106 +25,37 @@ function toListingError(error: unknown): ModulesListingError {
|
|
|
33
25
|
return { kind: "failed", status };
|
|
34
26
|
}
|
|
35
27
|
|
|
28
|
+
/**
|
|
29
|
+
* The modules catalog of the Modules page, read for platform owners only (a
|
|
30
|
+
* member's request would be refused): with the page, so a server render
|
|
31
|
+
* shows it and hands it to the browser, which does not ask again.
|
|
32
|
+
*/
|
|
36
33
|
export const useModulesListing = () => {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
const isOwner = useIsOwner();
|
|
35
|
+
const listing = useApiData<ModuleCatalogEntry[]>(() =>
|
|
36
|
+
isOwner.value ? ENDPOINT_MODULES_LISTING : null,
|
|
40
37
|
);
|
|
41
|
-
const isLoading = useDmsState<boolean>(STATE_KEY_LOADING, () => false);
|
|
42
|
-
const loadingError = useDmsState<ModulesListingError | null>(
|
|
43
|
-
STATE_KEY_ERROR,
|
|
44
|
-
() => null,
|
|
45
|
-
);
|
|
46
|
-
const fetchedAt = useDmsState<number>(STATE_KEY_FETCHED_AT, () => 0);
|
|
47
|
-
|
|
48
|
-
// Held on the DMS app, like the in-flight fetches of useSiteLayout: every
|
|
49
|
-
// caller writes the same listing state, so they share one fetch. Not module
|
|
50
|
-
// scope, which the server shares across the requests it renders at once: a
|
|
51
|
-
// second render would wait on the first one's fetch, whose answer lands in
|
|
52
|
-
// the first request's state, and render an empty listing.
|
|
53
|
-
const dmsApp = useDmsApp() as unknown as Record<PropertyKey, unknown>;
|
|
54
|
-
const inFlightLoadingPromise = {
|
|
55
|
-
get value(): Promise<void> | null {
|
|
56
|
-
return (dmsApp[IN_FLIGHT_MODULES_LISTING_KEY] as Promise<void>) ?? null;
|
|
57
|
-
},
|
|
58
|
-
set value(promise: Promise<void> | null) {
|
|
59
|
-
dmsApp[IN_FLIGHT_MODULES_LISTING_KEY] = promise;
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Fetches the catalog. With `keepOnFailure`, a failed request leaves the
|
|
65
|
-
* modules already on screen in place (a background revalidation must not
|
|
66
|
-
* blank the page); otherwise the failure replaces them with the error.
|
|
67
|
-
*/
|
|
68
|
-
function fetchModulesListing(keepOnFailure = false): Promise<void> {
|
|
69
|
-
const { $authFetch } = useAuthFetch();
|
|
70
|
-
isLoading.value = true;
|
|
71
|
-
if (!keepOnFailure) loadingError.value = null;
|
|
72
38
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const listingError = toListingError(error);
|
|
81
|
-
if (keepOnFailure && listingError.kind === "failed") return;
|
|
82
|
-
loadingError.value = listingError;
|
|
83
|
-
modules.value = undefined;
|
|
84
|
-
})
|
|
85
|
-
.finally(() => {
|
|
86
|
-
isLoading.value = false;
|
|
87
|
-
inFlightLoadingPromise.value = null;
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
inFlightLoadingPromise.value = promise;
|
|
91
|
-
return promise;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async function loadModulesListing(): Promise<void> {
|
|
95
|
-
if (inFlightLoadingPromise.value) {
|
|
96
|
-
await inFlightLoadingPromise.value;
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
if (modules.value !== undefined) {
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
await fetchModulesListing();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Loads the catalog again on request (Refresh, Try again): the current list
|
|
107
|
-
* stays on screen while loading, and a failure is shown, not swallowed.
|
|
108
|
-
*/
|
|
109
|
-
async function refresh(): Promise<void> {
|
|
110
|
-
if (inFlightLoadingPromise.value) {
|
|
111
|
-
await inFlightLoadingPromise.value;
|
|
112
|
-
}
|
|
113
|
-
await fetchModulesListing();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Reloads statuses and readouts while keeping the current list on screen,
|
|
118
|
-
* unless the listing is only a few seconds old: readouts are live figures,
|
|
119
|
-
* so a later revisit should not show stale ones.
|
|
120
|
-
*/
|
|
121
|
-
async function revalidate(): Promise<void> {
|
|
122
|
-
if (inFlightLoadingPromise.value) {
|
|
123
|
-
await inFlightLoadingPromise.value;
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
if (Date.now() - fetchedAt.value < REVALIDATE_AFTER_MS) return;
|
|
127
|
-
await fetchModulesListing(true);
|
|
128
|
-
}
|
|
39
|
+
const loadingError = computed<ModulesListingError | null>(() =>
|
|
40
|
+
listing.error.value ? toListingError(listing.error.value) : null,
|
|
41
|
+
);
|
|
42
|
+
// A failed read replaces the modules with the error.
|
|
43
|
+
const modules = computed<ModuleCatalogEntry[] | undefined>(() =>
|
|
44
|
+
loadingError.value ? undefined : (listing.data.value ?? undefined),
|
|
45
|
+
);
|
|
129
46
|
|
|
130
47
|
return {
|
|
131
48
|
modules,
|
|
132
|
-
isLoading,
|
|
49
|
+
isLoading: listing.pending,
|
|
133
50
|
loadingError,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
51
|
+
/** Waits for the catalog to be read. */
|
|
52
|
+
loadModulesListing: async (): Promise<void> => {
|
|
53
|
+
await listing;
|
|
54
|
+
},
|
|
55
|
+
/**
|
|
56
|
+
* Reads the catalog again (Refresh, Try again): the current list stays
|
|
57
|
+
* on screen while loading, and a failure is shown, not swallowed.
|
|
58
|
+
*/
|
|
59
|
+
refresh: (): Promise<void> => listing.refresh(),
|
|
137
60
|
};
|
|
138
61
|
};
|
|
@@ -57,13 +57,3 @@ export function preloadPageLayoutComponents(
|
|
|
57
57
|
|
|
58
58
|
loadPageLayoutComponents(layout).catch(() => {});
|
|
59
59
|
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Same as preloadPageLayoutComponents, for speculative loading (e.g. link
|
|
63
|
-
* hover) where the user may never navigate to the page.
|
|
64
|
-
*/
|
|
65
|
-
export function prefetchPageLayoutComponents(
|
|
66
|
-
layout: PageLayout | null | undefined,
|
|
67
|
-
): void {
|
|
68
|
-
preloadPageLayoutComponents(layout);
|
|
69
|
-
}
|