@conduction/nextcloud-vue 2.141.0 → 2.142.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
  2. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +13 -0
  3. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  4. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +3 -3
  5. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  6. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +21 -0
  7. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  8. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +1 -1
  9. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  10. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  11. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +34 -3
  12. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  13. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  14. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  15. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +43 -3
  16. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  17. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +1 -1
  18. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  19. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +21 -1
  20. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  21. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +2 -1
  22. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  23. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +14 -0
  24. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  25. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
  26. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +3 -1
  27. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  28. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +2 -1
  29. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  30. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js +1 -1
  31. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js +31 -1
  32. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js.map +1 -1
  33. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js.map +1 -1
  34. package/dist/esm/integrations/builtin/contacts/CnContactsTab.vue.js +1 -1
  35. package/dist/esm/integrations/builtin/contacts/CnContactsTab.vue2.js +52 -1
  36. package/dist/esm/integrations/builtin/contacts/CnContactsTab.vue2.js.map +1 -1
  37. package/dist/esm/integrations/builtin/contacts/CnContactsTab.vue3.js +119 -65
  38. package/dist/esm/integrations/builtin/contacts/CnContactsTab.vue3.js.map +1 -1
  39. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue.js +1 -1
  40. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +86 -4
  41. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
  42. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js +67 -5
  43. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
  44. package/dist/esm/l10n/en.json.js +10 -1
  45. package/dist/esm/l10n/en.json.js.map +1 -1
  46. package/dist/esm/l10n/nl.json.js +11 -2
  47. package/dist/esm/l10n/nl.json.js.map +1 -1
  48. package/dist/esm/library-style.js +1 -1
  49. package/dist/esm/nextcloud-vue.css +527 -358
  50. package/dist/esm/utils/buildViewTree.js +23 -8
  51. package/dist/esm/utils/buildViewTree.js.map +1 -1
  52. package/dist/nextcloud-vue.cjs.js +1064 -616
  53. package/dist/nextcloud-vue.cjs.js.map +2 -2
  54. package/dist/nextcloud-vue.css +527 -358
  55. package/l10n/en.json +10 -1
  56. package/l10n/nl.json +11 -2
  57. package/package.json +1 -1
  58. package/src/components/CnCellRenderer/CnCellRenderer.vue +15 -1
  59. package/src/components/CnDataTable/CnDataTable.md +1 -0
  60. package/src/components/CnDataTable/CnDataTable.vue +22 -1
  61. package/src/components/CnFactsWidget/CnFactsWidget.vue +34 -3
  62. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +44 -4
  63. package/src/components/CnIndexPage/CnIndexPage.vue +22 -1
  64. package/src/components/CnIndexPage/useSelfFetchList.js +14 -0
  65. package/src/components/CnObjectSidebar/CnFilesTab.vue +4 -1
  66. package/src/components/CnPartyPicker/CnPartyPicker.vue +31 -1
  67. package/src/css/look-board-detail.css +95 -0
  68. package/src/css/look-board-index.css +53 -0
  69. package/src/css/look-board.css +17 -0
  70. package/src/integrations/builtin/contacts/CnContactsTab.vue +101 -26
  71. package/src/integrations/builtin/contacts/__tests__/CnContactsTabBoardRows.spec.js +67 -0
  72. package/src/integrations/builtin/deck/CnDeckCard.vue +121 -2
  73. package/src/integrations/builtin/deck/__tests__/CnDeckCardHeaderActions.spec.js +68 -0
  74. package/src/utils/buildViewTree.js +23 -8
@@ -1,5 +1,5 @@
1
1
  import { translate } from '@nextcloud/l10n';
2
- import { NcAvatar, NcButton, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue';
2
+ import { NcActionButton, NcActions, NcAvatar, NcButton, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue';
3
3
  import AccountMultipleOutlineIcon from 'vue-material-design-icons/AccountMultipleOutline.vue';
4
4
  import AccountPlus from 'vue-material-design-icons/AccountPlus.vue';
5
5
  import AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue';
@@ -14,6 +14,7 @@ import __sfc_main$1 from '../../../components/CnContactPicker/CnContactPicker.vu
14
14
  import __sfc_main from '../../../components/CnStatusBadge/CnStatusBadge.vue.js';
15
15
  import __sfc_main$3 from '../../../components/CnWidgetWrapper/CnWidgetHeaderActionButtons.vue.js';
16
16
  import { normalizeLook } from '../../../composables/useLook.js';
17
+ import { formatBoardDate } from '../../../utils/boardDate.js';
17
18
  import { WIDGET_BODY_ACTIONS } from '../../../utils/widgetHeaderActions.js';
18
19
  import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
19
20
 
@@ -44,6 +45,8 @@ var script = {
44
45
  name: 'CnContactsTab',
45
46
 
46
47
  components: {
48
+ NcActionButton,
49
+ NcActions,
47
50
  NcAvatar,
48
51
  NcButton,
49
52
  NcEmptyContent,
@@ -134,6 +137,11 @@ var script = {
134
137
  },
135
138
 
136
139
  computed: {
140
+ /** @return {boolean} The board look draws the rows as DqZaakBetrokkenen does. */
141
+ isBoard() {
142
+ return normalizeLook(this.cnLook) === 'board'
143
+ },
144
+
137
145
  /**
138
146
  * The two header buttons in the order and weight of the look: the
139
147
  * board look draws Link contact then New contact, both outlined; the
@@ -426,6 +434,49 @@ var script = {
426
434
  *
427
435
  * @return {string} The window.
428
436
  */
437
+ /**
438
+ * A person's details on one line for the board look: e-mail, phone,
439
+ * organisation and note, separated by middots.
440
+ *
441
+ * @param {object} contact The contact link.
442
+ * @return {string}
443
+ */
444
+ boardMetaOf(contact) {
445
+ return [contact?.email, contact?.phone, contact?.org, contact?.note].filter((part) => typeof part === 'string' && part !== '').join(' · ')
446
+ },
447
+
448
+ /**
449
+ * The validity window in the board's short dates: "Vanaf 3 okt",
450
+ * "Tot 31 okt" or "3 okt – 31 okt".
451
+ *
452
+ * @param {object} contact The contact link.
453
+ * @return {string}
454
+ */
455
+ boardPeriodOf(contact) {
456
+ const from = formatBoardDate(contact?.validFrom || '');
457
+ const until = formatBoardDate(contact?.validUntil || '');
458
+ if (from === '' && until === '') {
459
+ return ''
460
+ }
461
+ if (until === '') {
462
+ return translate('nextcloud-vue', 'From {date}', { date: from })
463
+ }
464
+ if (from === '') {
465
+ return translate('nextcloud-vue', 'Until {date}', { date: until })
466
+ }
467
+ return `${from} – ${until}`
468
+ },
469
+
470
+ /**
471
+ * The label of a person's actions menu.
472
+ *
473
+ * @param {object} contact The contact link.
474
+ * @return {string}
475
+ */
476
+ moreActionsLabel(contact) {
477
+ return translate('nextcloud-vue', 'More actions for {name}', { name: contact?.displayName || this.unknownLabel })
478
+ },
479
+
429
480
  periodOf(contact) {
430
481
  const from = contact?.validFrom || '';
431
482
  const until = contact?.validUntil || '';
@@ -1 +1 @@
1
- {"version":3,"file":"CnContactsTab.vue2.js","sources":["../../../../../src/integrations/builtin/contacts/CnContactsTab.vue"],"sourcesContent":["<!--\n - SPDX-License-Identifier: EUPL-1.2\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n -\n - CnContactsTab — bespoke sidebar tab for the `contacts` integration leaf.\n -\n - Renders the people linked to an OR object grouped by role. Since\n - people-on-objects a person is a Nextcloud user or a vCard contact, and\n - the grouping is the server's when it sends one: the listing carries\n - `byRole` and the schema's `roles`, and the tab falls back to its own\n - buckets (Applicants / Handlers / Advisors / Other, AD-1 of\n - `openspec/changes/integration-contacts/design.md`) for a server that\n - does not. Each card shows avatar, display name, email and role, and a\n - link whose validity has passed reads as inactive.\n -\n - Clicking a contact opens the reverse-lookup flyout (AD-3) listing every\n - OR object linked to that vCard.\n -\n - Tier-2 (this revision): the tab now hosts two header actions — \"Link\n - existing contact\" (opens `CnContactPicker`) and \"Add new contact\"\n - (opens `CnContactCreate`). Both dialogs are loaded lazily via\n - dynamic import to keep the bundle below the integration-tab\n - threshold, and both delegate the actual API call back to this tab\n - so the loading + refresh handshake stays here.\n -\n - Backed by:\n - GET /api/objects/{register}/{schema}/{id}/contacts\n - POST /api/objects/{register}/{schema}/{id}/contacts — link existing\n - POST /api/objects/{register}/{schema}/{id}/contacts/new — create + link\n - DELETE /api/objects/{register}/{schema}/{id}/contacts/{uid} — unlink\n -\n - The payload shape mirrors `ContactLink::jsonSerialize()` —\n - `{id, contactUid, displayName, email, phone, org, avatarUrl, role,\n - addressbookId, contactUri, schemaId, metadata, linkedAt}`.\n -\n - @spec openspec/changes/integration-contacts-tier2/specs/integrations/contacts/spec.md\n -->\n<template>\n\t<div class=\"cn-contacts-tab\">\n\t\t<!-- Header actions: link existing / create new. The board look draws\n\t\t Link contact first and both outlined (DqZaakBetrokkenen); the\n\t\t Nextcloud look keeps Add new contact first and Link as the primary. -->\n\t\t<div class=\"cn-contacts-tab__header\">\n\t\t\t<NcButton\n\t\t\t\tv-for=\"button in headerButtons\"\n\t\t\t\t:key=\"button.key\"\n\t\t\t\t:variant=\"button.variant\"\n\t\t\t\t:aria-label=\"button.label\"\n\t\t\t\t@click=\"button.key === 'link' ? (showLinkDialog = true) : (showCreateDialog = true)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant v-if=\"button.key === 'link'\" :size=\"20\" />\n\t\t\t\t\t<AccountPlus v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ button.label }}\n\t\t\t</NcButton>\n\t\t\t<CnWidgetHeaderActionButtons v-if=\"bodyActions.length > 0\" :actions=\"bodyActions\" />\n\t\t</div>\n\n\t\t<!-- Loading -->\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- Error -->\n\t\t<NcEmptyContent\n\t\t\tv-else-if=\"error\"\n\t\t\t:name=\"errorLabel\"\n\t\t\t:description=\"error\">\n\t\t\t<template #icon>\n\t\t\t\t<AlertCircleOutline :size=\"48\" />\n\t\t\t</template>\n\t\t</NcEmptyContent>\n\n\t\t<!-- Empty -->\n\t\t<NcEmptyContent\n\t\t\tv-else-if=\"contactsArray.length === 0\"\n\t\t\t:name=\"emptyTitleLabel\"\n\t\t\t:description=\"emptyDescriptionLabel\">\n\t\t\t<template #icon>\n\t\t\t\t<AccountMultipleOutline :size=\"48\" />\n\t\t\t</template>\n\t\t</NcEmptyContent>\n\n\t\t<!-- Grouped list -->\n\t\t<div v-else class=\"cn-contacts-tab__groups\">\n\t\t\t<section\n\t\t\t\tv-for=\"group in groupedContacts\"\n\t\t\t\t:key=\"group.key\"\n\t\t\t\tclass=\"cn-contacts-tab__group\">\n\t\t\t\t<h3 class=\"cn-contacts-tab__group-title\">\n\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t<span class=\"cn-contacts-tab__group-count\">{{ group.items.length }}</span>\n\t\t\t\t</h3>\n\t\t\t\t<ul class=\"cn-contacts-tab__list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"contact in group.items\"\n\t\t\t\t\t\t:key=\"contact.id\"\n\t\t\t\t\t\tclass=\"cn-contacts-tab__item\"\n\t\t\t\t\t\t@click=\"openReverseLookup(contact)\">\n\t\t\t\t\t\t<div class=\"cn-contacts-tab__avatar\">\n\t\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\t\t:size=\"40\"\n\t\t\t\t\t\t\t\t:displayName=\"contact.displayName || unknownLabel\"\n\t\t\t\t\t\t\t\t:user=\"avatarSeed(contact)\"\n\t\t\t\t\t\t\t\t:url=\"contact.avatarUrl || undefined\"\n\t\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t\t\t:disableTooltip=\"true\"\n\t\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"cn-contacts-tab__details\">\n\t\t\t\t\t\t\t<div class=\"cn-contacts-tab__name-row\">\n\t\t\t\t\t\t\t\t<span class=\"cn-contacts-tab__name\">\n\t\t\t\t\t\t\t\t\t{{ contact.displayName || unknownLabel }}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\tv-if=\"contact.role\"\n\t\t\t\t\t\t\t\t\t:label=\"roleLabel(contact)\"\n\t\t\t\t\t\t\t\t\t:variant=\"roleVariant(contact)\"\n\t\t\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\tv-if=\"contact.active === false\"\n\t\t\t\t\t\t\t\t\t:label=\"inactiveLabel\"\n\t\t\t\t\t\t\t\t\tvariant=\"default\"\n\t\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-contacts-tab-inactive\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div v-if=\"contact.email\" class=\"cn-contacts-tab__sub\">\n\t\t\t\t\t\t\t\t<Email :size=\"14\" class=\"cn-contacts-tab__sub-icon\" />\n\t\t\t\t\t\t\t\t<span class=\"cn-contacts-tab__sub-text\">{{ contact.email }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div v-if=\"contact.phone\" class=\"cn-contacts-tab__sub\">\n\t\t\t\t\t\t\t\t<Phone :size=\"14\" class=\"cn-contacts-tab__sub-icon\" />\n\t\t\t\t\t\t\t\t<span class=\"cn-contacts-tab__sub-text\">{{ contact.phone }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div v-if=\"contact.org\" class=\"cn-contacts-tab__sub\">\n\t\t\t\t\t\t\t\t<OfficeBuildingOutline :size=\"14\" class=\"cn-contacts-tab__sub-icon\" />\n\t\t\t\t\t\t\t\t<span class=\"cn-contacts-tab__sub-text\">{{ contact.org }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tv-if=\"periodOf(contact)\"\n\t\t\t\t\t\t\t\tclass=\"cn-contacts-tab__sub\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-contacts-tab-period\">\n\t\t\t\t\t\t\t\t<CalendarRange :size=\"14\" class=\"cn-contacts-tab__sub-icon\" />\n\t\t\t\t\t\t\t\t<span class=\"cn-contacts-tab__sub-text\">{{ periodOf(contact) }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div v-if=\"contact.note\" class=\"cn-contacts-tab__sub\">\n\t\t\t\t\t\t\t\t<span class=\"cn-contacts-tab__sub-text\">{{ contact.note }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclass=\"cn-contacts-tab__unlink\"\n\t\t\t\t\t\t\t:aria-label=\"unlinkLabel\"\n\t\t\t\t\t\t\t:title=\"unlinkLabel\"\n\t\t\t\t\t\t\t@click.stop=\"unlink(contact)\">\n\t\t\t\t\t\t\t<Close :size=\"18\" />\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\t\t</div>\n\n\t\t<!-- Link existing contact dialog -->\n\t\t<CnContactPicker\n\t\t\tv-if=\"showLinkDialog\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\tv-bind=\"pickerRoleOptions\"\n\t\t\t@link=\"onPickerLink\"\n\t\t\t@close=\"showLinkDialog = false\" />\n\n\t\t<!-- Create new contact dialog -->\n\t\t<CnContactCreate\n\t\t\tv-if=\"showCreateDialog\"\n\t\t\t:loading=\"createLoading\"\n\t\t\t@create=\"onCreateSubmit\"\n\t\t\t@close=\"showCreateDialog = false\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountMultipleOutline from 'vue-material-design-icons/AccountMultipleOutline.vue'\nimport AccountPlus from 'vue-material-design-icons/AccountPlus.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport CalendarRange from 'vue-material-design-icons/CalendarRange.vue'\nimport Close from 'vue-material-design-icons/Close.vue'\nimport Email from 'vue-material-design-icons/Email.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport OfficeBuildingOutline from 'vue-material-design-icons/OfficeBuildingOutline.vue'\nimport Phone from 'vue-material-design-icons/Phone.vue'\nimport CnContactCreate from '../../../components/CnContactCreate/CnContactCreate.vue'\nimport CnContactPicker from '../../../components/CnContactPicker/CnContactPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport CnWidgetHeaderActionButtons from '../../../components/CnWidgetWrapper/CnWidgetHeaderActionButtons.vue'\nimport { normalizeLook } from '../../../composables/useLook.js'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { WIDGET_BODY_ACTIONS } from '../../../utils/widgetHeaderActions.js'\n\n/**\n * Normalised role keys for grouping. Free-text vCard roles are\n * normalised by lowercasing + trimming; unknown roles fall into `other`.\n *\n * @type {Array<{key: string, match: (r: ?string) => boolean, labelKey: string}>}\n */\nconst ROLE_BUCKETS = [\n\t{ key: 'applicant', match: (r) => /^(applicant|aanvrager|requestor)$/i.test(r ?? ''), labelKey: 'Applicants' },\n\t{ key: 'handler', match: (r) => /^(handler|behandelaar|owner|assignee)$/i.test(r ?? ''), labelKey: 'Handlers' },\n\t{ key: 'advisor', match: (r) => /^(advisor|advisor|adviseur|reviewer)$/i.test(r ?? ''), labelKey: 'Advisors' },\n]\n\n/**\n * CnContactsTab — vCard contacts linked to an OR object, grouped by role.\n *\n * Basic usage\n * ```vue\n * <CnContactsTab\n * register=\"my-register\"\n * schema=\"case\"\n * object-id=\"abc-123\" />\n * ```\n */\nexport default {\n\tname: 'CnContactsTab',\n\n\tcomponents: {\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcEmptyContent,\n\t\tNcLoadingIcon,\n\t\tAccountPlus,\n\t\tAccountMultipleOutline,\n\t\tAlertCircleOutline,\n\t\tCalendarRange,\n\t\tClose,\n\t\tEmail,\n\t\tLinkVariant,\n\t\tOfficeBuildingOutline,\n\t\tPhone,\n\t\tCnStatusBadge,\n\t\tCnContactPicker,\n\t\tCnContactCreate,\n\t\tCnWidgetHeaderActionButtons,\n\t},\n\n\tinject: {\n\t\t/** The look the app is drawn in (`board` or `nextcloud`), from CnAppRoot or the page. */\n\t\tcnLook: { default: 'nextcloud' },\n\t\t/** The widget's manifest `bodyActions`, drawn after this tab's own buttons. */\n\t\tcnWidgetBodyActions: { from: WIDGET_BODY_ACTIONS, default: null },\n\t},\n\n\tprops: {\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL for OpenRegister. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\n\t\t// --- Pre-translated labels (consumer-overridable for i18n flexibility) ---\n\t\t/** Label for the button that links an existing contact. */\n\t\tlinkExistingLabel: { type: String, default: () => t('nextcloud-vue', 'Link contact') },\n\t\t/** Label for the button that creates a contact; empty takes the look's own (New contact, or Add new contact). */\n\t\taddNewLabel: { type: String, default: '' },\n\t\t/** Title of the empty state shown when no contact is linked. */\n\t\temptyTitleLabel: { type: String, default: () => t('nextcloud-vue', 'No contacts linked') },\n\t\t/** Body of the empty state shown when no contact is linked. */\n\t\temptyDescriptionLabel: { type: String, default: () => t('nextcloud-vue', 'Link a contact from your address book.') },\n\t\t/** Message shown when the contacts could not be loaded. */\n\t\terrorLabel: { type: String, default: () => t('nextcloud-vue', 'Could not load contacts') },\n\t\t/** Stand-in name for a contact whose name is missing. */\n\t\tunknownLabel: { type: String, default: () => t('nextcloud-vue', 'Unknown contact') },\n\t\t/** Label for the action that unlinks a contact from the object. */\n\t\tunlinkLabel: { type: String, default: () => t('nextcloud-vue', 'Unlink contact') },\n\t\t/** Heading above the contacts grouped as applicants. */\n\t\tapplicantsLabel: { type: String, default: () => t('nextcloud-vue', 'Applicants') },\n\t\t/** Heading above the contacts grouped as handlers. */\n\t\thandlersLabel: { type: String, default: () => t('nextcloud-vue', 'Handlers') },\n\t\t/** Heading above the contacts grouped as advisors. */\n\t\tadvisorsLabel: { type: String, default: () => t('nextcloud-vue', 'Advisors') },\n\t\t/** Heading above the contacts with no recognised role. */\n\t\totherLabel: { type: String, default: () => t('nextcloud-vue', 'Other') },\n\t\t/** Badge on a link whose validity window has passed or not yet begun. */\n\t\tinactiveLabel: { type: String, default: () => t('nextcloud-vue', 'Not active') },\n\t\t/** Prefix for a validity window with an open end. */\n\t\tfromLabel: { type: String, default: () => t('nextcloud-vue', 'From') },\n\t\t/** Prefix for a validity window with an open start. */\n\t\tuntilLabel: { type: String, default: () => t('nextcloud-vue', 'Until') },\n\t\t/** Role badge shown on an applicant. */\n\t\tapplicantRoleLabel: { type: String, default: () => t('nextcloud-vue', 'Applicant') },\n\t\t/** Role badge shown on a handler. */\n\t\thandlerRoleLabel: { type: String, default: () => t('nextcloud-vue', 'Handler') },\n\t\t/** Role badge shown on an advisor. */\n\t\tadvisorRoleLabel: { type: String, default: () => t('nextcloud-vue', 'Advisor') },\n\t},\n\n\temits: ['open-reverse-lookup'],\n\n\tdata() {\n\t\treturn {\n\t\t\t// The server's grouping and role vocabulary, when it sends them.\n\t\t\tserverGroups: null,\n\t\t\troleVocabulary: [],\n\t\t\tcontacts: [],\n\t\t\tloading: false,\n\t\t\terror: null,\n\t\t\tshowLinkDialog: false,\n\t\t\tshowCreateDialog: false,\n\t\t\tcreateLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The two header buttons in the order and weight of the look: the\n\t\t * board look draws Link contact then New contact, both outlined; the\n\t\t * Nextcloud look keeps Add new contact first and Link as the primary.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, variant: string}>} The buttons.\n\t\t */\n\t\theaderButtons() {\n\t\t\tconst board = normalizeLook(this.cnLook) === 'board'\n\t\t\tconst newLabel = this.addNewLabel !== ''\n\t\t\t\t? this.addNewLabel\n\t\t\t\t: (board ? t('nextcloud-vue', 'New contact') : t('nextcloud-vue', 'Add new contact'))\n\t\t\tconst link = { key: 'link', label: this.linkExistingLabel, variant: board ? 'secondary' : 'primary' }\n\t\t\tconst create = { key: 'new', label: newLabel, variant: 'secondary' }\n\t\t\treturn board ? [link, create] : [create, link]\n\t\t},\n\n\t\t/**\n\t\t * The widget's body actions, unwrapped.\n\t\t *\n\t\t * @return {Array<object>} The resolved buttons, or none.\n\t\t */\n\t\tbodyActions() {\n\t\t\tconst provided = this.cnWidgetBodyActions\n\t\t\tconst list = provided && typeof provided === 'object' && 'value' in provided ? provided.value : provided\n\t\t\treturn Array.isArray(list) ? list : []\n\t\t},\n\n\t\t/**\n\t\t * Defensive view onto `this.contacts` — always an array.\n\t\t *\n\t\t * Guards against the well-known \"this.contacts is not iterable\"\n\t\t * TypeError observed in Phase A / D-1 when the provider returns\n\t\t * an object-shaped payload (or `null`) and the iteration site\n\t\t * blows up before the watch has even fired.\n\t\t *\n\t\t * @return {Array}\n\t\t */\n\t\tcontactsArray() {\n\t\t\treturn Array.isArray(this.contacts) ? this.contacts : []\n\t\t},\n\n\t\t/**\n\t\t * Group contacts by normalised role bucket.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, items: Array}>}\n\t\t */\n\t\tgroupedContacts() {\n\t\t\tif (this.serverGroups !== null) {\n\t\t\t\treturn this.groupsFromServer()\n\t\t\t}\n\t\t\treturn this.groupsFromBuckets\n\t\t},\n\n\t\t/**\n\t\t * The role vocabulary as CnContactPicker options, or nothing when\n\t\t * the schema declares none (the picker keeps its own defaults).\n\t\t *\n\t\t * @return {object} Props to bind onto the picker.\n\t\t */\n\t\tpickerRoleOptions() {\n\t\t\tif (this.roleVocabulary.length === 0) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\troleOptions: this.roleVocabulary.map((role) => ({ label: role.label || role.key, value: role.key })),\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Group by the tab's own role buckets, for a server that does not group.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, items: Array}>}\n\t\t */\n\t\tgroupsFromBuckets() {\n\t\t\tconst labelMap = {\n\t\t\t\tapplicant: this.applicantsLabel,\n\t\t\t\thandler: this.handlersLabel,\n\t\t\t\tadvisor: this.advisorsLabel,\n\t\t\t\tother: this.otherLabel,\n\t\t\t}\n\t\t\tconst buckets = { applicant: [], handler: [], advisor: [], other: [] }\n\t\t\tfor (const c of this.contactsArray) {\n\t\t\t\tconst matched = ROLE_BUCKETS.find((b) => b.match(c.role))\n\t\t\t\tif (matched) {\n\t\t\t\t\tbuckets[matched.key].push(c)\n\t\t\t\t} else {\n\t\t\t\t\tbuckets.other.push(c)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst out = []\n\t\t\tfor (const key of ['applicant', 'handler', 'advisor', 'other']) {\n\t\t\t\tif (buckets[key].length > 0) {\n\t\t\t\t\tout.push({ key, label: labelMap[key], items: buckets[key] })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/contacts`\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchContacts()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Initials from displayName (\"Jan de Vries\" → \"JV\"); falls back to `?`.\n\t\t *\n\t\t * @param {object} contact ContactLink JSON.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tinitialsFor(contact) {\n\t\t\tconst name = (contact?.displayName || '').trim()\n\t\t\tif (name === '') {\n\t\t\t\treturn '?'\n\t\t\t}\n\t\t\tconst parts = name.split(/\\s+/).filter(Boolean)\n\t\t\tif (parts.length === 1) {\n\t\t\t\treturn parts[0].charAt(0).toUpperCase()\n\t\t\t}\n\t\t\treturn (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase()\n\t\t},\n\n\t\t/**\n\t\t * Stable seed for NcAvatar's deterministic colour + initials.\n\t\t * Prefer the contact UID, then email, then display name so the\n\t\t * generated avatar colour stays consistent for a given contact.\n\t\t *\n\t\t * @param {object} contact ContactLink JSON.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tavatarSeed(contact) {\n\t\t\treturn contact.contactUid || contact.email || contact.displayName || '?'\n\t\t},\n\n\t\t/**\n\t\t * Resolve the normalised role bucket for a contact.\n\t\t *\n\t\t * @param {object} contact ContactLink JSON.\n\t\t *\n\t\t * @return {string} one of applicant|handler|advisor|other\n\t\t */\n\t\troleKey(contact) {\n\t\t\tconst matched = ROLE_BUCKETS.find((b) => b.match(contact.role))\n\t\t\treturn matched ? matched.key : 'other'\n\t\t},\n\n\t\t/**\n\t\t * Human-readable role chip label — the translated bucket name\n\t\t * when the role maps to a known bucket, else the raw vCard role.\n\t\t *\n\t\t * @param {object} contact ContactLink JSON.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\troleLabel(contact) {\n\t\t\tconst map = {\n\t\t\t\tapplicant: this.applicantRoleLabel,\n\t\t\t\thandler: this.handlerRoleLabel,\n\t\t\t\tadvisor: this.advisorRoleLabel,\n\t\t\t}\n\t\t\treturn map[this.roleKey(contact)] || contact.role\n\t\t},\n\n\t\t/**\n\t\t * CnStatusBadge variant per role bucket so each role reads as a\n\t\t * distinct accent (handlers = primary, advisors = info, etc.).\n\t\t *\n\t\t * @param {object} contact ContactLink JSON.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\troleVariant(contact) {\n\t\t\tconst map = {\n\t\t\t\tapplicant: 'info',\n\t\t\t\thandler: 'primary',\n\t\t\t\tadvisor: 'success',\n\t\t\t}\n\t\t\treturn map[this.roleKey(contact)] || 'default'\n\t\t},\n\n\t\t/**\n\t\t * Open the reverse-lookup flyout for a contact (AD-3). Emits an\n\t\t * event for the consuming app to handle (since the flyout's\n\t\t * implementation depends on the host router).\n\t\t *\n\t\t * @param {object} contact ContactLink JSON.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\topenReverseLookup(contact) {\n\t\t\tthis.$emit('open-reverse-lookup', {\n\t\t\t\tcontactUid: contact.contactUid,\n\t\t\t\tdisplayName: contact.displayName,\n\t\t\t})\n\t\t},\n\n\t\tasync fetchContacts() {\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 = null\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\tthis.error = `${response.status} ${response.statusText}`\n\t\t\t\t\tthis.contacts = []\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst data = await response.json()\n\t\t\t\t// Canonical wrapper-key cascade (per ADR-022): provider\n\t\t\t\t// may return `{results: [...]}` (paginated), `{items:\n\t\t\t\t// [...]}` (some leaves), or a bare array. Anything else\n\t\t\t\t// falls back to `[]` rather than letting a non-iterable\n\t\t\t\t// object reach `groupedContacts` (Phase A / D-1 bug).\n\t\t\t\tthis.contacts = this.unwrapList(data)\n\t\t\t\t// people-on-objects: the server groups by role and names the\n\t\t\t\t// schema's vocabulary. Both optional, so a server that sends\n\t\t\t\t// neither keeps the tab's own buckets.\n\t\t\t\tthis.serverGroups = (data && typeof data === 'object' && data.byRole) ? data.byRole : null\n\t\t\t\tthis.roleVocabulary = (data && Array.isArray(data.roles)) ? data.roles : []\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\tconsole.error('CnContactsTab: Failed to fetch contacts', err)\n\t\t\t\tthis.error = String(err?.message || err)\n\t\t\t\tthis.contacts = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Normalise a list-shaped response body to an array. Accepts\n\t\t * `{results:[...]}`, `{items:[...]}`, or a bare array; any\n\t\t * other shape (object, null, undefined) becomes `[]`.\n\t\t *\n\t\t * @param {object|Array<object>|null} data parsed JSON response body\n\t\t *\n\t\t * @return {Array}\n\t\t */\n\t\tunwrapList(data) {\n\t\t\tif (Array.isArray(data)) {\n\t\t\t\treturn data\n\t\t\t}\n\t\t\tif (data && typeof data === 'object') {\n\t\t\t\tif (Array.isArray(data.results)) {\n\t\t\t\t\treturn data.results\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(data.items)) {\n\t\t\t\t\treturn data.items\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn []\n\t\t},\n\n\t\t/**\n\t\t * The server's own grouping, labelled by the schema's vocabulary.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, items: Array}>}\n\t\t */\n\t\tgroupsFromServer() {\n\t\t\tconst labels = new Map(this.roleVocabulary.map((role) => [role.key, role.label || role.key]))\n\t\t\treturn Object.entries(this.serverGroups)\n\t\t\t\t.filter(([, items]) => Array.isArray(items) && items.length > 0)\n\t\t\t\t.map(([key, items]) => ({\n\t\t\t\t\tkey,\n\t\t\t\t\tlabel: labels.get(key) || (key === 'other' ? this.otherLabel : key),\n\t\t\t\t\titems,\n\t\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * The validity window of a link as a line, or '' when it is open.\n\t\t *\n\t\t * @param {object} contact The link.\n\t\t *\n\t\t * @return {string} The window.\n\t\t */\n\t\tperiodOf(contact) {\n\t\t\tconst from = contact?.validFrom || ''\n\t\t\tconst until = contact?.validUntil || ''\n\t\t\tif (from === '' && until === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (until === '') {\n\t\t\t\treturn `${this.fromLabel} ${from}`\n\t\t\t}\n\t\t\tif (from === '') {\n\t\t\t\treturn `${this.untilLabel} ${until}`\n\t\t\t}\n\t\t\treturn `${from} – ${until}`\n\t\t},\n\n\t\tasync unlink(contact) {\n\t\t\tif (!contact?.contactUid) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// people-on-objects: a person can hold several roles on one\n\t\t\t\t// object, so removing a row removes that role, not the person.\n\t\t\t\tconst role = contact.role ? `?role=${encodeURIComponent(contact.role)}` : ''\n\t\t\t\tconst url = prefixUrl(`${this.baseUrl}/${encodeURIComponent(contact.contactUid)}${role}`)\n\t\t\t\tawait fetch(url, { method: 'DELETE', headers: buildHeaders() })\n\t\t\t\tthis.contacts = this.contactsArray.filter((c) => c.id !== contact.id)\n\t\t\t\tthis.serverGroups = null\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\tconsole.error('CnContactsTab: Failed to unlink contact', err)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Handle a `link` event from CnContactPicker — POST the picked\n\t\t * contact to the bare `/contacts` endpoint, then refresh.\n\t\t *\n\t\t * @param {object} payload picker payload\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onPickerLink(payload) {\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t...buildHeaders(),\n\t\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t},\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\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\t\tconsole.error('CnContactsTab: Failed to link picked contact', response.status)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.showLinkDialog = false\n\t\t\t\tawait this.fetchContacts()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\tconsole.error('CnContactsTab: Failed to link picked contact', err)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Handle a `create` event from CnContactCreate — POST the form\n\t\t * payload to the dedicated `/contacts/new` endpoint.\n\t\t *\n\t\t * @param {object} payload form payload\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onCreateSubmit(payload) {\n\t\t\tthis.createLoading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl}/new`), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t...buildHeaders(),\n\t\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t},\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\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\t\tconsole.error('CnContactsTab: Failed to create+link contact', response.status)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.showCreateDialog = false\n\t\t\t\tawait this.fetchContacts()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\tconsole.error('CnContactsTab: Failed to create+link contact', err)\n\t\t\t} finally {\n\t\t\t\tthis.createLoading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-contacts-tab {\n\tpadding: 12px;\n\toverflow-x: hidden;\n}\n\n.cn-contacts-tab__header {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tgap: 8px;\n\tmargin-bottom: 12px;\n\tflex-wrap: wrap;\n}\n\n.cn-contacts-tab__group {\n\tmargin-bottom: 16px;\n}\n\n.cn-contacts-tab__group-title {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 13px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0 0 4px;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.5px;\n}\n\n.cn-contacts-tab__group-count {\n\tfont-weight: 600;\n\tfont-size: 11px;\n\tmin-width: 18px;\n\tpadding: 0 6px;\n\theight: 18px;\n\tline-height: 18px;\n\ttext-align: center;\n\tborder-radius: 9px;\n\tbackground-color: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-contacts-tab__list {\n\tlist-style: none;\n\tpadding: 0;\n\tmargin: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-contacts-tab__item {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 12px;\n\tpadding: 10px 8px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tcursor: pointer;\n\ttransition: background-color 0.1s ease-in-out;\n}\n\n.cn-contacts-tab__item:hover,\n.cn-contacts-tab__item:focus-within {\n\tbackground-color: var(--color-background-hover);\n}\n\n.cn-contacts-tab__avatar {\n\tflex-shrink: 0;\n\tdisplay: flex;\n\talign-items: center;\n}\n\n.cn-contacts-tab__details {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tpadding-top: 2px;\n}\n\n.cn-contacts-tab__name-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmin-width: 0;\n}\n\n.cn-contacts-tab__name {\n\tfont-size: 14px;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-contacts-tab__sub {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 5px;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tmin-width: 0;\n}\n\n.cn-contacts-tab__sub-icon {\n\tflex-shrink: 0;\n\topacity: 0.7;\n}\n\n.cn-contacts-tab__sub-text {\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-contacts-tab__unlink {\n\tbackground: none;\n\tborder: none;\n\tpadding: 4px;\n\tcursor: pointer;\n\tcolor: var(--color-text-maxcontrast);\n\tborder-radius: 50%;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tflex-shrink: 0;\n\talign-self: center;\n}\n\n.cn-contacts-tab__unlink:hover,\n.cn-contacts-tab__unlink:focus {\n\tbackground-color: var(--color-background-dark);\n\tcolor: var(--color-error);\n}\n</style>\n"],"names":["AccountMultipleOutline","OfficeBuildingOutline","CnStatusBadge","CnContactPicker","CnContactCreate","CnWidgetHeaderActionButtons","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnContactsTab.vue2.js","sources":["../../../../../src/integrations/builtin/contacts/CnContactsTab.vue"],"sourcesContent":["<!--\n - SPDX-License-Identifier: EUPL-1.2\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n -\n - CnContactsTab — bespoke sidebar tab for the `contacts` integration leaf.\n -\n - Renders the people linked to an OR object grouped by role. Since\n - people-on-objects a person is a Nextcloud user or a vCard contact, and\n - the grouping is the server's when it sends one: the listing carries\n - `byRole` and the schema's `roles`, and the tab falls back to its own\n - buckets (Applicants / Handlers / Advisors / Other, AD-1 of\n - `openspec/changes/integration-contacts/design.md`) for a server that\n - does not. Each card shows avatar, display name, email and role, and a\n - link whose validity has passed reads as inactive.\n -\n - Clicking a contact opens the reverse-lookup flyout (AD-3) listing every\n - OR object linked to that vCard.\n -\n - Tier-2 (this revision): the tab now hosts two header actions — \"Link\n - existing contact\" (opens `CnContactPicker`) and \"Add new contact\"\n - (opens `CnContactCreate`). Both dialogs are loaded lazily via\n - dynamic import to keep the bundle below the integration-tab\n - threshold, and both delegate the actual API call back to this tab\n - so the loading + refresh handshake stays here.\n -\n - Backed by:\n - GET /api/objects/{register}/{schema}/{id}/contacts\n - POST /api/objects/{register}/{schema}/{id}/contacts — link existing\n - POST /api/objects/{register}/{schema}/{id}/contacts/new — create + link\n - DELETE /api/objects/{register}/{schema}/{id}/contacts/{uid} — unlink\n -\n - The payload shape mirrors `ContactLink::jsonSerialize()` —\n - `{id, contactUid, displayName, email, phone, org, avatarUrl, role,\n - addressbookId, contactUri, schemaId, metadata, linkedAt}`.\n -\n - @spec openspec/changes/integration-contacts-tier2/specs/integrations/contacts/spec.md\n -->\n<template>\n\t<div class=\"cn-contacts-tab\">\n\t\t<!-- Header actions: link existing / create new. The board look draws\n\t\t Link contact first and both outlined (DqZaakBetrokkenen); the\n\t\t Nextcloud look keeps Add new contact first and Link as the primary. -->\n\t\t<div class=\"cn-contacts-tab__header\">\n\t\t\t<NcButton\n\t\t\t\tv-for=\"button in headerButtons\"\n\t\t\t\t:key=\"button.key\"\n\t\t\t\t:variant=\"button.variant\"\n\t\t\t\t:aria-label=\"button.label\"\n\t\t\t\t@click=\"button.key === 'link' ? (showLinkDialog = true) : (showCreateDialog = true)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant v-if=\"button.key === 'link'\" :size=\"20\" />\n\t\t\t\t\t<AccountPlus v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ button.label }}\n\t\t\t</NcButton>\n\t\t\t<CnWidgetHeaderActionButtons v-if=\"bodyActions.length > 0\" :actions=\"bodyActions\" />\n\t\t</div>\n\n\t\t<!-- Loading -->\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- Error -->\n\t\t<NcEmptyContent\n\t\t\tv-else-if=\"error\"\n\t\t\t:name=\"errorLabel\"\n\t\t\t:description=\"error\">\n\t\t\t<template #icon>\n\t\t\t\t<AlertCircleOutline :size=\"48\" />\n\t\t\t</template>\n\t\t</NcEmptyContent>\n\n\t\t<!-- Empty -->\n\t\t<NcEmptyContent\n\t\t\tv-else-if=\"contactsArray.length === 0\"\n\t\t\t:name=\"emptyTitleLabel\"\n\t\t\t:description=\"emptyDescriptionLabel\">\n\t\t\t<template #icon>\n\t\t\t\t<AccountMultipleOutline :size=\"48\" />\n\t\t\t</template>\n\t\t</NcEmptyContent>\n\n\t\t<!-- Grouped list -->\n\t\t<div v-else class=\"cn-contacts-tab__groups\">\n\t\t\t<section\n\t\t\t\tv-for=\"group in groupedContacts\"\n\t\t\t\t:key=\"group.key\"\n\t\t\t\tclass=\"cn-contacts-tab__group\">\n\t\t\t\t<h3 class=\"cn-contacts-tab__group-title\">\n\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t<span v-if=\"!isBoard\" class=\"cn-contacts-tab__group-count\">{{ group.items.length }}</span>\n\t\t\t\t</h3>\n\t\t\t\t<ul class=\"cn-contacts-tab__list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"contact in group.items\"\n\t\t\t\t\t\t:key=\"contact.id\"\n\t\t\t\t\t\tclass=\"cn-contacts-tab__item\"\n\t\t\t\t\t\t@click=\"openReverseLookup(contact)\">\n\t\t\t\t\t\t<div class=\"cn-contacts-tab__avatar\">\n\t\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\t\t:size=\"isBoard ? 32 : 40\"\n\t\t\t\t\t\t\t\t:displayName=\"contact.displayName || unknownLabel\"\n\t\t\t\t\t\t\t\t:user=\"avatarSeed(contact)\"\n\t\t\t\t\t\t\t\t:url=\"contact.avatarUrl || undefined\"\n\t\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t\t\t:disableTooltip=\"true\"\n\t\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"cn-contacts-tab__details\">\n\t\t\t\t\t\t\t<div class=\"cn-contacts-tab__name-row\">\n\t\t\t\t\t\t\t\t<span class=\"cn-contacts-tab__name\">\n\t\t\t\t\t\t\t\t\t{{ contact.displayName || unknownLabel }}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\tv-if=\"contact.role && !isBoard\"\n\t\t\t\t\t\t\t\t\t:label=\"roleLabel(contact)\"\n\t\t\t\t\t\t\t\t\t:variant=\"roleVariant(contact)\"\n\t\t\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\tv-if=\"contact.active === false\"\n\t\t\t\t\t\t\t\t\t:label=\"inactiveLabel\"\n\t\t\t\t\t\t\t\t\tvariant=\"default\"\n\t\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-contacts-tab-inactive\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<!-- The board look sets the details on one line (DqZaakBetrokkenen). -->\n\t\t\t\t\t\t\t<span v-if=\"isBoard && boardMetaOf(contact)\"\n\t\t\t\t\t\t\t\tclass=\"cn-contacts-tab__meta\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-contacts-tab-meta\">{{ boardMetaOf(contact) }}</span>\n\t\t\t\t\t\t\t<template v-if=\"!isBoard\">\n\t\t\t\t\t\t\t\t<div v-if=\"contact.email\" class=\"cn-contacts-tab__sub\">\n\t\t\t\t\t\t\t\t\t<Email :size=\"14\" class=\"cn-contacts-tab__sub-icon\" />\n\t\t\t\t\t\t\t\t\t<span class=\"cn-contacts-tab__sub-text\">{{ contact.email }}</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div v-if=\"contact.phone\" class=\"cn-contacts-tab__sub\">\n\t\t\t\t\t\t\t\t\t<Phone :size=\"14\" class=\"cn-contacts-tab__sub-icon\" />\n\t\t\t\t\t\t\t\t\t<span class=\"cn-contacts-tab__sub-text\">{{ contact.phone }}</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div v-if=\"contact.org\" class=\"cn-contacts-tab__sub\">\n\t\t\t\t\t\t\t\t\t<OfficeBuildingOutline :size=\"14\" class=\"cn-contacts-tab__sub-icon\" />\n\t\t\t\t\t\t\t\t\t<span class=\"cn-contacts-tab__sub-text\">{{ contact.org }}</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tv-if=\"periodOf(contact)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-contacts-tab__sub\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-contacts-tab-period\">\n\t\t\t\t\t\t\t\t\t<CalendarRange :size=\"14\" class=\"cn-contacts-tab__sub-icon\" />\n\t\t\t\t\t\t\t\t\t<span class=\"cn-contacts-tab__sub-text\">{{ periodOf(contact) }}</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div v-if=\"contact.note\" class=\"cn-contacts-tab__sub\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-contacts-tab__sub-text\">{{ contact.note }}</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<template v-if=\"isBoard\">\n\t\t\t\t\t\t\t<span v-if=\"boardPeriodOf(contact)\"\n\t\t\t\t\t\t\t\tclass=\"cn-contacts-tab__period\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-contacts-tab-board-period\">{{ boardPeriodOf(contact) }}</span>\n\t\t\t\t\t\t\t<NcActions class=\"cn-contacts-tab__menu\"\n\t\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\t\t:aria-label=\"moreActionsLabel(contact)\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-contacts-tab-menu\"\n\t\t\t\t\t\t\t\t@click.stop>\n\t\t\t\t\t\t\t\t<NcActionButton :closeAfterClick=\"true\" @click=\"unlink(contact)\">\n\t\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t\t<Close :size=\"20\" />\n\t\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t\t{{ unlinkLabel }}\n\t\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t</NcActions>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tv-else\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclass=\"cn-contacts-tab__unlink\"\n\t\t\t\t\t\t\t:aria-label=\"unlinkLabel\"\n\t\t\t\t\t\t\t:title=\"unlinkLabel\"\n\t\t\t\t\t\t\t@click.stop=\"unlink(contact)\">\n\t\t\t\t\t\t\t<Close :size=\"18\" />\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\t\t</div>\n\n\t\t<!-- Link existing contact dialog -->\n\t\t<CnContactPicker\n\t\t\tv-if=\"showLinkDialog\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\tv-bind=\"pickerRoleOptions\"\n\t\t\t@link=\"onPickerLink\"\n\t\t\t@close=\"showLinkDialog = false\" />\n\n\t\t<!-- Create new contact dialog -->\n\t\t<CnContactCreate\n\t\t\tv-if=\"showCreateDialog\"\n\t\t\t:loading=\"createLoading\"\n\t\t\t@create=\"onCreateSubmit\"\n\t\t\t@close=\"showCreateDialog = false\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActions, NcAvatar, NcButton, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountMultipleOutline from 'vue-material-design-icons/AccountMultipleOutline.vue'\nimport AccountPlus from 'vue-material-design-icons/AccountPlus.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport CalendarRange from 'vue-material-design-icons/CalendarRange.vue'\nimport Close from 'vue-material-design-icons/Close.vue'\nimport Email from 'vue-material-design-icons/Email.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport OfficeBuildingOutline from 'vue-material-design-icons/OfficeBuildingOutline.vue'\nimport Phone from 'vue-material-design-icons/Phone.vue'\nimport CnContactCreate from '../../../components/CnContactCreate/CnContactCreate.vue'\nimport CnContactPicker from '../../../components/CnContactPicker/CnContactPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport CnWidgetHeaderActionButtons from '../../../components/CnWidgetWrapper/CnWidgetHeaderActionButtons.vue'\nimport { normalizeLook } from '../../../composables/useLook.js'\nimport { formatBoardDate } from '../../../utils/boardDate.js'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { WIDGET_BODY_ACTIONS } from '../../../utils/widgetHeaderActions.js'\n\n/**\n * Normalised role keys for grouping. Free-text vCard roles are\n * normalised by lowercasing + trimming; unknown roles fall into `other`.\n *\n * @type {Array<{key: string, match: (r: ?string) => boolean, labelKey: string}>}\n */\nconst ROLE_BUCKETS = [\n\t{ key: 'applicant', match: (r) => /^(applicant|aanvrager|requestor)$/i.test(r ?? ''), labelKey: 'Applicants' },\n\t{ key: 'handler', match: (r) => /^(handler|behandelaar|owner|assignee)$/i.test(r ?? ''), labelKey: 'Handlers' },\n\t{ key: 'advisor', match: (r) => /^(advisor|advisor|adviseur|reviewer)$/i.test(r ?? ''), labelKey: 'Advisors' },\n]\n\n/**\n * CnContactsTab — vCard contacts linked to an OR object, grouped by role.\n *\n * Basic usage\n * ```vue\n * <CnContactsTab\n * register=\"my-register\"\n * schema=\"case\"\n * object-id=\"abc-123\" />\n * ```\n */\nexport default {\n\tname: 'CnContactsTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcActions,\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcEmptyContent,\n\t\tNcLoadingIcon,\n\t\tAccountPlus,\n\t\tAccountMultipleOutline,\n\t\tAlertCircleOutline,\n\t\tCalendarRange,\n\t\tClose,\n\t\tEmail,\n\t\tLinkVariant,\n\t\tOfficeBuildingOutline,\n\t\tPhone,\n\t\tCnStatusBadge,\n\t\tCnContactPicker,\n\t\tCnContactCreate,\n\t\tCnWidgetHeaderActionButtons,\n\t},\n\n\tinject: {\n\t\t/** The look the app is drawn in (`board` or `nextcloud`), from CnAppRoot or the page. */\n\t\tcnLook: { default: 'nextcloud' },\n\t\t/** The widget's manifest `bodyActions`, drawn after this tab's own buttons. */\n\t\tcnWidgetBodyActions: { from: WIDGET_BODY_ACTIONS, default: null },\n\t},\n\n\tprops: {\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL for OpenRegister. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\n\t\t// --- Pre-translated labels (consumer-overridable for i18n flexibility) ---\n\t\t/** Label for the button that links an existing contact. */\n\t\tlinkExistingLabel: { type: String, default: () => t('nextcloud-vue', 'Link contact') },\n\t\t/** Label for the button that creates a contact; empty takes the look's own (New contact, or Add new contact). */\n\t\taddNewLabel: { type: String, default: '' },\n\t\t/** Title of the empty state shown when no contact is linked. */\n\t\temptyTitleLabel: { type: String, default: () => t('nextcloud-vue', 'No contacts linked') },\n\t\t/** Body of the empty state shown when no contact is linked. */\n\t\temptyDescriptionLabel: { type: String, default: () => t('nextcloud-vue', 'Link a contact from your address book.') },\n\t\t/** Message shown when the contacts could not be loaded. */\n\t\terrorLabel: { type: String, default: () => t('nextcloud-vue', 'Could not load contacts') },\n\t\t/** Stand-in name for a contact whose name is missing. */\n\t\tunknownLabel: { type: String, default: () => t('nextcloud-vue', 'Unknown contact') },\n\t\t/** Label for the action that unlinks a contact from the object. */\n\t\tunlinkLabel: { type: String, default: () => t('nextcloud-vue', 'Unlink contact') },\n\t\t/** Heading above the contacts grouped as applicants. */\n\t\tapplicantsLabel: { type: String, default: () => t('nextcloud-vue', 'Applicants') },\n\t\t/** Heading above the contacts grouped as handlers. */\n\t\thandlersLabel: { type: String, default: () => t('nextcloud-vue', 'Handlers') },\n\t\t/** Heading above the contacts grouped as advisors. */\n\t\tadvisorsLabel: { type: String, default: () => t('nextcloud-vue', 'Advisors') },\n\t\t/** Heading above the contacts with no recognised role. */\n\t\totherLabel: { type: String, default: () => t('nextcloud-vue', 'Other') },\n\t\t/** Badge on a link whose validity window has passed or not yet begun. */\n\t\tinactiveLabel: { type: String, default: () => t('nextcloud-vue', 'Not active') },\n\t\t/** Prefix for a validity window with an open end. */\n\t\tfromLabel: { type: String, default: () => t('nextcloud-vue', 'From') },\n\t\t/** Prefix for a validity window with an open start. */\n\t\tuntilLabel: { type: String, default: () => t('nextcloud-vue', 'Until') },\n\t\t/** Role badge shown on an applicant. */\n\t\tapplicantRoleLabel: { type: String, default: () => t('nextcloud-vue', 'Applicant') },\n\t\t/** Role badge shown on a handler. */\n\t\thandlerRoleLabel: { type: String, default: () => t('nextcloud-vue', 'Handler') },\n\t\t/** Role badge shown on an advisor. */\n\t\tadvisorRoleLabel: { type: String, default: () => t('nextcloud-vue', 'Advisor') },\n\t},\n\n\temits: ['open-reverse-lookup'],\n\n\tdata() {\n\t\treturn {\n\t\t\t// The server's grouping and role vocabulary, when it sends them.\n\t\t\tserverGroups: null,\n\t\t\troleVocabulary: [],\n\t\t\tcontacts: [],\n\t\t\tloading: false,\n\t\t\terror: null,\n\t\t\tshowLinkDialog: false,\n\t\t\tshowCreateDialog: false,\n\t\t\tcreateLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {boolean} The board look draws the rows as DqZaakBetrokkenen does. */\n\t\tisBoard() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/**\n\t\t * The two header buttons in the order and weight of the look: the\n\t\t * board look draws Link contact then New contact, both outlined; the\n\t\t * Nextcloud look keeps Add new contact first and Link as the primary.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, variant: string}>} The buttons.\n\t\t */\n\t\theaderButtons() {\n\t\t\tconst board = normalizeLook(this.cnLook) === 'board'\n\t\t\tconst newLabel = this.addNewLabel !== ''\n\t\t\t\t? this.addNewLabel\n\t\t\t\t: (board ? t('nextcloud-vue', 'New contact') : t('nextcloud-vue', 'Add new contact'))\n\t\t\tconst link = { key: 'link', label: this.linkExistingLabel, variant: board ? 'secondary' : 'primary' }\n\t\t\tconst create = { key: 'new', label: newLabel, variant: 'secondary' }\n\t\t\treturn board ? [link, create] : [create, link]\n\t\t},\n\n\t\t/**\n\t\t * The widget's body actions, unwrapped.\n\t\t *\n\t\t * @return {Array<object>} The resolved buttons, or none.\n\t\t */\n\t\tbodyActions() {\n\t\t\tconst provided = this.cnWidgetBodyActions\n\t\t\tconst list = provided && typeof provided === 'object' && 'value' in provided ? provided.value : provided\n\t\t\treturn Array.isArray(list) ? list : []\n\t\t},\n\n\t\t/**\n\t\t * Defensive view onto `this.contacts` — always an array.\n\t\t *\n\t\t * Guards against the well-known \"this.contacts is not iterable\"\n\t\t * TypeError observed in Phase A / D-1 when the provider returns\n\t\t * an object-shaped payload (or `null`) and the iteration site\n\t\t * blows up before the watch has even fired.\n\t\t *\n\t\t * @return {Array}\n\t\t */\n\t\tcontactsArray() {\n\t\t\treturn Array.isArray(this.contacts) ? this.contacts : []\n\t\t},\n\n\t\t/**\n\t\t * Group contacts by normalised role bucket.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, items: Array}>}\n\t\t */\n\t\tgroupedContacts() {\n\t\t\tif (this.serverGroups !== null) {\n\t\t\t\treturn this.groupsFromServer()\n\t\t\t}\n\t\t\treturn this.groupsFromBuckets\n\t\t},\n\n\t\t/**\n\t\t * The role vocabulary as CnContactPicker options, or nothing when\n\t\t * the schema declares none (the picker keeps its own defaults).\n\t\t *\n\t\t * @return {object} Props to bind onto the picker.\n\t\t */\n\t\tpickerRoleOptions() {\n\t\t\tif (this.roleVocabulary.length === 0) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\troleOptions: this.roleVocabulary.map((role) => ({ label: role.label || role.key, value: role.key })),\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Group by the tab's own role buckets, for a server that does not group.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, items: Array}>}\n\t\t */\n\t\tgroupsFromBuckets() {\n\t\t\tconst labelMap = {\n\t\t\t\tapplicant: this.applicantsLabel,\n\t\t\t\thandler: this.handlersLabel,\n\t\t\t\tadvisor: this.advisorsLabel,\n\t\t\t\tother: this.otherLabel,\n\t\t\t}\n\t\t\tconst buckets = { applicant: [], handler: [], advisor: [], other: [] }\n\t\t\tfor (const c of this.contactsArray) {\n\t\t\t\tconst matched = ROLE_BUCKETS.find((b) => b.match(c.role))\n\t\t\t\tif (matched) {\n\t\t\t\t\tbuckets[matched.key].push(c)\n\t\t\t\t} else {\n\t\t\t\t\tbuckets.other.push(c)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst out = []\n\t\t\tfor (const key of ['applicant', 'handler', 'advisor', 'other']) {\n\t\t\t\tif (buckets[key].length > 0) {\n\t\t\t\t\tout.push({ key, label: labelMap[key], items: buckets[key] })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/contacts`\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchContacts()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Initials from displayName (\"Jan de Vries\" → \"JV\"); falls back to `?`.\n\t\t *\n\t\t * @param {object} contact ContactLink JSON.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tinitialsFor(contact) {\n\t\t\tconst name = (contact?.displayName || '').trim()\n\t\t\tif (name === '') {\n\t\t\t\treturn '?'\n\t\t\t}\n\t\t\tconst parts = name.split(/\\s+/).filter(Boolean)\n\t\t\tif (parts.length === 1) {\n\t\t\t\treturn parts[0].charAt(0).toUpperCase()\n\t\t\t}\n\t\t\treturn (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase()\n\t\t},\n\n\t\t/**\n\t\t * Stable seed for NcAvatar's deterministic colour + initials.\n\t\t * Prefer the contact UID, then email, then display name so the\n\t\t * generated avatar colour stays consistent for a given contact.\n\t\t *\n\t\t * @param {object} contact ContactLink JSON.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tavatarSeed(contact) {\n\t\t\treturn contact.contactUid || contact.email || contact.displayName || '?'\n\t\t},\n\n\t\t/**\n\t\t * Resolve the normalised role bucket for a contact.\n\t\t *\n\t\t * @param {object} contact ContactLink JSON.\n\t\t *\n\t\t * @return {string} one of applicant|handler|advisor|other\n\t\t */\n\t\troleKey(contact) {\n\t\t\tconst matched = ROLE_BUCKETS.find((b) => b.match(contact.role))\n\t\t\treturn matched ? matched.key : 'other'\n\t\t},\n\n\t\t/**\n\t\t * Human-readable role chip label — the translated bucket name\n\t\t * when the role maps to a known bucket, else the raw vCard role.\n\t\t *\n\t\t * @param {object} contact ContactLink JSON.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\troleLabel(contact) {\n\t\t\tconst map = {\n\t\t\t\tapplicant: this.applicantRoleLabel,\n\t\t\t\thandler: this.handlerRoleLabel,\n\t\t\t\tadvisor: this.advisorRoleLabel,\n\t\t\t}\n\t\t\treturn map[this.roleKey(contact)] || contact.role\n\t\t},\n\n\t\t/**\n\t\t * CnStatusBadge variant per role bucket so each role reads as a\n\t\t * distinct accent (handlers = primary, advisors = info, etc.).\n\t\t *\n\t\t * @param {object} contact ContactLink JSON.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\troleVariant(contact) {\n\t\t\tconst map = {\n\t\t\t\tapplicant: 'info',\n\t\t\t\thandler: 'primary',\n\t\t\t\tadvisor: 'success',\n\t\t\t}\n\t\t\treturn map[this.roleKey(contact)] || 'default'\n\t\t},\n\n\t\t/**\n\t\t * Open the reverse-lookup flyout for a contact (AD-3). Emits an\n\t\t * event for the consuming app to handle (since the flyout's\n\t\t * implementation depends on the host router).\n\t\t *\n\t\t * @param {object} contact ContactLink JSON.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\topenReverseLookup(contact) {\n\t\t\tthis.$emit('open-reverse-lookup', {\n\t\t\t\tcontactUid: contact.contactUid,\n\t\t\t\tdisplayName: contact.displayName,\n\t\t\t})\n\t\t},\n\n\t\tasync fetchContacts() {\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 = null\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\tthis.error = `${response.status} ${response.statusText}`\n\t\t\t\t\tthis.contacts = []\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst data = await response.json()\n\t\t\t\t// Canonical wrapper-key cascade (per ADR-022): provider\n\t\t\t\t// may return `{results: [...]}` (paginated), `{items:\n\t\t\t\t// [...]}` (some leaves), or a bare array. Anything else\n\t\t\t\t// falls back to `[]` rather than letting a non-iterable\n\t\t\t\t// object reach `groupedContacts` (Phase A / D-1 bug).\n\t\t\t\tthis.contacts = this.unwrapList(data)\n\t\t\t\t// people-on-objects: the server groups by role and names the\n\t\t\t\t// schema's vocabulary. Both optional, so a server that sends\n\t\t\t\t// neither keeps the tab's own buckets.\n\t\t\t\tthis.serverGroups = (data && typeof data === 'object' && data.byRole) ? data.byRole : null\n\t\t\t\tthis.roleVocabulary = (data && Array.isArray(data.roles)) ? data.roles : []\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\tconsole.error('CnContactsTab: Failed to fetch contacts', err)\n\t\t\t\tthis.error = String(err?.message || err)\n\t\t\t\tthis.contacts = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Normalise a list-shaped response body to an array. Accepts\n\t\t * `{results:[...]}`, `{items:[...]}`, or a bare array; any\n\t\t * other shape (object, null, undefined) becomes `[]`.\n\t\t *\n\t\t * @param {object|Array<object>|null} data parsed JSON response body\n\t\t *\n\t\t * @return {Array}\n\t\t */\n\t\tunwrapList(data) {\n\t\t\tif (Array.isArray(data)) {\n\t\t\t\treturn data\n\t\t\t}\n\t\t\tif (data && typeof data === 'object') {\n\t\t\t\tif (Array.isArray(data.results)) {\n\t\t\t\t\treturn data.results\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(data.items)) {\n\t\t\t\t\treturn data.items\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn []\n\t\t},\n\n\t\t/**\n\t\t * The server's own grouping, labelled by the schema's vocabulary.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, items: Array}>}\n\t\t */\n\t\tgroupsFromServer() {\n\t\t\tconst labels = new Map(this.roleVocabulary.map((role) => [role.key, role.label || role.key]))\n\t\t\treturn Object.entries(this.serverGroups)\n\t\t\t\t.filter(([, items]) => Array.isArray(items) && items.length > 0)\n\t\t\t\t.map(([key, items]) => ({\n\t\t\t\t\tkey,\n\t\t\t\t\tlabel: labels.get(key) || (key === 'other' ? this.otherLabel : key),\n\t\t\t\t\titems,\n\t\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * The validity window of a link as a line, or '' when it is open.\n\t\t *\n\t\t * @param {object} contact The link.\n\t\t *\n\t\t * @return {string} The window.\n\t\t */\n\t\t/**\n\t\t * A person's details on one line for the board look: e-mail, phone,\n\t\t * organisation and note, separated by middots.\n\t\t *\n\t\t * @param {object} contact The contact link.\n\t\t * @return {string}\n\t\t */\n\t\tboardMetaOf(contact) {\n\t\t\treturn [contact?.email, contact?.phone, contact?.org, contact?.note].filter((part) => typeof part === 'string' && part !== '').join(' · ')\n\t\t},\n\n\t\t/**\n\t\t * The validity window in the board's short dates: \"Vanaf 3 okt\",\n\t\t * \"Tot 31 okt\" or \"3 okt – 31 okt\".\n\t\t *\n\t\t * @param {object} contact The contact link.\n\t\t * @return {string}\n\t\t */\n\t\tboardPeriodOf(contact) {\n\t\t\tconst from = formatBoardDate(contact?.validFrom || '')\n\t\t\tconst until = formatBoardDate(contact?.validUntil || '')\n\t\t\tif (from === '' && until === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (until === '') {\n\t\t\t\treturn t('nextcloud-vue', 'From {date}', { date: from })\n\t\t\t}\n\t\t\tif (from === '') {\n\t\t\t\treturn t('nextcloud-vue', 'Until {date}', { date: until })\n\t\t\t}\n\t\t\treturn `${from} – ${until}`\n\t\t},\n\n\t\t/**\n\t\t * The label of a person's actions menu.\n\t\t *\n\t\t * @param {object} contact The contact link.\n\t\t * @return {string}\n\t\t */\n\t\tmoreActionsLabel(contact) {\n\t\t\treturn t('nextcloud-vue', 'More actions for {name}', { name: contact?.displayName || this.unknownLabel })\n\t\t},\n\n\t\tperiodOf(contact) {\n\t\t\tconst from = contact?.validFrom || ''\n\t\t\tconst until = contact?.validUntil || ''\n\t\t\tif (from === '' && until === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (until === '') {\n\t\t\t\treturn `${this.fromLabel} ${from}`\n\t\t\t}\n\t\t\tif (from === '') {\n\t\t\t\treturn `${this.untilLabel} ${until}`\n\t\t\t}\n\t\t\treturn `${from} – ${until}`\n\t\t},\n\n\t\tasync unlink(contact) {\n\t\t\tif (!contact?.contactUid) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// people-on-objects: a person can hold several roles on one\n\t\t\t\t// object, so removing a row removes that role, not the person.\n\t\t\t\tconst role = contact.role ? `?role=${encodeURIComponent(contact.role)}` : ''\n\t\t\t\tconst url = prefixUrl(`${this.baseUrl}/${encodeURIComponent(contact.contactUid)}${role}`)\n\t\t\t\tawait fetch(url, { method: 'DELETE', headers: buildHeaders() })\n\t\t\t\tthis.contacts = this.contactsArray.filter((c) => c.id !== contact.id)\n\t\t\t\tthis.serverGroups = null\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\tconsole.error('CnContactsTab: Failed to unlink contact', err)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Handle a `link` event from CnContactPicker — POST the picked\n\t\t * contact to the bare `/contacts` endpoint, then refresh.\n\t\t *\n\t\t * @param {object} payload picker payload\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onPickerLink(payload) {\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t...buildHeaders(),\n\t\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t},\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\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\t\tconsole.error('CnContactsTab: Failed to link picked contact', response.status)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.showLinkDialog = false\n\t\t\t\tawait this.fetchContacts()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\tconsole.error('CnContactsTab: Failed to link picked contact', err)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Handle a `create` event from CnContactCreate — POST the form\n\t\t * payload to the dedicated `/contacts/new` endpoint.\n\t\t *\n\t\t * @param {object} payload form payload\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onCreateSubmit(payload) {\n\t\t\tthis.createLoading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl}/new`), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t...buildHeaders(),\n\t\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t},\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\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\t\tconsole.error('CnContactsTab: Failed to create+link contact', response.status)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.showCreateDialog = false\n\t\t\t\tawait this.fetchContacts()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on\n\t\t\t\tconsole.error('CnContactsTab: Failed to create+link contact', err)\n\t\t\t} finally {\n\t\t\t\tthis.createLoading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-contacts-tab {\n\tpadding: 12px;\n\toverflow-x: hidden;\n}\n\n.cn-contacts-tab__header {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tgap: 8px;\n\tmargin-bottom: 12px;\n\tflex-wrap: wrap;\n}\n\n.cn-contacts-tab__group {\n\tmargin-bottom: 16px;\n}\n\n.cn-contacts-tab__group-title {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 13px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0 0 4px;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.5px;\n}\n\n.cn-contacts-tab__group-count {\n\tfont-weight: 600;\n\tfont-size: 11px;\n\tmin-width: 18px;\n\tpadding: 0 6px;\n\theight: 18px;\n\tline-height: 18px;\n\ttext-align: center;\n\tborder-radius: 9px;\n\tbackground-color: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-contacts-tab__list {\n\tlist-style: none;\n\tpadding: 0;\n\tmargin: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-contacts-tab__item {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 12px;\n\tpadding: 10px 8px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tcursor: pointer;\n\ttransition: background-color 0.1s ease-in-out;\n}\n\n.cn-contacts-tab__item:hover,\n.cn-contacts-tab__item:focus-within {\n\tbackground-color: var(--color-background-hover);\n}\n\n.cn-contacts-tab__avatar {\n\tflex-shrink: 0;\n\tdisplay: flex;\n\talign-items: center;\n}\n\n.cn-contacts-tab__details {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tpadding-top: 2px;\n}\n\n.cn-contacts-tab__name-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmin-width: 0;\n}\n\n.cn-contacts-tab__name {\n\tfont-size: 14px;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-contacts-tab__sub {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 5px;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tmin-width: 0;\n}\n\n.cn-contacts-tab__sub-icon {\n\tflex-shrink: 0;\n\topacity: 0.7;\n}\n\n.cn-contacts-tab__sub-text {\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-contacts-tab__unlink {\n\tbackground: none;\n\tborder: none;\n\tpadding: 4px;\n\tcursor: pointer;\n\tcolor: var(--color-text-maxcontrast);\n\tborder-radius: 50%;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tflex-shrink: 0;\n\talign-self: center;\n}\n\n.cn-contacts-tab__unlink:hover,\n.cn-contacts-tab__unlink:focus {\n\tbackground-color: var(--color-background-dark);\n\tcolor: var(--color-error);\n}\n</style>\n"],"names":["AccountMultipleOutline","OfficeBuildingOutline","CnStatusBadge","CnContactPicker","CnContactCreate","CnWidgetHeaderActionButtons","t"],"mappings":"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"}
@@ -7,7 +7,10 @@ const _hoisted_3 = {
7
7
  class: "cn-contacts-tab__groups"
8
8
  };
9
9
  const _hoisted_4 = { class: "cn-contacts-tab__group-title" };
10
- const _hoisted_5 = { class: "cn-contacts-tab__group-count" };
10
+ const _hoisted_5 = {
11
+ key: 0,
12
+ class: "cn-contacts-tab__group-count"
13
+ };
11
14
  const _hoisted_6 = { class: "cn-contacts-tab__list" };
12
15
  const _hoisted_7 = ["onClick"];
13
16
  const _hoisted_8 = { class: "cn-contacts-tab__avatar" };
@@ -15,32 +18,42 @@ const _hoisted_9 = { class: "cn-contacts-tab__details" };
15
18
  const _hoisted_10 = { class: "cn-contacts-tab__name-row" };
16
19
  const _hoisted_11 = { class: "cn-contacts-tab__name" };
17
20
  const _hoisted_12 = {
21
+ key: 0,
22
+ class: "cn-contacts-tab__meta",
23
+ "data-testid": "cn-contacts-tab-meta"
24
+ };
25
+ const _hoisted_13 = {
18
26
  key: 0,
19
27
  class: "cn-contacts-tab__sub"
20
28
  };
21
- const _hoisted_13 = { class: "cn-contacts-tab__sub-text" };
22
- const _hoisted_14 = {
29
+ const _hoisted_14 = { class: "cn-contacts-tab__sub-text" };
30
+ const _hoisted_15 = {
23
31
  key: 1,
24
32
  class: "cn-contacts-tab__sub"
25
33
  };
26
- const _hoisted_15 = { class: "cn-contacts-tab__sub-text" };
27
- const _hoisted_16 = {
34
+ const _hoisted_16 = { class: "cn-contacts-tab__sub-text" };
35
+ const _hoisted_17 = {
28
36
  key: 2,
29
37
  class: "cn-contacts-tab__sub"
30
38
  };
31
- const _hoisted_17 = { class: "cn-contacts-tab__sub-text" };
32
- const _hoisted_18 = {
39
+ const _hoisted_18 = { class: "cn-contacts-tab__sub-text" };
40
+ const _hoisted_19 = {
33
41
  key: 3,
34
42
  class: "cn-contacts-tab__sub",
35
43
  "data-testid": "cn-contacts-tab-period"
36
44
  };
37
- const _hoisted_19 = { class: "cn-contacts-tab__sub-text" };
38
- const _hoisted_20 = {
45
+ const _hoisted_20 = { class: "cn-contacts-tab__sub-text" };
46
+ const _hoisted_21 = {
39
47
  key: 4,
40
48
  class: "cn-contacts-tab__sub"
41
49
  };
42
- const _hoisted_21 = { class: "cn-contacts-tab__sub-text" };
43
- const _hoisted_22 = ["aria-label", "title", "onClick"];
50
+ const _hoisted_22 = { class: "cn-contacts-tab__sub-text" };
51
+ const _hoisted_23 = {
52
+ key: 0,
53
+ class: "cn-contacts-tab__period",
54
+ "data-testid": "cn-contacts-tab-board-period"
55
+ };
56
+ const _hoisted_24 = ["aria-label", "title", "onClick"];
44
57
 
45
58
  function render(_ctx, _cache, $props, $setup, $data, $options) {
46
59
  const _component_LinkVariant = resolveComponent("LinkVariant");
@@ -58,6 +71,8 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
58
71
  const _component_OfficeBuildingOutline = resolveComponent("OfficeBuildingOutline");
59
72
  const _component_CalendarRange = resolveComponent("CalendarRange");
60
73
  const _component_Close = resolveComponent("Close");
74
+ const _component_NcActionButton = resolveComponent("NcActionButton");
75
+ const _component_NcActions = resolveComponent("NcActions");
61
76
  const _component_CnContactPicker = resolveComponent("CnContactPicker");
62
77
  const _component_CnContactCreate = resolveComponent("CnContactCreate");
63
78
 
@@ -126,7 +141,9 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
126
141
  }, [
127
142
  createElementVNode("h3", _hoisted_4, [
128
143
  createTextVNode(toDisplayString(group.label) + " ", 1 /* TEXT */),
129
- createElementVNode("span", _hoisted_5, toDisplayString(group.items.length), 1 /* TEXT */)
144
+ (!$options.isBoard)
145
+ ? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString(group.items.length), 1 /* TEXT */))
146
+ : createCommentVNode("v-if", true)
130
147
  ]),
131
148
  createElementVNode("ul", _hoisted_6, [
132
149
  (openBlock(true), createElementBlock(Fragment, null, renderList(group.items, (contact) => {
@@ -137,7 +154,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
137
154
  }, [
138
155
  createElementVNode("div", _hoisted_8, [
139
156
  createVNode(_component_NcAvatar, {
140
- size: 40,
157
+ size: $options.isBoard ? 32 : 40,
141
158
  displayName: contact.displayName || $props.unknownLabel,
142
159
  user: $options.avatarSeed(contact),
143
160
  url: contact.avatarUrl || undefined,
@@ -145,12 +162,12 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
145
162
  disableMenu: true,
146
163
  disableTooltip: true,
147
164
  hideStatus: ""
148
- }, null, 8 /* PROPS */, ["displayName", "user", "url"])
165
+ }, null, 8 /* PROPS */, ["size", "displayName", "user", "url"])
149
166
  ]),
150
167
  createElementVNode("div", _hoisted_9, [
151
168
  createElementVNode("div", _hoisted_10, [
152
169
  createElementVNode("span", _hoisted_11, toDisplayString(contact.displayName || $props.unknownLabel), 1 /* TEXT */),
153
- (contact.role)
170
+ (contact.role && !$options.isBoard)
154
171
  ? (openBlock(), createBlock(_component_CnStatusBadge, {
155
172
  key: 0,
156
173
  label: $options.roleLabel(contact),
@@ -168,57 +185,94 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
168
185
  }, null, 8 /* PROPS */, ["label"]))
169
186
  : createCommentVNode("v-if", true)
170
187
  ]),
171
- (contact.email)
172
- ? (openBlock(), createElementBlock("div", _hoisted_12, [
173
- createVNode(_component_Email, {
174
- size: 14,
175
- class: "cn-contacts-tab__sub-icon"
176
- }),
177
- createElementVNode("span", _hoisted_13, toDisplayString(contact.email), 1 /* TEXT */)
178
- ]))
179
- : createCommentVNode("v-if", true),
180
- (contact.phone)
181
- ? (openBlock(), createElementBlock("div", _hoisted_14, [
182
- createVNode(_component_Phone, {
183
- size: 14,
184
- class: "cn-contacts-tab__sub-icon"
185
- }),
186
- createElementVNode("span", _hoisted_15, toDisplayString(contact.phone), 1 /* TEXT */)
187
- ]))
188
- : createCommentVNode("v-if", true),
189
- (contact.org)
190
- ? (openBlock(), createElementBlock("div", _hoisted_16, [
191
- createVNode(_component_OfficeBuildingOutline, {
192
- size: 14,
193
- class: "cn-contacts-tab__sub-icon"
194
- }),
195
- createElementVNode("span", _hoisted_17, toDisplayString(contact.org), 1 /* TEXT */)
196
- ]))
197
- : createCommentVNode("v-if", true),
198
- ($options.periodOf(contact))
199
- ? (openBlock(), createElementBlock("div", _hoisted_18, [
200
- createVNode(_component_CalendarRange, {
201
- size: 14,
202
- class: "cn-contacts-tab__sub-icon"
203
- }),
204
- createElementVNode("span", _hoisted_19, toDisplayString($options.periodOf(contact)), 1 /* TEXT */)
205
- ]))
188
+ ($options.isBoard && $options.boardMetaOf(contact))
189
+ ? (openBlock(), createElementBlock("span", _hoisted_12, toDisplayString($options.boardMetaOf(contact)), 1 /* TEXT */))
206
190
  : createCommentVNode("v-if", true),
207
- (contact.note)
208
- ? (openBlock(), createElementBlock("div", _hoisted_20, [
209
- createElementVNode("span", _hoisted_21, toDisplayString(contact.note), 1 /* TEXT */)
210
- ]))
191
+ (!$options.isBoard)
192
+ ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
193
+ (contact.email)
194
+ ? (openBlock(), createElementBlock("div", _hoisted_13, [
195
+ createVNode(_component_Email, {
196
+ size: 14,
197
+ class: "cn-contacts-tab__sub-icon"
198
+ }),
199
+ createElementVNode("span", _hoisted_14, toDisplayString(contact.email), 1 /* TEXT */)
200
+ ]))
201
+ : createCommentVNode("v-if", true),
202
+ (contact.phone)
203
+ ? (openBlock(), createElementBlock("div", _hoisted_15, [
204
+ createVNode(_component_Phone, {
205
+ size: 14,
206
+ class: "cn-contacts-tab__sub-icon"
207
+ }),
208
+ createElementVNode("span", _hoisted_16, toDisplayString(contact.phone), 1 /* TEXT */)
209
+ ]))
210
+ : createCommentVNode("v-if", true),
211
+ (contact.org)
212
+ ? (openBlock(), createElementBlock("div", _hoisted_17, [
213
+ createVNode(_component_OfficeBuildingOutline, {
214
+ size: 14,
215
+ class: "cn-contacts-tab__sub-icon"
216
+ }),
217
+ createElementVNode("span", _hoisted_18, toDisplayString(contact.org), 1 /* TEXT */)
218
+ ]))
219
+ : createCommentVNode("v-if", true),
220
+ ($options.periodOf(contact))
221
+ ? (openBlock(), createElementBlock("div", _hoisted_19, [
222
+ createVNode(_component_CalendarRange, {
223
+ size: 14,
224
+ class: "cn-contacts-tab__sub-icon"
225
+ }),
226
+ createElementVNode("span", _hoisted_20, toDisplayString($options.periodOf(contact)), 1 /* TEXT */)
227
+ ]))
228
+ : createCommentVNode("v-if", true),
229
+ (contact.note)
230
+ ? (openBlock(), createElementBlock("div", _hoisted_21, [
231
+ createElementVNode("span", _hoisted_22, toDisplayString(contact.note), 1 /* TEXT */)
232
+ ]))
233
+ : createCommentVNode("v-if", true)
234
+ ], 64 /* STABLE_FRAGMENT */))
211
235
  : createCommentVNode("v-if", true)
212
236
  ]),
213
- createElementVNode("button", {
214
- type: "button",
215
- class: "cn-contacts-tab__unlink",
216
- "aria-label": $props.unlinkLabel,
217
- title: $props.unlinkLabel,
218
- onClick: withModifiers($event => ($options.unlink(contact)), ["stop"])
219
- }, [
220
- createVNode(_component_Close, { size: 18 })
221
- ], 8 /* PROPS */, _hoisted_22)
237
+ ($options.isBoard)
238
+ ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
239
+ ($options.boardPeriodOf(contact))
240
+ ? (openBlock(), createElementBlock("span", _hoisted_23, toDisplayString($options.boardPeriodOf(contact)), 1 /* TEXT */))
241
+ : createCommentVNode("v-if", true),
242
+ createVNode(_component_NcActions, {
243
+ class: "cn-contacts-tab__menu",
244
+ variant: "secondary",
245
+ "aria-label": $options.moreActionsLabel(contact),
246
+ "data-testid": "cn-contacts-tab-menu",
247
+ onClick: _cache[0] || (_cache[0] = withModifiers(() => {}, ["stop"]))
248
+ }, {
249
+ default: withCtx(() => [
250
+ createVNode(_component_NcActionButton, {
251
+ closeAfterClick: true,
252
+ onClick: $event => ($options.unlink(contact))
253
+ }, {
254
+ icon: withCtx(() => [
255
+ createVNode(_component_Close, { size: 20 })
256
+ ]),
257
+ default: withCtx(() => [
258
+ createTextVNode(" " + toDisplayString($props.unlinkLabel), 1 /* TEXT */)
259
+ ]),
260
+ _: 1 /* STABLE */
261
+ }, 8 /* PROPS */, ["onClick"])
262
+ ]),
263
+ _: 2 /* DYNAMIC */
264
+ }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["aria-label"])
265
+ ], 64 /* STABLE_FRAGMENT */))
266
+ : (openBlock(), createElementBlock("button", {
267
+ key: 1,
268
+ type: "button",
269
+ class: "cn-contacts-tab__unlink",
270
+ "aria-label": $props.unlinkLabel,
271
+ title: $props.unlinkLabel,
272
+ onClick: withModifiers($event => ($options.unlink(contact)), ["stop"])
273
+ }, [
274
+ createVNode(_component_Close, { size: 18 })
275
+ ], 8 /* PROPS */, _hoisted_24))
222
276
  ], 8 /* PROPS */, _hoisted_7))
223
277
  }), 128 /* KEYED_FRAGMENT */))
224
278
  ])
@@ -231,7 +285,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
231
285
  apiBase: $props.apiBase
232
286
  }, $options.pickerRoleOptions, {
233
287
  onLink: $options.onPickerLink,
234
- onClose: _cache[0] || (_cache[0] = $event => ($data.showLinkDialog = false))
288
+ onClose: _cache[1] || (_cache[1] = $event => ($data.showLinkDialog = false))
235
289
  }), null, 16 /* FULL_PROPS */, ["apiBase", "onLink"]))
236
290
  : createCommentVNode("v-if", true),
237
291
  ($data.showCreateDialog)
@@ -239,7 +293,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
239
293
  key: 5,
240
294
  loading: $data.createLoading,
241
295
  onCreate: $options.onCreateSubmit,
242
- onClose: _cache[1] || (_cache[1] = $event => ($data.showCreateDialog = false))
296
+ onClose: _cache[2] || (_cache[2] = $event => ($data.showCreateDialog = false))
243
297
  }, null, 8 /* PROPS */, ["loading", "onCreate"]))
244
298
  : createCommentVNode("v-if", true)
245
299
  ]))