@conduction/nextcloud-vue 2.56.0-beta.3 → 2.56.0-beta.5
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/CnActionsBar/CnActionsBar.vue2.js +12 -2
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +1 -2
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +0 -3
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -2
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +21 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +12 -5
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +2 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +9 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +6 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +116 -15
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +27 -14
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js +13 -6
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js.map +1 -1
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js +2 -0
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js +1 -2
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +3 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +17 -17
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js +4 -21
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +0 -13
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js +8 -8
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js +3 -2
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +67 -13
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +15 -8
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/manifestActionDispatch.js +31 -11
- package/dist/esm/components/CnIndexPage/manifestActionDispatch.js.map +1 -1
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue2.js.map +1 -1
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js +1 -2
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js +0 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js +2 -17
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js +35 -11
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js +3 -2
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +23 -5
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectList/CnObjectList.vue2.js +19 -3
- package/dist/esm/components/CnObjectList/CnObjectList.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectList/CnObjectList.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +45 -15
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +24 -6
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +0 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js +4 -2
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +7 -2
- package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
- package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +49 -5
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js +5 -3
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +3 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +1 -0
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js +6 -0
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js +68 -53
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js +17 -0
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js +21 -19
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js +3 -6
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js +4 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js +6 -3
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js.map +1 -1
- package/dist/esm/l10n/en.json.js +2 -0
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +2 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +78 -7
- package/dist/esm/utils/actionLink.js +5 -0
- package/dist/esm/utils/actionLink.js.map +1 -1
- package/dist/esm/utils/actionsDispatcher.js +16 -1
- package/dist/esm/utils/actionsDispatcher.js.map +1 -1
- package/dist/esm/utils/linkNavigation.js +6 -3
- package/dist/esm/utils/linkNavigation.js.map +1 -1
- package/dist/esm/utils/rowAuxClick.js +30 -5
- package/dist/esm/utils/rowAuxClick.js.map +1 -1
- package/dist/esm/utils/safeHref.js +18 -3
- package/dist/esm/utils/safeHref.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +885 -393
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +78 -7
- package/l10n/en.json +2 -0
- package/l10n/nl.json +2 -0
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.vue +14 -5
- package/src/components/CnActionsMenu/CnActionsMenu.vue +0 -3
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -2
- package/src/components/CnAppRoot/CnAppRoot.vue +31 -2
- package/src/components/CnBoardView/CnBoardView.vue +13 -5
- package/src/components/CnCardGrid/CnCardGrid.vue +9 -1
- package/src/components/CnContextMenu/CnContextMenu.vue +6 -1
- package/src/components/CnDataTable/CnDataTable.md +1 -1
- package/src/components/CnDataTable/CnDataTable.vue +131 -17
- package/src/components/CnDateAxisView/CnDateAxisView.vue +15 -6
- package/src/components/CnDetailPage/CnDetailPage.md +1 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +29 -1
- package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -2
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +5 -3
- package/src/components/CnFilesWidget/CnFilesWidget.vue +4 -21
- package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +8 -21
- package/src/components/CnFlowsPage/CnFlowsPage.vue +2 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +75 -14
- package/src/components/CnIndexPage/manifestActionDispatch.js +31 -11
- package/src/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue +1 -2
- package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +0 -1
- package/src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue +2 -17
- package/src/components/CnLogsPage/CnLogsPage.vue +37 -12
- package/src/components/CnObjectCard/CnObjectCard.vue +24 -6
- package/src/components/CnObjectList/CnObjectList.vue +19 -3
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +47 -15
- package/src/components/CnObjectRow/CnObjectRow.vue +25 -7
- package/src/components/CnObjectSidebar/CnFilesTab.vue +0 -1
- package/src/components/CnPageRenderer/CnPageRenderer.vue +2 -0
- package/src/components/CnRowActions/CnRowActions.vue +7 -2
- package/src/components/CnSetupWizard/CnSetupWizard.vue +57 -6
- package/src/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue +5 -3
- package/src/components/CnTabs/CnTabs.vue +7 -3
- package/src/components/CnTasksWidget/CnTasksWidget.vue +4 -1
- package/src/css/table.css +26 -0
- package/src/integrations/builtin/analytics/CnAnalyticsCard.vue +3 -2
- package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +5 -6
- package/src/integrations/builtin/bookmarks/CnBookmarksCard.vue +12 -1
- package/src/integrations/builtin/bookmarks/CnBookmarksTab.vue +23 -6
- package/src/integrations/builtin/bookmarks/__tests__/CnBookmarksTab.spec.js +7 -2
- package/src/integrations/builtin/collectives/CnCollectivesCard.vue +3 -2
- package/src/integrations/builtin/collectives/CnCollectivesTab.vue +5 -6
- package/src/integrations/builtin/cospend/CnCospendCard.vue +2 -2
- package/src/integrations/builtin/cospend/CnCospendTab.vue +3 -4
- package/src/integrations/builtin/deck/CnDeckCard.vue +2 -2
- package/src/integrations/builtin/deck/CnDeckTab.vue +3 -4
- package/src/integrations/builtin/email/CnEmailCard.vue +6 -2
- package/src/integrations/builtin/email/CnEmailTab.vue +7 -4
- package/src/integrations/builtin/flow/CnFlowCard.vue +2 -2
- package/src/integrations/builtin/flow/CnFlowTab.vue +3 -4
- package/src/integrations/builtin/forms/CnFormsCard.vue +3 -2
- package/src/integrations/builtin/forms/CnFormsTab.vue +5 -6
- package/src/integrations/builtin/maps/CnMapsCard.vue +3 -2
- package/src/integrations/builtin/maps/CnMapsTab.vue +5 -6
- package/src/integrations/builtin/maps/__tests__/CnMapsTab.spec.js +9 -0
- package/src/integrations/builtin/openproject/CnOpenprojectCard.vue +3 -2
- package/src/integrations/builtin/openproject/CnOpenprojectTab.vue +5 -6
- package/src/integrations/builtin/photos/CnPhotosCard.vue +2 -2
- package/src/integrations/builtin/photos/CnPhotosTab.vue +3 -4
- package/src/integrations/builtin/polls/CnPollsCard.vue +2 -2
- package/src/integrations/builtin/polls/CnPollsTab.vue +3 -4
- package/src/integrations/builtin/shares/CnSharesTab.vue +3 -6
- package/src/integrations/builtin/talk/CnTalkCard.vue +3 -2
- package/src/integrations/builtin/talk/CnTalkTab.vue +5 -6
- package/src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue +2 -2
- package/src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue +3 -4
- package/src/integrations/builtin/xwiki/CnXwikiCard.vue +4 -2
- package/src/integrations/builtin/xwiki/CnXwikiTab.vue +7 -7
- package/src/integrations/builtin/xwiki/__tests__/CnXwikiTab.spec.js +9 -0
- package/src/schemas/app-manifest-v2.schema.json +2 -1
- package/src/utils/actionLink.js +5 -0
- package/src/utils/actionsDispatcher.js +15 -0
- package/src/utils/linkNavigation.js +6 -3
- package/src/utils/rowAuxClick.js +29 -4
- package/src/utils/safeHref.js +18 -3
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnEmailTab.vue2.js","sources":["../../../../../src/integrations/builtin/email/CnEmailTab.vue"],"sourcesContent":["<!--\n CnEmailTab — sidebar tab for the `email` integration.\n\n Renders the full list of Nextcloud Mail messages linked to an OR\n object. Backed by `EmailService` via the registry endpoint\n (`/integrations/email`). Paged via load-more; rows deep-link into the\n NC Mail app per AD-2 (Mail owns compose, OR owns the link).\n\n Visual fidelity: mirrors the real NC Mail message list — a sender\n avatar, a bold subject, a \"sender · date\" subline, a one-line snippet\n preview, and an unread indicator (accent dot + bold subject) when the\n message has not been read.\n\n Storage strategy: link-table (`openregister_email_links`). Per design\n AD-1 the tab does NOT compose — users open Mail, send, return and\n link via the \"Link existing\" picker (compose UI tracked separately).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-email-tab\">\n\t\t<div class=\"cn-email-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ linkExistingLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"composeUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<EmailEditOutline :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ composeLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<div v-if=\"loading\" class=\"cn-sidebar-tab__loading\">\n\t\t\t<NcLoadingIcon />\n\t\t</div>\n\t\t<div v-else-if=\"errored === true\" class=\"cn-sidebar-tab__empty cn-sidebar-tab__empty--error\">\n\t\t\t{{ errorLabel }}\n\t\t</div>\n\t\t<div v-else-if=\"messages.length === 0\" class=\"cn-sidebar-tab__empty\">\n\t\t\t{{ noMessagesLabel }}\n\t\t</div>\n\t\t<ul v-else class=\"cn-email-tab__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t:key=\"row.id\"\n\t\t\t\tclass=\"cn-email-tab__item\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"row.href ? 'a' : 'div'\"\n\t\t\t\t\tclass=\"cn-email-tab__row\"\n\t\t\t\t\t:class=\"{ 'cn-email-tab__row--unread': row.unread }\"\n\t\t\t\t\tv-bind=\"row.href ? { href: row.href, target: '_blank', rel: 'noopener noreferrer' } : {}\">\n\t\t\t\t\t<span class=\"cn-email-tab__unread-dot\" :class=\"{ 'is-shown': row.unread }\" />\n\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\tclass=\"cn-email-tab__avatar\"\n\t\t\t\t\t\t:size=\"36\"\n\t\t\t\t\t\t:displayName=\"row.avatarName\"\n\t\t\t\t\t\t:user=\"row.avatarUser\"\n\t\t\t\t\t\t:isNoUser=\"true\" />\n\t\t\t\t\t<div class=\"cn-email-tab__body\">\n\t\t\t\t\t\t<div class=\"cn-email-tab__line\">\n\t\t\t\t\t\t\t<span class=\"cn-email-tab__subject\" :title=\"row.subject\">{{ row.subject }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-email-tab__date\">\n\t\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\t\tv-if=\"row.dateValid\"\n\t\t\t\t\t\t\t\t\t:timestamp=\"row.dateMs\"\n\t\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t\t<template v-else>{{ row.dateRaw }}</template>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"cn-email-tab__sender\" :title=\"row.sender\">\n\t\t\t\t\t\t\t{{ row.sender }}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div v-if=\"row.snippet\" class=\"cn-email-tab__snippet\" :title=\"row.snippet\">\n\t\t\t\t\t\t\t{{ row.snippet }}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</component>\n\t\t\t</li>\n\t\t</ul>\n\t\t<NcButton\n\t\t\tv-if=\"hasMore === true\"\n\t\t\tvariant=\"tertiary\"\n\t\t\t:wide=\"true\"\n\t\t\t:disabled=\"loadingMore\"\n\t\t\tclass=\"cn-sidebar-tab__load-more\"\n\t\t\t@click=\"loadMore\">\n\t\t\t<template v-if=\"loadingMore\" #icon>\n\t\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t</template>\n\t\t\t{{ loadMoreLabel }}\n\t\t</NcButton>\n\n\t\t<CnEmailPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcDateTime, NcLoadingIcon } from '@nextcloud/vue'\nimport EmailEditOutline from 'vue-material-design-icons/EmailEditOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport CnEmailPicker from '../../../components/CnEmailPicker/CnEmailPicker.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\n/**\n * CnEmailTab — sidebar tab rendering the full list of Nextcloud Mail\n * messages linked to an OpenRegister object.\n *\n * Basic usage\n * ```vue\n * <CnEmailTab\n * :object-id=\"objectId\"\n * :register=\"registerId\"\n * :schema=\"schemaId\" />\n * ```\n */\nexport default {\n\tname: 'CnEmailTab',\n\n\tcomponents: {\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcLoadingIcon,\n\t\tEmailEditOutline,\n\t\tLinkVariant,\n\t\tCnEmailPicker,\n\t},\n\n\tprops: {\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Page size for paged fetches. */\n\t\tpageSize: { type: Number, default: 25 },\n\t\t/** Pre-translated empty label. */\n\t\tnoMessagesLabel: { type: String, default: () => t('nextcloud-vue', 'No linked emails yet') },\n\t\t/** Pre-translated error label. */\n\t\terrorLabel: { type: String, default: () => t('nextcloud-vue', 'Could not load emails') },\n\t\t/** Pre-translated load-more label. */\n\t\tloadMoreLabel: { type: String, default: () => t('nextcloud-vue', 'Load more') },\n\t\t/** Pre-translated fallback subject label. */\n\t\tnoSubjectLabel: { type: String, default: () => t('nextcloud-vue', '(no subject)') },\n\t\t/** Pre-translated fallback sender label. */\n\t\tunknownSenderLabel: { type: String, default: () => t('nextcloud-vue', 'Unknown sender') },\n\t\t/** Pre-translated link-existing CTA label. */\n\t\tlinkExistingLabel: { type: String, default: () => t('nextcloud-vue', 'Link existing email') },\n\t\t/** Pre-translated compose-in-Mail CTA label. */\n\t\tcomposeLabel: { type: String, default: () => t('nextcloud-vue', 'Compose in Mail') },\n\t\t/** Base path for the Mail app (for the compose deep-link). */\n\t\tmailAppPath: { type: String, default: '/index.php/apps/mail' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tmessages: [],\n\t\t\ttotal: 0,\n\t\t\tnextCursor: null,\n\t\t\tloading: false,\n\t\t\tloadingMore: false,\n\t\t\terrored: false,\n\t\t\tpickerOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether a further page is available.\n\t\t *\n\t\t * Prefers the backend's `nextCursor` (Tier-3 standardized\n\t\t * `{items, total, nextCursor}` envelope from the generic\n\t\t * integrations endpoint). Falls back to the count comparison\n\t\t * for any backend that still omits the cursor.\n\t\t *\n\t\t * @return {boolean} True when more messages can be loaded.\n\t\t */\n\t\thasMore() {\n\t\t\tif (this.nextCursor !== null) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn this.messages.length < this.total\n\t\t},\n\n\t\t/**\n\t\t * Pre-computed, template-safe view rows mirroring NC Mail.\n\t\t *\n\t\t * All optional-chaining / nullish logic lives here (in JS) so the\n\t\t * buble-compiled Vue 2 template stays free of `?.` / `??`.\n\t\t *\n\t\t * @return {object[]} One descriptor per message.\n\t\t */\n\t\trows() {\n\t\t\treturn this.messages.map((message) => {\n\t\t\t\tconst dateRaw = this.rawWhen(message)\n\t\t\t\tconst d = dateRaw === '' ? null : new Date(dateRaw)\n\t\t\t\tconst dateValid = d !== null && Number.isNaN(d.getTime()) === false\n\t\t\t\tconst sender = this.formatSender(message)\n\t\t\t\treturn {\n\t\t\t\t\tid: message.id,\n\t\t\t\t\tmessage,\n\t\t\t\t\tsubject: this.formatSubject(message),\n\t\t\t\t\tsender,\n\t\t\t\t\tsnippet: this.formatSnippet(message),\n\t\t\t\t\tunread: this.isUnread(message),\n\t\t\t\t\tdateValid,\n\t\t\t\t\tdateMs: dateValid ? d.getTime() : 0,\n\t\t\t\t\tdateRaw: String(dateRaw),\n\t\t\t\t\tavatarName: sender,\n\t\t\t\t\tavatarUser: this.senderEmail(message),\n\t\t\t\t\thref: this.threadUrl(message),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Deep-link into NC Mail's new-message composer.\n\t\t *\n\t\t * AD-2 (Mail owns compose / OR owns the link): we do NOT\n\t\t * implement an in-app composer. \"Compose in Mail\" opens NC Mail\n\t\t * in a new tab with the new-message view; once the user has\n\t\t * sent, they return to OR and link the sent message via the\n\t\t * picker.\n\t\t *\n\t\t * @return {string} The composer URL.\n\t\t */\n\t\tcomposeUrl() {\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl('/apps/mail/box/draft')\n\t\t\t\t: `${this.mailAppPath}/box/draft`\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.reset()\n\t\t\t\t\tthis.fetchMessages()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\treset() {\n\t\t\tthis.messages = []\n\t\t\tthis.total = 0\n\t\t\tthis.nextCursor = null\n\t\t\tthis.errored = false\n\t\t},\n\n\t\tasync fetchMessages(append = false) {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (append === true) {\n\t\t\t\tthis.loadingMore = true\n\t\t\t} else {\n\t\t\t\tthis.loading = true\n\t\t\t}\n\t\t\tthis.errored = false\n\t\t\ttry {\n\t\t\t\tconst params = new URLSearchParams({ _limit: String(this.pageSize) })\n\t\t\t\t// The Tier-3 envelope's `nextCursor` is the next zero-indexed\n\t\t\t\t// page number consumed by `_page`; walk it on append.\n\t\t\t\tif (append === true && this.nextCursor !== null) {\n\t\t\t\t\tparams.set('_page', String(this.nextCursor))\n\t\t\t\t} else {\n\t\t\t\t\tparams.set('_page', '0')\n\t\t\t\t}\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/email?${params.toString()}`),\n\t\t\t\t\t{ headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (response.ok === true) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst list = data.items || data.results || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tconst items = Array.isArray(list) === true ? list : []\n\t\t\t\t\tthis.messages = append === true ? [...this.messages, ...items] : items\n\t\t\t\t\tthis.total = typeof data.total === 'number' ? data.total : this.messages.length\n\t\t\t\t\tconst next = data.nextCursor\n\t\t\t\t\tthis.nextCursor = (next === null || next === undefined) ? null : next\n\t\t\t\t} else {\n\t\t\t\t\tthis.errored = true\n\t\t\t\t\tif (append !== true) {\n\t\t\t\t\t\tthis.messages = []\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnEmailTab] failed to fetch linked emails', err)\n\t\t\t\tthis.errored = true\n\t\t\t\tif (append !== true) {\n\t\t\t\t\tthis.messages = []\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t\tthis.loadingMore = false\n\t\t\t}\n\t\t},\n\n\t\tloadMore() {\n\t\t\tif (this.nextCursor === null && this.messages.length >= this.total) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchMessages(true)\n\t\t},\n\n\t\tformatSubject(message) {\n\t\t\tconst raw = message.subject\n\t\t\tif (typeof raw === 'string' && raw.trim() !== '') {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t\treturn this.noSubjectLabel\n\t\t},\n\n\t\tformatSender(message) {\n\t\t\tconst raw = message.senderName || message.fromName || message.sender || message.from\n\t\t\tif (typeof raw === 'string' && raw.trim() !== '') {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t\treturn this.unknownSenderLabel\n\t\t},\n\n\t\t/**\n\t\t * Best-effort bare email address for the NcAvatar `user` seed so\n\t\t * gravatar/initials are stable per correspondent.\n\t\t *\n\t\t * @param {object} message The message record.\n\t\t * @return {string} An email-ish identifier, or '' when unknown.\n\t\t */\n\t\tsenderEmail(message) {\n\t\t\tconst candidate = message.senderEmail || message.fromEmail || message.sender || message.from\n\t\t\tif (typeof candidate === 'string' && candidate.indexOf('@') !== -1) {\n\t\t\t\treturn candidate.trim()\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/**\n\t\t * One-line preview snippet, mirroring NC Mail's message list.\n\t\t *\n\t\t * @param {object} message The message record.\n\t\t * @return {string} A trimmed snippet, or '' when none is available.\n\t\t */\n\t\tformatSnippet(message) {\n\t\t\tconst raw = message.preview || message.snippet || message.summary || message.bodyPreview\n\t\t\tif (typeof raw === 'string' && raw.trim() !== '') {\n\t\t\t\treturn raw.trim()\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/**\n\t\t * Whether the message is unread, tolerant of the several shapes\n\t\t * the backend may report read-state in.\n\t\t *\n\t\t * @param {object} message The message record.\n\t\t * @return {boolean} True when the message has not been read.\n\t\t */\n\t\tisUnread(message) {\n\t\t\tif (typeof message.unread === 'boolean') {\n\t\t\t\treturn message.unread\n\t\t\t}\n\t\t\tif (typeof message.isRead === 'boolean') {\n\t\t\t\treturn message.isRead === false\n\t\t\t}\n\t\t\tif (typeof message.seen === 'boolean') {\n\t\t\t\treturn message.seen === false\n\t\t\t}\n\t\t\tif (Array.isArray(message.flags)) {\n\t\t\t\treturn message.flags.indexOf('seen') === -1 && message.flags.indexOf('\\\\Seen') === -1\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\n\t\trawWhen(message) {\n\t\t\tconst raw = message.mailDate || message.date || message.linkedAt\n\t\t\tif (raw === undefined || raw === null || raw === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn raw\n\t\t},\n\n\t\t// Thread deep-link into NC Mail; undefined when the row lacks ids.\n\t\tthreadUrl(message) {\n\t\t\tconst accountId = message.mailAccountId\n\t\t\tconst messageId = message.mailMessageId\n\t\t\tif (accountId === undefined || accountId === null || messageId === undefined || messageId === null) {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl(`/apps/mail/box/${accountId}/thread/${messageId}`)\n\t\t\t\t: `/index.php/apps/mail/box/${accountId}/thread/${messageId}`\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/emails`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif (response.ok === true || response.status === 409) {\n\t\t\t\t\t// 409 = already-linked; treated as success since the row\n\t\t\t\t\t// already exists for this object.\n\t\t\t\t\tthis.reset()\n\t\t\t\t\tawait this.fetchMessages()\n\t\t\t\t} else {\n\t\t\t\t\tthis.errored = true\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnEmailTab] link failed', err)\n\t\t\t\tthis.errored = true\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sidebar-tab {\n\tpadding: 12px;\n\toverflow-x: hidden;\n}\n\n.cn-sidebar-tab__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 24px 0;\n}\n\n.cn-sidebar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-sidebar-tab__empty--error {\n\tcolor: var(--color-error);\n}\n\n.cn-sidebar-tab__load-more {\n\tmargin-top: 8px;\n}\n\n.cn-email-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\tmargin-bottom: 8px;\n}\n\n/* NC-Mail-style message list */\n.cn-email-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-email-tab__row {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 10px;\n\tpadding: 8px 6px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tcolor: inherit;\n\ttext-decoration: none;\n\tcursor: pointer;\n\tposition: relative;\n}\n\n.cn-email-tab__row:hover {\n\tbackground-color: var(--color-background-hover);\n}\n\n.cn-email-tab__unread-dot {\n\tflex: 0 0 auto;\n\twidth: 8px;\n\talign-self: center;\n\tvisibility: hidden;\n}\n\n.cn-email-tab__unread-dot.is-shown {\n\tvisibility: visible;\n}\n\n.cn-email-tab__unread-dot.is-shown::before {\n\tcontent: '';\n\tdisplay: block;\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tbackground-color: var(--cn-email-accent, var(--color-primary-element));\n}\n\n.cn-email-tab__avatar {\n\tflex: 0 0 auto;\n}\n\n.cn-email-tab__body {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-email-tab__line {\n\tdisplay: flex;\n\talign-items: baseline;\n\tgap: 8px;\n}\n\n.cn-email-tab__subject {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-email-tab__row--unread .cn-email-tab__subject {\n\tfont-weight: bold;\n}\n\n.cn-email-tab__date {\n\tflex: 0 0 auto;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.8em;\n\twhite-space: nowrap;\n}\n\n.cn-email-tab__sender {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-email-tab__snippet {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tmargin-top: 1px;\n}\n</style>\n"],"names":["CnEmailPicker","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnEmailTab.vue2.js","sources":["../../../../../src/integrations/builtin/email/CnEmailTab.vue"],"sourcesContent":["<!--\n CnEmailTab — sidebar tab for the `email` integration.\n\n Renders the full list of Nextcloud Mail messages linked to an OR\n object. Backed by `EmailService` via the registry endpoint\n (`/integrations/email`). Paged via load-more; rows deep-link into the\n NC Mail app per AD-2 (Mail owns compose, OR owns the link).\n\n Visual fidelity: mirrors the real NC Mail message list — a sender\n avatar, a bold subject, a \"sender · date\" subline, a one-line snippet\n preview, and an unread indicator (accent dot + bold subject) when the\n message has not been read.\n\n Storage strategy: link-table (`openregister_email_links`). Per design\n AD-1 the tab does NOT compose — users open Mail, send, return and\n link via the \"Link existing\" picker (compose UI tracked separately).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-email-tab\">\n\t\t<div class=\"cn-email-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ linkExistingLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"composeUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<EmailEditOutline :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ composeLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<div v-if=\"loading\" class=\"cn-sidebar-tab__loading\">\n\t\t\t<NcLoadingIcon />\n\t\t</div>\n\t\t<div v-else-if=\"errored === true\" class=\"cn-sidebar-tab__empty cn-sidebar-tab__empty--error\">\n\t\t\t{{ errorLabel }}\n\t\t</div>\n\t\t<div v-else-if=\"messages.length === 0\" class=\"cn-sidebar-tab__empty\">\n\t\t\t{{ noMessagesLabel }}\n\t\t</div>\n\t\t<ul v-else class=\"cn-email-tab__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t:key=\"row.id\"\n\t\t\t\tclass=\"cn-email-tab__item\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"row.href ? 'a' : 'div'\"\n\t\t\t\t\tclass=\"cn-email-tab__row\"\n\t\t\t\t\t:class=\"{ 'cn-email-tab__row--unread': row.unread }\"\n\t\t\t\t\tv-bind=\"row.href ? { href: row.href, target: '_blank', rel: 'noopener noreferrer' } : {}\">\n\t\t\t\t\t<span class=\"cn-email-tab__unread-dot\" :class=\"{ 'is-shown': row.unread }\" />\n\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\tclass=\"cn-email-tab__avatar\"\n\t\t\t\t\t\t:size=\"36\"\n\t\t\t\t\t\t:displayName=\"row.avatarName\"\n\t\t\t\t\t\t:user=\"row.avatarUser\"\n\t\t\t\t\t\t:isNoUser=\"true\" />\n\t\t\t\t\t<div class=\"cn-email-tab__body\">\n\t\t\t\t\t\t<div class=\"cn-email-tab__line\">\n\t\t\t\t\t\t\t<span class=\"cn-email-tab__subject\" :title=\"row.subject\">{{ row.subject }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-email-tab__date\">\n\t\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\t\tv-if=\"row.dateValid\"\n\t\t\t\t\t\t\t\t\t:timestamp=\"row.dateMs\"\n\t\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t\t<template v-else>{{ row.dateRaw }}</template>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"cn-email-tab__sender\" :title=\"row.sender\">\n\t\t\t\t\t\t\t{{ row.sender }}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div v-if=\"row.snippet\" class=\"cn-email-tab__snippet\" :title=\"row.snippet\">\n\t\t\t\t\t\t\t{{ row.snippet }}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</component>\n\t\t\t</li>\n\t\t</ul>\n\t\t<NcButton\n\t\t\tv-if=\"hasMore === true\"\n\t\t\tvariant=\"tertiary\"\n\t\t\t:wide=\"true\"\n\t\t\t:disabled=\"loadingMore\"\n\t\t\tclass=\"cn-sidebar-tab__load-more\"\n\t\t\t@click=\"loadMore\">\n\t\t\t<template v-if=\"loadingMore\" #icon>\n\t\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t</template>\n\t\t\t{{ loadMoreLabel }}\n\t\t</NcButton>\n\n\t\t<CnEmailPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcDateTime, NcLoadingIcon } from '@nextcloud/vue'\nimport EmailEditOutline from 'vue-material-design-icons/EmailEditOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport CnEmailPicker from '../../../components/CnEmailPicker/CnEmailPicker.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\n/**\n * CnEmailTab — sidebar tab rendering the full list of Nextcloud Mail\n * messages linked to an OpenRegister object.\n *\n * Basic usage\n * ```vue\n * <CnEmailTab\n * :object-id=\"objectId\"\n * :register=\"registerId\"\n * :schema=\"schemaId\" />\n * ```\n */\nexport default {\n\tname: 'CnEmailTab',\n\n\tcomponents: {\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcLoadingIcon,\n\t\tEmailEditOutline,\n\t\tLinkVariant,\n\t\tCnEmailPicker,\n\t},\n\n\tprops: {\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Page size for paged fetches. */\n\t\tpageSize: { type: Number, default: 25 },\n\t\t/** Pre-translated empty label. */\n\t\tnoMessagesLabel: { type: String, default: () => t('nextcloud-vue', 'No linked emails yet') },\n\t\t/** Pre-translated error label. */\n\t\terrorLabel: { type: String, default: () => t('nextcloud-vue', 'Could not load emails') },\n\t\t/** Pre-translated load-more label. */\n\t\tloadMoreLabel: { type: String, default: () => t('nextcloud-vue', 'Load more') },\n\t\t/** Pre-translated fallback subject label. */\n\t\tnoSubjectLabel: { type: String, default: () => t('nextcloud-vue', '(no subject)') },\n\t\t/** Pre-translated fallback sender label. */\n\t\tunknownSenderLabel: { type: String, default: () => t('nextcloud-vue', 'Unknown sender') },\n\t\t/** Pre-translated link-existing CTA label. */\n\t\tlinkExistingLabel: { type: String, default: () => t('nextcloud-vue', 'Link existing email') },\n\t\t/** Pre-translated compose-in-Mail CTA label. */\n\t\tcomposeLabel: { type: String, default: () => t('nextcloud-vue', 'Compose in Mail') },\n\t\t/** Base path for the Mail app (for the compose deep-link). */\n\t\tmailAppPath: { type: String, default: '/index.php/apps/mail' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tmessages: [],\n\t\t\ttotal: 0,\n\t\t\tnextCursor: null,\n\t\t\tloading: false,\n\t\t\tloadingMore: false,\n\t\t\terrored: false,\n\t\t\tpickerOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether a further page is available.\n\t\t *\n\t\t * Prefers the backend's `nextCursor` (Tier-3 standardized\n\t\t * `{items, total, nextCursor}` envelope from the generic\n\t\t * integrations endpoint). Falls back to the count comparison\n\t\t * for any backend that still omits the cursor.\n\t\t *\n\t\t * @return {boolean} True when more messages can be loaded.\n\t\t */\n\t\thasMore() {\n\t\t\tif (this.nextCursor !== null) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn this.messages.length < this.total\n\t\t},\n\n\t\t/**\n\t\t * Pre-computed, template-safe view rows mirroring NC Mail.\n\t\t *\n\t\t * All optional-chaining / nullish logic lives here (in JS) so the\n\t\t * buble-compiled Vue 2 template stays free of `?.` / `??`.\n\t\t *\n\t\t * @return {object[]} One descriptor per message.\n\t\t */\n\t\trows() {\n\t\t\treturn this.messages.map((message) => {\n\t\t\t\tconst dateRaw = this.rawWhen(message)\n\t\t\t\tconst d = dateRaw === '' ? null : new Date(dateRaw)\n\t\t\t\tconst dateValid = d !== null && Number.isNaN(d.getTime()) === false\n\t\t\t\tconst sender = this.formatSender(message)\n\t\t\t\treturn {\n\t\t\t\t\tid: message.id,\n\t\t\t\t\tmessage,\n\t\t\t\t\tsubject: this.formatSubject(message),\n\t\t\t\t\tsender,\n\t\t\t\t\tsnippet: this.formatSnippet(message),\n\t\t\t\t\tunread: this.isUnread(message),\n\t\t\t\t\tdateValid,\n\t\t\t\t\tdateMs: dateValid ? d.getTime() : 0,\n\t\t\t\t\tdateRaw: String(dateRaw),\n\t\t\t\t\tavatarName: sender,\n\t\t\t\t\tavatarUser: this.senderEmail(message),\n\t\t\t\t\thref: this.threadUrl(message),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Deep-link into NC Mail's new-message composer.\n\t\t *\n\t\t * AD-2 (Mail owns compose / OR owns the link): we do NOT\n\t\t * implement an in-app composer. \"Compose in Mail\" opens NC Mail\n\t\t * in a new tab with the new-message view; once the user has\n\t\t * sent, they return to OR and link the sent message via the\n\t\t * picker.\n\t\t *\n\t\t * @return {string} The composer URL.\n\t\t */\n\t\tcomposeUrl() {\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl('/apps/mail/box/draft')\n\t\t\t\t: `${this.mailAppPath}/box/draft`\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.reset()\n\t\t\t\t\tthis.fetchMessages()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\treset() {\n\t\t\tthis.messages = []\n\t\t\tthis.total = 0\n\t\t\tthis.nextCursor = null\n\t\t\tthis.errored = false\n\t\t},\n\n\t\tasync fetchMessages(append = false) {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (append === true) {\n\t\t\t\tthis.loadingMore = true\n\t\t\t} else {\n\t\t\t\tthis.loading = true\n\t\t\t}\n\t\t\tthis.errored = false\n\t\t\ttry {\n\t\t\t\tconst params = new URLSearchParams({ _limit: String(this.pageSize) })\n\t\t\t\t// The Tier-3 envelope's `nextCursor` is the next zero-indexed\n\t\t\t\t// page number consumed by `_page`; walk it on append.\n\t\t\t\tif (append === true && this.nextCursor !== null) {\n\t\t\t\t\tparams.set('_page', String(this.nextCursor))\n\t\t\t\t} else {\n\t\t\t\t\tparams.set('_page', '0')\n\t\t\t\t}\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/email?${params.toString()}`),\n\t\t\t\t\t{ headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (response.ok === true) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst list = data.items || data.results || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tconst items = Array.isArray(list) === true ? list : []\n\t\t\t\t\tthis.messages = append === true ? [...this.messages, ...items] : items\n\t\t\t\t\tthis.total = typeof data.total === 'number' ? data.total : this.messages.length\n\t\t\t\t\tconst next = data.nextCursor\n\t\t\t\t\tthis.nextCursor = (next === null || next === undefined) ? null : next\n\t\t\t\t} else {\n\t\t\t\t\tthis.errored = true\n\t\t\t\t\tif (append !== true) {\n\t\t\t\t\t\tthis.messages = []\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnEmailTab] failed to fetch linked emails', err)\n\t\t\t\tthis.errored = true\n\t\t\t\tif (append !== true) {\n\t\t\t\t\tthis.messages = []\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t\tthis.loadingMore = false\n\t\t\t}\n\t\t},\n\n\t\tloadMore() {\n\t\t\tif (this.nextCursor === null && this.messages.length >= this.total) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchMessages(true)\n\t\t},\n\n\t\tformatSubject(message) {\n\t\t\tconst raw = message.subject\n\t\t\tif (typeof raw === 'string' && raw.trim() !== '') {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t\treturn this.noSubjectLabel\n\t\t},\n\n\t\tformatSender(message) {\n\t\t\tconst raw = message.senderName || message.fromName || message.sender || message.from\n\t\t\tif (typeof raw === 'string' && raw.trim() !== '') {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t\treturn this.unknownSenderLabel\n\t\t},\n\n\t\t/**\n\t\t * Best-effort bare email address for the NcAvatar `user` seed so\n\t\t * gravatar/initials are stable per correspondent.\n\t\t *\n\t\t * @param {object} message The message record.\n\t\t * @return {string} An email-ish identifier, or '' when unknown.\n\t\t */\n\t\tsenderEmail(message) {\n\t\t\tconst candidate = message.senderEmail || message.fromEmail || message.sender || message.from\n\t\t\tif (typeof candidate === 'string' && candidate.indexOf('@') !== -1) {\n\t\t\t\treturn candidate.trim()\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/**\n\t\t * One-line preview snippet, mirroring NC Mail's message list.\n\t\t *\n\t\t * @param {object} message The message record.\n\t\t * @return {string} A trimmed snippet, or '' when none is available.\n\t\t */\n\t\tformatSnippet(message) {\n\t\t\tconst raw = message.preview || message.snippet || message.summary || message.bodyPreview\n\t\t\tif (typeof raw === 'string' && raw.trim() !== '') {\n\t\t\t\treturn raw.trim()\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/**\n\t\t * Whether the message is unread, tolerant of the several shapes\n\t\t * the backend may report read-state in.\n\t\t *\n\t\t * @param {object} message The message record.\n\t\t * @return {boolean} True when the message has not been read.\n\t\t */\n\t\tisUnread(message) {\n\t\t\tif (typeof message.unread === 'boolean') {\n\t\t\t\treturn message.unread\n\t\t\t}\n\t\t\tif (typeof message.isRead === 'boolean') {\n\t\t\t\treturn message.isRead === false\n\t\t\t}\n\t\t\tif (typeof message.seen === 'boolean') {\n\t\t\t\treturn message.seen === false\n\t\t\t}\n\t\t\tif (Array.isArray(message.flags)) {\n\t\t\t\treturn message.flags.indexOf('seen') === -1 && message.flags.indexOf('\\\\Seen') === -1\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\n\t\trawWhen(message) {\n\t\t\tconst raw = message.mailDate || message.date || message.linkedAt\n\t\t\tif (raw === undefined || raw === null || raw === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn raw\n\t\t},\n\n\t\t// Thread deep-link into NC Mail; undefined when the row lacks ids.\n\t\tthreadUrl(message) {\n\t\t\tconst accountId = message.mailAccountId\n\t\t\tconst messageId = message.mailMessageId\n\t\t\tif (accountId === undefined || accountId === null || messageId === undefined || messageId === null) {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl(`/apps/mail/box/${accountId}/thread/${messageId}`)\n\t\t\t\t: `/index.php/apps/mail/box/${accountId}/thread/${messageId}`\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/emails`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif (response.ok === true || response.status === 409) {\n\t\t\t\t\t// 409 = already-linked; treated as success since the row\n\t\t\t\t\t// already exists for this object.\n\t\t\t\t\tthis.reset()\n\t\t\t\t\tawait this.fetchMessages()\n\t\t\t\t} else {\n\t\t\t\t\tthis.errored = true\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnEmailTab] link failed', err)\n\t\t\t\tthis.errored = true\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sidebar-tab {\n\tpadding: 12px;\n\toverflow-x: hidden;\n}\n\n.cn-sidebar-tab__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 24px 0;\n}\n\n.cn-sidebar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-sidebar-tab__empty--error {\n\tcolor: var(--color-error);\n}\n\n.cn-sidebar-tab__load-more {\n\tmargin-top: 8px;\n}\n\n.cn-email-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\tmargin-bottom: 8px;\n}\n\n/* NC-Mail-style message list */\n.cn-email-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-email-tab__row {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 10px;\n\tpadding: 8px 6px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tcolor: inherit;\n\ttext-decoration: none;\n\tposition: relative;\n}\n\n/* Only a row with Mail ids is a link; the rest must not look clickable. */\na.cn-email-tab__row {\n\tcursor: pointer;\n}\n\na.cn-email-tab__row:hover {\n\tbackground-color: var(--color-background-hover);\n}\n\n.cn-email-tab__unread-dot {\n\tflex: 0 0 auto;\n\twidth: 8px;\n\talign-self: center;\n\tvisibility: hidden;\n}\n\n.cn-email-tab__unread-dot.is-shown {\n\tvisibility: visible;\n}\n\n.cn-email-tab__unread-dot.is-shown::before {\n\tcontent: '';\n\tdisplay: block;\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tbackground-color: var(--cn-email-accent, var(--color-primary-element));\n}\n\n.cn-email-tab__avatar {\n\tflex: 0 0 auto;\n}\n\n.cn-email-tab__body {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-email-tab__line {\n\tdisplay: flex;\n\talign-items: baseline;\n\tgap: 8px;\n}\n\n.cn-email-tab__subject {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-email-tab__row--unread .cn-email-tab__subject {\n\tfont-weight: bold;\n}\n\n.cn-email-tab__date {\n\tflex: 0 0 auto;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.8em;\n\twhite-space: nowrap;\n}\n\n.cn-email-tab__sender {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-email-tab__snippet {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tmargin-top: 1px;\n}\n</style>\n"],"names":["CnEmailPicker","t"],"mappings":"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"}
|
|
@@ -51,8 +51,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
51
51
|
createVNode(_component_NcButton, {
|
|
52
52
|
variant: "primary",
|
|
53
53
|
href: $options.composeUrl,
|
|
54
|
-
target: "_blank"
|
|
55
|
-
rel: "noopener noreferrer"
|
|
54
|
+
target: "_blank"
|
|
56
55
|
}, {
|
|
57
56
|
icon: withCtx(() => [
|
|
58
57
|
createVNode(_component_EmailEditOutline, { size: 18 })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnEmailTab.vue3.js","sources":["../../../../../src/integrations/builtin/email/CnEmailTab.vue?vue&type=template&id=0bcebbac&lang.js"],"sourcesContent":["<!--\n CnEmailTab — sidebar tab for the `email` integration.\n\n Renders the full list of Nextcloud Mail messages linked to an OR\n object. Backed by `EmailService` via the registry endpoint\n (`/integrations/email`). Paged via load-more; rows deep-link into the\n NC Mail app per AD-2 (Mail owns compose, OR owns the link).\n\n Visual fidelity: mirrors the real NC Mail message list — a sender\n avatar, a bold subject, a \"sender · date\" subline, a one-line snippet\n preview, and an unread indicator (accent dot + bold subject) when the\n message has not been read.\n\n Storage strategy: link-table (`openregister_email_links`). Per design\n AD-1 the tab does NOT compose — users open Mail, send, return and\n link via the \"Link existing\" picker (compose UI tracked separately).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-email-tab\">\n\t\t<div class=\"cn-email-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ linkExistingLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"composeUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<EmailEditOutline :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ composeLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<div v-if=\"loading\" class=\"cn-sidebar-tab__loading\">\n\t\t\t<NcLoadingIcon />\n\t\t</div>\n\t\t<div v-else-if=\"errored === true\" class=\"cn-sidebar-tab__empty cn-sidebar-tab__empty--error\">\n\t\t\t{{ errorLabel }}\n\t\t</div>\n\t\t<div v-else-if=\"messages.length === 0\" class=\"cn-sidebar-tab__empty\">\n\t\t\t{{ noMessagesLabel }}\n\t\t</div>\n\t\t<ul v-else class=\"cn-email-tab__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t:key=\"row.id\"\n\t\t\t\tclass=\"cn-email-tab__item\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"row.href ? 'a' : 'div'\"\n\t\t\t\t\tclass=\"cn-email-tab__row\"\n\t\t\t\t\t:class=\"{ 'cn-email-tab__row--unread': row.unread }\"\n\t\t\t\t\tv-bind=\"row.href ? { href: row.href, target: '_blank', rel: 'noopener noreferrer' } : {}\">\n\t\t\t\t\t<span class=\"cn-email-tab__unread-dot\" :class=\"{ 'is-shown': row.unread }\" />\n\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\tclass=\"cn-email-tab__avatar\"\n\t\t\t\t\t\t:size=\"36\"\n\t\t\t\t\t\t:displayName=\"row.avatarName\"\n\t\t\t\t\t\t:user=\"row.avatarUser\"\n\t\t\t\t\t\t:isNoUser=\"true\" />\n\t\t\t\t\t<div class=\"cn-email-tab__body\">\n\t\t\t\t\t\t<div class=\"cn-email-tab__line\">\n\t\t\t\t\t\t\t<span class=\"cn-email-tab__subject\" :title=\"row.subject\">{{ row.subject }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-email-tab__date\">\n\t\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\t\tv-if=\"row.dateValid\"\n\t\t\t\t\t\t\t\t\t:timestamp=\"row.dateMs\"\n\t\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t\t<template v-else>{{ row.dateRaw }}</template>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"cn-email-tab__sender\" :title=\"row.sender\">\n\t\t\t\t\t\t\t{{ row.sender }}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div v-if=\"row.snippet\" class=\"cn-email-tab__snippet\" :title=\"row.snippet\">\n\t\t\t\t\t\t\t{{ row.snippet }}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</component>\n\t\t\t</li>\n\t\t</ul>\n\t\t<NcButton\n\t\t\tv-if=\"hasMore === true\"\n\t\t\tvariant=\"tertiary\"\n\t\t\t:wide=\"true\"\n\t\t\t:disabled=\"loadingMore\"\n\t\t\tclass=\"cn-sidebar-tab__load-more\"\n\t\t\t@click=\"loadMore\">\n\t\t\t<template v-if=\"loadingMore\" #icon>\n\t\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t</template>\n\t\t\t{{ loadMoreLabel }}\n\t\t</NcButton>\n\n\t\t<CnEmailPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcDateTime, NcLoadingIcon } from '@nextcloud/vue'\nimport EmailEditOutline from 'vue-material-design-icons/EmailEditOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport CnEmailPicker from '../../../components/CnEmailPicker/CnEmailPicker.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\n/**\n * CnEmailTab — sidebar tab rendering the full list of Nextcloud Mail\n * messages linked to an OpenRegister object.\n *\n * Basic usage\n * ```vue\n * <CnEmailTab\n * :object-id=\"objectId\"\n * :register=\"registerId\"\n * :schema=\"schemaId\" />\n * ```\n */\nexport default {\n\tname: 'CnEmailTab',\n\n\tcomponents: {\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcLoadingIcon,\n\t\tEmailEditOutline,\n\t\tLinkVariant,\n\t\tCnEmailPicker,\n\t},\n\n\tprops: {\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Page size for paged fetches. */\n\t\tpageSize: { type: Number, default: 25 },\n\t\t/** Pre-translated empty label. */\n\t\tnoMessagesLabel: { type: String, default: () => t('nextcloud-vue', 'No linked emails yet') },\n\t\t/** Pre-translated error label. */\n\t\terrorLabel: { type: String, default: () => t('nextcloud-vue', 'Could not load emails') },\n\t\t/** Pre-translated load-more label. */\n\t\tloadMoreLabel: { type: String, default: () => t('nextcloud-vue', 'Load more') },\n\t\t/** Pre-translated fallback subject label. */\n\t\tnoSubjectLabel: { type: String, default: () => t('nextcloud-vue', '(no subject)') },\n\t\t/** Pre-translated fallback sender label. */\n\t\tunknownSenderLabel: { type: String, default: () => t('nextcloud-vue', 'Unknown sender') },\n\t\t/** Pre-translated link-existing CTA label. */\n\t\tlinkExistingLabel: { type: String, default: () => t('nextcloud-vue', 'Link existing email') },\n\t\t/** Pre-translated compose-in-Mail CTA label. */\n\t\tcomposeLabel: { type: String, default: () => t('nextcloud-vue', 'Compose in Mail') },\n\t\t/** Base path for the Mail app (for the compose deep-link). */\n\t\tmailAppPath: { type: String, default: '/index.php/apps/mail' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tmessages: [],\n\t\t\ttotal: 0,\n\t\t\tnextCursor: null,\n\t\t\tloading: false,\n\t\t\tloadingMore: false,\n\t\t\terrored: false,\n\t\t\tpickerOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether a further page is available.\n\t\t *\n\t\t * Prefers the backend's `nextCursor` (Tier-3 standardized\n\t\t * `{items, total, nextCursor}` envelope from the generic\n\t\t * integrations endpoint). Falls back to the count comparison\n\t\t * for any backend that still omits the cursor.\n\t\t *\n\t\t * @return {boolean} True when more messages can be loaded.\n\t\t */\n\t\thasMore() {\n\t\t\tif (this.nextCursor !== null) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn this.messages.length < this.total\n\t\t},\n\n\t\t/**\n\t\t * Pre-computed, template-safe view rows mirroring NC Mail.\n\t\t *\n\t\t * All optional-chaining / nullish logic lives here (in JS) so the\n\t\t * buble-compiled Vue 2 template stays free of `?.` / `??`.\n\t\t *\n\t\t * @return {object[]} One descriptor per message.\n\t\t */\n\t\trows() {\n\t\t\treturn this.messages.map((message) => {\n\t\t\t\tconst dateRaw = this.rawWhen(message)\n\t\t\t\tconst d = dateRaw === '' ? null : new Date(dateRaw)\n\t\t\t\tconst dateValid = d !== null && Number.isNaN(d.getTime()) === false\n\t\t\t\tconst sender = this.formatSender(message)\n\t\t\t\treturn {\n\t\t\t\t\tid: message.id,\n\t\t\t\t\tmessage,\n\t\t\t\t\tsubject: this.formatSubject(message),\n\t\t\t\t\tsender,\n\t\t\t\t\tsnippet: this.formatSnippet(message),\n\t\t\t\t\tunread: this.isUnread(message),\n\t\t\t\t\tdateValid,\n\t\t\t\t\tdateMs: dateValid ? d.getTime() : 0,\n\t\t\t\t\tdateRaw: String(dateRaw),\n\t\t\t\t\tavatarName: sender,\n\t\t\t\t\tavatarUser: this.senderEmail(message),\n\t\t\t\t\thref: this.threadUrl(message),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Deep-link into NC Mail's new-message composer.\n\t\t *\n\t\t * AD-2 (Mail owns compose / OR owns the link): we do NOT\n\t\t * implement an in-app composer. \"Compose in Mail\" opens NC Mail\n\t\t * in a new tab with the new-message view; once the user has\n\t\t * sent, they return to OR and link the sent message via the\n\t\t * picker.\n\t\t *\n\t\t * @return {string} The composer URL.\n\t\t */\n\t\tcomposeUrl() {\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl('/apps/mail/box/draft')\n\t\t\t\t: `${this.mailAppPath}/box/draft`\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.reset()\n\t\t\t\t\tthis.fetchMessages()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\treset() {\n\t\t\tthis.messages = []\n\t\t\tthis.total = 0\n\t\t\tthis.nextCursor = null\n\t\t\tthis.errored = false\n\t\t},\n\n\t\tasync fetchMessages(append = false) {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (append === true) {\n\t\t\t\tthis.loadingMore = true\n\t\t\t} else {\n\t\t\t\tthis.loading = true\n\t\t\t}\n\t\t\tthis.errored = false\n\t\t\ttry {\n\t\t\t\tconst params = new URLSearchParams({ _limit: String(this.pageSize) })\n\t\t\t\t// The Tier-3 envelope's `nextCursor` is the next zero-indexed\n\t\t\t\t// page number consumed by `_page`; walk it on append.\n\t\t\t\tif (append === true && this.nextCursor !== null) {\n\t\t\t\t\tparams.set('_page', String(this.nextCursor))\n\t\t\t\t} else {\n\t\t\t\t\tparams.set('_page', '0')\n\t\t\t\t}\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/email?${params.toString()}`),\n\t\t\t\t\t{ headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (response.ok === true) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst list = data.items || data.results || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tconst items = Array.isArray(list) === true ? list : []\n\t\t\t\t\tthis.messages = append === true ? [...this.messages, ...items] : items\n\t\t\t\t\tthis.total = typeof data.total === 'number' ? data.total : this.messages.length\n\t\t\t\t\tconst next = data.nextCursor\n\t\t\t\t\tthis.nextCursor = (next === null || next === undefined) ? null : next\n\t\t\t\t} else {\n\t\t\t\t\tthis.errored = true\n\t\t\t\t\tif (append !== true) {\n\t\t\t\t\t\tthis.messages = []\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnEmailTab] failed to fetch linked emails', err)\n\t\t\t\tthis.errored = true\n\t\t\t\tif (append !== true) {\n\t\t\t\t\tthis.messages = []\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t\tthis.loadingMore = false\n\t\t\t}\n\t\t},\n\n\t\tloadMore() {\n\t\t\tif (this.nextCursor === null && this.messages.length >= this.total) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchMessages(true)\n\t\t},\n\n\t\tformatSubject(message) {\n\t\t\tconst raw = message.subject\n\t\t\tif (typeof raw === 'string' && raw.trim() !== '') {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t\treturn this.noSubjectLabel\n\t\t},\n\n\t\tformatSender(message) {\n\t\t\tconst raw = message.senderName || message.fromName || message.sender || message.from\n\t\t\tif (typeof raw === 'string' && raw.trim() !== '') {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t\treturn this.unknownSenderLabel\n\t\t},\n\n\t\t/**\n\t\t * Best-effort bare email address for the NcAvatar `user` seed so\n\t\t * gravatar/initials are stable per correspondent.\n\t\t *\n\t\t * @param {object} message The message record.\n\t\t * @return {string} An email-ish identifier, or '' when unknown.\n\t\t */\n\t\tsenderEmail(message) {\n\t\t\tconst candidate = message.senderEmail || message.fromEmail || message.sender || message.from\n\t\t\tif (typeof candidate === 'string' && candidate.indexOf('@') !== -1) {\n\t\t\t\treturn candidate.trim()\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/**\n\t\t * One-line preview snippet, mirroring NC Mail's message list.\n\t\t *\n\t\t * @param {object} message The message record.\n\t\t * @return {string} A trimmed snippet, or '' when none is available.\n\t\t */\n\t\tformatSnippet(message) {\n\t\t\tconst raw = message.preview || message.snippet || message.summary || message.bodyPreview\n\t\t\tif (typeof raw === 'string' && raw.trim() !== '') {\n\t\t\t\treturn raw.trim()\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/**\n\t\t * Whether the message is unread, tolerant of the several shapes\n\t\t * the backend may report read-state in.\n\t\t *\n\t\t * @param {object} message The message record.\n\t\t * @return {boolean} True when the message has not been read.\n\t\t */\n\t\tisUnread(message) {\n\t\t\tif (typeof message.unread === 'boolean') {\n\t\t\t\treturn message.unread\n\t\t\t}\n\t\t\tif (typeof message.isRead === 'boolean') {\n\t\t\t\treturn message.isRead === false\n\t\t\t}\n\t\t\tif (typeof message.seen === 'boolean') {\n\t\t\t\treturn message.seen === false\n\t\t\t}\n\t\t\tif (Array.isArray(message.flags)) {\n\t\t\t\treturn message.flags.indexOf('seen') === -1 && message.flags.indexOf('\\\\Seen') === -1\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\n\t\trawWhen(message) {\n\t\t\tconst raw = message.mailDate || message.date || message.linkedAt\n\t\t\tif (raw === undefined || raw === null || raw === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn raw\n\t\t},\n\n\t\t// Thread deep-link into NC Mail; undefined when the row lacks ids.\n\t\tthreadUrl(message) {\n\t\t\tconst accountId = message.mailAccountId\n\t\t\tconst messageId = message.mailMessageId\n\t\t\tif (accountId === undefined || accountId === null || messageId === undefined || messageId === null) {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl(`/apps/mail/box/${accountId}/thread/${messageId}`)\n\t\t\t\t: `/index.php/apps/mail/box/${accountId}/thread/${messageId}`\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/emails`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif (response.ok === true || response.status === 409) {\n\t\t\t\t\t// 409 = already-linked; treated as success since the row\n\t\t\t\t\t// already exists for this object.\n\t\t\t\t\tthis.reset()\n\t\t\t\t\tawait this.fetchMessages()\n\t\t\t\t} else {\n\t\t\t\t\tthis.errored = true\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnEmailTab] link failed', err)\n\t\t\t\tthis.errored = true\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sidebar-tab {\n\tpadding: 12px;\n\toverflow-x: hidden;\n}\n\n.cn-sidebar-tab__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 24px 0;\n}\n\n.cn-sidebar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-sidebar-tab__empty--error {\n\tcolor: var(--color-error);\n}\n\n.cn-sidebar-tab__load-more {\n\tmargin-top: 8px;\n}\n\n.cn-email-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\tmargin-bottom: 8px;\n}\n\n/* NC-Mail-style message list */\n.cn-email-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-email-tab__row {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 10px;\n\tpadding: 8px 6px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tcolor: inherit;\n\ttext-decoration: none;\n\tcursor: pointer;\n\tposition: relative;\n}\n\n.cn-email-tab__row:hover {\n\tbackground-color: var(--color-background-hover);\n}\n\n.cn-email-tab__unread-dot {\n\tflex: 0 0 auto;\n\twidth: 8px;\n\talign-self: center;\n\tvisibility: hidden;\n}\n\n.cn-email-tab__unread-dot.is-shown {\n\tvisibility: visible;\n}\n\n.cn-email-tab__unread-dot.is-shown::before {\n\tcontent: '';\n\tdisplay: block;\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tbackground-color: var(--cn-email-accent, var(--color-primary-element));\n}\n\n.cn-email-tab__avatar {\n\tflex: 0 0 auto;\n}\n\n.cn-email-tab__body {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-email-tab__line {\n\tdisplay: flex;\n\talign-items: baseline;\n\tgap: 8px;\n}\n\n.cn-email-tab__subject {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-email-tab__row--unread .cn-email-tab__subject {\n\tfont-weight: bold;\n}\n\n.cn-email-tab__date {\n\tflex: 0 0 auto;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.8em;\n\twhite-space: nowrap;\n}\n\n.cn-email-tab__sender {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-email-tab__snippet {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tmargin-top: 1px;\n}\n</style>\n"],"names":["_createElementVNode","_createVNode","_createTextVNode","_createElementBlock","_toDisplayString","_openBlock","_Fragment","_renderList","_createBlock","_resolveDynamicComponent","_mergeProps","_normalizeClass"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnEmailTab.vue3.js","sources":["../../../../../src/integrations/builtin/email/CnEmailTab.vue?vue&type=template&id=0bcebbac&lang.js"],"sourcesContent":["<!--\n CnEmailTab — sidebar tab for the `email` integration.\n\n Renders the full list of Nextcloud Mail messages linked to an OR\n object. Backed by `EmailService` via the registry endpoint\n (`/integrations/email`). Paged via load-more; rows deep-link into the\n NC Mail app per AD-2 (Mail owns compose, OR owns the link).\n\n Visual fidelity: mirrors the real NC Mail message list — a sender\n avatar, a bold subject, a \"sender · date\" subline, a one-line snippet\n preview, and an unread indicator (accent dot + bold subject) when the\n message has not been read.\n\n Storage strategy: link-table (`openregister_email_links`). Per design\n AD-1 the tab does NOT compose — users open Mail, send, return and\n link via the \"Link existing\" picker (compose UI tracked separately).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-email-tab\">\n\t\t<div class=\"cn-email-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ linkExistingLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"composeUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<EmailEditOutline :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ composeLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<div v-if=\"loading\" class=\"cn-sidebar-tab__loading\">\n\t\t\t<NcLoadingIcon />\n\t\t</div>\n\t\t<div v-else-if=\"errored === true\" class=\"cn-sidebar-tab__empty cn-sidebar-tab__empty--error\">\n\t\t\t{{ errorLabel }}\n\t\t</div>\n\t\t<div v-else-if=\"messages.length === 0\" class=\"cn-sidebar-tab__empty\">\n\t\t\t{{ noMessagesLabel }}\n\t\t</div>\n\t\t<ul v-else class=\"cn-email-tab__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t:key=\"row.id\"\n\t\t\t\tclass=\"cn-email-tab__item\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"row.href ? 'a' : 'div'\"\n\t\t\t\t\tclass=\"cn-email-tab__row\"\n\t\t\t\t\t:class=\"{ 'cn-email-tab__row--unread': row.unread }\"\n\t\t\t\t\tv-bind=\"row.href ? { href: row.href, target: '_blank', rel: 'noopener noreferrer' } : {}\">\n\t\t\t\t\t<span class=\"cn-email-tab__unread-dot\" :class=\"{ 'is-shown': row.unread }\" />\n\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\tclass=\"cn-email-tab__avatar\"\n\t\t\t\t\t\t:size=\"36\"\n\t\t\t\t\t\t:displayName=\"row.avatarName\"\n\t\t\t\t\t\t:user=\"row.avatarUser\"\n\t\t\t\t\t\t:isNoUser=\"true\" />\n\t\t\t\t\t<div class=\"cn-email-tab__body\">\n\t\t\t\t\t\t<div class=\"cn-email-tab__line\">\n\t\t\t\t\t\t\t<span class=\"cn-email-tab__subject\" :title=\"row.subject\">{{ row.subject }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-email-tab__date\">\n\t\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\t\tv-if=\"row.dateValid\"\n\t\t\t\t\t\t\t\t\t:timestamp=\"row.dateMs\"\n\t\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t\t<template v-else>{{ row.dateRaw }}</template>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"cn-email-tab__sender\" :title=\"row.sender\">\n\t\t\t\t\t\t\t{{ row.sender }}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div v-if=\"row.snippet\" class=\"cn-email-tab__snippet\" :title=\"row.snippet\">\n\t\t\t\t\t\t\t{{ row.snippet }}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</component>\n\t\t\t</li>\n\t\t</ul>\n\t\t<NcButton\n\t\t\tv-if=\"hasMore === true\"\n\t\t\tvariant=\"tertiary\"\n\t\t\t:wide=\"true\"\n\t\t\t:disabled=\"loadingMore\"\n\t\t\tclass=\"cn-sidebar-tab__load-more\"\n\t\t\t@click=\"loadMore\">\n\t\t\t<template v-if=\"loadingMore\" #icon>\n\t\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t</template>\n\t\t\t{{ loadMoreLabel }}\n\t\t</NcButton>\n\n\t\t<CnEmailPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcDateTime, NcLoadingIcon } from '@nextcloud/vue'\nimport EmailEditOutline from 'vue-material-design-icons/EmailEditOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport CnEmailPicker from '../../../components/CnEmailPicker/CnEmailPicker.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\n/**\n * CnEmailTab — sidebar tab rendering the full list of Nextcloud Mail\n * messages linked to an OpenRegister object.\n *\n * Basic usage\n * ```vue\n * <CnEmailTab\n * :object-id=\"objectId\"\n * :register=\"registerId\"\n * :schema=\"schemaId\" />\n * ```\n */\nexport default {\n\tname: 'CnEmailTab',\n\n\tcomponents: {\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcLoadingIcon,\n\t\tEmailEditOutline,\n\t\tLinkVariant,\n\t\tCnEmailPicker,\n\t},\n\n\tprops: {\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Page size for paged fetches. */\n\t\tpageSize: { type: Number, default: 25 },\n\t\t/** Pre-translated empty label. */\n\t\tnoMessagesLabel: { type: String, default: () => t('nextcloud-vue', 'No linked emails yet') },\n\t\t/** Pre-translated error label. */\n\t\terrorLabel: { type: String, default: () => t('nextcloud-vue', 'Could not load emails') },\n\t\t/** Pre-translated load-more label. */\n\t\tloadMoreLabel: { type: String, default: () => t('nextcloud-vue', 'Load more') },\n\t\t/** Pre-translated fallback subject label. */\n\t\tnoSubjectLabel: { type: String, default: () => t('nextcloud-vue', '(no subject)') },\n\t\t/** Pre-translated fallback sender label. */\n\t\tunknownSenderLabel: { type: String, default: () => t('nextcloud-vue', 'Unknown sender') },\n\t\t/** Pre-translated link-existing CTA label. */\n\t\tlinkExistingLabel: { type: String, default: () => t('nextcloud-vue', 'Link existing email') },\n\t\t/** Pre-translated compose-in-Mail CTA label. */\n\t\tcomposeLabel: { type: String, default: () => t('nextcloud-vue', 'Compose in Mail') },\n\t\t/** Base path for the Mail app (for the compose deep-link). */\n\t\tmailAppPath: { type: String, default: '/index.php/apps/mail' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tmessages: [],\n\t\t\ttotal: 0,\n\t\t\tnextCursor: null,\n\t\t\tloading: false,\n\t\t\tloadingMore: false,\n\t\t\terrored: false,\n\t\t\tpickerOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether a further page is available.\n\t\t *\n\t\t * Prefers the backend's `nextCursor` (Tier-3 standardized\n\t\t * `{items, total, nextCursor}` envelope from the generic\n\t\t * integrations endpoint). Falls back to the count comparison\n\t\t * for any backend that still omits the cursor.\n\t\t *\n\t\t * @return {boolean} True when more messages can be loaded.\n\t\t */\n\t\thasMore() {\n\t\t\tif (this.nextCursor !== null) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn this.messages.length < this.total\n\t\t},\n\n\t\t/**\n\t\t * Pre-computed, template-safe view rows mirroring NC Mail.\n\t\t *\n\t\t * All optional-chaining / nullish logic lives here (in JS) so the\n\t\t * buble-compiled Vue 2 template stays free of `?.` / `??`.\n\t\t *\n\t\t * @return {object[]} One descriptor per message.\n\t\t */\n\t\trows() {\n\t\t\treturn this.messages.map((message) => {\n\t\t\t\tconst dateRaw = this.rawWhen(message)\n\t\t\t\tconst d = dateRaw === '' ? null : new Date(dateRaw)\n\t\t\t\tconst dateValid = d !== null && Number.isNaN(d.getTime()) === false\n\t\t\t\tconst sender = this.formatSender(message)\n\t\t\t\treturn {\n\t\t\t\t\tid: message.id,\n\t\t\t\t\tmessage,\n\t\t\t\t\tsubject: this.formatSubject(message),\n\t\t\t\t\tsender,\n\t\t\t\t\tsnippet: this.formatSnippet(message),\n\t\t\t\t\tunread: this.isUnread(message),\n\t\t\t\t\tdateValid,\n\t\t\t\t\tdateMs: dateValid ? d.getTime() : 0,\n\t\t\t\t\tdateRaw: String(dateRaw),\n\t\t\t\t\tavatarName: sender,\n\t\t\t\t\tavatarUser: this.senderEmail(message),\n\t\t\t\t\thref: this.threadUrl(message),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Deep-link into NC Mail's new-message composer.\n\t\t *\n\t\t * AD-2 (Mail owns compose / OR owns the link): we do NOT\n\t\t * implement an in-app composer. \"Compose in Mail\" opens NC Mail\n\t\t * in a new tab with the new-message view; once the user has\n\t\t * sent, they return to OR and link the sent message via the\n\t\t * picker.\n\t\t *\n\t\t * @return {string} The composer URL.\n\t\t */\n\t\tcomposeUrl() {\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl('/apps/mail/box/draft')\n\t\t\t\t: `${this.mailAppPath}/box/draft`\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.reset()\n\t\t\t\t\tthis.fetchMessages()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\treset() {\n\t\t\tthis.messages = []\n\t\t\tthis.total = 0\n\t\t\tthis.nextCursor = null\n\t\t\tthis.errored = false\n\t\t},\n\n\t\tasync fetchMessages(append = false) {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (append === true) {\n\t\t\t\tthis.loadingMore = true\n\t\t\t} else {\n\t\t\t\tthis.loading = true\n\t\t\t}\n\t\t\tthis.errored = false\n\t\t\ttry {\n\t\t\t\tconst params = new URLSearchParams({ _limit: String(this.pageSize) })\n\t\t\t\t// The Tier-3 envelope's `nextCursor` is the next zero-indexed\n\t\t\t\t// page number consumed by `_page`; walk it on append.\n\t\t\t\tif (append === true && this.nextCursor !== null) {\n\t\t\t\t\tparams.set('_page', String(this.nextCursor))\n\t\t\t\t} else {\n\t\t\t\t\tparams.set('_page', '0')\n\t\t\t\t}\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/email?${params.toString()}`),\n\t\t\t\t\t{ headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (response.ok === true) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst list = data.items || data.results || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tconst items = Array.isArray(list) === true ? list : []\n\t\t\t\t\tthis.messages = append === true ? [...this.messages, ...items] : items\n\t\t\t\t\tthis.total = typeof data.total === 'number' ? data.total : this.messages.length\n\t\t\t\t\tconst next = data.nextCursor\n\t\t\t\t\tthis.nextCursor = (next === null || next === undefined) ? null : next\n\t\t\t\t} else {\n\t\t\t\t\tthis.errored = true\n\t\t\t\t\tif (append !== true) {\n\t\t\t\t\t\tthis.messages = []\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnEmailTab] failed to fetch linked emails', err)\n\t\t\t\tthis.errored = true\n\t\t\t\tif (append !== true) {\n\t\t\t\t\tthis.messages = []\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t\tthis.loadingMore = false\n\t\t\t}\n\t\t},\n\n\t\tloadMore() {\n\t\t\tif (this.nextCursor === null && this.messages.length >= this.total) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchMessages(true)\n\t\t},\n\n\t\tformatSubject(message) {\n\t\t\tconst raw = message.subject\n\t\t\tif (typeof raw === 'string' && raw.trim() !== '') {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t\treturn this.noSubjectLabel\n\t\t},\n\n\t\tformatSender(message) {\n\t\t\tconst raw = message.senderName || message.fromName || message.sender || message.from\n\t\t\tif (typeof raw === 'string' && raw.trim() !== '') {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t\treturn this.unknownSenderLabel\n\t\t},\n\n\t\t/**\n\t\t * Best-effort bare email address for the NcAvatar `user` seed so\n\t\t * gravatar/initials are stable per correspondent.\n\t\t *\n\t\t * @param {object} message The message record.\n\t\t * @return {string} An email-ish identifier, or '' when unknown.\n\t\t */\n\t\tsenderEmail(message) {\n\t\t\tconst candidate = message.senderEmail || message.fromEmail || message.sender || message.from\n\t\t\tif (typeof candidate === 'string' && candidate.indexOf('@') !== -1) {\n\t\t\t\treturn candidate.trim()\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/**\n\t\t * One-line preview snippet, mirroring NC Mail's message list.\n\t\t *\n\t\t * @param {object} message The message record.\n\t\t * @return {string} A trimmed snippet, or '' when none is available.\n\t\t */\n\t\tformatSnippet(message) {\n\t\t\tconst raw = message.preview || message.snippet || message.summary || message.bodyPreview\n\t\t\tif (typeof raw === 'string' && raw.trim() !== '') {\n\t\t\t\treturn raw.trim()\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/**\n\t\t * Whether the message is unread, tolerant of the several shapes\n\t\t * the backend may report read-state in.\n\t\t *\n\t\t * @param {object} message The message record.\n\t\t * @return {boolean} True when the message has not been read.\n\t\t */\n\t\tisUnread(message) {\n\t\t\tif (typeof message.unread === 'boolean') {\n\t\t\t\treturn message.unread\n\t\t\t}\n\t\t\tif (typeof message.isRead === 'boolean') {\n\t\t\t\treturn message.isRead === false\n\t\t\t}\n\t\t\tif (typeof message.seen === 'boolean') {\n\t\t\t\treturn message.seen === false\n\t\t\t}\n\t\t\tif (Array.isArray(message.flags)) {\n\t\t\t\treturn message.flags.indexOf('seen') === -1 && message.flags.indexOf('\\\\Seen') === -1\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\n\t\trawWhen(message) {\n\t\t\tconst raw = message.mailDate || message.date || message.linkedAt\n\t\t\tif (raw === undefined || raw === null || raw === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn raw\n\t\t},\n\n\t\t// Thread deep-link into NC Mail; undefined when the row lacks ids.\n\t\tthreadUrl(message) {\n\t\t\tconst accountId = message.mailAccountId\n\t\t\tconst messageId = message.mailMessageId\n\t\t\tif (accountId === undefined || accountId === null || messageId === undefined || messageId === null) {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl(`/apps/mail/box/${accountId}/thread/${messageId}`)\n\t\t\t\t: `/index.php/apps/mail/box/${accountId}/thread/${messageId}`\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/emails`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif (response.ok === true || response.status === 409) {\n\t\t\t\t\t// 409 = already-linked; treated as success since the row\n\t\t\t\t\t// already exists for this object.\n\t\t\t\t\tthis.reset()\n\t\t\t\t\tawait this.fetchMessages()\n\t\t\t\t} else {\n\t\t\t\t\tthis.errored = true\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnEmailTab] link failed', err)\n\t\t\t\tthis.errored = true\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sidebar-tab {\n\tpadding: 12px;\n\toverflow-x: hidden;\n}\n\n.cn-sidebar-tab__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 24px 0;\n}\n\n.cn-sidebar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-sidebar-tab__empty--error {\n\tcolor: var(--color-error);\n}\n\n.cn-sidebar-tab__load-more {\n\tmargin-top: 8px;\n}\n\n.cn-email-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\tmargin-bottom: 8px;\n}\n\n/* NC-Mail-style message list */\n.cn-email-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-email-tab__row {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 10px;\n\tpadding: 8px 6px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tcolor: inherit;\n\ttext-decoration: none;\n\tposition: relative;\n}\n\n/* Only a row with Mail ids is a link; the rest must not look clickable. */\na.cn-email-tab__row {\n\tcursor: pointer;\n}\n\na.cn-email-tab__row:hover {\n\tbackground-color: var(--color-background-hover);\n}\n\n.cn-email-tab__unread-dot {\n\tflex: 0 0 auto;\n\twidth: 8px;\n\talign-self: center;\n\tvisibility: hidden;\n}\n\n.cn-email-tab__unread-dot.is-shown {\n\tvisibility: visible;\n}\n\n.cn-email-tab__unread-dot.is-shown::before {\n\tcontent: '';\n\tdisplay: block;\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tbackground-color: var(--cn-email-accent, var(--color-primary-element));\n}\n\n.cn-email-tab__avatar {\n\tflex: 0 0 auto;\n}\n\n.cn-email-tab__body {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-email-tab__line {\n\tdisplay: flex;\n\talign-items: baseline;\n\tgap: 8px;\n}\n\n.cn-email-tab__subject {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-email-tab__row--unread .cn-email-tab__subject {\n\tfont-weight: bold;\n}\n\n.cn-email-tab__date {\n\tflex: 0 0 auto;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.8em;\n\twhite-space: nowrap;\n}\n\n.cn-email-tab__sender {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-email-tab__snippet {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tmargin-top: 1px;\n}\n</style>\n"],"names":["_createVNode","_createTextVNode","_createElementBlock","_toDisplayString","_openBlock","_Fragment","_renderList","_createBlock","_resolveDynamicComponent","_mergeProps","_normalizeClass","_createElementVNode"],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import { translate, translatePlural } from '@nextcloud/l10n';
|
|
|
2
2
|
import { NcLoadingIcon } from '@nextcloud/vue';
|
|
3
3
|
import SitemapOutline from 'vue-material-design-icons/SitemapOutline.vue';
|
|
4
4
|
import __sfc_main from '../../../components/CnDetailCard/CnDetailCard.vue.js';
|
|
5
|
+
import { safeHref } from '../../../utils/safeHref.js';
|
|
5
6
|
import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
|
|
6
7
|
|
|
7
8
|
const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'];
|
|
@@ -137,7 +138,7 @@ var script = {
|
|
|
137
138
|
},
|
|
138
139
|
|
|
139
140
|
opUrl(op) {
|
|
140
|
-
return op.url
|
|
141
|
+
return op.url && safeHref(op.url) !== '#' ? op.url : this.flowSettingsUrl
|
|
141
142
|
},
|
|
142
143
|
|
|
143
144
|
shortClass(className) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFlowCard.vue2.js","sources":["../../../../../src/integrations/builtin/flow/CnFlowCard.vue"],"sourcesContent":["<!--\n CnFlowCard — bespoke surface-aware widget for the `flow` (automation)\n integration.\n\n Replaces the generic CnIntegrationCard for the `flow` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N automations · M\n active\" + most-recent name with its entity binding.\n - detail-page : compact list of linked\n operations with entity / operation type / enabled indicator\n per row.\n - single-entity : chip with operation name + an\n enabled dot (referenceType: 'flow').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnFlowTab; for `single-entity` the optional `value` prop addresses a\n single operation by id (matching CnIntegrationCard's fetchSingle\n contract).\n\n See `openregister/openspec/changes/integration-flow/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span v-if=\"entity\" class=\"cn-flow-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<SitemapOutline :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"opUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ opTitle(entity) }}</a>\n\t\t\t\t<span\n\t\t\t\t\tclass=\"cn-flow-card__chip-enabled\"\n\t\t\t\t\t:class=\"enabledClass(entity)\" />\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-flow-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"operations.length === 0\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-flow-card__headline\">\n\t\t\t\t<div class=\"cn-flow-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-flow-card__headline-recent\">\n\t\t\t\t\t<SitemapOutline :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"opUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ opTitle(mostRecent) }}</a>\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"entityLabel(mostRecent)\"\n\t\t\t\t\t\tclass=\"cn-flow-card__headline-entity\">\n\t\t\t\t\t\t· {{ entityLabel(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"operations.length === 0\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-flow-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"op in displayedOperations\"\n\t\t\t\t\t:key=\"opKey(op)\"\n\t\t\t\t\tclass=\"cn-flow-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-flow-card__row--disabled': isDisabled(op) }\">\n\t\t\t\t\t<div class=\"cn-flow-card__row-header\">\n\t\t\t\t\t\t<SitemapOutline :size=\"16\" class=\"cn-flow-card__row-icon\" />\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"opUrl(op)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-flow-card__title\">{{ opTitle(op) }}</a>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclass=\"cn-flow-card__enabled\"\n\t\t\t\t\t\t\t:class=\"enabledClass(op)\"\n\t\t\t\t\t\t\t:aria-label=\"enabledLabel(op)\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<span v-if=\"rowMeta(op)\" class=\"cn-flow-card__subtitle\">{{ rowMeta(op) }}</span>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport SitemapOutline from 'vue-material-design-icons/SitemapOutline.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\n\n/**\n * CnFlowCard — bespoke surface-aware widget for the `flow` integration.\n *\n * Renders Flow-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnFlowCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, SitemapOutline },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'flow'`). */\n\t\tintegrationId: { type: String, default: 'flow' },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, required: true },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, required: true },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** Rendering surface (AD-19). */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (s) => VALID_SURFACES.includes(s),\n\t\t},\n\n\t\t/** Optional single-entity reference (operation id). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Automation') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => SitemapOutline },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No automations linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Flow is currently unavailable.') },\n\t\t/** URL of the NC Flow admin settings page. */\n\t\tflowSettingsUrl: { type: String, default: '/index.php/settings/admin/workflow' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\toperations: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\tdisplayedOperations() {\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\treturn this.operations.slice(0, COMPACT_LIMIT)\n\t\t\t}\n\t\t\treturn this.operations\n\t\t},\n\n\t\tactiveCount() {\n\t\t\treturn this.operations.reduce((sum, op) => sum + (this.isDisabled(op) ? 0 : 1), 0)\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.operations.length\n\t\t\tconst active = this.activeCount\n\t\t\tif (total === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (active === total) {\n\t\t\t\treturn n('nextcloud-vue', '{count} automation', '{count} automations', total, { count: total })\n\t\t\t}\n\t\t\tconst totalFragment = n('nextcloud-vue', '{count} automation', '{count} automations', total, { count: total })\n\t\t\tconst activeFragment = n('nextcloud-vue', '{count} active', '{count} active', active, { count: active })\n\t\t\treturn `${totalFragment} · ${activeFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.operations.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// NC Flow operations don't carry a stable \"created\" field,\n\t\t\t// so we just use the natural list order — the provider\n\t\t\t// returns admin-scoped operations newest-first via the\n\t\t\t// Manager API.\n\t\t\treturn this.operations[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\topKey(op) {\n\t\t\treturn op.id ?? op.title ?? ''\n\t\t},\n\n\t\topTitle(op) {\n\t\t\treturn op.title || op.displayName || op.name || this.shortClass(op.class) || t('nextcloud-vue', 'Untitled automation')\n\t\t},\n\n\t\topUrl(op) {\n\t\t\treturn op.url || this.flowSettingsUrl\n\t\t},\n\n\t\tshortClass(className) {\n\t\t\tif (!className) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst parts = String(className).split('\\\\')\n\t\t\treturn parts[parts.length - 1] || className\n\t\t},\n\n\t\tentityLabel(op) {\n\t\t\tif (!op.entity) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.shortClass(op.entity)\n\t\t},\n\n\t\tisDisabled(op) {\n\t\t\tconst v = op.enabled ?? op.data?.enabled\n\t\t\tif (v === undefined || v === null) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn v === false || v === 0 || v === '0'\n\t\t},\n\n\t\tenabledLabel(op) {\n\t\t\treturn this.isDisabled(op) === true\n\t\t\t\t? t('nextcloud-vue', 'Disabled')\n\t\t\t\t: t('nextcloud-vue', 'Enabled')\n\t\t},\n\n\t\tenabledClass(op) {\n\t\t\treturn this.isDisabled(op) === true\n\t\t\t\t? 'cn-flow-card__enabled--off'\n\t\t\t\t: 'cn-flow-card__enabled--on'\n\t\t},\n\n\t\trowMeta(op) {\n\t\t\tconst parts = []\n\t\t\tconst entity = this.entityLabel(op)\n\t\t\tif (entity !== '') {\n\t\t\t\tparts.push(entity)\n\t\t\t}\n\t\t\tconst operation = op.operation || ''\n\t\t\tif (operation !== '') {\n\t\t\t\tparts.push(operation)\n\t\t\t}\n\t\t\tconst checks = this.checksCount(op)\n\t\t\tif (checks > 0) {\n\t\t\t\tparts.push(t('nextcloud-vue', '{n} conditions', { n: checks }))\n\t\t\t}\n\t\t\treturn parts.join(' · ')\n\t\t},\n\n\t\tchecksCount(op) {\n\t\t\tconst raw = op.checks ?? op.data?.checks ?? []\n\t\t\tif (Array.isArray(raw) === true) {\n\t\t\t\treturn raw.length\n\t\t\t}\n\t\t\treturn 0\n\t\t},\n\n\t\tchipSubtitle(op) {\n\t\t\treturn this.entityLabel(op) || this.opTitle(op)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.operations = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.operations = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else if (response.status === 403) {\n\t\t\t\t\tthis.operations = []\n\t\t\t\t\tthis.degraded = t('nextcloud-vue', 'Flow operations are only visible to administrators.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.operations = []\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnFlowCard] failed to fetch operations', err)\n\t\t\t\tthis.operations = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnFlowCard] failed to fetch single operation', err)\n\t\t\t\tthis.entity = null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-flow-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-flow-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-flow-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-flow-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-flow-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-flow-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-flow-card__headline-entity {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-flow-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-hover);\n\tfont-size: 0.9em;\n\tmax-width: 100%;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-flow-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-flow-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-flow-card__chip-enabled,\n.cn-flow-card__enabled {\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tflex-shrink: 0;\n}\n\n.cn-flow-card__enabled--on {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-flow-card__enabled--off {\n\tbackground: var(--color-text-maxcontrast);\n}\n\n.cn-flow-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-flow-card__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 8px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-flow-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-flow-card__row--disabled {\n\topacity: 0.7;\n}\n\n.cn-flow-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-flow-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n\tflex-shrink: 0;\n}\n\n.cn-flow-card__title {\n\tflex: 1;\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-flow-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-flow-card__subtitle {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 24px;\n}\n</style>\n"],"names":["CnDetailCard","t","n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnFlowCard.vue2.js","sources":["../../../../../src/integrations/builtin/flow/CnFlowCard.vue"],"sourcesContent":["<!--\n CnFlowCard — bespoke surface-aware widget for the `flow` (automation)\n integration.\n\n Replaces the generic CnIntegrationCard for the `flow` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N automations · M\n active\" + most-recent name with its entity binding.\n - detail-page : compact list of linked\n operations with entity / operation type / enabled indicator\n per row.\n - single-entity : chip with operation name + an\n enabled dot (referenceType: 'flow').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnFlowTab; for `single-entity` the optional `value` prop addresses a\n single operation by id (matching CnIntegrationCard's fetchSingle\n contract).\n\n See `openregister/openspec/changes/integration-flow/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span v-if=\"entity\" class=\"cn-flow-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<SitemapOutline :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"opUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ opTitle(entity) }}</a>\n\t\t\t\t<span\n\t\t\t\t\tclass=\"cn-flow-card__chip-enabled\"\n\t\t\t\t\t:class=\"enabledClass(entity)\" />\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-flow-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"operations.length === 0\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-flow-card__headline\">\n\t\t\t\t<div class=\"cn-flow-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-flow-card__headline-recent\">\n\t\t\t\t\t<SitemapOutline :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"opUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ opTitle(mostRecent) }}</a>\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"entityLabel(mostRecent)\"\n\t\t\t\t\t\tclass=\"cn-flow-card__headline-entity\">\n\t\t\t\t\t\t· {{ entityLabel(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"operations.length === 0\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-flow-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"op in displayedOperations\"\n\t\t\t\t\t:key=\"opKey(op)\"\n\t\t\t\t\tclass=\"cn-flow-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-flow-card__row--disabled': isDisabled(op) }\">\n\t\t\t\t\t<div class=\"cn-flow-card__row-header\">\n\t\t\t\t\t\t<SitemapOutline :size=\"16\" class=\"cn-flow-card__row-icon\" />\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"opUrl(op)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-flow-card__title\">{{ opTitle(op) }}</a>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclass=\"cn-flow-card__enabled\"\n\t\t\t\t\t\t\t:class=\"enabledClass(op)\"\n\t\t\t\t\t\t\t:aria-label=\"enabledLabel(op)\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<span v-if=\"rowMeta(op)\" class=\"cn-flow-card__subtitle\">{{ rowMeta(op) }}</span>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport SitemapOutline from 'vue-material-design-icons/SitemapOutline.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\n\n/**\n * CnFlowCard — bespoke surface-aware widget for the `flow` integration.\n *\n * Renders Flow-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnFlowCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, SitemapOutline },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'flow'`). */\n\t\tintegrationId: { type: String, default: 'flow' },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, required: true },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, required: true },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** Rendering surface (AD-19). */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (s) => VALID_SURFACES.includes(s),\n\t\t},\n\n\t\t/** Optional single-entity reference (operation id). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Automation') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => SitemapOutline },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No automations linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Flow is currently unavailable.') },\n\t\t/** URL of the NC Flow admin settings page. */\n\t\tflowSettingsUrl: { type: String, default: '/index.php/settings/admin/workflow' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\toperations: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\tdisplayedOperations() {\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\treturn this.operations.slice(0, COMPACT_LIMIT)\n\t\t\t}\n\t\t\treturn this.operations\n\t\t},\n\n\t\tactiveCount() {\n\t\t\treturn this.operations.reduce((sum, op) => sum + (this.isDisabled(op) ? 0 : 1), 0)\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.operations.length\n\t\t\tconst active = this.activeCount\n\t\t\tif (total === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (active === total) {\n\t\t\t\treturn n('nextcloud-vue', '{count} automation', '{count} automations', total, { count: total })\n\t\t\t}\n\t\t\tconst totalFragment = n('nextcloud-vue', '{count} automation', '{count} automations', total, { count: total })\n\t\t\tconst activeFragment = n('nextcloud-vue', '{count} active', '{count} active', active, { count: active })\n\t\t\treturn `${totalFragment} · ${activeFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.operations.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// NC Flow operations don't carry a stable \"created\" field,\n\t\t\t// so we just use the natural list order — the provider\n\t\t\t// returns admin-scoped operations newest-first via the\n\t\t\t// Manager API.\n\t\t\treturn this.operations[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\topKey(op) {\n\t\t\treturn op.id ?? op.title ?? ''\n\t\t},\n\n\t\topTitle(op) {\n\t\t\treturn op.title || op.displayName || op.name || this.shortClass(op.class) || t('nextcloud-vue', 'Untitled automation')\n\t\t},\n\n\t\topUrl(op) {\n\t\t\treturn op.url && safeHref(op.url) !== '#' ? op.url : this.flowSettingsUrl\n\t\t},\n\n\t\tshortClass(className) {\n\t\t\tif (!className) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst parts = String(className).split('\\\\')\n\t\t\treturn parts[parts.length - 1] || className\n\t\t},\n\n\t\tentityLabel(op) {\n\t\t\tif (!op.entity) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.shortClass(op.entity)\n\t\t},\n\n\t\tisDisabled(op) {\n\t\t\tconst v = op.enabled ?? op.data?.enabled\n\t\t\tif (v === undefined || v === null) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn v === false || v === 0 || v === '0'\n\t\t},\n\n\t\tenabledLabel(op) {\n\t\t\treturn this.isDisabled(op) === true\n\t\t\t\t? t('nextcloud-vue', 'Disabled')\n\t\t\t\t: t('nextcloud-vue', 'Enabled')\n\t\t},\n\n\t\tenabledClass(op) {\n\t\t\treturn this.isDisabled(op) === true\n\t\t\t\t? 'cn-flow-card__enabled--off'\n\t\t\t\t: 'cn-flow-card__enabled--on'\n\t\t},\n\n\t\trowMeta(op) {\n\t\t\tconst parts = []\n\t\t\tconst entity = this.entityLabel(op)\n\t\t\tif (entity !== '') {\n\t\t\t\tparts.push(entity)\n\t\t\t}\n\t\t\tconst operation = op.operation || ''\n\t\t\tif (operation !== '') {\n\t\t\t\tparts.push(operation)\n\t\t\t}\n\t\t\tconst checks = this.checksCount(op)\n\t\t\tif (checks > 0) {\n\t\t\t\tparts.push(t('nextcloud-vue', '{n} conditions', { n: checks }))\n\t\t\t}\n\t\t\treturn parts.join(' · ')\n\t\t},\n\n\t\tchecksCount(op) {\n\t\t\tconst raw = op.checks ?? op.data?.checks ?? []\n\t\t\tif (Array.isArray(raw) === true) {\n\t\t\t\treturn raw.length\n\t\t\t}\n\t\t\treturn 0\n\t\t},\n\n\t\tchipSubtitle(op) {\n\t\t\treturn this.entityLabel(op) || this.opTitle(op)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.operations = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.operations = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else if (response.status === 403) {\n\t\t\t\t\tthis.operations = []\n\t\t\t\t\tthis.degraded = t('nextcloud-vue', 'Flow operations are only visible to administrators.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.operations = []\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnFlowCard] failed to fetch operations', err)\n\t\t\t\tthis.operations = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnFlowCard] failed to fetch single operation', err)\n\t\t\t\tthis.entity = null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-flow-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-flow-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-flow-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-flow-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-flow-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-flow-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-flow-card__headline-entity {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-flow-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-hover);\n\tfont-size: 0.9em;\n\tmax-width: 100%;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-flow-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-flow-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-flow-card__chip-enabled,\n.cn-flow-card__enabled {\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tflex-shrink: 0;\n}\n\n.cn-flow-card__enabled--on {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-flow-card__enabled--off {\n\tbackground: var(--color-text-maxcontrast);\n}\n\n.cn-flow-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-flow-card__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 8px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-flow-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-flow-card__row--disabled {\n\topacity: 0.7;\n}\n\n.cn-flow-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-flow-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n\tflex-shrink: 0;\n}\n\n.cn-flow-card__title {\n\tflex: 1;\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-flow-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-flow-card__subtitle {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 24px;\n}\n</style>\n"],"names":["CnDetailCard","t","n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFlowCard.vue3.js","sources":["../../../../../src/integrations/builtin/flow/CnFlowCard.vue?vue&type=template&id=3dfce3f2&lang.js"],"sourcesContent":["<!--\n CnFlowCard — bespoke surface-aware widget for the `flow` (automation)\n integration.\n\n Replaces the generic CnIntegrationCard for the `flow` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N automations · M\n active\" + most-recent name with its entity binding.\n - detail-page : compact list of linked\n operations with entity / operation type / enabled indicator\n per row.\n - single-entity : chip with operation name + an\n enabled dot (referenceType: 'flow').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnFlowTab; for `single-entity` the optional `value` prop addresses a\n single operation by id (matching CnIntegrationCard's fetchSingle\n contract).\n\n See `openregister/openspec/changes/integration-flow/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span v-if=\"entity\" class=\"cn-flow-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<SitemapOutline :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"opUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ opTitle(entity) }}</a>\n\t\t\t\t<span\n\t\t\t\t\tclass=\"cn-flow-card__chip-enabled\"\n\t\t\t\t\t:class=\"enabledClass(entity)\" />\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-flow-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"operations.length === 0\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-flow-card__headline\">\n\t\t\t\t<div class=\"cn-flow-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-flow-card__headline-recent\">\n\t\t\t\t\t<SitemapOutline :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"opUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ opTitle(mostRecent) }}</a>\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"entityLabel(mostRecent)\"\n\t\t\t\t\t\tclass=\"cn-flow-card__headline-entity\">\n\t\t\t\t\t\t· {{ entityLabel(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"operations.length === 0\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-flow-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"op in displayedOperations\"\n\t\t\t\t\t:key=\"opKey(op)\"\n\t\t\t\t\tclass=\"cn-flow-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-flow-card__row--disabled': isDisabled(op) }\">\n\t\t\t\t\t<div class=\"cn-flow-card__row-header\">\n\t\t\t\t\t\t<SitemapOutline :size=\"16\" class=\"cn-flow-card__row-icon\" />\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"opUrl(op)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-flow-card__title\">{{ opTitle(op) }}</a>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclass=\"cn-flow-card__enabled\"\n\t\t\t\t\t\t\t:class=\"enabledClass(op)\"\n\t\t\t\t\t\t\t:aria-label=\"enabledLabel(op)\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<span v-if=\"rowMeta(op)\" class=\"cn-flow-card__subtitle\">{{ rowMeta(op) }}</span>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport SitemapOutline from 'vue-material-design-icons/SitemapOutline.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\n\n/**\n * CnFlowCard — bespoke surface-aware widget for the `flow` integration.\n *\n * Renders Flow-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnFlowCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, SitemapOutline },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'flow'`). */\n\t\tintegrationId: { type: String, default: 'flow' },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, required: true },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, required: true },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** Rendering surface (AD-19). */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (s) => VALID_SURFACES.includes(s),\n\t\t},\n\n\t\t/** Optional single-entity reference (operation id). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Automation') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => SitemapOutline },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No automations linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Flow is currently unavailable.') },\n\t\t/** URL of the NC Flow admin settings page. */\n\t\tflowSettingsUrl: { type: String, default: '/index.php/settings/admin/workflow' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\toperations: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\tdisplayedOperations() {\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\treturn this.operations.slice(0, COMPACT_LIMIT)\n\t\t\t}\n\t\t\treturn this.operations\n\t\t},\n\n\t\tactiveCount() {\n\t\t\treturn this.operations.reduce((sum, op) => sum + (this.isDisabled(op) ? 0 : 1), 0)\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.operations.length\n\t\t\tconst active = this.activeCount\n\t\t\tif (total === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (active === total) {\n\t\t\t\treturn n('nextcloud-vue', '{count} automation', '{count} automations', total, { count: total })\n\t\t\t}\n\t\t\tconst totalFragment = n('nextcloud-vue', '{count} automation', '{count} automations', total, { count: total })\n\t\t\tconst activeFragment = n('nextcloud-vue', '{count} active', '{count} active', active, { count: active })\n\t\t\treturn `${totalFragment} · ${activeFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.operations.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// NC Flow operations don't carry a stable \"created\" field,\n\t\t\t// so we just use the natural list order — the provider\n\t\t\t// returns admin-scoped operations newest-first via the\n\t\t\t// Manager API.\n\t\t\treturn this.operations[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\topKey(op) {\n\t\t\treturn op.id ?? op.title ?? ''\n\t\t},\n\n\t\topTitle(op) {\n\t\t\treturn op.title || op.displayName || op.name || this.shortClass(op.class) || t('nextcloud-vue', 'Untitled automation')\n\t\t},\n\n\t\topUrl(op) {\n\t\t\treturn op.url || this.flowSettingsUrl\n\t\t},\n\n\t\tshortClass(className) {\n\t\t\tif (!className) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst parts = String(className).split('\\\\')\n\t\t\treturn parts[parts.length - 1] || className\n\t\t},\n\n\t\tentityLabel(op) {\n\t\t\tif (!op.entity) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.shortClass(op.entity)\n\t\t},\n\n\t\tisDisabled(op) {\n\t\t\tconst v = op.enabled ?? op.data?.enabled\n\t\t\tif (v === undefined || v === null) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn v === false || v === 0 || v === '0'\n\t\t},\n\n\t\tenabledLabel(op) {\n\t\t\treturn this.isDisabled(op) === true\n\t\t\t\t? t('nextcloud-vue', 'Disabled')\n\t\t\t\t: t('nextcloud-vue', 'Enabled')\n\t\t},\n\n\t\tenabledClass(op) {\n\t\t\treturn this.isDisabled(op) === true\n\t\t\t\t? 'cn-flow-card__enabled--off'\n\t\t\t\t: 'cn-flow-card__enabled--on'\n\t\t},\n\n\t\trowMeta(op) {\n\t\t\tconst parts = []\n\t\t\tconst entity = this.entityLabel(op)\n\t\t\tif (entity !== '') {\n\t\t\t\tparts.push(entity)\n\t\t\t}\n\t\t\tconst operation = op.operation || ''\n\t\t\tif (operation !== '') {\n\t\t\t\tparts.push(operation)\n\t\t\t}\n\t\t\tconst checks = this.checksCount(op)\n\t\t\tif (checks > 0) {\n\t\t\t\tparts.push(t('nextcloud-vue', '{n} conditions', { n: checks }))\n\t\t\t}\n\t\t\treturn parts.join(' · ')\n\t\t},\n\n\t\tchecksCount(op) {\n\t\t\tconst raw = op.checks ?? op.data?.checks ?? []\n\t\t\tif (Array.isArray(raw) === true) {\n\t\t\t\treturn raw.length\n\t\t\t}\n\t\t\treturn 0\n\t\t},\n\n\t\tchipSubtitle(op) {\n\t\t\treturn this.entityLabel(op) || this.opTitle(op)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.operations = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.operations = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else if (response.status === 403) {\n\t\t\t\t\tthis.operations = []\n\t\t\t\t\tthis.degraded = t('nextcloud-vue', 'Flow operations are only visible to administrators.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.operations = []\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnFlowCard] failed to fetch operations', err)\n\t\t\t\tthis.operations = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnFlowCard] failed to fetch single operation', err)\n\t\t\t\tthis.entity = null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-flow-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-flow-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-flow-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-flow-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-flow-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-flow-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-flow-card__headline-entity {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-flow-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-hover);\n\tfont-size: 0.9em;\n\tmax-width: 100%;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-flow-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-flow-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-flow-card__chip-enabled,\n.cn-flow-card__enabled {\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tflex-shrink: 0;\n}\n\n.cn-flow-card__enabled--on {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-flow-card__enabled--off {\n\tbackground: var(--color-text-maxcontrast);\n}\n\n.cn-flow-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-flow-card__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 8px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-flow-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-flow-card__row--disabled {\n\topacity: 0.7;\n}\n\n.cn-flow-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-flow-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n\tflex-shrink: 0;\n}\n\n.cn-flow-card__title {\n\tflex: 1;\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-flow-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-flow-card__subtitle {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 24px;\n}\n</style>\n"],"names":["_createBlock","_createElementBlock","_Fragment","_createVNode","_createElementVNode","_toDisplayString","_normalizeClass","_openBlock"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnFlowCard.vue3.js","sources":["../../../../../src/integrations/builtin/flow/CnFlowCard.vue?vue&type=template&id=3dfce3f2&lang.js"],"sourcesContent":["<!--\n CnFlowCard — bespoke surface-aware widget for the `flow` (automation)\n integration.\n\n Replaces the generic CnIntegrationCard for the `flow` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N automations · M\n active\" + most-recent name with its entity binding.\n - detail-page : compact list of linked\n operations with entity / operation type / enabled indicator\n per row.\n - single-entity : chip with operation name + an\n enabled dot (referenceType: 'flow').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnFlowTab; for `single-entity` the optional `value` prop addresses a\n single operation by id (matching CnIntegrationCard's fetchSingle\n contract).\n\n See `openregister/openspec/changes/integration-flow/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span v-if=\"entity\" class=\"cn-flow-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<SitemapOutline :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"opUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ opTitle(entity) }}</a>\n\t\t\t\t<span\n\t\t\t\t\tclass=\"cn-flow-card__chip-enabled\"\n\t\t\t\t\t:class=\"enabledClass(entity)\" />\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-flow-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"operations.length === 0\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-flow-card__headline\">\n\t\t\t\t<div class=\"cn-flow-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-flow-card__headline-recent\">\n\t\t\t\t\t<SitemapOutline :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"opUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ opTitle(mostRecent) }}</a>\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"entityLabel(mostRecent)\"\n\t\t\t\t\t\tclass=\"cn-flow-card__headline-entity\">\n\t\t\t\t\t\t· {{ entityLabel(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"operations.length === 0\" class=\"cn-flow-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-flow-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"op in displayedOperations\"\n\t\t\t\t\t:key=\"opKey(op)\"\n\t\t\t\t\tclass=\"cn-flow-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-flow-card__row--disabled': isDisabled(op) }\">\n\t\t\t\t\t<div class=\"cn-flow-card__row-header\">\n\t\t\t\t\t\t<SitemapOutline :size=\"16\" class=\"cn-flow-card__row-icon\" />\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"opUrl(op)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-flow-card__title\">{{ opTitle(op) }}</a>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclass=\"cn-flow-card__enabled\"\n\t\t\t\t\t\t\t:class=\"enabledClass(op)\"\n\t\t\t\t\t\t\t:aria-label=\"enabledLabel(op)\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<span v-if=\"rowMeta(op)\" class=\"cn-flow-card__subtitle\">{{ rowMeta(op) }}</span>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport SitemapOutline from 'vue-material-design-icons/SitemapOutline.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\n\n/**\n * CnFlowCard — bespoke surface-aware widget for the `flow` integration.\n *\n * Renders Flow-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnFlowCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, SitemapOutline },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'flow'`). */\n\t\tintegrationId: { type: String, default: 'flow' },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, required: true },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, required: true },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** Rendering surface (AD-19). */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (s) => VALID_SURFACES.includes(s),\n\t\t},\n\n\t\t/** Optional single-entity reference (operation id). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Automation') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => SitemapOutline },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No automations linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Flow is currently unavailable.') },\n\t\t/** URL of the NC Flow admin settings page. */\n\t\tflowSettingsUrl: { type: String, default: '/index.php/settings/admin/workflow' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\toperations: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\tdisplayedOperations() {\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\treturn this.operations.slice(0, COMPACT_LIMIT)\n\t\t\t}\n\t\t\treturn this.operations\n\t\t},\n\n\t\tactiveCount() {\n\t\t\treturn this.operations.reduce((sum, op) => sum + (this.isDisabled(op) ? 0 : 1), 0)\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.operations.length\n\t\t\tconst active = this.activeCount\n\t\t\tif (total === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (active === total) {\n\t\t\t\treturn n('nextcloud-vue', '{count} automation', '{count} automations', total, { count: total })\n\t\t\t}\n\t\t\tconst totalFragment = n('nextcloud-vue', '{count} automation', '{count} automations', total, { count: total })\n\t\t\tconst activeFragment = n('nextcloud-vue', '{count} active', '{count} active', active, { count: active })\n\t\t\treturn `${totalFragment} · ${activeFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.operations.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// NC Flow operations don't carry a stable \"created\" field,\n\t\t\t// so we just use the natural list order — the provider\n\t\t\t// returns admin-scoped operations newest-first via the\n\t\t\t// Manager API.\n\t\t\treturn this.operations[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\topKey(op) {\n\t\t\treturn op.id ?? op.title ?? ''\n\t\t},\n\n\t\topTitle(op) {\n\t\t\treturn op.title || op.displayName || op.name || this.shortClass(op.class) || t('nextcloud-vue', 'Untitled automation')\n\t\t},\n\n\t\topUrl(op) {\n\t\t\treturn op.url && safeHref(op.url) !== '#' ? op.url : this.flowSettingsUrl\n\t\t},\n\n\t\tshortClass(className) {\n\t\t\tif (!className) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst parts = String(className).split('\\\\')\n\t\t\treturn parts[parts.length - 1] || className\n\t\t},\n\n\t\tentityLabel(op) {\n\t\t\tif (!op.entity) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.shortClass(op.entity)\n\t\t},\n\n\t\tisDisabled(op) {\n\t\t\tconst v = op.enabled ?? op.data?.enabled\n\t\t\tif (v === undefined || v === null) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn v === false || v === 0 || v === '0'\n\t\t},\n\n\t\tenabledLabel(op) {\n\t\t\treturn this.isDisabled(op) === true\n\t\t\t\t? t('nextcloud-vue', 'Disabled')\n\t\t\t\t: t('nextcloud-vue', 'Enabled')\n\t\t},\n\n\t\tenabledClass(op) {\n\t\t\treturn this.isDisabled(op) === true\n\t\t\t\t? 'cn-flow-card__enabled--off'\n\t\t\t\t: 'cn-flow-card__enabled--on'\n\t\t},\n\n\t\trowMeta(op) {\n\t\t\tconst parts = []\n\t\t\tconst entity = this.entityLabel(op)\n\t\t\tif (entity !== '') {\n\t\t\t\tparts.push(entity)\n\t\t\t}\n\t\t\tconst operation = op.operation || ''\n\t\t\tif (operation !== '') {\n\t\t\t\tparts.push(operation)\n\t\t\t}\n\t\t\tconst checks = this.checksCount(op)\n\t\t\tif (checks > 0) {\n\t\t\t\tparts.push(t('nextcloud-vue', '{n} conditions', { n: checks }))\n\t\t\t}\n\t\t\treturn parts.join(' · ')\n\t\t},\n\n\t\tchecksCount(op) {\n\t\t\tconst raw = op.checks ?? op.data?.checks ?? []\n\t\t\tif (Array.isArray(raw) === true) {\n\t\t\t\treturn raw.length\n\t\t\t}\n\t\t\treturn 0\n\t\t},\n\n\t\tchipSubtitle(op) {\n\t\t\treturn this.entityLabel(op) || this.opTitle(op)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.operations = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.operations = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else if (response.status === 403) {\n\t\t\t\t\tthis.operations = []\n\t\t\t\t\tthis.degraded = t('nextcloud-vue', 'Flow operations are only visible to administrators.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.operations = []\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnFlowCard] failed to fetch operations', err)\n\t\t\t\tthis.operations = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnFlowCard] failed to fetch single operation', err)\n\t\t\t\tthis.entity = null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-flow-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-flow-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-flow-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-flow-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-flow-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-flow-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-flow-card__headline-entity {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-flow-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-hover);\n\tfont-size: 0.9em;\n\tmax-width: 100%;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-flow-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-flow-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-flow-card__chip-enabled,\n.cn-flow-card__enabled {\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tflex-shrink: 0;\n}\n\n.cn-flow-card__enabled--on {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-flow-card__enabled--off {\n\tbackground: var(--color-text-maxcontrast);\n}\n\n.cn-flow-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-flow-card__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 8px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-flow-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-flow-card__row--disabled {\n\topacity: 0.7;\n}\n\n.cn-flow-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-flow-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n\tflex-shrink: 0;\n}\n\n.cn-flow-card__title {\n\tflex: 1;\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-flow-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-flow-card__subtitle {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 24px;\n}\n</style>\n"],"names":["_createBlock","_createElementBlock","_Fragment","_createVNode","_createElementVNode","_toDisplayString","_normalizeClass","_openBlock"],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@ import Plus from 'vue-material-design-icons/Plus.vue';
|
|
|
6
6
|
import SitemapOutline from 'vue-material-design-icons/SitemapOutline.vue';
|
|
7
7
|
import __sfc_main from '../../../components/CnFlowOperationPicker/CnFlowOperationPicker.vue.js';
|
|
8
8
|
import __sfc_main$1 from '../../../components/CnStatusBadge/CnStatusBadge.vue.js';
|
|
9
|
+
import { safeHref } from '../../../utils/safeHref.js';
|
|
9
10
|
import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -85,7 +86,7 @@ var script = {
|
|
|
85
86
|
},
|
|
86
87
|
|
|
87
88
|
opUrl(op) {
|
|
88
|
-
return op.url
|
|
89
|
+
return op.url && safeHref(op.url) !== '#' ? op.url : this.flowSettingsUrl
|
|
89
90
|
},
|
|
90
91
|
|
|
91
92
|
shortClass(className) {
|