@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
|
@@ -16,11 +16,27 @@ import type { ArrayOrNested } from "@nuxt/ui/runtime/types/utils.js";
|
|
|
16
16
|
|
|
17
17
|
import { refDebounced } from "@vueuse/core";
|
|
18
18
|
import DmsForm from "../Form.vue";
|
|
19
|
+
import { dmsOwnDrawer } from "../../../build/composables/containers/context";
|
|
19
20
|
import { FIELD_TRIGGER_ICON } from "../../../build/utils/fieldTrigger";
|
|
21
|
+
import { unlabeledRelationIds } from "../../../build/composables/data-types/relationValue";
|
|
22
|
+
import {
|
|
23
|
+
notFoundRelationIds,
|
|
24
|
+
useRelationRows,
|
|
25
|
+
} from "../../../build/composables/data-types/relationRows";
|
|
20
26
|
import type { FormProps } from "../../../composables/form/types";
|
|
27
|
+
import { sameFormValue } from "../../../build/composables/unsaved-changes/formValue";
|
|
28
|
+
import { FORM_ENTRY_CONTEXT_KEY } from "../../../build/composables/form/formEntryContext";
|
|
29
|
+
import {
|
|
30
|
+
type RelationFilterBy,
|
|
31
|
+
relationFilterParams,
|
|
32
|
+
resolveRelationPick,
|
|
33
|
+
} from "../../../build/composables/data-types/relationFilter";
|
|
21
34
|
|
|
22
35
|
const SEARCH_DEBOUNCE_MS = 200;
|
|
23
|
-
|
|
36
|
+
// How long the menu's content gets to leave the page before the add form opens
|
|
37
|
+
// anyway, and the beat its focus return takes once it has.
|
|
38
|
+
const MENU_CLOSE_MAX_MS = 1000;
|
|
39
|
+
const MENU_FOCUS_SETTLE_MS = 60;
|
|
24
40
|
|
|
25
41
|
/** Title and description of the drawer a relation picker's "add" opens. */
|
|
26
42
|
interface RelationAddFormTexts {
|
|
@@ -38,6 +54,11 @@ interface RelationProps extends SelectMenuProps<T, "value", M> {
|
|
|
38
54
|
avatar?: string;
|
|
39
55
|
disabled?: string;
|
|
40
56
|
};
|
|
57
|
+
/**
|
|
58
|
+
* Lists only the related rows whose `field` equals the value of the form
|
|
59
|
+
* field `from`; ignored outside a form (a filter's picker).
|
|
60
|
+
*/
|
|
61
|
+
filterBy?: RelationFilterBy;
|
|
41
62
|
addForm?: { componentName: string; options?: FormProps };
|
|
42
63
|
addPermissionIds?: string[];
|
|
43
64
|
/**
|
|
@@ -63,33 +84,24 @@ const forwardedProps = computed(() => {
|
|
|
63
84
|
addForm: ______,
|
|
64
85
|
addPermissionIds: _______,
|
|
65
86
|
addFormTexts: ________,
|
|
87
|
+
filterBy: _________,
|
|
66
88
|
...rest
|
|
67
89
|
} = props;
|
|
68
90
|
return rest as SelectMenuProps<T, "value", M>;
|
|
69
91
|
});
|
|
70
|
-
const
|
|
92
|
+
const forwardedPropsEmits = useForwardPropsEmits(forwardedProps, emits);
|
|
93
|
+
// The picks go through `handleUpdateModelValue` alone, which deselects a value
|
|
94
|
+
// picked again: forwarded as well, the form got the pick it had undone.
|
|
95
|
+
const forwarded = computed(() => {
|
|
96
|
+
const { "onUpdate:modelValue": _, ...rest } = forwardedPropsEmits.value as {
|
|
97
|
+
"onUpdate:modelValue"?: unknown;
|
|
98
|
+
};
|
|
99
|
+
return rest as typeof forwardedPropsEmits.value;
|
|
100
|
+
});
|
|
71
101
|
|
|
72
102
|
type ModelValue = SelectMenuEmits<T, "value", M>["update:modelValue"][0];
|
|
73
103
|
|
|
74
|
-
function isAddNewValue(value: unknown): boolean {
|
|
75
|
-
if (value === ADD_NEW_VALUE) return true;
|
|
76
|
-
if (Array.isArray(value)) return value.includes(ADD_NEW_VALUE);
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
104
|
function handleUpdateModelValue(value: ModelValue) {
|
|
81
|
-
if (isAddNewValue(value)) {
|
|
82
|
-
openAddDrawer();
|
|
83
|
-
if (Array.isArray(value)) {
|
|
84
|
-
emits(
|
|
85
|
-
"update:modelValue",
|
|
86
|
-
(value as unknown[]).filter((v) => v !== ADD_NEW_VALUE) as ModelValue,
|
|
87
|
-
);
|
|
88
|
-
} else {
|
|
89
|
-
emits("update:modelValue", props.modelValue as ModelValue);
|
|
90
|
-
}
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
105
|
const shouldDeselect =
|
|
94
106
|
props.deselectable && !props.multiple && value === props.modelValue;
|
|
95
107
|
emits(
|
|
@@ -103,7 +115,23 @@ const instanceId = useId();
|
|
|
103
115
|
const searchTerm = ref("");
|
|
104
116
|
const searchTermDebounced = refDebounced(searchTerm, SEARCH_DEBOUNCE_MS);
|
|
105
117
|
|
|
118
|
+
// The form this picker fills, whose `filterBy.from` field narrows it.
|
|
119
|
+
const formContext = inject(FORM_ENTRY_CONTEXT_KEY, null);
|
|
120
|
+
const filterSourceValue = computed(() =>
|
|
121
|
+
props.filterBy && formContext
|
|
122
|
+
? formContext.state.value[props.filterBy.from]
|
|
123
|
+
: undefined,
|
|
124
|
+
);
|
|
125
|
+
const filterParams = computed(() =>
|
|
126
|
+
relationFilterParams(props.filterBy, filterSourceValue.value),
|
|
127
|
+
);
|
|
128
|
+
// A string, so a recomputed but equal filter does not search again.
|
|
129
|
+
const filterKey = computed(() => JSON.stringify(filterParams.value));
|
|
130
|
+
// The source value the record loaded with, which its loaded pick matches.
|
|
131
|
+
const loadedFilterSource = filterSourceValue.value;
|
|
132
|
+
|
|
106
133
|
const { $authFetch } = useAuthFetch();
|
|
134
|
+
const { readRelationRows } = useRelationRows();
|
|
107
135
|
const toast = useToast();
|
|
108
136
|
const { t } = useI18n();
|
|
109
137
|
const { processI18n } = useTranslation();
|
|
@@ -118,6 +146,9 @@ const addContainerId = `relation-add-${useId()}`;
|
|
|
118
146
|
// unwrapped item is an unbounded type for the checker. The list is always
|
|
119
147
|
// replaced, never mutated in place.
|
|
120
148
|
const locallyAddedItems: Ref<SelectMenuItem[]> = shallowRef([]);
|
|
149
|
+
// The rows of the ids the value holds, read from the `select` route when the
|
|
150
|
+
// form's data answers the ids rather than the joined rows.
|
|
151
|
+
const valueItems: Ref<SelectMenuItem[]> = shallowRef([]);
|
|
121
152
|
|
|
122
153
|
const canAdd = computed(
|
|
123
154
|
() =>
|
|
@@ -197,6 +228,7 @@ async function fetchRelationItems(): Promise<RelationItems> {
|
|
|
197
228
|
const data = await $authFetch<RelationResponse>(url, {
|
|
198
229
|
params: {
|
|
199
230
|
search: searchTermDebounced.value || undefined,
|
|
231
|
+
...filterParams.value,
|
|
200
232
|
},
|
|
201
233
|
onRequestError: ({ error }: { error: Error }) => {
|
|
202
234
|
toast.add({
|
|
@@ -234,7 +266,7 @@ const { data, status, execute } = await useDmsAsyncData(
|
|
|
234
266
|
{
|
|
235
267
|
lazy: true,
|
|
236
268
|
immediate: false,
|
|
237
|
-
watch: [searchTermDebounced],
|
|
269
|
+
watch: [searchTermDebounced, filterKey],
|
|
238
270
|
// Options to pick from, not the page's data.
|
|
239
271
|
refreshOnPageRefresh: false,
|
|
240
272
|
},
|
|
@@ -246,6 +278,8 @@ function isMappableObject(value: unknown): value is Record<string, string> {
|
|
|
246
278
|
|
|
247
279
|
const initialItems = computed(() => {
|
|
248
280
|
if (!props.initialValue) return [];
|
|
281
|
+
// The loaded row matched the loaded source value, not another one.
|
|
282
|
+
if (!sameFormValue(filterSourceValue.value, loadedFilterSource)) return [];
|
|
249
283
|
|
|
250
284
|
const values = Array.isArray(props.initialValue)
|
|
251
285
|
? props.initialValue
|
|
@@ -254,42 +288,28 @@ const initialItems = computed(() => {
|
|
|
254
288
|
return values.filter(isMappableObject).map(mapData);
|
|
255
289
|
});
|
|
256
290
|
|
|
291
|
+
function itemValue(item: SelectMenuItem): unknown {
|
|
292
|
+
return typeof item === "object" && item !== null && "value" in item
|
|
293
|
+
? item.value
|
|
294
|
+
: item;
|
|
295
|
+
}
|
|
296
|
+
|
|
257
297
|
const mergedItems = computed<SelectMenuItem[]>(() => {
|
|
258
298
|
const searchResults = data.value?.results || [];
|
|
259
299
|
const initial = initialItems.value || [];
|
|
260
300
|
const added = locallyAddedItems.value;
|
|
261
301
|
|
|
262
302
|
const merged: SelectMenuItem[] = [...initial, ...added];
|
|
263
|
-
const existingValues = new Set(
|
|
264
|
-
merged.map((item) =>
|
|
265
|
-
typeof item === "object" && item !== null && "value" in item
|
|
266
|
-
? item.value
|
|
267
|
-
: item,
|
|
268
|
-
),
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
searchResults.forEach((item) => {
|
|
272
|
-
const itemValue =
|
|
273
|
-
typeof item === "object" && item !== null && "value" in item
|
|
274
|
-
? item.value
|
|
275
|
-
: item;
|
|
303
|
+
const existingValues = new Set(merged.map(itemValue));
|
|
276
304
|
|
|
277
|
-
|
|
305
|
+
[...valueItems.value, ...searchResults].forEach((item) => {
|
|
306
|
+
const value = itemValue(item);
|
|
307
|
+
if (!existingValues.has(value)) {
|
|
308
|
+
existingValues.add(value);
|
|
278
309
|
merged.push(item);
|
|
279
310
|
}
|
|
280
311
|
});
|
|
281
312
|
|
|
282
|
-
if (canAdd.value) {
|
|
283
|
-
if (merged.length > 0) {
|
|
284
|
-
merged.push({ type: "separator" });
|
|
285
|
-
}
|
|
286
|
-
merged.push({
|
|
287
|
-
label: t("dms.form.relation.add_new"),
|
|
288
|
-
value: ADD_NEW_VALUE,
|
|
289
|
-
icon: "i-ph-plus",
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
|
|
293
313
|
return merged;
|
|
294
314
|
});
|
|
295
315
|
|
|
@@ -326,34 +346,156 @@ watch(
|
|
|
326
346
|
{ immediate: true },
|
|
327
347
|
);
|
|
328
348
|
|
|
349
|
+
// The pick as the form holds it, read alongside the source field: a record
|
|
350
|
+
// loading both values changes them in the same tick.
|
|
351
|
+
const pickValue = computed(() =>
|
|
352
|
+
formContext && props.id
|
|
353
|
+
? formContext.state.value[props.id]
|
|
354
|
+
: props.modelValue,
|
|
355
|
+
);
|
|
356
|
+
let pickSource = loadedFilterSource;
|
|
357
|
+
|
|
358
|
+
// A pick made for another value of the source field is void once it changes.
|
|
359
|
+
watch(
|
|
360
|
+
[filterSourceValue, pickValue],
|
|
361
|
+
([source, pick], [prevSource, prevPick]) => {
|
|
362
|
+
if (!props.filterBy || !formContext) return;
|
|
363
|
+
const outcome = resolveRelationPick({
|
|
364
|
+
source,
|
|
365
|
+
pick,
|
|
366
|
+
previousSource: prevSource,
|
|
367
|
+
previousPick: prevPick,
|
|
368
|
+
pickSource,
|
|
369
|
+
});
|
|
370
|
+
pickSource = outcome.pickSource;
|
|
371
|
+
if (!outcome.shouldClear) return;
|
|
372
|
+
emits("update:modelValue", (props.multiple ? [] : undefined) as ModelValue);
|
|
373
|
+
},
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
const isMenuOpen = ref(false);
|
|
377
|
+
|
|
378
|
+
const slots = useSlots();
|
|
379
|
+
// `content-bottom` is filled below, after the add entry.
|
|
380
|
+
const forwardedSlotNames = computed(() =>
|
|
381
|
+
Object.keys(slots).filter((name) => name !== "content-bottom"),
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
const labeledIds = computed(
|
|
385
|
+
() => new Set(mergedItems.value.map((item) => String(itemValue(item)))),
|
|
386
|
+
);
|
|
387
|
+
// Asked once each: an id the route does not answer (deleted, out of reach)
|
|
388
|
+
// keeps showing as is rather than being asked again.
|
|
389
|
+
const requestedIds = new Set<string>();
|
|
390
|
+
|
|
391
|
+
// The ids the route did not answer (deleted, out of reach), shown as "Not
|
|
392
|
+
// found" with the id on hover.
|
|
393
|
+
const notFoundIds: Ref<ReadonlySet<string>> = shallowRef(new Set());
|
|
394
|
+
|
|
395
|
+
async function loadValueItems(ids: string[]) {
|
|
396
|
+
for (const id of ids) requestedIds.add(id);
|
|
397
|
+
const source = { searchUrl: props.searchUrl, keyMapping: props.keyMapping };
|
|
398
|
+
const rows = await readRelationRows(source, ids);
|
|
399
|
+
const missing = notFoundRelationIds(source, ids, rows);
|
|
400
|
+
if (!rows.length && !missing.length) return;
|
|
401
|
+
notFoundIds.value = new Set([...notFoundIds.value, ...missing]);
|
|
402
|
+
valueItems.value = [
|
|
403
|
+
...valueItems.value,
|
|
404
|
+
...rows.map((item) => mapData(item as Record<string, string>)),
|
|
405
|
+
// Kept pickable, so the value can still be deselected.
|
|
406
|
+
...missing.map((id) => ({
|
|
407
|
+
label: t("dms.form.relation.not_found"),
|
|
408
|
+
value: id,
|
|
409
|
+
})),
|
|
410
|
+
];
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const notFoundTitle = computed(() => {
|
|
414
|
+
const ids = [props.modelValue]
|
|
415
|
+
.flat()
|
|
416
|
+
.map(String)
|
|
417
|
+
.filter((id) => notFoundIds.value.has(id));
|
|
418
|
+
return ids.length ? ids.join(", ") : undefined;
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// The picker shows a value through its item: an id no item carries yet is
|
|
422
|
+
// labelled (and given its avatar) from its row before the picker is opened.
|
|
423
|
+
onMounted(() => {
|
|
424
|
+
watch(
|
|
425
|
+
() =>
|
|
426
|
+
unlabeledRelationIds(props.modelValue, labeledIds.value).filter(
|
|
427
|
+
(id) => !requestedIds.has(id),
|
|
428
|
+
),
|
|
429
|
+
(ids) => {
|
|
430
|
+
if (ids.length > 0) void loadValueItems(ids);
|
|
431
|
+
},
|
|
432
|
+
{ immediate: true },
|
|
433
|
+
);
|
|
434
|
+
});
|
|
435
|
+
|
|
329
436
|
function onOpen(isOpen: boolean) {
|
|
437
|
+
isMenuOpen.value = isOpen;
|
|
330
438
|
if (isOpen && !data.value?.results?.length) {
|
|
331
439
|
execute();
|
|
332
440
|
}
|
|
333
441
|
}
|
|
334
442
|
|
|
443
|
+
const addEntryRef = useTemplateRef<HTMLElement>("addEntry");
|
|
444
|
+
|
|
445
|
+
function waitForDetached(element: HTMLElement | null): Promise<void> {
|
|
446
|
+
const startedAt = performance.now();
|
|
447
|
+
return new Promise((resolve) => {
|
|
448
|
+
const check = () => {
|
|
449
|
+
if (
|
|
450
|
+
!element?.isConnected ||
|
|
451
|
+
performance.now() - startedAt > MENU_CLOSE_MAX_MS
|
|
452
|
+
) {
|
|
453
|
+
resolve();
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
requestAnimationFrame(check);
|
|
457
|
+
};
|
|
458
|
+
check();
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Opens the add form once the menu has left the page: its content animates
|
|
464
|
+
* out, then hands the focus back to the field, and a drawer opened before
|
|
465
|
+
* that took the focus leaving it for a click outside and closed at once.
|
|
466
|
+
*/
|
|
467
|
+
async function openAddDrawerOnceClosed(): Promise<void> {
|
|
468
|
+
const menuContent = addEntryRef.value;
|
|
469
|
+
isMenuOpen.value = false;
|
|
470
|
+
await waitForDetached(menuContent);
|
|
471
|
+
await new Promise((resolve) => setTimeout(resolve, MENU_FOCUS_SETTLE_MS));
|
|
472
|
+
openAddDrawer();
|
|
473
|
+
}
|
|
474
|
+
|
|
335
475
|
function openAddDrawer() {
|
|
336
476
|
const formOptions = addFormOptions.value;
|
|
337
477
|
if (!formOptions) return;
|
|
338
|
-
const drawer = openDrawer(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
478
|
+
const drawer = openDrawer(
|
|
479
|
+
dmsOwnDrawer({
|
|
480
|
+
title: props.addFormTexts?.displayName
|
|
481
|
+
? processI18n(props.addFormTexts.displayName)
|
|
482
|
+
: t("dms.table.new_item"),
|
|
483
|
+
description: props.addFormTexts?.description
|
|
484
|
+
? processI18n(props.addFormTexts.description)
|
|
485
|
+
: t("dms.table.new_item_description"),
|
|
486
|
+
direction: "bottom",
|
|
487
|
+
containerId: addContainerId,
|
|
488
|
+
component: DmsForm,
|
|
489
|
+
componentOptions: {
|
|
490
|
+
...formOptions,
|
|
491
|
+
onSuccessCallback: (response: unknown, formData: unknown) => {
|
|
492
|
+
clearGuards(addContainerId);
|
|
493
|
+
drawer.close();
|
|
494
|
+
handleAddSuccess(response, formData);
|
|
495
|
+
},
|
|
354
496
|
},
|
|
355
|
-
},
|
|
356
|
-
|
|
497
|
+
}),
|
|
498
|
+
);
|
|
357
499
|
}
|
|
358
500
|
|
|
359
501
|
function extractInsertedId(response: unknown): string | undefined {
|
|
@@ -428,20 +570,39 @@ const addFormOptions = computed(() => {
|
|
|
428
570
|
v-model:search-term="searchTerm"
|
|
429
571
|
:trailing-icon="props.trailingIcon ?? FIELD_TRIGGER_ICON"
|
|
430
572
|
:model-value="props.modelValue"
|
|
573
|
+
:open="isMenuOpen"
|
|
431
574
|
:items="displayItems as T"
|
|
432
575
|
:loading="status === 'pending'"
|
|
433
576
|
value-key="value"
|
|
434
577
|
:portal="false"
|
|
435
578
|
:ignore-filter="true"
|
|
436
579
|
:ui="{ content: 'z-50' }"
|
|
580
|
+
:aria-label="t('dms.a11y.show_options')"
|
|
581
|
+
:title="notFoundTitle"
|
|
437
582
|
@update:open="onOpen"
|
|
438
583
|
@update:model-value="handleUpdateModelValue"
|
|
439
584
|
>
|
|
440
|
-
<template v-for="
|
|
585
|
+
<template v-for="name in forwardedSlotNames" :key="name" #[name]="slotData">
|
|
441
586
|
<slot
|
|
442
587
|
:name="name as keyof SelectMenuSlots<T, 'value', M>"
|
|
443
588
|
v-bind="slotData ?? {}"
|
|
444
589
|
/>
|
|
445
590
|
</template>
|
|
591
|
+
<!-- The add entry is no item: picked, it became the field's value. -->
|
|
592
|
+
<template v-if="canAdd || $slots['content-bottom']" #content-bottom>
|
|
593
|
+
<div v-if="canAdd" ref="addEntry" class="border-default border-t p-1">
|
|
594
|
+
<UButton
|
|
595
|
+
:label="t('dms.form.relation.add_new')"
|
|
596
|
+
icon="i-ph-plus"
|
|
597
|
+
color="neutral"
|
|
598
|
+
variant="ghost"
|
|
599
|
+
size="sm"
|
|
600
|
+
block
|
|
601
|
+
class="justify-start"
|
|
602
|
+
@click="openAddDrawerOnceClosed"
|
|
603
|
+
/>
|
|
604
|
+
</div>
|
|
605
|
+
<slot name="content-bottom" />
|
|
606
|
+
</template>
|
|
446
607
|
</USelectMenu>
|
|
447
608
|
</template>
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
useFormField,
|
|
7
7
|
} from "@nuxt/ui/composables/useFormField";
|
|
8
8
|
import { resolveDmsComponent } from "../../../composables/resolveDmsComponent";
|
|
9
|
+
import DmsRepeaterTable from "./RepeaterTable.vue";
|
|
9
10
|
|
|
10
11
|
/** A field of the rows: one column. */
|
|
11
12
|
interface RepeaterColumn {
|
|
@@ -72,21 +73,43 @@ const FLUID_COLUMN = "minmax(0,1fr)";
|
|
|
72
73
|
const COLUMN_WIDTHS: Record<string, string> = { boolean: "20px" };
|
|
73
74
|
const HANDLE_COLUMN = "20px";
|
|
74
75
|
const REMOVE_COLUMN = "28px";
|
|
76
|
+
|
|
77
|
+
/** The columns of a row: the handle, the given cell columns, the remove button. */
|
|
78
|
+
function rowTemplate(cellColumns: string[]): string {
|
|
79
|
+
return [props.sortable ? HANDLE_COLUMN : "", ...cellColumns, REMOVE_COLUMN]
|
|
80
|
+
.filter(Boolean)
|
|
81
|
+
.join(" ");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Below 480px of the field's own width (a drawer, a phone), columns side by
|
|
85
|
+
// side would each be a few characters wide: each row stacks its cells in one
|
|
86
|
+
// column instead, a labelled field under another, as a card, and the column
|
|
87
|
+
// heads hide.
|
|
88
|
+
const STACKED_HIDDEN_CLASS = "@max-[30rem]:hidden";
|
|
89
|
+
const STACKED_ROW_CLASSES = {
|
|
90
|
+
row: "@max-[30rem]:grid-cols-(--dms-repeater-stacked-columns) @max-[30rem]:items-start @max-[30rem]:rounded-md @max-[30rem]:border @max-[30rem]:border-default @max-[30rem]:p-2.5",
|
|
91
|
+
// Side by side, each cell is a column of the row itself.
|
|
92
|
+
cells: "contents @max-[30rem]:grid @max-[30rem]:min-w-0 @max-[30rem]:gap-2.5",
|
|
93
|
+
cell: "grid min-w-0 gap-1",
|
|
94
|
+
label: `${EYEBROW_CLASS} text-dimmed hidden truncate @max-[30rem]:block`,
|
|
95
|
+
};
|
|
75
96
|
const gridTemplate = computed(() => ({
|
|
76
|
-
|
|
77
|
-
props.
|
|
78
|
-
...props.columns.map(
|
|
97
|
+
"--dms-repeater-columns": rowTemplate(
|
|
98
|
+
props.columns.map(
|
|
79
99
|
(column) => COLUMN_WIDTHS[column.type ?? ""] ?? FLUID_COLUMN,
|
|
80
100
|
),
|
|
81
|
-
|
|
82
|
-
]
|
|
83
|
-
.filter(Boolean)
|
|
84
|
-
.join(" "),
|
|
101
|
+
),
|
|
102
|
+
"--dms-repeater-stacked-columns": rowTemplate([FLUID_COLUMN]),
|
|
85
103
|
}));
|
|
86
104
|
|
|
87
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Sets the rows; `shouldValidate: false` leaves the form's validation alone: a row
|
|
107
|
+
* just added is empty, and is only checked once filled or on submit, never
|
|
108
|
+
* flagged "required" the moment it appears.
|
|
109
|
+
*/
|
|
110
|
+
function emitRows(next: RepeaterRow[], shouldValidate = true): void {
|
|
88
111
|
model.value = next;
|
|
89
|
-
emitFormChange();
|
|
112
|
+
if (shouldValidate) emitFormChange();
|
|
90
113
|
}
|
|
91
114
|
|
|
92
115
|
function setCell(index: number, column: string, value: unknown): void {
|
|
@@ -99,7 +122,7 @@ function setCell(index: number, column: string, value: unknown): void {
|
|
|
99
122
|
|
|
100
123
|
function addRow(): void {
|
|
101
124
|
rowKeys.value.push(nextKey++);
|
|
102
|
-
emitRows([...rows.value, {}]);
|
|
125
|
+
emitRows([...rows.value, {}], false);
|
|
103
126
|
}
|
|
104
127
|
|
|
105
128
|
function removeRow(index: number): void {
|
|
@@ -176,10 +199,21 @@ const hasHeaders = computed(() => props.columns.some((column) => column.label));
|
|
|
176
199
|
</script>
|
|
177
200
|
|
|
178
201
|
<template>
|
|
179
|
-
|
|
202
|
+
<!-- Read-only, the rows are values to read, not controls: a table. -->
|
|
203
|
+
<DmsRepeaterTable
|
|
204
|
+
v-if="props.disabled"
|
|
205
|
+
:id="props.id"
|
|
206
|
+
:columns="props.columns"
|
|
207
|
+
:rows="rows"
|
|
208
|
+
/>
|
|
209
|
+
<div v-else :id="props.id" class="@container grid gap-1.5" role="group">
|
|
180
210
|
<div
|
|
181
211
|
v-if="hasHeaders && rows.length"
|
|
182
|
-
:class="[
|
|
212
|
+
:class="[
|
|
213
|
+
EYEBROW_CLASS,
|
|
214
|
+
'text-dimmed grid grid-cols-(--dms-repeater-columns) items-end gap-2',
|
|
215
|
+
STACKED_HIDDEN_CLASS,
|
|
216
|
+
]"
|
|
183
217
|
:style="gridTemplate"
|
|
184
218
|
aria-hidden="true"
|
|
185
219
|
>
|
|
@@ -193,8 +227,11 @@ const hasHeaders = computed(() => props.columns.some((column) => column.label));
|
|
|
193
227
|
<div
|
|
194
228
|
v-for="(row, index) in rows"
|
|
195
229
|
:key="rowKeys[index]"
|
|
196
|
-
class="
|
|
197
|
-
|
|
230
|
+
:class="[
|
|
231
|
+
'grid grid-cols-(--dms-repeater-columns) items-center gap-2',
|
|
232
|
+
STACKED_ROW_CLASSES.row,
|
|
233
|
+
draggedIndex === index && 'opacity-50',
|
|
234
|
+
]"
|
|
198
235
|
:style="gridTemplate"
|
|
199
236
|
@dragover.prevent
|
|
200
237
|
@drop.prevent="onDrop(index)"
|
|
@@ -212,22 +249,35 @@ const hasHeaders = computed(() => props.columns.some((column) => column.label));
|
|
|
212
249
|
>
|
|
213
250
|
<UIcon name="i-ph-dots-six-vertical" class="size-4" />
|
|
214
251
|
</button>
|
|
215
|
-
<
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
252
|
+
<div :class="STACKED_ROW_CLASSES.cells">
|
|
253
|
+
<div
|
|
254
|
+
v-for="column in props.columns"
|
|
255
|
+
:key="column.id"
|
|
256
|
+
:class="STACKED_ROW_CLASSES.cell"
|
|
257
|
+
>
|
|
258
|
+
<span
|
|
259
|
+
v-if="column.label"
|
|
260
|
+
:class="STACKED_ROW_CLASSES.label"
|
|
261
|
+
aria-hidden="true"
|
|
262
|
+
>
|
|
263
|
+
{{ column.label }}
|
|
264
|
+
</span>
|
|
265
|
+
<Component
|
|
266
|
+
:is="cellComponent(column)"
|
|
267
|
+
:id="cellName(index, column.id)"
|
|
268
|
+
:model-value="row[column.id]"
|
|
269
|
+
size="sm"
|
|
270
|
+
class="w-full min-w-0"
|
|
271
|
+
:disabled="props.disabled"
|
|
272
|
+
:aria-label="column.label"
|
|
273
|
+
:aria-invalid="isCellInvalid(index, column.id) || undefined"
|
|
274
|
+
:color="isCellInvalid(index, column.id) ? 'error' : undefined"
|
|
275
|
+
:highlight="isCellInvalid(index, column.id) || undefined"
|
|
276
|
+
v-bind="column.component.options || {}"
|
|
277
|
+
@update:model-value="setCell(index, column.id, $event)"
|
|
278
|
+
/>
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
231
281
|
<UButton
|
|
232
282
|
icon="i-ph-x"
|
|
233
283
|
color="neutral"
|