@antelopejs/dms 0.9.8 → 0.11.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 +171 -0
- package/README.md +1 -1
- package/data/module-store/catalog.json +2 -2
- package/dist/attachments/table-view.js +2 -2
- package/dist/attachments/table-view.js.map +1 -1
- package/dist/automation/actions.d.ts +2 -0
- package/dist/automation/actions.js +116 -71
- package/dist/automation/actions.js.map +1 -1
- package/dist/automation/i18n.d.ts +4 -0
- package/dist/automation/i18n.js +19 -0
- package/dist/automation/i18n.js.map +1 -0
- package/dist/automation/send-notification.d.ts +21 -0
- package/dist/automation/send-notification.js +163 -0
- package/dist/automation/send-notification.js.map +1 -0
- package/dist/automation/triggers.js +19 -25
- package/dist/automation/triggers.js.map +1 -1
- package/dist/client-origin-cors.d.ts +1 -0
- package/dist/client-origin-cors.js +19 -0
- package/dist/client-origin-cors.js.map +1 -0
- package/dist/config.d.ts +3 -2
- package/dist/config.js +5 -7
- package/dist/config.js.map +1 -1
- package/dist/frontend-module.d.ts +4 -0
- package/dist/frontend-module.js +37 -0
- package/dist/frontend-module.js.map +1 -0
- package/dist/implementations/dms/client-ip.js +1 -1
- package/dist/implementations/dms/client-ip.js.map +1 -1
- package/dist/implementations/dms/dev-reload.d.ts +5 -0
- package/dist/implementations/dms/dev-reload.js +23 -3
- package/dist/implementations/dms/dev-reload.js.map +1 -1
- package/dist/implementations/dms/frontend-modules.d.ts +2 -4
- package/dist/implementations/dms/frontend-modules.js +38 -94
- package/dist/implementations/dms/frontend-modules.js.map +1 -1
- package/dist/implementations/dms/frontend-ready.d.ts +12 -2
- package/dist/implementations/dms/frontend-ready.js +34 -4
- package/dist/implementations/dms/frontend-ready.js.map +1 -1
- package/dist/implementations/dms/layout-banners.d.ts +2 -1
- package/dist/implementations/dms/layout-banners.js +9 -0
- package/dist/implementations/dms/layout-banners.js.map +1 -1
- package/dist/implementations/dms/manifest.d.ts +7 -2
- package/dist/implementations/dms/manifest.js +12 -7
- package/dist/implementations/dms/manifest.js.map +1 -1
- package/dist/implementations/dms/page.d.ts +4 -3
- package/dist/implementations/dms/page.js +49 -18
- package/dist/implementations/dms/page.js.map +1 -1
- package/dist/implementations/dms/permissions-resolver.d.ts +2 -2
- package/dist/implementations/dms/permissions-resolver.js +2 -2
- package/dist/implementations/dms/permissions-resolver.js.map +1 -1
- package/dist/implementations/dms/realtime.js +1 -0
- package/dist/implementations/dms/realtime.js.map +1 -1
- package/dist/implementations/dms-base/bulk-selection.d.ts +1 -1
- package/dist/implementations/dms-base/bulk-selection.js +23 -1
- package/dist/implementations/dms-base/bulk-selection.js.map +1 -1
- package/dist/implementations/dms-base/filter-tokens.js +5 -3
- package/dist/implementations/dms-base/filter-tokens.js.map +1 -1
- package/dist/implementations/dms-base/search-route.d.ts +1 -0
- package/dist/implementations/dms-base/search-route.js +35 -13
- package/dist/implementations/dms-base/search-route.js.map +1 -1
- package/dist/implementations/dms-base/table-view.js +11 -6
- package/dist/implementations/dms-base/table-view.js.map +1 -1
- package/dist/implementations/dms-notifications/index.d.ts +2 -2
- package/dist/implementations/dms-notifications/index.js +5 -5
- package/dist/implementations/dms-notifications/index.js.map +1 -1
- package/dist/index.js +32 -55
- package/dist/index.js.map +1 -1
- package/dist/pages/modules/index.d.ts +1 -0
- package/dist/pages/modules/index.js +1 -0
- package/dist/pages/modules/index.js.map +1 -1
- package/dist/pages/modules/modules-store.d.ts +9 -0
- package/dist/pages/modules/modules-store.js +30 -0
- package/dist/pages/modules/modules-store.js.map +1 -0
- package/dist/pages/settings/users/account-credentials.js +2 -2
- package/dist/pages/settings/users/account-credentials.js.map +1 -1
- package/dist/pages/settings/users/account-data-store.js +25 -8
- package/dist/pages/settings/users/account-data-store.js.map +1 -1
- package/dist/pages/settings/users/invite-extension-routes.js +9 -3
- package/dist/pages/settings/users/invite-extension-routes.js.map +1 -1
- package/dist/pages/settings/users/member-management.js +13 -14
- package/dist/pages/settings/users/member-management.js.map +1 -1
- package/dist/pages/settings/users/profile.d.ts +1 -4
- package/dist/pages/settings/users/profile.js +0 -35
- package/dist/pages/settings/users/profile.js.map +1 -1
- package/dist/pages/settings/users/role-editor-store.js +26 -18
- package/dist/pages/settings/users/role-editor-store.js.map +1 -1
- package/dist/pages/settings/users/role-editor.d.ts +0 -1
- package/dist/pages/settings/users/role-editor.js +1 -2
- package/dist/pages/settings/users/role-editor.js.map +1 -1
- package/dist/pages/settings/users/role-preview.js +1 -2
- package/dist/pages/settings/users/role-preview.js.map +1 -1
- package/dist/pages/settings/users/roles.js +2 -2
- package/dist/pages/settings/users/roles.js.map +1 -1
- package/dist/pages/settings/users/two-factor-operations.js.map +1 -1
- package/dist/realtime/broker.d.ts +6 -0
- package/dist/realtime/broker.js +8 -1
- package/dist/realtime/broker.js.map +1 -1
- package/dist/realtime/presence.d.ts +8 -3
- package/dist/realtime/presence.js +8 -4
- package/dist/realtime/presence.js.map +1 -1
- package/dist/realtime/sessions.d.ts +11 -1
- package/dist/realtime/sessions.js +21 -3
- package/dist/realtime/sessions.js.map +1 -1
- package/dist/realtime/table-view.d.ts +2 -2
- package/dist/realtime/table-view.js +4 -3
- package/dist/realtime/table-view.js.map +1 -1
- package/dist/routes/auth/client-ip.d.ts +1 -0
- package/dist/routes/auth/client-ip.js +18 -0
- package/dist/routes/auth/client-ip.js.map +1 -1
- package/dist/routes/auth/oauth/config.d.ts +2 -1
- package/dist/routes/auth/oauth/config.js +5 -1
- package/dist/routes/auth/oauth/config.js.map +1 -1
- package/dist/routes/auth/session-handoff.d.ts +0 -1
- package/dist/routes/auth/session-handoff.js +0 -2
- package/dist/routes/auth/session-handoff.js.map +1 -1
- package/dist/routes/auth.js +3 -2
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/index.d.ts +0 -1
- package/dist/routes/index.js +0 -1
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/realtime.d.ts +13 -1
- package/dist/routes/realtime.js +114 -29
- package/dist/routes/realtime.js.map +1 -1
- package/dist/startup-config.d.ts +2 -0
- package/dist/startup-config.js +62 -0
- package/dist/startup-config.js.map +1 -0
- package/dist/store/interfaces.d.ts +3 -2
- package/dist/store/interfaces.js +16 -6
- package/dist/store/interfaces.js.map +1 -1
- package/dist/store/listing.js +25 -15
- package/dist/store/listing.js.map +1 -1
- package/dist/store/operations.js +5 -2
- package/dist/store/operations.js.map +1 -1
- package/dist/store/requirements.js +3 -2
- package/dist/store/requirements.js.map +1 -1
- package/dist/store/runtime.d.ts +10 -1
- package/dist/store/runtime.js +33 -6
- package/dist/store/runtime.js.map +1 -1
- package/dist/store/types.d.ts +10 -0
- package/dist/utils/ensure-default-tenant.d.ts +2 -1
- package/dist/utils/ensure-default-tenant.js +13 -1
- package/dist/utils/ensure-default-tenant.js.map +1 -1
- package/dist/utils/export-jobs.js +2 -1
- package/dist/utils/export-jobs.js.map +1 -1
- package/dist/validation/auth.schema.d.ts +2 -2
- package/dist/validation/member-invite.schema.d.ts +2 -10
- package/dist/validation/member-invite.schema.js +2 -15
- package/dist/validation/member-invite.schema.js.map +1 -1
- package/frontend-vue/dms.frontend.ts +24 -5
- package/frontend-vue/dms.server.ts +17 -0
- package/frontend-vue/layers/dms-auth/server/accounts.ts +3 -4
- package/frontend-vue/layers/dms-core/app/build/composables/renderNow.ts +33 -0
- package/frontend-vue/layers/dms-core/app/build/composables/user/tablePreferences.ts +5 -1
- package/frontend-vue/layers/dms-core/app/build/utils/crossTab.ts +199 -0
- package/frontend-vue/layers/dms-core/app/build/utils/sharedRealtimeConnection.ts +410 -0
- package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +77 -12
- package/frontend-vue/layers/dms-core/app/composables/data-types/useDataType.ts +26 -0
- package/frontend-vue/layers/dms-core/app/composables/live/livePauseSource.ts +29 -0
- package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +150 -9
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +6 -4
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +27 -1
- package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +256 -119
- package/frontend-vue/layers/dms-core/app/composables/translation/useComposedText.ts +21 -8
- package/frontend-vue/layers/dms-core/app/composables/translation/useTranslation.ts +25 -5
- package/frontend-vue/layers/dms-core/app/composables/useApiError.ts +3 -5
- package/frontend-vue/layers/dms-core/app/composables/useFieldErrors.ts +61 -26
- package/frontend-vue/layers/dms-core/app/plugins/dev-error-log.ts +16 -0
- package/frontend-vue/layers/dms-core/app/types/user.ts +6 -0
- package/frontend-vue/layers/dms-core/app/utils/composedText.ts +7 -2
- package/frontend-vue/layers/dms-core/app/utils/formatter.ts +134 -35
- package/frontend-vue/layers/dms-core/app/utils/url-interpolation.ts +18 -1
- package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +136 -2
- package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +136 -2
- package/frontend-vue/layers/dms-core/shared/types/app-config.ts +0 -2
- package/frontend-vue/layers/dms-layout/app/app.config.ts +0 -1
- package/frontend-vue/layers/dms-layout/app/assets/css/main.css +11 -1
- package/frontend-vue/layers/dms-layout/app/assets/fonts/Geist-Italic-Variable.woff2 +0 -0
- package/frontend-vue/layers/dms-layout/app/assets/fonts/Geist-Variable.woff2 +0 -0
- package/frontend-vue/layers/dms-layout/app/assets/fonts/GeistMono-Italic-Variable.woff2 +0 -0
- package/frontend-vue/layers/dms-layout/app/assets/fonts/GeistMono-Variable.woff2 +0 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardBanners.vue +3 -2
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +41 -21
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardPageBody.vue +6 -2
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +4 -19
- package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +11 -107
- package/frontend-vue/layers/dms-layout/app/build/components/layout/GithubStarPrompt.vue +18 -7
- package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionsOutlet.vue +4 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/RowSidePanel.vue +43 -82
- package/frontend-vue/layers/dms-layout/app/build/components/layout/SidebarUserMenu.vue +44 -3
- package/frontend-vue/layers/dms-layout/app/build/components/layout/UserIdentity.vue +11 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleCatalogTile.vue +132 -35
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleManageMenu.vue +59 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleSourceLabel.vue +25 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreBanners.vue +73 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreOperation.vue +116 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +8 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +85 -207
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +10 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTileSkeleton.vue +68 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +24 -8
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxItem.vue +8 -5
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/region/RegionTimeZoneInput.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-permissions.ts +19 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/useRoleEditor.ts +2 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/layout/useSidePanelWidth.ts +31 -70
- package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationGroupsOpen.ts +11 -1
- package/frontend-vue/layers/dms-layout/app/build/utils/dev-reload-stream.ts +81 -0
- package/frontend-vue/layers/dms-layout/app/build/utils/github-repository.ts +2 -2
- package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +232 -36
- package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +122 -14
- package/frontend-vue/layers/dms-layout/app/composables/layout/useLayoutBanners.ts +32 -3
- package/frontend-vue/layers/dms-layout/app/composables/layout/useServedHeaderActions.ts +49 -11
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleHistory.ts +3 -2
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +59 -4
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStoreActions.ts +194 -0
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +23 -3
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageLayout.ts +4 -2
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageRecordDeletion.ts +162 -0
- package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +6 -1
- package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +6 -2
- package/frontend-vue/layers/dms-layout/app/custom-layouts/SettingsLayout.vue +6 -2
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules-store.vue +291 -0
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +87 -474
- package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +3 -0
- package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +13 -22
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-navigation.client.ts +11 -10
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-personal-pages.client.ts +22 -52
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-quick-actions.client.ts +7 -7
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-session.client.ts +11 -10
- package/frontend-vue/layers/dms-layout/app/plugins/dms-dev-reload.client.ts +14 -9
- package/frontend-vue/layers/dms-layout/app/theme/actions.ts +5 -4
- package/frontend-vue/layers/dms-layout/app/theme/form-controls.ts +9 -4
- package/frontend-vue/layers/dms-layout/app/theme/surfaces.ts +22 -6
- package/frontend-vue/layers/dms-layout/app/types/module-store.ts +9 -0
- package/frontend-vue/layers/dms-layout/app/types/page.ts +15 -1
- package/frontend-vue/layers/dms-layout/app/utils/account-menu.ts +11 -0
- package/frontend-vue/layers/dms-layout/app/utils/breadcrumb.ts +57 -0
- package/frontend-vue/layers/dms-layout/app/utils/pageQuery.ts +26 -0
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +45 -18
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +44 -17
- package/frontend-vue/layers/dms-ui/app/build/components/containers/TargetComponentTree.vue +31 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/constants.ts +25 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +244 -15
- package/frontend-vue/layers/dms-ui/app/build/components/containers/modal/DynamicModal.vue +42 -25
- package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowHeader.vue +153 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowNotFound.vue +44 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/row/drawerRowHeader.ts +35 -0
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormEntries.vue +44 -9
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormErrorSummary.vue +11 -4
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldControl.vue +38 -5
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldNotes.vue +29 -5
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormLoadError.vue +5 -1
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormSectionNav.vue +3 -1
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormSections.vue +106 -27
- package/frontend-vue/layers/dms-ui/app/build/components/table/Actions.vue +12 -4
- package/frontend-vue/layers/dms-ui/app/build/components/table/Empty.vue +4 -2
- package/frontend-vue/layers/dms-ui/app/build/components/table/FiltersRow.vue +48 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/Pagination.vue +55 -54
- package/frontend-vue/layers/dms-ui/app/build/components/table/RowSelection.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +396 -288
- package/frontend-vue/layers/dms-ui/app/build/components/table/Tabs.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/Views.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardAvatar.vue +59 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardGridSkeleton.vue +19 -2
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardRowActions.vue +38 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardsDisplay.vue +148 -104
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/LiveHoldBanner.vue +17 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/bulkSelection.ts +43 -9
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowComponentOptions.ts +76 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowNavigation.ts +172 -27
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +270 -46
- package/frontend-vue/layers/dms-ui/app/build/composables/confirm/actionError.ts +6 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/confirm/confirmDialogTexts.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/closingLayers.ts +186 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/context.ts +14 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/drawerSize.ts +229 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/useResizableSize.ts +175 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/metricCells.ts +18 -35
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/numberSuffix.ts +44 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +123 -27
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerFieldTypes.ts +2 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationFilter.ts +75 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationRows.ts +92 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationValue.ts +39 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/twoLineCell.ts +3 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/useColumnValueRenderer.ts +6 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/form/createFormFields.ts +15 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldSaved.ts +40 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldValueRule.ts +62 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formEntryContext.ts +5 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +42 -7
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formLayout.ts +106 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formRecordLoad.ts +11 -5
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formSections.ts +32 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formValidation.ts +36 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/form/readonlyValue.ts +19 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/registerFormFunctions.ts +27 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/repeaterTable.ts +78 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/serverFieldErrors.ts +49 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/toasterClearance.ts +118 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/useInstantForm.ts +12 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/live/currentPageLive.ts +41 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/live/livePoller.ts +10 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/live/pageReadClock.ts +28 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/live/shortcuts.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageLiveHost.ts +17 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/live/useUpdatedLabel.ts +38 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/pageToolbarContext.ts +75 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/toolbarControlHeight.ts +29 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodComparison.ts +52 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +21 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodShortLabels.ts +28 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/usePeriodDraft.ts +1 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useRelationFilterLabels.ts +97 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useRowScrollAnchor.ts +106 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useTable.ts +9 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useTableColumns.ts +1 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/archivedCount.ts +31 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/filterEditorValue.ts +38 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/formatFilterValue.ts +33 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/scrollEdges.ts +31 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useCardFields.ts +33 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useMobileDisplay.ts +48 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useScrolledPastTop.ts +41 -18
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +3 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLinkedVisit.ts +78 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLiveHold.ts +135 -22
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +56 -25
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +90 -35
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +86 -10
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/bulkActions.ts +14 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/card.ts +127 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/compareValues.ts +13 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/expandedRows.ts +28 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/groupedRows.ts +114 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/linkedArrival.ts +308 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/locateRow.ts +106 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pageArrival.ts +34 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pinnedRows.ts +17 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/quickFilters.ts +41 -33
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/recordLink.ts +24 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableCounts.ts +113 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableQuery.ts +27 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/views.ts +22 -6
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/writeError.ts +21 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/addressSuggestions.ts +49 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/codeHighlight.ts +67 -29
- package/frontend-vue/layers/dms-ui/app/build/utils/eyebrow.ts +12 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/fieldOptionsI18n.ts +51 -11
- package/frontend-vue/layers/dms-ui/app/build/utils/htmlGrammar.ts +79 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/inputWidth.ts +53 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/numberAdjustment.ts +6 -3
- package/frontend-vue/layers/dms-ui/app/build/utils/relativeDate.ts +19 -5
- package/frontend-vue/layers/dms-ui/app/build/utils/scrollWithin.ts +99 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/shellParser.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/build/utils/streamHighlight.ts +90 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +36 -14
- package/frontend-vue/layers/dms-ui/app/components/PasswordStrength.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/RecursiveComponent.vue +9 -1
- package/frontend-vue/layers/dms-ui/app/components/blocks/LiveToggleBlock.vue +80 -32
- package/frontend-vue/layers/dms-ui/app/components/blocks/PageToolbarBlock.vue +137 -0
- package/frontend-vue/layers/dms-ui/app/components/blocks/StatGroupBlock.vue +5 -0
- package/frontend-vue/layers/dms-ui/app/components/chart/ApexChartHost.vue +16 -2
- package/frontend-vue/layers/dms-ui/app/components/chart/Chart.vue +24 -5
- package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +41 -23
- package/frontend-vue/layers/dms-ui/app/components/code-snippet/CodeSnippet.vue +23 -12
- package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/container-footer/ContainerFooter.vue +34 -0
- package/frontend-vue/layers/dms-ui/app/components/field-row/FieldRow.vue +25 -2
- package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +2 -2
- package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +119 -36
- package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +53 -22
- package/frontend-vue/layers/dms-ui/app/components/form/components/DatePicker.vue +20 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Display.vue +13 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +8 -3
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +53 -5
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputCode.vue +29 -5
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputNumber.vue +45 -8
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputPercentage.vue +28 -5
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputSecret.vue +5 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputText.vue +19 -2
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputTime.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +226 -65
- package/frontend-vue/layers/dms-ui/app/components/form/components/Repeater.vue +80 -30
- package/frontend-vue/layers/dms-ui/app/components/form/components/RepeaterTable.vue +210 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/Select.vue +4 -2
- package/frontend-vue/layers/dms-ui/app/components/grid/GridRow.vue +11 -2
- package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +79 -70
- package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +117 -21
- package/frontend-vue/layers/dms-ui/app/components/meter/Meter.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +113 -34
- package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +14 -2
- package/frontend-vue/layers/dms-ui/app/components/section-header/Eyebrow.vue +6 -1
- package/frontend-vue/layers/dms-ui/app/components/segmented/Segmented.vue +13 -0
- package/frontend-vue/layers/dms-ui/app/components/stat-group/StatGroup.vue +52 -3
- package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/CascaderPath.vue +74 -25
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +48 -44
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanDisplay.vue +4 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +426 -97
- package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +33 -5
- package/frontend-vue/layers/dms-ui/app/composables/chart/apexCategoryLabels.ts +68 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/apexDatetimeBuckets.ts +306 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/formatValue.ts +4 -3
- package/frontend-vue/layers/dms-ui/app/composables/chart/heatmapColors.ts +93 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/localizeSeries.ts +62 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.ts +116 -8
- package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.types.ts +7 -0
- package/frontend-vue/layers/dms-ui/app/composables/containers/types.ts +84 -1
- package/frontend-vue/layers/dms-ui/app/composables/containers/useDrawer.ts +69 -8
- package/frontend-vue/layers/dms-ui/app/composables/form/types/field-saved.ts +11 -0
- package/frontend-vue/layers/dms-ui/app/composables/form/types/field.ts +16 -0
- package/frontend-vue/layers/dms-ui/app/composables/form/types/functions.ts +1 -0
- package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +189 -28
- package/frontend-vue/layers/dms-ui/app/composables/live/usePageLive.ts +29 -12
- package/frontend-vue/layers/dms-ui/app/composables/page-toolbar/usePageToolbarSize.ts +30 -0
- package/frontend-vue/layers/dms-ui/app/composables/table-view/kanban.ts +4 -2
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +22 -3
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +4 -0
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/display.ts +24 -1
- package/frontend-vue/layers/dms-ui/app/plugins/closing-layers.client.ts +10 -0
- package/frontend-vue/layers/dms-ui/app/plugins/live-pause-source.ts +15 -0
- package/frontend-vue/layers/dms-ui/app/plugins/register.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/types/input-number.ts +3 -2
- package/frontend-vue/layers/dms-ui/app/types/page-toolbar.ts +7 -0
- package/frontend-vue/layers/dms-ui/app/types/row-action.ts +0 -2
- package/frontend-vue/layers/dms-ui/app/types/row-navigation.ts +17 -0
- package/frontend-vue/layers/dms-ui/app/types/table/query.ts +2 -1
- package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +3 -1
- package/frontend-vue/layers/dms-ui/app/utils/cascader.ts +94 -3
- package/frontend-vue/layers/dms-ui/app/utils/componentTree.ts +24 -0
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +20 -5
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +20 -5
- package/frontend-vue/package.json +6 -2
- package/package.json +11 -10
- package/skills/dms/SKILL.md +1 -1
- package/skills/dms-auth/SKILL.md +5 -3
- package/skills/dms-best-practices/REFERENCE.md +2 -0
- package/skills/dms-best-practices/SKILL.md +4 -0
- package/skills/dms-module/SKILL.md +3 -2
- package/skills/dms-pages/REFERENCE.md +129 -16
- package/skills/dms-pages/SKILL.md +1 -1
- package/skills/dms-project/DEV-SERVERS.md +23 -1
- package/skills/dms-project/REFERENCE.md +1 -1
- package/skills/dms-project/SKILL.md +4 -1
- package/skills/dms-project/scripts/dms-dev.sh +205 -17
- package/dist/hooks/tenant-instances.d.ts +0 -1
- package/dist/hooks/tenant-instances.js +0 -13
- package/dist/hooks/tenant-instances.js.map +0 -1
- package/dist/routes/startup-gate.d.ts +0 -7
- package/dist/routes/startup-gate.js +0 -39
- package/dist/routes/startup-gate.js.map +0 -1
- package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +0 -42
|
@@ -54,6 +54,12 @@ export interface ResolvedTableView extends TableViewViewConfig {
|
|
|
54
54
|
|
|
55
55
|
export const VIEW_URL_KEY = "view";
|
|
56
56
|
const NO_VIEW = "";
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The open view as the preferences keep it: no view open is kept too, so the
|
|
60
|
+
* next visit does not fall back to the default view.
|
|
61
|
+
*/
|
|
62
|
+
export const keptViewId = (id: string | undefined): string => id ?? NO_VIEW;
|
|
57
63
|
const VIEW_NAME_FIELD = "label";
|
|
58
64
|
|
|
59
65
|
/**
|
|
@@ -128,11 +134,58 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
128
134
|
applySnapshot(viewSnapshot(view, defaults));
|
|
129
135
|
};
|
|
130
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Opens a view, or, for the one already open, closes it: back to the
|
|
139
|
+
* table's own default state without a view.
|
|
140
|
+
*/
|
|
141
|
+
const selectView = (id: string) =>
|
|
142
|
+
openView(id === activeViewId.value ? undefined : id);
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Back on the view kept from the last visit. The table's state is kept
|
|
146
|
+
* apart and restored as it was left, modified or not, but the search is
|
|
147
|
+
* not kept: the view's own comes back with it.
|
|
148
|
+
*/
|
|
149
|
+
const restoreKeptView = (id: string) => {
|
|
150
|
+
const view = findView(id);
|
|
151
|
+
activeViewId.value = view?.id;
|
|
152
|
+
if (view) state.globalFilter.value = view.search || undefined;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Opens a link followed on the table already shown as a linked arrival
|
|
157
|
+
* reads it: the view it names, else the default view, else the table's
|
|
158
|
+
* default listing state under the reader's own presentation.
|
|
159
|
+
*/
|
|
160
|
+
const openLinkedView = (query: Record<string, unknown>) => {
|
|
161
|
+
const named = readTableUrlKey(query, urlScope, VIEW_URL_KEY);
|
|
162
|
+
const view = named.isCleared
|
|
163
|
+
? undefined
|
|
164
|
+
: (findView(named.value) ?? findView(views?.defaultView));
|
|
165
|
+
if (view) return openView(view.id);
|
|
166
|
+
openDefaultListing();
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* No view open, the table's default listing state (filters, search, sort)
|
|
171
|
+
* under the reader's own presentation: what a link to no view opens.
|
|
172
|
+
*/
|
|
173
|
+
const openDefaultListing = () => {
|
|
174
|
+
const snapshot = viewSnapshot(undefined, defaults);
|
|
175
|
+
activeViewId.value = undefined;
|
|
176
|
+
state.columnFilters.value = snapshot.filters.map((filter) => ({
|
|
177
|
+
...filter,
|
|
178
|
+
}));
|
|
179
|
+
state.globalFilter.value = undefined;
|
|
180
|
+
state.sorting.value = snapshot.sorting;
|
|
181
|
+
state.pagination.value = { ...state.pagination.value, pageIndex: 0 };
|
|
182
|
+
};
|
|
183
|
+
|
|
131
184
|
// Read once, on arrival: a view named by the URL wins over the state kept
|
|
132
185
|
// from the last visit; a view the caller may not open falls back to the
|
|
133
186
|
// default one, with a word (on the client, once mounted). The user's views
|
|
134
187
|
// are kept in the browser, so a view that is not the module's is resolved
|
|
135
|
-
// once mounted.
|
|
188
|
+
// once mounted. An empty one (`?view=`) names no view.
|
|
136
189
|
const urlView = readTableUrlKey(route.query, urlScope, VIEW_URL_KEY);
|
|
137
190
|
const persistedViewId = preferences.get<string | undefined>(
|
|
138
191
|
"activeView",
|
|
@@ -141,7 +194,9 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
141
194
|
let isUrlViewUnavailable = false;
|
|
142
195
|
let isUrlViewPending = false;
|
|
143
196
|
let isOpenedOnArrival = false;
|
|
144
|
-
if (urlView.
|
|
197
|
+
if (urlView.isCleared) {
|
|
198
|
+
openDefaultListing();
|
|
199
|
+
} else if (urlView.value && findView(urlView.value)) {
|
|
145
200
|
openView(urlView.value);
|
|
146
201
|
isOpenedOnArrival = true;
|
|
147
202
|
} else if (urlView.value) {
|
|
@@ -150,7 +205,7 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
150
205
|
if (views?.defaultView) openView(views.defaultView);
|
|
151
206
|
isOpenedOnArrival = !!views?.defaultView;
|
|
152
207
|
} else {
|
|
153
|
-
|
|
208
|
+
restoreKeptView(persistedViewId);
|
|
154
209
|
}
|
|
155
210
|
|
|
156
211
|
// The browser-only state (order, density) is restored over the view opened
|
|
@@ -168,7 +223,7 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
168
223
|
} else if (isOpenedOnArrival) {
|
|
169
224
|
reapplyBrowserState();
|
|
170
225
|
} else if (persistedViewId && !activeViewId.value) {
|
|
171
|
-
|
|
226
|
+
restoreKeptView(persistedViewId);
|
|
172
227
|
}
|
|
173
228
|
};
|
|
174
229
|
|
|
@@ -189,7 +244,11 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
189
244
|
});
|
|
190
245
|
|
|
191
246
|
watch(activeViewId, (id) => {
|
|
192
|
-
preferences.set<string | undefined>(
|
|
247
|
+
preferences.set<string | undefined>(
|
|
248
|
+
"activeView",
|
|
249
|
+
keptViewId(id),
|
|
250
|
+
undefined,
|
|
251
|
+
);
|
|
193
252
|
});
|
|
194
253
|
|
|
195
254
|
const isModified = computed(() => {
|
|
@@ -265,11 +324,26 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
265
324
|
activeViewId.value = view.id;
|
|
266
325
|
};
|
|
267
326
|
|
|
268
|
-
/**
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
327
|
+
/**
|
|
328
|
+
* Deletes one of the user's views, once confirmed. Deleting the open one
|
|
329
|
+
* goes back to the table's own default state: what it set (its search
|
|
330
|
+
* above all) would otherwise stay applied with no view to show it.
|
|
331
|
+
*/
|
|
332
|
+
const deleteView = async (id: string) => {
|
|
333
|
+
const view = findView(id);
|
|
334
|
+
if (!view?.isUserView) return;
|
|
335
|
+
const isConfirmed = await confirm({
|
|
336
|
+
title: t("dms.table.views.delete_title"),
|
|
337
|
+
description: t("dms.table.views.delete_description", {
|
|
338
|
+
view: view.label,
|
|
339
|
+
}),
|
|
340
|
+
icon: "i-ph-trash",
|
|
341
|
+
color: "error",
|
|
342
|
+
confirmLabel: t("dms.table.views.delete"),
|
|
343
|
+
});
|
|
344
|
+
if (!isConfirmed) return;
|
|
345
|
+
writeUserViews(storedUserViews.value.filter((stored) => stored.id !== id));
|
|
346
|
+
if (activeViewId.value === id) openView(undefined);
|
|
273
347
|
};
|
|
274
348
|
|
|
275
349
|
return {
|
|
@@ -279,6 +353,8 @@ export function useTableViews(options: TableViewsOptions) {
|
|
|
279
353
|
isModified,
|
|
280
354
|
canSaveViews: !!views?.userViews,
|
|
281
355
|
openView,
|
|
356
|
+
selectView,
|
|
357
|
+
openLinkedView,
|
|
282
358
|
resetView,
|
|
283
359
|
saveView,
|
|
284
360
|
saveAsNewView,
|
|
@@ -90,10 +90,20 @@ export interface BulkActionOutcome {
|
|
|
90
90
|
|
|
91
91
|
// The count each bulk route answers with: delete sends the number of deleted
|
|
92
92
|
// rows (or `{ deleted }` when a row rule filtered out every id), archive and
|
|
93
|
-
// restore send `{ archivedCount }` / `{ restoredCount }
|
|
94
|
-
|
|
93
|
+
// restore send `{ archivedCount }` / `{ restoredCount }`, and a custom bulk
|
|
94
|
+
// route `{ count }`.
|
|
95
|
+
const COUNT_KEYS = [
|
|
96
|
+
"count",
|
|
97
|
+
"deleted",
|
|
98
|
+
"archivedCount",
|
|
99
|
+
"restoredCount",
|
|
100
|
+
] as const;
|
|
95
101
|
|
|
96
|
-
|
|
102
|
+
/**
|
|
103
|
+
* The number of rows a bulk route says it changed: a bare number or one of
|
|
104
|
+
* its count keys, `undefined` when it does not say.
|
|
105
|
+
*/
|
|
106
|
+
export const readBulkCount = (response: unknown): number | undefined => {
|
|
97
107
|
if (typeof response === "number") return response;
|
|
98
108
|
if (!response || typeof response !== "object") return undefined;
|
|
99
109
|
for (const key of COUNT_KEYS) {
|
|
@@ -112,7 +122,7 @@ export function bulkActionOutcome(
|
|
|
112
122
|
response: unknown,
|
|
113
123
|
requested: number,
|
|
114
124
|
): BulkActionOutcome {
|
|
115
|
-
const count =
|
|
125
|
+
const count = readBulkCount(response);
|
|
116
126
|
const processed =
|
|
117
127
|
count === undefined ? requested : Math.min(Math.max(count, 0), requested);
|
|
118
128
|
return { processed, skipped: requested - processed };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CSSProperties } from "vue";
|
|
1
2
|
import { get } from "@nuxt/ui/runtime/utils/index.js";
|
|
2
3
|
import type {
|
|
3
4
|
TableViewCardProps,
|
|
@@ -68,8 +69,132 @@ export function cardFieldColumns(
|
|
|
68
69
|
]);
|
|
69
70
|
return columns
|
|
70
71
|
.filter(
|
|
71
|
-
(column) =>
|
|
72
|
-
column.listable !== false && !skipped.has(column.accessorKey),
|
|
72
|
+
(column) => column.listable !== false && !skipped.has(column.accessorKey),
|
|
73
73
|
)
|
|
74
74
|
.slice(0, FALLBACK_CARD_FIELD_COUNT);
|
|
75
75
|
}
|
|
76
|
+
|
|
77
|
+
/** The id of the data type drawing a name with its avatar and subtitle. */
|
|
78
|
+
const IDENTITY_DISPLAY_TYPE = "identity";
|
|
79
|
+
|
|
80
|
+
/** The database id: a technical key, never worth a line on a card. */
|
|
81
|
+
const DATABASE_ID_KEY = "_id";
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* What a built-in card reads off its row besides its title: the subtitle and
|
|
85
|
+
* the avatar the title column's `identity` display declares, so a card shows
|
|
86
|
+
* the row as its grid cell does.
|
|
87
|
+
*/
|
|
88
|
+
export interface CardIdentity {
|
|
89
|
+
/** Row field drawn under the title. */
|
|
90
|
+
subtitleField?: string;
|
|
91
|
+
/** Row field holding the avatar: an image value or a URL. */
|
|
92
|
+
avatarField?: string;
|
|
93
|
+
/** File storage the avatar is read from. */
|
|
94
|
+
storage?: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const optionString = (
|
|
98
|
+
options: Record<string, unknown> | undefined,
|
|
99
|
+
key: string,
|
|
100
|
+
): string | undefined =>
|
|
101
|
+
typeof options?.[key] === "string" ? options[key] : undefined;
|
|
102
|
+
|
|
103
|
+
/** The identity the title column (`labelKey`) declares, if it is one. */
|
|
104
|
+
export function cardIdentity(
|
|
105
|
+
columns: TableViewColumn[],
|
|
106
|
+
labelKey: string | undefined,
|
|
107
|
+
): CardIdentity {
|
|
108
|
+
if (!labelKey) return {};
|
|
109
|
+
const display = columns.find(
|
|
110
|
+
(column) => column.accessorKey === labelKey,
|
|
111
|
+
)?.display;
|
|
112
|
+
if (display?.type !== IDENTITY_DISPLAY_TYPE) return {};
|
|
113
|
+
return {
|
|
114
|
+
subtitleField: optionString(display.options, "subtitleField"),
|
|
115
|
+
avatarField: optionString(display.options, "avatarField"),
|
|
116
|
+
storage: optionString(display.options, "storage"),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const hasValue = (value: unknown): boolean =>
|
|
121
|
+
value !== undefined && value !== null && value !== "";
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The line a card draws under its title, as the row holds it: the identity's
|
|
125
|
+
* subtitle, else the row's id when the table names it with a key of its own
|
|
126
|
+
* (`rowIdKey: "code"`) and the title is not it already. The database `_id`
|
|
127
|
+
* is never shown: a UUID or `proj-074` tells the reader nothing.
|
|
128
|
+
*/
|
|
129
|
+
export function cardSubtitle<T>(
|
|
130
|
+
item: T,
|
|
131
|
+
identity: CardIdentity,
|
|
132
|
+
shown: CardShownKeys,
|
|
133
|
+
): unknown {
|
|
134
|
+
const row = item as Record<string, unknown>;
|
|
135
|
+
if (identity.subtitleField) {
|
|
136
|
+
const subtitle: unknown = get(row, identity.subtitleField);
|
|
137
|
+
if (hasValue(subtitle)) return subtitle;
|
|
138
|
+
}
|
|
139
|
+
if (shown.rowIdKey === DATABASE_ID_KEY) return undefined;
|
|
140
|
+
if (!shown.labelKey || !hasValue(get(row, shown.labelKey))) return undefined;
|
|
141
|
+
const id = cardRowId(item, shown.rowIdKey);
|
|
142
|
+
return id || undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** The avatar a card draws in place of the title's initials, if any. */
|
|
146
|
+
export function cardAvatar<T>(
|
|
147
|
+
item: T,
|
|
148
|
+
identity: CardIdentity,
|
|
149
|
+
): { key: string } | string | undefined {
|
|
150
|
+
if (!identity.avatarField) return undefined;
|
|
151
|
+
const avatar: unknown = get(
|
|
152
|
+
item as Record<string, unknown>,
|
|
153
|
+
identity.avatarField,
|
|
154
|
+
);
|
|
155
|
+
if (typeof avatar === "string") return avatar || undefined;
|
|
156
|
+
if (avatar && typeof avatar === "object" && "key" in avatar) {
|
|
157
|
+
const { key } = avatar as { key: unknown };
|
|
158
|
+
return typeof key === "string" && key ? { key } : undefined;
|
|
159
|
+
}
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** The narrowest a card of the `cards` display gets (backend `card.minWidth`). */
|
|
164
|
+
export const DEFAULT_CARD_MIN_WIDTH = "230px";
|
|
165
|
+
|
|
166
|
+
// The grid's gap and side padding, as `CARD_GRID_CLASS` sets them: the width
|
|
167
|
+
// cap below adds them to the cards' own.
|
|
168
|
+
const CARD_GRID_GAP = "0.75rem";
|
|
169
|
+
const CARD_GRID_PADDING = "36px";
|
|
170
|
+
|
|
171
|
+
// How much wider than `card.minWidth` a card may stretch when the row has room
|
|
172
|
+
// for more cards than the list holds. A full row hardly reaches it: n cards
|
|
173
|
+
// fit, but not n + 1, so each one stays under twice the minimum, give or take
|
|
174
|
+
// a gap.
|
|
175
|
+
const CARD_MAX_WIDTH_FACTOR = 2;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* The cards grid: auto-fit tracks of at least `--dms-card-min-width` (the
|
|
179
|
+
* whole row on a narrower screen), so a short list's empty tracks collapse
|
|
180
|
+
* and its cards stretch across the row instead of leaving a hole at its end.
|
|
181
|
+
*/
|
|
182
|
+
export const CARD_GRID_CLASS =
|
|
183
|
+
"grid grid-cols-[repeat(auto-fit,minmax(min(var(--dms-card-min-width),100%),1fr))] gap-3 px-[18px] pt-4 pb-[18px]";
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* The style of a cards grid holding `count` cards of at least `minWidth` (a
|
|
187
|
+
* CSS length): the width its tracks start from, and a cap keeping one or two
|
|
188
|
+
* cards from stretching across a whole wide screen.
|
|
189
|
+
*/
|
|
190
|
+
export function cardGridStyle(
|
|
191
|
+
minWidth: string | undefined,
|
|
192
|
+
count: number,
|
|
193
|
+
): CSSProperties {
|
|
194
|
+
const width = minWidth || DEFAULT_CARD_MIN_WIDTH;
|
|
195
|
+
const cards = Math.max(1, count);
|
|
196
|
+
return {
|
|
197
|
+
"--dms-card-min-width": width,
|
|
198
|
+
maxWidth: `calc(${cards} * ${CARD_MAX_WIDTH_FACTOR} * ${width} + ${cards - 1} * ${CARD_GRID_GAP} + ${CARD_GRID_PADDING})`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** The text of a cell value, as the browser searches and sorts it. */
|
|
2
|
+
export const textOf = (value: unknown): string =>
|
|
3
|
+
value === null || value === undefined
|
|
4
|
+
? ""
|
|
5
|
+
: typeof value === "object"
|
|
6
|
+
? JSON.stringify(value)
|
|
7
|
+
: String(value);
|
|
8
|
+
|
|
9
|
+
/** Orders two cell values the browser sorts: numbers by value, else as text. */
|
|
10
|
+
export const compareValues = (a: unknown, b: unknown): number => {
|
|
11
|
+
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
12
|
+
return textOf(a).localeCompare(textOf(b), undefined, { numeric: true });
|
|
13
|
+
};
|
|
@@ -49,3 +49,31 @@ export function keepListedRows(
|
|
|
49
49
|
Object.entries(expanded).filter(([id]) => listed.has(id)),
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
/** A table's open rows, and the listed set (its query) they belong to. */
|
|
54
|
+
export interface ExpandedRowsState {
|
|
55
|
+
expanded: ExpandedRowMap;
|
|
56
|
+
listedKey?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The open rows once the listed rows change: a newly listed set (another
|
|
61
|
+
* `listedKey`: page, filter, search, sort, tab, archive view) opens with
|
|
62
|
+
* `defaultExpanded`; a refresh of the same set keeps what the user opened,
|
|
63
|
+
* minus rows no longer listed. Rows that have not arrived yet (`undefined`,
|
|
64
|
+
* the list still loading, as on a first load) list no set: the default waits
|
|
65
|
+
* for them instead of being spent on an empty table.
|
|
66
|
+
*/
|
|
67
|
+
export function expandedRowsOnListing(
|
|
68
|
+
state: ExpandedRowsState,
|
|
69
|
+
rowIds: string[] | undefined,
|
|
70
|
+
listedKey: string,
|
|
71
|
+
mode?: ExpandedDefault,
|
|
72
|
+
single?: boolean,
|
|
73
|
+
): ExpandedRowsState {
|
|
74
|
+
if (!rowIds) return state;
|
|
75
|
+
if (listedKey === state.listedKey) {
|
|
76
|
+
return { listedKey, expanded: keepListedRows(state.expanded, rowIds) };
|
|
77
|
+
}
|
|
78
|
+
return { listedKey, expanded: defaultExpandedRows(rowIds, mode, single) };
|
|
79
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { get } from "@nuxt/ui/runtime/utils/index.js";
|
|
2
|
+
import type { ColumnSort, SortingState } from "@tanstack/vue-table";
|
|
2
3
|
import {
|
|
3
4
|
regionalDateTimeFormat,
|
|
4
5
|
regionalDayKey,
|
|
@@ -13,6 +14,7 @@ import type {
|
|
|
13
14
|
TableViewGroupedConfig,
|
|
14
15
|
} from "../../../../composables/table-view/types";
|
|
15
16
|
import type { TableFilter } from "../../../components/table/Table.vue";
|
|
17
|
+
import { compareValues } from "./compareValues";
|
|
16
18
|
import { sortValueKind } from "./sortableColumns";
|
|
17
19
|
|
|
18
20
|
/** Key of the group of rows without a value. */
|
|
@@ -146,6 +148,91 @@ export function groupedSorting(
|
|
|
146
148
|
return [{ id: groupByField, desc: own ? !!own.desc : by !== "value" }];
|
|
147
149
|
}
|
|
148
150
|
|
|
151
|
+
/**
|
|
152
|
+
* The sort a group's rows follow on the grouped display. The list route sorts
|
|
153
|
+
* on one field, the grouped column, so the sort picked on another column —
|
|
154
|
+
* by the user or a view, or the hand order of a `reorder` table — orders each
|
|
155
|
+
* group's rows in the browser instead, over the page listed.
|
|
156
|
+
*/
|
|
157
|
+
export function withinGroupSort(
|
|
158
|
+
picked: SortingState,
|
|
159
|
+
grouped: TableViewGroupedConfig,
|
|
160
|
+
): ColumnSort | undefined {
|
|
161
|
+
return picked.find((entry) => entry.id !== grouped.groupByField);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** What decides the order a table view lists its rows in. */
|
|
165
|
+
export interface TableSortingInput {
|
|
166
|
+
/** The order field of a hand-ordered table (`reorder`). */
|
|
167
|
+
reorderField?: string;
|
|
168
|
+
/** The grouped options, while the grouped display shows. */
|
|
169
|
+
grouped?: TableViewGroupedConfig;
|
|
170
|
+
/** The display shown offers the sort picker. */
|
|
171
|
+
canSort: boolean;
|
|
172
|
+
/** The sort the user picked or a view opened with. */
|
|
173
|
+
sorting: SortingState;
|
|
174
|
+
/** The table's default sort. */
|
|
175
|
+
defaultSorting: SortingState;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* The order the rows are wanted in: the hand order of a `reorder` table,
|
|
180
|
+
* else the picked sort. The grouped display hides the sort picker but keeps
|
|
181
|
+
* the sort a view opened it with: it orders the rows of each group.
|
|
182
|
+
*/
|
|
183
|
+
export function pickedTableSorting(input: TableSortingInput): SortingState {
|
|
184
|
+
if (input.reorderField) return [{ id: input.reorderField, desc: false }];
|
|
185
|
+
return input.canSort || input.grouped ? input.sorting : input.defaultSorting;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* The sort the list route is asked for (the route sorts on one field): on the
|
|
190
|
+
* grouped display, the grouped column, so that a group's rows follow each
|
|
191
|
+
* other — the picked order then sorts each group, see
|
|
192
|
+
* {@link orderGroupedRows}; else the picked order.
|
|
193
|
+
*/
|
|
194
|
+
export function listTableSorting(input: TableSortingInput): SortingState {
|
|
195
|
+
const picked = pickedTableSorting(input);
|
|
196
|
+
if (input.grouped) return groupedSorting(picked, input.grouped);
|
|
197
|
+
return picked;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* The rows of a page listed on the grouped column, each group's rows in the
|
|
202
|
+
* order `picked` gives them (see {@link withinGroupSort}). The groups keep
|
|
203
|
+
* the order the list gave them; rows the sort cannot tell apart keep theirs.
|
|
204
|
+
*/
|
|
205
|
+
export function orderGroupedRows<T>(
|
|
206
|
+
rows: T[],
|
|
207
|
+
grouped: TableViewGroupedConfig,
|
|
208
|
+
picked: SortingState,
|
|
209
|
+
locale: string,
|
|
210
|
+
): T[] {
|
|
211
|
+
const sort = withinGroupSort(picked, grouped);
|
|
212
|
+
if (!sort) return rows;
|
|
213
|
+
const by = grouped.by ?? "value";
|
|
214
|
+
const groupOf = new Map<string, number>();
|
|
215
|
+
const entries = rows.map((row, index) => {
|
|
216
|
+
const key = rowGroupKey(
|
|
217
|
+
get(row as Record<string, unknown>, grouped.groupByField),
|
|
218
|
+
by,
|
|
219
|
+
locale,
|
|
220
|
+
);
|
|
221
|
+
if (!groupOf.has(key)) groupOf.set(key, groupOf.size);
|
|
222
|
+
return { row, index, group: groupOf.get(key)! };
|
|
223
|
+
});
|
|
224
|
+
const direction = sort.desc ? -1 : 1;
|
|
225
|
+
const valueOf = (row: T) => get(row as Record<string, unknown>, sort.id);
|
|
226
|
+
return entries
|
|
227
|
+
.sort(
|
|
228
|
+
(a, b) =>
|
|
229
|
+
a.group - b.group ||
|
|
230
|
+
direction * compareValues(valueOf(a.row), valueOf(b.row)) ||
|
|
231
|
+
a.index - b.index,
|
|
232
|
+
)
|
|
233
|
+
.map((entry) => entry.row);
|
|
234
|
+
}
|
|
235
|
+
|
|
149
236
|
/** How a date column picked in the options menu is cut, unless configured. */
|
|
150
237
|
const DEFAULT_DATE_GROUP_BY: TableViewGroupBy = "day";
|
|
151
238
|
|
|
@@ -178,3 +265,29 @@ export function groupedOptionsFor(
|
|
|
178
265
|
grouped.by && grouped.by !== "value" ? grouped.by : DEFAULT_DATE_GROUP_BY;
|
|
179
266
|
return { ...grouped, groupByField: field, by };
|
|
180
267
|
}
|
|
268
|
+
|
|
269
|
+
/** The rows of one group, under its key. */
|
|
270
|
+
export interface RowGroup<T> {
|
|
271
|
+
key: string;
|
|
272
|
+
rows: T[];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* The rows filed under their group, each group once, in the order its first
|
|
277
|
+
* row comes. The list is sorted on the grouped column, but rows listed in
|
|
278
|
+
* another order — the previous sort, still shown while the grouped one loads
|
|
279
|
+
* — must not open a group twice: two headers under one key.
|
|
280
|
+
*/
|
|
281
|
+
export function groupRows<T>(
|
|
282
|
+
rows: readonly T[],
|
|
283
|
+
keyOf: (row: T) => string,
|
|
284
|
+
): RowGroup<T>[] {
|
|
285
|
+
const groups = new Map<string, T[]>();
|
|
286
|
+
for (const row of rows) {
|
|
287
|
+
const key = keyOf(row);
|
|
288
|
+
const group = groups.get(key);
|
|
289
|
+
if (group) group.push(row);
|
|
290
|
+
else groups.set(key, [row]);
|
|
291
|
+
}
|
|
292
|
+
return [...groups].map(([key, grouped]) => ({ key, rows: grouped }));
|
|
293
|
+
}
|