@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
|
@@ -23,6 +23,12 @@ interface SessionInfo {
|
|
|
23
23
|
isCurrent: boolean;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/** The sessions, and when they were read: what "active 5 minutes ago" counts from. */
|
|
27
|
+
interface SessionsRead {
|
|
28
|
+
sessions: SessionInfo[];
|
|
29
|
+
readAt: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
interface SessionsRevokedResponse {
|
|
27
33
|
count: number;
|
|
28
34
|
sessionHandoff?: SessionHandoff;
|
|
@@ -47,10 +53,25 @@ const adoptSessionHandoff = useSessionHandoff();
|
|
|
47
53
|
const { overview, refresh: refreshOverview } = useSecurityOverview();
|
|
48
54
|
const { formatDate, formatRelative, errorMessage } = useSecurityFormat();
|
|
49
55
|
|
|
50
|
-
|
|
51
|
-
//
|
|
52
|
-
const
|
|
53
|
-
|
|
56
|
+
// Read with the page: the server renders the list and hands it over, and
|
|
57
|
+
// the browser says "active 5 minutes ago" as of the same read.
|
|
58
|
+
const list = useApiData<SessionsRead>(SESSIONS_URL, {
|
|
59
|
+
transform: (sessions) => ({
|
|
60
|
+
sessions: sessions as SessionInfo[],
|
|
61
|
+
readAt: Date.now(),
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
const sessions = computed(() => list.data.value?.sessions ?? []);
|
|
65
|
+
const readAt = computed(() => list.data.value?.readAt);
|
|
66
|
+
// The list could not be loaded: shown as such, never as "no sessions". A
|
|
67
|
+
// failed reload keeps the list on screen.
|
|
68
|
+
const hasLoadError = computed(
|
|
69
|
+
() =>
|
|
70
|
+
!list.data.value &&
|
|
71
|
+
!!list.error.value &&
|
|
72
|
+
list.status.value !== "pending",
|
|
73
|
+
);
|
|
74
|
+
const isLoading = computed(() => !list.data.value && !hasLoadError.value);
|
|
54
75
|
const revokingId = ref<string | null>(null);
|
|
55
76
|
const isConfirmOpen = ref(false);
|
|
56
77
|
const isSigningOutOthers = ref(false);
|
|
@@ -73,18 +94,17 @@ const sessionName = (session: SessionInfo): string =>
|
|
|
73
94
|
const sessionPlace = (session: SessionInfo): string =>
|
|
74
95
|
session.location || t("page.settings.sessions.unknown_location");
|
|
75
96
|
|
|
76
|
-
|
|
77
|
-
sessions.value = await $authFetch<SessionInfo[]>(SESSIONS_URL);
|
|
78
|
-
hasLoadError.value = false;
|
|
79
|
-
}
|
|
97
|
+
const fetchSessions = (): Promise<void> => list.refresh({ dedupe: "defer" });
|
|
80
98
|
|
|
81
99
|
async function revoke(session: SessionInfo): Promise<void> {
|
|
82
100
|
revokingId.value = session._id;
|
|
83
101
|
try {
|
|
84
102
|
await $authFetch(`${SESSIONS_URL}/${session._id}`, { method: "DELETE" });
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
103
|
+
if (list.data.value)
|
|
104
|
+
list.data.value = {
|
|
105
|
+
...list.data.value,
|
|
106
|
+
sessions: sessions.value.filter((entry) => entry._id !== session._id),
|
|
107
|
+
};
|
|
88
108
|
toast.add({ title: t("page.settings.sessions.revoked"), color: "success" });
|
|
89
109
|
await refreshOverview();
|
|
90
110
|
} catch (error) {
|
|
@@ -131,34 +151,21 @@ watch(
|
|
|
131
151
|
() => overview.value?.activeSessions,
|
|
132
152
|
(count, previous) => {
|
|
133
153
|
if (previous !== undefined && count !== sessions.value.length) {
|
|
134
|
-
|
|
135
|
-
fetchSessions().catch(() => {});
|
|
154
|
+
void fetchSessions();
|
|
136
155
|
}
|
|
137
156
|
},
|
|
138
157
|
);
|
|
139
158
|
|
|
140
|
-
async function loadSessions(): Promise<void> {
|
|
141
|
-
isLoading.value = true;
|
|
142
|
-
try {
|
|
143
|
-
await fetchSessions();
|
|
144
|
-
} catch {
|
|
145
|
-
hasLoadError.value = true;
|
|
146
|
-
} finally {
|
|
147
|
-
isLoading.value = false;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
159
|
const retryActions = computed(() => [
|
|
152
160
|
{
|
|
153
161
|
label: t("dms.table.load_error_retry"),
|
|
154
162
|
icon: "i-ph-arrows-clockwise",
|
|
155
163
|
color: "neutral" as const,
|
|
156
164
|
variant: "outline" as const,
|
|
157
|
-
onClick: () => void
|
|
165
|
+
onClick: () => void fetchSessions(),
|
|
158
166
|
},
|
|
159
167
|
]);
|
|
160
168
|
|
|
161
|
-
onMounted(loadSessions);
|
|
162
169
|
</script>
|
|
163
170
|
|
|
164
171
|
<template>
|
|
@@ -249,7 +256,7 @@ onMounted(loadSessions);
|
|
|
249
256
|
<span v-else>
|
|
250
257
|
{{
|
|
251
258
|
t("page.settings.security.sessions.active_ago", {
|
|
252
|
-
time: formatRelative(session.lastActiveAt),
|
|
259
|
+
time: formatRelative(session.lastActiveAt, readAt),
|
|
253
260
|
})
|
|
254
261
|
}}
|
|
255
262
|
</span>
|
|
@@ -323,7 +330,7 @@ onMounted(loadSessions);
|
|
|
323
330
|
{{
|
|
324
331
|
session.isCurrent
|
|
325
332
|
? t("page.settings.security.sessions.active_now_lower")
|
|
326
|
-
: formatRelative(session.lastActiveAt)
|
|
333
|
+
: formatRelative(session.lastActiveAt, readAt)
|
|
327
334
|
}}
|
|
328
335
|
</small>
|
|
329
336
|
</span>
|
|
@@ -73,7 +73,7 @@ const { formatDate, errorMessage } = useSecurityFormat();
|
|
|
73
73
|
const isProcessing = ref(false);
|
|
74
74
|
const isTotpOpen = ref(false);
|
|
75
75
|
const totpSetup = ref<TotpSetup | null>(null);
|
|
76
|
-
const { user } =
|
|
76
|
+
const { user } = useDmsUserSession<User>();
|
|
77
77
|
const {
|
|
78
78
|
codes,
|
|
79
79
|
isRegenerated,
|
package/frontend-vue/layers/dms-layout/app/build/composables/dev-reload/useDevReloadHolder.ts
CHANGED
|
@@ -256,7 +256,8 @@ async function awaitRoute(
|
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
// Deadline spent waiting on someone else's pass.
|
|
259
|
-
if (state.refreshing || deps.now() >= deadline)
|
|
259
|
+
if (state.refreshing || deps.now() >= deadline)
|
|
260
|
+
return deps.isRouteKnown(path);
|
|
260
261
|
|
|
261
262
|
const committed = await runRefresh(state, path, deadline);
|
|
262
263
|
// A trailing re-run booked over ours may be the one that brought the route
|
|
@@ -293,14 +294,14 @@ export function createDevReloadCoordinator(
|
|
|
293
294
|
// has been (re)committed for the current route. Kept separate from the layout
|
|
294
295
|
// commit so the commit can stay atomic with its route check (see
|
|
295
296
|
// probeAndCommitRoute) — the 404-prone part was committing the layout, not this.
|
|
296
|
-
|
|
297
|
+
// The page layouts reload with the layout commit, which empties their cache:
|
|
298
|
+
// no async data key holds them.
|
|
299
|
+
async function postLayoutRefresh(): Promise<void> {
|
|
297
300
|
const permissions = usePermissions();
|
|
298
301
|
// fetchPermissions() refetches and swaps the set atomically, so there is no
|
|
299
302
|
// need to clear() first — clearing would briefly empty permissions and flash
|
|
300
303
|
// permission-gated UI.
|
|
301
304
|
await permissions.fetchPermissions();
|
|
302
|
-
await refreshDmsData(currentRoutePath);
|
|
303
|
-
await refreshDmsData(`layout-${currentRoutePath}`);
|
|
304
305
|
}
|
|
305
306
|
|
|
306
307
|
interface DevReloadHolder {
|
|
@@ -326,7 +327,7 @@ export function useDevReloadHolder(): DevReloadHolder {
|
|
|
326
327
|
const reloading = import.meta.env.DEV ? useDevReloading() : ref(false);
|
|
327
328
|
const siteLayout = useSiteLayout();
|
|
328
329
|
const router = useDmsRouter();
|
|
329
|
-
const currentError =
|
|
330
|
+
const currentError = useDmsError();
|
|
330
331
|
|
|
331
332
|
const coordinator = createDevReloadCoordinator({
|
|
332
333
|
isDev: import.meta.env.DEV,
|
|
@@ -343,7 +344,7 @@ export function useDevReloadHolder(): DevReloadHolder {
|
|
|
343
344
|
if (router.currentRoute.value.path !== recoveredPath) return;
|
|
344
345
|
const err = currentError.value;
|
|
345
346
|
if (!err || err.statusCode !== HTTP_NOT_FOUND) return;
|
|
346
|
-
void
|
|
347
|
+
void clearDmsError();
|
|
347
348
|
},
|
|
348
349
|
setReloading: (value) => {
|
|
349
350
|
reloading.value = value;
|
|
@@ -172,7 +172,7 @@ export function useActiveSidePanel(): ActiveSidePanel {
|
|
|
172
172
|
const { panels } = useAppSidePanels();
|
|
173
173
|
const preferences = useSidePanelPreferences();
|
|
174
174
|
const dragWidth = useSidePanelDragWidth();
|
|
175
|
-
const { loggedIn } =
|
|
175
|
+
const { loggedIn } = useDmsUserSession();
|
|
176
176
|
const panel = computed(() => {
|
|
177
177
|
if (!loggedIn.value) return null;
|
|
178
178
|
const { openId } = readSidePanelPreferences(preferences.value);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { watch } from "vue";
|
|
2
|
+
import { useDmsInertiaPage } from "#dms/frontend-module";
|
|
3
|
+
import {
|
|
4
|
+
type CountedNavBadge,
|
|
5
|
+
useNavBadges,
|
|
6
|
+
} from "#dms-ui/app/build/composables/navigation/useNavBadges";
|
|
7
|
+
|
|
8
|
+
interface CountedPage {
|
|
9
|
+
navBadges?: Record<string, CountedNavBadge>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Draws the navigation badges each page visit carries (`navBadges`): they
|
|
14
|
+
* are counted for the visit and travel with the page, not with the site
|
|
15
|
+
* layout a browser keeps for minutes. The dashboard frame calls it, so the
|
|
16
|
+
* server render draws them as the browser does.
|
|
17
|
+
*/
|
|
18
|
+
export function useVisitNavBadges(): void {
|
|
19
|
+
const inertiaPage = useDmsInertiaPage();
|
|
20
|
+
const { applyCountedNavBadges } = useNavBadges();
|
|
21
|
+
watch(
|
|
22
|
+
() => (inertiaPage.props.page as CountedPage | undefined)?.navBadges,
|
|
23
|
+
applyCountedNavBadges,
|
|
24
|
+
{ immediate: true },
|
|
25
|
+
);
|
|
26
|
+
}
|
package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationCatalog.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { pageRoutePath } from "#dms-core/app/utils/pageRoutes";
|
|
1
2
|
export type NotificationTogglePermission = "allowed" | "forbidden" | "default";
|
|
2
3
|
|
|
3
4
|
export interface NotificationCategory {
|
|
@@ -32,34 +33,34 @@ export const buildPreferenceKey = (categoryId: string, subjectId: string) =>
|
|
|
32
33
|
export const subjectPreferenceKey = (subject: NotificationSubject) =>
|
|
33
34
|
buildPreferenceKey(subject.category.id, subject.id);
|
|
34
35
|
|
|
36
|
+
const CATALOG_URL = pageRoutePath(
|
|
37
|
+
"/settings/user/notifications",
|
|
38
|
+
"/categories",
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
/** Whether a read has answered with a failure, and no retry is running. */
|
|
42
|
+
export const hasFailed = (read: ApiDataResult<unknown>): boolean =>
|
|
43
|
+
!!read.error.value && read.status.value !== "pending";
|
|
44
|
+
|
|
35
45
|
/**
|
|
36
|
-
* The registered notification categories and subjects,
|
|
37
|
-
* shared by the preferences matrix and the inbox (which tags each
|
|
46
|
+
* The registered notification categories and subjects, read once with the
|
|
47
|
+
* page and shared by the preferences matrix and the inbox (which tags each
|
|
38
48
|
* notification with its source).
|
|
49
|
+
*
|
|
50
|
+
* @param enabled Whether the page shows them: nothing is read otherwise
|
|
39
51
|
*/
|
|
40
|
-
export const useNotificationCatalog = () => {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
"notification-catalog-categories",
|
|
44
|
-
() => [],
|
|
45
|
-
);
|
|
46
|
-
const subjects = useDmsState<NotificationSubject[]>(
|
|
47
|
-
"notification-catalog-subjects",
|
|
48
|
-
() => [],
|
|
49
|
-
);
|
|
50
|
-
const isLoaded = useDmsState<boolean>(
|
|
51
|
-
"notification-catalog-loaded",
|
|
52
|
-
() => false,
|
|
52
|
+
export const useNotificationCatalog = (enabled: () => boolean = () => true) => {
|
|
53
|
+
const catalog = useApiData<NotificationCatalogResponse>(() =>
|
|
54
|
+
enabled() ? CATALOG_URL : null,
|
|
53
55
|
);
|
|
56
|
+
const categories = computed(() => catalog.data.value?.categories ?? []);
|
|
57
|
+
const subjects = computed(() => catalog.data.value?.subjects ?? []);
|
|
58
|
+
const isLoaded = computed(() => !!catalog.data.value);
|
|
59
|
+
const loadFailed = computed(() => !isLoaded.value && hasFailed(catalog));
|
|
54
60
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
);
|
|
59
|
-
categories.value = response.categories;
|
|
60
|
-
subjects.value = response.subjects;
|
|
61
|
-
isLoaded.value = true;
|
|
62
|
-
};
|
|
61
|
+
/** Reads the catalog again (a retry). */
|
|
62
|
+
const loadCatalog = (): Promise<void> =>
|
|
63
|
+
catalog.refresh({ dedupe: "defer" });
|
|
63
64
|
|
|
64
65
|
const subjectsOf = (categoryId: string) =>
|
|
65
66
|
subjects.value.filter((subject) => subject.category.id === categoryId);
|
|
@@ -77,6 +78,7 @@ export const useNotificationCatalog = () => {
|
|
|
77
78
|
categories,
|
|
78
79
|
subjects,
|
|
79
80
|
isLoaded,
|
|
81
|
+
loadFailed,
|
|
80
82
|
loadCatalog,
|
|
81
83
|
subjectsOf,
|
|
82
84
|
findCategory,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { pageRoutePath } from "#dms-core/app/utils/pageRoutes";
|
|
1
2
|
import type { SaveStatusState } from "#dms-ui/app/components/save-bar/SaveStatus.vue";
|
|
2
3
|
import { useInstantSave } from "#dms-ui/app/build/composables/instant-save/useInstantSave";
|
|
3
4
|
import {
|
|
5
|
+
hasFailed,
|
|
4
6
|
type NotificationCategory,
|
|
5
7
|
type NotificationSubject,
|
|
6
8
|
subjectPreferenceKey,
|
|
@@ -9,19 +11,45 @@ import {
|
|
|
9
11
|
|
|
10
12
|
type PreferenceMap = Record<string, boolean>;
|
|
11
13
|
|
|
12
|
-
const PREFERENCES_URL =
|
|
14
|
+
const PREFERENCES_URL = pageRoutePath(
|
|
15
|
+
"/settings/user/notifications",
|
|
16
|
+
"/preferences",
|
|
17
|
+
);
|
|
13
18
|
|
|
14
19
|
/**
|
|
15
|
-
* State of the notification preferences matrix
|
|
16
|
-
*
|
|
17
|
-
*
|
|
20
|
+
* State of the notification preferences matrix, read with the page (the
|
|
21
|
+
* stored preferences and the catalog). Each switch saves on its own through
|
|
22
|
+
* the shared instant save (a PATCH of the keys it changes); a failed save
|
|
23
|
+
* puts the switches back and keeps the change for a retry.
|
|
24
|
+
*
|
|
25
|
+
* @param enabled Whether the page shows them: nothing is read otherwise
|
|
18
26
|
*/
|
|
19
|
-
export const useNotificationPreferences = (
|
|
27
|
+
export const useNotificationPreferences = (
|
|
28
|
+
enabled: () => boolean = () => true,
|
|
29
|
+
) => {
|
|
20
30
|
const { $authFetch } = useAuthFetch();
|
|
21
|
-
const catalog = useNotificationCatalog();
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
const catalog = useNotificationCatalog(enabled);
|
|
32
|
+
const stored = useApiData<PreferenceMap>(() =>
|
|
33
|
+
enabled() ? PREFERENCES_URL : null,
|
|
34
|
+
);
|
|
35
|
+
// The switches edit the read's data in place.
|
|
36
|
+
const preferences = computed<PreferenceMap>({
|
|
37
|
+
get: () => stored.data.value ?? {},
|
|
38
|
+
set: (value) => {
|
|
39
|
+
stored.data.value = value;
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
// A user who never changed a switch has no stored preferences (null).
|
|
43
|
+
const isStoredRead = computed(
|
|
44
|
+
() => stored.status.value === "success" || stored.data.value != null,
|
|
45
|
+
);
|
|
46
|
+
const loadFailed = computed(
|
|
47
|
+
() => (!isStoredRead.value && hasFailed(stored)) || catalog.loadFailed.value,
|
|
48
|
+
);
|
|
49
|
+
const isLoading = computed(
|
|
50
|
+
() =>
|
|
51
|
+
!(isStoredRead.value && catalog.isLoaded.value) && !loadFailed.value,
|
|
52
|
+
);
|
|
25
53
|
|
|
26
54
|
const instant = useInstantSave<PreferenceMap>({
|
|
27
55
|
read: (key) => preferences.value[key] ?? true,
|
|
@@ -54,21 +82,14 @@ export const useNotificationPreferences = () => {
|
|
|
54
82
|
),
|
|
55
83
|
);
|
|
56
84
|
|
|
85
|
+
/** Reads the preferences and the catalog again (a retry). */
|
|
57
86
|
const load = async () => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
]);
|
|
65
|
-
preferences.value = stored ?? {};
|
|
66
|
-
instant.confirm(preferences.value);
|
|
67
|
-
} catch {
|
|
68
|
-
loadFailed.value = true;
|
|
69
|
-
} finally {
|
|
70
|
-
isLoading.value = false;
|
|
71
|
-
}
|
|
87
|
+
await Promise.all([
|
|
88
|
+
stored.refresh({ dedupe: "defer" }),
|
|
89
|
+
catalog.loadCatalog(),
|
|
90
|
+
]);
|
|
91
|
+
// A failed save then puts back what the server holds now.
|
|
92
|
+
if (stored.status.value === "success") instant.confirm(preferences.value);
|
|
72
93
|
};
|
|
73
94
|
|
|
74
95
|
return {
|
|
@@ -28,7 +28,7 @@ interface BrowserSession {
|
|
|
28
28
|
* account entry, and the old entry — holding a dead refresh token — is dropped.
|
|
29
29
|
*/
|
|
30
30
|
export function useSessionHandoff() {
|
|
31
|
-
const { session, fetch: refreshUser } =
|
|
31
|
+
const { session, fetch: refreshUser } = useDmsUserSession<
|
|
32
32
|
unknown,
|
|
33
33
|
BrowserSession
|
|
34
34
|
>();
|
package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/preferencesSummary.ts
CHANGED
|
@@ -59,7 +59,7 @@ export function resolvePreferenceRows(
|
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
/** What `GET /settings/user/profile/access` answers. */
|
|
62
|
+
/** What `GET /dms/pages/settings/user/profile/access` answers. */
|
|
63
63
|
export interface ProfileAccess {
|
|
64
64
|
roles: string[];
|
|
65
65
|
workspaceOwner: boolean;
|
package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useAccountData.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { pageRoutePath } from "#dms-core/app/utils/pageRoutes";
|
|
1
2
|
import UFormField from "@nuxt/ui/components/FormField.vue";
|
|
2
3
|
import UIcon from "@nuxt/ui/runtime/vue/components/Icon.vue";
|
|
3
4
|
import UInput from "@nuxt/ui/components/Input.vue";
|
|
@@ -12,7 +13,7 @@ import { resolveFieldErrors } from "#dms-core/app/composables/useFieldErrors";
|
|
|
12
13
|
import { REQUIRED_MESSAGE } from "#dms-core/app/composables/useFormValidation";
|
|
13
14
|
import { useSecurityFormat } from "../security/useSecurityFormat";
|
|
14
15
|
|
|
15
|
-
const PROFILE_ENDPOINT = "/settings/user/profile";
|
|
16
|
+
const PROFILE_ENDPOINT = pageRoutePath("/settings/user/profile");
|
|
16
17
|
const EXPORT_ENDPOINT = `${PROFILE_ENDPOINT}/account-export`;
|
|
17
18
|
const DELETION_ENDPOINT = `${PROFILE_ENDPOINT}/account-deletion`;
|
|
18
19
|
const EXPORT_FILE_PREFIX = "dms-account-export";
|
|
@@ -64,7 +65,7 @@ export function accountExportFilename(at: Date = new Date()): string {
|
|
|
64
65
|
export function useAccountData() {
|
|
65
66
|
const { $authFetch } = useAuthFetch();
|
|
66
67
|
const { confirm } = useConfirm();
|
|
67
|
-
const { user, clear } =
|
|
68
|
+
const { user, clear } = useDmsUserSession<User>();
|
|
68
69
|
const { removeAccount } = useMultiAccount();
|
|
69
70
|
const { processI18n, processApiMessage } = useTranslation();
|
|
70
71
|
const { errorMessage, errorCode } = useSecurityFormat();
|
package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useProfileAvatar.ts
CHANGED
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
} from "#dms-ui/app/utils/imageResize";
|
|
13
13
|
|
|
14
14
|
const PRESIGN_URL = "/api/files/presign";
|
|
15
|
-
const METADATA_URL = "/api/files/metadata";
|
|
16
15
|
|
|
17
16
|
/** Upload options the backend serialized for the avatar image field. */
|
|
18
17
|
export interface AvatarFieldOptions {
|
|
@@ -25,10 +24,6 @@ export interface AvatarFieldOptions {
|
|
|
25
24
|
/** Why a picked file was refused before any upload. */
|
|
26
25
|
export type AvatarRejection = "mimetype" | "size";
|
|
27
26
|
|
|
28
|
-
interface FileMetadataResponse {
|
|
29
|
-
url: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
27
|
/** Outcome of an avatar upload: a staged key, or why it was refused. */
|
|
33
28
|
export interface AvatarUploadResult {
|
|
34
29
|
key?: string;
|
|
@@ -79,17 +74,5 @@ export function useProfileAvatar(options: () => AvatarFieldOptions) {
|
|
|
79
74
|
return { key: presign.resourceKey };
|
|
80
75
|
}
|
|
81
76
|
|
|
82
|
-
|
|
83
|
-
async function resolveUrl(key: string): Promise<string | null> {
|
|
84
|
-
try {
|
|
85
|
-
const metadata = await $authFetch<FileMetadataResponse>(METADATA_URL, {
|
|
86
|
-
query: { resourceKey: key, storage: options().storage },
|
|
87
|
-
});
|
|
88
|
-
return metadata.url;
|
|
89
|
-
} catch {
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return { progress, upload, resolveUrl };
|
|
77
|
+
return { progress, upload };
|
|
95
78
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { pageRoutePath } from "#dms-core/app/utils/pageRoutes";
|
|
1
2
|
import { usePreferredDark } from "@vueuse/core";
|
|
2
3
|
import { computed, onMounted, ref } from "vue";
|
|
3
4
|
import { useUserRegionalPreferences } from "#dms-core/app/composables/user/useUserRegionalPreferences";
|
|
@@ -24,7 +25,10 @@ import {
|
|
|
24
25
|
tallySubjects,
|
|
25
26
|
} from "./preferencesSummary";
|
|
26
27
|
|
|
27
|
-
export const PROFILE_ACCESS_ENDPOINT =
|
|
28
|
+
export const PROFILE_ACCESS_ENDPOINT = pageRoutePath(
|
|
29
|
+
"/settings/user/profile",
|
|
30
|
+
"/access",
|
|
31
|
+
);
|
|
28
32
|
// Sample values the skeletons are shaped from.
|
|
29
33
|
const SAMPLE_ACCESS: ProfileAccess = {
|
|
30
34
|
roles: ["Admin", "Finance"],
|
|
@@ -50,16 +54,15 @@ const MONDAY = 1;
|
|
|
50
54
|
* and each one's sentence, `undefined` while it is not known yet (the row
|
|
51
55
|
* shows a skeleton then, never a made-up "0 unread").
|
|
52
56
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* own; the regional and appearance sentences, which depend on
|
|
56
|
-
* (time zone, today's date, the system theme), are written after
|
|
57
|
-
* the server and the hydrating browser render the same skeleton.
|
|
57
|
+
* The roles and the notification preferences are read with the page (a
|
|
58
|
+
* server render shows them and hands them over); the unread count is the
|
|
59
|
+
* header bell's own; the regional and appearance sentences, which depend on
|
|
60
|
+
* the browser (time zone, today's date, the system theme), are written after
|
|
61
|
+
* mount so the server and the hydrating browser render the same skeleton.
|
|
58
62
|
*/
|
|
59
63
|
export function useProfilePreferences() {
|
|
60
64
|
const { t: translate, locale } = useI18n();
|
|
61
65
|
const t = translate as SummaryTranslate;
|
|
62
|
-
const { $authFetch } = useAuthFetch();
|
|
63
66
|
const { groups } = useSettingsNavigation();
|
|
64
67
|
const isOwner = useIsOwner();
|
|
65
68
|
const isMounted = ref(false);
|
|
@@ -76,23 +79,14 @@ export function useProfilePreferences() {
|
|
|
76
79
|
),
|
|
77
80
|
);
|
|
78
81
|
|
|
79
|
-
const access =
|
|
80
|
-
const accessFailed = ref(false);
|
|
82
|
+
const access = useApiData<ProfileAccess>(PROFILE_ACCESS_ENDPOINT);
|
|
81
83
|
const accessSummary = computed(() => {
|
|
82
|
-
if (access.value) return buildAccessSummary(t, access.value);
|
|
83
|
-
return
|
|
84
|
+
if (access.data.value) return buildAccessSummary(t, access.data.value);
|
|
85
|
+
return access.error.value
|
|
84
86
|
? t("page.settings.profile.preferences.access_unavailable")
|
|
85
87
|
: undefined;
|
|
86
88
|
});
|
|
87
89
|
|
|
88
|
-
async function loadAccess(): Promise<void> {
|
|
89
|
-
try {
|
|
90
|
-
access.value = await $authFetch<ProfileAccess>(PROFILE_ACCESS_ENDPOINT);
|
|
91
|
-
} catch {
|
|
92
|
-
accessFailed.value = true;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
90
|
const { uniqueLocales } = useUniqueLocales();
|
|
97
91
|
const regional = useUserRegionalPreferences();
|
|
98
92
|
// Frozen at mount: the row shows formats, not a ticking clock.
|
|
@@ -117,8 +111,12 @@ export function useProfilePreferences() {
|
|
|
117
111
|
});
|
|
118
112
|
});
|
|
119
113
|
|
|
120
|
-
const notificationPreferences = useNotificationPreferences(
|
|
121
|
-
|
|
114
|
+
const notificationPreferences = useNotificationPreferences(
|
|
115
|
+
() => rows.value.notifications.visible,
|
|
116
|
+
);
|
|
117
|
+
const { subjects } = useNotificationCatalog(
|
|
118
|
+
() => rows.value.notifications.visible,
|
|
119
|
+
);
|
|
122
120
|
const { unreadCount, unreadTone, areCountsLoaded } = useNotifications();
|
|
123
121
|
const isNotificationsLoading = computed(
|
|
124
122
|
() => notificationPreferences.isLoading.value || !areCountsLoaded.value,
|
|
@@ -188,8 +186,6 @@ export function useProfilePreferences() {
|
|
|
188
186
|
onMounted(() => {
|
|
189
187
|
today = new Date();
|
|
190
188
|
isMounted.value = true;
|
|
191
|
-
void loadAccess();
|
|
192
|
-
if (rows.value.notifications.visible) void notificationPreferences.load();
|
|
193
189
|
});
|
|
194
190
|
|
|
195
191
|
return {
|
package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityFormat.ts
CHANGED
|
@@ -53,9 +53,12 @@ export function useSecurityFormat() {
|
|
|
53
53
|
Math.floor((Date.now() - new Date(value).getTime()) / MS_PER_DAY),
|
|
54
54
|
);
|
|
55
55
|
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
/**
|
|
57
|
+
* "3 days ago", "in an hour"… or "now" under a minute, as of `now` (a
|
|
58
|
+
* list's read time, which the server and the hydrating browser share).
|
|
59
|
+
*/
|
|
60
|
+
const formatRelative = (value: DateInput, now = Date.now()): string => {
|
|
61
|
+
const elapsed = new Date(value).getTime() - now;
|
|
59
62
|
const formatter = new Intl.RelativeTimeFormat(locale.value, {
|
|
60
63
|
numeric: "auto",
|
|
61
64
|
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { pageRoutePath } from "#dms-core/app/utils/pageRoutes";
|
|
1
2
|
import { useNavBadges } from "#dms-ui/app/build/composables/navigation/useNavBadges";
|
|
2
3
|
import type { Tone } from "#dms-ui/app/types/tone";
|
|
3
4
|
|
|
4
5
|
/** Base URL of the Security page API. */
|
|
5
|
-
export const SECURITY_ENDPOINT = "/settings/user/security";
|
|
6
|
+
export const SECURITY_ENDPOINT = pageRoutePath("/settings/user/security");
|
|
6
7
|
/** Route of the Security page. */
|
|
7
8
|
export const SECURITY_PAGE_PATH = "/settings/user/security";
|
|
8
9
|
const SECURITY_PAGE_ID = "settings.user.security";
|
|
@@ -68,21 +69,19 @@ const ATTENTION_TONE_ORDER: Tone[] = [
|
|
|
68
69
|
|
|
69
70
|
/**
|
|
70
71
|
* Shared Security summary: the status strip, every Security block and the
|
|
71
|
-
* profile pointer card read the same
|
|
72
|
-
*
|
|
72
|
+
* profile pointer card read the same request, with the page (a server
|
|
73
|
+
* render shows it and hands it over), and any change refreshes it for all
|
|
74
|
+
* of them. It also keeps the navigation badge of the Security page, the
|
|
73
75
|
* count of what needs attention in the tone the server gives it, in sync
|
|
74
76
|
* after a change.
|
|
75
77
|
*/
|
|
76
78
|
export function useSecurityOverview() {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
const summary = useApiData<SecurityOverview>(SECURITY_ENDPOINT);
|
|
80
|
+
const overview = computed(() => summary.data.value ?? null);
|
|
81
|
+
// A retry in progress is not a failure to show.
|
|
82
|
+
const isUnavailable = computed(
|
|
83
|
+
() => !!summary.error.value && summary.status.value !== "pending",
|
|
80
84
|
);
|
|
81
|
-
const isUnavailable = useDmsState<boolean>(
|
|
82
|
-
"dms-security-overview-unavailable",
|
|
83
|
-
() => false,
|
|
84
|
-
);
|
|
85
|
-
const { $authFetch } = useAuthFetch();
|
|
86
85
|
const { setNavBadge } = useNavBadges();
|
|
87
86
|
|
|
88
87
|
const attention = computed<SecurityAttention[]>(
|
|
@@ -105,14 +104,10 @@ export function useSecurityOverview() {
|
|
|
105
104
|
return ATTENTION_TONE_ORDER.find((tone) => present.includes(tone));
|
|
106
105
|
}
|
|
107
106
|
|
|
107
|
+
/** Reads the summary again after a change: every block follows. */
|
|
108
108
|
async function refresh(): Promise<void> {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
isUnavailable.value = false;
|
|
112
|
-
} catch {
|
|
113
|
-
isUnavailable.value = true;
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
109
|
+
await summary.refresh({ dedupe: "defer" });
|
|
110
|
+
if (summary.error.value) return;
|
|
116
111
|
const count = attention.value.length;
|
|
117
112
|
setNavBadge(
|
|
118
113
|
SECURITY_PAGE_ID,
|