@conduction/nextcloud-vue 2.143.0 → 2.144.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 (101) hide show
  1. package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
  2. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +7 -2
  3. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
  5. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +1 -1
  6. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -1
  7. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js +1 -1
  8. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -1
  9. package/dist/esm/components/CnEventsCard/CnEventsCard.vue.js +1 -1
  10. package/dist/esm/components/CnEventsCard/CnEventsCard.vue2.js +9 -0
  11. package/dist/esm/components/CnEventsCard/CnEventsCard.vue2.js.map +1 -1
  12. package/dist/esm/components/CnEventsCard/CnEventsCard.vue3.js +24 -13
  13. package/dist/esm/components/CnEventsCard/CnEventsCard.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 +2 -1
  16. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  17. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  18. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +3 -1
  19. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  20. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  21. package/dist/esm/components/CnNotesCard/CnNotesCard.vue.js +1 -1
  22. package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js +3 -0
  23. package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js.map +1 -1
  24. package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js +19 -14
  25. package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js.map +1 -1
  26. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue.js +1 -1
  27. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js +30 -0
  28. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js.map +1 -1
  29. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue3.js +158 -102
  30. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue3.js.map +1 -1
  31. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +1 -1
  32. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -1
  33. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js +12 -0
  34. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -1
  35. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue.js +1 -1
  36. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +110 -2
  37. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  38. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +146 -80
  39. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  40. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue.js +1 -1
  41. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js +45 -0
  42. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js.map +1 -1
  43. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue3.js +67 -34
  44. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue3.js.map +1 -1
  45. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue.js +1 -1
  46. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js +3 -22
  47. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js.map +1 -1
  48. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue3.js.map +1 -1
  49. package/dist/esm/integrations/builtin/calendar/eventStatus.js +67 -0
  50. package/dist/esm/integrations/builtin/calendar/eventStatus.js.map +1 -0
  51. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue.js +1 -1
  52. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +39 -0
  53. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
  54. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js +81 -32
  55. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
  56. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue.js +1 -1
  57. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +5 -6
  58. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
  59. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
  60. package/dist/esm/integrations/builtin/deck/deckCardParts.js +59 -0
  61. package/dist/esm/integrations/builtin/deck/deckCardParts.js.map +1 -0
  62. package/dist/esm/l10n/en.json.js +6 -0
  63. package/dist/esm/l10n/en.json.js.map +1 -1
  64. package/dist/esm/l10n/nl.json.js +6 -0
  65. package/dist/esm/l10n/nl.json.js.map +1 -1
  66. package/dist/esm/library-style.js +1 -1
  67. package/dist/esm/nextcloud-vue.css +815 -363
  68. package/dist/esm/utils/fetchFilterCounts.js +5 -2
  69. package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
  70. package/dist/esm/utils/nextcloudDirectory.js +19 -6
  71. package/dist/esm/utils/nextcloudDirectory.js.map +1 -1
  72. package/dist/nextcloud-vue.cjs.js +1347 -758
  73. package/dist/nextcloud-vue.cjs.js.map +2 -2
  74. package/dist/nextcloud-vue.css +815 -363
  75. package/l10n/en.json +6 -0
  76. package/l10n/nl.json +6 -0
  77. package/package.json +1 -1
  78. package/src/components/CnActionDialog/CnActionDialog.vue +28 -3
  79. package/src/components/CnConversationThread/CnConversationThread.vue +5 -2
  80. package/src/components/CnEventsCard/CnEventsCard.vue +27 -5
  81. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +15 -1
  82. package/src/components/CnIndexPage/CnIndexPage.vue +3 -1
  83. package/src/components/CnNotesCard/CnNotesCard.vue +8 -0
  84. package/src/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue +114 -46
  85. package/src/components/CnRetentionWidget/CnRetentionWidget.vue +26 -1
  86. package/src/components/CnTasksWidget/CnTasksWidget.vue +170 -13
  87. package/src/css/dialog.css +7 -1
  88. package/src/css/kpi-card.css +3 -0
  89. package/src/css/look-board-detail.css +317 -0
  90. package/src/css/look-board-index.css +14 -0
  91. package/src/css/look-board.css +25 -0
  92. package/src/integrations/builtin/calendar/CnCalendarCard.vue +76 -11
  93. package/src/integrations/builtin/calendar/CnCalendarTab.vue +3 -22
  94. package/src/integrations/builtin/calendar/__tests__/CnCalendarCardBoardRows.spec.js +52 -0
  95. package/src/integrations/builtin/calendar/eventStatus.js +63 -0
  96. package/src/integrations/builtin/deck/CnDeckCard.vue +66 -0
  97. package/src/integrations/builtin/deck/CnDeckTab.vue +5 -6
  98. package/src/integrations/builtin/deck/__tests__/CnDeckCardBoardRows.spec.js +49 -0
  99. package/src/integrations/builtin/deck/deckCardParts.js +56 -0
  100. package/src/utils/fetchFilterCounts.js +5 -2
  101. package/src/utils/nextcloudDirectory.js +19 -6
@@ -1 +1 @@
1
- {"version":3,"file":"CnDeckTab.vue2.js","sources":["../../../../../src/integrations/builtin/deck/CnDeckTab.vue"],"sourcesContent":["<!--\n CnDeckTab — bespoke sidebar tab for the `deck` integration.\n\n Replaces the generic CnIntegrationTab for the `deck` leaf: renders a\n kanban-mini view of the cards linked to the parent OR object, grouped\n by stack (\"To Do\" / \"Doing\" / \"Done\" or whatever stacks the cards live\n in). Each card row shows title, optional due date, and optional label\n chips. Clicking a row deep-links to the card in NC Deck.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/deck`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\DeckProvider`\n (which delegates to `OCA\\OpenRegister\\Service\\DeckCardService`).\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open Deck\" CTA when no linked cards.\n - Loading spinner during fetch.\n - 503 \"currently unavailable\" banner when Deck is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link list\n which loses Deck's primary signal — *where the card sits in the\n workflow* (stack name). Grouping by stack reproduces the kanban affordance\n inline so case handlers can see \"card X is in Doing\" at a glance.\n\n See `openregister/openspec/changes/integration-deck/` for the spec\n delta, ADR-019 (registry mechanism) and ADR-022 (sidebar tab contract).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-deck-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-deck-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-deck-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 card') }}\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 card') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-deck-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-sidebar-tab__empty cn-deck-tab__empty\">\n\t\t\t<ViewColumnOutline :size=\"32\" class=\"cn-deck-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=\"deckAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<ViewColumnOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openDeckLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<div v-else class=\"cn-deck-tab__kanban\">\n\t\t\t<section\n\t\t\t\tv-for=\"stack in stackColumns\"\n\t\t\t\t:key=\"stack.key\"\n\t\t\t\tclass=\"cn-deck-tab__column\">\n\t\t\t\t<header class=\"cn-deck-tab__column-header\">\n\t\t\t\t\t<span class=\"cn-deck-tab__column-title\">{{ stack.label }}</span>\n\t\t\t\t\t<span class=\"cn-deck-tab__column-count\">{{ stack.cards.length }}</span>\n\t\t\t\t</header>\n\t\t\t\t<ul class=\"cn-deck-tab__column-list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"card in stack.cards\"\n\t\t\t\t\t\t:key=\"cardKey(card)\"\n\t\t\t\t\t\tclass=\"cn-deck-tab__card\"\n\t\t\t\t\t\t:class=\"{ 'cn-deck-tab__card--overdue': isOverdue(card) }\">\n\t\t\t\t\t\t<!-- Colored label strip along the top edge, like a real Deck card. -->\n\t\t\t\t\t\t<div v-if=\"cardLabels(card).length > 0\" class=\"cn-deck-tab__card-labels\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"label in cardLabels(card)\"\n\t\t\t\t\t\t\t\t:key=\"label.id || label.title\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__chip\"\n\t\t\t\t\t\t\t\t:style=\"chipStyle(label)\"\n\t\t\t\t\t\t\t\t:title=\"chipText(label)\">\n\t\t\t\t\t\t\t\t{{ chipText(label) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Board / stack context badge above the title. -->\n\t\t\t\t\t\t<span v-if=\"boardBadge(card)\" class=\"cn-deck-tab__card-board\">\n\t\t\t\t\t\t\t<ViewColumnOutline :size=\"12\" />\n\t\t\t\t\t\t\t{{ boardBadge(card) }}\n\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"cardUrl(card)\"\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-deck-tab__card-title\">{{ cardTitle(card) }}</a>\n\n\t\t\t\t\t\t<!-- Footer: due-date chip + assignee avatars, like Deck's card footer. -->\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-if=\"dueLabel(card) || cardAssignees(card).length > 0\"\n\t\t\t\t\t\t\tclass=\"cn-deck-tab__card-footer\">\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"dueLabel(card)\"\n\t\t\t\t\t\t\t\t:label=\"dueLabel(card)\"\n\t\t\t\t\t\t\t\t:variant=\"dueVariant(card)\"\n\t\t\t\t\t\t\t\tsize=\"small\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<ClockOutline :size=\"12\" class=\"cn-deck-tab__due-icon\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t</CnStatusBadge>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tv-if=\"cardAssignees(card).length > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatars\">\n\t\t\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\t\t\tv-for=\"assignee in visibleAssignees(card)\"\n\t\t\t\t\t\t\t\t\t:key=\"assigneeKey(assignee)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatar\"\n\t\t\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\t\t\t:displayName=\"assigneeName(assignee)\"\n\t\t\t\t\t\t\t\t\t:user=\"assigneeSeed(assignee)\"\n\t\t\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t\t\t\t:disableTooltip=\"false\"\n\t\t\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"assigneeOverflow(card) > 0\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatar-overflow\"\n\t\t\t\t\t\t\t\t\t:title=\"overflowTitle(card)\">+{{ assigneeOverflow(card) }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\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<CnDeckCardPicker\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<CnDeckCardCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue'\nimport CnDeckCardCreate from '../../../components/CnDeckCardCreate/CnDeckCardCreate.vue'\nimport CnDeckCardPicker from '../../../components/CnDeckCardPicker/CnDeckCardPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\n\n/** Maximum assignee avatars shown before collapsing the rest into a +N overflow badge. */\nconst MAX_VISIBLE_ASSIGNEES = 3\n\n/**\n * CnDeckTab — kanban-mini sidebar tab for the `deck` integration.\n *\n * Groups linked cards by stack so case handlers see workflow position\n * inline; deep-links to NC Deck for editing. See file-level docblock\n * for surface behaviour.\n */\nexport default {\n\tname: 'CnDeckTab',\n\n\tcomponents: {\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tClockOutline,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tViewColumnOutline,\n\t\tCnDeckCardPicker,\n\t\tCnDeckCardCreate,\n\t\tCnStatusBadge,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'deck'`). */\n\t\tintegrationId: { type: String, default: 'deck' },\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 cards linked yet') },\n\t\t/** Pre-translated label for the \"Open Deck\" CTA. */\n\t\topenDeckLabel: { type: String, default: () => t('nextcloud-vue', 'Open Deck') },\n\t\t/** Pre-translated banner when Deck is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Deck is currently unavailable.') },\n\t\t/** URL of the NC Deck app entry. */\n\t\tdeckAppUrl: { type: String, default: '/index.php/apps/deck' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tcards: [],\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\tcomputed: {\n\t\t/**\n\t\t * Cards grouped by stack — preserves stack order via first-seen ordering.\n\t\t *\n\t\t * Each column is `{ key, label, cards: [] }`. The `key` is the\n\t\t * numeric stack id (or 'unstacked' for cards without one); the\n\t\t * `label` is the stack title when the provider returned one,\n\t\t * otherwise a placeholder.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tstackColumns() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const card of this.cards) {\n\t\t\t\tconst stackId = card.stackId ?? card.stack?.id ?? 'unstacked'\n\t\t\t\tconst stackLabel = card.stackTitle\n\t\t\t\t\t?? card.stack?.title\n\t\t\t\t\t?? (stackId === 'unstacked'\n\t\t\t\t\t\t? t('nextcloud-vue', 'No stack')\n\t\t\t\t\t\t: t('nextcloud-vue', 'Stack {n}', { n: stackId }))\n\t\t\t\tconst key = String(stackId)\n\t\t\t\tif (!groups.has(key)) {\n\t\t\t\t\tgroups.set(key, { key, label: stackLabel, cards: [] })\n\t\t\t\t}\n\t\t\t\tgroups.get(key).cards.push(card)\n\t\t\t}\n\t\t\treturn Array.from(groups.values())\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.fetchCards()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchCards()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchCards()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\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\t/**\n\t\t * Base for the Tier-2 deck endpoints (link/new). The provider's\n\t\t * generic `/integrations/deck` surface accepts the same POST\n\t\t * shape, but the bespoke Tier-2 routes are nicer for the\n\t\t * picker's `/deck/new` create-flow.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tdeckEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/deck`\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.deckEndpoint()), {\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.fetchCards()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This card is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link card.')\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('[CnDeckTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link card.')\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.deckEndpoint()}/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.fetchCards()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create card.')\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('[CnDeckTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create card.')\n\t\t\t}\n\t\t},\n\n\t\tcardKey(card) {\n\t\t\treturn card.cardId ?? card.id ?? ''\n\t\t},\n\n\t\tcardTitle(card) {\n\t\t\treturn card.cardTitle ?? card.title ?? this.cardKey(card)\n\t\t},\n\n\t\tcardUrl(card) {\n\t\t\tif (card.url && safeHref(card.url) !== '#') {\n\t\t\t\treturn card.url\n\t\t\t}\n\t\t\tconst boardId = card.boardId ?? card.board?.id ?? ''\n\t\t\tconst cardId = card.cardId ?? card.id ?? ''\n\t\t\tif (boardId && cardId) {\n\t\t\t\treturn `/index.php/apps/deck/board/${boardId}/card/${cardId}`\n\t\t\t}\n\t\t\treturn this.deckAppUrl\n\t\t},\n\n\t\tcardLabels(card) {\n\t\t\tconst labels = card.labels ?? card.tags ?? []\n\t\t\treturn Array.isArray(labels) ? labels : []\n\t\t},\n\n\t\tchipText(label) {\n\t\t\tif (typeof label === 'string') {\n\t\t\t\treturn label\n\t\t\t}\n\t\t\treturn label.title ?? label.name ?? ''\n\t\t},\n\n\t\tchipStyle(label) {\n\t\t\tif (typeof label === 'object' && label.color) {\n\t\t\t\tconst colour = String(label.color)\n\t\t\t\tconst fill = colour.startsWith('#') ? colour : `#${colour}`\n\t\t\t\treturn { background: fill, color: this.readableTextColour(fill) }\n\t\t\t}\n\t\t\treturn {}\n\t\t},\n\n\t\t/**\n\t\t * Pick black or white text for a hex background so Deck's coloured\n\t\t * label chips stay legible (per-channel luminance, ITU-R BT.601).\n\t\t *\n\t\t * @param {string} hex background colour, e.g. `#31cc7c`\n\t\t * @return {string} `#000000` or `#ffffff`\n\t\t */\n\t\treadableTextColour(hex) {\n\t\t\tconst clean = hex.replace('#', '')\n\t\t\tconst full = clean.length === 3\n\t\t\t\t? clean.split('').map((c) => c + c).join('')\n\t\t\t\t: clean\n\t\t\tif (full.length < 6) {\n\t\t\t\treturn '#ffffff'\n\t\t\t}\n\t\t\tconst r = parseInt(full.slice(0, 2), 16)\n\t\t\tconst g = parseInt(full.slice(2, 4), 16)\n\t\t\tconst b = parseInt(full.slice(4, 6), 16)\n\t\t\tconst luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255\n\t\t\treturn luminance > 0.6 ? '#000000' : '#ffffff'\n\t\t},\n\n\t\tisOverdue(card) {\n\t\t\tconst due = card.duedate ?? card.dueDate ?? null\n\t\t\tif (!due) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst date = new Date(due)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn date.getTime() < Date.now()\n\t\t},\n\n\t\tdueLabel(card) {\n\t\t\tconst due = card.duedate ?? card.dueDate ?? null\n\t\t\tif (!due) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst date = new Date(due)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn date.toLocaleDateString(userLocale(), { dateStyle: 'medium' })\n\t\t},\n\n\t\t/**\n\t\t * Status-badge variant for the due-date chip: red when overdue,\n\t\t * neutral otherwise — mirrors Deck's own overdue-date highlight.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} CnStatusBadge variant\n\t\t */\n\t\tdueVariant(card) {\n\t\t\treturn this.isOverdue(card) ? 'error' : 'default'\n\t\t},\n\n\t\t/**\n\t\t * Short board/stack context label rendered as a badge above the\n\t\t * title — the \"where does this card live\" signal a real Deck card\n\t\t * carries via its board colour bar + stack column.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} board (or board · stack) label, or '' when unknown\n\t\t */\n\t\tboardBadge(card) {\n\t\t\tconst board = card.boardTitle ?? card.board?.title ?? ''\n\t\t\tconst stack = card.stackTitle ?? card.stack?.title ?? ''\n\t\t\tif (board && stack) {\n\t\t\t\treturn `${board} · ${stack}`\n\t\t\t}\n\t\t\treturn board || stack || ''\n\t\t},\n\n\t\t/**\n\t\t * Normalised assignee list for a card. Accepts Deck's\n\t\t * `assignedUsers` (objects with a nested `participant`) as well as\n\t\t * a plain `assignees` array of strings/objects.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {Array} assignee entries\n\t\t */\n\t\tcardAssignees(card) {\n\t\t\tconst raw = card.assignedUsers ?? card.assignees ?? card.users ?? []\n\t\t\treturn Array.isArray(raw) ? raw : []\n\t\t},\n\n\t\t/**\n\t\t * The first {@link MAX_VISIBLE_ASSIGNEES} assignees to render as\n\t\t * avatars; the remainder collapse into a +N overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {Array} visible assignee entries\n\t\t */\n\t\tvisibleAssignees(card) {\n\t\t\treturn this.cardAssignees(card).slice(0, MAX_VISIBLE_ASSIGNEES)\n\t\t},\n\n\t\t/**\n\t\t * Count of assignees hidden behind the +N overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {number} overflow count (0 when all fit)\n\t\t */\n\t\tassigneeOverflow(card) {\n\t\t\tconst extra = this.cardAssignees(card).length - MAX_VISIBLE_ASSIGNEES\n\t\t\treturn extra > 0 ? extra : 0\n\t\t},\n\n\t\t/**\n\t\t * Tooltip listing the assignees collapsed into the overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} comma-separated overflow assignee names\n\t\t */\n\t\toverflowTitle(card) {\n\t\t\treturn this.cardAssignees(card)\n\t\t\t\t.slice(MAX_VISIBLE_ASSIGNEES)\n\t\t\t\t.map((a) => this.assigneeName(a))\n\t\t\t\t.join(', ')\n\t\t},\n\n\t\t/**\n\t\t * Unwrap an assignee entry to its participant payload — Deck nests\n\t\t * the user under `participant`; flat shapes pass through.\n\t\t *\n\t\t * @param {object|string} assignee raw assignee entry\n\t\t * @return {object} normalised participant object\n\t\t */\n\t\tassigneeParticipant(assignee) {\n\t\t\tif (typeof assignee === 'string') {\n\t\t\t\treturn { uid: assignee, displayname: assignee }\n\t\t\t}\n\t\t\treturn assignee.participant ?? assignee\n\t\t},\n\n\t\tassigneeName(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn p.displayname ?? p.displayName ?? p.uid ?? p.id ?? t('nextcloud-vue', 'Unknown')\n\t\t},\n\n\t\tassigneeSeed(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn String(p.uid ?? p.id ?? p.displayname ?? p.displayName ?? '')\n\t\t},\n\n\t\tassigneeKey(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn String(p.uid ?? p.id ?? p.displayname ?? p.displayName ?? Math.random())\n\t\t},\n\n\t\tasync fetchCards() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.cards = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.cards = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.cards = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load cards.')\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('[CnDeckTab] failed to fetch cards', err)\n\t\t\t\tthis.cards = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load cards.')\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-deck-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 8px;\n\tflex-wrap: wrap;\n}\n\n.cn-deck-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-deck-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-deck-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-deck-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-tab__kanban {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-deck-tab__column {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 8px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-deck-tab__column-header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tpadding: 0 2px;\n}\n\n.cn-deck-tab__column-title {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-tab__column-count {\n\tfont-size: 0.75em;\n\tcolor: var(--color-text-maxcontrast);\n\tbackground: var(--color-background-dark);\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n}\n\n.cn-deck-tab__column-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-deck-tab__card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 5px;\n\tpadding: 8px 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);\n\ttransition: box-shadow 0.1s ease-in-out, border-color 0.1s ease-in-out;\n}\n\n.cn-deck-tab__card:hover {\n\tbox-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);\n\tborder-color: var(--color-border-dark);\n}\n\n.cn-deck-tab__card--overdue {\n\tborder-color: var(--color-error);\n}\n\n.cn-deck-tab__card-board {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\talign-self: flex-start;\n\tmax-width: 100%;\n\tpadding: 1px 7px;\n\tfont-size: 0.7em;\n\tfont-weight: 500;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-tab__card-title {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\tfont-weight: 600;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\na.cn-deck-tab__card-title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-tab__card-labels {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n}\n\n/* Deck label chips: solid card-label colour with a readable text shade. */\n.cn-deck-tab__chip {\n\tdisplay: inline-block;\n\tmax-width: 100%;\n\tpadding: 1px 8px;\n\tfont-size: 0.7em;\n\tfont-weight: 500;\n\tline-height: 1.5;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-tab__card-footer {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\tmargin-top: 2px;\n}\n\n.cn-deck-tab__due-icon {\n\tdisplay: inline-flex;\n\tmargin-right: 2px;\n\tvertical-align: text-bottom;\n}\n\n.cn-deck-tab__avatars {\n\tdisplay: flex;\n\talign-items: center;\n\tmargin-left: auto;\n}\n\n.cn-deck-tab__avatar {\n\tborder: 2px solid var(--color-main-background);\n\tborder-radius: 50%;\n}\n\n.cn-deck-tab__avatar:not(:first-child) {\n\tmargin-left: -8px;\n}\n\n.cn-deck-tab__avatar-overflow {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 24px;\n\theight: 24px;\n\tmargin-left: -8px;\n\tborder: 2px solid var(--color-main-background);\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.65em;\n\tfont-weight: 600;\n}\n</style>\n"],"names":["CnDeckCardPicker","CnDeckCardCreate","CnStatusBadge","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnDeckTab.vue2.js","sources":["../../../../../src/integrations/builtin/deck/CnDeckTab.vue"],"sourcesContent":["<!--\n CnDeckTab — bespoke sidebar tab for the `deck` integration.\n\n Replaces the generic CnIntegrationTab for the `deck` leaf: renders a\n kanban-mini view of the cards linked to the parent OR object, grouped\n by stack (\"To Do\" / \"Doing\" / \"Done\" or whatever stacks the cards live\n in). Each card row shows title, optional due date, and optional label\n chips. Clicking a row deep-links to the card in NC Deck.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/deck`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\DeckProvider`\n (which delegates to `OCA\\OpenRegister\\Service\\DeckCardService`).\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open Deck\" CTA when no linked cards.\n - Loading spinner during fetch.\n - 503 \"currently unavailable\" banner when Deck is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link list\n which loses Deck's primary signal — *where the card sits in the\n workflow* (stack name). Grouping by stack reproduces the kanban affordance\n inline so case handlers can see \"card X is in Doing\" at a glance.\n\n See `openregister/openspec/changes/integration-deck/` for the spec\n delta, ADR-019 (registry mechanism) and ADR-022 (sidebar tab contract).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-deck-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-deck-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-deck-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 card') }}\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 card') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-deck-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-sidebar-tab__empty cn-deck-tab__empty\">\n\t\t\t<ViewColumnOutline :size=\"32\" class=\"cn-deck-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=\"deckAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<ViewColumnOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openDeckLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<div v-else class=\"cn-deck-tab__kanban\">\n\t\t\t<section\n\t\t\t\tv-for=\"stack in stackColumns\"\n\t\t\t\t:key=\"stack.key\"\n\t\t\t\tclass=\"cn-deck-tab__column\">\n\t\t\t\t<header class=\"cn-deck-tab__column-header\">\n\t\t\t\t\t<span class=\"cn-deck-tab__column-title\">{{ stack.label }}</span>\n\t\t\t\t\t<span class=\"cn-deck-tab__column-count\">{{ stack.cards.length }}</span>\n\t\t\t\t</header>\n\t\t\t\t<ul class=\"cn-deck-tab__column-list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"card in stack.cards\"\n\t\t\t\t\t\t:key=\"cardKey(card)\"\n\t\t\t\t\t\tclass=\"cn-deck-tab__card\"\n\t\t\t\t\t\t:class=\"{ 'cn-deck-tab__card--overdue': isOverdue(card) }\">\n\t\t\t\t\t\t<!-- Colored label strip along the top edge, like a real Deck card. -->\n\t\t\t\t\t\t<div v-if=\"cardLabels(card).length > 0\" class=\"cn-deck-tab__card-labels\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"label in cardLabels(card)\"\n\t\t\t\t\t\t\t\t:key=\"label.id || label.title\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__chip\"\n\t\t\t\t\t\t\t\t:style=\"chipStyle(label)\"\n\t\t\t\t\t\t\t\t:title=\"chipText(label)\">\n\t\t\t\t\t\t\t\t{{ chipText(label) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Board / stack context badge above the title. -->\n\t\t\t\t\t\t<span v-if=\"boardBadge(card)\" class=\"cn-deck-tab__card-board\">\n\t\t\t\t\t\t\t<ViewColumnOutline :size=\"12\" />\n\t\t\t\t\t\t\t{{ boardBadge(card) }}\n\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"cardUrl(card)\"\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-deck-tab__card-title\">{{ cardTitle(card) }}</a>\n\n\t\t\t\t\t\t<!-- Footer: due-date chip + assignee avatars, like Deck's card footer. -->\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-if=\"dueLabel(card) || cardAssignees(card).length > 0\"\n\t\t\t\t\t\t\tclass=\"cn-deck-tab__card-footer\">\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"dueLabel(card)\"\n\t\t\t\t\t\t\t\t:label=\"dueLabel(card)\"\n\t\t\t\t\t\t\t\t:variant=\"dueVariant(card)\"\n\t\t\t\t\t\t\t\tsize=\"small\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<ClockOutline :size=\"12\" class=\"cn-deck-tab__due-icon\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t</CnStatusBadge>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tv-if=\"cardAssignees(card).length > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatars\">\n\t\t\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\t\t\tv-for=\"assignee in visibleAssignees(card)\"\n\t\t\t\t\t\t\t\t\t:key=\"assigneeKey(assignee)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatar\"\n\t\t\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\t\t\t:displayName=\"assigneeName(assignee)\"\n\t\t\t\t\t\t\t\t\t:user=\"assigneeSeed(assignee)\"\n\t\t\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t\t\t\t:disableTooltip=\"false\"\n\t\t\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"assigneeOverflow(card) > 0\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatar-overflow\"\n\t\t\t\t\t\t\t\t\t:title=\"overflowTitle(card)\">+{{ assigneeOverflow(card) }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\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<CnDeckCardPicker\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<CnDeckCardCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue'\nimport CnDeckCardCreate from '../../../components/CnDeckCardCreate/CnDeckCardCreate.vue'\nimport CnDeckCardPicker from '../../../components/CnDeckCardPicker/CnDeckCardPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\nimport { cardAssignees, cardBoardTitle, cardLabels, cardStackTitle } from './deckCardParts.js'\n\n/** Maximum assignee avatars shown before collapsing the rest into a +N overflow badge. */\nconst MAX_VISIBLE_ASSIGNEES = 3\n\n/**\n * CnDeckTab — kanban-mini sidebar tab for the `deck` integration.\n *\n * Groups linked cards by stack so case handlers see workflow position\n * inline; deep-links to NC Deck for editing. See file-level docblock\n * for surface behaviour.\n */\nexport default {\n\tname: 'CnDeckTab',\n\n\tcomponents: {\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tClockOutline,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tViewColumnOutline,\n\t\tCnDeckCardPicker,\n\t\tCnDeckCardCreate,\n\t\tCnStatusBadge,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'deck'`). */\n\t\tintegrationId: { type: String, default: 'deck' },\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 cards linked yet') },\n\t\t/** Pre-translated label for the \"Open Deck\" CTA. */\n\t\topenDeckLabel: { type: String, default: () => t('nextcloud-vue', 'Open Deck') },\n\t\t/** Pre-translated banner when Deck is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Deck is currently unavailable.') },\n\t\t/** URL of the NC Deck app entry. */\n\t\tdeckAppUrl: { type: String, default: '/index.php/apps/deck' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tcards: [],\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\tcomputed: {\n\t\t/**\n\t\t * Cards grouped by stack — preserves stack order via first-seen ordering.\n\t\t *\n\t\t * Each column is `{ key, label, cards: [] }`. The `key` is the\n\t\t * numeric stack id (or 'unstacked' for cards without one); the\n\t\t * `label` is the stack title when the provider returned one,\n\t\t * otherwise a placeholder.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tstackColumns() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const card of this.cards) {\n\t\t\t\tconst stackId = card.stackId ?? card.stack?.id ?? 'unstacked'\n\t\t\t\tconst stackLabel = card.stackTitle\n\t\t\t\t\t?? card.stack?.title\n\t\t\t\t\t?? (stackId === 'unstacked'\n\t\t\t\t\t\t? t('nextcloud-vue', 'No stack')\n\t\t\t\t\t\t: t('nextcloud-vue', 'Stack {n}', { n: stackId }))\n\t\t\t\tconst key = String(stackId)\n\t\t\t\tif (!groups.has(key)) {\n\t\t\t\t\tgroups.set(key, { key, label: stackLabel, cards: [] })\n\t\t\t\t}\n\t\t\t\tgroups.get(key).cards.push(card)\n\t\t\t}\n\t\t\treturn Array.from(groups.values())\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.fetchCards()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchCards()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchCards()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\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\t/**\n\t\t * Base for the Tier-2 deck endpoints (link/new). The provider's\n\t\t * generic `/integrations/deck` surface accepts the same POST\n\t\t * shape, but the bespoke Tier-2 routes are nicer for the\n\t\t * picker's `/deck/new` create-flow.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tdeckEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/deck`\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.deckEndpoint()), {\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.fetchCards()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This card is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link card.')\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('[CnDeckTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link card.')\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.deckEndpoint()}/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.fetchCards()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create card.')\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('[CnDeckTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create card.')\n\t\t\t}\n\t\t},\n\n\t\tcardKey(card) {\n\t\t\treturn card.cardId ?? card.id ?? ''\n\t\t},\n\n\t\tcardTitle(card) {\n\t\t\treturn card.cardTitle ?? card.title ?? this.cardKey(card)\n\t\t},\n\n\t\tcardUrl(card) {\n\t\t\tif (card.url && safeHref(card.url) !== '#') {\n\t\t\t\treturn card.url\n\t\t\t}\n\t\t\tconst boardId = card.boardId ?? card.board?.id ?? ''\n\t\t\tconst cardId = card.cardId ?? card.id ?? ''\n\t\t\tif (boardId && cardId) {\n\t\t\t\treturn `/index.php/apps/deck/board/${boardId}/card/${cardId}`\n\t\t\t}\n\t\t\treturn this.deckAppUrl\n\t\t},\n\n\t\tcardLabels(card) {\n\t\t\treturn cardLabels(card)\n\t\t},\n\n\t\tchipText(label) {\n\t\t\tif (typeof label === 'string') {\n\t\t\t\treturn label\n\t\t\t}\n\t\t\treturn label.title ?? label.name ?? ''\n\t\t},\n\n\t\tchipStyle(label) {\n\t\t\tif (typeof label === 'object' && label.color) {\n\t\t\t\tconst colour = String(label.color)\n\t\t\t\tconst fill = colour.startsWith('#') ? colour : `#${colour}`\n\t\t\t\treturn { background: fill, color: this.readableTextColour(fill) }\n\t\t\t}\n\t\t\treturn {}\n\t\t},\n\n\t\t/**\n\t\t * Pick black or white text for a hex background so Deck's coloured\n\t\t * label chips stay legible (per-channel luminance, ITU-R BT.601).\n\t\t *\n\t\t * @param {string} hex background colour, e.g. `#31cc7c`\n\t\t * @return {string} `#000000` or `#ffffff`\n\t\t */\n\t\treadableTextColour(hex) {\n\t\t\tconst clean = hex.replace('#', '')\n\t\t\tconst full = clean.length === 3\n\t\t\t\t? clean.split('').map((c) => c + c).join('')\n\t\t\t\t: clean\n\t\t\tif (full.length < 6) {\n\t\t\t\treturn '#ffffff'\n\t\t\t}\n\t\t\tconst r = parseInt(full.slice(0, 2), 16)\n\t\t\tconst g = parseInt(full.slice(2, 4), 16)\n\t\t\tconst b = parseInt(full.slice(4, 6), 16)\n\t\t\tconst luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255\n\t\t\treturn luminance > 0.6 ? '#000000' : '#ffffff'\n\t\t},\n\n\t\tisOverdue(card) {\n\t\t\tconst due = card.duedate ?? card.dueDate ?? null\n\t\t\tif (!due) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst date = new Date(due)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn date.getTime() < Date.now()\n\t\t},\n\n\t\tdueLabel(card) {\n\t\t\tconst due = card.duedate ?? card.dueDate ?? null\n\t\t\tif (!due) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst date = new Date(due)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn date.toLocaleDateString(userLocale(), { dateStyle: 'medium' })\n\t\t},\n\n\t\t/**\n\t\t * Status-badge variant for the due-date chip: red when overdue,\n\t\t * neutral otherwise — mirrors Deck's own overdue-date highlight.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} CnStatusBadge variant\n\t\t */\n\t\tdueVariant(card) {\n\t\t\treturn this.isOverdue(card) ? 'error' : 'default'\n\t\t},\n\n\t\t/**\n\t\t * Short board/stack context label rendered as a badge above the\n\t\t * title — the \"where does this card live\" signal a real Deck card\n\t\t * carries via its board colour bar + stack column.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} board (or board · stack) label, or '' when unknown\n\t\t */\n\t\tboardBadge(card) {\n\t\t\tconst board = cardBoardTitle(card)\n\t\t\tconst stack = cardStackTitle(card)\n\t\t\tif (board && stack) {\n\t\t\t\treturn `${board} · ${stack}`\n\t\t\t}\n\t\t\treturn board || stack || ''\n\t\t},\n\n\t\t/**\n\t\t * Normalised assignee list for a card. Accepts Deck's\n\t\t * `assignedUsers` (objects with a nested `participant`) as well as\n\t\t * a plain `assignees` array of strings/objects.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {Array} assignee entries\n\t\t */\n\t\tcardAssignees(card) {\n\t\t\treturn cardAssignees(card)\n\t\t},\n\n\t\t/**\n\t\t * The first {@link MAX_VISIBLE_ASSIGNEES} assignees to render as\n\t\t * avatars; the remainder collapse into a +N overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {Array} visible assignee entries\n\t\t */\n\t\tvisibleAssignees(card) {\n\t\t\treturn this.cardAssignees(card).slice(0, MAX_VISIBLE_ASSIGNEES)\n\t\t},\n\n\t\t/**\n\t\t * Count of assignees hidden behind the +N overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {number} overflow count (0 when all fit)\n\t\t */\n\t\tassigneeOverflow(card) {\n\t\t\tconst extra = this.cardAssignees(card).length - MAX_VISIBLE_ASSIGNEES\n\t\t\treturn extra > 0 ? extra : 0\n\t\t},\n\n\t\t/**\n\t\t * Tooltip listing the assignees collapsed into the overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} comma-separated overflow assignee names\n\t\t */\n\t\toverflowTitle(card) {\n\t\t\treturn this.cardAssignees(card)\n\t\t\t\t.slice(MAX_VISIBLE_ASSIGNEES)\n\t\t\t\t.map((a) => this.assigneeName(a))\n\t\t\t\t.join(', ')\n\t\t},\n\n\t\t/**\n\t\t * Unwrap an assignee entry to its participant payload — Deck nests\n\t\t * the user under `participant`; flat shapes pass through.\n\t\t *\n\t\t * @param {object|string} assignee raw assignee entry\n\t\t * @return {object} normalised participant object\n\t\t */\n\t\tassigneeParticipant(assignee) {\n\t\t\tif (typeof assignee === 'string') {\n\t\t\t\treturn { uid: assignee, displayname: assignee }\n\t\t\t}\n\t\t\treturn assignee.participant ?? assignee\n\t\t},\n\n\t\tassigneeName(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn p.displayname ?? p.displayName ?? p.uid ?? p.id ?? t('nextcloud-vue', 'Unknown')\n\t\t},\n\n\t\tassigneeSeed(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn String(p.uid ?? p.id ?? p.displayname ?? p.displayName ?? '')\n\t\t},\n\n\t\tassigneeKey(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn String(p.uid ?? p.id ?? p.displayname ?? p.displayName ?? Math.random())\n\t\t},\n\n\t\tasync fetchCards() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.cards = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.cards = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.cards = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load cards.')\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('[CnDeckTab] failed to fetch cards', err)\n\t\t\t\tthis.cards = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load cards.')\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-deck-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 8px;\n\tflex-wrap: wrap;\n}\n\n.cn-deck-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-deck-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-deck-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-deck-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-tab__kanban {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-deck-tab__column {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 8px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-deck-tab__column-header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tpadding: 0 2px;\n}\n\n.cn-deck-tab__column-title {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-tab__column-count {\n\tfont-size: 0.75em;\n\tcolor: var(--color-text-maxcontrast);\n\tbackground: var(--color-background-dark);\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n}\n\n.cn-deck-tab__column-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-deck-tab__card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 5px;\n\tpadding: 8px 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);\n\ttransition: box-shadow 0.1s ease-in-out, border-color 0.1s ease-in-out;\n}\n\n.cn-deck-tab__card:hover {\n\tbox-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);\n\tborder-color: var(--color-border-dark);\n}\n\n.cn-deck-tab__card--overdue {\n\tborder-color: var(--color-error);\n}\n\n.cn-deck-tab__card-board {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\talign-self: flex-start;\n\tmax-width: 100%;\n\tpadding: 1px 7px;\n\tfont-size: 0.7em;\n\tfont-weight: 500;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-tab__card-title {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\tfont-weight: 600;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\na.cn-deck-tab__card-title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-tab__card-labels {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n}\n\n/* Deck label chips: solid card-label colour with a readable text shade. */\n.cn-deck-tab__chip {\n\tdisplay: inline-block;\n\tmax-width: 100%;\n\tpadding: 1px 8px;\n\tfont-size: 0.7em;\n\tfont-weight: 500;\n\tline-height: 1.5;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-tab__card-footer {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\tmargin-top: 2px;\n}\n\n.cn-deck-tab__due-icon {\n\tdisplay: inline-flex;\n\tmargin-right: 2px;\n\tvertical-align: text-bottom;\n}\n\n.cn-deck-tab__avatars {\n\tdisplay: flex;\n\talign-items: center;\n\tmargin-left: auto;\n}\n\n.cn-deck-tab__avatar {\n\tborder: 2px solid var(--color-main-background);\n\tborder-radius: 50%;\n}\n\n.cn-deck-tab__avatar:not(:first-child) {\n\tmargin-left: -8px;\n}\n\n.cn-deck-tab__avatar-overflow {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 24px;\n\theight: 24px;\n\tmargin-left: -8px;\n\tborder: 2px solid var(--color-main-background);\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.65em;\n\tfont-weight: 600;\n}\n</style>\n"],"names":["CnDeckCardPicker","CnDeckCardCreate","CnStatusBadge","t"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnDeckTab.vue3.js","sources":["../../../../../src/integrations/builtin/deck/CnDeckTab.vue?vue&type=template&id=d4b5abb2&lang.js"],"sourcesContent":["<!--\n CnDeckTab — bespoke sidebar tab for the `deck` integration.\n\n Replaces the generic CnIntegrationTab for the `deck` leaf: renders a\n kanban-mini view of the cards linked to the parent OR object, grouped\n by stack (\"To Do\" / \"Doing\" / \"Done\" or whatever stacks the cards live\n in). Each card row shows title, optional due date, and optional label\n chips. Clicking a row deep-links to the card in NC Deck.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/deck`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\DeckProvider`\n (which delegates to `OCA\\OpenRegister\\Service\\DeckCardService`).\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open Deck\" CTA when no linked cards.\n - Loading spinner during fetch.\n - 503 \"currently unavailable\" banner when Deck is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link list\n which loses Deck's primary signal — *where the card sits in the\n workflow* (stack name). Grouping by stack reproduces the kanban affordance\n inline so case handlers can see \"card X is in Doing\" at a glance.\n\n See `openregister/openspec/changes/integration-deck/` for the spec\n delta, ADR-019 (registry mechanism) and ADR-022 (sidebar tab contract).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-deck-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-deck-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-deck-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 card') }}\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 card') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-deck-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-sidebar-tab__empty cn-deck-tab__empty\">\n\t\t\t<ViewColumnOutline :size=\"32\" class=\"cn-deck-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=\"deckAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<ViewColumnOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openDeckLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<div v-else class=\"cn-deck-tab__kanban\">\n\t\t\t<section\n\t\t\t\tv-for=\"stack in stackColumns\"\n\t\t\t\t:key=\"stack.key\"\n\t\t\t\tclass=\"cn-deck-tab__column\">\n\t\t\t\t<header class=\"cn-deck-tab__column-header\">\n\t\t\t\t\t<span class=\"cn-deck-tab__column-title\">{{ stack.label }}</span>\n\t\t\t\t\t<span class=\"cn-deck-tab__column-count\">{{ stack.cards.length }}</span>\n\t\t\t\t</header>\n\t\t\t\t<ul class=\"cn-deck-tab__column-list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"card in stack.cards\"\n\t\t\t\t\t\t:key=\"cardKey(card)\"\n\t\t\t\t\t\tclass=\"cn-deck-tab__card\"\n\t\t\t\t\t\t:class=\"{ 'cn-deck-tab__card--overdue': isOverdue(card) }\">\n\t\t\t\t\t\t<!-- Colored label strip along the top edge, like a real Deck card. -->\n\t\t\t\t\t\t<div v-if=\"cardLabels(card).length > 0\" class=\"cn-deck-tab__card-labels\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"label in cardLabels(card)\"\n\t\t\t\t\t\t\t\t:key=\"label.id || label.title\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__chip\"\n\t\t\t\t\t\t\t\t:style=\"chipStyle(label)\"\n\t\t\t\t\t\t\t\t:title=\"chipText(label)\">\n\t\t\t\t\t\t\t\t{{ chipText(label) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Board / stack context badge above the title. -->\n\t\t\t\t\t\t<span v-if=\"boardBadge(card)\" class=\"cn-deck-tab__card-board\">\n\t\t\t\t\t\t\t<ViewColumnOutline :size=\"12\" />\n\t\t\t\t\t\t\t{{ boardBadge(card) }}\n\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"cardUrl(card)\"\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-deck-tab__card-title\">{{ cardTitle(card) }}</a>\n\n\t\t\t\t\t\t<!-- Footer: due-date chip + assignee avatars, like Deck's card footer. -->\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-if=\"dueLabel(card) || cardAssignees(card).length > 0\"\n\t\t\t\t\t\t\tclass=\"cn-deck-tab__card-footer\">\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"dueLabel(card)\"\n\t\t\t\t\t\t\t\t:label=\"dueLabel(card)\"\n\t\t\t\t\t\t\t\t:variant=\"dueVariant(card)\"\n\t\t\t\t\t\t\t\tsize=\"small\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<ClockOutline :size=\"12\" class=\"cn-deck-tab__due-icon\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t</CnStatusBadge>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tv-if=\"cardAssignees(card).length > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatars\">\n\t\t\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\t\t\tv-for=\"assignee in visibleAssignees(card)\"\n\t\t\t\t\t\t\t\t\t:key=\"assigneeKey(assignee)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatar\"\n\t\t\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\t\t\t:displayName=\"assigneeName(assignee)\"\n\t\t\t\t\t\t\t\t\t:user=\"assigneeSeed(assignee)\"\n\t\t\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t\t\t\t:disableTooltip=\"false\"\n\t\t\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"assigneeOverflow(card) > 0\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatar-overflow\"\n\t\t\t\t\t\t\t\t\t:title=\"overflowTitle(card)\">+{{ assigneeOverflow(card) }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\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<CnDeckCardPicker\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<CnDeckCardCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue'\nimport CnDeckCardCreate from '../../../components/CnDeckCardCreate/CnDeckCardCreate.vue'\nimport CnDeckCardPicker from '../../../components/CnDeckCardPicker/CnDeckCardPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\n\n/** Maximum assignee avatars shown before collapsing the rest into a +N overflow badge. */\nconst MAX_VISIBLE_ASSIGNEES = 3\n\n/**\n * CnDeckTab — kanban-mini sidebar tab for the `deck` integration.\n *\n * Groups linked cards by stack so case handlers see workflow position\n * inline; deep-links to NC Deck for editing. See file-level docblock\n * for surface behaviour.\n */\nexport default {\n\tname: 'CnDeckTab',\n\n\tcomponents: {\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tClockOutline,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tViewColumnOutline,\n\t\tCnDeckCardPicker,\n\t\tCnDeckCardCreate,\n\t\tCnStatusBadge,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'deck'`). */\n\t\tintegrationId: { type: String, default: 'deck' },\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 cards linked yet') },\n\t\t/** Pre-translated label for the \"Open Deck\" CTA. */\n\t\topenDeckLabel: { type: String, default: () => t('nextcloud-vue', 'Open Deck') },\n\t\t/** Pre-translated banner when Deck is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Deck is currently unavailable.') },\n\t\t/** URL of the NC Deck app entry. */\n\t\tdeckAppUrl: { type: String, default: '/index.php/apps/deck' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tcards: [],\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\tcomputed: {\n\t\t/**\n\t\t * Cards grouped by stack — preserves stack order via first-seen ordering.\n\t\t *\n\t\t * Each column is `{ key, label, cards: [] }`. The `key` is the\n\t\t * numeric stack id (or 'unstacked' for cards without one); the\n\t\t * `label` is the stack title when the provider returned one,\n\t\t * otherwise a placeholder.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tstackColumns() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const card of this.cards) {\n\t\t\t\tconst stackId = card.stackId ?? card.stack?.id ?? 'unstacked'\n\t\t\t\tconst stackLabel = card.stackTitle\n\t\t\t\t\t?? card.stack?.title\n\t\t\t\t\t?? (stackId === 'unstacked'\n\t\t\t\t\t\t? t('nextcloud-vue', 'No stack')\n\t\t\t\t\t\t: t('nextcloud-vue', 'Stack {n}', { n: stackId }))\n\t\t\t\tconst key = String(stackId)\n\t\t\t\tif (!groups.has(key)) {\n\t\t\t\t\tgroups.set(key, { key, label: stackLabel, cards: [] })\n\t\t\t\t}\n\t\t\t\tgroups.get(key).cards.push(card)\n\t\t\t}\n\t\t\treturn Array.from(groups.values())\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.fetchCards()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchCards()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchCards()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\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\t/**\n\t\t * Base for the Tier-2 deck endpoints (link/new). The provider's\n\t\t * generic `/integrations/deck` surface accepts the same POST\n\t\t * shape, but the bespoke Tier-2 routes are nicer for the\n\t\t * picker's `/deck/new` create-flow.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tdeckEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/deck`\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.deckEndpoint()), {\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.fetchCards()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This card is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link card.')\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('[CnDeckTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link card.')\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.deckEndpoint()}/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.fetchCards()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create card.')\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('[CnDeckTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create card.')\n\t\t\t}\n\t\t},\n\n\t\tcardKey(card) {\n\t\t\treturn card.cardId ?? card.id ?? ''\n\t\t},\n\n\t\tcardTitle(card) {\n\t\t\treturn card.cardTitle ?? card.title ?? this.cardKey(card)\n\t\t},\n\n\t\tcardUrl(card) {\n\t\t\tif (card.url && safeHref(card.url) !== '#') {\n\t\t\t\treturn card.url\n\t\t\t}\n\t\t\tconst boardId = card.boardId ?? card.board?.id ?? ''\n\t\t\tconst cardId = card.cardId ?? card.id ?? ''\n\t\t\tif (boardId && cardId) {\n\t\t\t\treturn `/index.php/apps/deck/board/${boardId}/card/${cardId}`\n\t\t\t}\n\t\t\treturn this.deckAppUrl\n\t\t},\n\n\t\tcardLabels(card) {\n\t\t\tconst labels = card.labels ?? card.tags ?? []\n\t\t\treturn Array.isArray(labels) ? labels : []\n\t\t},\n\n\t\tchipText(label) {\n\t\t\tif (typeof label === 'string') {\n\t\t\t\treturn label\n\t\t\t}\n\t\t\treturn label.title ?? label.name ?? ''\n\t\t},\n\n\t\tchipStyle(label) {\n\t\t\tif (typeof label === 'object' && label.color) {\n\t\t\t\tconst colour = String(label.color)\n\t\t\t\tconst fill = colour.startsWith('#') ? colour : `#${colour}`\n\t\t\t\treturn { background: fill, color: this.readableTextColour(fill) }\n\t\t\t}\n\t\t\treturn {}\n\t\t},\n\n\t\t/**\n\t\t * Pick black or white text for a hex background so Deck's coloured\n\t\t * label chips stay legible (per-channel luminance, ITU-R BT.601).\n\t\t *\n\t\t * @param {string} hex background colour, e.g. `#31cc7c`\n\t\t * @return {string} `#000000` or `#ffffff`\n\t\t */\n\t\treadableTextColour(hex) {\n\t\t\tconst clean = hex.replace('#', '')\n\t\t\tconst full = clean.length === 3\n\t\t\t\t? clean.split('').map((c) => c + c).join('')\n\t\t\t\t: clean\n\t\t\tif (full.length < 6) {\n\t\t\t\treturn '#ffffff'\n\t\t\t}\n\t\t\tconst r = parseInt(full.slice(0, 2), 16)\n\t\t\tconst g = parseInt(full.slice(2, 4), 16)\n\t\t\tconst b = parseInt(full.slice(4, 6), 16)\n\t\t\tconst luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255\n\t\t\treturn luminance > 0.6 ? '#000000' : '#ffffff'\n\t\t},\n\n\t\tisOverdue(card) {\n\t\t\tconst due = card.duedate ?? card.dueDate ?? null\n\t\t\tif (!due) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst date = new Date(due)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn date.getTime() < Date.now()\n\t\t},\n\n\t\tdueLabel(card) {\n\t\t\tconst due = card.duedate ?? card.dueDate ?? null\n\t\t\tif (!due) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst date = new Date(due)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn date.toLocaleDateString(userLocale(), { dateStyle: 'medium' })\n\t\t},\n\n\t\t/**\n\t\t * Status-badge variant for the due-date chip: red when overdue,\n\t\t * neutral otherwise — mirrors Deck's own overdue-date highlight.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} CnStatusBadge variant\n\t\t */\n\t\tdueVariant(card) {\n\t\t\treturn this.isOverdue(card) ? 'error' : 'default'\n\t\t},\n\n\t\t/**\n\t\t * Short board/stack context label rendered as a badge above the\n\t\t * title — the \"where does this card live\" signal a real Deck card\n\t\t * carries via its board colour bar + stack column.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} board (or board · stack) label, or '' when unknown\n\t\t */\n\t\tboardBadge(card) {\n\t\t\tconst board = card.boardTitle ?? card.board?.title ?? ''\n\t\t\tconst stack = card.stackTitle ?? card.stack?.title ?? ''\n\t\t\tif (board && stack) {\n\t\t\t\treturn `${board} · ${stack}`\n\t\t\t}\n\t\t\treturn board || stack || ''\n\t\t},\n\n\t\t/**\n\t\t * Normalised assignee list for a card. Accepts Deck's\n\t\t * `assignedUsers` (objects with a nested `participant`) as well as\n\t\t * a plain `assignees` array of strings/objects.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {Array} assignee entries\n\t\t */\n\t\tcardAssignees(card) {\n\t\t\tconst raw = card.assignedUsers ?? card.assignees ?? card.users ?? []\n\t\t\treturn Array.isArray(raw) ? raw : []\n\t\t},\n\n\t\t/**\n\t\t * The first {@link MAX_VISIBLE_ASSIGNEES} assignees to render as\n\t\t * avatars; the remainder collapse into a +N overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {Array} visible assignee entries\n\t\t */\n\t\tvisibleAssignees(card) {\n\t\t\treturn this.cardAssignees(card).slice(0, MAX_VISIBLE_ASSIGNEES)\n\t\t},\n\n\t\t/**\n\t\t * Count of assignees hidden behind the +N overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {number} overflow count (0 when all fit)\n\t\t */\n\t\tassigneeOverflow(card) {\n\t\t\tconst extra = this.cardAssignees(card).length - MAX_VISIBLE_ASSIGNEES\n\t\t\treturn extra > 0 ? extra : 0\n\t\t},\n\n\t\t/**\n\t\t * Tooltip listing the assignees collapsed into the overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} comma-separated overflow assignee names\n\t\t */\n\t\toverflowTitle(card) {\n\t\t\treturn this.cardAssignees(card)\n\t\t\t\t.slice(MAX_VISIBLE_ASSIGNEES)\n\t\t\t\t.map((a) => this.assigneeName(a))\n\t\t\t\t.join(', ')\n\t\t},\n\n\t\t/**\n\t\t * Unwrap an assignee entry to its participant payload — Deck nests\n\t\t * the user under `participant`; flat shapes pass through.\n\t\t *\n\t\t * @param {object|string} assignee raw assignee entry\n\t\t * @return {object} normalised participant object\n\t\t */\n\t\tassigneeParticipant(assignee) {\n\t\t\tif (typeof assignee === 'string') {\n\t\t\t\treturn { uid: assignee, displayname: assignee }\n\t\t\t}\n\t\t\treturn assignee.participant ?? assignee\n\t\t},\n\n\t\tassigneeName(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn p.displayname ?? p.displayName ?? p.uid ?? p.id ?? t('nextcloud-vue', 'Unknown')\n\t\t},\n\n\t\tassigneeSeed(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn String(p.uid ?? p.id ?? p.displayname ?? p.displayName ?? '')\n\t\t},\n\n\t\tassigneeKey(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn String(p.uid ?? p.id ?? p.displayname ?? p.displayName ?? Math.random())\n\t\t},\n\n\t\tasync fetchCards() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.cards = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.cards = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.cards = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load cards.')\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('[CnDeckTab] failed to fetch cards', err)\n\t\t\t\tthis.cards = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load cards.')\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-deck-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 8px;\n\tflex-wrap: wrap;\n}\n\n.cn-deck-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-deck-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-deck-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-deck-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-tab__kanban {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-deck-tab__column {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 8px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-deck-tab__column-header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tpadding: 0 2px;\n}\n\n.cn-deck-tab__column-title {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-tab__column-count {\n\tfont-size: 0.75em;\n\tcolor: var(--color-text-maxcontrast);\n\tbackground: var(--color-background-dark);\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n}\n\n.cn-deck-tab__column-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-deck-tab__card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 5px;\n\tpadding: 8px 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);\n\ttransition: box-shadow 0.1s ease-in-out, border-color 0.1s ease-in-out;\n}\n\n.cn-deck-tab__card:hover {\n\tbox-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);\n\tborder-color: var(--color-border-dark);\n}\n\n.cn-deck-tab__card--overdue {\n\tborder-color: var(--color-error);\n}\n\n.cn-deck-tab__card-board {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\talign-self: flex-start;\n\tmax-width: 100%;\n\tpadding: 1px 7px;\n\tfont-size: 0.7em;\n\tfont-weight: 500;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-tab__card-title {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\tfont-weight: 600;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\na.cn-deck-tab__card-title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-tab__card-labels {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n}\n\n/* Deck label chips: solid card-label colour with a readable text shade. */\n.cn-deck-tab__chip {\n\tdisplay: inline-block;\n\tmax-width: 100%;\n\tpadding: 1px 8px;\n\tfont-size: 0.7em;\n\tfont-weight: 500;\n\tline-height: 1.5;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-tab__card-footer {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\tmargin-top: 2px;\n}\n\n.cn-deck-tab__due-icon {\n\tdisplay: inline-flex;\n\tmargin-right: 2px;\n\tvertical-align: text-bottom;\n}\n\n.cn-deck-tab__avatars {\n\tdisplay: flex;\n\talign-items: center;\n\tmargin-left: auto;\n}\n\n.cn-deck-tab__avatar {\n\tborder: 2px solid var(--color-main-background);\n\tborder-radius: 50%;\n}\n\n.cn-deck-tab__avatar:not(:first-child) {\n\tmargin-left: -8px;\n}\n\n.cn-deck-tab__avatar-overflow {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 24px;\n\theight: 24px;\n\tmargin-left: -8px;\n\tborder: 2px solid var(--color-main-background);\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.65em;\n\tfont-weight: 600;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_createElementVNode","_createTextVNode","_toDisplayString","_renderList","_normalizeClass","_Fragment","_normalizeStyle","_createBlock"],"mappings":"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"}
1
+ {"version":3,"file":"CnDeckTab.vue3.js","sources":["../../../../../src/integrations/builtin/deck/CnDeckTab.vue?vue&type=template&id=bf899238&lang.js"],"sourcesContent":["<!--\n CnDeckTab — bespoke sidebar tab for the `deck` integration.\n\n Replaces the generic CnIntegrationTab for the `deck` leaf: renders a\n kanban-mini view of the cards linked to the parent OR object, grouped\n by stack (\"To Do\" / \"Doing\" / \"Done\" or whatever stacks the cards live\n in). Each card row shows title, optional due date, and optional label\n chips. Clicking a row deep-links to the card in NC Deck.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/deck`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\DeckProvider`\n (which delegates to `OCA\\OpenRegister\\Service\\DeckCardService`).\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open Deck\" CTA when no linked cards.\n - Loading spinner during fetch.\n - 503 \"currently unavailable\" banner when Deck is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link list\n which loses Deck's primary signal — *where the card sits in the\n workflow* (stack name). Grouping by stack reproduces the kanban affordance\n inline so case handlers can see \"card X is in Doing\" at a glance.\n\n See `openregister/openspec/changes/integration-deck/` for the spec\n delta, ADR-019 (registry mechanism) and ADR-022 (sidebar tab contract).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-deck-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-deck-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-deck-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 card') }}\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 card') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-deck-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-sidebar-tab__empty cn-deck-tab__empty\">\n\t\t\t<ViewColumnOutline :size=\"32\" class=\"cn-deck-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=\"deckAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<ViewColumnOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openDeckLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<div v-else class=\"cn-deck-tab__kanban\">\n\t\t\t<section\n\t\t\t\tv-for=\"stack in stackColumns\"\n\t\t\t\t:key=\"stack.key\"\n\t\t\t\tclass=\"cn-deck-tab__column\">\n\t\t\t\t<header class=\"cn-deck-tab__column-header\">\n\t\t\t\t\t<span class=\"cn-deck-tab__column-title\">{{ stack.label }}</span>\n\t\t\t\t\t<span class=\"cn-deck-tab__column-count\">{{ stack.cards.length }}</span>\n\t\t\t\t</header>\n\t\t\t\t<ul class=\"cn-deck-tab__column-list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"card in stack.cards\"\n\t\t\t\t\t\t:key=\"cardKey(card)\"\n\t\t\t\t\t\tclass=\"cn-deck-tab__card\"\n\t\t\t\t\t\t:class=\"{ 'cn-deck-tab__card--overdue': isOverdue(card) }\">\n\t\t\t\t\t\t<!-- Colored label strip along the top edge, like a real Deck card. -->\n\t\t\t\t\t\t<div v-if=\"cardLabels(card).length > 0\" class=\"cn-deck-tab__card-labels\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"label in cardLabels(card)\"\n\t\t\t\t\t\t\t\t:key=\"label.id || label.title\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__chip\"\n\t\t\t\t\t\t\t\t:style=\"chipStyle(label)\"\n\t\t\t\t\t\t\t\t:title=\"chipText(label)\">\n\t\t\t\t\t\t\t\t{{ chipText(label) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Board / stack context badge above the title. -->\n\t\t\t\t\t\t<span v-if=\"boardBadge(card)\" class=\"cn-deck-tab__card-board\">\n\t\t\t\t\t\t\t<ViewColumnOutline :size=\"12\" />\n\t\t\t\t\t\t\t{{ boardBadge(card) }}\n\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"cardUrl(card)\"\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-deck-tab__card-title\">{{ cardTitle(card) }}</a>\n\n\t\t\t\t\t\t<!-- Footer: due-date chip + assignee avatars, like Deck's card footer. -->\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-if=\"dueLabel(card) || cardAssignees(card).length > 0\"\n\t\t\t\t\t\t\tclass=\"cn-deck-tab__card-footer\">\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"dueLabel(card)\"\n\t\t\t\t\t\t\t\t:label=\"dueLabel(card)\"\n\t\t\t\t\t\t\t\t:variant=\"dueVariant(card)\"\n\t\t\t\t\t\t\t\tsize=\"small\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<ClockOutline :size=\"12\" class=\"cn-deck-tab__due-icon\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t</CnStatusBadge>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tv-if=\"cardAssignees(card).length > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatars\">\n\t\t\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\t\t\tv-for=\"assignee in visibleAssignees(card)\"\n\t\t\t\t\t\t\t\t\t:key=\"assigneeKey(assignee)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatar\"\n\t\t\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\t\t\t:displayName=\"assigneeName(assignee)\"\n\t\t\t\t\t\t\t\t\t:user=\"assigneeSeed(assignee)\"\n\t\t\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t\t\t\t:disableTooltip=\"false\"\n\t\t\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"assigneeOverflow(card) > 0\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatar-overflow\"\n\t\t\t\t\t\t\t\t\t:title=\"overflowTitle(card)\">+{{ assigneeOverflow(card) }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\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<CnDeckCardPicker\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<CnDeckCardCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue'\nimport CnDeckCardCreate from '../../../components/CnDeckCardCreate/CnDeckCardCreate.vue'\nimport CnDeckCardPicker from '../../../components/CnDeckCardPicker/CnDeckCardPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\nimport { cardAssignees, cardBoardTitle, cardLabels, cardStackTitle } from './deckCardParts.js'\n\n/** Maximum assignee avatars shown before collapsing the rest into a +N overflow badge. */\nconst MAX_VISIBLE_ASSIGNEES = 3\n\n/**\n * CnDeckTab — kanban-mini sidebar tab for the `deck` integration.\n *\n * Groups linked cards by stack so case handlers see workflow position\n * inline; deep-links to NC Deck for editing. See file-level docblock\n * for surface behaviour.\n */\nexport default {\n\tname: 'CnDeckTab',\n\n\tcomponents: {\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tClockOutline,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tViewColumnOutline,\n\t\tCnDeckCardPicker,\n\t\tCnDeckCardCreate,\n\t\tCnStatusBadge,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'deck'`). */\n\t\tintegrationId: { type: String, default: 'deck' },\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 cards linked yet') },\n\t\t/** Pre-translated label for the \"Open Deck\" CTA. */\n\t\topenDeckLabel: { type: String, default: () => t('nextcloud-vue', 'Open Deck') },\n\t\t/** Pre-translated banner when Deck is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Deck is currently unavailable.') },\n\t\t/** URL of the NC Deck app entry. */\n\t\tdeckAppUrl: { type: String, default: '/index.php/apps/deck' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tcards: [],\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\tcomputed: {\n\t\t/**\n\t\t * Cards grouped by stack — preserves stack order via first-seen ordering.\n\t\t *\n\t\t * Each column is `{ key, label, cards: [] }`. The `key` is the\n\t\t * numeric stack id (or 'unstacked' for cards without one); the\n\t\t * `label` is the stack title when the provider returned one,\n\t\t * otherwise a placeholder.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tstackColumns() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const card of this.cards) {\n\t\t\t\tconst stackId = card.stackId ?? card.stack?.id ?? 'unstacked'\n\t\t\t\tconst stackLabel = card.stackTitle\n\t\t\t\t\t?? card.stack?.title\n\t\t\t\t\t?? (stackId === 'unstacked'\n\t\t\t\t\t\t? t('nextcloud-vue', 'No stack')\n\t\t\t\t\t\t: t('nextcloud-vue', 'Stack {n}', { n: stackId }))\n\t\t\t\tconst key = String(stackId)\n\t\t\t\tif (!groups.has(key)) {\n\t\t\t\t\tgroups.set(key, { key, label: stackLabel, cards: [] })\n\t\t\t\t}\n\t\t\t\tgroups.get(key).cards.push(card)\n\t\t\t}\n\t\t\treturn Array.from(groups.values())\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.fetchCards()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchCards()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchCards()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\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\t/**\n\t\t * Base for the Tier-2 deck endpoints (link/new). The provider's\n\t\t * generic `/integrations/deck` surface accepts the same POST\n\t\t * shape, but the bespoke Tier-2 routes are nicer for the\n\t\t * picker's `/deck/new` create-flow.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tdeckEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/deck`\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.deckEndpoint()), {\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.fetchCards()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This card is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link card.')\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('[CnDeckTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link card.')\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.deckEndpoint()}/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.fetchCards()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create card.')\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('[CnDeckTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create card.')\n\t\t\t}\n\t\t},\n\n\t\tcardKey(card) {\n\t\t\treturn card.cardId ?? card.id ?? ''\n\t\t},\n\n\t\tcardTitle(card) {\n\t\t\treturn card.cardTitle ?? card.title ?? this.cardKey(card)\n\t\t},\n\n\t\tcardUrl(card) {\n\t\t\tif (card.url && safeHref(card.url) !== '#') {\n\t\t\t\treturn card.url\n\t\t\t}\n\t\t\tconst boardId = card.boardId ?? card.board?.id ?? ''\n\t\t\tconst cardId = card.cardId ?? card.id ?? ''\n\t\t\tif (boardId && cardId) {\n\t\t\t\treturn `/index.php/apps/deck/board/${boardId}/card/${cardId}`\n\t\t\t}\n\t\t\treturn this.deckAppUrl\n\t\t},\n\n\t\tcardLabels(card) {\n\t\t\treturn cardLabels(card)\n\t\t},\n\n\t\tchipText(label) {\n\t\t\tif (typeof label === 'string') {\n\t\t\t\treturn label\n\t\t\t}\n\t\t\treturn label.title ?? label.name ?? ''\n\t\t},\n\n\t\tchipStyle(label) {\n\t\t\tif (typeof label === 'object' && label.color) {\n\t\t\t\tconst colour = String(label.color)\n\t\t\t\tconst fill = colour.startsWith('#') ? colour : `#${colour}`\n\t\t\t\treturn { background: fill, color: this.readableTextColour(fill) }\n\t\t\t}\n\t\t\treturn {}\n\t\t},\n\n\t\t/**\n\t\t * Pick black or white text for a hex background so Deck's coloured\n\t\t * label chips stay legible (per-channel luminance, ITU-R BT.601).\n\t\t *\n\t\t * @param {string} hex background colour, e.g. `#31cc7c`\n\t\t * @return {string} `#000000` or `#ffffff`\n\t\t */\n\t\treadableTextColour(hex) {\n\t\t\tconst clean = hex.replace('#', '')\n\t\t\tconst full = clean.length === 3\n\t\t\t\t? clean.split('').map((c) => c + c).join('')\n\t\t\t\t: clean\n\t\t\tif (full.length < 6) {\n\t\t\t\treturn '#ffffff'\n\t\t\t}\n\t\t\tconst r = parseInt(full.slice(0, 2), 16)\n\t\t\tconst g = parseInt(full.slice(2, 4), 16)\n\t\t\tconst b = parseInt(full.slice(4, 6), 16)\n\t\t\tconst luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255\n\t\t\treturn luminance > 0.6 ? '#000000' : '#ffffff'\n\t\t},\n\n\t\tisOverdue(card) {\n\t\t\tconst due = card.duedate ?? card.dueDate ?? null\n\t\t\tif (!due) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst date = new Date(due)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn date.getTime() < Date.now()\n\t\t},\n\n\t\tdueLabel(card) {\n\t\t\tconst due = card.duedate ?? card.dueDate ?? null\n\t\t\tif (!due) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst date = new Date(due)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn date.toLocaleDateString(userLocale(), { dateStyle: 'medium' })\n\t\t},\n\n\t\t/**\n\t\t * Status-badge variant for the due-date chip: red when overdue,\n\t\t * neutral otherwise — mirrors Deck's own overdue-date highlight.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} CnStatusBadge variant\n\t\t */\n\t\tdueVariant(card) {\n\t\t\treturn this.isOverdue(card) ? 'error' : 'default'\n\t\t},\n\n\t\t/**\n\t\t * Short board/stack context label rendered as a badge above the\n\t\t * title — the \"where does this card live\" signal a real Deck card\n\t\t * carries via its board colour bar + stack column.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} board (or board · stack) label, or '' when unknown\n\t\t */\n\t\tboardBadge(card) {\n\t\t\tconst board = cardBoardTitle(card)\n\t\t\tconst stack = cardStackTitle(card)\n\t\t\tif (board && stack) {\n\t\t\t\treturn `${board} · ${stack}`\n\t\t\t}\n\t\t\treturn board || stack || ''\n\t\t},\n\n\t\t/**\n\t\t * Normalised assignee list for a card. Accepts Deck's\n\t\t * `assignedUsers` (objects with a nested `participant`) as well as\n\t\t * a plain `assignees` array of strings/objects.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {Array} assignee entries\n\t\t */\n\t\tcardAssignees(card) {\n\t\t\treturn cardAssignees(card)\n\t\t},\n\n\t\t/**\n\t\t * The first {@link MAX_VISIBLE_ASSIGNEES} assignees to render as\n\t\t * avatars; the remainder collapse into a +N overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {Array} visible assignee entries\n\t\t */\n\t\tvisibleAssignees(card) {\n\t\t\treturn this.cardAssignees(card).slice(0, MAX_VISIBLE_ASSIGNEES)\n\t\t},\n\n\t\t/**\n\t\t * Count of assignees hidden behind the +N overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {number} overflow count (0 when all fit)\n\t\t */\n\t\tassigneeOverflow(card) {\n\t\t\tconst extra = this.cardAssignees(card).length - MAX_VISIBLE_ASSIGNEES\n\t\t\treturn extra > 0 ? extra : 0\n\t\t},\n\n\t\t/**\n\t\t * Tooltip listing the assignees collapsed into the overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} comma-separated overflow assignee names\n\t\t */\n\t\toverflowTitle(card) {\n\t\t\treturn this.cardAssignees(card)\n\t\t\t\t.slice(MAX_VISIBLE_ASSIGNEES)\n\t\t\t\t.map((a) => this.assigneeName(a))\n\t\t\t\t.join(', ')\n\t\t},\n\n\t\t/**\n\t\t * Unwrap an assignee entry to its participant payload — Deck nests\n\t\t * the user under `participant`; flat shapes pass through.\n\t\t *\n\t\t * @param {object|string} assignee raw assignee entry\n\t\t * @return {object} normalised participant object\n\t\t */\n\t\tassigneeParticipant(assignee) {\n\t\t\tif (typeof assignee === 'string') {\n\t\t\t\treturn { uid: assignee, displayname: assignee }\n\t\t\t}\n\t\t\treturn assignee.participant ?? assignee\n\t\t},\n\n\t\tassigneeName(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn p.displayname ?? p.displayName ?? p.uid ?? p.id ?? t('nextcloud-vue', 'Unknown')\n\t\t},\n\n\t\tassigneeSeed(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn String(p.uid ?? p.id ?? p.displayname ?? p.displayName ?? '')\n\t\t},\n\n\t\tassigneeKey(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn String(p.uid ?? p.id ?? p.displayname ?? p.displayName ?? Math.random())\n\t\t},\n\n\t\tasync fetchCards() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.cards = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.cards = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.cards = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load cards.')\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('[CnDeckTab] failed to fetch cards', err)\n\t\t\t\tthis.cards = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load cards.')\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-deck-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 8px;\n\tflex-wrap: wrap;\n}\n\n.cn-deck-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-deck-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-deck-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-deck-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-tab__kanban {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-deck-tab__column {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 8px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-deck-tab__column-header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tpadding: 0 2px;\n}\n\n.cn-deck-tab__column-title {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-tab__column-count {\n\tfont-size: 0.75em;\n\tcolor: var(--color-text-maxcontrast);\n\tbackground: var(--color-background-dark);\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n}\n\n.cn-deck-tab__column-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-deck-tab__card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 5px;\n\tpadding: 8px 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);\n\ttransition: box-shadow 0.1s ease-in-out, border-color 0.1s ease-in-out;\n}\n\n.cn-deck-tab__card:hover {\n\tbox-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);\n\tborder-color: var(--color-border-dark);\n}\n\n.cn-deck-tab__card--overdue {\n\tborder-color: var(--color-error);\n}\n\n.cn-deck-tab__card-board {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\talign-self: flex-start;\n\tmax-width: 100%;\n\tpadding: 1px 7px;\n\tfont-size: 0.7em;\n\tfont-weight: 500;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-tab__card-title {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\tfont-weight: 600;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\na.cn-deck-tab__card-title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-tab__card-labels {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n}\n\n/* Deck label chips: solid card-label colour with a readable text shade. */\n.cn-deck-tab__chip {\n\tdisplay: inline-block;\n\tmax-width: 100%;\n\tpadding: 1px 8px;\n\tfont-size: 0.7em;\n\tfont-weight: 500;\n\tline-height: 1.5;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-tab__card-footer {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\tmargin-top: 2px;\n}\n\n.cn-deck-tab__due-icon {\n\tdisplay: inline-flex;\n\tmargin-right: 2px;\n\tvertical-align: text-bottom;\n}\n\n.cn-deck-tab__avatars {\n\tdisplay: flex;\n\talign-items: center;\n\tmargin-left: auto;\n}\n\n.cn-deck-tab__avatar {\n\tborder: 2px solid var(--color-main-background);\n\tborder-radius: 50%;\n}\n\n.cn-deck-tab__avatar:not(:first-child) {\n\tmargin-left: -8px;\n}\n\n.cn-deck-tab__avatar-overflow {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 24px;\n\theight: 24px;\n\tmargin-left: -8px;\n\tborder: 2px solid var(--color-main-background);\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.65em;\n\tfont-weight: 600;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_createElementVNode","_createTextVNode","_toDisplayString","_renderList","_normalizeClass","_Fragment","_normalizeStyle","_createBlock"],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ * SPDX-License-Identifier: EUPL-1.2
4
+ *
5
+ * The parts of a linked Deck card a row shows, shared by the Deck tab and
6
+ * the Deck card: its board, its stack, its assignees and its labels.
7
+ */
8
+
9
+ /**
10
+ * @param {object} card The card row.
11
+ * @return {string} The board's title, or ''.
12
+ */
13
+ function cardBoardTitle(card) {
14
+ return card?.boardTitle ?? card?.board?.title ?? ''
15
+ }
16
+
17
+ /**
18
+ * @param {object} card The card row.
19
+ * @return {string} The stack's title, or ''.
20
+ */
21
+ function cardStackTitle(card) {
22
+ return card?.stackTitle ?? card?.stack?.title ?? ''
23
+ }
24
+
25
+ /**
26
+ * Deck's `assignedUsers` (each with a nested `participant`), or a plain
27
+ * `assignees` / `users` list of strings or objects.
28
+ *
29
+ * @param {object} card The card row.
30
+ * @return {Array} The assignee entries.
31
+ */
32
+ function cardAssignees(card) {
33
+ const raw = card?.assignedUsers ?? card?.assignees ?? card?.users ?? [];
34
+ return Array.isArray(raw) ? raw : []
35
+ }
36
+
37
+ /**
38
+ * @param {object|string} assignee An assignee entry.
39
+ * @return {string} Its display name, or ''.
40
+ */
41
+ function assigneeDisplayName(assignee) {
42
+ if (typeof assignee === 'string') {
43
+ return assignee
44
+ }
45
+ const p = assignee?.participant ?? assignee ?? {};
46
+ return p.displayname ?? p.displayName ?? p.uid ?? p.id ?? ''
47
+ }
48
+
49
+ /**
50
+ * @param {object} card The card row.
51
+ * @return {Array<object>} Its labels.
52
+ */
53
+ function cardLabels(card) {
54
+ const labels = card?.labels ?? card?.tags ?? [];
55
+ return Array.isArray(labels) ? labels : []
56
+ }
57
+
58
+ export { assigneeDisplayName, cardAssignees, cardBoardTitle, cardLabels, cardStackTitle };
59
+ //# sourceMappingURL=deckCardParts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deckCardParts.js","sources":["../../../../../src/integrations/builtin/deck/deckCardParts.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * The parts of a linked Deck card a row shows, shared by the Deck tab and\n * the Deck card: its board, its stack, its assignees and its labels.\n */\n\n/**\n * @param {object} card The card row.\n * @return {string} The board's title, or ''.\n */\nexport function cardBoardTitle(card) {\n\treturn card?.boardTitle ?? card?.board?.title ?? ''\n}\n\n/**\n * @param {object} card The card row.\n * @return {string} The stack's title, or ''.\n */\nexport function cardStackTitle(card) {\n\treturn card?.stackTitle ?? card?.stack?.title ?? ''\n}\n\n/**\n * Deck's `assignedUsers` (each with a nested `participant`), or a plain\n * `assignees` / `users` list of strings or objects.\n *\n * @param {object} card The card row.\n * @return {Array} The assignee entries.\n */\nexport function cardAssignees(card) {\n\tconst raw = card?.assignedUsers ?? card?.assignees ?? card?.users ?? []\n\treturn Array.isArray(raw) ? raw : []\n}\n\n/**\n * @param {object|string} assignee An assignee entry.\n * @return {string} Its display name, or ''.\n */\nexport function assigneeDisplayName(assignee) {\n\tif (typeof assignee === 'string') {\n\t\treturn assignee\n\t}\n\tconst p = assignee?.participant ?? assignee ?? {}\n\treturn p.displayname ?? p.displayName ?? p.uid ?? p.id ?? ''\n}\n\n/**\n * @param {object} card The card row.\n * @return {Array<object>} Its labels.\n */\nexport function cardLabels(card) {\n\tconst labels = card?.labels ?? card?.tags ?? []\n\treturn Array.isArray(labels) ? labels : []\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,cAAc,CAAC,IAAI,EAAE;AACrC,CAAC,OAAO,IAAI,EAAE,UAAU,IAAI,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE;AACpD,CAAC;AACD;AACA;AACA;AACA;AACA;AACO,SAAS,cAAc,CAAC,IAAI,EAAE;AACrC,CAAC,OAAO,IAAI,EAAE,UAAU,IAAI,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE;AACpD,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,aAAa,CAAC,IAAI,EAAE;AACpC,CAAC,MAAM,GAAG,GAAG,IAAI,EAAE,aAAa,IAAI,IAAI,EAAE,SAAS,IAAI,IAAI,EAAE,KAAK,IAAI,GAAE;AACxE,CAAC,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,EAAE;AACrC,CAAC;AACD;AACA;AACA;AACA;AACA;AACO,SAAS,mBAAmB,CAAC,QAAQ,EAAE;AAC9C,CAAC,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE;AACnC,EAAE,OAAO,QAAQ;AACjB,EAAE;AACF,CAAC,MAAM,CAAC,GAAG,QAAQ,EAAE,WAAW,IAAI,QAAQ,IAAI,GAAE;AAClD,CAAC,OAAO,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE;AAC7D,CAAC;AACD;AACA;AACA;AACA;AACA;AACO,SAAS,UAAU,CAAC,IAAI,EAAE;AACjC,CAAC,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,IAAI,IAAI,EAAE,IAAI,IAAI,GAAE;AAChD,CAAC,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,EAAE;AAC3C;;;;"}
@@ -1705,6 +1705,12 @@ var translations = {
1705
1705
  "expires {date}": "expires {date}",
1706
1706
  "Loading …": "Loading …",
1707
1707
  "Reorder {item}: drag, or press the up and down arrow keys": "Reorder {item}: drag, or press the up and down arrow keys",
1708
+ "Board {name}": "Board {name}",
1709
+ "list {name}": "list {name}",
1710
+ Tentative: "Tentative",
1711
+ Confirmed: "Confirmed",
1712
+ Cancelled: "Cancelled",
1713
+ "{start} until {end}": "{start} until {end}",
1708
1714
  "No contacts linked": "No contacts linked",
1709
1715
  "No relations yet": "No relations yet",
1710
1716
  Period: "Period",
@@ -1 +1 @@
1
- {"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1918,6 +1918,12 @@ var translations = {
1918
1918
  "Made {date}": "Gemaakt {date}",
1919
1919
  "expires {date}": "verloopt {date}",
1920
1920
  "Reorder {item}: drag, or press the up and down arrow keys": "{item} verplaatsen: slepen, of de pijltoetsen omhoog en omlaag",
1921
+ "Board {name}": "Bord {name}",
1922
+ "list {name}": "lijst {name}",
1923
+ Tentative: "Voorlopig",
1924
+ Confirmed: "Bevestigd",
1925
+ Cancelled: "Geannuleerd",
1926
+ "{start} until {end}": "{start} tot {end}",
1921
1927
  "No contacts linked": "Geen contacten gekoppeld",
1922
1928
  "No relations yet": "Nog geen relaties",
1923
1929
  Period: "Periode",
@@ -1 +1 @@
1
- {"version":3,"file":"nl.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"nl.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}