@conduction/nextcloud-vue 2.56.0-beta.4 → 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/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/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 +1 -0
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +1 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +71 -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/nextcloud-vue.cjs.js +809 -383
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +71 -7
- package/l10n/en.json +1 -0
- package/l10n/nl.json +1 -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/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/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/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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnTimeTrackerCard.vue2.js","sources":["../../../../../src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue"],"sourcesContent":["<!--\n CnTimeTrackerCard — bespoke surface-aware widget for the `time-tracker`\n integration.\n\n Replaces the generic CnIntegrationCard for the `time-tracker` leaf.\n Branches on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N tracked entries\";\n sub-line with total tracked time (hours + minutes) summed over\n all linked time-entries; plus a most-recent row link.\n - detail-page : bounded row list with kind chip,\n duration, billable indicator — same shape as the tab but\n read-only.\n - single-entity : chip with row title + kind pill\n (referenceType: 'time-tracker').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnTimeTrackerTab; for `single-entity` the optional `value` prop\n addresses a single row by id.\n\n Note: the leaf slug is `time-tracker` (with a hyphen); the underlying\n NC app id is `timemanager` (no hyphen).\n\n See `openregister/openspec/changes/integration-time-tracker/` for the\n spec delta, ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardHeaderTitle\" :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-time-tracker-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<Clock :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"rowUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ rowTitle(entity) }}</a>\n\t\t\t\t<span class=\"cn-time-tracker-card__chip-pill\" :class=\"kindChipClass(entity)\">\n\t\t\t\t\t{{ kindLabel(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-time-tracker-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + total tracked time + 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-time-tracker-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-time-tracker-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-time-tracker-card__headline\">\n\t\t\t\t<div class=\"cn-time-tracker-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=\"totalDurationLabel\" class=\"cn-time-tracker-card__total\">\n\t\t\t\t\t<Timer :size=\"14\" />\n\t\t\t\t\t{{ totalDurationLabel }}\n\t\t\t\t</div>\n\t\t\t\t<ul v-if=\"kindDistribution.length > 0\" class=\"cn-time-tracker-card__distribution\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in kindDistribution\"\n\t\t\t\t\t\t:key=\"bucket.key\"\n\t\t\t\t\t\tclass=\"cn-time-tracker-card__distribution-row\">\n\t\t\t\t\t\t<span class=\"cn-time-tracker-card__distribution-label\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclass=\"cn-time-tracker-card__distribution-dot\"\n\t\t\t\t\t\t\t\t:class=\"`cn-time-tracker-card__distribution-dot--${bucket.key}`\" />\n\t\t\t\t\t\t\t{{ bucket.label }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-card__distribution-count\">{{ bucket.count }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-time-tracker-card__headline-recent\">\n\t\t\t\t\t<Clock :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"rowUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ rowTitle(mostRecent) }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: bounded row list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-time-tracker-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-time-tracker-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-time-tracker-card__rows\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-time-tracker-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-time-tracker-card__row--highlight': isLinked(row) }\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\tclass=\"cn-time-tracker-card__title\">{{ rowTitle(row) }}</a>\n\t\t\t\t\t<span class=\"cn-time-tracker-card__chip-pill\" :class=\"kindChipClass(row)\">\n\t\t\t\t\t\t{{ kindLabel(row) }}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"durationLabel(row)\" class=\"cn-time-tracker-card__duration\">\n\t\t\t\t\t\t{{ durationLabel(row) }}\n\t\t\t\t\t</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 Clock from 'vue-material-design-icons/Clock.vue'\nimport Timer from 'vue-material-design-icons/Timer.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']\n\n/**\n * CnTimeTrackerCard — bespoke surface-aware widget for the\n * `time-tracker` integration. See file-level docblock.\n */\nexport default {\n\tname: 'CnTimeTrackerCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, Clock, Timer },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'time-tracker'`). */\n\t\tintegrationId: { type: String, default: 'time-tracker' },\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 (row id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Time tracker') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => Clock },\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 tracked time linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC TimeManager is currently unavailable.') },\n\t\t/** URL of the NC TimeManager app entry. */\n\t\ttimeTrackerAppUrl: { type: String, default: '/index.php/apps/timemanager' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardHeaderTitle() {\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\t/**\n\t\t * Rows bucketed by kind so the dashboard surface can show the\n\t\t * clients / tasks / entries distribution.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, count: number}>}\n\t\t */\n\t\tkindDistribution() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst kind = this.rowKind(row)\n\t\t\t\tif (!groups.has(kind)) {\n\t\t\t\t\tgroups.set(kind, { key: kind, label: this.kindLabel(row), count: 0 })\n\t\t\t\t}\n\t\t\t\tgroups.get(kind).count += 1\n\t\t\t}\n\t\t\treturn Array.from(groups.values())\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.rows.length\n\t\t\treturn n('nextcloud-vue', '{count} entry', '{count} entries', total, { count: total })\n\t\t},\n\n\t\t/**\n\t\t * Total tracked seconds across all rows that carry a `duration`\n\t\t * (clients without a duration don't contribute to this total —\n\t\t * they're parent containers).\n\t\t *\n\t\t * @return {number} Seconds total.\n\t\t */\n\t\ttotalSeconds() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst d = this.durationSeconds(row)\n\t\t\t\tif (d !== null) {\n\t\t\t\t\ttotal += d\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\ttotalDurationLabel() {\n\t\t\tif (this.totalSeconds === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst totalMinutes = Math.round(this.totalSeconds / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn t('nextcloud-vue', '{hours}h {minutes}m tracked', { hours, minutes })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{minutes}m tracked', { minutes })\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.rows.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst sorted = [...this.rows].sort((a, b) => {\n\t\t\t\tconst ta = Date.parse(a.startedAt ?? a.started_at ?? a.linkedAt ?? a.created_at ?? '') || 0\n\t\t\t\tconst tb = Date.parse(b.startedAt ?? b.started_at ?? b.linkedAt ?? b.created_at ?? '') || 0\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[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\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? row.data?.uuid ?? ''\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\trowKind(row) {\n\t\t\tconst k = String(row.kind ?? row.type ?? '').toLowerCase()\n\t\t\tif (k === 'task') {\n\t\t\t\treturn 'task'\n\t\t\t}\n\t\t\tif (k === 'time' || k === 'entry' || k === 'time-entry') {\n\t\t\t\treturn 'time'\n\t\t\t}\n\t\t\treturn 'client'\n\t\t},\n\n\t\tkindLabel(row) {\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tif (kind === 'task') {\n\t\t\t\treturn t('nextcloud-vue', 'Task')\n\t\t\t}\n\t\t\tif (kind === 'time') {\n\t\t\t\treturn t('nextcloud-vue', 'Entry')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tkindChipClass(row) {\n\t\t\treturn `cn-time-tracker-card__chip-pill--${this.rowKind(row)}`\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url) {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (kind === 'client') {\n\t\t\t\tconst clientId = row.clientId ?? id\n\t\t\t\tif (clientId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/clients/${clientId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'task') {\n\t\t\t\tconst taskId = row.taskId ?? id\n\t\t\t\tif (taskId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/tasks/${taskId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'time' && id) {\n\t\t\t\treturn `/index.php/apps/timemanager/times/${id}`\n\t\t\t}\n\t\t\tif (id) {\n\t\t\t\treturn `/index.php/apps/timemanager/projects/${id}`\n\t\t\t}\n\t\t\treturn this.timeTrackerAppUrl\n\t\t},\n\n\t\tdurationSeconds(row) {\n\t\t\tconst raw = row.duration ?? row.seconds ?? row.data?.duration ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\tdurationLabel(row) {\n\t\t\tconst total = this.durationSeconds(row)\n\t\t\tif (total === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst totalMinutes = Math.round(total / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn `${hours}h ${minutes}m`\n\t\t\t}\n\t\t\treturn `${minutes}m`\n\t\t},\n\n\t\tisLinked(row) {\n\t\t\tif (!this.value) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn String(this.rowKey(row)) === String(this.value)\n\t\t},\n\n\t\tchipSubtitle(row) {\n\t\t\treturn this.durationLabel(row) || this.kindLabel(row)\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.rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\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('[CnTimeTrackerCard] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\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('[CnTimeTrackerCard] failed to fetch single row', 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-time-tracker-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-time-tracker-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-time-tracker-card__headline-line {\n\tfont-size: 1.05em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-time-tracker-card__total {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n\tfont-size: 0.95em;\n\tcolor: var(--color-main-text);\n\tfont-weight: 600;\n}\n\n.cn-time-tracker-card__distribution {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-time-tracker-card__distribution-row {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-card__distribution-label {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.cn-time-tracker-card__distribution-dot {\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n}\n\n.cn-time-tracker-card__distribution-dot--client {\n\tbackground: var(--color-primary-element, #21468B);\n}\n\n.cn-time-tracker-card__distribution-dot--task {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-time-tracker-card__distribution-dot--time {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-time-tracker-card__distribution-count {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-time-tracker-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-time-tracker-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-time-tracker-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-time-tracker-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-time-tracker-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-time-tracker-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-time-tracker-card__chip-pill {\n\tdisplay: inline-block;\n\tpadding: 1px 6px;\n\tfont-size: 0.7em;\n\tfont-weight: 600;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-card__chip-pill--client {\n\tbackground: var(--color-primary-element, #21468B);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-card__chip-pill--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-card__chip-pill--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-card__rows {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-time-tracker-card__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tfont-size: 0.85em;\n}\n\n.cn-time-tracker-card__row--highlight {\n\tborder-color: var(--color-primary-element, #21468B);\n\tbackground: var(--color-primary-element-light, var(--color-background-darker));\n\tfont-weight: 600;\n}\n\n.cn-time-tracker-card__title {\n\tflex: 1;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\na.cn-time-tracker-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-time-tracker-card__duration {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\tfont-size: 0.85em;\n}\n</style>\n"],"names":["CnDetailCard","t","n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnTimeTrackerCard.vue2.js","sources":["../../../../../src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue"],"sourcesContent":["<!--\n CnTimeTrackerCard — bespoke surface-aware widget for the `time-tracker`\n integration.\n\n Replaces the generic CnIntegrationCard for the `time-tracker` leaf.\n Branches on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N tracked entries\";\n sub-line with total tracked time (hours + minutes) summed over\n all linked time-entries; plus a most-recent row link.\n - detail-page : bounded row list with kind chip,\n duration, billable indicator — same shape as the tab but\n read-only.\n - single-entity : chip with row title + kind pill\n (referenceType: 'time-tracker').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnTimeTrackerTab; for `single-entity` the optional `value` prop\n addresses a single row by id.\n\n Note: the leaf slug is `time-tracker` (with a hyphen); the underlying\n NC app id is `timemanager` (no hyphen).\n\n See `openregister/openspec/changes/integration-time-tracker/` for the\n spec delta, ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardHeaderTitle\" :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-time-tracker-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<Clock :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"rowUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ rowTitle(entity) }}</a>\n\t\t\t\t<span class=\"cn-time-tracker-card__chip-pill\" :class=\"kindChipClass(entity)\">\n\t\t\t\t\t{{ kindLabel(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-time-tracker-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + total tracked time + 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-time-tracker-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-time-tracker-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-time-tracker-card__headline\">\n\t\t\t\t<div class=\"cn-time-tracker-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=\"totalDurationLabel\" class=\"cn-time-tracker-card__total\">\n\t\t\t\t\t<Timer :size=\"14\" />\n\t\t\t\t\t{{ totalDurationLabel }}\n\t\t\t\t</div>\n\t\t\t\t<ul v-if=\"kindDistribution.length > 0\" class=\"cn-time-tracker-card__distribution\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in kindDistribution\"\n\t\t\t\t\t\t:key=\"bucket.key\"\n\t\t\t\t\t\tclass=\"cn-time-tracker-card__distribution-row\">\n\t\t\t\t\t\t<span class=\"cn-time-tracker-card__distribution-label\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclass=\"cn-time-tracker-card__distribution-dot\"\n\t\t\t\t\t\t\t\t:class=\"`cn-time-tracker-card__distribution-dot--${bucket.key}`\" />\n\t\t\t\t\t\t\t{{ bucket.label }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-card__distribution-count\">{{ bucket.count }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-time-tracker-card__headline-recent\">\n\t\t\t\t\t<Clock :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"rowUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ rowTitle(mostRecent) }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: bounded row list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-time-tracker-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-time-tracker-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-time-tracker-card__rows\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-time-tracker-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-time-tracker-card__row--highlight': isLinked(row) }\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\tclass=\"cn-time-tracker-card__title\">{{ rowTitle(row) }}</a>\n\t\t\t\t\t<span class=\"cn-time-tracker-card__chip-pill\" :class=\"kindChipClass(row)\">\n\t\t\t\t\t\t{{ kindLabel(row) }}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"durationLabel(row)\" class=\"cn-time-tracker-card__duration\">\n\t\t\t\t\t\t{{ durationLabel(row) }}\n\t\t\t\t\t</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 Clock from 'vue-material-design-icons/Clock.vue'\nimport Timer from 'vue-material-design-icons/Timer.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']\n\n/**\n * CnTimeTrackerCard — bespoke surface-aware widget for the\n * `time-tracker` integration. See file-level docblock.\n */\nexport default {\n\tname: 'CnTimeTrackerCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, Clock, Timer },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'time-tracker'`). */\n\t\tintegrationId: { type: String, default: 'time-tracker' },\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 (row id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Time tracker') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => Clock },\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 tracked time linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC TimeManager is currently unavailable.') },\n\t\t/** URL of the NC TimeManager app entry. */\n\t\ttimeTrackerAppUrl: { type: String, default: '/index.php/apps/timemanager' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardHeaderTitle() {\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\t/**\n\t\t * Rows bucketed by kind so the dashboard surface can show the\n\t\t * clients / tasks / entries distribution.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, count: number}>}\n\t\t */\n\t\tkindDistribution() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst kind = this.rowKind(row)\n\t\t\t\tif (!groups.has(kind)) {\n\t\t\t\t\tgroups.set(kind, { key: kind, label: this.kindLabel(row), count: 0 })\n\t\t\t\t}\n\t\t\t\tgroups.get(kind).count += 1\n\t\t\t}\n\t\t\treturn Array.from(groups.values())\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.rows.length\n\t\t\treturn n('nextcloud-vue', '{count} entry', '{count} entries', total, { count: total })\n\t\t},\n\n\t\t/**\n\t\t * Total tracked seconds across all rows that carry a `duration`\n\t\t * (clients without a duration don't contribute to this total —\n\t\t * they're parent containers).\n\t\t *\n\t\t * @return {number} Seconds total.\n\t\t */\n\t\ttotalSeconds() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst d = this.durationSeconds(row)\n\t\t\t\tif (d !== null) {\n\t\t\t\t\ttotal += d\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\ttotalDurationLabel() {\n\t\t\tif (this.totalSeconds === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst totalMinutes = Math.round(this.totalSeconds / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn t('nextcloud-vue', '{hours}h {minutes}m tracked', { hours, minutes })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{minutes}m tracked', { minutes })\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.rows.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst sorted = [...this.rows].sort((a, b) => {\n\t\t\t\tconst ta = Date.parse(a.startedAt ?? a.started_at ?? a.linkedAt ?? a.created_at ?? '') || 0\n\t\t\t\tconst tb = Date.parse(b.startedAt ?? b.started_at ?? b.linkedAt ?? b.created_at ?? '') || 0\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[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\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? row.data?.uuid ?? ''\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\trowKind(row) {\n\t\t\tconst k = String(row.kind ?? row.type ?? '').toLowerCase()\n\t\t\tif (k === 'task') {\n\t\t\t\treturn 'task'\n\t\t\t}\n\t\t\tif (k === 'time' || k === 'entry' || k === 'time-entry') {\n\t\t\t\treturn 'time'\n\t\t\t}\n\t\t\treturn 'client'\n\t\t},\n\n\t\tkindLabel(row) {\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tif (kind === 'task') {\n\t\t\t\treturn t('nextcloud-vue', 'Task')\n\t\t\t}\n\t\t\tif (kind === 'time') {\n\t\t\t\treturn t('nextcloud-vue', 'Entry')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tkindChipClass(row) {\n\t\t\treturn `cn-time-tracker-card__chip-pill--${this.rowKind(row)}`\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url && safeHref(row.url) !== '#') {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (kind === 'client') {\n\t\t\t\tconst clientId = row.clientId ?? id\n\t\t\t\tif (clientId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/clients/${clientId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'task') {\n\t\t\t\tconst taskId = row.taskId ?? id\n\t\t\t\tif (taskId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/tasks/${taskId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'time' && id) {\n\t\t\t\treturn `/index.php/apps/timemanager/times/${id}`\n\t\t\t}\n\t\t\tif (id) {\n\t\t\t\treturn `/index.php/apps/timemanager/projects/${id}`\n\t\t\t}\n\t\t\treturn this.timeTrackerAppUrl\n\t\t},\n\n\t\tdurationSeconds(row) {\n\t\t\tconst raw = row.duration ?? row.seconds ?? row.data?.duration ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\tdurationLabel(row) {\n\t\t\tconst total = this.durationSeconds(row)\n\t\t\tif (total === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst totalMinutes = Math.round(total / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn `${hours}h ${minutes}m`\n\t\t\t}\n\t\t\treturn `${minutes}m`\n\t\t},\n\n\t\tisLinked(row) {\n\t\t\tif (!this.value) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn String(this.rowKey(row)) === String(this.value)\n\t\t},\n\n\t\tchipSubtitle(row) {\n\t\t\treturn this.durationLabel(row) || this.kindLabel(row)\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.rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\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('[CnTimeTrackerCard] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\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('[CnTimeTrackerCard] failed to fetch single row', 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-time-tracker-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-time-tracker-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-time-tracker-card__headline-line {\n\tfont-size: 1.05em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-time-tracker-card__total {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n\tfont-size: 0.95em;\n\tcolor: var(--color-main-text);\n\tfont-weight: 600;\n}\n\n.cn-time-tracker-card__distribution {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-time-tracker-card__distribution-row {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-card__distribution-label {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.cn-time-tracker-card__distribution-dot {\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n}\n\n.cn-time-tracker-card__distribution-dot--client {\n\tbackground: var(--color-primary-element, #21468B);\n}\n\n.cn-time-tracker-card__distribution-dot--task {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-time-tracker-card__distribution-dot--time {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-time-tracker-card__distribution-count {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-time-tracker-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-time-tracker-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-time-tracker-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-time-tracker-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-time-tracker-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-time-tracker-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-time-tracker-card__chip-pill {\n\tdisplay: inline-block;\n\tpadding: 1px 6px;\n\tfont-size: 0.7em;\n\tfont-weight: 600;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-card__chip-pill--client {\n\tbackground: var(--color-primary-element, #21468B);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-card__chip-pill--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-card__chip-pill--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-card__rows {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-time-tracker-card__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tfont-size: 0.85em;\n}\n\n.cn-time-tracker-card__row--highlight {\n\tborder-color: var(--color-primary-element, #21468B);\n\tbackground: var(--color-primary-element-light, var(--color-background-darker));\n\tfont-weight: 600;\n}\n\n.cn-time-tracker-card__title {\n\tflex: 1;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\na.cn-time-tracker-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-time-tracker-card__duration {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\tfont-size: 0.85em;\n}\n</style>\n"],"names":["CnDetailCard","t","n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnTimeTrackerCard.vue3.js","sources":["../../../../../src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue?vue&type=template&id=4ee344cc&lang.js"],"sourcesContent":["<!--\n CnTimeTrackerCard — bespoke surface-aware widget for the `time-tracker`\n integration.\n\n Replaces the generic CnIntegrationCard for the `time-tracker` leaf.\n Branches on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N tracked entries\";\n sub-line with total tracked time (hours + minutes) summed over\n all linked time-entries; plus a most-recent row link.\n - detail-page : bounded row list with kind chip,\n duration, billable indicator — same shape as the tab but\n read-only.\n - single-entity : chip with row title + kind pill\n (referenceType: 'time-tracker').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnTimeTrackerTab; for `single-entity` the optional `value` prop\n addresses a single row by id.\n\n Note: the leaf slug is `time-tracker` (with a hyphen); the underlying\n NC app id is `timemanager` (no hyphen).\n\n See `openregister/openspec/changes/integration-time-tracker/` for the\n spec delta, ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardHeaderTitle\" :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-time-tracker-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<Clock :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"rowUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ rowTitle(entity) }}</a>\n\t\t\t\t<span class=\"cn-time-tracker-card__chip-pill\" :class=\"kindChipClass(entity)\">\n\t\t\t\t\t{{ kindLabel(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-time-tracker-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + total tracked time + 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-time-tracker-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-time-tracker-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-time-tracker-card__headline\">\n\t\t\t\t<div class=\"cn-time-tracker-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=\"totalDurationLabel\" class=\"cn-time-tracker-card__total\">\n\t\t\t\t\t<Timer :size=\"14\" />\n\t\t\t\t\t{{ totalDurationLabel }}\n\t\t\t\t</div>\n\t\t\t\t<ul v-if=\"kindDistribution.length > 0\" class=\"cn-time-tracker-card__distribution\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in kindDistribution\"\n\t\t\t\t\t\t:key=\"bucket.key\"\n\t\t\t\t\t\tclass=\"cn-time-tracker-card__distribution-row\">\n\t\t\t\t\t\t<span class=\"cn-time-tracker-card__distribution-label\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclass=\"cn-time-tracker-card__distribution-dot\"\n\t\t\t\t\t\t\t\t:class=\"`cn-time-tracker-card__distribution-dot--${bucket.key}`\" />\n\t\t\t\t\t\t\t{{ bucket.label }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-card__distribution-count\">{{ bucket.count }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-time-tracker-card__headline-recent\">\n\t\t\t\t\t<Clock :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"rowUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ rowTitle(mostRecent) }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: bounded row list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-time-tracker-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-time-tracker-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-time-tracker-card__rows\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-time-tracker-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-time-tracker-card__row--highlight': isLinked(row) }\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\tclass=\"cn-time-tracker-card__title\">{{ rowTitle(row) }}</a>\n\t\t\t\t\t<span class=\"cn-time-tracker-card__chip-pill\" :class=\"kindChipClass(row)\">\n\t\t\t\t\t\t{{ kindLabel(row) }}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"durationLabel(row)\" class=\"cn-time-tracker-card__duration\">\n\t\t\t\t\t\t{{ durationLabel(row) }}\n\t\t\t\t\t</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 Clock from 'vue-material-design-icons/Clock.vue'\nimport Timer from 'vue-material-design-icons/Timer.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']\n\n/**\n * CnTimeTrackerCard — bespoke surface-aware widget for the\n * `time-tracker` integration. See file-level docblock.\n */\nexport default {\n\tname: 'CnTimeTrackerCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, Clock, Timer },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'time-tracker'`). */\n\t\tintegrationId: { type: String, default: 'time-tracker' },\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 (row id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Time tracker') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => Clock },\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 tracked time linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC TimeManager is currently unavailable.') },\n\t\t/** URL of the NC TimeManager app entry. */\n\t\ttimeTrackerAppUrl: { type: String, default: '/index.php/apps/timemanager' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardHeaderTitle() {\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\t/**\n\t\t * Rows bucketed by kind so the dashboard surface can show the\n\t\t * clients / tasks / entries distribution.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, count: number}>}\n\t\t */\n\t\tkindDistribution() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst kind = this.rowKind(row)\n\t\t\t\tif (!groups.has(kind)) {\n\t\t\t\t\tgroups.set(kind, { key: kind, label: this.kindLabel(row), count: 0 })\n\t\t\t\t}\n\t\t\t\tgroups.get(kind).count += 1\n\t\t\t}\n\t\t\treturn Array.from(groups.values())\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.rows.length\n\t\t\treturn n('nextcloud-vue', '{count} entry', '{count} entries', total, { count: total })\n\t\t},\n\n\t\t/**\n\t\t * Total tracked seconds across all rows that carry a `duration`\n\t\t * (clients without a duration don't contribute to this total —\n\t\t * they're parent containers).\n\t\t *\n\t\t * @return {number} Seconds total.\n\t\t */\n\t\ttotalSeconds() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst d = this.durationSeconds(row)\n\t\t\t\tif (d !== null) {\n\t\t\t\t\ttotal += d\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\ttotalDurationLabel() {\n\t\t\tif (this.totalSeconds === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst totalMinutes = Math.round(this.totalSeconds / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn t('nextcloud-vue', '{hours}h {minutes}m tracked', { hours, minutes })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{minutes}m tracked', { minutes })\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.rows.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst sorted = [...this.rows].sort((a, b) => {\n\t\t\t\tconst ta = Date.parse(a.startedAt ?? a.started_at ?? a.linkedAt ?? a.created_at ?? '') || 0\n\t\t\t\tconst tb = Date.parse(b.startedAt ?? b.started_at ?? b.linkedAt ?? b.created_at ?? '') || 0\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[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\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? row.data?.uuid ?? ''\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\trowKind(row) {\n\t\t\tconst k = String(row.kind ?? row.type ?? '').toLowerCase()\n\t\t\tif (k === 'task') {\n\t\t\t\treturn 'task'\n\t\t\t}\n\t\t\tif (k === 'time' || k === 'entry' || k === 'time-entry') {\n\t\t\t\treturn 'time'\n\t\t\t}\n\t\t\treturn 'client'\n\t\t},\n\n\t\tkindLabel(row) {\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tif (kind === 'task') {\n\t\t\t\treturn t('nextcloud-vue', 'Task')\n\t\t\t}\n\t\t\tif (kind === 'time') {\n\t\t\t\treturn t('nextcloud-vue', 'Entry')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tkindChipClass(row) {\n\t\t\treturn `cn-time-tracker-card__chip-pill--${this.rowKind(row)}`\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url) {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (kind === 'client') {\n\t\t\t\tconst clientId = row.clientId ?? id\n\t\t\t\tif (clientId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/clients/${clientId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'task') {\n\t\t\t\tconst taskId = row.taskId ?? id\n\t\t\t\tif (taskId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/tasks/${taskId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'time' && id) {\n\t\t\t\treturn `/index.php/apps/timemanager/times/${id}`\n\t\t\t}\n\t\t\tif (id) {\n\t\t\t\treturn `/index.php/apps/timemanager/projects/${id}`\n\t\t\t}\n\t\t\treturn this.timeTrackerAppUrl\n\t\t},\n\n\t\tdurationSeconds(row) {\n\t\t\tconst raw = row.duration ?? row.seconds ?? row.data?.duration ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\tdurationLabel(row) {\n\t\t\tconst total = this.durationSeconds(row)\n\t\t\tif (total === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst totalMinutes = Math.round(total / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn `${hours}h ${minutes}m`\n\t\t\t}\n\t\t\treturn `${minutes}m`\n\t\t},\n\n\t\tisLinked(row) {\n\t\t\tif (!this.value) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn String(this.rowKey(row)) === String(this.value)\n\t\t},\n\n\t\tchipSubtitle(row) {\n\t\t\treturn this.durationLabel(row) || this.kindLabel(row)\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.rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\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('[CnTimeTrackerCard] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\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('[CnTimeTrackerCard] failed to fetch single row', 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-time-tracker-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-time-tracker-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-time-tracker-card__headline-line {\n\tfont-size: 1.05em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-time-tracker-card__total {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n\tfont-size: 0.95em;\n\tcolor: var(--color-main-text);\n\tfont-weight: 600;\n}\n\n.cn-time-tracker-card__distribution {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-time-tracker-card__distribution-row {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-card__distribution-label {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.cn-time-tracker-card__distribution-dot {\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n}\n\n.cn-time-tracker-card__distribution-dot--client {\n\tbackground: var(--color-primary-element, #21468B);\n}\n\n.cn-time-tracker-card__distribution-dot--task {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-time-tracker-card__distribution-dot--time {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-time-tracker-card__distribution-count {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-time-tracker-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-time-tracker-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-time-tracker-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-time-tracker-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-time-tracker-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-time-tracker-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-time-tracker-card__chip-pill {\n\tdisplay: inline-block;\n\tpadding: 1px 6px;\n\tfont-size: 0.7em;\n\tfont-weight: 600;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-card__chip-pill--client {\n\tbackground: var(--color-primary-element, #21468B);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-card__chip-pill--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-card__chip-pill--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-card__rows {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-time-tracker-card__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tfont-size: 0.85em;\n}\n\n.cn-time-tracker-card__row--highlight {\n\tborder-color: var(--color-primary-element, #21468B);\n\tbackground: var(--color-primary-element-light, var(--color-background-darker));\n\tfont-weight: 600;\n}\n\n.cn-time-tracker-card__title {\n\tflex: 1;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\na.cn-time-tracker-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-time-tracker-card__duration {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\tfont-size: 0.85em;\n}\n</style>\n"],"names":["_Fragment","_createElementBlock","_createVNode","_createElementVNode","_toDisplayString","_normalizeClass","_openBlock","_createTextVNode","_renderList"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnTimeTrackerCard.vue3.js","sources":["../../../../../src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue?vue&type=template&id=4ee344cc&lang.js"],"sourcesContent":["<!--\n CnTimeTrackerCard — bespoke surface-aware widget for the `time-tracker`\n integration.\n\n Replaces the generic CnIntegrationCard for the `time-tracker` leaf.\n Branches on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N tracked entries\";\n sub-line with total tracked time (hours + minutes) summed over\n all linked time-entries; plus a most-recent row link.\n - detail-page : bounded row list with kind chip,\n duration, billable indicator — same shape as the tab but\n read-only.\n - single-entity : chip with row title + kind pill\n (referenceType: 'time-tracker').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnTimeTrackerTab; for `single-entity` the optional `value` prop\n addresses a single row by id.\n\n Note: the leaf slug is `time-tracker` (with a hyphen); the underlying\n NC app id is `timemanager` (no hyphen).\n\n See `openregister/openspec/changes/integration-time-tracker/` for the\n spec delta, ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardHeaderTitle\" :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-time-tracker-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<Clock :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"rowUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ rowTitle(entity) }}</a>\n\t\t\t\t<span class=\"cn-time-tracker-card__chip-pill\" :class=\"kindChipClass(entity)\">\n\t\t\t\t\t{{ kindLabel(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-time-tracker-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + total tracked time + 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-time-tracker-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-time-tracker-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-time-tracker-card__headline\">\n\t\t\t\t<div class=\"cn-time-tracker-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=\"totalDurationLabel\" class=\"cn-time-tracker-card__total\">\n\t\t\t\t\t<Timer :size=\"14\" />\n\t\t\t\t\t{{ totalDurationLabel }}\n\t\t\t\t</div>\n\t\t\t\t<ul v-if=\"kindDistribution.length > 0\" class=\"cn-time-tracker-card__distribution\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in kindDistribution\"\n\t\t\t\t\t\t:key=\"bucket.key\"\n\t\t\t\t\t\tclass=\"cn-time-tracker-card__distribution-row\">\n\t\t\t\t\t\t<span class=\"cn-time-tracker-card__distribution-label\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclass=\"cn-time-tracker-card__distribution-dot\"\n\t\t\t\t\t\t\t\t:class=\"`cn-time-tracker-card__distribution-dot--${bucket.key}`\" />\n\t\t\t\t\t\t\t{{ bucket.label }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-card__distribution-count\">{{ bucket.count }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-time-tracker-card__headline-recent\">\n\t\t\t\t\t<Clock :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"rowUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ rowTitle(mostRecent) }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: bounded row list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-time-tracker-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-time-tracker-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-time-tracker-card__rows\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-time-tracker-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-time-tracker-card__row--highlight': isLinked(row) }\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\tclass=\"cn-time-tracker-card__title\">{{ rowTitle(row) }}</a>\n\t\t\t\t\t<span class=\"cn-time-tracker-card__chip-pill\" :class=\"kindChipClass(row)\">\n\t\t\t\t\t\t{{ kindLabel(row) }}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"durationLabel(row)\" class=\"cn-time-tracker-card__duration\">\n\t\t\t\t\t\t{{ durationLabel(row) }}\n\t\t\t\t\t</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 Clock from 'vue-material-design-icons/Clock.vue'\nimport Timer from 'vue-material-design-icons/Timer.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']\n\n/**\n * CnTimeTrackerCard — bespoke surface-aware widget for the\n * `time-tracker` integration. See file-level docblock.\n */\nexport default {\n\tname: 'CnTimeTrackerCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, Clock, Timer },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'time-tracker'`). */\n\t\tintegrationId: { type: String, default: 'time-tracker' },\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 (row id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Time tracker') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => Clock },\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 tracked time linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC TimeManager is currently unavailable.') },\n\t\t/** URL of the NC TimeManager app entry. */\n\t\ttimeTrackerAppUrl: { type: String, default: '/index.php/apps/timemanager' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardHeaderTitle() {\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\t/**\n\t\t * Rows bucketed by kind so the dashboard surface can show the\n\t\t * clients / tasks / entries distribution.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, count: number}>}\n\t\t */\n\t\tkindDistribution() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst kind = this.rowKind(row)\n\t\t\t\tif (!groups.has(kind)) {\n\t\t\t\t\tgroups.set(kind, { key: kind, label: this.kindLabel(row), count: 0 })\n\t\t\t\t}\n\t\t\t\tgroups.get(kind).count += 1\n\t\t\t}\n\t\t\treturn Array.from(groups.values())\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.rows.length\n\t\t\treturn n('nextcloud-vue', '{count} entry', '{count} entries', total, { count: total })\n\t\t},\n\n\t\t/**\n\t\t * Total tracked seconds across all rows that carry a `duration`\n\t\t * (clients without a duration don't contribute to this total —\n\t\t * they're parent containers).\n\t\t *\n\t\t * @return {number} Seconds total.\n\t\t */\n\t\ttotalSeconds() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst d = this.durationSeconds(row)\n\t\t\t\tif (d !== null) {\n\t\t\t\t\ttotal += d\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\ttotalDurationLabel() {\n\t\t\tif (this.totalSeconds === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst totalMinutes = Math.round(this.totalSeconds / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn t('nextcloud-vue', '{hours}h {minutes}m tracked', { hours, minutes })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{minutes}m tracked', { minutes })\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.rows.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst sorted = [...this.rows].sort((a, b) => {\n\t\t\t\tconst ta = Date.parse(a.startedAt ?? a.started_at ?? a.linkedAt ?? a.created_at ?? '') || 0\n\t\t\t\tconst tb = Date.parse(b.startedAt ?? b.started_at ?? b.linkedAt ?? b.created_at ?? '') || 0\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[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\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? row.data?.uuid ?? ''\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\trowKind(row) {\n\t\t\tconst k = String(row.kind ?? row.type ?? '').toLowerCase()\n\t\t\tif (k === 'task') {\n\t\t\t\treturn 'task'\n\t\t\t}\n\t\t\tif (k === 'time' || k === 'entry' || k === 'time-entry') {\n\t\t\t\treturn 'time'\n\t\t\t}\n\t\t\treturn 'client'\n\t\t},\n\n\t\tkindLabel(row) {\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tif (kind === 'task') {\n\t\t\t\treturn t('nextcloud-vue', 'Task')\n\t\t\t}\n\t\t\tif (kind === 'time') {\n\t\t\t\treturn t('nextcloud-vue', 'Entry')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tkindChipClass(row) {\n\t\t\treturn `cn-time-tracker-card__chip-pill--${this.rowKind(row)}`\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url && safeHref(row.url) !== '#') {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (kind === 'client') {\n\t\t\t\tconst clientId = row.clientId ?? id\n\t\t\t\tif (clientId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/clients/${clientId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'task') {\n\t\t\t\tconst taskId = row.taskId ?? id\n\t\t\t\tif (taskId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/tasks/${taskId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'time' && id) {\n\t\t\t\treturn `/index.php/apps/timemanager/times/${id}`\n\t\t\t}\n\t\t\tif (id) {\n\t\t\t\treturn `/index.php/apps/timemanager/projects/${id}`\n\t\t\t}\n\t\t\treturn this.timeTrackerAppUrl\n\t\t},\n\n\t\tdurationSeconds(row) {\n\t\t\tconst raw = row.duration ?? row.seconds ?? row.data?.duration ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\tdurationLabel(row) {\n\t\t\tconst total = this.durationSeconds(row)\n\t\t\tif (total === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst totalMinutes = Math.round(total / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn `${hours}h ${minutes}m`\n\t\t\t}\n\t\t\treturn `${minutes}m`\n\t\t},\n\n\t\tisLinked(row) {\n\t\t\tif (!this.value) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn String(this.rowKey(row)) === String(this.value)\n\t\t},\n\n\t\tchipSubtitle(row) {\n\t\t\treturn this.durationLabel(row) || this.kindLabel(row)\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.rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\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('[CnTimeTrackerCard] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\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('[CnTimeTrackerCard] failed to fetch single row', 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-time-tracker-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-time-tracker-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-time-tracker-card__headline-line {\n\tfont-size: 1.05em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-time-tracker-card__total {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n\tfont-size: 0.95em;\n\tcolor: var(--color-main-text);\n\tfont-weight: 600;\n}\n\n.cn-time-tracker-card__distribution {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-time-tracker-card__distribution-row {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-card__distribution-label {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.cn-time-tracker-card__distribution-dot {\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n}\n\n.cn-time-tracker-card__distribution-dot--client {\n\tbackground: var(--color-primary-element, #21468B);\n}\n\n.cn-time-tracker-card__distribution-dot--task {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-time-tracker-card__distribution-dot--time {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-time-tracker-card__distribution-count {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-time-tracker-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-time-tracker-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-time-tracker-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-time-tracker-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-time-tracker-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-time-tracker-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-time-tracker-card__chip-pill {\n\tdisplay: inline-block;\n\tpadding: 1px 6px;\n\tfont-size: 0.7em;\n\tfont-weight: 600;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-card__chip-pill--client {\n\tbackground: var(--color-primary-element, #21468B);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-card__chip-pill--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-card__chip-pill--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-card__rows {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-time-tracker-card__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tfont-size: 0.85em;\n}\n\n.cn-time-tracker-card__row--highlight {\n\tborder-color: var(--color-primary-element, #21468B);\n\tbackground: var(--color-primary-element-light, var(--color-background-darker));\n\tfont-weight: 600;\n}\n\n.cn-time-tracker-card__title {\n\tflex: 1;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\na.cn-time-tracker-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-time-tracker-card__duration {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\tfont-size: 0.85em;\n}\n</style>\n"],"names":["_Fragment","_createElementBlock","_createVNode","_createElementVNode","_toDisplayString","_normalizeClass","_openBlock","_createTextVNode","_renderList"],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ import Plus from 'vue-material-design-icons/Plus.vue';
|
|
|
12
12
|
import Timer from 'vue-material-design-icons/Timer.vue';
|
|
13
13
|
import __sfc_main$1 from '../../../components/CnTimeTrackerCreate/CnTimeTrackerCreate.vue.js';
|
|
14
14
|
import __sfc_main from '../../../components/CnTimeTrackerPicker/CnTimeTrackerPicker.vue.js';
|
|
15
|
+
import { safeHref } from '../../../utils/safeHref.js';
|
|
15
16
|
import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -188,7 +189,7 @@ var script = {
|
|
|
188
189
|
},
|
|
189
190
|
|
|
190
191
|
rowUrl(row) {
|
|
191
|
-
if (row.url) {
|
|
192
|
+
if (row.url && safeHref(row.url) !== '#') {
|
|
192
193
|
return row.url
|
|
193
194
|
}
|
|
194
195
|
const kind = this.rowKind(row);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnTimeTrackerTab.vue2.js","sources":["../../../../../src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue"],"sourcesContent":["<!--\n CnTimeTrackerTab — bespoke sidebar tab for the `time-tracker`\n integration.\n\n Replaces the generic CnIntegrationTab for the `time-tracker` leaf:\n renders the NC TimeManager rows linked to the parent OR object as a\n flat list. TimeManager exposes THREE kinds of rows in the same\n payload (per wave-2.4 design — see `TimeProvider.php`); each row\n carries a `kind` discriminator (`'client'` | `'task'` | `'time'`)\n which this tab shows as a coloured chip alongside the title.\n\n Rows show:\n - client: name + linked-tasks count\n - task: name + duration + billable indicator\n - time: start time + duration + billable indicator\n\n Clicking a row deep-links to the TimeManager UI — own `url` field\n wins; otherwise the tab synthesises a sensible permalink per kind.\n\n Talks to the OpenRegister Tier-2 time-tracker link API\n GET /api/objects/{register}/{schema}/{id}/time-tracker\n POST /api/objects/{register}/{schema}/{id}/time-tracker (link existing)\n POST /api/objects/{register}/{schema}/{id}/time-tracker/new (create + link client)\n DELETE /api/objects/{register}/{schema}/{id}/time-tracker/{entryId} (unlink)\n served by `OCA\\OpenRegister\\Controller\\TimeTrackerLinksController`\n (link-table strategy — rows in `openregister_timetracker_links`; the\n `TimeProvider` still falls back to the legacy `[or:{uuid}]` note marker).\n\n Tier-2 surface: a Link/Create actions bar drives the\n CnTimeTrackerPicker (pick an existing entry) + CnTimeTrackerCreate\n (create a client) dialogs; each row has a per-row unlink button.\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open TimeManager\" CTA when no linked rows.\n - Loading spinner during fetch.\n - 503 / 501 \"currently unavailable\" banner when TimeManager is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses TimeManager's primary signals — *what kind of entity\n the row is*, its duration, and whether it's billable. Surfacing them\n inline lets case handlers triage tracked work at a glance without\n switching to TimeManager itself.\n\n Note: the leaf slug is `time-tracker` (with a hyphen); the underlying\n NC app id is `timemanager` (no hyphen).\n\n See `openregister/openspec/changes/integration-time-tracker/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract), `openregister#1322` for provider follow-ups.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-time-tracker-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-time-tracker-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div v-if=\"!degraded\" class=\"cn-time-tracker-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{{ t('nextcloud-vue', 'Link existing entry') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Create new client') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-time-tracker-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-sidebar-tab__empty cn-time-tracker-tab__empty\">\n\t\t\t<Clock :size=\"32\" class=\"cn-time-tracker-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"timeTrackerAppUrl\"\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<Clock :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openTimeTrackerLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<div v-if=\"totalDurationLabel\" class=\"cn-time-tracker-tab__summary\">\n\t\t\t\t<span class=\"cn-time-tracker-tab__summary-label\">\n\t\t\t\t\t<Timer :size=\"16\" />\n\t\t\t\t\t{{ t('nextcloud-vue', 'Total tracked') }}\n\t\t\t\t</span>\n\t\t\t\t<span class=\"cn-time-tracker-tab__summary-value\">{{ totalDurationLabel }}</span>\n\t\t\t</div>\n\t\t\t<ul class=\"cn-time-tracker-tab__list\">\n\t\t\t\t<NcListItem\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-time-tracker-tab__row\"\n\t\t\t\t\t:class=\"rowClass(row)\"\n\t\t\t\t\t:name=\"rowTitle(row)\"\n\t\t\t\t\t:bold=\"true\"\n\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__row-icon\" :class=\"rowClass(row)\">\n\t\t\t\t\t\t\t<ClipboardTextOutline v-if=\"rowKind(row) === 'task'\" :size=\"22\" />\n\t\t\t\t\t\t\t<AccountOutline v-else-if=\"rowKind(row) === 'client'\" :size=\"22\" />\n\t\t\t\t\t\t\t<ClockOutline v-else :size=\"22\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__subline\">\n\t\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__kind-chip\" :class=\"kindChipClass(row)\">\n\t\t\t\t\t\t\t\t{{ kindLabel(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\tv-if=\"startedAtMs(row) !== null\"\n\t\t\t\t\t\t\t\tclass=\"cn-time-tracker-tab__date\"\n\t\t\t\t\t\t\t\t:timestamp=\"startedAtMs(row)\"\n\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t<span v-else-if=\"taskCountLabel(row)\" class=\"cn-time-tracker-tab__task-count\">\n\t\t\t\t\t\t\t\t{{ taskCountLabel(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #details>\n\t\t\t\t\t\t<span v-if=\"durationLabel(row)\" class=\"cn-time-tracker-tab__duration\">\n\t\t\t\t\t\t\t{{ durationLabel(row) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template v-if=\"isBillable(row)\" #indicator>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__billable\" :title=\"billableTitle\">\n\t\t\t\t\t\t\t<CurrencyEur :size=\"14\" />\n\t\t\t\t\t\t\t{{ billableLabel }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\tclass=\"cn-time-tracker-tab__unlink\"\n\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t@click=\"unlinkRow(row)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink entry') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</template>\n\t\t\t\t</NcListItem>\n\t\t\t</ul>\n\t\t</template>\n\n\t\t<CnTimeTrackerPicker\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\n\t\t<CnTimeTrackerCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcDateTime, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountOutline from 'vue-material-design-icons/AccountOutline.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClipboardTextOutline from 'vue-material-design-icons/ClipboardTextOutline.vue'\nimport Clock from 'vue-material-design-icons/Clock.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport CurrencyEur from 'vue-material-design-icons/CurrencyEur.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport Timer from 'vue-material-design-icons/Timer.vue'\nimport CnTimeTrackerCreate from '../../../components/CnTimeTrackerCreate/CnTimeTrackerCreate.vue'\nimport CnTimeTrackerPicker from '../../../components/CnTimeTrackerPicker/CnTimeTrackerPicker.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\n/**\n * CnTimeTrackerTab — bespoke sidebar tab for the `time-tracker`\n * integration.\n *\n * Renders linked TimeManager clients / tasks / time entries with a\n * kind chip, duration, billable indicator. See file-level docblock.\n */\nexport default {\n\tname: 'CnTimeTrackerTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAccountOutline,\n\t\tAlertCircleOutline,\n\t\tClipboardTextOutline,\n\t\tClock,\n\t\tClockOutline,\n\t\tCurrencyEur,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tTimer,\n\t\tCnTimeTrackerPicker,\n\t\tCnTimeTrackerCreate,\n\t},\n\n\tprops: {\n\t\t/* eslint-disable vue/no-unused-properties -- the integration dispatch binds integrationId on every integration component (see CnIntegrationWidgetGrid), so declaring it keeps it out of $attrs */\n\t\t/** Stable integration id (forwarded from the registry — always `'time-tracker'`). */\n\t\tintegrationId: { type: String, default: 'time-tracker' },\n\t\t/* eslint-enable vue/no-unused-properties */\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/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No tracked time linked yet') },\n\t\t/** Pre-translated label for the \"Open TimeManager\" CTA. */\n\t\topenTimeTrackerLabel: { type: String, default: () => t('nextcloud-vue', 'Open TimeManager') },\n\t\t/** Pre-translated banner when TimeManager is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC TimeManager is currently unavailable.') },\n\t\t/** URL of the NC TimeManager app entry. */\n\t\ttimeTrackerAppUrl: { type: String, default: '/index.php/apps/timemanager' },\n\t\t/** Pre-translated \"billable\" label. */\n\t\tbillableLabel: { type: String, default: () => t('nextcloud-vue', 'Billable') },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t\tbillableTitle: t('nextcloud-vue', 'This entry is marked as billable.'),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Total tracked seconds across all linked rows that carry a\n\t\t * `duration` (clients are parent containers and usually omit it).\n\t\t *\n\t\t * @return {number} Seconds total.\n\t\t */\n\t\ttotalSeconds() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst d = this.durationSeconds(row)\n\t\t\t\tif (d !== null) {\n\t\t\t\t\ttotal += d\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\t/**\n\t\t * Human-readable total duration shown in the summary header, or\n\t\t * empty when nothing tracked.\n\t\t *\n\t\t * @return {string} Formatted \"Xh Ym\" / \"Ym\" total.\n\t\t */\n\t\ttotalDurationLabel() {\n\t\t\tif (this.totalSeconds === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.formatDuration(this.totalSeconds)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchRows()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchRows()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Base for the Tier-2 time-tracker endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/time-tracker`\n\t\t},\n\n\t\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? row.data?.uuid ?? ''\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\t/**\n\t\t * Discriminator. Defaults to 'client' when the provider doesn't\n\t\t * surface a `kind` field (the current shipping TimeProvider\n\t\t * only returns projects-as-clients without an explicit kind).\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {'client'|'task'|'time'} Row kind.\n\t\t */\n\t\trowKind(row) {\n\t\t\tconst k = String(row.kind ?? row.type ?? '').toLowerCase()\n\t\t\tif (k === 'task') {\n\t\t\t\treturn 'task'\n\t\t\t}\n\t\t\tif (k === 'time' || k === 'entry' || k === 'time-entry') {\n\t\t\t\treturn 'time'\n\t\t\t}\n\t\t\treturn 'client'\n\t\t},\n\n\t\tkindLabel(row) {\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tif (kind === 'task') {\n\t\t\t\treturn t('nextcloud-vue', 'Task')\n\t\t\t}\n\t\t\tif (kind === 'time') {\n\t\t\t\treturn t('nextcloud-vue', 'Entry')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tkindChipClass(row) {\n\t\t\treturn `cn-time-tracker-tab__kind-chip--${this.rowKind(row)}`\n\t\t},\n\n\t\trowClass(row) {\n\t\t\treturn `cn-time-tracker-tab__row--${this.rowKind(row)}`\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url) {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (kind === 'client') {\n\t\t\t\tconst clientId = row.clientId ?? id\n\t\t\t\tif (clientId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/clients/${clientId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'task') {\n\t\t\t\tconst taskId = row.taskId ?? id\n\t\t\t\tif (taskId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/tasks/${taskId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'time' && id) {\n\t\t\t\treturn `/index.php/apps/timemanager/times/${id}`\n\t\t\t}\n\t\t\tif (id) {\n\t\t\t\treturn `/index.php/apps/timemanager/projects/${id}`\n\t\t\t}\n\t\t\treturn this.timeTrackerAppUrl\n\t\t},\n\n\t\t/**\n\t\t * Total seconds carried on the row, normalised across the kind\n\t\t * variants. Tasks/time entries usually carry `duration` as\n\t\t * seconds (or minutes when explicitly suffixed).\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {number|null} Seconds total or null if absent / unparseable.\n\t\t */\n\t\tdurationSeconds(row) {\n\t\t\tconst raw = row.duration ?? row.seconds ?? row.data?.duration ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\t/**\n\t\t * Format a seconds total as a compact \"Xh Ym\" / \"Ym\" duration.\n\t\t *\n\t\t * @param {number} seconds Total seconds.\n\t\t * @return {string} Formatted duration.\n\t\t */\n\t\tformatDuration(seconds) {\n\t\t\tconst totalMinutes = Math.round(seconds / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn `${hours}h ${minutes}m`\n\t\t\t}\n\t\t\treturn `${minutes}m`\n\t\t},\n\n\t\tdurationLabel(row) {\n\t\t\tconst total = this.durationSeconds(row)\n\t\t\tif (total === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.formatDuration(total)\n\t\t},\n\n\t\ttaskCountLabel(row) {\n\t\t\tif (this.rowKind(row) !== 'client') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst raw = row.tasks ?? row.taskCount ?? row.data?.task_count ?? null\n\t\t\tif (raw === null || raw === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\tif (Number.isNaN(num)) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{count} tasks', { count: num })\n\t\t},\n\n\t\t/**\n\t\t * Start timestamp in milliseconds for the row, suitable for\n\t\t * `NcDateTime`. Only time entries carry a start; returns null when\n\t\t * absent or unparseable.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {number|null} Epoch milliseconds or null.\n\t\t */\n\t\tstartedAtMs(row) {\n\t\t\tif (this.rowKind(row) !== 'time') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst raw = row.startedAt ?? row.started_at ?? row.start ?? row.data?.start ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst ms = typeof raw === 'number' ? raw * 1000 : Date.parse(raw)\n\t\t\tif (Number.isNaN(ms)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn ms\n\t\t},\n\n\t\tisBillable(row) {\n\t\t\treturn row.billable === true || row.billable === 1 || row.billable === '1'\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This entry is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\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('[CnTimeTrackerTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/new`), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create client.')\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('[CnTimeTrackerTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create client.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkRow(row) {\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(id)}`), {\n\t\t\t\t\tmethod: 'DELETE',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\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('[CnTimeTrackerTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchRows() {\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.error = ''\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\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.rows = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load tracked time.')\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('[CnTimeTrackerTab] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load tracked time.')\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-time-tracker-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-time-tracker-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-time-tracker-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-time-tracker-tab__empty {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px 8px;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n}\n\n.cn-time-tracker-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-tab__summary {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tpadding: 8px 12px;\n\tmargin-bottom: 8px;\n\tborder-radius: var(--border-radius-large, var(--border-radius));\n\tbackground: var(--color-background-hover);\n}\n\n.cn-time-tracker-tab__summary-label {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__summary-value {\n\tfont-weight: 700;\n\tfont-size: 1.15em;\n\tcolor: var(--color-main-text);\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-time-tracker-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-time-tracker-tab__row-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--client {\n\tbackground: var(--color-primary-element-light, var(--color-background-dark));\n\tcolor: var(--color-primary-element, #21468B);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tflex-wrap: wrap;\n}\n\n.cn-time-tracker-tab__date,\n.cn-time-tracker-tab__task-count {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__kind-chip {\n\tdisplay: inline-block;\n\tpadding: 1px 6px;\n\tfont-size: 0.7em;\n\tfont-weight: 600;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-tab__kind-chip--client {\n\tbackground: var(--color-primary-element, #21468B);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__kind-chip--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__kind-chip--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__duration {\n\tfont-weight: 700;\n\tfont-size: 1.05em;\n\tcolor: var(--color-main-text);\n\tfont-variant-numeric: tabular-nums;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-tab__billable {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n\tfont-size: 0.72em;\n\tfont-weight: 600;\n\tcolor: var(--color-main-background);\n\tbackground: var(--color-success, #46ba61);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["ClipboardTextOutline","CnTimeTrackerPicker","CnTimeTrackerCreate","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnTimeTrackerTab.vue2.js","sources":["../../../../../src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue"],"sourcesContent":["<!--\n CnTimeTrackerTab — bespoke sidebar tab for the `time-tracker`\n integration.\n\n Replaces the generic CnIntegrationTab for the `time-tracker` leaf:\n renders the NC TimeManager rows linked to the parent OR object as a\n flat list. TimeManager exposes THREE kinds of rows in the same\n payload (per wave-2.4 design — see `TimeProvider.php`); each row\n carries a `kind` discriminator (`'client'` | `'task'` | `'time'`)\n which this tab shows as a coloured chip alongside the title.\n\n Rows show:\n - client: name + linked-tasks count\n - task: name + duration + billable indicator\n - time: start time + duration + billable indicator\n\n Clicking a row deep-links to the TimeManager UI — own `url` field\n wins; otherwise the tab synthesises a sensible permalink per kind.\n\n Talks to the OpenRegister Tier-2 time-tracker link API\n GET /api/objects/{register}/{schema}/{id}/time-tracker\n POST /api/objects/{register}/{schema}/{id}/time-tracker (link existing)\n POST /api/objects/{register}/{schema}/{id}/time-tracker/new (create + link client)\n DELETE /api/objects/{register}/{schema}/{id}/time-tracker/{entryId} (unlink)\n served by `OCA\\OpenRegister\\Controller\\TimeTrackerLinksController`\n (link-table strategy — rows in `openregister_timetracker_links`; the\n `TimeProvider` still falls back to the legacy `[or:{uuid}]` note marker).\n\n Tier-2 surface: a Link/Create actions bar drives the\n CnTimeTrackerPicker (pick an existing entry) + CnTimeTrackerCreate\n (create a client) dialogs; each row has a per-row unlink button.\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open TimeManager\" CTA when no linked rows.\n - Loading spinner during fetch.\n - 503 / 501 \"currently unavailable\" banner when TimeManager is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses TimeManager's primary signals — *what kind of entity\n the row is*, its duration, and whether it's billable. Surfacing them\n inline lets case handlers triage tracked work at a glance without\n switching to TimeManager itself.\n\n Note: the leaf slug is `time-tracker` (with a hyphen); the underlying\n NC app id is `timemanager` (no hyphen).\n\n See `openregister/openspec/changes/integration-time-tracker/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract), `openregister#1322` for provider follow-ups.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-time-tracker-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-time-tracker-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div v-if=\"!degraded\" class=\"cn-time-tracker-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{{ t('nextcloud-vue', 'Link existing entry') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Create new client') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-time-tracker-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-sidebar-tab__empty cn-time-tracker-tab__empty\">\n\t\t\t<Clock :size=\"32\" class=\"cn-time-tracker-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"timeTrackerAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Clock :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openTimeTrackerLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<div v-if=\"totalDurationLabel\" class=\"cn-time-tracker-tab__summary\">\n\t\t\t\t<span class=\"cn-time-tracker-tab__summary-label\">\n\t\t\t\t\t<Timer :size=\"16\" />\n\t\t\t\t\t{{ t('nextcloud-vue', 'Total tracked') }}\n\t\t\t\t</span>\n\t\t\t\t<span class=\"cn-time-tracker-tab__summary-value\">{{ totalDurationLabel }}</span>\n\t\t\t</div>\n\t\t\t<ul class=\"cn-time-tracker-tab__list\">\n\t\t\t\t<NcListItem\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-time-tracker-tab__row\"\n\t\t\t\t\t:class=\"rowClass(row)\"\n\t\t\t\t\t:name=\"rowTitle(row)\"\n\t\t\t\t\t:bold=\"true\"\n\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__row-icon\" :class=\"rowClass(row)\">\n\t\t\t\t\t\t\t<ClipboardTextOutline v-if=\"rowKind(row) === 'task'\" :size=\"22\" />\n\t\t\t\t\t\t\t<AccountOutline v-else-if=\"rowKind(row) === 'client'\" :size=\"22\" />\n\t\t\t\t\t\t\t<ClockOutline v-else :size=\"22\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__subline\">\n\t\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__kind-chip\" :class=\"kindChipClass(row)\">\n\t\t\t\t\t\t\t\t{{ kindLabel(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\tv-if=\"startedAtMs(row) !== null\"\n\t\t\t\t\t\t\t\tclass=\"cn-time-tracker-tab__date\"\n\t\t\t\t\t\t\t\t:timestamp=\"startedAtMs(row)\"\n\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t<span v-else-if=\"taskCountLabel(row)\" class=\"cn-time-tracker-tab__task-count\">\n\t\t\t\t\t\t\t\t{{ taskCountLabel(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #details>\n\t\t\t\t\t\t<span v-if=\"durationLabel(row)\" class=\"cn-time-tracker-tab__duration\">\n\t\t\t\t\t\t\t{{ durationLabel(row) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template v-if=\"isBillable(row)\" #indicator>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__billable\" :title=\"billableTitle\">\n\t\t\t\t\t\t\t<CurrencyEur :size=\"14\" />\n\t\t\t\t\t\t\t{{ billableLabel }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\tclass=\"cn-time-tracker-tab__unlink\"\n\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t@click=\"unlinkRow(row)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink entry') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</template>\n\t\t\t\t</NcListItem>\n\t\t\t</ul>\n\t\t</template>\n\n\t\t<CnTimeTrackerPicker\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\n\t\t<CnTimeTrackerCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcDateTime, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountOutline from 'vue-material-design-icons/AccountOutline.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClipboardTextOutline from 'vue-material-design-icons/ClipboardTextOutline.vue'\nimport Clock from 'vue-material-design-icons/Clock.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport CurrencyEur from 'vue-material-design-icons/CurrencyEur.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport Timer from 'vue-material-design-icons/Timer.vue'\nimport CnTimeTrackerCreate from '../../../components/CnTimeTrackerCreate/CnTimeTrackerCreate.vue'\nimport CnTimeTrackerPicker from '../../../components/CnTimeTrackerPicker/CnTimeTrackerPicker.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\n\n/**\n * CnTimeTrackerTab — bespoke sidebar tab for the `time-tracker`\n * integration.\n *\n * Renders linked TimeManager clients / tasks / time entries with a\n * kind chip, duration, billable indicator. See file-level docblock.\n */\nexport default {\n\tname: 'CnTimeTrackerTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAccountOutline,\n\t\tAlertCircleOutline,\n\t\tClipboardTextOutline,\n\t\tClock,\n\t\tClockOutline,\n\t\tCurrencyEur,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tTimer,\n\t\tCnTimeTrackerPicker,\n\t\tCnTimeTrackerCreate,\n\t},\n\n\tprops: {\n\t\t/* eslint-disable vue/no-unused-properties -- the integration dispatch binds integrationId on every integration component (see CnIntegrationWidgetGrid), so declaring it keeps it out of $attrs */\n\t\t/** Stable integration id (forwarded from the registry — always `'time-tracker'`). */\n\t\tintegrationId: { type: String, default: 'time-tracker' },\n\t\t/* eslint-enable vue/no-unused-properties */\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/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No tracked time linked yet') },\n\t\t/** Pre-translated label for the \"Open TimeManager\" CTA. */\n\t\topenTimeTrackerLabel: { type: String, default: () => t('nextcloud-vue', 'Open TimeManager') },\n\t\t/** Pre-translated banner when TimeManager is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC TimeManager is currently unavailable.') },\n\t\t/** URL of the NC TimeManager app entry. */\n\t\ttimeTrackerAppUrl: { type: String, default: '/index.php/apps/timemanager' },\n\t\t/** Pre-translated \"billable\" label. */\n\t\tbillableLabel: { type: String, default: () => t('nextcloud-vue', 'Billable') },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t\tbillableTitle: t('nextcloud-vue', 'This entry is marked as billable.'),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Total tracked seconds across all linked rows that carry a\n\t\t * `duration` (clients are parent containers and usually omit it).\n\t\t *\n\t\t * @return {number} Seconds total.\n\t\t */\n\t\ttotalSeconds() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst d = this.durationSeconds(row)\n\t\t\t\tif (d !== null) {\n\t\t\t\t\ttotal += d\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\t/**\n\t\t * Human-readable total duration shown in the summary header, or\n\t\t * empty when nothing tracked.\n\t\t *\n\t\t * @return {string} Formatted \"Xh Ym\" / \"Ym\" total.\n\t\t */\n\t\ttotalDurationLabel() {\n\t\t\tif (this.totalSeconds === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.formatDuration(this.totalSeconds)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchRows()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchRows()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Base for the Tier-2 time-tracker endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/time-tracker`\n\t\t},\n\n\t\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? row.data?.uuid ?? ''\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\t/**\n\t\t * Discriminator. Defaults to 'client' when the provider doesn't\n\t\t * surface a `kind` field (the current shipping TimeProvider\n\t\t * only returns projects-as-clients without an explicit kind).\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {'client'|'task'|'time'} Row kind.\n\t\t */\n\t\trowKind(row) {\n\t\t\tconst k = String(row.kind ?? row.type ?? '').toLowerCase()\n\t\t\tif (k === 'task') {\n\t\t\t\treturn 'task'\n\t\t\t}\n\t\t\tif (k === 'time' || k === 'entry' || k === 'time-entry') {\n\t\t\t\treturn 'time'\n\t\t\t}\n\t\t\treturn 'client'\n\t\t},\n\n\t\tkindLabel(row) {\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tif (kind === 'task') {\n\t\t\t\treturn t('nextcloud-vue', 'Task')\n\t\t\t}\n\t\t\tif (kind === 'time') {\n\t\t\t\treturn t('nextcloud-vue', 'Entry')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tkindChipClass(row) {\n\t\t\treturn `cn-time-tracker-tab__kind-chip--${this.rowKind(row)}`\n\t\t},\n\n\t\trowClass(row) {\n\t\t\treturn `cn-time-tracker-tab__row--${this.rowKind(row)}`\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url && safeHref(row.url) !== '#') {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (kind === 'client') {\n\t\t\t\tconst clientId = row.clientId ?? id\n\t\t\t\tif (clientId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/clients/${clientId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'task') {\n\t\t\t\tconst taskId = row.taskId ?? id\n\t\t\t\tif (taskId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/tasks/${taskId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'time' && id) {\n\t\t\t\treturn `/index.php/apps/timemanager/times/${id}`\n\t\t\t}\n\t\t\tif (id) {\n\t\t\t\treturn `/index.php/apps/timemanager/projects/${id}`\n\t\t\t}\n\t\t\treturn this.timeTrackerAppUrl\n\t\t},\n\n\t\t/**\n\t\t * Total seconds carried on the row, normalised across the kind\n\t\t * variants. Tasks/time entries usually carry `duration` as\n\t\t * seconds (or minutes when explicitly suffixed).\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {number|null} Seconds total or null if absent / unparseable.\n\t\t */\n\t\tdurationSeconds(row) {\n\t\t\tconst raw = row.duration ?? row.seconds ?? row.data?.duration ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\t/**\n\t\t * Format a seconds total as a compact \"Xh Ym\" / \"Ym\" duration.\n\t\t *\n\t\t * @param {number} seconds Total seconds.\n\t\t * @return {string} Formatted duration.\n\t\t */\n\t\tformatDuration(seconds) {\n\t\t\tconst totalMinutes = Math.round(seconds / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn `${hours}h ${minutes}m`\n\t\t\t}\n\t\t\treturn `${minutes}m`\n\t\t},\n\n\t\tdurationLabel(row) {\n\t\t\tconst total = this.durationSeconds(row)\n\t\t\tif (total === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.formatDuration(total)\n\t\t},\n\n\t\ttaskCountLabel(row) {\n\t\t\tif (this.rowKind(row) !== 'client') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst raw = row.tasks ?? row.taskCount ?? row.data?.task_count ?? null\n\t\t\tif (raw === null || raw === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\tif (Number.isNaN(num)) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{count} tasks', { count: num })\n\t\t},\n\n\t\t/**\n\t\t * Start timestamp in milliseconds for the row, suitable for\n\t\t * `NcDateTime`. Only time entries carry a start; returns null when\n\t\t * absent or unparseable.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {number|null} Epoch milliseconds or null.\n\t\t */\n\t\tstartedAtMs(row) {\n\t\t\tif (this.rowKind(row) !== 'time') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst raw = row.startedAt ?? row.started_at ?? row.start ?? row.data?.start ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst ms = typeof raw === 'number' ? raw * 1000 : Date.parse(raw)\n\t\t\tif (Number.isNaN(ms)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn ms\n\t\t},\n\n\t\tisBillable(row) {\n\t\t\treturn row.billable === true || row.billable === 1 || row.billable === '1'\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This entry is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\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('[CnTimeTrackerTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/new`), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create client.')\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('[CnTimeTrackerTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create client.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkRow(row) {\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(id)}`), {\n\t\t\t\t\tmethod: 'DELETE',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\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('[CnTimeTrackerTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchRows() {\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.error = ''\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\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.rows = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load tracked time.')\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('[CnTimeTrackerTab] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load tracked time.')\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-time-tracker-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-time-tracker-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-time-tracker-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-time-tracker-tab__empty {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px 8px;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n}\n\n.cn-time-tracker-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-tab__summary {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tpadding: 8px 12px;\n\tmargin-bottom: 8px;\n\tborder-radius: var(--border-radius-large, var(--border-radius));\n\tbackground: var(--color-background-hover);\n}\n\n.cn-time-tracker-tab__summary-label {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__summary-value {\n\tfont-weight: 700;\n\tfont-size: 1.15em;\n\tcolor: var(--color-main-text);\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-time-tracker-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-time-tracker-tab__row-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--client {\n\tbackground: var(--color-primary-element-light, var(--color-background-dark));\n\tcolor: var(--color-primary-element, #21468B);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tflex-wrap: wrap;\n}\n\n.cn-time-tracker-tab__date,\n.cn-time-tracker-tab__task-count {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__kind-chip {\n\tdisplay: inline-block;\n\tpadding: 1px 6px;\n\tfont-size: 0.7em;\n\tfont-weight: 600;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-tab__kind-chip--client {\n\tbackground: var(--color-primary-element, #21468B);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__kind-chip--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__kind-chip--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__duration {\n\tfont-weight: 700;\n\tfont-size: 1.05em;\n\tcolor: var(--color-main-text);\n\tfont-variant-numeric: tabular-nums;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-tab__billable {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n\tfont-size: 0.72em;\n\tfont-weight: 600;\n\tcolor: var(--color-main-background);\n\tbackground: var(--color-success, #46ba61);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["ClipboardTextOutline","CnTimeTrackerPicker","CnTimeTrackerCreate","t"],"mappings":"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"}
|
|
@@ -105,8 +105,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
105
105
|
createVNode(_component_NcButton, {
|
|
106
106
|
variant: "primary",
|
|
107
107
|
href: $props.timeTrackerAppUrl,
|
|
108
|
-
target: "_blank"
|
|
109
|
-
rel: "noopener noreferrer"
|
|
108
|
+
target: "_blank"
|
|
110
109
|
}, {
|
|
111
110
|
icon: withCtx(() => [
|
|
112
111
|
createVNode(_component_Clock, { size: 20 })
|