@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
|
@@ -3,6 +3,7 @@ import { NcLoadingIcon } from '@nextcloud/vue';
|
|
|
3
3
|
import MapMarker from 'vue-material-design-icons/MapMarker.vue';
|
|
4
4
|
import __sfc_main from '../../../components/CnDetailCard/CnDetailCard.vue.js';
|
|
5
5
|
import { stripMarker } from '../../utils/marker.js';
|
|
6
|
+
import { safeHref } from '../../../utils/safeHref.js';
|
|
6
7
|
import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
|
|
7
8
|
|
|
8
9
|
const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'];
|
|
@@ -168,8 +169,9 @@ var script = {
|
|
|
168
169
|
return `${lat.toFixed(5)}, ${lng.toFixed(5)}`
|
|
169
170
|
},
|
|
170
171
|
|
|
172
|
+
// A provider URL is used only when it is a safe scheme; else the app link.
|
|
171
173
|
pointUrl(point) {
|
|
172
|
-
if (typeof point.url === 'string' && point.url !== '') {
|
|
174
|
+
if (typeof point.url === 'string' && point.url !== '' && safeHref(point.url) !== '#') {
|
|
173
175
|
return point.url
|
|
174
176
|
}
|
|
175
177
|
const lat = this.pointLat(point);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnMapsCard.vue2.js","sources":["../../../../../src/integrations/builtin/maps/CnMapsCard.vue"],"sourcesContent":["<!--\n CnMapsCard — bespoke surface-aware widget for the `maps` integration.\n\n Replaces the generic CnIntegrationCard for the `maps` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard : map-pin icon + headline\n \"N linked locations\" + the most-recently added POI (name +\n category + coords).\n - detail-page : compact list of linked POIs\n (icon + name + category badge + coords) capped at COMPACT_LIMIT\n with a \"view all\" trail-off pointing at NC Maps.\n - single-entity : pin + name chip for\n reference-property auto-rendering (referenceType: 'maps').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnMapsTab. Provider shape and field-fallback strategy matches the\n tab (see the tab's docblock).\n\n See `openregister/openspec/changes/integration-maps/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span\n\t\t\t\tv-if=\"entity\"\n\t\t\t\tclass=\"cn-maps-card__chip\"\n\t\t\t\t:title=\"chipSubtitle(entity)\">\n\t\t\t\t<MapMarker :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"pointUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ pointName(entity) }}</a>\n\t\t\t\t<span v-if=\"pointCategory(entity)\" class=\"cn-maps-card__chip-category\">\n\t\t\t\t\t· {{ pointCategory(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else-if=\"value\" class=\"cn-maps-card__chip cn-maps-card__chip--fallback\">\n\t\t\t\t<MapMarker :size=\"14\" />\n\t\t\t\t<span>{{ value }}</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-maps-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: count + 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-maps-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"points.length === 0\" class=\"cn-maps-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-maps-card__headline\">\n\t\t\t\t<div class=\"cn-maps-card__headline-line\">\n\t\t\t\t\t<MapMarker :size=\"16\" class=\"cn-maps-card__headline-icon\" />\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-maps-card__headline-recent\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pointUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ pointName(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"pointCategory(mostRecent)\" class=\"cn-maps-card__headline-category\">\n\t\t\t\t\t\t· {{ pointCategory(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent && coordsLabel(mostRecent)\" class=\"cn-maps-card__headline-coords\">\n\t\t\t\t\t{{ coordsLabel(mostRecent) }}\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"points.length > 1\" class=\"cn-maps-card__view-all\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"mapsAppUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ viewAllLabel }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-maps-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"points.length === 0\" class=\"cn-maps-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-maps-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"point in displayedPoints\"\n\t\t\t\t\t:key=\"pointKey(point)\"\n\t\t\t\t\tclass=\"cn-maps-card__row\">\n\t\t\t\t\t<MapMarker :size=\"14\" class=\"cn-maps-card__row-icon\" />\n\t\t\t\t\t<div class=\"cn-maps-card__row-main\">\n\t\t\t\t\t\t<div class=\"cn-maps-card__row-header\">\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\t\tclass=\"cn-maps-card__title\">{{ pointName(point) }}</a>\n\t\t\t\t\t\t\t<span v-if=\"pointCategory(point)\" class=\"cn-maps-card__category\">\n\t\t\t\t\t\t\t\t{{ pointCategory(point) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<span v-if=\"coordsLabel(point)\" class=\"cn-maps-card__coords\">\n\t\t\t\t\t\t\t{{ coordsLabel(point) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t\t<div v-if=\"points.length > COMPACT_LIMIT\" class=\"cn-maps-card__view-all\">\n\t\t\t\t<a\n\t\t\t\t\t:href=\"mapsAppUrl\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ viewAllLabel }}</a>\n\t\t\t</div>\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 MapMarker from 'vue-material-design-icons/MapMarker.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\n\n/**\n * CnMapsCard — bespoke surface-aware widget for the `maps` integration.\n *\n * Renders Maps-aware metadata across all four AD-19 surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnMapsCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, MapMarker },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'maps'`). */\n\t\tintegrationId: { type: String, default: 'maps' },\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 (POI id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Locations') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => MapMarker },\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 locations linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Location is currently unavailable.') },\n\t\t/** Pre-translated \"view all\" trail-off link label. */\n\t\tviewAllLabel: { type: String, default: () => t('nextcloud-vue', 'View all in Locations') },\n\t\t/** URL of the NC Maps app entry. */\n\t\tmapsAppUrl: { type: String, default: '/index.php/apps/maps' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tCOMPACT_LIMIT,\n\t\t\tpoints: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\tdisplayedPoints() {\n\t\t\treturn this.points.slice(0, COMPACT_LIMIT)\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.points.length\n\t\t\treturn n('nextcloud-vue', '{count} linked location', '{count} linked locations', total, { count: total })\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.points.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Maps provider has no modified-time column; fall back to id-order\n\t\t\t// (highest id == most recently added). Defensive: also check\n\t\t\t// addedAt / created when present in a future expansion.\n\t\t\tconst sorted = [...this.points].sort((a, b) => {\n\t\t\t\tconst ka = this.recencyKey(a)\n\t\t\t\tconst kb = this.recencyKey(b)\n\t\t\t\treturn kb - ka\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\tdataOf(point) {\n\t\t\treturn (point && typeof point.data === 'object' && point.data !== null) ? point.data : {}\n\t\t},\n\n\t\tpointKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.id ?? d.id ?? this.pointName(point)\n\t\t},\n\n\t\tpointName(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst candidate = point.name ?? point.title ?? d.name ?? d.title ?? ''\n\t\t\treturn stripMarker(candidate) || String(this.pointKey(point))\n\t\t},\n\n\t\tpointCategory(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst c = point.category ?? d.category ?? ''\n\t\t\treturn stripMarker(c)\n\t\t},\n\n\t\tcoordOf(point, key) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point[key] ?? d[key] ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : null\n\t\t},\n\n\t\tpointLat(point) {\n\t\t\treturn this.coordOf(point, 'lat')\n\t\t},\n\n\t\tpointLng(point) {\n\t\t\treturn this.coordOf(point, 'lng')\n\t\t},\n\n\t\tcoordsLabel(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${lat.toFixed(5)}, ${lng.toFixed(5)}`\n\t\t},\n\n\t\tpointUrl(point) {\n\t\t\tif (typeof point.url === 'string' && point.url !== '') {\n\t\t\t\treturn point.url\n\t\t\t}\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat !== null && lng !== null) {\n\t\t\t\treturn `${this.mapsAppUrl}/?point=${lat},${lng}`\n\t\t\t}\n\t\t\treturn this.mapsAppUrl\n\t\t},\n\n\t\tchipSubtitle(point) {\n\t\t\treturn this.coordsLabel(point) || this.pointCategory(point) || this.pointName(point)\n\t\t},\n\n\t\trecencyKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point.addedAt ?? point.added ?? point.created ?? d.addedAt ?? d.added ?? d.created ?? point.id ?? d.id ?? 0\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : 0\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.points = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.points = []\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('[CnMapsCard] failed to fetch locations', err)\n\t\t\t\tthis.points = []\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.value === undefined || this.value === null || !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('[CnMapsCard] failed to fetch single location', 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-maps-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-maps-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-maps-card__headline-line {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-maps-card__headline-icon {\n\tcolor: var(--color-primary-element, #21468B);\n\tflex-shrink: 0;\n}\n\n.cn-maps-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 4px;\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-maps-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-card__headline-category {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-card__headline-coords {\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-size: 0.75em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-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-maps-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-card__chip-category {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-card__chip--fallback {\n\topacity: 0.85;\n}\n\n.cn-maps-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-maps-card__row {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 8px;\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-maps-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-maps-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n\tflex-shrink: 0;\n\tpadding-top: 3px;\n}\n\n.cn-maps-card__row-main {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-maps-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-maps-card__title {\n\tflex: 1;\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-maps-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-card__category {\n\tflex-shrink: 0;\n\tfont-size: 0.65em;\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.03em;\n}\n\n.cn-maps-card__coords {\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-size: 0.7em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-card__view-all {\n\tmargin-top: 6px;\n\tfont-size: 0.8em;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-maps-card__view-all a {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: none;\n}\n\n.cn-maps-card__view-all a:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["CnDetailCard","t","n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnMapsCard.vue2.js","sources":["../../../../../src/integrations/builtin/maps/CnMapsCard.vue"],"sourcesContent":["<!--\n CnMapsCard — bespoke surface-aware widget for the `maps` integration.\n\n Replaces the generic CnIntegrationCard for the `maps` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard : map-pin icon + headline\n \"N linked locations\" + the most-recently added POI (name +\n category + coords).\n - detail-page : compact list of linked POIs\n (icon + name + category badge + coords) capped at COMPACT_LIMIT\n with a \"view all\" trail-off pointing at NC Maps.\n - single-entity : pin + name chip for\n reference-property auto-rendering (referenceType: 'maps').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnMapsTab. Provider shape and field-fallback strategy matches the\n tab (see the tab's docblock).\n\n See `openregister/openspec/changes/integration-maps/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span\n\t\t\t\tv-if=\"entity\"\n\t\t\t\tclass=\"cn-maps-card__chip\"\n\t\t\t\t:title=\"chipSubtitle(entity)\">\n\t\t\t\t<MapMarker :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"pointUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ pointName(entity) }}</a>\n\t\t\t\t<span v-if=\"pointCategory(entity)\" class=\"cn-maps-card__chip-category\">\n\t\t\t\t\t· {{ pointCategory(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else-if=\"value\" class=\"cn-maps-card__chip cn-maps-card__chip--fallback\">\n\t\t\t\t<MapMarker :size=\"14\" />\n\t\t\t\t<span>{{ value }}</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-maps-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: count + 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-maps-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"points.length === 0\" class=\"cn-maps-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-maps-card__headline\">\n\t\t\t\t<div class=\"cn-maps-card__headline-line\">\n\t\t\t\t\t<MapMarker :size=\"16\" class=\"cn-maps-card__headline-icon\" />\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-maps-card__headline-recent\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pointUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ pointName(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"pointCategory(mostRecent)\" class=\"cn-maps-card__headline-category\">\n\t\t\t\t\t\t· {{ pointCategory(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent && coordsLabel(mostRecent)\" class=\"cn-maps-card__headline-coords\">\n\t\t\t\t\t{{ coordsLabel(mostRecent) }}\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"points.length > 1\" class=\"cn-maps-card__view-all\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"mapsAppUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ viewAllLabel }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-maps-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"points.length === 0\" class=\"cn-maps-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-maps-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"point in displayedPoints\"\n\t\t\t\t\t:key=\"pointKey(point)\"\n\t\t\t\t\tclass=\"cn-maps-card__row\">\n\t\t\t\t\t<MapMarker :size=\"14\" class=\"cn-maps-card__row-icon\" />\n\t\t\t\t\t<div class=\"cn-maps-card__row-main\">\n\t\t\t\t\t\t<div class=\"cn-maps-card__row-header\">\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\t\tclass=\"cn-maps-card__title\">{{ pointName(point) }}</a>\n\t\t\t\t\t\t\t<span v-if=\"pointCategory(point)\" class=\"cn-maps-card__category\">\n\t\t\t\t\t\t\t\t{{ pointCategory(point) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<span v-if=\"coordsLabel(point)\" class=\"cn-maps-card__coords\">\n\t\t\t\t\t\t\t{{ coordsLabel(point) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t\t<div v-if=\"points.length > COMPACT_LIMIT\" class=\"cn-maps-card__view-all\">\n\t\t\t\t<a\n\t\t\t\t\t:href=\"mapsAppUrl\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ viewAllLabel }}</a>\n\t\t\t</div>\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 MapMarker from 'vue-material-design-icons/MapMarker.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\n\n/**\n * CnMapsCard — bespoke surface-aware widget for the `maps` integration.\n *\n * Renders Maps-aware metadata across all four AD-19 surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnMapsCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, MapMarker },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'maps'`). */\n\t\tintegrationId: { type: String, default: 'maps' },\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 (POI id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Locations') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => MapMarker },\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 locations linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Location is currently unavailable.') },\n\t\t/** Pre-translated \"view all\" trail-off link label. */\n\t\tviewAllLabel: { type: String, default: () => t('nextcloud-vue', 'View all in Locations') },\n\t\t/** URL of the NC Maps app entry. */\n\t\tmapsAppUrl: { type: String, default: '/index.php/apps/maps' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tCOMPACT_LIMIT,\n\t\t\tpoints: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\tdisplayedPoints() {\n\t\t\treturn this.points.slice(0, COMPACT_LIMIT)\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.points.length\n\t\t\treturn n('nextcloud-vue', '{count} linked location', '{count} linked locations', total, { count: total })\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.points.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Maps provider has no modified-time column; fall back to id-order\n\t\t\t// (highest id == most recently added). Defensive: also check\n\t\t\t// addedAt / created when present in a future expansion.\n\t\t\tconst sorted = [...this.points].sort((a, b) => {\n\t\t\t\tconst ka = this.recencyKey(a)\n\t\t\t\tconst kb = this.recencyKey(b)\n\t\t\t\treturn kb - ka\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\tdataOf(point) {\n\t\t\treturn (point && typeof point.data === 'object' && point.data !== null) ? point.data : {}\n\t\t},\n\n\t\tpointKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.id ?? d.id ?? this.pointName(point)\n\t\t},\n\n\t\tpointName(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst candidate = point.name ?? point.title ?? d.name ?? d.title ?? ''\n\t\t\treturn stripMarker(candidate) || String(this.pointKey(point))\n\t\t},\n\n\t\tpointCategory(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst c = point.category ?? d.category ?? ''\n\t\t\treturn stripMarker(c)\n\t\t},\n\n\t\tcoordOf(point, key) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point[key] ?? d[key] ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : null\n\t\t},\n\n\t\tpointLat(point) {\n\t\t\treturn this.coordOf(point, 'lat')\n\t\t},\n\n\t\tpointLng(point) {\n\t\t\treturn this.coordOf(point, 'lng')\n\t\t},\n\n\t\tcoordsLabel(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${lat.toFixed(5)}, ${lng.toFixed(5)}`\n\t\t},\n\n\t\t// A provider URL is used only when it is a safe scheme; else the app link.\n\t\tpointUrl(point) {\n\t\t\tif (typeof point.url === 'string' && point.url !== '' && safeHref(point.url) !== '#') {\n\t\t\t\treturn point.url\n\t\t\t}\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat !== null && lng !== null) {\n\t\t\t\treturn `${this.mapsAppUrl}/?point=${lat},${lng}`\n\t\t\t}\n\t\t\treturn this.mapsAppUrl\n\t\t},\n\n\t\tchipSubtitle(point) {\n\t\t\treturn this.coordsLabel(point) || this.pointCategory(point) || this.pointName(point)\n\t\t},\n\n\t\trecencyKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point.addedAt ?? point.added ?? point.created ?? d.addedAt ?? d.added ?? d.created ?? point.id ?? d.id ?? 0\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : 0\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.points = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.points = []\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('[CnMapsCard] failed to fetch locations', err)\n\t\t\t\tthis.points = []\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.value === undefined || this.value === null || !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('[CnMapsCard] failed to fetch single location', 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-maps-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-maps-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-maps-card__headline-line {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-maps-card__headline-icon {\n\tcolor: var(--color-primary-element, #21468B);\n\tflex-shrink: 0;\n}\n\n.cn-maps-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 4px;\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-maps-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-card__headline-category {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-card__headline-coords {\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-size: 0.75em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-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-maps-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-card__chip-category {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-card__chip--fallback {\n\topacity: 0.85;\n}\n\n.cn-maps-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-maps-card__row {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 8px;\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-maps-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-maps-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n\tflex-shrink: 0;\n\tpadding-top: 3px;\n}\n\n.cn-maps-card__row-main {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-maps-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-maps-card__title {\n\tflex: 1;\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-maps-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-card__category {\n\tflex-shrink: 0;\n\tfont-size: 0.65em;\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.03em;\n}\n\n.cn-maps-card__coords {\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-size: 0.7em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-card__view-all {\n\tmargin-top: 6px;\n\tfont-size: 0.8em;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-maps-card__view-all a {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: none;\n}\n\n.cn-maps-card__view-all a:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["CnDetailCard","t","n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnMapsCard.vue3.js","sources":["../../../../../src/integrations/builtin/maps/CnMapsCard.vue?vue&type=template&id=4c0775e7&lang.js"],"sourcesContent":["<!--\n CnMapsCard — bespoke surface-aware widget for the `maps` integration.\n\n Replaces the generic CnIntegrationCard for the `maps` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard : map-pin icon + headline\n \"N linked locations\" + the most-recently added POI (name +\n category + coords).\n - detail-page : compact list of linked POIs\n (icon + name + category badge + coords) capped at COMPACT_LIMIT\n with a \"view all\" trail-off pointing at NC Maps.\n - single-entity : pin + name chip for\n reference-property auto-rendering (referenceType: 'maps').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnMapsTab. Provider shape and field-fallback strategy matches the\n tab (see the tab's docblock).\n\n See `openregister/openspec/changes/integration-maps/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span\n\t\t\t\tv-if=\"entity\"\n\t\t\t\tclass=\"cn-maps-card__chip\"\n\t\t\t\t:title=\"chipSubtitle(entity)\">\n\t\t\t\t<MapMarker :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"pointUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ pointName(entity) }}</a>\n\t\t\t\t<span v-if=\"pointCategory(entity)\" class=\"cn-maps-card__chip-category\">\n\t\t\t\t\t· {{ pointCategory(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else-if=\"value\" class=\"cn-maps-card__chip cn-maps-card__chip--fallback\">\n\t\t\t\t<MapMarker :size=\"14\" />\n\t\t\t\t<span>{{ value }}</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-maps-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: count + 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-maps-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"points.length === 0\" class=\"cn-maps-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-maps-card__headline\">\n\t\t\t\t<div class=\"cn-maps-card__headline-line\">\n\t\t\t\t\t<MapMarker :size=\"16\" class=\"cn-maps-card__headline-icon\" />\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-maps-card__headline-recent\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pointUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ pointName(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"pointCategory(mostRecent)\" class=\"cn-maps-card__headline-category\">\n\t\t\t\t\t\t· {{ pointCategory(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent && coordsLabel(mostRecent)\" class=\"cn-maps-card__headline-coords\">\n\t\t\t\t\t{{ coordsLabel(mostRecent) }}\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"points.length > 1\" class=\"cn-maps-card__view-all\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"mapsAppUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ viewAllLabel }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-maps-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"points.length === 0\" class=\"cn-maps-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-maps-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"point in displayedPoints\"\n\t\t\t\t\t:key=\"pointKey(point)\"\n\t\t\t\t\tclass=\"cn-maps-card__row\">\n\t\t\t\t\t<MapMarker :size=\"14\" class=\"cn-maps-card__row-icon\" />\n\t\t\t\t\t<div class=\"cn-maps-card__row-main\">\n\t\t\t\t\t\t<div class=\"cn-maps-card__row-header\">\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\t\tclass=\"cn-maps-card__title\">{{ pointName(point) }}</a>\n\t\t\t\t\t\t\t<span v-if=\"pointCategory(point)\" class=\"cn-maps-card__category\">\n\t\t\t\t\t\t\t\t{{ pointCategory(point) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<span v-if=\"coordsLabel(point)\" class=\"cn-maps-card__coords\">\n\t\t\t\t\t\t\t{{ coordsLabel(point) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t\t<div v-if=\"points.length > COMPACT_LIMIT\" class=\"cn-maps-card__view-all\">\n\t\t\t\t<a\n\t\t\t\t\t:href=\"mapsAppUrl\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ viewAllLabel }}</a>\n\t\t\t</div>\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 MapMarker from 'vue-material-design-icons/MapMarker.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\n\n/**\n * CnMapsCard — bespoke surface-aware widget for the `maps` integration.\n *\n * Renders Maps-aware metadata across all four AD-19 surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnMapsCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, MapMarker },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'maps'`). */\n\t\tintegrationId: { type: String, default: 'maps' },\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 (POI id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Locations') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => MapMarker },\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 locations linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Location is currently unavailable.') },\n\t\t/** Pre-translated \"view all\" trail-off link label. */\n\t\tviewAllLabel: { type: String, default: () => t('nextcloud-vue', 'View all in Locations') },\n\t\t/** URL of the NC Maps app entry. */\n\t\tmapsAppUrl: { type: String, default: '/index.php/apps/maps' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tCOMPACT_LIMIT,\n\t\t\tpoints: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\tdisplayedPoints() {\n\t\t\treturn this.points.slice(0, COMPACT_LIMIT)\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.points.length\n\t\t\treturn n('nextcloud-vue', '{count} linked location', '{count} linked locations', total, { count: total })\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.points.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Maps provider has no modified-time column; fall back to id-order\n\t\t\t// (highest id == most recently added). Defensive: also check\n\t\t\t// addedAt / created when present in a future expansion.\n\t\t\tconst sorted = [...this.points].sort((a, b) => {\n\t\t\t\tconst ka = this.recencyKey(a)\n\t\t\t\tconst kb = this.recencyKey(b)\n\t\t\t\treturn kb - ka\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\tdataOf(point) {\n\t\t\treturn (point && typeof point.data === 'object' && point.data !== null) ? point.data : {}\n\t\t},\n\n\t\tpointKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.id ?? d.id ?? this.pointName(point)\n\t\t},\n\n\t\tpointName(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst candidate = point.name ?? point.title ?? d.name ?? d.title ?? ''\n\t\t\treturn stripMarker(candidate) || String(this.pointKey(point))\n\t\t},\n\n\t\tpointCategory(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst c = point.category ?? d.category ?? ''\n\t\t\treturn stripMarker(c)\n\t\t},\n\n\t\tcoordOf(point, key) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point[key] ?? d[key] ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : null\n\t\t},\n\n\t\tpointLat(point) {\n\t\t\treturn this.coordOf(point, 'lat')\n\t\t},\n\n\t\tpointLng(point) {\n\t\t\treturn this.coordOf(point, 'lng')\n\t\t},\n\n\t\tcoordsLabel(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${lat.toFixed(5)}, ${lng.toFixed(5)}`\n\t\t},\n\n\t\tpointUrl(point) {\n\t\t\tif (typeof point.url === 'string' && point.url !== '') {\n\t\t\t\treturn point.url\n\t\t\t}\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat !== null && lng !== null) {\n\t\t\t\treturn `${this.mapsAppUrl}/?point=${lat},${lng}`\n\t\t\t}\n\t\t\treturn this.mapsAppUrl\n\t\t},\n\n\t\tchipSubtitle(point) {\n\t\t\treturn this.coordsLabel(point) || this.pointCategory(point) || this.pointName(point)\n\t\t},\n\n\t\trecencyKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point.addedAt ?? point.added ?? point.created ?? d.addedAt ?? d.added ?? d.created ?? point.id ?? d.id ?? 0\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : 0\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.points = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.points = []\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('[CnMapsCard] failed to fetch locations', err)\n\t\t\t\tthis.points = []\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.value === undefined || this.value === null || !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('[CnMapsCard] failed to fetch single location', 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-maps-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-maps-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-maps-card__headline-line {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-maps-card__headline-icon {\n\tcolor: var(--color-primary-element, #21468B);\n\tflex-shrink: 0;\n}\n\n.cn-maps-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 4px;\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-maps-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-card__headline-category {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-card__headline-coords {\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-size: 0.75em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-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-maps-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-card__chip-category {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-card__chip--fallback {\n\topacity: 0.85;\n}\n\n.cn-maps-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-maps-card__row {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 8px;\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-maps-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-maps-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n\tflex-shrink: 0;\n\tpadding-top: 3px;\n}\n\n.cn-maps-card__row-main {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-maps-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-maps-card__title {\n\tflex: 1;\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-maps-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-card__category {\n\tflex-shrink: 0;\n\tfont-size: 0.65em;\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.03em;\n}\n\n.cn-maps-card__coords {\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-size: 0.7em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-card__view-all {\n\tmargin-top: 6px;\n\tfont-size: 0.8em;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-maps-card__view-all a {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: none;\n}\n\n.cn-maps-card__view-all a:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["_createBlock","_createElementBlock","_Fragment","_createVNode","_createElementVNode","_toDisplayString","_openBlock"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnMapsCard.vue3.js","sources":["../../../../../src/integrations/builtin/maps/CnMapsCard.vue?vue&type=template&id=4c0775e7&lang.js"],"sourcesContent":["<!--\n CnMapsCard — bespoke surface-aware widget for the `maps` integration.\n\n Replaces the generic CnIntegrationCard for the `maps` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard : map-pin icon + headline\n \"N linked locations\" + the most-recently added POI (name +\n category + coords).\n - detail-page : compact list of linked POIs\n (icon + name + category badge + coords) capped at COMPACT_LIMIT\n with a \"view all\" trail-off pointing at NC Maps.\n - single-entity : pin + name chip for\n reference-property auto-rendering (referenceType: 'maps').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnMapsTab. Provider shape and field-fallback strategy matches the\n tab (see the tab's docblock).\n\n See `openregister/openspec/changes/integration-maps/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span\n\t\t\t\tv-if=\"entity\"\n\t\t\t\tclass=\"cn-maps-card__chip\"\n\t\t\t\t:title=\"chipSubtitle(entity)\">\n\t\t\t\t<MapMarker :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"pointUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ pointName(entity) }}</a>\n\t\t\t\t<span v-if=\"pointCategory(entity)\" class=\"cn-maps-card__chip-category\">\n\t\t\t\t\t· {{ pointCategory(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else-if=\"value\" class=\"cn-maps-card__chip cn-maps-card__chip--fallback\">\n\t\t\t\t<MapMarker :size=\"14\" />\n\t\t\t\t<span>{{ value }}</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-maps-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: count + 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-maps-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"points.length === 0\" class=\"cn-maps-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-maps-card__headline\">\n\t\t\t\t<div class=\"cn-maps-card__headline-line\">\n\t\t\t\t\t<MapMarker :size=\"16\" class=\"cn-maps-card__headline-icon\" />\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-maps-card__headline-recent\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pointUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ pointName(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"pointCategory(mostRecent)\" class=\"cn-maps-card__headline-category\">\n\t\t\t\t\t\t· {{ pointCategory(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent && coordsLabel(mostRecent)\" class=\"cn-maps-card__headline-coords\">\n\t\t\t\t\t{{ coordsLabel(mostRecent) }}\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"points.length > 1\" class=\"cn-maps-card__view-all\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"mapsAppUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ viewAllLabel }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-maps-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"points.length === 0\" class=\"cn-maps-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-maps-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"point in displayedPoints\"\n\t\t\t\t\t:key=\"pointKey(point)\"\n\t\t\t\t\tclass=\"cn-maps-card__row\">\n\t\t\t\t\t<MapMarker :size=\"14\" class=\"cn-maps-card__row-icon\" />\n\t\t\t\t\t<div class=\"cn-maps-card__row-main\">\n\t\t\t\t\t\t<div class=\"cn-maps-card__row-header\">\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\t\tclass=\"cn-maps-card__title\">{{ pointName(point) }}</a>\n\t\t\t\t\t\t\t<span v-if=\"pointCategory(point)\" class=\"cn-maps-card__category\">\n\t\t\t\t\t\t\t\t{{ pointCategory(point) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<span v-if=\"coordsLabel(point)\" class=\"cn-maps-card__coords\">\n\t\t\t\t\t\t\t{{ coordsLabel(point) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t\t<div v-if=\"points.length > COMPACT_LIMIT\" class=\"cn-maps-card__view-all\">\n\t\t\t\t<a\n\t\t\t\t\t:href=\"mapsAppUrl\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ viewAllLabel }}</a>\n\t\t\t</div>\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 MapMarker from 'vue-material-design-icons/MapMarker.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\n\n/**\n * CnMapsCard — bespoke surface-aware widget for the `maps` integration.\n *\n * Renders Maps-aware metadata across all four AD-19 surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnMapsCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, MapMarker },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'maps'`). */\n\t\tintegrationId: { type: String, default: 'maps' },\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 (POI id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Locations') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => MapMarker },\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 locations linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Location is currently unavailable.') },\n\t\t/** Pre-translated \"view all\" trail-off link label. */\n\t\tviewAllLabel: { type: String, default: () => t('nextcloud-vue', 'View all in Locations') },\n\t\t/** URL of the NC Maps app entry. */\n\t\tmapsAppUrl: { type: String, default: '/index.php/apps/maps' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tCOMPACT_LIMIT,\n\t\t\tpoints: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\tdisplayedPoints() {\n\t\t\treturn this.points.slice(0, COMPACT_LIMIT)\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.points.length\n\t\t\treturn n('nextcloud-vue', '{count} linked location', '{count} linked locations', total, { count: total })\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.points.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Maps provider has no modified-time column; fall back to id-order\n\t\t\t// (highest id == most recently added). Defensive: also check\n\t\t\t// addedAt / created when present in a future expansion.\n\t\t\tconst sorted = [...this.points].sort((a, b) => {\n\t\t\t\tconst ka = this.recencyKey(a)\n\t\t\t\tconst kb = this.recencyKey(b)\n\t\t\t\treturn kb - ka\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\tdataOf(point) {\n\t\t\treturn (point && typeof point.data === 'object' && point.data !== null) ? point.data : {}\n\t\t},\n\n\t\tpointKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.id ?? d.id ?? this.pointName(point)\n\t\t},\n\n\t\tpointName(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst candidate = point.name ?? point.title ?? d.name ?? d.title ?? ''\n\t\t\treturn stripMarker(candidate) || String(this.pointKey(point))\n\t\t},\n\n\t\tpointCategory(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst c = point.category ?? d.category ?? ''\n\t\t\treturn stripMarker(c)\n\t\t},\n\n\t\tcoordOf(point, key) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point[key] ?? d[key] ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : null\n\t\t},\n\n\t\tpointLat(point) {\n\t\t\treturn this.coordOf(point, 'lat')\n\t\t},\n\n\t\tpointLng(point) {\n\t\t\treturn this.coordOf(point, 'lng')\n\t\t},\n\n\t\tcoordsLabel(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${lat.toFixed(5)}, ${lng.toFixed(5)}`\n\t\t},\n\n\t\t// A provider URL is used only when it is a safe scheme; else the app link.\n\t\tpointUrl(point) {\n\t\t\tif (typeof point.url === 'string' && point.url !== '' && safeHref(point.url) !== '#') {\n\t\t\t\treturn point.url\n\t\t\t}\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat !== null && lng !== null) {\n\t\t\t\treturn `${this.mapsAppUrl}/?point=${lat},${lng}`\n\t\t\t}\n\t\t\treturn this.mapsAppUrl\n\t\t},\n\n\t\tchipSubtitle(point) {\n\t\t\treturn this.coordsLabel(point) || this.pointCategory(point) || this.pointName(point)\n\t\t},\n\n\t\trecencyKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point.addedAt ?? point.added ?? point.created ?? d.addedAt ?? d.added ?? d.created ?? point.id ?? d.id ?? 0\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : 0\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.points = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.points = []\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('[CnMapsCard] failed to fetch locations', err)\n\t\t\t\tthis.points = []\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.value === undefined || this.value === null || !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('[CnMapsCard] failed to fetch single location', 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-maps-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-maps-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-maps-card__headline-line {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-maps-card__headline-icon {\n\tcolor: var(--color-primary-element, #21468B);\n\tflex-shrink: 0;\n}\n\n.cn-maps-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 4px;\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-maps-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-card__headline-category {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-card__headline-coords {\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-size: 0.75em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-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-maps-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-card__chip-category {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-card__chip--fallback {\n\topacity: 0.85;\n}\n\n.cn-maps-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-maps-card__row {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 8px;\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-maps-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-maps-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n\tflex-shrink: 0;\n\tpadding-top: 3px;\n}\n\n.cn-maps-card__row-main {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-maps-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-maps-card__title {\n\tflex: 1;\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-maps-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-card__category {\n\tflex-shrink: 0;\n\tfont-size: 0.65em;\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.03em;\n}\n\n.cn-maps-card__coords {\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-size: 0.7em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-card__view-all {\n\tmargin-top: 6px;\n\tfont-size: 0.8em;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-maps-card__view-all a {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: none;\n}\n\n.cn-maps-card__view-all a:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["_createBlock","_createElementBlock","_Fragment","_createVNode","_createElementVNode","_toDisplayString","_openBlock"],"mappings":"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"}
|
|
@@ -10,6 +10,7 @@ import Plus from 'vue-material-design-icons/Plus.vue';
|
|
|
10
10
|
import __sfc_main$1 from '../../../components/CnMapPoiCreate/CnMapPoiCreate.vue.js';
|
|
11
11
|
import __sfc_main from '../../../components/CnMapPoiPicker/CnMapPoiPicker.vue.js';
|
|
12
12
|
import { stripMarker } from '../../utils/marker.js';
|
|
13
|
+
import { safeHref } from '../../../utils/safeHref.js';
|
|
13
14
|
import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
|
|
14
15
|
|
|
15
16
|
const COMMENT_MAX_CHARS = 160;
|
|
@@ -210,8 +211,9 @@ var script = {
|
|
|
210
211
|
return `${lat.toFixed(5)}, ${lng.toFixed(5)}`
|
|
211
212
|
},
|
|
212
213
|
|
|
214
|
+
// A provider URL is used only when it is a safe scheme; else the app link.
|
|
213
215
|
pointUrl(point) {
|
|
214
|
-
if (typeof point.url === 'string' && point.url !== '') {
|
|
216
|
+
if (typeof point.url === 'string' && point.url !== '' && safeHref(point.url) !== '#') {
|
|
215
217
|
return point.url
|
|
216
218
|
}
|
|
217
219
|
if (this.pointHasCoords(point)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnMapsTab.vue2.js","sources":["../../../../../src/integrations/builtin/maps/CnMapsTab.vue"],"sourcesContent":["<!--\n CnMapsTab — bespoke sidebar tab for the `maps` integration.\n\n Replaces the generic CnIntegrationTab for the `maps` leaf: renders\n linked POIs / favourites with name, category badge, comment snippet,\n and a nicely-formatted lat/lng coordinate pair. Each row deep-links\n into the NC Maps app — the provider already supplies a per-POI URL\n (`/index.php/apps/maps/#/m={id}`); the tab also exposes a separate\n \"Open on map\" link in the format requested by host bundles\n (`/index.php/apps/maps/?point={lat},{lng}` — see the task brief).\n\n Talks to the same OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/maps`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\MapsProvider`\n (storage strategy `link-table` — marker `[or:{uuid}]` lives in the\n Maps `maps_favorites.name` column). Provider payload shape (verified\n against `MapsProvider::list`):\n { id, title, url, data: { id, name, lat, lng, category, comment, ... } }\n The richer \"spec\" shape (name, category, lat, lng, comment, url) is\n accepted too — the UI reads top-level and `data.*` fields defensively.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat title\n list which loses the category badge, the human-readable coordinate\n string and the comment snippet — three signals that make a POI\n legible at-a-glance. The bespoke tab surfaces them per row.\n\n Visual fidelity: each row mirrors a real NC Maps favourite — an\n offline-safe map-pin tile (no remote tiles / no external images),\n the bold location name, an address subline (falls back to the\n category when no street address is present), a monospace lat/long\n line, and a compact coordinate chip when the row carries coordinates.\n Rows are rendered as `NcListItem`s so the tab matches NC Maps' own\n favourites list look-and-feel. The pin tile is tinted with the leaf\n accent (`--cn-iw-accent`, set from the descriptor's `accentColor`)\n falling back to the theme primary.\n\n Mini-map preview: deliberately skipped — the only inline map\n component in this repo (`CnMapWidget`) is a heavy Leaflet wrapper\n intended for full-page rendering, not for a sidebar row, and would\n pull remote tiles. We surface a static pin tile + formatted\n coordinates + an \"Open on map\" link instead.\n\n Surface behaviour:\n - Empty state (\"No locations linked yet\") + \"Open Locations\" CTA\n when zero rows.\n - Loading state via NcLoadingIcon for the full-tab spin.\n - 503 unavailable banner (matches CnIntegrationTab's AD-23 graceful\n degradation), generic error path on non-OK / fetch throw.\n\n See `openregister/openspec/changes/integration-maps/` for the spec\n delta and ADR-019 (registry mechanism), ADR-022 (consumption).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-maps-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-maps-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 class=\"cn-maps-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 location') }}\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 location') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<div v-else-if=\"error\" class=\"cn-maps-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<div\n\t\t\tv-else-if=\"points.length === 0 && !degraded\"\n\t\t\tclass=\"cn-sidebar-tab__empty cn-maps-tab__empty\">\n\t\t\t<MapMarker :size=\"32\" class=\"cn-maps-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=\"mapsAppUrl\"\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<MapMarker :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openMapsLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<ul v-else-if=\"points.length > 0\" class=\"cn-maps-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"point in points\"\n\t\t\t\t:key=\"pointKey(point)\"\n\t\t\t\tclass=\"cn-maps-tab__row\"\n\t\t\t\t:name=\"pointName(point)\"\n\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<span class=\"cn-maps-tab__pin\" aria-hidden=\"true\">\n\t\t\t\t\t\t<MapMarker :size=\"22\" class=\"cn-maps-tab__pin-icon\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #name>\n\t\t\t\t\t<span class=\"cn-maps-tab__title\">{{ pointName(point) }}</span>\n\t\t\t\t\t<span v-if=\"pointCategory(point)\" class=\"cn-maps-tab__category\">\n\t\t\t\t\t\t{{ pointCategory(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-maps-tab__subname\">\n\t\t\t\t\t\t<span v-if=\"addressLabel(point)\" class=\"cn-maps-tab__address\">{{ addressLabel(point) }}</span>\n\t\t\t\t\t\t<span v-if=\"coordsLabel(point)\" class=\"cn-maps-tab__coords\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"13\" class=\"cn-maps-tab__coords-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-maps-tab__coords-text\">{{ coordsLabel(point) }}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\tv-if=\"pointHasCoords(point)\"\n\t\t\t\t\t\t\t:href=\"openOnMapUrl(point)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-maps-tab__open-link\"\n\t\t\t\t\t\t\t@click.stop>\n\t\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"pointComment(point)\" class=\"cn-maps-tab__comment\">\n\t\t\t\t\t\t{{ pointComment(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"pointHasCoords(point)\" #indicator>\n\t\t\t\t\t<span class=\"cn-maps-tab__coord-chip\" :title=\"coordsLabel(point)\">\n\t\t\t\t\t\t<MapMarker :size=\"11\" />\n\t\t\t\t\t\t{{ coordsLabel(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #actions>\n\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t<NcActionButton :closeAfterClick=\"true\" @click=\"unlinkPoint(point)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink location') }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcListItem>\n\t\t</ul>\n\n\t\t<CnMapPoiPicker\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<CnMapPoiCreate\n\t\t\tv-if=\"createOpen\"\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, NcActionLink, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport MapMarker from 'vue-material-design-icons/MapMarker.vue'\nimport MapMarkerOutline from 'vue-material-design-icons/MapMarkerOutline.vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnMapPoiCreate from '../../../components/CnMapPoiCreate/CnMapPoiCreate.vue'\nimport CnMapPoiPicker from '../../../components/CnMapPoiPicker/CnMapPoiPicker.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst COMMENT_MAX_CHARS = 160\n\n/**\n * CnMapsTab — bespoke sidebar list for the `maps` integration.\n *\n * Renders POIs / favourites pulled from the OR pluggable-integration\n * endpoint with name, category, comment snippet, formatted coordinates,\n * and an \"Open on map\" deep-link.\n */\nexport default {\n\tname: 'CnMapsTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tMapMarker,\n\t\tMapMarkerOutline,\n\t\tOpenInNew,\n\t\tPlus,\n\t\tCnMapPoiPicker,\n\t\tCnMapPoiCreate,\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 `'maps'`). */\n\t\tintegrationId: { type: String, default: 'maps' },\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 locations linked yet') },\n\t\t/** Pre-translated \"Open Locations\" CTA label. */\n\t\topenMapsLabel: { type: String, default: () => t('nextcloud-vue', 'Open Locations') },\n\t\t/** Pre-translated \"Open on map\" inline link label. */\n\t\topenOnMapLabel: { type: String, default: () => t('nextcloud-vue', 'Open on map') },\n\t\t/** Pre-translated banner when Maps is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Location is currently unavailable.') },\n\t\t/** URL of the NC Maps app entry. */\n\t\tmapsAppUrl: { type: String, default: '/index.php/apps/maps' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpoints: [],\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}\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchPoints()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Base for the Tier-2 maps endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tmapsEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/maps`\n\t\t},\n\n\t\tdataOf(point) {\n\t\t\treturn (point && typeof point.data === 'object' && point.data !== null) ? point.data : {}\n\t\t},\n\n\t\tpointKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? this.pointName(point)\n\t\t},\n\n\t\t/**\n\t\t * The NC Maps favorite id used for the Tier-2 unlink DELETE.\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {(number|string)} The favorite id.\n\t\t */\n\t\tfavoriteIdOf(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? ''\n\t\t},\n\n\t\tpointName(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst candidate = point.name ?? point.title ?? d.name ?? d.title ?? ''\n\t\t\t// Provider returns the name (which carries the `[or:{uuid}]`\n\t\t\t// marker) as title fallback. Strip the marker for display.\n\t\t\treturn stripMarker(candidate) || String(this.pointKey(point))\n\t\t},\n\n\t\tpointCategory(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst c = point.category ?? d.category ?? ''\n\t\t\t// Maps fixtures sometimes land a bare `or:{uuid}` marker in\n\t\t\t// the category column when no real category exists. Strip\n\t\t\t// the marker and suppress the chip when nothing meaningful\n\t\t\t// remains — surfacing the marker as a chip artefact (see\n\t\t\t// phase-d1 maps-tab.png) is the bug we're fixing.\n\t\t\treturn stripMarker(c)\n\t\t},\n\n\t\tpointComment(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.comment ?? d.comment ?? ''\n\t\t\tconst text = String(raw).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (text.length > COMMENT_MAX_CHARS) {\n\t\t\t\treturn text.slice(0, COMMENT_MAX_CHARS).trimEnd() + '…'\n\t\t\t}\n\t\t\treturn text\n\t\t},\n\n\t\t/**\n\t\t * Human-readable address subline for the row.\n\t\t *\n\t\t * NC Maps favourites don't always carry a structured address, so\n\t\t * read a few common fields defensively (top-level + `data.*`) and\n\t\t * fall back to the category when no street address exists — that\n\t\t * keeps the subline informative without surfacing the bare coords\n\t\t * twice (coords have their own line).\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {string} The address subline (may be empty).\n\t\t */\n\t\taddressLabel(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.address ?? point.formattedAddress ?? d.address ?? d.formattedAddress ?? d.street ?? ''\n\t\t\tconst text = stripMarker(String(raw)).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text !== '') {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\treturn this.pointCategory(point)\n\t\t},\n\n\t\tcoordOf(point, key) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point[key] ?? d[key] ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : null\n\t\t},\n\n\t\tpointLat(point) {\n\t\t\treturn this.coordOf(point, 'lat')\n\t\t},\n\n\t\tpointLng(point) {\n\t\t\treturn this.coordOf(point, 'lng')\n\t\t},\n\n\t\tpointHasCoords(point) {\n\t\t\treturn this.pointLat(point) !== null && this.pointLng(point) !== null\n\t\t},\n\n\t\tcoordsLabel(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${lat.toFixed(5)}, ${lng.toFixed(5)}`\n\t\t},\n\n\t\tpointUrl(point) {\n\t\t\tif (typeof point.url === 'string' && point.url !== '') {\n\t\t\t\treturn point.url\n\t\t\t}\n\t\t\tif (this.pointHasCoords(point)) {\n\t\t\t\treturn this.openOnMapUrl(point)\n\t\t\t}\n\t\t\treturn this.mapsAppUrl\n\t\t},\n\n\t\topenOnMapUrl(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn this.mapsAppUrl\n\t\t\t}\n\t\t\treturn `${this.mapsAppUrl}/?point=${lat},${lng}`\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.mapsEndpoint()), {\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.fetchPoints()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This location is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\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('[CnMapsTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\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.mapsEndpoint()}/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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\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('[CnMapsTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkPoint(point) {\n\t\t\tconst favoriteId = this.favoriteIdOf(point)\n\t\t\tif (!favoriteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.mapsEndpoint()}/${favoriteId}`), {\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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\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('[CnMapsTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchPoints() {\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.mapsEndpoint()), { 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.points = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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('[CnMapsTab] failed to fetch locations', err)\n\t\t\t\tthis.points = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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-maps-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-maps-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-maps-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-maps-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-maps-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Offline-safe static pin tile — a tinted rounded square with a marker\n glyph, mirroring a Maps favourite thumbnail without loading any tiles. */\n.cn-maps-tab__pin {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--cn-iw-accent, var(--color-primary-element, #0082c9));\n\tcolor: var(--color-primary-element-text, #fff);\n\tflex-shrink: 0;\n}\n\n.cn-maps-tab__pin-icon {\n\tcolor: var(--color-primary-element-text, #fff);\n}\n\n.cn-maps-tab__title {\n\tfont-weight: 500;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__category {\n\tflex-shrink: 0;\n\tmargin-inline-start: 6px;\n\tfont-size: 0.7em;\n\tpadding: 2px 8px;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.03em;\n\tvertical-align: middle;\n}\n\n.cn-maps-tab__subname {\n\tdisplay: flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 4px 10px;\n}\n\n.cn-maps-tab__address {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__coords {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__coords-icon {\n\tcolor: var(--cn-iw-accent, var(--color-primary-element));\n}\n\n.cn-maps-tab__coords-text {\n\tfont-family: var(--font-face-monospace, monospace);\n}\n\n.cn-maps-tab__open-link {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: none;\n}\n\n.cn-maps-tab__open-link:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-tab__comment {\n\tdisplay: -webkit-box;\n\tmargin-top: 2px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\t-webkit-line-clamp: 2;\n\t-webkit-box-orient: vertical;\n}\n\n/* Compact coordinate chip in the row indicator slot. */\n.cn-maps-tab__coord-chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 10px;\n\tfont-size: 0.7em;\n\tfont-family: var(--font-face-monospace, monospace);\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["CnMapPoiPicker","CnMapPoiCreate","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnMapsTab.vue2.js","sources":["../../../../../src/integrations/builtin/maps/CnMapsTab.vue"],"sourcesContent":["<!--\n CnMapsTab — bespoke sidebar tab for the `maps` integration.\n\n Replaces the generic CnIntegrationTab for the `maps` leaf: renders\n linked POIs / favourites with name, category badge, comment snippet,\n and a nicely-formatted lat/lng coordinate pair. Each row deep-links\n into the NC Maps app — the provider already supplies a per-POI URL\n (`/index.php/apps/maps/#/m={id}`); the tab also exposes a separate\n \"Open on map\" link in the format requested by host bundles\n (`/index.php/apps/maps/?point={lat},{lng}` — see the task brief).\n\n Talks to the same OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/maps`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\MapsProvider`\n (storage strategy `link-table` — marker `[or:{uuid}]` lives in the\n Maps `maps_favorites.name` column). Provider payload shape (verified\n against `MapsProvider::list`):\n { id, title, url, data: { id, name, lat, lng, category, comment, ... } }\n The richer \"spec\" shape (name, category, lat, lng, comment, url) is\n accepted too — the UI reads top-level and `data.*` fields defensively.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat title\n list which loses the category badge, the human-readable coordinate\n string and the comment snippet — three signals that make a POI\n legible at-a-glance. The bespoke tab surfaces them per row.\n\n Visual fidelity: each row mirrors a real NC Maps favourite — an\n offline-safe map-pin tile (no remote tiles / no external images),\n the bold location name, an address subline (falls back to the\n category when no street address is present), a monospace lat/long\n line, and a compact coordinate chip when the row carries coordinates.\n Rows are rendered as `NcListItem`s so the tab matches NC Maps' own\n favourites list look-and-feel. The pin tile is tinted with the leaf\n accent (`--cn-iw-accent`, set from the descriptor's `accentColor`)\n falling back to the theme primary.\n\n Mini-map preview: deliberately skipped — the only inline map\n component in this repo (`CnMapWidget`) is a heavy Leaflet wrapper\n intended for full-page rendering, not for a sidebar row, and would\n pull remote tiles. We surface a static pin tile + formatted\n coordinates + an \"Open on map\" link instead.\n\n Surface behaviour:\n - Empty state (\"No locations linked yet\") + \"Open Locations\" CTA\n when zero rows.\n - Loading state via NcLoadingIcon for the full-tab spin.\n - 503 unavailable banner (matches CnIntegrationTab's AD-23 graceful\n degradation), generic error path on non-OK / fetch throw.\n\n See `openregister/openspec/changes/integration-maps/` for the spec\n delta and ADR-019 (registry mechanism), ADR-022 (consumption).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-maps-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-maps-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 class=\"cn-maps-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 location') }}\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 location') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<div v-else-if=\"error\" class=\"cn-maps-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<div\n\t\t\tv-else-if=\"points.length === 0 && !degraded\"\n\t\t\tclass=\"cn-sidebar-tab__empty cn-maps-tab__empty\">\n\t\t\t<MapMarker :size=\"32\" class=\"cn-maps-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=\"mapsAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<MapMarker :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openMapsLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<ul v-else-if=\"points.length > 0\" class=\"cn-maps-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"point in points\"\n\t\t\t\t:key=\"pointKey(point)\"\n\t\t\t\tclass=\"cn-maps-tab__row\"\n\t\t\t\t:name=\"pointName(point)\"\n\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<span class=\"cn-maps-tab__pin\" aria-hidden=\"true\">\n\t\t\t\t\t\t<MapMarker :size=\"22\" class=\"cn-maps-tab__pin-icon\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #name>\n\t\t\t\t\t<span class=\"cn-maps-tab__title\">{{ pointName(point) }}</span>\n\t\t\t\t\t<span v-if=\"pointCategory(point)\" class=\"cn-maps-tab__category\">\n\t\t\t\t\t\t{{ pointCategory(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-maps-tab__subname\">\n\t\t\t\t\t\t<span v-if=\"addressLabel(point)\" class=\"cn-maps-tab__address\">{{ addressLabel(point) }}</span>\n\t\t\t\t\t\t<span v-if=\"coordsLabel(point)\" class=\"cn-maps-tab__coords\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"13\" class=\"cn-maps-tab__coords-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-maps-tab__coords-text\">{{ coordsLabel(point) }}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\tv-if=\"pointHasCoords(point)\"\n\t\t\t\t\t\t\t:href=\"openOnMapUrl(point)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-maps-tab__open-link\"\n\t\t\t\t\t\t\t@click.stop>\n\t\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"pointComment(point)\" class=\"cn-maps-tab__comment\">\n\t\t\t\t\t\t{{ pointComment(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"pointHasCoords(point)\" #indicator>\n\t\t\t\t\t<span class=\"cn-maps-tab__coord-chip\" :title=\"coordsLabel(point)\">\n\t\t\t\t\t\t<MapMarker :size=\"11\" />\n\t\t\t\t\t\t{{ coordsLabel(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #actions>\n\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t<NcActionButton :closeAfterClick=\"true\" @click=\"unlinkPoint(point)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink location') }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcListItem>\n\t\t</ul>\n\n\t\t<CnMapPoiPicker\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<CnMapPoiCreate\n\t\t\tv-if=\"createOpen\"\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, NcActionLink, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport MapMarker from 'vue-material-design-icons/MapMarker.vue'\nimport MapMarkerOutline from 'vue-material-design-icons/MapMarkerOutline.vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnMapPoiCreate from '../../../components/CnMapPoiCreate/CnMapPoiCreate.vue'\nimport CnMapPoiPicker from '../../../components/CnMapPoiPicker/CnMapPoiPicker.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst COMMENT_MAX_CHARS = 160\n\n/**\n * CnMapsTab — bespoke sidebar list for the `maps` integration.\n *\n * Renders POIs / favourites pulled from the OR pluggable-integration\n * endpoint with name, category, comment snippet, formatted coordinates,\n * and an \"Open on map\" deep-link.\n */\nexport default {\n\tname: 'CnMapsTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tMapMarker,\n\t\tMapMarkerOutline,\n\t\tOpenInNew,\n\t\tPlus,\n\t\tCnMapPoiPicker,\n\t\tCnMapPoiCreate,\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 `'maps'`). */\n\t\tintegrationId: { type: String, default: 'maps' },\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 locations linked yet') },\n\t\t/** Pre-translated \"Open Locations\" CTA label. */\n\t\topenMapsLabel: { type: String, default: () => t('nextcloud-vue', 'Open Locations') },\n\t\t/** Pre-translated \"Open on map\" inline link label. */\n\t\topenOnMapLabel: { type: String, default: () => t('nextcloud-vue', 'Open on map') },\n\t\t/** Pre-translated banner when Maps is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Location is currently unavailable.') },\n\t\t/** URL of the NC Maps app entry. */\n\t\tmapsAppUrl: { type: String, default: '/index.php/apps/maps' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpoints: [],\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}\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchPoints()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Base for the Tier-2 maps endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tmapsEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/maps`\n\t\t},\n\n\t\tdataOf(point) {\n\t\t\treturn (point && typeof point.data === 'object' && point.data !== null) ? point.data : {}\n\t\t},\n\n\t\tpointKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? this.pointName(point)\n\t\t},\n\n\t\t/**\n\t\t * The NC Maps favorite id used for the Tier-2 unlink DELETE.\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {(number|string)} The favorite id.\n\t\t */\n\t\tfavoriteIdOf(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? ''\n\t\t},\n\n\t\tpointName(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst candidate = point.name ?? point.title ?? d.name ?? d.title ?? ''\n\t\t\t// Provider returns the name (which carries the `[or:{uuid}]`\n\t\t\t// marker) as title fallback. Strip the marker for display.\n\t\t\treturn stripMarker(candidate) || String(this.pointKey(point))\n\t\t},\n\n\t\tpointCategory(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst c = point.category ?? d.category ?? ''\n\t\t\t// Maps fixtures sometimes land a bare `or:{uuid}` marker in\n\t\t\t// the category column when no real category exists. Strip\n\t\t\t// the marker and suppress the chip when nothing meaningful\n\t\t\t// remains — surfacing the marker as a chip artefact (see\n\t\t\t// phase-d1 maps-tab.png) is the bug we're fixing.\n\t\t\treturn stripMarker(c)\n\t\t},\n\n\t\tpointComment(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.comment ?? d.comment ?? ''\n\t\t\tconst text = String(raw).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (text.length > COMMENT_MAX_CHARS) {\n\t\t\t\treturn text.slice(0, COMMENT_MAX_CHARS).trimEnd() + '…'\n\t\t\t}\n\t\t\treturn text\n\t\t},\n\n\t\t/**\n\t\t * Human-readable address subline for the row.\n\t\t *\n\t\t * NC Maps favourites don't always carry a structured address, so\n\t\t * read a few common fields defensively (top-level + `data.*`) and\n\t\t * fall back to the category when no street address exists — that\n\t\t * keeps the subline informative without surfacing the bare coords\n\t\t * twice (coords have their own line).\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {string} The address subline (may be empty).\n\t\t */\n\t\taddressLabel(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.address ?? point.formattedAddress ?? d.address ?? d.formattedAddress ?? d.street ?? ''\n\t\t\tconst text = stripMarker(String(raw)).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text !== '') {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\treturn this.pointCategory(point)\n\t\t},\n\n\t\tcoordOf(point, key) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point[key] ?? d[key] ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : null\n\t\t},\n\n\t\tpointLat(point) {\n\t\t\treturn this.coordOf(point, 'lat')\n\t\t},\n\n\t\tpointLng(point) {\n\t\t\treturn this.coordOf(point, 'lng')\n\t\t},\n\n\t\tpointHasCoords(point) {\n\t\t\treturn this.pointLat(point) !== null && this.pointLng(point) !== null\n\t\t},\n\n\t\tcoordsLabel(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${lat.toFixed(5)}, ${lng.toFixed(5)}`\n\t\t},\n\n\t\t// A provider URL is used only when it is a safe scheme; else the app link.\n\t\tpointUrl(point) {\n\t\t\tif (typeof point.url === 'string' && point.url !== '' && safeHref(point.url) !== '#') {\n\t\t\t\treturn point.url\n\t\t\t}\n\t\t\tif (this.pointHasCoords(point)) {\n\t\t\t\treturn this.openOnMapUrl(point)\n\t\t\t}\n\t\t\treturn this.mapsAppUrl\n\t\t},\n\n\t\topenOnMapUrl(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn this.mapsAppUrl\n\t\t\t}\n\t\t\treturn `${this.mapsAppUrl}/?point=${lat},${lng}`\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.mapsEndpoint()), {\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.fetchPoints()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This location is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\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('[CnMapsTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\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.mapsEndpoint()}/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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\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('[CnMapsTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkPoint(point) {\n\t\t\tconst favoriteId = this.favoriteIdOf(point)\n\t\t\tif (!favoriteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.mapsEndpoint()}/${favoriteId}`), {\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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\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('[CnMapsTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchPoints() {\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.mapsEndpoint()), { 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.points = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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('[CnMapsTab] failed to fetch locations', err)\n\t\t\t\tthis.points = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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-maps-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-maps-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-maps-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-maps-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-maps-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Offline-safe static pin tile — a tinted rounded square with a marker\n glyph, mirroring a Maps favourite thumbnail without loading any tiles. */\n.cn-maps-tab__pin {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--cn-iw-accent, var(--color-primary-element, #0082c9));\n\tcolor: var(--color-primary-element-text, #fff);\n\tflex-shrink: 0;\n}\n\n.cn-maps-tab__pin-icon {\n\tcolor: var(--color-primary-element-text, #fff);\n}\n\n.cn-maps-tab__title {\n\tfont-weight: 500;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__category {\n\tflex-shrink: 0;\n\tmargin-inline-start: 6px;\n\tfont-size: 0.7em;\n\tpadding: 2px 8px;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.03em;\n\tvertical-align: middle;\n}\n\n.cn-maps-tab__subname {\n\tdisplay: flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 4px 10px;\n}\n\n.cn-maps-tab__address {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__coords {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__coords-icon {\n\tcolor: var(--cn-iw-accent, var(--color-primary-element));\n}\n\n.cn-maps-tab__coords-text {\n\tfont-family: var(--font-face-monospace, monospace);\n}\n\n.cn-maps-tab__open-link {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: none;\n}\n\n.cn-maps-tab__open-link:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-tab__comment {\n\tdisplay: -webkit-box;\n\tmargin-top: 2px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\t-webkit-line-clamp: 2;\n\t-webkit-box-orient: vertical;\n}\n\n/* Compact coordinate chip in the row indicator slot. */\n.cn-maps-tab__coord-chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 10px;\n\tfont-size: 0.7em;\n\tfont-family: var(--font-face-monospace, monospace);\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["CnMapPoiPicker","CnMapPoiCreate","t"],"mappings":";;;;;;;;;;;;;;;AA+LA,MAAM,iBAAgB,GAAI,IAAE;;AAE5B;;AAEC;;AAEA;;;AAGD,aAAe;AACd,CAAA,IAAI,EAAE,WAAW;;CAEjB,UAAU,EAAE;AACX,EAAA,cAAc;EACd,YAAY;AACZ,EAAA,QAAQ;EACR,UAAU;EACV,aAAa;AACb,EAAA,kBAAkB;AAClB,EAAA,OAAO;EACP,WAAW;AACX,EAAA,SAAS;AACT,EAAA,gBAAgB;AAChB,EAAA,SAAS;EACT,IAAI;AACJ,kBAAAA,UAAc;AACd,kBAAAC,YAAc;EACd;;AAED,CAAA,KAAK,EAAE;AACN;AACA;AACA,EAAA,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ;AAChD;AACA;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;AAC1C;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AACvC;AACA,EAAA,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AACrC;EACA,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAuB,EAAG;AAC5D;AACA,EAAA,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMC,SAAC,CAAC,eAAe,EAAE,yBAAyB,GAAG;AAC1F;AACA,EAAA,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,gBAAgB,GAAG;AACpF;AACA,EAAA,cAAc,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,aAAa,GAAG;;EAElF,gBAAgB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,uCAAuC,CAAA,EAAG;;EAE9G,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAqB,EAAG;EAC7D;;AAED,CAAA,IAAI,GAAG;AACN,EAAA,OAAO;AACN,GAAA,MAAM,EAAE,EAAE;GACV,OAAO,EAAE,KAAK;AACd,GAAA,KAAK,EAAE,EAAE;GACT,QAAQ,EAAE,EAAE;AACZ,GAAA,UAAU,EAAE,KAAK;AACjB,GAAA,UAAU,EAAE,KAAK;AAClB,GAAA;EACA;;AAED,CAAA,KAAK,EAAE;AACN,EAAA,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE;AACxC,GAAA,IAAI,EAAE,EAAE;AACP,IAAA,IAAI,CAAC,WAAW,GAAC;AAClB,IAAA;AACD,GAAA,EAAG;;EAEH,QAAQ,GAAG;AACV,GAAA,IAAI,CAAC,WAAW,GAAC;GACjB;;AAED,EAAA,MAAM,GAAG;AACR,GAAA,IAAI,CAAC,WAAW,GAAC;GACjB;EACD;;AAED,CAAA,OAAO,EAAE;AACR;;AAEC;AACA;;AAED,EAAA,YAAY,GAAG;AACd,GAAA,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAA;GACrF;;EAED,MAAM,CAAC,KAAK,EAAE;AACb,GAAA,OAAO,CAAC,KAAM,IAAG,OAAO,KAAK,CAAC,SAAS,QAAO,IAAK,KAAK,CAAC,IAAG,KAAM,IAAI,IAAI,KAAK,CAAC,IAAG,GAAI,EAAC;GACxF;;AAED,EAAA,QAAQ,CAAC,KAAK,EAAE;GACf,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAA;AAC3B,GAAA,OAAO,KAAK,CAAC,UAAS,IAAK,KAAK,CAAC,EAAC,IAAK,CAAC,CAAC,UAAS,IAAK,CAAC,CAAC,EAAG,IAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAA;GACnF;;AAED;;AAEC;AACA;AACA;AACA;;AAED,EAAA,YAAY,CAAC,KAAK,EAAE;GACnB,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAA;GAC3B,OAAO,KAAK,CAAC,UAAW,IAAG,KAAK,CAAC,EAAC,IAAK,CAAC,CAAC,UAAS,IAAK,CAAC,CAAC,EAAC,IAAK,EAAC;GAChE;;AAED,EAAA,SAAS,CAAC,KAAK,EAAE;GAChB,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAA;GAC3B,MAAM,SAAQ,GAAI,KAAK,CAAC,IAAG,IAAK,KAAK,CAAC,KAAI,IAAK,CAAC,CAAC,IAAK,IAAG,CAAC,CAAC,KAAI,IAAK,GAAC;;;GAGrE,OAAO,WAAW,CAAC,SAAS,CAAA,IAAK,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;GAC5D;;AAED,EAAA,aAAa,CAAC,KAAK,EAAE;GACpB,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAA;AAC3B,GAAA,MAAM,IAAI,KAAK,CAAC,QAAS,IAAG,CAAC,CAAC,QAAS,IAAG,GAAC;;;;;AAK3C;AACA,GAAA,OAAO,WAAW,CAAC,CAAC,CAAA;GACpB;;AAED,EAAA,YAAY,CAAC,KAAK,EAAE;GACnB,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAA;AAC3B,GAAA,MAAM,GAAE,GAAI,KAAK,CAAC,OAAM,IAAK,CAAC,CAAC,OAAQ,IAAG,GAAC;GAC3C,MAAM,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,GAAC;GACnD,IAAI,IAAK,KAAI,EAAE,EAAE;AAChB,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,IAAI,IAAI,CAAC,SAAS,iBAAiB,EAAE;IACpC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,OAAO,EAAC,GAAI,GAAE;AACvD,IAAA;AACA,GAAA,OAAO,IAAG;GACV;;AAED;AACC;AACA;;;;;AAKA;AACA;AACA;AACA;AACA;;AAED,EAAA,YAAY,CAAC,KAAK,EAAE;GACnB,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAA;AAC3B,GAAA,MAAM,MAAM,KAAK,CAAC,OAAM,IAAK,KAAK,CAAC,gBAAiB,IAAG,CAAC,CAAC,WAAW,CAAC,CAAC,oBAAoB,CAAC,CAAC,MAAO,IAAG,GAAC;GACvG,MAAM,IAAK,GAAE,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,GAAC;GAChE,IAAI,IAAK,KAAI,EAAE,EAAE;AAChB,IAAA,OAAO,IAAG;AACX,IAAA;AACA,GAAA,OAAO,IAAI,CAAC,aAAa,CAAC,KAAK,CAAA;GAC/B;;AAED,EAAA,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE;GACnB,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAA;AAC3B,GAAA,MAAM,CAAE,GAAE,KAAK,CAAC,GAAG,CAAA,IAAK,CAAC,CAAC,GAAG,CAAA,IAAK,KAAG;AACrC,GAAA,IAAI,CAAE,KAAI,IAAK,IAAG,CAAA,KAAM,SAAQ,IAAK,MAAM,EAAE,EAAE;AAC9C,IAAA,OAAO,IAAG;AACX,IAAA;AACA,GAAA,MAAM,GAAI,GAAE,OAAO,CAAA,KAAM,WAAW,CAAA,GAAI,MAAM,CAAC,CAAC,EAAA;AAChD,GAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,IAAG,GAAI,MAAM,IAAG;GAChD;;AAED,EAAA,QAAQ,CAAC,KAAK,EAAE;AACf,GAAA,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAA;GAChC;;AAED,EAAA,QAAQ,CAAC,KAAK,EAAE;AACf,GAAA,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAA;GAChC;;AAED,EAAA,cAAc,CAAC,KAAK,EAAE;GACrB,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAA,KAAM,IAAK,IAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAE,KAAI,IAAG;GACpE;;AAED,EAAA,WAAW,CAAC,KAAK,EAAE;GAClB,MAAM,GAAE,GAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAA;GAC/B,MAAM,GAAE,GAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAA;GAC/B,IAAI,GAAE,KAAM,IAAG,IAAK,GAAE,KAAM,IAAI,EAAE;AACjC,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAA;GAC5C;;;AAGD,EAAA,QAAQ,CAAC,KAAK,EAAE;AACf,GAAA,IAAI,OAAO,KAAK,CAAC,GAAI,KAAI,YAAY,KAAK,CAAC,GAAE,KAAM,EAAC,IAAK,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAA,KAAM,GAAG,EAAE;AACrF,IAAA,OAAO,KAAK,CAAC,GAAE;AAChB,IAAA;AACA,GAAA,IAAI,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE;IAC/B,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,CAAA;AAC/B,IAAA;AACA,GAAA,OAAO,IAAI,CAAC,UAAS;GACrB;;AAED,EAAA,YAAY,CAAC,KAAK,EAAE;GACnB,MAAM,GAAE,GAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAA;GAC/B,MAAM,GAAE,GAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAA;GAC/B,IAAI,GAAE,KAAM,IAAG,IAAK,GAAE,KAAM,IAAI,EAAE;AACjC,IAAA,OAAO,IAAI,CAAC,UAAS;AACtB,IAAA;GACA,OAAO,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;GAC/C;;EAED,UAAU,GAAG;AACZ,GAAA,IAAI,CAAC,aAAa,KAAG;GACrB;;EAED,UAAU,GAAG;AACZ,GAAA,IAAI,CAAC,aAAa,KAAG;GACrB;;EAED,MAAM,UAAU,CAAC,OAAO,EAAE;AACzB,GAAA,IAAI,CAAC,UAAS,GAAI,MAAI;AACtB,GAAA,IAAI;IACH,MAAM,QAAO,GAAI,MAAM,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE;KAC5D,MAAM,EAAE,MAAM;KACd,OAAO,EAAE,EAAE,GAAG,YAAY,EAAE,EAAE,cAAc,EAAE,kBAAiB,EAAG;KAClE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC7B,EAAA;AACD,IAAA,IAAI,QAAQ,CAAC,EAAE,EAAE;KAChB,MAAM,IAAI,CAAC,WAAW,GAAC;AACxB,KAAA,MAAO,IAAI,QAAQ,CAAC,MAAK,KAAM,GAAG,EAAE;KACnC,IAAI,CAAC,KAAI,GAAIA,SAAC,CAAC,eAAe,EAAE,kCAAkC,EAAA;KACjE,MAAK;KACN,IAAI,CAAC,KAAI,GAAIA,SAAC,CAAC,eAAe,EAAE,0BAA0B,EAAA;AAC3D,KAAA;IACC,CAAA,OAAO,GAAG,EAAE;AACb;AACA,IAAA,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,GAAG,EAAA;IAC5C,IAAI,CAAC,KAAI,GAAIA,SAAC,CAAC,eAAe,EAAE,0BAA0B,EAAA;AAC3D,IAAA;GACA;;EAED,MAAM,YAAY,CAAC,OAAO,EAAE;AAC3B,GAAA,IAAI,CAAC,UAAS,GAAI,MAAI;AACtB,GAAA,IAAI;AACH,IAAA,MAAM,QAAO,GAAI,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE;KACrE,MAAM,EAAE,MAAM;KACd,OAAO,EAAE,EAAE,GAAG,YAAY,EAAE,EAAE,cAAc,EAAE,kBAAiB,EAAG;KAClE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC7B,EAAA;AACD,IAAA,IAAI,QAAQ,CAAC,EAAE,EAAE;KAChB,MAAM,IAAI,CAAC,WAAW,GAAC;KACtB,MAAK;KACN,IAAI,CAAC,KAAM,GAAEA,SAAC,CAAC,eAAe,EAAE,4BAA4B,EAAA;AAC7D,KAAA;IACC,CAAA,OAAO,GAAG,EAAE;AACb;AACA,IAAA,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,GAAG,EAAA;IAC9C,IAAI,CAAC,KAAM,GAAEA,SAAC,CAAC,eAAe,EAAE,4BAA4B,EAAA;AAC7D,IAAA;GACA;;EAED,MAAM,WAAW,CAAC,KAAK,EAAE;AACxB,GAAA,MAAM,UAAW,GAAE,IAAI,CAAC,YAAY,CAAC,KAAK,EAAA;AAC1C,GAAA,IAAI,CAAC,UAAU,EAAE;IAChB,MAAK;AACN,IAAA;AACA,GAAA,IAAI;AACH,IAAA,MAAM,QAAO,GAAI,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE;AAC/E,KAAA,MAAM,EAAE,QAAQ;KAChB,OAAO,EAAE,YAAY,EAAE;KACvB,EAAA;AACD,IAAA,IAAI,QAAQ,CAAC,EAAE,EAAE;KAChB,MAAM,IAAI,CAAC,WAAW,GAAC;KACtB,MAAK;KACN,IAAI,CAAC,KAAM,GAAEA,SAAC,CAAC,eAAe,EAAE,4BAA4B,EAAA;AAC7D,KAAA;IACC,CAAA,OAAO,GAAG,EAAE;AACb;AACA,IAAA,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,GAAG,EAAA;IAC9C,IAAI,CAAC,KAAM,GAAEA,SAAC,CAAC,eAAe,EAAE,4BAA4B,EAAA;AAC7D,IAAA;GACA;;AAED,EAAA,MAAM,WAAW,GAAG;GACnB,IAAI,CAAC,IAAI,CAAC,QAAO,IAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE;IACrD,MAAK;AACN,IAAA;AACA,GAAA,IAAI,CAAC,OAAM,GAAI,KAAG;GAClB,IAAI,CAAC,KAAM,GAAE,GAAC;AACd,GAAA,IAAI,CAAC,QAAO,GAAI,GAAC;AACjB,GAAA,IAAI;AACH,IAAA,MAAM,QAAS,GAAE,MAAM,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,YAAY,IAAI,EAAA;AACxF,IAAA,IAAI,QAAQ,CAAC,EAAE,EAAE;KAChB,MAAM,IAAK,GAAE,MAAM,QAAQ,CAAC,IAAI,GAAC;AACjC,KAAA,MAAM,IAAG,GAAI,IAAI,CAAC,OAAM,IAAK,IAAI,CAAC,KAAI,KAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAE,GAAE,IAAK,GAAE,EAAE,CAAE,IAAG,GAAC;AACjF,KAAA,IAAI,CAAC,MAAK,GAAI,KAAG;KAChB,MAAK,IAAI,QAAQ,CAAC,MAAO,KAAI,GAAE,IAAK,QAAQ,CAAC,MAAO,KAAI,GAAG,EAAE;KAC9D,IAAI,CAAC,MAAO,GAAE,GAAC;AACf,KAAA,IAAI,CAAC,QAAS,GAAE,IAAI,CAAC,iBAAe;KACnC,MAAK;KACN,IAAI,CAAC,MAAO,GAAE,GAAC;KACf,IAAI,CAAC,KAAI,GAAIA,SAAC,CAAC,eAAe,EAAE,2BAA2B,EAAA;AAC5D,KAAA;IACC,CAAA,OAAO,GAAG,EAAE;AACb;IACA,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,GAAG,EAAA;IAC1D,IAAI,CAAC,MAAO,GAAE,GAAC;IACf,IAAI,CAAC,KAAI,GAAIA,SAAC,CAAC,eAAe,EAAE,2BAA2B,EAAA;AAC5D,IAAA,SAAU;AACT,IAAA,IAAI,CAAC,OAAQ,GAAE,MAAI;AACpB,IAAA;GACA;EACD;AACF;;;;"}
|
|
@@ -109,8 +109,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
109
109
|
createVNode(_component_NcButton, {
|
|
110
110
|
variant: "primary",
|
|
111
111
|
href: $props.mapsAppUrl,
|
|
112
|
-
target: "_blank"
|
|
113
|
-
rel: "noopener noreferrer"
|
|
112
|
+
target: "_blank"
|
|
114
113
|
}, {
|
|
115
114
|
icon: withCtx(() => [
|
|
116
115
|
createVNode(_component_MapMarker, { size: 20 })
|
|
@@ -179,8 +178,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
179
178
|
createVNode(_component_NcActionLink, {
|
|
180
179
|
closeAfterClick: true,
|
|
181
180
|
href: $options.pointUrl(point),
|
|
182
|
-
target: "_blank"
|
|
183
|
-
rel: "noopener noreferrer"
|
|
181
|
+
target: "_blank"
|
|
184
182
|
}, {
|
|
185
183
|
icon: withCtx(() => [
|
|
186
184
|
createVNode(_component_OpenInNew, { size: 20 })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnMapsTab.vue3.js","sources":["../../../../../src/integrations/builtin/maps/CnMapsTab.vue?vue&type=template&id=00a3cb8e&lang.js"],"sourcesContent":["<!--\n CnMapsTab — bespoke sidebar tab for the `maps` integration.\n\n Replaces the generic CnIntegrationTab for the `maps` leaf: renders\n linked POIs / favourites with name, category badge, comment snippet,\n and a nicely-formatted lat/lng coordinate pair. Each row deep-links\n into the NC Maps app — the provider already supplies a per-POI URL\n (`/index.php/apps/maps/#/m={id}`); the tab also exposes a separate\n \"Open on map\" link in the format requested by host bundles\n (`/index.php/apps/maps/?point={lat},{lng}` — see the task brief).\n\n Talks to the same OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/maps`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\MapsProvider`\n (storage strategy `link-table` — marker `[or:{uuid}]` lives in the\n Maps `maps_favorites.name` column). Provider payload shape (verified\n against `MapsProvider::list`):\n { id, title, url, data: { id, name, lat, lng, category, comment, ... } }\n The richer \"spec\" shape (name, category, lat, lng, comment, url) is\n accepted too — the UI reads top-level and `data.*` fields defensively.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat title\n list which loses the category badge, the human-readable coordinate\n string and the comment snippet — three signals that make a POI\n legible at-a-glance. The bespoke tab surfaces them per row.\n\n Visual fidelity: each row mirrors a real NC Maps favourite — an\n offline-safe map-pin tile (no remote tiles / no external images),\n the bold location name, an address subline (falls back to the\n category when no street address is present), a monospace lat/long\n line, and a compact coordinate chip when the row carries coordinates.\n Rows are rendered as `NcListItem`s so the tab matches NC Maps' own\n favourites list look-and-feel. The pin tile is tinted with the leaf\n accent (`--cn-iw-accent`, set from the descriptor's `accentColor`)\n falling back to the theme primary.\n\n Mini-map preview: deliberately skipped — the only inline map\n component in this repo (`CnMapWidget`) is a heavy Leaflet wrapper\n intended for full-page rendering, not for a sidebar row, and would\n pull remote tiles. We surface a static pin tile + formatted\n coordinates + an \"Open on map\" link instead.\n\n Surface behaviour:\n - Empty state (\"No locations linked yet\") + \"Open Locations\" CTA\n when zero rows.\n - Loading state via NcLoadingIcon for the full-tab spin.\n - 503 unavailable banner (matches CnIntegrationTab's AD-23 graceful\n degradation), generic error path on non-OK / fetch throw.\n\n See `openregister/openspec/changes/integration-maps/` for the spec\n delta and ADR-019 (registry mechanism), ADR-022 (consumption).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-maps-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-maps-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 class=\"cn-maps-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 location') }}\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 location') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<div v-else-if=\"error\" class=\"cn-maps-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<div\n\t\t\tv-else-if=\"points.length === 0 && !degraded\"\n\t\t\tclass=\"cn-sidebar-tab__empty cn-maps-tab__empty\">\n\t\t\t<MapMarker :size=\"32\" class=\"cn-maps-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=\"mapsAppUrl\"\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<MapMarker :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openMapsLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<ul v-else-if=\"points.length > 0\" class=\"cn-maps-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"point in points\"\n\t\t\t\t:key=\"pointKey(point)\"\n\t\t\t\tclass=\"cn-maps-tab__row\"\n\t\t\t\t:name=\"pointName(point)\"\n\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<span class=\"cn-maps-tab__pin\" aria-hidden=\"true\">\n\t\t\t\t\t\t<MapMarker :size=\"22\" class=\"cn-maps-tab__pin-icon\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #name>\n\t\t\t\t\t<span class=\"cn-maps-tab__title\">{{ pointName(point) }}</span>\n\t\t\t\t\t<span v-if=\"pointCategory(point)\" class=\"cn-maps-tab__category\">\n\t\t\t\t\t\t{{ pointCategory(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-maps-tab__subname\">\n\t\t\t\t\t\t<span v-if=\"addressLabel(point)\" class=\"cn-maps-tab__address\">{{ addressLabel(point) }}</span>\n\t\t\t\t\t\t<span v-if=\"coordsLabel(point)\" class=\"cn-maps-tab__coords\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"13\" class=\"cn-maps-tab__coords-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-maps-tab__coords-text\">{{ coordsLabel(point) }}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\tv-if=\"pointHasCoords(point)\"\n\t\t\t\t\t\t\t:href=\"openOnMapUrl(point)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-maps-tab__open-link\"\n\t\t\t\t\t\t\t@click.stop>\n\t\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"pointComment(point)\" class=\"cn-maps-tab__comment\">\n\t\t\t\t\t\t{{ pointComment(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"pointHasCoords(point)\" #indicator>\n\t\t\t\t\t<span class=\"cn-maps-tab__coord-chip\" :title=\"coordsLabel(point)\">\n\t\t\t\t\t\t<MapMarker :size=\"11\" />\n\t\t\t\t\t\t{{ coordsLabel(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #actions>\n\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t<NcActionButton :closeAfterClick=\"true\" @click=\"unlinkPoint(point)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink location') }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcListItem>\n\t\t</ul>\n\n\t\t<CnMapPoiPicker\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<CnMapPoiCreate\n\t\t\tv-if=\"createOpen\"\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, NcActionLink, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport MapMarker from 'vue-material-design-icons/MapMarker.vue'\nimport MapMarkerOutline from 'vue-material-design-icons/MapMarkerOutline.vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnMapPoiCreate from '../../../components/CnMapPoiCreate/CnMapPoiCreate.vue'\nimport CnMapPoiPicker from '../../../components/CnMapPoiPicker/CnMapPoiPicker.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst COMMENT_MAX_CHARS = 160\n\n/**\n * CnMapsTab — bespoke sidebar list for the `maps` integration.\n *\n * Renders POIs / favourites pulled from the OR pluggable-integration\n * endpoint with name, category, comment snippet, formatted coordinates,\n * and an \"Open on map\" deep-link.\n */\nexport default {\n\tname: 'CnMapsTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tMapMarker,\n\t\tMapMarkerOutline,\n\t\tOpenInNew,\n\t\tPlus,\n\t\tCnMapPoiPicker,\n\t\tCnMapPoiCreate,\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 `'maps'`). */\n\t\tintegrationId: { type: String, default: 'maps' },\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 locations linked yet') },\n\t\t/** Pre-translated \"Open Locations\" CTA label. */\n\t\topenMapsLabel: { type: String, default: () => t('nextcloud-vue', 'Open Locations') },\n\t\t/** Pre-translated \"Open on map\" inline link label. */\n\t\topenOnMapLabel: { type: String, default: () => t('nextcloud-vue', 'Open on map') },\n\t\t/** Pre-translated banner when Maps is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Location is currently unavailable.') },\n\t\t/** URL of the NC Maps app entry. */\n\t\tmapsAppUrl: { type: String, default: '/index.php/apps/maps' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpoints: [],\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}\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchPoints()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Base for the Tier-2 maps endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tmapsEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/maps`\n\t\t},\n\n\t\tdataOf(point) {\n\t\t\treturn (point && typeof point.data === 'object' && point.data !== null) ? point.data : {}\n\t\t},\n\n\t\tpointKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? this.pointName(point)\n\t\t},\n\n\t\t/**\n\t\t * The NC Maps favorite id used for the Tier-2 unlink DELETE.\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {(number|string)} The favorite id.\n\t\t */\n\t\tfavoriteIdOf(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? ''\n\t\t},\n\n\t\tpointName(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst candidate = point.name ?? point.title ?? d.name ?? d.title ?? ''\n\t\t\t// Provider returns the name (which carries the `[or:{uuid}]`\n\t\t\t// marker) as title fallback. Strip the marker for display.\n\t\t\treturn stripMarker(candidate) || String(this.pointKey(point))\n\t\t},\n\n\t\tpointCategory(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst c = point.category ?? d.category ?? ''\n\t\t\t// Maps fixtures sometimes land a bare `or:{uuid}` marker in\n\t\t\t// the category column when no real category exists. Strip\n\t\t\t// the marker and suppress the chip when nothing meaningful\n\t\t\t// remains — surfacing the marker as a chip artefact (see\n\t\t\t// phase-d1 maps-tab.png) is the bug we're fixing.\n\t\t\treturn stripMarker(c)\n\t\t},\n\n\t\tpointComment(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.comment ?? d.comment ?? ''\n\t\t\tconst text = String(raw).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (text.length > COMMENT_MAX_CHARS) {\n\t\t\t\treturn text.slice(0, COMMENT_MAX_CHARS).trimEnd() + '…'\n\t\t\t}\n\t\t\treturn text\n\t\t},\n\n\t\t/**\n\t\t * Human-readable address subline for the row.\n\t\t *\n\t\t * NC Maps favourites don't always carry a structured address, so\n\t\t * read a few common fields defensively (top-level + `data.*`) and\n\t\t * fall back to the category when no street address exists — that\n\t\t * keeps the subline informative without surfacing the bare coords\n\t\t * twice (coords have their own line).\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {string} The address subline (may be empty).\n\t\t */\n\t\taddressLabel(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.address ?? point.formattedAddress ?? d.address ?? d.formattedAddress ?? d.street ?? ''\n\t\t\tconst text = stripMarker(String(raw)).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text !== '') {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\treturn this.pointCategory(point)\n\t\t},\n\n\t\tcoordOf(point, key) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point[key] ?? d[key] ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : null\n\t\t},\n\n\t\tpointLat(point) {\n\t\t\treturn this.coordOf(point, 'lat')\n\t\t},\n\n\t\tpointLng(point) {\n\t\t\treturn this.coordOf(point, 'lng')\n\t\t},\n\n\t\tpointHasCoords(point) {\n\t\t\treturn this.pointLat(point) !== null && this.pointLng(point) !== null\n\t\t},\n\n\t\tcoordsLabel(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${lat.toFixed(5)}, ${lng.toFixed(5)}`\n\t\t},\n\n\t\tpointUrl(point) {\n\t\t\tif (typeof point.url === 'string' && point.url !== '') {\n\t\t\t\treturn point.url\n\t\t\t}\n\t\t\tif (this.pointHasCoords(point)) {\n\t\t\t\treturn this.openOnMapUrl(point)\n\t\t\t}\n\t\t\treturn this.mapsAppUrl\n\t\t},\n\n\t\topenOnMapUrl(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn this.mapsAppUrl\n\t\t\t}\n\t\t\treturn `${this.mapsAppUrl}/?point=${lat},${lng}`\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.mapsEndpoint()), {\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.fetchPoints()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This location is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\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('[CnMapsTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\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.mapsEndpoint()}/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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\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('[CnMapsTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkPoint(point) {\n\t\t\tconst favoriteId = this.favoriteIdOf(point)\n\t\t\tif (!favoriteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.mapsEndpoint()}/${favoriteId}`), {\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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\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('[CnMapsTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchPoints() {\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.mapsEndpoint()), { 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.points = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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('[CnMapsTab] failed to fetch locations', err)\n\t\t\t\tthis.points = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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-maps-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-maps-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-maps-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-maps-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-maps-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Offline-safe static pin tile — a tinted rounded square with a marker\n glyph, mirroring a Maps favourite thumbnail without loading any tiles. */\n.cn-maps-tab__pin {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--cn-iw-accent, var(--color-primary-element, #0082c9));\n\tcolor: var(--color-primary-element-text, #fff);\n\tflex-shrink: 0;\n}\n\n.cn-maps-tab__pin-icon {\n\tcolor: var(--color-primary-element-text, #fff);\n}\n\n.cn-maps-tab__title {\n\tfont-weight: 500;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__category {\n\tflex-shrink: 0;\n\tmargin-inline-start: 6px;\n\tfont-size: 0.7em;\n\tpadding: 2px 8px;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.03em;\n\tvertical-align: middle;\n}\n\n.cn-maps-tab__subname {\n\tdisplay: flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 4px 10px;\n}\n\n.cn-maps-tab__address {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__coords {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__coords-icon {\n\tcolor: var(--cn-iw-accent, var(--color-primary-element));\n}\n\n.cn-maps-tab__coords-text {\n\tfont-family: var(--font-face-monospace, monospace);\n}\n\n.cn-maps-tab__open-link {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: none;\n}\n\n.cn-maps-tab__open-link:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-tab__comment {\n\tdisplay: -webkit-box;\n\tmargin-top: 2px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\t-webkit-line-clamp: 2;\n\t-webkit-box-orient: vertical;\n}\n\n/* Compact coordinate chip in the row indicator slot. */\n.cn-maps-tab__coord-chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 10px;\n\tfont-size: 0.7em;\n\tfont-family: var(--font-face-monospace, monospace);\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_createTextVNode","_createVNode","_toDisplayString","_createBlock"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnMapsTab.vue3.js","sources":["../../../../../src/integrations/builtin/maps/CnMapsTab.vue?vue&type=template&id=00a3cb8e&lang.js"],"sourcesContent":["<!--\n CnMapsTab — bespoke sidebar tab for the `maps` integration.\n\n Replaces the generic CnIntegrationTab for the `maps` leaf: renders\n linked POIs / favourites with name, category badge, comment snippet,\n and a nicely-formatted lat/lng coordinate pair. Each row deep-links\n into the NC Maps app — the provider already supplies a per-POI URL\n (`/index.php/apps/maps/#/m={id}`); the tab also exposes a separate\n \"Open on map\" link in the format requested by host bundles\n (`/index.php/apps/maps/?point={lat},{lng}` — see the task brief).\n\n Talks to the same OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/maps`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\MapsProvider`\n (storage strategy `link-table` — marker `[or:{uuid}]` lives in the\n Maps `maps_favorites.name` column). Provider payload shape (verified\n against `MapsProvider::list`):\n { id, title, url, data: { id, name, lat, lng, category, comment, ... } }\n The richer \"spec\" shape (name, category, lat, lng, comment, url) is\n accepted too — the UI reads top-level and `data.*` fields defensively.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat title\n list which loses the category badge, the human-readable coordinate\n string and the comment snippet — three signals that make a POI\n legible at-a-glance. The bespoke tab surfaces them per row.\n\n Visual fidelity: each row mirrors a real NC Maps favourite — an\n offline-safe map-pin tile (no remote tiles / no external images),\n the bold location name, an address subline (falls back to the\n category when no street address is present), a monospace lat/long\n line, and a compact coordinate chip when the row carries coordinates.\n Rows are rendered as `NcListItem`s so the tab matches NC Maps' own\n favourites list look-and-feel. The pin tile is tinted with the leaf\n accent (`--cn-iw-accent`, set from the descriptor's `accentColor`)\n falling back to the theme primary.\n\n Mini-map preview: deliberately skipped — the only inline map\n component in this repo (`CnMapWidget`) is a heavy Leaflet wrapper\n intended for full-page rendering, not for a sidebar row, and would\n pull remote tiles. We surface a static pin tile + formatted\n coordinates + an \"Open on map\" link instead.\n\n Surface behaviour:\n - Empty state (\"No locations linked yet\") + \"Open Locations\" CTA\n when zero rows.\n - Loading state via NcLoadingIcon for the full-tab spin.\n - 503 unavailable banner (matches CnIntegrationTab's AD-23 graceful\n degradation), generic error path on non-OK / fetch throw.\n\n See `openregister/openspec/changes/integration-maps/` for the spec\n delta and ADR-019 (registry mechanism), ADR-022 (consumption).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-maps-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-maps-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 class=\"cn-maps-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 location') }}\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 location') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<div v-else-if=\"error\" class=\"cn-maps-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<div\n\t\t\tv-else-if=\"points.length === 0 && !degraded\"\n\t\t\tclass=\"cn-sidebar-tab__empty cn-maps-tab__empty\">\n\t\t\t<MapMarker :size=\"32\" class=\"cn-maps-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=\"mapsAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<MapMarker :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openMapsLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<ul v-else-if=\"points.length > 0\" class=\"cn-maps-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"point in points\"\n\t\t\t\t:key=\"pointKey(point)\"\n\t\t\t\tclass=\"cn-maps-tab__row\"\n\t\t\t\t:name=\"pointName(point)\"\n\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<span class=\"cn-maps-tab__pin\" aria-hidden=\"true\">\n\t\t\t\t\t\t<MapMarker :size=\"22\" class=\"cn-maps-tab__pin-icon\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #name>\n\t\t\t\t\t<span class=\"cn-maps-tab__title\">{{ pointName(point) }}</span>\n\t\t\t\t\t<span v-if=\"pointCategory(point)\" class=\"cn-maps-tab__category\">\n\t\t\t\t\t\t{{ pointCategory(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-maps-tab__subname\">\n\t\t\t\t\t\t<span v-if=\"addressLabel(point)\" class=\"cn-maps-tab__address\">{{ addressLabel(point) }}</span>\n\t\t\t\t\t\t<span v-if=\"coordsLabel(point)\" class=\"cn-maps-tab__coords\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"13\" class=\"cn-maps-tab__coords-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-maps-tab__coords-text\">{{ coordsLabel(point) }}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\tv-if=\"pointHasCoords(point)\"\n\t\t\t\t\t\t\t:href=\"openOnMapUrl(point)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-maps-tab__open-link\"\n\t\t\t\t\t\t\t@click.stop>\n\t\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"pointComment(point)\" class=\"cn-maps-tab__comment\">\n\t\t\t\t\t\t{{ pointComment(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"pointHasCoords(point)\" #indicator>\n\t\t\t\t\t<span class=\"cn-maps-tab__coord-chip\" :title=\"coordsLabel(point)\">\n\t\t\t\t\t\t<MapMarker :size=\"11\" />\n\t\t\t\t\t\t{{ coordsLabel(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #actions>\n\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t<NcActionButton :closeAfterClick=\"true\" @click=\"unlinkPoint(point)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink location') }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcListItem>\n\t\t</ul>\n\n\t\t<CnMapPoiPicker\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<CnMapPoiCreate\n\t\t\tv-if=\"createOpen\"\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, NcActionLink, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport MapMarker from 'vue-material-design-icons/MapMarker.vue'\nimport MapMarkerOutline from 'vue-material-design-icons/MapMarkerOutline.vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnMapPoiCreate from '../../../components/CnMapPoiCreate/CnMapPoiCreate.vue'\nimport CnMapPoiPicker from '../../../components/CnMapPoiPicker/CnMapPoiPicker.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst COMMENT_MAX_CHARS = 160\n\n/**\n * CnMapsTab — bespoke sidebar list for the `maps` integration.\n *\n * Renders POIs / favourites pulled from the OR pluggable-integration\n * endpoint with name, category, comment snippet, formatted coordinates,\n * and an \"Open on map\" deep-link.\n */\nexport default {\n\tname: 'CnMapsTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tMapMarker,\n\t\tMapMarkerOutline,\n\t\tOpenInNew,\n\t\tPlus,\n\t\tCnMapPoiPicker,\n\t\tCnMapPoiCreate,\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 `'maps'`). */\n\t\tintegrationId: { type: String, default: 'maps' },\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 locations linked yet') },\n\t\t/** Pre-translated \"Open Locations\" CTA label. */\n\t\topenMapsLabel: { type: String, default: () => t('nextcloud-vue', 'Open Locations') },\n\t\t/** Pre-translated \"Open on map\" inline link label. */\n\t\topenOnMapLabel: { type: String, default: () => t('nextcloud-vue', 'Open on map') },\n\t\t/** Pre-translated banner when Maps is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Location is currently unavailable.') },\n\t\t/** URL of the NC Maps app entry. */\n\t\tmapsAppUrl: { type: String, default: '/index.php/apps/maps' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpoints: [],\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}\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchPoints()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Base for the Tier-2 maps endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tmapsEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/maps`\n\t\t},\n\n\t\tdataOf(point) {\n\t\t\treturn (point && typeof point.data === 'object' && point.data !== null) ? point.data : {}\n\t\t},\n\n\t\tpointKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? this.pointName(point)\n\t\t},\n\n\t\t/**\n\t\t * The NC Maps favorite id used for the Tier-2 unlink DELETE.\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {(number|string)} The favorite id.\n\t\t */\n\t\tfavoriteIdOf(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? ''\n\t\t},\n\n\t\tpointName(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst candidate = point.name ?? point.title ?? d.name ?? d.title ?? ''\n\t\t\t// Provider returns the name (which carries the `[or:{uuid}]`\n\t\t\t// marker) as title fallback. Strip the marker for display.\n\t\t\treturn stripMarker(candidate) || String(this.pointKey(point))\n\t\t},\n\n\t\tpointCategory(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst c = point.category ?? d.category ?? ''\n\t\t\t// Maps fixtures sometimes land a bare `or:{uuid}` marker in\n\t\t\t// the category column when no real category exists. Strip\n\t\t\t// the marker and suppress the chip when nothing meaningful\n\t\t\t// remains — surfacing the marker as a chip artefact (see\n\t\t\t// phase-d1 maps-tab.png) is the bug we're fixing.\n\t\t\treturn stripMarker(c)\n\t\t},\n\n\t\tpointComment(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.comment ?? d.comment ?? ''\n\t\t\tconst text = String(raw).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (text.length > COMMENT_MAX_CHARS) {\n\t\t\t\treturn text.slice(0, COMMENT_MAX_CHARS).trimEnd() + '…'\n\t\t\t}\n\t\t\treturn text\n\t\t},\n\n\t\t/**\n\t\t * Human-readable address subline for the row.\n\t\t *\n\t\t * NC Maps favourites don't always carry a structured address, so\n\t\t * read a few common fields defensively (top-level + `data.*`) and\n\t\t * fall back to the category when no street address exists — that\n\t\t * keeps the subline informative without surfacing the bare coords\n\t\t * twice (coords have their own line).\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {string} The address subline (may be empty).\n\t\t */\n\t\taddressLabel(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.address ?? point.formattedAddress ?? d.address ?? d.formattedAddress ?? d.street ?? ''\n\t\t\tconst text = stripMarker(String(raw)).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text !== '') {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\treturn this.pointCategory(point)\n\t\t},\n\n\t\tcoordOf(point, key) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point[key] ?? d[key] ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : null\n\t\t},\n\n\t\tpointLat(point) {\n\t\t\treturn this.coordOf(point, 'lat')\n\t\t},\n\n\t\tpointLng(point) {\n\t\t\treturn this.coordOf(point, 'lng')\n\t\t},\n\n\t\tpointHasCoords(point) {\n\t\t\treturn this.pointLat(point) !== null && this.pointLng(point) !== null\n\t\t},\n\n\t\tcoordsLabel(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${lat.toFixed(5)}, ${lng.toFixed(5)}`\n\t\t},\n\n\t\t// A provider URL is used only when it is a safe scheme; else the app link.\n\t\tpointUrl(point) {\n\t\t\tif (typeof point.url === 'string' && point.url !== '' && safeHref(point.url) !== '#') {\n\t\t\t\treturn point.url\n\t\t\t}\n\t\t\tif (this.pointHasCoords(point)) {\n\t\t\t\treturn this.openOnMapUrl(point)\n\t\t\t}\n\t\t\treturn this.mapsAppUrl\n\t\t},\n\n\t\topenOnMapUrl(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn this.mapsAppUrl\n\t\t\t}\n\t\t\treturn `${this.mapsAppUrl}/?point=${lat},${lng}`\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.mapsEndpoint()), {\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.fetchPoints()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This location is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\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('[CnMapsTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\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.mapsEndpoint()}/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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\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('[CnMapsTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkPoint(point) {\n\t\t\tconst favoriteId = this.favoriteIdOf(point)\n\t\t\tif (!favoriteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.mapsEndpoint()}/${favoriteId}`), {\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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\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('[CnMapsTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchPoints() {\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.mapsEndpoint()), { 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.points = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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('[CnMapsTab] failed to fetch locations', err)\n\t\t\t\tthis.points = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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-maps-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-maps-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-maps-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-maps-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-maps-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Offline-safe static pin tile — a tinted rounded square with a marker\n glyph, mirroring a Maps favourite thumbnail without loading any tiles. */\n.cn-maps-tab__pin {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--cn-iw-accent, var(--color-primary-element, #0082c9));\n\tcolor: var(--color-primary-element-text, #fff);\n\tflex-shrink: 0;\n}\n\n.cn-maps-tab__pin-icon {\n\tcolor: var(--color-primary-element-text, #fff);\n}\n\n.cn-maps-tab__title {\n\tfont-weight: 500;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__category {\n\tflex-shrink: 0;\n\tmargin-inline-start: 6px;\n\tfont-size: 0.7em;\n\tpadding: 2px 8px;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.03em;\n\tvertical-align: middle;\n}\n\n.cn-maps-tab__subname {\n\tdisplay: flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 4px 10px;\n}\n\n.cn-maps-tab__address {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__coords {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__coords-icon {\n\tcolor: var(--cn-iw-accent, var(--color-primary-element));\n}\n\n.cn-maps-tab__coords-text {\n\tfont-family: var(--font-face-monospace, monospace);\n}\n\n.cn-maps-tab__open-link {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: none;\n}\n\n.cn-maps-tab__open-link:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-tab__comment {\n\tdisplay: -webkit-box;\n\tmargin-top: 2px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\t-webkit-line-clamp: 2;\n\t-webkit-box-orient: vertical;\n}\n\n/* Compact coordinate chip in the row indicator slot. */\n.cn-maps-tab__coord-chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 10px;\n\tfont-size: 0.7em;\n\tfont-family: var(--font-face-monospace, monospace);\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_createVNode","_createTextVNode","_createBlock","_toDisplayString"],"mappings":"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"}
|