@conduction/nextcloud-vue 2.79.0 → 2.81.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/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js +3 -1
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +32 -3
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +13 -11
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +80 -4
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +95 -91
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +179 -13
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +52 -16
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +96 -4
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +158 -110
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js +10 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js +252 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js +140 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/index.js +26 -0
- package/dist/esm/components/CnChipsFieldWidget/index.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js +10 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js +354 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js +172 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/index.js +27 -0
- package/dist/esm/components/CnChoicePickerWidget/index.js.map +1 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +2 -2
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +65 -5
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +19 -11
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +4 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +134 -4
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +21 -4
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +24 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +43 -4
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +1 -1
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js +7 -7
- package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +11 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +867 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +238 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/index.js +30 -0
- package/dist/esm/components/CnFactsWidget/index.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +10 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +626 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +396 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/index.js +29 -0
- package/dist/esm/components/CnFieldListWidget/index.js.map +1 -0
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +302 -4
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +417 -244
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js +101 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +141 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +65 -17
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +4 -4
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +78 -3
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue3.js +74 -49
- package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +333 -73
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +79 -17
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +10 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js.map +1 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +99 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +48 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -0
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +7 -7
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +1 -1
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +65 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +63 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +8 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +14 -0
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js +5 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js +29 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js +10 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js +174 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js +75 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/index.js +28 -0
- package/dist/esm/components/CnRadioCardsWidget/index.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +10 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +411 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +178 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/index.js +31 -0
- package/dist/esm/components/CnRecordListWidget/index.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js +10 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js +222 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js +64 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/index.js +30 -0
- package/dist/esm/components/CnSavedViewsWidget/index.js.map +1 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js +10 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js +351 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js +71 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSectionsWidget/index.js +35 -0
- package/dist/esm/components/CnSectionsWidget/index.js.map +1 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js +10 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js +347 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js +184 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/index.js +27 -0
- package/dist/esm/components/CnSettingsFormWidget/index.js.map +1 -0
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +81 -2
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +87 -6
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +1 -0
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +84 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +85 -41
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js +10 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js +159 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js +60 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/index.js +26 -0
- package/dist/esm/components/CnSwitchRowsWidget/index.js.map +1 -0
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +168 -7
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +86 -23
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +80 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +75 -32
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +10 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +269 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js +106 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/index.js +27 -0
- package/dist/esm/components/CnToggleTableWidget/index.js.map +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +11 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +7 -2
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
- package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +301 -6
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +153 -55
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
- package/dist/esm/composables/useFormDraft.js +140 -9
- package/dist/esm/composables/useFormDraft.js.map +1 -1
- package/dist/esm/composables/useListView.js +4 -4
- package/dist/esm/composables/useListView.js.map +1 -1
- package/dist/esm/index.js +21 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js +28 -20
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js +9 -0
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection.js +21 -0
- package/dist/esm/integrations/builtin/field-inspection.js.map +1 -1
- package/dist/esm/integrations/offline/checklistSubmission.js +141 -0
- package/dist/esm/integrations/offline/checklistSubmission.js.map +1 -0
- package/dist/esm/integrations/offline/fieldCollectionHelpers.js +70 -5
- package/dist/esm/integrations/offline/fieldCollectionHelpers.js.map +1 -1
- package/dist/esm/integrations/offline/index.js +2 -1
- package/dist/esm/integrations/offline/index.js.map +1 -1
- package/dist/esm/integrations/offline/offlineDb.js +6 -1
- package/dist/esm/integrations/offline/offlineDb.js.map +1 -1
- package/dist/esm/integrations/offline/syncReplayService.js +8 -2
- package/dist/esm/integrations/offline/syncReplayService.js.map +1 -1
- package/dist/esm/l10n/en.json.js +20 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +70 -2
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +2192 -222
- package/dist/esm/utils/contentHeaderLink.js +51 -0
- package/dist/esm/utils/contentHeaderLink.js.map +1 -0
- package/dist/esm/utils/detailActionModel.js +60 -3
- package/dist/esm/utils/detailActionModel.js.map +1 -1
- package/dist/esm/utils/facets.js +75 -3
- package/dist/esm/utils/facets.js.map +1 -1
- package/dist/esm/utils/fetchFilterCounts.js +40 -5
- package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
- package/dist/esm/utils/fillTemplate.js +120 -0
- package/dist/esm/utils/fillTemplate.js.map +1 -0
- package/dist/esm/utils/initials.js +32 -0
- package/dist/esm/utils/initials.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +11 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/objectWrite.js +59 -0
- package/dist/esm/utils/objectWrite.js.map +1 -0
- package/dist/esm/utils/pageControls.js +81 -0
- package/dist/esm/utils/pageControls.js.map +1 -0
- package/dist/esm/utils/personalLenses.js +40 -3
- package/dist/esm/utils/personalLenses.js.map +1 -1
- package/dist/esm/utils/queryPresets.js +96 -0
- package/dist/esm/utils/queryPresets.js.map +1 -0
- package/dist/esm/utils/resolveFilterTokens.js +12 -1
- package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
- package/dist/esm/utils/resolveManifestTokens.js +2 -0
- package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
- package/dist/esm/utils/schema.js +21 -0
- package/dist/esm/utils/schema.js.map +1 -1
- package/dist/esm/utils/sentinelTokens.js +22 -4
- package/dist/esm/utils/sentinelTokens.js.map +1 -1
- package/dist/esm/utils/userGroupsPeek.js +91 -0
- package/dist/esm/utils/userGroupsPeek.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/visibleWhen.js +57 -5
- package/dist/esm/utils/visibleWhen.js.map +1 -1
- package/dist/esm/utils/widgetDispatch.js +7 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/esm/utils/widgetVisibility.js +4 -57
- package/dist/esm/utils/widgetVisibility.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +19708 -9301
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +2192 -222
- package/l10n/en.json +20 -1
- package/l10n/nl.json +70 -2
- package/package.json +2 -1
- package/scripts/check-docs-mdx.mjs +108 -0
- package/src/components/CnActionButtons/CnActionButtons.vue +2 -0
- package/src/components/CnActionsBar/CnActionsBar.md +4 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +48 -11
- package/src/components/CnAppRoot/CnAppRoot.md +4 -2
- package/src/components/CnAppRoot/CnAppRoot.vue +151 -59
- package/src/components/CnBannerWidget/CnBannerWidget.vue +244 -23
- package/src/components/CnCellRenderer/CnCellRenderer.md +2 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +235 -5
- package/src/components/CnChipsFieldWidget/CnChipsFieldWidget.vue +433 -0
- package/src/components/CnChipsFieldWidget/index.js +25 -0
- package/src/components/CnChoicePickerWidget/CnChoicePickerWidget.vue +556 -0
- package/src/components/CnChoicePickerWidget/index.js +26 -0
- package/src/components/CnConversationThread/CnConversationWidget.vue +1 -1
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +2 -2
- package/src/components/CnDashboardPage/CnDashboardPage.vue +78 -5
- package/src/components/CnDataTable/CnDataTable.vue +22 -12
- package/src/components/CnDetailPage/CnDetailPage.md +1 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +152 -6
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +66 -0
- package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +1 -1
- package/src/components/CnFacetSidebar/CnFacetSidebar.vue +7 -7
- package/src/components/CnFactsWidget/CnFactsWidget.vue +1190 -0
- package/src/components/CnFactsWidget/index.js +29 -0
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +1008 -0
- package/src/components/CnFieldListWidget/index.js +28 -0
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +588 -117
- package/src/components/CnFilesBrowser/filesBrowser.js +100 -0
- package/src/components/CnFilesBrowser/filesBrowserColumns.js +140 -0
- package/src/components/CnFormDialog/CnFormDialog.md +9 -3
- package/src/components/CnFormDialog/CnFormDialog.vue +69 -21
- package/src/components/CnFormPage/CnFormPage.vue +99 -3
- package/src/components/CnIndexPage/CnIndexPage.vue +309 -9
- package/src/components/CnIndexPage/CnIndexPageWidgets.vue +169 -0
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +7 -7
- package/src/components/CnNextStepCard/CnNextStepWidget.vue +1 -1
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +69 -2
- package/src/components/CnObjectSidebar/CnFilesTab.vue +70 -1
- package/src/components/CnObjectSidebar/CnNotesTab.vue +16 -0
- package/src/components/CnPageRenderer/CnPageRenderer.vue +29 -1
- package/src/components/CnRadioCardsWidget/CnRadioCardsWidget.vue +313 -0
- package/src/components/CnRadioCardsWidget/index.js +27 -0
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +652 -0
- package/src/components/CnRecordListWidget/index.js +30 -0
- package/src/components/CnSavedViewsWidget/CnSavedViewsWidget.vue +296 -0
- package/src/components/CnSavedViewsWidget/index.js +29 -0
- package/src/components/CnSectionsWidget/CnSectionsWidget.vue +489 -0
- package/src/components/CnSectionsWidget/index.js +34 -0
- package/src/components/CnSegmentedControl/CnSegmentedControl.vue +13 -0
- package/src/components/CnSettingsFormWidget/CnSettingsFormWidget.vue +555 -0
- package/src/components/CnSettingsFormWidget/index.js +26 -0
- package/src/components/CnStagesWidget/CnStagesWidget.vue +81 -2
- package/src/components/CnStatWidget/CnStatWidget.vue +88 -7
- package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +180 -3
- package/src/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue +226 -0
- package/src/components/CnSwitchRowsWidget/index.js +25 -0
- package/src/components/CnTabsWidget/CnTabsWidget.vue +263 -8
- package/src/components/CnTasksWidget/CnTasksWidget.vue +175 -15
- package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +386 -0
- package/src/components/CnToggleTableWidget/index.js +26 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +11 -0
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +7 -2
- package/src/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +493 -11
- package/src/components/index.js +19 -0
- package/src/composables/__tests__/useFormDraft.spec.js +69 -0
- package/src/composables/useFormDraft.js +139 -8
- package/src/composables/useListView.js +4 -4
- package/src/css/form-field.css +14 -0
- package/src/css/index-page.css +71 -0
- package/src/css/kpi-card.css +32 -0
- package/src/css/look-board-detail.css +113 -2
- package/src/css/look-board-index.css +90 -8
- package/src/css/look-board.css +109 -2
- package/src/css/table.css +19 -0
- package/src/index.js +19 -0
- package/src/integrations/builtin/field-inspection/CnFieldInspectionCard.vue +35 -20
- package/src/integrations/builtin/field-inspection.js +21 -0
- package/src/integrations/offline/checklistSubmission.js +137 -0
- package/src/integrations/offline/fieldCollectionHelpers.js +69 -4
- package/src/integrations/offline/index.js +3 -0
- package/src/integrations/offline/offlineDb.js +6 -1
- package/src/integrations/offline/syncReplayService.js +8 -2
- package/src/schemas/app-manifest-v2.schema.json +196 -13
- package/src/utils/contentHeaderLink.js +48 -0
- package/src/utils/detailActionModel.js +59 -2
- package/src/utils/facets.js +74 -2
- package/src/utils/fetchFilterCounts.js +39 -4
- package/src/utils/fillTemplate.js +116 -0
- package/src/utils/initials.js +29 -0
- package/src/utils/libraryWidgetKeys.js +11 -0
- package/src/utils/objectWrite.js +55 -0
- package/src/utils/pageControls.js +78 -0
- package/src/utils/personalLenses.js +39 -2
- package/src/utils/queryPresets.js +93 -0
- package/src/utils/resolveFilterTokens.js +14 -1
- package/src/utils/resolveManifestTokens.js +2 -0
- package/src/utils/schema.js +21 -0
- package/src/utils/sentinelTokens.js +22 -4
- package/src/utils/userGroupsPeek.js +87 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/visibleWhen.js +56 -4
- package/src/utils/widgetDispatch.js +7 -1
- package/src/utils/widgetVisibility.js +7 -56
|
@@ -178,8 +178,10 @@ import {
|
|
|
178
178
|
readAvailableActions,
|
|
179
179
|
transitionError,
|
|
180
180
|
} from '../../composables/useLifecycleTransitions.js'
|
|
181
|
+
import { normalizeLook } from '../../composables/useLook.js'
|
|
181
182
|
import { useObjectStore } from '../../store/useObjectStore.js'
|
|
182
183
|
import { resolveObjectOpType } from '../../utils/actionsDispatcher.js'
|
|
184
|
+
import { formatBoardDate } from '../../utils/boardDate.js'
|
|
183
185
|
import { resolveObjectTokenContext } from '../../utils/detailObjectContext.js'
|
|
184
186
|
import {
|
|
185
187
|
dropOptionalUnresolved,
|
|
@@ -264,6 +266,14 @@ function userError(message) {
|
|
|
264
266
|
* labelField?, descriptionField?, finalField?, limit? }`, an OpenRegister
|
|
265
267
|
* query whose filter takes the same tokens.
|
|
266
268
|
*
|
|
269
|
+
* `datesSource`: `{ url, method?, params?, path?, idField?, dateField? }`, an
|
|
270
|
+
* endpoint (same token grammar) answering the date the record reached each
|
|
271
|
+
* stage, when the stage list does not carry it: the case type's statuses list
|
|
272
|
+
* the steps, `/blueprint?case=@objectId` says when this case reached them.
|
|
273
|
+
* Read once, merged into the steps by `idField` (default `id`) with the value
|
|
274
|
+
* at `dateField` (default `reachedAt`); a date it does not give falls back to
|
|
275
|
+
* the stage list's own `dateField`. The bars variant draws it.
|
|
276
|
+
*
|
|
267
277
|
* `finalField` marks the stages that close the record, which the timeline
|
|
268
278
|
* draws differently. It grants nothing either.
|
|
269
279
|
*
|
|
@@ -356,7 +366,7 @@ export default {
|
|
|
356
366
|
* `label`, the placement's own title, so a titled card does not have to
|
|
357
367
|
* repeat itself, and then to "Stages".
|
|
358
368
|
*
|
|
359
|
-
* @type {{currentField?: string, stagesEndpoint?: object, stagesSource?: object, transition?: object, unreachableReason?: string, orientation?: ('horizontal'|'vertical'), size?: ('medium'|'small'), ariaLabel?: string, label?: string, variant?: ('dots'|'bars')}}
|
|
369
|
+
* @type {{currentField?: string, stagesEndpoint?: object, stagesSource?: object, transition?: object, unreachableReason?: string, orientation?: ('horizontal'|'vertical'), size?: ('medium'|'small'), ariaLabel?: string, label?: string, variant?: ('dots'|'bars'), datesSource?: {url: string, method?: string, params?: object, path?: string, idField?: string, dateField?: string}}}
|
|
360
370
|
*/
|
|
361
371
|
content: {
|
|
362
372
|
type: Object,
|
|
@@ -437,6 +447,8 @@ export default {
|
|
|
437
447
|
const workspaceRaw = inject('cnWorkspaceContext', ref({}))
|
|
438
448
|
const appConfigRaw = inject('cnAppConfig', ref({}))
|
|
439
449
|
const cnTranslate = inject('cnTranslate', (key) => key)
|
|
450
|
+
// The app's look (`nextcloud` or `board`), provided by CnAppRoot or CnPageRenderer.
|
|
451
|
+
const cnLook = inject('cnLook', 'nextcloud')
|
|
440
452
|
|
|
441
453
|
/**
|
|
442
454
|
* The token context: the bound record, its id, and the page context.
|
|
@@ -462,15 +474,24 @@ export default {
|
|
|
462
474
|
return (cfg && cfg.url) ? { url: cfg.url, method: cfg.method, params: cfg.params } : null
|
|
463
475
|
}, { ctx: tokenCtx })
|
|
464
476
|
|
|
477
|
+
// The dates the record reached each stage, when they live apart from
|
|
478
|
+
// the stage list (`datesSource`): one read, merged into the steps by id.
|
|
479
|
+
const datesRead = useEndpointSource(() => {
|
|
480
|
+
const cfg = props.content?.datesSource
|
|
481
|
+
return (cfg && cfg.url) ? { url: cfg.url, method: cfg.method, params: cfg.params } : null
|
|
482
|
+
}, { ctx: tokenCtx })
|
|
483
|
+
|
|
465
484
|
return {
|
|
466
485
|
detailCtxRaw,
|
|
467
486
|
objectCtxRaw,
|
|
468
487
|
cnTranslate,
|
|
488
|
+
cnLook,
|
|
469
489
|
tokenCtx,
|
|
470
490
|
stagesBody: stagesRead.data,
|
|
471
491
|
stagesBodyLoading: stagesRead.loading,
|
|
472
492
|
stagesBodyError: stagesRead.error,
|
|
473
493
|
stagesBodyBlocked: stagesRead.blocked,
|
|
494
|
+
datesBody: datesRead.data,
|
|
474
495
|
}
|
|
475
496
|
},
|
|
476
497
|
|
|
@@ -614,6 +635,42 @@ export default {
|
|
|
614
635
|
return normalizeStages(rows, { ...cfg, orderField })
|
|
615
636
|
},
|
|
616
637
|
|
|
638
|
+
/**
|
|
639
|
+
* The reached dates by stage id, from `datesSource`: the rows at its
|
|
640
|
+
* `path` (or the response itself when it is a list), keyed by
|
|
641
|
+
* `idField` (default `id`), the value at `dateField` (default
|
|
642
|
+
* `reachedAt`). Empty without the option, while it loads, or when it
|
|
643
|
+
* failed: the steps then show the stage list's own dates.
|
|
644
|
+
*
|
|
645
|
+
* @return {{[id: string]: string}} Date by stage id.
|
|
646
|
+
* @spec openspec/changes/screens-stage-dates-source/specs/stage-dates-source/spec.md#requirement-the-reached-dates-can-come-from-their-own-endpoint
|
|
647
|
+
*/
|
|
648
|
+
stageDates() {
|
|
649
|
+
const cfg = this.content.datesSource
|
|
650
|
+
const body = this.datesBody
|
|
651
|
+
if (!cfg || !cfg.url || body === null || body === undefined) {
|
|
652
|
+
return {}
|
|
653
|
+
}
|
|
654
|
+
const rows = Array.isArray(body) ? body : getByPath(body, cfg.path || '')
|
|
655
|
+
if (!Array.isArray(rows)) {
|
|
656
|
+
return {}
|
|
657
|
+
}
|
|
658
|
+
const idField = cfg.idField || 'id'
|
|
659
|
+
const dateField = cfg.dateField || 'reachedAt'
|
|
660
|
+
const out = {}
|
|
661
|
+
for (const row of rows) {
|
|
662
|
+
if (!row || typeof row !== 'object') {
|
|
663
|
+
continue
|
|
664
|
+
}
|
|
665
|
+
const id = getByPath(row, idField)
|
|
666
|
+
const date = getByPath(row, dateField)
|
|
667
|
+
if (id !== null && id !== undefined && id !== '' && typeof date === 'string' && date !== '') {
|
|
668
|
+
out[String(id)] = date
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return out
|
|
672
|
+
},
|
|
673
|
+
|
|
617
674
|
/**
|
|
618
675
|
* Whether the stage list is still on its way.
|
|
619
676
|
*
|
|
@@ -795,7 +852,7 @@ export default {
|
|
|
795
852
|
}
|
|
796
853
|
return {
|
|
797
854
|
...stage,
|
|
798
|
-
date: this.stages[index]?.date || '',
|
|
855
|
+
date: this.barDate(this.stageDates[stage.id] || this.stages[index]?.date || '', state),
|
|
799
856
|
state,
|
|
800
857
|
clickable: this.interactive && state !== 'current',
|
|
801
858
|
}
|
|
@@ -891,6 +948,28 @@ export default {
|
|
|
891
948
|
},
|
|
892
949
|
|
|
893
950
|
methods: {
|
|
951
|
+
/**
|
|
952
|
+
* A bar's date line. Under the board look a date value reads in the
|
|
953
|
+
* board's short form ("3 okt"), and the current step's reads "sinds
|
|
954
|
+
* 4 okt", as DqZaak draws it. Any other value, and every value outside
|
|
955
|
+
* the board look, shows as written.
|
|
956
|
+
*
|
|
957
|
+
* @param {string} value The `dateField` value of the stage row.
|
|
958
|
+
* @param {string} state The bar state (`done`, `current`, `todo`).
|
|
959
|
+
* @return {string} The date line, or ''.
|
|
960
|
+
* @spec openspec/changes/screens-stage-bar-dates-parity/specs/stage-bar-dates/spec.md#requirement-a-bar-date-reads-as-the-board-draws-it
|
|
961
|
+
*/
|
|
962
|
+
barDate(value, state) {
|
|
963
|
+
if (!value || normalizeLook(this.cnLook) !== 'board') {
|
|
964
|
+
return value
|
|
965
|
+
}
|
|
966
|
+
const short = formatBoardDate(value)
|
|
967
|
+
if (short === '') {
|
|
968
|
+
return value
|
|
969
|
+
}
|
|
970
|
+
return state === 'current' ? t('nextcloud-vue', 'since {date}', { date: short }) : short
|
|
971
|
+
},
|
|
972
|
+
|
|
894
973
|
/**
|
|
895
974
|
* Translate a library string.
|
|
896
975
|
*
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
aria-hidden="true">
|
|
47
47
|
<CnWidgetIcon :name="resolvedIcon" :size="18" />
|
|
48
48
|
</span>
|
|
49
|
-
<span v-if="content.label" :title="resolvedLabel">{{ resolvedLabel }}</span>
|
|
49
|
+
<span v-if="content.label" class="cn-kpi-card__title-text" :title="resolvedLabel">{{ resolvedLabel }}</span>
|
|
50
50
|
|
|
51
51
|
<!-- Per-tile range override. Rendered ONLY when the tile declares its
|
|
52
52
|
own `content.dateRange.presets`; a tile that merely follows the
|
|
@@ -163,6 +163,7 @@ import widgetLink from '../../mixins/widgetLink.js'
|
|
|
163
163
|
import { useObjectStore } from '../../store/useObjectStore.js'
|
|
164
164
|
import { resolveObjectOpType } from '../../utils/actionsDispatcher.js'
|
|
165
165
|
import { resolveObjectTokenContext } from '../../utils/detailObjectContext.js'
|
|
166
|
+
import { fetchListTotal, needsListEndpoint } from '../../utils/fetchFilterCounts.js'
|
|
166
167
|
import { formatMetricValue, unwrapAppConfig } from '../../utils/formatMetric.js'
|
|
167
168
|
import { dropOptionalUnresolved, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
168
169
|
import { STATUS_TEXT_COLORS } from '../../utils/statusColors.js'
|
|
@@ -1746,8 +1747,7 @@ export default {
|
|
|
1746
1747
|
// drop any optional `@workspace.<key>?` / `@config.<key>?` that stayed
|
|
1747
1748
|
// unresolved so an unset value omits the filter (show all) instead of
|
|
1748
1749
|
// sending a literal token.
|
|
1749
|
-
|
|
1750
|
-
filter = dropOptionalUnresolved(resolveFilterTokens(filter, ctx))
|
|
1750
|
+
filter = this.resolveSourceFilter(filter)
|
|
1751
1751
|
for (const [k, v] of Object.entries(filter)) {
|
|
1752
1752
|
if (v && typeof v === 'object') {
|
|
1753
1753
|
for (const [op, ov] of Object.entries(v)) {
|
|
@@ -1759,6 +1759,21 @@ export default {
|
|
|
1759
1759
|
}
|
|
1760
1760
|
},
|
|
1761
1761
|
|
|
1762
|
+
/**
|
|
1763
|
+
* A source filter with its @-tokens resolved against the object, page
|
|
1764
|
+
* and config contexts, and unresolved optional clauses dropped.
|
|
1765
|
+
*
|
|
1766
|
+
* @param {object} filter The raw filter map.
|
|
1767
|
+
* @return {object} The resolved filter.
|
|
1768
|
+
*/
|
|
1769
|
+
resolveSourceFilter(filter) {
|
|
1770
|
+
if (!filter || typeof filter !== 'object') {
|
|
1771
|
+
return {}
|
|
1772
|
+
}
|
|
1773
|
+
const ctx = { ...(this.objectCtx || {}), workspace: this.pageCtx, config: this.configCtx }
|
|
1774
|
+
return dropOptionalUnresolved(resolveFilterTokens(filter, ctx))
|
|
1775
|
+
},
|
|
1776
|
+
|
|
1762
1777
|
/**
|
|
1763
1778
|
* Fetch one scalar from the OpenRegister `/value` aggregation endpoint.
|
|
1764
1779
|
*
|
|
@@ -1768,9 +1783,19 @@ export default {
|
|
|
1768
1783
|
* @param {string} metric The aggregation metric.
|
|
1769
1784
|
* @param {?string} field The numeric field (non-count metrics).
|
|
1770
1785
|
* @param {object} filter The filter map.
|
|
1786
|
+
* @param {string} [via] `"list"` counts through the list endpoint's total.
|
|
1771
1787
|
* @return {Promise<number|null>} The aggregated value.
|
|
1772
1788
|
*/
|
|
1773
|
-
async fetchAggregate(axios, generateUrl, s, metric, field, filter) {
|
|
1789
|
+
async fetchAggregate(axios, generateUrl, s, metric, field, filter, via) {
|
|
1790
|
+
// A count the aggregation endpoint cannot express (a filter holding a
|
|
1791
|
+
// list of values, a test for no value (`null` or `{ isnull: true }`),
|
|
1792
|
+
// or `via: "list"`) reads the list endpoint's total instead, because
|
|
1793
|
+
// the aggregation endpoint has no null operator.
|
|
1794
|
+
const resolved = this.resolveSourceFilter(filter)
|
|
1795
|
+
const listed = Object.values(resolved).some((v) => Array.isArray(v) || (v && typeof v === 'object' && Object.values(v).some(Array.isArray)))
|
|
1796
|
+
if ((!metric || metric === 'count') && (via === 'list' || listed || needsListEndpoint(resolved))) {
|
|
1797
|
+
return fetchListTotal(s.register, s.schema, resolved, {})
|
|
1798
|
+
}
|
|
1774
1799
|
const url = generateUrl(
|
|
1775
1800
|
'/apps/openregister/api/objects/aggregations/{register}/{schema}/value',
|
|
1776
1801
|
{ register: s.register, schema: s.schema },
|
|
@@ -1827,9 +1852,15 @@ export default {
|
|
|
1827
1852
|
if (s.kind === 'endpoint') {
|
|
1828
1853
|
this.value = await this.fetchEndpoint(s, refresh)
|
|
1829
1854
|
} else if (s.kind === 'ratio') {
|
|
1830
|
-
|
|
1831
|
-
|
|
1855
|
+
// Each part may name its own metric, field and `via`, so "answered
|
|
1856
|
+
// in time" over "answered" is two counts, and a share of a sum is
|
|
1857
|
+
// two sums.
|
|
1858
|
+
const part = (p) => this.fetchAggregate(axios, generateUrl, s, (p && p.metric) || s.metric, (p && p.field) || s.field, (p && p.filter) || {}, (p && p.via) || s.via)
|
|
1859
|
+
const num = await part(s.numerator)
|
|
1860
|
+
const den = await part(s.denominator)
|
|
1832
1861
|
this.value = (den && Number(den) !== 0) ? (Number(num) / Number(den)) * 100 : null
|
|
1862
|
+
} else if (s.kind === 'elapsed') {
|
|
1863
|
+
this.value = await this.fetchElapsed(axios, generateUrl, s)
|
|
1833
1864
|
} else if (s.kind === 'computed') {
|
|
1834
1865
|
// Fetch each named part, then evaluate the formula over them.
|
|
1835
1866
|
const { evalFormula } = await import('../../utils/evalFormula.js')
|
|
@@ -1842,7 +1873,7 @@ export default {
|
|
|
1842
1873
|
} else if (s.kind === 'weighted') {
|
|
1843
1874
|
this.value = await this.fetchWeighted(axios, generateUrl, s)
|
|
1844
1875
|
} else {
|
|
1845
|
-
this.value = await this.fetchAggregate(axios, generateUrl, s, s.metric, s.field, s.filter || {})
|
|
1876
|
+
this.value = await this.fetchAggregate(axios, generateUrl, s, s.metric, s.field, s.filter || {}, s.via)
|
|
1846
1877
|
}
|
|
1847
1878
|
} catch (e) {
|
|
1848
1879
|
this.error = (e && e.message) || 'error'
|
|
@@ -1852,6 +1883,56 @@ export default {
|
|
|
1852
1883
|
}
|
|
1853
1884
|
},
|
|
1854
1885
|
|
|
1886
|
+
/**
|
|
1887
|
+
* The average time between two date fields of the matching objects
|
|
1888
|
+
* (`{ from, to, unit?, filter?, limit? }`): an average answer time is
|
|
1889
|
+
* `occurredAt` to `resolvedAt`. Computed client-side over the fetched
|
|
1890
|
+
* objects (capped at `limit`, default 1000), because the aggregation
|
|
1891
|
+
* endpoint averages one numeric field, not a difference. Objects missing
|
|
1892
|
+
* either date, or with `to` before `from`, are left out; null when none
|
|
1893
|
+
* is left. `unit` is `days` (default), `hours` or `minutes`.
|
|
1894
|
+
*
|
|
1895
|
+
* @param {object} axios The axios instance.
|
|
1896
|
+
* @param {(url: string, params?: object) => string} generateUrl The router helper.
|
|
1897
|
+
* @param {object} s The elapsed source.
|
|
1898
|
+
* @return {Promise<number|null>} The average, in `unit`.
|
|
1899
|
+
* @spec openspec/changes/screens-ratio-stat/specs/ratio-stat/spec.md#requirement-a-stat-can-average-the-time-between-two-dates
|
|
1900
|
+
*/
|
|
1901
|
+
async fetchElapsed(axios, generateUrl, s) {
|
|
1902
|
+
if (!s.from || !s.to) {
|
|
1903
|
+
return null
|
|
1904
|
+
}
|
|
1905
|
+
const url = generateUrl(
|
|
1906
|
+
'/apps/openregister/api/objects/{register}/{schema}',
|
|
1907
|
+
{ register: s.register, schema: s.schema },
|
|
1908
|
+
)
|
|
1909
|
+
const params = { _limit: s.limit || 1000 }
|
|
1910
|
+
const filter = this.resolveSourceFilter(s.filter || {})
|
|
1911
|
+
for (const [k, v] of Object.entries(filter)) {
|
|
1912
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
1913
|
+
for (const [op, operand] of Object.entries(v)) {
|
|
1914
|
+
params[`${k}[${op}]`] = operand
|
|
1915
|
+
}
|
|
1916
|
+
} else if (v !== '' && v !== null && v !== undefined) {
|
|
1917
|
+
params[k] = v
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
const res = await axios.get(url, { params })
|
|
1921
|
+
const rows = (res && res.data && res.data.results) || []
|
|
1922
|
+
const per = { minutes: 60000, hours: 3600000, days: 86400000 }[s.unit] || 86400000
|
|
1923
|
+
let sum = 0
|
|
1924
|
+
let n = 0
|
|
1925
|
+
for (const r of rows) {
|
|
1926
|
+
const from = Date.parse(r && r[s.from])
|
|
1927
|
+
const to = Date.parse(r && r[s.to])
|
|
1928
|
+
if (Number.isFinite(from) && Number.isFinite(to) && to >= from) {
|
|
1929
|
+
sum += (to - from) / per
|
|
1930
|
+
n += 1
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
return n > 0 ? sum / n : null
|
|
1934
|
+
},
|
|
1935
|
+
|
|
1855
1936
|
/**
|
|
1856
1937
|
* Compute a weighted sum `Σ (field × weightField) ÷ divisor` client-side
|
|
1857
1938
|
* (no OpenRegister expression-aggregation primitive yet). Pulls the
|
|
@@ -17,10 +17,42 @@
|
|
|
17
17
|
<template>
|
|
18
18
|
<div
|
|
19
19
|
class="cn-stats-block-widget"
|
|
20
|
-
:class="[iconClass, { 'cn-stats-block-widget--multi': hasEntries }]">
|
|
20
|
+
:class="[iconClass, { 'cn-stats-block-widget--multi': hasEntries && !isRows, 'cn-stats-block-widget--rows': isRows }]">
|
|
21
|
+
<!-- Rows layout: a title and one label-left, number-right row per entry
|
|
22
|
+
(the board's "Mijn Zuiddrecht nu" card). -->
|
|
23
|
+
<template v-if="isRows">
|
|
24
|
+
<h2 v-if="title" class="cn-stats-block-widget__rows-title" data-testid="cn-stats-block-rows-title">
|
|
25
|
+
{{ tl(title) }}
|
|
26
|
+
</h2>
|
|
27
|
+
<dl class="cn-stats-block-widget__rows" data-testid="cn-stats-block-rows">
|
|
28
|
+
<div
|
|
29
|
+
v-for="view in rowViews"
|
|
30
|
+
:key="view.key"
|
|
31
|
+
class="cn-stats-block-widget__row"
|
|
32
|
+
data-testid="cn-stats-block-row">
|
|
33
|
+
<dt class="cn-stats-block-widget__row-label">
|
|
34
|
+
<a
|
|
35
|
+
v-if="view.href"
|
|
36
|
+
:href="view.href"
|
|
37
|
+
data-testid="cn-stats-block-row-link"
|
|
38
|
+
@click="onRowClick($event, view.route)">{{ view.label }}</a>
|
|
39
|
+
<template v-else>
|
|
40
|
+
{{ view.label }}
|
|
41
|
+
</template>
|
|
42
|
+
</dt>
|
|
43
|
+
<dd
|
|
44
|
+
class="cn-stats-block-widget__row-value"
|
|
45
|
+
:class="'cn-stats-block-widget__row-value--' + view.variant"
|
|
46
|
+
data-testid="cn-stats-block-row-value">
|
|
47
|
+
{{ view.display }}
|
|
48
|
+
</dd>
|
|
49
|
+
</div>
|
|
50
|
+
</dl>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
21
53
|
<!-- Multi-entry mode: one KPI per entry, all inside this widget card.
|
|
22
54
|
An entry with hideWhenZero whose resolved count is 0 is omitted. -->
|
|
23
|
-
<template v-if="hasEntries">
|
|
55
|
+
<template v-else-if="hasEntries">
|
|
24
56
|
<CnStatsBlock
|
|
25
57
|
v-for="view in entryViews"
|
|
26
58
|
:key="view.key"
|
|
@@ -71,6 +103,7 @@ import { subscribe, unsubscribe } from '@nextcloud/event-bus'
|
|
|
71
103
|
import CnStatsBlock from '../CnStatsBlock/CnStatsBlock.vue'
|
|
72
104
|
import { useDataSource } from '../../composables/useDataSource.js'
|
|
73
105
|
import { formatMetricValue, normalizeMetricFormat, unwrapAppConfig } from '../../utils/formatMetric.js'
|
|
106
|
+
import { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'
|
|
74
107
|
import { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
75
108
|
|
|
76
109
|
// The canonical KPI scale (`--cn-kpi-*`) lives in one stylesheet. Imported
|
|
@@ -167,6 +200,8 @@ export default {
|
|
|
167
200
|
* currency (`currency`) and `@config.<key>` tokens in `format`.
|
|
168
201
|
*/
|
|
169
202
|
cnAppConfig: { default: () => ({}) },
|
|
203
|
+
/** The host's label lookup (CnAppRoot provides it); identity without one. */
|
|
204
|
+
cnTranslate: { default: () => (key) => key },
|
|
170
205
|
},
|
|
171
206
|
|
|
172
207
|
props: {
|
|
@@ -264,7 +299,10 @@ export default {
|
|
|
264
299
|
|
|
265
300
|
/**
|
|
266
301
|
* `stacked` gives every block the board look (see CnStatsBlock
|
|
267
|
-
* `layout`).
|
|
302
|
+
* `layout`). `rows` draws the title and then one row per entry (or the
|
|
303
|
+
* single source): the label on the left, the number on the right, the
|
|
304
|
+
* label a link when the entry has a `route`. Empty (the default) keeps
|
|
305
|
+
* the KPI card.
|
|
268
306
|
*
|
|
269
307
|
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-stats-block-can-take-the-stacked-board-look
|
|
270
308
|
*/
|
|
@@ -353,6 +391,52 @@ export default {
|
|
|
353
391
|
return Array.isArray(this.entries) && this.entries.length > 0
|
|
354
392
|
},
|
|
355
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Whether the widget draws label/number rows (`layout: "rows"`).
|
|
396
|
+
*
|
|
397
|
+
* @return {boolean}
|
|
398
|
+
*/
|
|
399
|
+
isRows() {
|
|
400
|
+
return this.layout === 'rows'
|
|
401
|
+
},
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* The rows of the rows layout: one per entry, or a single row from the
|
|
405
|
+
* `dataSource` (labelled by `countLabel`). A row is `{ key, label,
|
|
406
|
+
* display, variant, route, href }`; `display` is the formatted value,
|
|
407
|
+
* a dash while it loads or when the count could not be read.
|
|
408
|
+
*
|
|
409
|
+
* @return {Array<object>}
|
|
410
|
+
*/
|
|
411
|
+
rowViews() {
|
|
412
|
+
if (!this.hasEntries) {
|
|
413
|
+
const formatted = this.formattedSingle
|
|
414
|
+
return [{
|
|
415
|
+
key: 'single',
|
|
416
|
+
label: this.tl(this.countLabel),
|
|
417
|
+
display: this.loading ? '\u2014' : (formatted !== null ? formatted : this.formatCount(this.resolvedCount)),
|
|
418
|
+
variant: this.variant || 'default',
|
|
419
|
+
route: this.route,
|
|
420
|
+
href: this.route ? resolveHref(this.route, this.$router) : '',
|
|
421
|
+
}]
|
|
422
|
+
}
|
|
423
|
+
return this.entryViews.map((view) => {
|
|
424
|
+
const resolved = view.resolved === true
|
|
425
|
+
let display = '\u2014'
|
|
426
|
+
if (resolved) {
|
|
427
|
+
display = view.formatted !== null ? view.formatted : this.formatCount(view.count)
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
key: view.key,
|
|
431
|
+
label: this.tl(view.entry.title || ''),
|
|
432
|
+
display,
|
|
433
|
+
variant: view.entry.variant || 'default',
|
|
434
|
+
route: view.route,
|
|
435
|
+
href: view.route ? resolveHref(view.route, this.$router) : '',
|
|
436
|
+
}
|
|
437
|
+
})
|
|
438
|
+
},
|
|
439
|
+
|
|
356
440
|
/**
|
|
357
441
|
* The unwrapped detail-page object context for token resolution, or
|
|
358
442
|
* null on surfaces (dashboards) that don't provide one.
|
|
@@ -566,6 +650,37 @@ export default {
|
|
|
566
650
|
},
|
|
567
651
|
|
|
568
652
|
methods: {
|
|
653
|
+
/**
|
|
654
|
+
* A manifest string through the host's label lookup.
|
|
655
|
+
*
|
|
656
|
+
* @param {string} text The text as written in the manifest.
|
|
657
|
+
* @return {string} The translated text.
|
|
658
|
+
*/
|
|
659
|
+
tl(text) {
|
|
660
|
+
return typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text
|
|
661
|
+
},
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* A count in the user's locale.
|
|
665
|
+
*
|
|
666
|
+
* @param {number} count The count.
|
|
667
|
+
* @return {string} The localized count.
|
|
668
|
+
*/
|
|
669
|
+
formatCount(count) {
|
|
670
|
+
return new Intl.NumberFormat().format(count)
|
|
671
|
+
},
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Follow a row label's link in the router, leaving a modified click to the browser.
|
|
675
|
+
*
|
|
676
|
+
* @param {MouseEvent} event The click.
|
|
677
|
+
* @param {object} route The route location.
|
|
678
|
+
* @return {void}
|
|
679
|
+
*/
|
|
680
|
+
onRowClick(event, route) {
|
|
681
|
+
followLinkClick(event, route, this.$router)
|
|
682
|
+
},
|
|
683
|
+
|
|
569
684
|
/**
|
|
570
685
|
* Re-query the tile's counts — the REST `/value` aggregation, the
|
|
571
686
|
* per-entry counts of multi-entry mode, and the `dataSource` GraphQL
|
|
@@ -758,4 +873,66 @@ export default {
|
|
|
758
873
|
flex-direction: column;
|
|
759
874
|
gap: var(--cn-kpi-stack-gap, 12px);
|
|
760
875
|
}
|
|
876
|
+
|
|
877
|
+
/* Rows layout: the board's "label left, number right" card body. */
|
|
878
|
+
.cn-stats-block-widget--rows {
|
|
879
|
+
display: flex;
|
|
880
|
+
flex-direction: column;
|
|
881
|
+
gap: 12px;
|
|
882
|
+
box-sizing: border-box;
|
|
883
|
+
padding: 20px 22px;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
.cn-stats-block-widget__rows-title {
|
|
887
|
+
margin: 0;
|
|
888
|
+
font-size: 15px;
|
|
889
|
+
font-weight: 600;
|
|
890
|
+
color: var(--color-text-maxcontrast);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
.cn-stats-block-widget__rows {
|
|
894
|
+
display: flex;
|
|
895
|
+
flex-direction: column;
|
|
896
|
+
gap: 8px;
|
|
897
|
+
margin: 0;
|
|
898
|
+
font-size: 14px;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
.cn-stats-block-widget__row {
|
|
902
|
+
display: flex;
|
|
903
|
+
align-items: baseline;
|
|
904
|
+
justify-content: space-between;
|
|
905
|
+
gap: 16px;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
.cn-stats-block-widget__row-label {
|
|
909
|
+
min-width: 0;
|
|
910
|
+
margin: 0;
|
|
911
|
+
overflow: hidden;
|
|
912
|
+
text-overflow: ellipsis;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
.cn-stats-block-widget__row-value {
|
|
916
|
+
flex: none;
|
|
917
|
+
margin: 0;
|
|
918
|
+
font-size: 17px;
|
|
919
|
+
font-weight: 700;
|
|
920
|
+
text-align: right;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
.cn-stats-block-widget__row-value--success {
|
|
924
|
+
color: var(--color-text-success, var(--color-success-text));
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
.cn-stats-block-widget__row-value--warning {
|
|
928
|
+
color: var(--color-text-warning, var(--color-warning-text));
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
.cn-stats-block-widget__row-value--error {
|
|
932
|
+
color: var(--color-text-error, var(--color-error-text));
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
.cn-stats-block-widget__row-value--primary {
|
|
936
|
+
color: var(--color-primary-element);
|
|
937
|
+
}
|
|
761
938
|
</style>
|