@antelopejs/dms 0.8.3 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/implementations/dms/frontend-backend-proxies.d.ts +3 -0
- package/dist/implementations/dms/frontend-backend-proxies.js +39 -0
- package/dist/implementations/dms/frontend-backend-proxies.js.map +1 -0
- package/dist/implementations/dms/frontend-bootstrap.d.ts +1 -0
- package/dist/implementations/dms/frontend-bootstrap.js +13 -0
- package/dist/implementations/dms/frontend-bootstrap.js.map +1 -1
- package/dist/implementations/dms/frontend-modules.d.ts +4 -2
- package/dist/implementations/dms/frontend-modules.js +8 -7
- package/dist/implementations/dms/frontend-modules.js.map +1 -1
- package/dist/implementations/dms/manifest.d.ts +6 -0
- package/dist/implementations/dms/manifest.js +13 -0
- package/dist/implementations/dms/manifest.js.map +1 -1
- package/dist/implementations/dms/menu-categories.d.ts +16 -0
- package/dist/implementations/dms/menu-categories.js +30 -0
- package/dist/implementations/dms/menu-categories.js.map +1 -0
- package/dist/implementations/dms/nav-badges.d.ts +0 -8
- package/dist/implementations/dms/nav-badges.js +0 -16
- package/dist/implementations/dms/nav-badges.js.map +1 -1
- package/dist/implementations/dms/page.d.ts +11 -5
- package/dist/implementations/dms/page.js +56 -41
- package/dist/implementations/dms/page.js.map +1 -1
- package/dist/implementations/dms-html-render/index.js +5 -2
- package/dist/implementations/dms-html-render/index.js.map +1 -1
- package/dist/implementations/dms-notifications/index.d.ts +4 -3
- package/dist/implementations/dms-notifications/index.js +14 -6
- package/dist/implementations/dms-notifications/index.js.map +1 -1
- package/dist/pages/settings/settings-index.js +3 -2
- package/dist/pages/settings/settings-index.js.map +1 -1
- package/dist/pages/settings/users/invites.js +2 -2
- package/dist/pages/settings/users/invites.js.map +1 -1
- package/dist/pages/settings/users/member-change-notifications.js +17 -6
- package/dist/pages/settings/users/member-change-notifications.js.map +1 -1
- package/dist/pages/settings/users/member-invite-form.d.ts +3 -2
- package/dist/pages/settings/users/member-invite-form.js +7 -4
- package/dist/pages/settings/users/member-invite-form.js.map +1 -1
- package/dist/pages/settings/users/member-roles-field.js +2 -1
- package/dist/pages/settings/users/member-roles-field.js.map +1 -1
- package/dist/pages/settings/users/members.js +4 -1
- package/dist/pages/settings/users/members.js.map +1 -1
- package/dist/pages/settings/users/notifications.d.ts +6 -0
- package/dist/pages/settings/users/notifications.js +23 -15
- package/dist/pages/settings/users/notifications.js.map +1 -1
- package/dist/pages/settings/users/profile.js +1 -1
- package/dist/pages/settings/users/profile.js.map +1 -1
- package/dist/pages/settings/users/region-form.d.ts +1 -1
- package/dist/pages/settings/users/region-form.js +2 -1
- package/dist/pages/settings/users/region-form.js.map +1 -1
- package/dist/pages/settings/users/role-editor-store.d.ts +3 -1
- package/dist/pages/settings/users/role-editor-store.js +17 -4
- package/dist/pages/settings/users/role-editor-store.js.map +1 -1
- package/dist/pages/settings/users/roles.js +1 -0
- package/dist/pages/settings/users/roles.js.map +1 -1
- package/dist/realtime/registry.d.ts +1 -0
- package/dist/realtime/registry.js +5 -0
- package/dist/realtime/registry.js.map +1 -1
- package/dist/routes/realtime.d.ts +5 -1
- package/dist/routes/realtime.js +16 -9
- package/dist/routes/realtime.js.map +1 -1
- package/dist/test/integration/account-deletion.test.js +2 -2
- package/dist/test/integration/account-deletion.test.js.map +1 -1
- package/dist/test/integration/auth/auth-key-rotation.test.js +2 -1
- package/dist/test/integration/auth/auth-key-rotation.test.js.map +1 -1
- package/dist/test/integration/auth/current-password.test.js +2 -1
- package/dist/test/integration/auth/current-password.test.js.map +1 -1
- package/dist/test/integration/auth/session-revocation.test.js +2 -1
- package/dist/test/integration/auth/session-revocation.test.js.map +1 -1
- package/dist/test/integration/auth/sign-in-country.test.js +2 -1
- package/dist/test/integration/auth/sign-in-country.test.js.map +1 -1
- package/dist/test/integration/auth/two-factor-codes.test.js +2 -1
- package/dist/test/integration/auth/two-factor-codes.test.js.map +1 -1
- package/dist/test/integration/auth/two-factor-password.test.js +3 -2
- package/dist/test/integration/auth/two-factor-password.test.js.map +1 -1
- package/dist/test/integration/email-change.test.js +2 -1
- package/dist/test/integration/email-change.test.js.map +1 -1
- package/dist/test/integration/files/component-files.test.js +2 -2
- package/dist/test/integration/files/component-files.test.js.map +1 -1
- package/dist/test/integration/member-settings-access.test.js +8 -7
- package/dist/test/integration/member-settings-access.test.js.map +1 -1
- package/dist/test/integration/menu-access-events.test.d.ts +1 -0
- package/dist/test/integration/menu-access-events.test.js +148 -0
- package/dist/test/integration/menu-access-events.test.js.map +1 -0
- package/dist/test/integration/notification-inbox.test.js +4 -1
- package/dist/test/integration/notification-inbox.test.js.map +1 -1
- package/dist/test/integration/notification-locked-subjects.test.js +3 -2
- package/dist/test/integration/notification-locked-subjects.test.js.map +1 -1
- package/dist/test/integration/notification-unread-badge.test.js +51 -5
- package/dist/test/integration/notification-unread-badge.test.js.map +1 -1
- package/dist/test/integration/page-layout-route-guard.test.d.ts +1 -0
- package/dist/test/integration/page-layout-route-guard.test.js +56 -0
- package/dist/test/integration/page-layout-route-guard.test.js.map +1 -0
- package/dist/test/integration/permission-warnings.test.js +3 -2
- package/dist/test/integration/permission-warnings.test.js.map +1 -1
- package/dist/test/integration/removed-member-notifications.test.js +3 -2
- package/dist/test/integration/removed-member-notifications.test.js.map +1 -1
- package/dist/test/integration/role-delete-reassign.test.js +2 -1
- package/dist/test/integration/role-delete-reassign.test.js.map +1 -1
- package/dist/test/integration/role-permission-ancestors.test.js +2 -1
- package/dist/test/integration/role-permission-ancestors.test.js.map +1 -1
- package/dist/test/unit/frontend-bootstrap.test.js +13 -0
- package/dist/test/unit/frontend-bootstrap.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/category-slug-registry.test.js +14 -5
- package/dist/test/unit/implementations/dms/category-slug-registry.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.d.ts +1 -0
- package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.js +66 -0
- package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.js.map +1 -0
- package/dist/test/unit/implementations/dms/manifest-strip.test.js +26 -0
- package/dist/test/unit/implementations/dms/manifest-strip.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/nav-badges.test.js +23 -9
- package/dist/test/unit/implementations/dms/nav-badges.test.js.map +1 -1
- package/dist/test/unit/implementations/dms-html-render/generate-html.test.js +22 -4
- package/dist/test/unit/implementations/dms-html-render/generate-html.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms/page-layout-query.test.js +12 -0
- package/dist/test/unit/interfaces/dms/page-layout-query.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms/page-teardown.test.js +1 -1
- package/dist/test/unit/interfaces/dms/page-teardown.test.js.map +1 -1
- package/dist/test/unit/pages/settings/member-roles-field.test.js +1 -1
- package/dist/test/unit/pages/settings/member-roles-field.test.js.map +1 -1
- package/dist/test/unit/pages/settings/role-delete-confirm.test.js.map +1 -1
- package/dist/test/unit/realtime/menu-topics.test.d.ts +1 -0
- package/dist/test/unit/realtime/menu-topics.test.js +26 -0
- package/dist/test/unit/realtime/menu-topics.test.js.map +1 -0
- package/frontend-vue/deferred-plugins.ts +3 -4
- package/frontend-vue/dms.email.ts +5 -1
- package/frontend-vue/dms.frontend.ts +15 -5
- package/frontend-vue/dms.server.ts +74 -0
- package/frontend-vue/layers/dms-auth/app/composables/useLogout.ts +1 -1
- package/frontend-vue/layers/dms-auth/app/composables/usePostLoginRedirect.ts +16 -3
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/2fa.vue +3 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/backup.vue +3 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/forgot-validation.vue +1 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/oauth-complete.vue +26 -3
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/recover.vue +1 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/signup.vue +22 -25
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/validate.vue +1 -1
- package/frontend-vue/layers/dms-auth/app/middleware/auth.ts +5 -7
- package/frontend-vue/layers/dms-auth/app/plugins/permissions-preload.ts +17 -0
- package/frontend-vue/layers/dms-auth/server/accounts.ts +135 -0
- package/frontend-vue/layers/dms-auth/server/actions.ts +251 -0
- package/frontend-vue/layers/dms-auth/server/oauth.ts +260 -0
- package/frontend-vue/layers/dms-auth/server/session.ts +158 -0
- package/frontend-vue/layers/dms-core/app/build/utils/asyncDataScope.ts +47 -0
- package/frontend-vue/layers/dms-core/app/build/utils/permission-preview.ts +6 -2
- package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +148 -0
- package/frontend-vue/layers/dms-core/app/composables/auth/useAuthFetch.ts +7 -20
- package/frontend-vue/layers/dms-core/app/composables/auth/useCurrentUser.ts +2 -2
- package/frontend-vue/layers/dms-core/app/composables/auth/useMultiAccount.ts +18 -5
- package/frontend-vue/layers/dms-core/app/composables/auth/usePermissions.ts +35 -6
- package/frontend-vue/layers/dms-core/app/composables/auth/useSessionRecovery.ts +1 -1
- package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +20 -14
- package/frontend-vue/layers/dms-core/app/composables/period/periodClock.ts +121 -0
- package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +42 -0
- package/frontend-vue/layers/dms-core/app/composables/period/periodTimeZone.ts +31 -0
- package/frontend-vue/layers/dms-core/app/composables/period/presetResolvers.ts +48 -19
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +33 -4
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +78 -25
- package/frontend-vue/layers/dms-core/app/composables/realtime/usePageRealtime.ts +3 -0
- package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +36 -6
- package/frontend-vue/layers/dms-core/app/composables/user/useUserRegionalPreferences.ts +1 -1
- package/frontend-vue/layers/dms-core/app/plugins/async-data-scope.ts +26 -0
- package/frontend-vue/layers/dms-core/app/utils/pageRoutes.ts +22 -0
- package/frontend-vue/layers/dms-core/app/utils/regional.ts +36 -3
- package/frontend-vue/layers/dms-core/app/utils/trailingDeduper.ts +10 -1
- package/frontend-vue/layers/dms-core/shared/types/app-config.ts +48 -4
- package/frontend-vue/layers/dms-layout/app/app.config.ts +42 -23
- package/frontend-vue/layers/dms-layout/app/assets/css/main.css +72 -20
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardFrame.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +0 -3
- package/frontend-vue/layers/dms-layout/app/build/components/notification/NotificationPopover.vue +9 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/members/MemberRolePicker.vue +13 -12
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +17 -7
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationPreferencesForm.vue +0 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfilePersonalInfo.vue +36 -33
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfileSecuritySummary.vue +1 -2
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-types.ts +5 -4
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecuritySessions.vue +35 -28
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityStatus.vue +0 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityTwoFactor.vue +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/dev-reload/useDevReloadHolder.ts +7 -6
- package/frontend-vue/layers/dms-layout/app/build/composables/layout/sidePanelState.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/navigation/useVisitNavBadges.ts +26 -0
- package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationCatalog.ts +25 -23
- package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationPreferences.ts +44 -23
- package/frontend-vue/layers/dms-layout/app/build/composables/security/useSessionHandoff.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/preferencesSummary.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useAccountData.ts +3 -2
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useProfileAvatar.ts +1 -18
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useProfilePreferences.ts +19 -23
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityFormat.ts +6 -3
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityOverview.ts +13 -18
- package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +8 -6
- package/frontend-vue/layers/dms-layout/app/build/utils/publicErrorMessage.ts +19 -0
- package/frontend-vue/layers/dms-layout/app/components/navigation/CollapsedMenuItem.vue +0 -11
- package/frontend-vue/layers/dms-layout/app/components/navigation/NavigationMenu.vue +3 -13
- package/frontend-vue/layers/dms-layout/app/composables/general/useSystemState.ts +7 -11
- package/frontend-vue/layers/dms-layout/app/composables/notification/useNotifications.ts +103 -22
- package/frontend-vue/layers/dms-layout/app/composables/page/useModulesListing.ts +25 -102
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +0 -10
- package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +177 -96
- package/frontend-vue/layers/dms-layout/app/composables/profile/useUserLanguage.ts +3 -2
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +6 -28
- package/frontend-vue/layers/dms-layout/app/emails/components/EmailLayout.vue +31 -11
- package/frontend-vue/layers/dms-layout/app/error.vue +5 -2
- package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/middleware/page-leave-guard.global.ts +2 -2
- package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +12 -9
- package/frontend-vue/layers/dms-layout/app/plugins/dms-menu-sync.client.ts +31 -3
- package/frontend-vue/layers/dms-layout/app/plugins/favicon.ts +58 -0
- package/frontend-vue/layers/dms-layout/app/plugins/notification-stream.client.ts +15 -7
- package/frontend-vue/layers/dms-layout/app/plugins/profile-sync.client.ts +5 -1
- package/frontend-vue/layers/dms-layout/app/plugins/regional-preferences.ts +10 -4
- package/frontend-vue/layers/dms-layout/app/plugins/time-zone-cookie.client.ts +12 -0
- package/frontend-vue/layers/dms-layout/app/types/page.ts +13 -0
- package/frontend-vue/layers/dms-layout/server/html-render.ts +58 -0
- package/frontend-vue/layers/dms-layout/server/render-token.ts +95 -0
- package/frontend-vue/layers/dms-onboarding/app/composables/onboarding/useOnboarding.ts +25 -21
- package/frontend-vue/layers/dms-onboarding/app/middleware/onboarding.global.ts +1 -1
- package/frontend-vue/layers/dms-onboarding/app/plugins/onboarding-preload.ts +9 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +4 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/files/useFileMetadata.ts +162 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formRecordLoad.ts +23 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageDataRefresh.ts +101 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/navigation/useNavBadges.ts +33 -6
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +19 -16
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodQuery.ts +40 -20
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +6 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableListData.ts +42 -14
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +19 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +5 -2
- package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +9 -9
- package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +20 -18
- package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/File.vue +26 -132
- package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +40 -105
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +2 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +4 -4
- package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +9 -13
- package/frontend-vue/layers/dms-ui/app/components/table-view/CascaderPath.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/FilePreview.vue +12 -24
- package/frontend-vue/layers/dms-ui/app/components/table-view/ImagePreview.vue +17 -29
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +7 -3
- package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +20 -8
- package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +8 -11
- package/frontend-vue/layers/dms-ui/app/composables/chart/useChartFetch.ts +146 -182
- package/frontend-vue/layers/dms-ui/app/composables/live/usePageLiveRefresh.ts +8 -2
- package/frontend-vue/layers/dms-ui/app/composables/useFileReadUrls.ts +11 -99
- package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +32 -3
- package/frontend-vue/package.json +2 -2
- package/frontend-vue/pnpm-lock.yaml +5 -5
- package/frontend-vue/server/private-options.ts +27 -0
- package/frontend-vue/tests/async-data-scope.test.ts +121 -0
- package/frontend-vue/tests/auth-middleware-error.test.ts +3 -3
- package/frontend-vue/tests/banner-block-fetch.test.ts +3 -0
- package/frontend-vue/tests/block-fetch-options-forwarding.test.ts +3 -0
- package/frontend-vue/tests/block-items-period-realtime.test.ts +3 -0
- package/frontend-vue/tests/block-not-found.test.ts +16 -2
- package/frontend-vue/tests/block-page-refresh.test.ts +3 -0
- package/frontend-vue/tests/block-route-params.test.ts +3 -0
- package/frontend-vue/tests/branding-defaults.test.ts +34 -0
- package/frontend-vue/tests/builtin-emails.test.ts +23 -2
- package/frontend-vue/tests/chart-live-pause.test.ts +4 -1
- package/frontend-vue/tests/chart-value-propagation.test.ts +4 -1
- package/frontend-vue/tests/composed-text-blocks.test.ts +3 -0
- package/frontend-vue/tests/email-layout.test.ts +66 -4
- package/frontend-vue/tests/error-page-message.test.ts +25 -2
- package/frontend-vue/tests/favicon.test.ts +131 -0
- package/frontend-vue/tests/file-fields-ssr.test.ts +145 -0
- package/frontend-vue/tests/file-read-urls.test.ts +145 -34
- package/frontend-vue/tests/file-unavailable.test.ts +5 -1
- package/frontend-vue/tests/fixtures/dataEngine.ts +44 -0
- package/frontend-vue/tests/form-load-error.test.ts +55 -1
- package/frontend-vue/tests/helpers/api-data-runtime.ts +77 -0
- package/frontend-vue/tests/image-hydration-ids.test.ts +21 -16
- package/frontend-vue/tests/image-reference-preservation.test.ts +59 -53
- package/frontend-vue/tests/input-number-adjust.test.ts +14 -0
- package/frontend-vue/tests/key-value-list-copy.test.ts +3 -0
- package/frontend-vue/tests/member-role-picker.test.ts +4 -2
- package/frontend-vue/tests/menu-sync-reconnect.test.ts +47 -0
- package/frontend-vue/tests/metadata-retry.test.ts +5 -1
- package/frontend-vue/tests/module-routing.test.ts +2 -5
- package/frontend-vue/tests/modules-catalog.test.ts +17 -4
- package/frontend-vue/tests/modules-listing-transfer.test.ts +95 -0
- package/frontend-vue/tests/multi-account-validate.test.ts +23 -1
- package/frontend-vue/tests/nav-badge-tones.test.ts +66 -3
- package/frontend-vue/tests/notification-counts-from-writes.test.ts +223 -0
- package/frontend-vue/tests/notification-counts-transfer.test.ts +88 -0
- package/frontend-vue/tests/notification-inbox-display.test.ts +57 -5
- package/frontend-vue/tests/notification-popover-errors.test.ts +21 -1
- package/frontend-vue/tests/oauth-complete-handoff.test.ts +209 -0
- package/frontend-vue/tests/onboarding-middleware.test.ts +1 -1
- package/frontend-vue/tests/onboarding-transfer.test.ts +106 -0
- package/frontend-vue/tests/page-data-refresh.test.ts +141 -0
- package/frontend-vue/tests/page-fill-height.test.ts +16 -3
- package/frontend-vue/tests/page-no-visible-blocks.test.ts +12 -2
- package/frontend-vue/tests/page-realtime-remount.test.ts +1 -1
- package/frontend-vue/tests/page-record-label.test.ts +0 -7
- package/frontend-vue/tests/page-validation-redirect.test.ts +12 -2
- package/frontend-vue/tests/period-hydration-now.test.ts +185 -0
- package/frontend-vue/tests/period-live-pause.test.ts +3 -0
- package/frontend-vue/tests/period-reader-zone.test.ts +137 -0
- package/frontend-vue/tests/period-scope-first-fetch.test.ts +17 -1
- package/frontend-vue/tests/period-ssr.test.ts +195 -0
- package/frontend-vue/tests/permission-preview-partial.test.ts +0 -2
- package/frontend-vue/tests/permissions-transfer.test.ts +194 -0
- package/frontend-vue/tests/post-login-redirect.test.ts +10 -5
- package/frontend-vue/tests/profile-personal-info-ssr.test.ts +150 -0
- package/frontend-vue/tests/profile-preferences-summary.test.ts +15 -11
- package/frontend-vue/tests/realtime-reconnect.test.ts +22 -3
- package/frontend-vue/tests/roles-name-errors.test.ts +1 -1
- package/frontend-vue/tests/row-side-panel.test.ts +1 -1
- package/frontend-vue/tests/security-sessions-load-error.test.ts +3 -1
- package/frontend-vue/tests/server-routes-vs-pages.test.ts +26 -0
- package/frontend-vue/tests/server-routes.test.ts +582 -0
- package/frontend-vue/tests/session-handoff.test.ts +1 -1
- package/frontend-vue/tests/settings-reads-ssr.test.ts +232 -0
- package/frontend-vue/tests/side-panels.test.ts +8 -1
- package/frontend-vue/tests/sign-in-exit-history.test.ts +219 -0
- package/frontend-vue/tests/signup-invitation-link.test.ts +56 -8
- package/frontend-vue/tests/site-layout-categories.test.ts +42 -0
- package/frontend-vue/tests/site-layout-hydration.test.ts +3 -3
- package/frontend-vue/tests/site-layout-rendered.test.ts +103 -0
- package/frontend-vue/tests/site-layout-resync.test.ts +87 -0
- package/frontend-vue/tests/ssr-guard-preload.test.ts +188 -0
- package/frontend-vue/tests/table-cell-tones.test.ts +36 -1
- package/frontend-vue/tests/table-data-ssr.test.ts +195 -0
- package/frontend-vue/tests/table-list-data-requests.test.ts +154 -0
- package/frontend-vue/tests/table-retry-loading.test.ts +37 -0
- package/frontend-vue/tests/table-source-navigation.test.ts +0 -1
- package/frontend-vue/tests/table-source-period-scope.test.ts +0 -1
- package/frontend-vue/tests/table-view-views.test.ts +1 -1
- package/frontend-vue/tests/top-list-card-actions.test.ts +3 -0
- package/frontend-vue/tests/top-list-card-period-badge.test.ts +79 -0
- package/frontend-vue/tests/top-list-card-value-suffix.test.ts +3 -0
- package/frontend-vue/tests/trailing-deduper.test.ts +22 -0
- package/frontend-vue/tests/use-api-data-update.test.ts +119 -0
- package/frontend-vue/tests/use-api-data.test.ts +410 -0
- package/frontend-vue/tests/user-language.test.ts +8 -5
- package/frontend-vue/tests/widget-label-i18n.test.ts +4 -1
- package/frontend-vue/vitest.config.ts +6 -0
- package/package.json +2 -2
- package/skills/dms/SKILL.md +2 -1
- package/skills/dms-module/SKILL.md +1 -1
- package/skills/dms-pages/SKILL.md +4 -1
- package/frontend-vue/layers/dms-core/app/build/composables/page/usePageRecordLabel.ts +0 -9
- package/frontend-vue/layers/dms-layout/app/composables/page/usePrefetch.ts +0 -21
- package/frontend-vue/layers/dms-ui/app/build/composables/form/useControlError.ts +0 -10
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useServerRenderedAsyncData.ts +0 -23
- package/frontend-vue/tests/control-error-path.test.ts +0 -11
- package/frontend-vue/tests/modules-listing-per-request.test.ts +0 -104
- package/frontend-vue/tests/page-layout-background-refresh.test.ts +0 -321
|
@@ -125,11 +125,23 @@
|
|
|
125
125
|
|
|
126
126
|
/* Accent for text, icons and lines. The light theme needs the deep dms-700
|
|
127
127
|
to stay readable on white; solid fills keep the bright dms-400 through
|
|
128
|
-
--dms-accent-fill instead, with dark ink on top, in both themes.
|
|
128
|
+
--dms-accent-fill instead, with dark ink on top, in both themes. The
|
|
129
|
+
whole accent family derives from the primary palette, so pointing
|
|
130
|
+
`ui.colors.primary` elsewhere leaves no cyan behind. The fill hover and
|
|
131
|
+
its ink sit between shades: on the dms palette they come within a few
|
|
132
|
+
units of the v2 design's #5fe0ec and #04181b. */
|
|
129
133
|
--ui-primary: var(--ui-color-primary-700);
|
|
130
134
|
--dms-accent-fill: var(--ui-color-primary-400);
|
|
131
|
-
--dms-accent-fill-hover:
|
|
132
|
-
|
|
135
|
+
--dms-accent-fill-hover: color-mix(
|
|
136
|
+
in srgb,
|
|
137
|
+
var(--ui-color-primary-400) 75%,
|
|
138
|
+
var(--ui-color-primary-200)
|
|
139
|
+
);
|
|
140
|
+
--dms-accent-on-fill: color-mix(
|
|
141
|
+
in srgb,
|
|
142
|
+
var(--ui-color-primary-950) 50%,
|
|
143
|
+
black
|
|
144
|
+
);
|
|
133
145
|
|
|
134
146
|
/* v2 status inks: the light theme uses the 700 shades so success, warning
|
|
135
147
|
and error text stays readable on white; tints and lines stay bright. */
|
|
@@ -191,11 +203,13 @@
|
|
|
191
203
|
0 0 0 1px color-mix(in srgb, var(--dms-accent) 30%, transparent),
|
|
192
204
|
0 0 24px -4px color-mix(in srgb, var(--dms-accent) 45%, transparent);
|
|
193
205
|
--dms-focus-ring:
|
|
194
|
-
0 0 0 2px var(--ui-bg-muted),
|
|
206
|
+
0 0 0 2px var(--ui-bg-muted),
|
|
207
|
+
0 0 0 4px color-mix(in srgb, var(--ui-color-primary-600) 55%, transparent);
|
|
195
208
|
/* Command palette: the deepest shadow, plus an accent halo ring. */
|
|
196
209
|
--dms-shadow-cmdk:
|
|
197
210
|
0 1px 2px rgb(11 17 20 / 0.06), 0 30px 80px rgb(11 17 20 / 0.18);
|
|
198
|
-
--dms-halo-accent: 0 0 0 6px
|
|
211
|
+
--dms-halo-accent: 0 0 0 6px
|
|
212
|
+
color-mix(in srgb, var(--ui-color-primary-600) 12%, transparent);
|
|
199
213
|
/* Assistant mode swaps the accent halo for a violet (secondary) one,
|
|
200
214
|
and tints its surfaces and lines with the same violet. Plain rgb():
|
|
201
215
|
Chromium fringes the anti-aliased edge of an oklab colour-mix border
|
|
@@ -213,16 +227,30 @@
|
|
|
213
227
|
0 1px 2px rgb(11 17 20 / 0.06), 0 24px 64px rgb(11 17 20 / 0.16);
|
|
214
228
|
--dms-overlay: rgb(11 17 20 / 0.28);
|
|
215
229
|
--dms-glow-accent:
|
|
216
|
-
0 0 0 1px var(--ui-color-primary-600),
|
|
230
|
+
0 0 0 1px var(--ui-color-primary-600),
|
|
231
|
+
0 4px 16px
|
|
232
|
+
color-mix(in srgb, var(--ui-color-primary-600) 18%, transparent);
|
|
217
233
|
|
|
218
234
|
--dms-accent: var(--ui-color-primary-500);
|
|
219
235
|
|
|
220
236
|
/* Form controls (v2 --accent-tint*, --error-tint, --border-top,
|
|
221
237
|
--btn-highlight): the 3px focus halo around fields, the strongest border
|
|
222
238
|
(unchecked boxes, dropzones) and the highlight on accent fills. */
|
|
223
|
-
--dms-accent-tint:
|
|
224
|
-
|
|
225
|
-
|
|
239
|
+
--dms-accent-tint: color-mix(
|
|
240
|
+
in srgb,
|
|
241
|
+
var(--ui-color-primary-600) 9%,
|
|
242
|
+
transparent
|
|
243
|
+
);
|
|
244
|
+
--dms-accent-tint-strong: color-mix(
|
|
245
|
+
in srgb,
|
|
246
|
+
var(--ui-color-primary-600) 15%,
|
|
247
|
+
transparent
|
|
248
|
+
);
|
|
249
|
+
--dms-accent-line: color-mix(
|
|
250
|
+
in srgb,
|
|
251
|
+
var(--ui-color-primary-600) 35%,
|
|
252
|
+
transparent
|
|
253
|
+
);
|
|
226
254
|
--dms-error-tint: rgb(220 38 38 / 0.08);
|
|
227
255
|
--dms-border-top: #c9d1d7;
|
|
228
256
|
--dms-field-border-hover: color-mix(
|
|
@@ -231,7 +259,8 @@
|
|
|
231
259
|
var(--ui-text-muted)
|
|
232
260
|
);
|
|
233
261
|
--dms-fill-highlight:
|
|
234
|
-
inset 0 1px 0 rgb(255 255 255 / 0.45),
|
|
262
|
+
inset 0 1px 0 rgb(255 255 255 / 0.45),
|
|
263
|
+
0 1px 2px color-mix(in srgb, var(--ui-color-primary-950) 18%, transparent);
|
|
235
264
|
|
|
236
265
|
/* Charts and dashboards (v2 --chart-*, --sparkline, --skeleton): step 1
|
|
237
266
|
is the main series, step 2 (violet) always the comparison. Lines use
|
|
@@ -285,12 +314,12 @@
|
|
|
285
314
|
--dms-bg-muted: #0d1114;
|
|
286
315
|
--dms-canvas-glow: radial-gradient(
|
|
287
316
|
900px 600px at 85% -10%,
|
|
288
|
-
|
|
317
|
+
color-mix(in srgb, var(--ui-color-primary-400) 5.5%, transparent),
|
|
289
318
|
transparent 70%
|
|
290
319
|
);
|
|
291
320
|
--dms-stage-glow: radial-gradient(
|
|
292
321
|
closest-side,
|
|
293
|
-
|
|
322
|
+
color-mix(in srgb, var(--ui-color-primary-400) 10%, transparent),
|
|
294
323
|
transparent
|
|
295
324
|
);
|
|
296
325
|
|
|
@@ -316,10 +345,12 @@
|
|
|
316
345
|
0 0 0 1px color-mix(in srgb, var(--dms-accent) 35%, transparent),
|
|
317
346
|
0 0 32px -4px color-mix(in srgb, var(--dms-accent) 45%, transparent);
|
|
318
347
|
--dms-focus-ring:
|
|
319
|
-
0 0 0 2px var(--ui-bg-muted),
|
|
348
|
+
0 0 0 2px var(--ui-bg-muted),
|
|
349
|
+
0 0 0 4px color-mix(in srgb, var(--ui-color-primary-400) 60%, transparent);
|
|
320
350
|
--dms-shadow-cmdk:
|
|
321
351
|
0 50px 140px rgb(0 0 0 / 0.7), inset 0 1px 0 rgb(255 255 255 / 0.04);
|
|
322
|
-
--dms-halo-accent: 0 0 0 8px
|
|
352
|
+
--dms-halo-accent: 0 0 0 8px
|
|
353
|
+
color-mix(in srgb, var(--ui-color-primary-400) 7%, transparent);
|
|
323
354
|
--dms-assistant-halo: 0 0 0 8px rgb(167 139 250 / 0.09);
|
|
324
355
|
--dms-assistant-tint: rgb(167 139 250 / 0.12);
|
|
325
356
|
--dms-assistant-line: rgb(167 139 250 / 0.4);
|
|
@@ -329,12 +360,25 @@
|
|
|
329
360
|
0 40px 120px rgb(0 0 0 / 0.7), inset 0 1px 0 rgb(255 255 255 / 0.04);
|
|
330
361
|
--dms-overlay: rgb(3 5 6 / 0.62);
|
|
331
362
|
--dms-glow-accent:
|
|
332
|
-
0 0 0 1px var(--ui-color-primary-400),
|
|
363
|
+
0 0 0 1px var(--ui-color-primary-400),
|
|
364
|
+
0 0 40px color-mix(in srgb, var(--ui-color-primary-400) 25%, transparent);
|
|
333
365
|
--dms-accent: var(--ui-color-primary-400);
|
|
334
366
|
|
|
335
|
-
--dms-accent-tint:
|
|
336
|
-
|
|
337
|
-
|
|
367
|
+
--dms-accent-tint: color-mix(
|
|
368
|
+
in srgb,
|
|
369
|
+
var(--ui-color-primary-400) 10%,
|
|
370
|
+
transparent
|
|
371
|
+
);
|
|
372
|
+
--dms-accent-tint-strong: color-mix(
|
|
373
|
+
in srgb,
|
|
374
|
+
var(--ui-color-primary-400) 16%,
|
|
375
|
+
transparent
|
|
376
|
+
);
|
|
377
|
+
--dms-accent-line: color-mix(
|
|
378
|
+
in srgb,
|
|
379
|
+
var(--ui-color-primary-400) 35%,
|
|
380
|
+
transparent
|
|
381
|
+
);
|
|
338
382
|
--dms-error-tint: rgb(248 113 113 / 0.13);
|
|
339
383
|
--dms-border-top: #3a4550;
|
|
340
384
|
--dms-field-border-hover: color-mix(
|
|
@@ -380,7 +424,11 @@
|
|
|
380
424
|
--ui-border-accented: #8a949d;
|
|
381
425
|
--dms-border-top: #8a949d;
|
|
382
426
|
--dms-chart-grid: #dfe4e8;
|
|
383
|
-
--dms-accent-line:
|
|
427
|
+
--dms-accent-line: color-mix(
|
|
428
|
+
in srgb,
|
|
429
|
+
var(--ui-color-primary-600) 60%,
|
|
430
|
+
transparent
|
|
431
|
+
);
|
|
384
432
|
--dms-success-line: rgb(22 163 74 / 0.55);
|
|
385
433
|
--dms-warning-line: rgb(217 119 6 / 0.6);
|
|
386
434
|
--dms-error-line: rgb(220 38 38 / 0.55);
|
|
@@ -398,7 +446,11 @@
|
|
|
398
446
|
--ui-border-accented: #5f6b76;
|
|
399
447
|
--dms-border-top: #6b7782;
|
|
400
448
|
--dms-chart-grid: #262e36;
|
|
401
|
-
--dms-accent-line:
|
|
449
|
+
--dms-accent-line: color-mix(
|
|
450
|
+
in srgb,
|
|
451
|
+
var(--ui-color-primary-400) 60%,
|
|
452
|
+
transparent
|
|
453
|
+
);
|
|
402
454
|
--dms-success-line: rgb(74 222 128 / 0.6);
|
|
403
455
|
--dms-warning-line: rgb(251 191 36 / 0.6);
|
|
404
456
|
--dms-error-line: rgb(248 113 113 / 0.6);
|
|
@@ -6,6 +6,7 @@ import DashboardBanners from "./DashboardBanners.vue";
|
|
|
6
6
|
import GithubStarPrompt from "./GithubStarPrompt.vue";
|
|
7
7
|
import RolePreviewBar from "./RolePreviewBar.vue";
|
|
8
8
|
import { useAppWidgets } from "../../../composables/useAppWidgets";
|
|
9
|
+
import { useVisitNavBadges } from "../../composables/navigation/useVisitNavBadges";
|
|
9
10
|
import {
|
|
10
11
|
sidePanelWidthStyle,
|
|
11
12
|
useActiveSidePanel,
|
|
@@ -45,6 +46,14 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
45
46
|
});
|
|
46
47
|
|
|
47
48
|
providePageFillHeight(toRef(props, "fillHeight"));
|
|
49
|
+
// A custom page brings no site layout: read with the page, and taken over by
|
|
50
|
+
// the browser before the sidebar, the header and the banners render it.
|
|
51
|
+
useRenderedSiteLayout();
|
|
52
|
+
// The bell's counts, read with the page too, before the header and the page
|
|
53
|
+
// (the inbox, the profile's summary) show them.
|
|
54
|
+
useRenderedBellCounts();
|
|
55
|
+
|
|
56
|
+
useVisitNavBadges();
|
|
48
57
|
|
|
49
58
|
const { widgets: appWidgets } = useAppWidgets();
|
|
50
59
|
// A fill-height page has nothing to scroll past the dock: no clearance.
|
package/frontend-vue/layers/dms-layout/app/build/components/notification/NotificationPopover.vue
CHANGED
|
@@ -3,7 +3,10 @@ import { formatRelativeTime } from "#dms-core/app/utils/formatter";
|
|
|
3
3
|
import NotificationCard from "./NotificationCard.vue";
|
|
4
4
|
import { useNavBadges } from "#dms-ui/app/build/composables/navigation/useNavBadges";
|
|
5
5
|
import { settleWidgetRequest } from "./widgetRequest";
|
|
6
|
-
import
|
|
6
|
+
import {
|
|
7
|
+
settledRenderedBellCounts,
|
|
8
|
+
type UserNotification,
|
|
9
|
+
} from "../../../composables/notification/useNotifications";
|
|
7
10
|
import { resolveNotificationTone } from "../pages/settings/notification/notificationDisplay";
|
|
8
11
|
import { DMS_TONE_SOLID, iconWellToneOf } from "#dms-ui/app/build/utils/tone";
|
|
9
12
|
import DmsRowSkeleton from "#dms-ui/app/build/components/skeleton/RowSkeleton.vue";
|
|
@@ -20,11 +23,12 @@ const SKELETON_ROW_COUNT = 4;
|
|
|
20
23
|
const NOTIFICATIONS_SETTINGS_PAGE = "settings.user.notifications";
|
|
21
24
|
|
|
22
25
|
const { t, locale } = useI18n();
|
|
23
|
-
const { loggedIn } =
|
|
26
|
+
const { loggedIn } = useDmsUserSession();
|
|
24
27
|
const { processI18n } = useTranslation();
|
|
25
28
|
const {
|
|
26
29
|
unreadCount,
|
|
27
30
|
unreadTone,
|
|
31
|
+
areCountsLoaded,
|
|
28
32
|
notifications,
|
|
29
33
|
hasMore,
|
|
30
34
|
fetchBellCounts,
|
|
@@ -89,7 +93,9 @@ const handleFormSubmitSuccess = async (event: Event) => {
|
|
|
89
93
|
|
|
90
94
|
onMounted(async () => {
|
|
91
95
|
if (!loggedIn.value) return;
|
|
92
|
-
|
|
96
|
+
// Read with the page (`useRenderedBellCounts`) unless that read failed.
|
|
97
|
+
if (!areCountsLoaded.value) await settledRenderedBellCounts();
|
|
98
|
+
if (!areCountsLoaded.value) await refreshCounts();
|
|
93
99
|
|
|
94
100
|
window.addEventListener(
|
|
95
101
|
FormEvents.SUBMIT_SUCCESS,
|
|
@@ -67,7 +67,6 @@ const props = defineProps<Props>();
|
|
|
67
67
|
const model = defineModel<string[] | null | undefined>();
|
|
68
68
|
|
|
69
69
|
const { t } = useI18n();
|
|
70
|
-
const { $authFetch } = useAuthFetch();
|
|
71
70
|
// The wrapping UFormField's error ("pick at least one role") marks the group
|
|
72
71
|
// invalid, describes it and rings the pills in the error ink.
|
|
73
72
|
const { ariaAttrs } = useFormField();
|
|
@@ -84,17 +83,19 @@ const showsOwnerHint = computed(() =>
|
|
|
84
83
|
props.ownerField ? isOwner.value : props.disabled,
|
|
85
84
|
);
|
|
86
85
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
86
|
+
// Read with the form: a page-mode form renders the pills on the server; a
|
|
87
|
+
// drawer opened again shows the roles at once and reads them again in the
|
|
88
|
+
// background (one may have been added since).
|
|
89
|
+
const roleOptions = useApiData<RoleOptionsResponse>(() => props.rolesUrl);
|
|
90
|
+
const options = computed<RoleOptionsResponse>(
|
|
91
|
+
() => roleOptions.data.value ?? { roles: [], totalPermissions: 0 },
|
|
92
|
+
);
|
|
93
|
+
const isLoading = computed(
|
|
94
|
+
() =>
|
|
95
|
+
props.loading ||
|
|
96
|
+
roleOptions.status.value === "idle" ||
|
|
97
|
+
roleOptions.status.value === "pending",
|
|
98
|
+
);
|
|
98
99
|
|
|
99
100
|
const selectedIds = computed(() => new Set(model.value ?? []));
|
|
100
101
|
const roleItems = computed<ChipGroupItem[]>(() =>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { nextTick } from "vue";
|
|
2
3
|
import { formatRelativeTime } from "#dms-core/app/utils/formatter";
|
|
3
4
|
import TableEmpty from "#dms-ui/app/build/components/table/Empty.vue";
|
|
4
5
|
import TablePagination from "#dms-ui/app/build/components/table/Pagination.vue";
|
|
@@ -63,12 +64,21 @@ const sourceOf = (
|
|
|
63
64
|
return labelKey ? { label: t(labelKey), isModule: false } : undefined;
|
|
64
65
|
};
|
|
65
66
|
|
|
66
|
-
// A change
|
|
67
|
+
// A change made here moves the counts too (the write's answer, then its
|
|
68
|
+
// realtime echo): the watcher below leaves it to the one read that follows it.
|
|
69
|
+
let ownChanges = 0;
|
|
70
|
+
|
|
71
|
+
// A change of a row is listed again, from the server, once.
|
|
67
72
|
const runThenRefresh = async (action: () => Promise<unknown>) => {
|
|
73
|
+
ownChanges++;
|
|
68
74
|
try {
|
|
69
75
|
await action();
|
|
76
|
+
// What the action changed reaches the watcher before the guard drops.
|
|
77
|
+
await nextTick();
|
|
70
78
|
} catch {
|
|
71
79
|
toast.add({ title: t("dms.form.error_title"), color: "error" });
|
|
80
|
+
} finally {
|
|
81
|
+
ownChanges--;
|
|
72
82
|
}
|
|
73
83
|
await props.context.refresh();
|
|
74
84
|
};
|
|
@@ -88,15 +98,15 @@ const toggleRead = (notification: UserNotification) =>
|
|
|
88
98
|
);
|
|
89
99
|
|
|
90
100
|
// What the stream changes elsewhere (a notification arriving, read in
|
|
91
|
-
// another tab) is listed again here
|
|
101
|
+
// another tab) is listed again here: once per change of the count or of the
|
|
102
|
+
// newest notification, not each time the bell's lists are rebuilt.
|
|
92
103
|
watch(
|
|
93
|
-
() =>
|
|
94
|
-
() =>
|
|
104
|
+
[unreadCount, () => notifications.value[0]?._id],
|
|
105
|
+
() => {
|
|
106
|
+
if (ownChanges === 0) void props.context.refresh();
|
|
107
|
+
},
|
|
95
108
|
);
|
|
96
109
|
|
|
97
|
-
onMounted(() => {
|
|
98
|
-
if (!catalog.isLoaded.value) void catalog.loadCatalog();
|
|
99
|
-
});
|
|
100
110
|
</script>
|
|
101
111
|
|
|
102
112
|
<template>
|
|
@@ -47,9 +47,9 @@ const toast = useToast();
|
|
|
47
47
|
const { $authFetch } = useAuthFetch();
|
|
48
48
|
const { processApiMessage } = useTranslation();
|
|
49
49
|
const { user, refresh: refreshSession } = useCurrentUser();
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
);
|
|
50
|
+
const avatarOptions = () => props.avatarField?.component?.options ?? {};
|
|
51
|
+
const { upload } = useProfileAvatar(avatarOptions);
|
|
52
|
+
const avatarUrls = useFileReadUrls(avatarOptions().storage);
|
|
53
53
|
// A refused name or picture shows under its field; a failed request is a
|
|
54
54
|
// toast.
|
|
55
55
|
const fieldErrors = useFieldErrors({
|
|
@@ -57,13 +57,29 @@ const fieldErrors = useFieldErrors({
|
|
|
57
57
|
codes: { [INVALID_AVATAR]: "avatar" },
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
// Read with the page: the server renders the name, the address with its
|
|
61
|
+
// verification, and the picture. The form edits a copy of it.
|
|
62
|
+
const profile = await useApiData<ProfileResponse>(() => props.endpoint);
|
|
63
|
+
const isProfileLoaded = computed(
|
|
64
|
+
() => profile.status.value === "success" || profile.status.value === "error",
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const saved = ref<ProfileResponse>(
|
|
68
|
+
profile.data.value ?? { name: user.value?.name, email: user.value?.email },
|
|
69
|
+
);
|
|
64
70
|
const name = ref(saved.value.name ?? "");
|
|
65
|
-
const avatar = ref<AvatarValue | null>(null);
|
|
66
|
-
|
|
71
|
+
const avatar = ref<AvatarValue | null>(saved.value.avatar ?? null);
|
|
72
|
+
// A picture picked here shows from the file until the profile is saved.
|
|
73
|
+
const pickedAvatarUrl = ref<string | null>(null);
|
|
74
|
+
const avatarUrl = computed(() => {
|
|
75
|
+
if (!avatar.value) return null;
|
|
76
|
+
return pickedAvatarUrl.value ?? avatarUrls.getUrl(avatar.value.key) ?? null;
|
|
77
|
+
});
|
|
78
|
+
watch(
|
|
79
|
+
() => avatar.value?.key,
|
|
80
|
+
(key) => key && void avatarUrls.resolve(key),
|
|
81
|
+
{ immediate: true },
|
|
82
|
+
);
|
|
67
83
|
const isUploading = ref(false);
|
|
68
84
|
const isSaving = ref(false);
|
|
69
85
|
const fileInput = useTemplateRef<HTMLInputElement>("fileInput");
|
|
@@ -86,14 +102,12 @@ const canSave = computed(() => !!name.value.trim() && !isUploading.value);
|
|
|
86
102
|
|
|
87
103
|
watch(name, () => fieldErrors.clear("name"));
|
|
88
104
|
|
|
89
|
-
|
|
105
|
+
function applyProfile(answer: ProfileResponse): void {
|
|
90
106
|
fieldErrors.clear();
|
|
91
|
-
saved.value =
|
|
92
|
-
name.value =
|
|
93
|
-
avatar.value =
|
|
94
|
-
|
|
95
|
-
? await resolveUrl(profile.avatar.key)
|
|
96
|
-
: null;
|
|
107
|
+
saved.value = answer;
|
|
108
|
+
name.value = answer.name ?? "";
|
|
109
|
+
avatar.value = answer.avatar ?? null;
|
|
110
|
+
pickedAvatarUrl.value = null;
|
|
97
111
|
}
|
|
98
112
|
|
|
99
113
|
function pickFile(): void {
|
|
@@ -117,7 +131,7 @@ async function onFileChange(event: Event): Promise<void> {
|
|
|
117
131
|
return;
|
|
118
132
|
}
|
|
119
133
|
avatar.value = { key: result.key! };
|
|
120
|
-
|
|
134
|
+
pickedAvatarUrl.value = URL.createObjectURL(file);
|
|
121
135
|
} catch {
|
|
122
136
|
toast.add({
|
|
123
137
|
title: t("page.settings.profile.avatar_error"),
|
|
@@ -131,11 +145,11 @@ async function onFileChange(event: Event): Promise<void> {
|
|
|
131
145
|
function removeAvatar(): void {
|
|
132
146
|
fieldErrors.clear("avatar");
|
|
133
147
|
avatar.value = null;
|
|
134
|
-
|
|
148
|
+
pickedAvatarUrl.value = null;
|
|
135
149
|
}
|
|
136
150
|
|
|
137
|
-
|
|
138
|
-
|
|
151
|
+
function discard(): void {
|
|
152
|
+
applyProfile(saved.value);
|
|
139
153
|
}
|
|
140
154
|
|
|
141
155
|
async function save(): Promise<void> {
|
|
@@ -146,11 +160,11 @@ async function save(): Promise<void> {
|
|
|
146
160
|
if (!canSave.value) return;
|
|
147
161
|
isSaving.value = true;
|
|
148
162
|
try {
|
|
149
|
-
const
|
|
163
|
+
const answer = await $authFetch<ProfileResponse>(props.endpoint, {
|
|
150
164
|
method: "POST",
|
|
151
165
|
body: { name: name.value.trim(), avatar: avatar.value },
|
|
152
166
|
});
|
|
153
|
-
|
|
167
|
+
applyProfile(answer);
|
|
154
168
|
toast.add({ title: t("page.settings.profile.saved"), color: "success" });
|
|
155
169
|
await refreshSession();
|
|
156
170
|
} catch (error) {
|
|
@@ -162,17 +176,6 @@ async function save(): Promise<void> {
|
|
|
162
176
|
}
|
|
163
177
|
}
|
|
164
178
|
|
|
165
|
-
// The verification state comes with the profile, fetched after mount: a
|
|
166
|
-
// skeleton holds the badge's place until then.
|
|
167
|
-
const isProfileLoaded = ref(false);
|
|
168
|
-
|
|
169
|
-
onMounted(async () => {
|
|
170
|
-
try {
|
|
171
|
-
await applyProfile(await $authFetch<ProfileResponse>(props.endpoint));
|
|
172
|
-
} finally {
|
|
173
|
-
isProfileLoaded.value = true;
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
179
|
</script>
|
|
177
180
|
|
|
178
181
|
<template>
|
|
@@ -7,7 +7,7 @@ import { iconWellToneOf } from "#dms-ui/app/build/utils/tone";
|
|
|
7
7
|
import ProfileSummaryRow from "./ProfileSummaryRow.vue";
|
|
8
8
|
|
|
9
9
|
const { t, locale } = useI18n();
|
|
10
|
-
const { overview, attention, attentionTone, isUnavailable
|
|
10
|
+
const { overview, attention, attentionTone, isUnavailable } =
|
|
11
11
|
useSecurityOverview();
|
|
12
12
|
|
|
13
13
|
const needsAttention = computed(() => attention.value.length > 0);
|
|
@@ -52,7 +52,6 @@ const placeholder = computed(
|
|
|
52
52
|
)}`,
|
|
53
53
|
);
|
|
54
54
|
|
|
55
|
-
onMounted(refresh);
|
|
56
55
|
</script>
|
|
57
56
|
|
|
58
57
|
<template>
|
package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { pageRoutePath } from "#dms-core/app/utils/pageRoutes";
|
|
2
|
+
/** A permission of the roles editor tree (`GET /dms/pages/settings/workspace/roles/editor-tree`). */
|
|
2
3
|
export interface RolePermissionNode {
|
|
3
4
|
id: string;
|
|
4
5
|
label: string;
|
|
@@ -17,7 +18,7 @@ export interface RoleMemberPreview {
|
|
|
17
18
|
name: string;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
/** One role of the roles list (`GET /settings/workspace/roles/overview`). */
|
|
21
|
+
/** One role of the roles list (`GET /dms/pages/settings/workspace/roles/overview`). */
|
|
21
22
|
export interface RoleSummary {
|
|
22
23
|
_id: string;
|
|
23
24
|
name: string;
|
|
@@ -42,7 +43,7 @@ export interface RoleEditorCapabilities {
|
|
|
42
43
|
canDelete: boolean;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
/** Payload of `GET /settings/workspace/roles/overview`. */
|
|
46
|
+
/** Payload of `GET /dms/pages/settings/workspace/roles/overview`. */
|
|
46
47
|
export interface RolesOverview {
|
|
47
48
|
roles: RoleSummary[];
|
|
48
49
|
owners: RoleOwnersSummary;
|
|
@@ -78,7 +79,7 @@ export const OWNER_ENTRY_ID = "__owner__";
|
|
|
78
79
|
export const NEW_ROLE_ENTRY_ID = "__new__";
|
|
79
80
|
|
|
80
81
|
/** Base path of the roles page API. */
|
|
81
|
-
export const ROLES_API_PATH = "/settings/workspace/roles";
|
|
82
|
+
export const ROLES_API_PATH = pageRoutePath("/settings/workspace/roles");
|
|
82
83
|
|
|
83
84
|
/** The roles' table data routes, which delete a role. */
|
|
84
85
|
export const ROLES_TABLE_API_PATH = "/api/tables/roles";
|
|
@@ -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,
|