@conduction/nextcloud-vue 2.141.0 → 2.142.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
  2. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +13 -0
  3. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  4. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +3 -3
  5. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  6. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +21 -0
  7. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  8. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +1 -1
  9. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  10. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  11. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +34 -3
  12. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  13. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  14. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  15. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +43 -3
  16. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  17. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +1 -1
  18. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  19. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +21 -1
  20. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  21. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +2 -1
  22. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  23. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +14 -0
  24. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  25. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
  26. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +3 -1
  27. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  28. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +2 -1
  29. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  30. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js +1 -1
  31. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js +31 -1
  32. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js.map +1 -1
  33. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js.map +1 -1
  34. package/dist/esm/integrations/builtin/contacts/CnContactsTab.vue.js +1 -1
  35. package/dist/esm/integrations/builtin/contacts/CnContactsTab.vue2.js +52 -1
  36. package/dist/esm/integrations/builtin/contacts/CnContactsTab.vue2.js.map +1 -1
  37. package/dist/esm/integrations/builtin/contacts/CnContactsTab.vue3.js +119 -65
  38. package/dist/esm/integrations/builtin/contacts/CnContactsTab.vue3.js.map +1 -1
  39. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue.js +1 -1
  40. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +86 -4
  41. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
  42. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js +67 -5
  43. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
  44. package/dist/esm/l10n/en.json.js +10 -1
  45. package/dist/esm/l10n/en.json.js.map +1 -1
  46. package/dist/esm/l10n/nl.json.js +11 -2
  47. package/dist/esm/l10n/nl.json.js.map +1 -1
  48. package/dist/esm/library-style.js +1 -1
  49. package/dist/esm/nextcloud-vue.css +527 -358
  50. package/dist/esm/utils/buildViewTree.js +23 -8
  51. package/dist/esm/utils/buildViewTree.js.map +1 -1
  52. package/dist/nextcloud-vue.cjs.js +1064 -616
  53. package/dist/nextcloud-vue.cjs.js.map +2 -2
  54. package/dist/nextcloud-vue.css +527 -358
  55. package/l10n/en.json +10 -1
  56. package/l10n/nl.json +11 -2
  57. package/package.json +1 -1
  58. package/src/components/CnCellRenderer/CnCellRenderer.vue +15 -1
  59. package/src/components/CnDataTable/CnDataTable.md +1 -0
  60. package/src/components/CnDataTable/CnDataTable.vue +22 -1
  61. package/src/components/CnFactsWidget/CnFactsWidget.vue +34 -3
  62. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +44 -4
  63. package/src/components/CnIndexPage/CnIndexPage.vue +22 -1
  64. package/src/components/CnIndexPage/useSelfFetchList.js +14 -0
  65. package/src/components/CnObjectSidebar/CnFilesTab.vue +4 -1
  66. package/src/components/CnPartyPicker/CnPartyPicker.vue +31 -1
  67. package/src/css/look-board-detail.css +95 -0
  68. package/src/css/look-board-index.css +53 -0
  69. package/src/css/look-board.css +17 -0
  70. package/src/integrations/builtin/contacts/CnContactsTab.vue +101 -26
  71. package/src/integrations/builtin/contacts/__tests__/CnContactsTabBoardRows.spec.js +67 -0
  72. package/src/integrations/builtin/deck/CnDeckCard.vue +121 -2
  73. package/src/integrations/builtin/deck/__tests__/CnDeckCardHeaderActions.spec.js +68 -0
  74. package/src/utils/buildViewTree.js +23 -8
@@ -3606,7 +3606,16 @@ var translations$s = {
3606
3606
  "Open profile": "Open profile",
3607
3607
  "Link a contact from your address book.": "Link a contact from your address book.",
3608
3608
  "The value could not be shown.": "The value could not be shown.",
3609
- "No value": "No value"
3609
+ "No value": "No value",
3610
+ "Link an existing card": "Link an existing card",
3611
+ "New card": "New card",
3612
+ "Could not link card.": "Could not link card.",
3613
+ "Could not create card.": "Could not create card.",
3614
+ "This card is already linked.": "This card is already linked.",
3615
+ "From {date}": "From {date}",
3616
+ "Until {date}": "Until {date}",
3617
+ "More actions for {name}": "More actions for {name}",
3618
+ "Unlink contact": "Unlink contact"
3610
3619
  };
3611
3620
  var plurals$1 = {
3612
3621
  "{count} member": [
@@ -5977,7 +5986,7 @@ var translations$c = {
5977
5986
  "Save current view": "Huidige weergave opslaan",
5978
5987
  "Share view: {name}": "Weergave delen: {name}",
5979
5988
  "The view \"{name}\" leaves your list and the menu on the left.": "De weergave \"{name}\" verdwijnt uit uw lijst en uit het menu links.",
5980
- "Whoever gets the view sees it under Shared with me. It is yours and stays yours.": "Wie de weergave krijgt, ziet die onder Gedeeld met mij. Die is van u en blijft dat.",
5989
+ "Whoever gets the view sees it under Shared with me. It is yours and stays yours.": "Wie de weergave krijgt, ziet hem onder Gedeeld met mij. Hij is van u en blijft dat.",
5981
5990
  "Whoever you share the view with sees the change at once.": "Iedereen met wie u de weergave deelt, ziet de wijziging meteen.",
5982
5991
  Reason: "Reden",
5983
5992
  "Could not be read": "Kon niet worden gelezen",
@@ -6623,7 +6632,16 @@ var translations$c = {
6623
6632
  "Save as draft": "Opslaan als concept",
6624
6633
  "Draft saved at {time}": "Concept opgeslagen om {time}",
6625
6634
  "The draft could not be saved": "Het concept kon niet worden opgeslagen",
6626
- "No value": "Geen waarde"
6635
+ "No value": "Geen waarde",
6636
+ "Link an existing card": "Bestaande kaart koppelen",
6637
+ "New card": "Nieuwe kaart",
6638
+ "Could not link card.": "De kaart kon niet worden gekoppeld.",
6639
+ "Could not create card.": "De kaart kon niet worden gemaakt.",
6640
+ "This card is already linked.": "Deze kaart is al gekoppeld.",
6641
+ "From {date}": "Vanaf {date}",
6642
+ "Until {date}": "Tot {date}",
6643
+ "More actions for {name}": "Meer acties voor {name}",
6644
+ "Unlink contact": "Contact ontkoppelen"
6627
6645
  };
6628
6646
  var plurals = {
6629
6647
  "{count} member": [
@@ -10507,7 +10525,7 @@ const _hoisted_20$1g = {
10507
10525
  "data-testid": "cn-ai-thinking"
10508
10526
  };
10509
10527
  const _hoisted_21$1e = ["aria-label"];
10510
- const _hoisted_22$1b = { class: "cn-ai-message-list__thinking-text" };
10528
+ const _hoisted_22$1c = { class: "cn-ai-message-list__thinking-text" };
10511
10529
 
10512
10530
  function render$8E(_ctx, _cache, $props, $setup, $data, $options) {
10513
10531
  const _component_NcRichText = vue$1.resolveComponent("NcRichText");
@@ -10646,7 +10664,7 @@ function render$8E(_ctx, _cache, $props, $setup, $data, $options) {
10646
10664
  role: "status",
10647
10665
  "aria-live": "polite"
10648
10666
  }, [
10649
- vue$1.createElementVNode("span", _hoisted_22$1b, vue$1.toDisplayString($options.cnTranslate('Thinking')), 1 /* TEXT */),
10667
+ vue$1.createElementVNode("span", _hoisted_22$1c, vue$1.toDisplayString($options.cnTranslate('Thinking')), 1 /* TEXT */),
10650
10668
  _cache[0] || (_cache[0] = vue$1.createElementVNode("span", {
10651
10669
  class: "cn-ai-message-list__thinking-dots",
10652
10670
  "aria-hidden": "true"
@@ -26819,7 +26837,7 @@ const _hoisted_20$1e = {
26819
26837
  "data-testid": "cn-chart-widget-footnote"
26820
26838
  };
26821
26839
  const _hoisted_21$1c = { class: "cn-chart-widget__notice-text" };
26822
- const _hoisted_22$1a = { key: 0 };
26840
+ const _hoisted_22$1b = { key: 0 };
26823
26841
 
26824
26842
  function render$8y(_ctx, _cache, $props, $setup, $data, $options) {
26825
26843
  const _component_CnEndpointNotice = vue$1.resolveComponent("CnEndpointNotice");
@@ -26992,7 +27010,7 @@ function render$8y(_ctx, _cache, $props, $setup, $data, $options) {
26992
27010
  default: vue$1.withCtx(() => [
26993
27011
  vue$1.createElementVNode("p", _hoisted_21$1c, [
26994
27012
  ($options.noticeParts.lead)
26995
- ? (vue$1.openBlock(), vue$1.createElementBlock("strong", _hoisted_22$1a, vue$1.toDisplayString($options.noticeParts.lead), 1 /* TEXT */))
27013
+ ? (vue$1.openBlock(), vue$1.createElementBlock("strong", _hoisted_22$1b, vue$1.toDisplayString($options.noticeParts.lead), 1 /* TEXT */))
26996
27014
  : vue$1.createCommentVNode("v-if", true),
26997
27015
  vue$1.createTextVNode(vue$1.toDisplayString($options.noticeParts.lead && $options.noticeParts.text ? ' ' : '') + vue$1.toDisplayString($options.noticeParts.text), 1 /* TEXT */)
26998
27016
  ])
@@ -30101,12 +30119,12 @@ const _hoisted_21$1b = {
30101
30119
  class: "cn-conversation-thread__note",
30102
30120
  "data-testid": "cn-conversation-note"
30103
30121
  };
30104
- const _hoisted_22$19 = {
30122
+ const _hoisted_22$1a = {
30105
30123
  key: 1,
30106
30124
  class: "cn-conversation-thread__message-actions"
30107
30125
  };
30108
- const _hoisted_23$_ = { class: "cn-conversation-thread__reply-head" };
30109
- const _hoisted_24$U = ["for"];
30126
+ const _hoisted_23$$ = { class: "cn-conversation-thread__reply-head" };
30127
+ const _hoisted_24$V = ["for"];
30110
30128
  const _hoisted_25$N = {
30111
30129
  key: 0,
30112
30130
  class: "cn-conversation-thread__reply-hint",
@@ -30209,7 +30227,7 @@ function render$8q(_ctx, _cache, $props, $setup, $data, $options) {
30209
30227
  ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_21$1b, vue$1.toDisplayString(row.note), 1 /* TEXT */))
30210
30228
  : vue$1.createCommentVNode("v-if", true),
30211
30229
  (row.actions.length > 0)
30212
- ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_22$19, [
30230
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_22$1a, [
30213
30231
  (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList(row.actions, (action) => {
30214
30232
  return (vue$1.openBlock(), vue$1.createBlock(_component_NcButton, {
30215
30233
  key: action.id,
@@ -30238,11 +30256,11 @@ function render$8q(_ctx, _cache, $props, $setup, $data, $options) {
30238
30256
  onSubmit: _cache[4] || (_cache[4] = vue$1.withModifiers((...args) => ($options.onSend && $options.onSend(...args)), ["prevent"]))
30239
30257
  }, [
30240
30258
  vue$1.renderSlot(_ctx.$slots, "reply-tools", { setDraft: $options.setDraft }),
30241
- vue$1.createElementVNode("div", _hoisted_23$_, [
30259
+ vue$1.createElementVNode("div", _hoisted_23$$, [
30242
30260
  vue$1.createElementVNode("label", {
30243
30261
  for: $data.inputId,
30244
30262
  class: "cn-conversation-thread__reply-label"
30245
- }, vue$1.toDisplayString($props.replyLabel), 9 /* TEXT, PROPS */, _hoisted_24$U),
30263
+ }, vue$1.toDisplayString($props.replyLabel), 9 /* TEXT, PROPS */, _hoisted_24$V),
30246
30264
  ($props.replyHint)
30247
30265
  ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_25$N, vue$1.toDisplayString($props.replyHint), 1 /* TEXT */))
30248
30266
  : vue$1.createCommentVNode("v-if", true)
@@ -42901,8 +42919,8 @@ const _hoisted_21$1a = {
42901
42919
  key: 3,
42902
42920
  class: "cn-column-filter__field"
42903
42921
  };
42904
- const _hoisted_22$18 = { class: "cn-column-filter__label" };
42905
- const _hoisted_23$Z = { class: "cn-column-filter__actions" };
42922
+ const _hoisted_22$19 = { class: "cn-column-filter__label" };
42923
+ const _hoisted_23$_ = { class: "cn-column-filter__actions" };
42906
42924
 
42907
42925
  function render$87(_ctx, _cache, $props, $setup, $data, $options) {
42908
42926
  return (vue$1.openBlock(), vue$1.createBlock(vue$1.Teleport, {
@@ -43014,7 +43032,7 @@ function render$87(_ctx, _cache, $props, $setup, $data, $options) {
43014
43032
  ])
43015
43033
  ]))
43016
43034
  : (vue$1.openBlock(), vue$1.createElementBlock("label", _hoisted_21$1a, [
43017
- vue$1.createElementVNode("span", _hoisted_22$18, vue$1.toDisplayString($options.containsLabel), 1 /* TEXT */),
43035
+ vue$1.createElementVNode("span", _hoisted_22$19, vue$1.toDisplayString($options.containsLabel), 1 /* TEXT */),
43018
43036
  vue$1.withDirectives(vue$1.createElementVNode("input", {
43019
43037
  "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => (($data.draft.value) = $event)),
43020
43038
  class: "cn-column-filter__input",
@@ -43024,7 +43042,7 @@ function render$87(_ctx, _cache, $props, $setup, $data, $options) {
43024
43042
  [vue$1.vModelText, $data.draft.value]
43025
43043
  ])
43026
43044
  ])),
43027
- vue$1.createElementVNode("div", _hoisted_23$Z, [
43045
+ vue$1.createElementVNode("div", _hoisted_23$_, [
43028
43046
  vue$1.createElementVNode("button", {
43029
43047
  type: "button",
43030
43048
  class: "cn-column-filter__button",
@@ -45202,6 +45220,19 @@ var script$85 = {
45202
45220
  * @return {number}
45203
45221
  * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells
45204
45222
  */
45223
+ /**
45224
+ * `avatar` widget: the chip's tone class in the board look,
45225
+ * `widgetProps.tone` `neutral` (DqContacten's grey chip) or the
45226
+ * default light primary.
45227
+ *
45228
+ * @return {string}
45229
+ * @spec openspec/changes/board-avatar-chip/specs/data-table/spec.md#requirement-a-board-avatar-cell-draws-the-initials-chip
45230
+ */
45231
+ avatarToneClass() {
45232
+ const tone = this.widgetProps && this.widgetProps.tone;
45233
+ return tone === 'neutral' ? 'cn-cell-renderer__avatar-initials--neutral' : ''
45234
+ },
45235
+
45205
45236
  avatarSize() {
45206
45237
  const size = Number(this.widgetProps && this.widgetProps.size);
45207
45238
  return Number.isFinite(size) && size > 0 ? size : (this.isBoardLook ? 32 : 24)
@@ -46336,13 +46367,13 @@ const _hoisted_21$19 = {
46336
46367
  key: 0,
46337
46368
  class: "cn-cell-renderer__avatar"
46338
46369
  };
46339
- const _hoisted_22$17 = {
46370
+ const _hoisted_22$18 = {
46340
46371
  key: 0,
46341
46372
  class: "cn-cell-renderer__avatar-picture",
46342
46373
  "aria-hidden": "true"
46343
46374
  };
46344
- const _hoisted_23$Y = { class: "cn-cell-renderer__avatar-text" };
46345
- const _hoisted_24$T = ["title"];
46375
+ const _hoisted_23$Z = { class: "cn-cell-renderer__avatar-text" };
46376
+ const _hoisted_24$U = ["title"];
46346
46377
  const _hoisted_25$M = {
46347
46378
  key: 0,
46348
46379
  class: "cn-cell-renderer__avatar-secondary",
@@ -46650,8 +46681,8 @@ function render$84(_ctx, _cache, $props, $setup, $data, $options) {
46650
46681
  ? (vue$1.openBlock(), vue$1.createElementBlock(vue$1.Fragment, { key: 10 }, [
46651
46682
  ($options.avatarName || $options.avatarUserId)
46652
46683
  ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_21$19, [
46653
- ($options.avatarUserId)
46654
- ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_22$17, [
46684
+ ($options.avatarUserId && !$options.isBoardLook)
46685
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_22$18, [
46655
46686
  vue$1.createVNode(_component_NcAvatar, {
46656
46687
  user: $options.avatarUserId,
46657
46688
  displayName: $options.avatarName || $options.avatarUserId,
@@ -46662,15 +46693,15 @@ function render$84(_ctx, _cache, $props, $setup, $data, $options) {
46662
46693
  ]))
46663
46694
  : (vue$1.openBlock(), vue$1.createElementBlock("span", {
46664
46695
  key: 1,
46665
- class: "cn-cell-renderer__avatar-initials",
46696
+ class: vue$1.normalizeClass(["cn-cell-renderer__avatar-initials", $options.avatarToneClass]),
46666
46697
  style: vue$1.normalizeStyle({ width: $options.avatarSize + 'px', height: $options.avatarSize + 'px' }),
46667
46698
  "aria-hidden": "true"
46668
- }, vue$1.toDisplayString($options.avatarInitials), 5 /* TEXT, STYLE */)),
46669
- vue$1.createElementVNode("span", _hoisted_23$Y, [
46699
+ }, vue$1.toDisplayString($options.avatarInitials), 7 /* TEXT, CLASS, STYLE */)),
46700
+ vue$1.createElementVNode("span", _hoisted_23$Z, [
46670
46701
  vue$1.createElementVNode("span", {
46671
46702
  class: "cn-cell-renderer__avatar-name",
46672
46703
  title: $options.avatarName || $options.avatarUserId
46673
- }, vue$1.toDisplayString($options.avatarName || $options.avatarUserId), 9 /* TEXT, PROPS */, _hoisted_24$T),
46704
+ }, vue$1.toDisplayString($options.avatarName || $options.avatarUserId), 9 /* TEXT, PROPS */, _hoisted_24$U),
46674
46705
  ($props.secondary)
46675
46706
  ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_25$M, vue$1.toDisplayString($props.secondary), 1 /* TEXT */))
46676
46707
  : vue$1.createCommentVNode("v-if", true)
@@ -46934,7 +46965,7 @@ function render$84(_ctx, _cache, $props, $setup, $data, $options) {
46934
46965
  }
46935
46966
 
46936
46967
  script$85.render = render$84;
46937
- script$85.__scopeId = "data-v-abd08d9e";
46968
+ script$85.__scopeId = "data-v-16a6d8b8";
46938
46969
  const __sfc_main$7$ = script$85;
46939
46970
 
46940
46971
  /**
@@ -47266,6 +47297,17 @@ var script$82 = {
47266
47297
  default: null,
47267
47298
  },
47268
47299
 
47300
+ /**
47301
+ * The tone of the leading icon's tile in the board look: `primary`
47302
+ * (default), `teal` or `neutral` (DqOrganisaties draws a teal tile).
47303
+ *
47304
+ * @type {string}
47305
+ */
47306
+ rowIconTone: {
47307
+ type: String,
47308
+ default: 'primary',
47309
+ },
47310
+
47269
47311
  /**
47270
47312
  * State indicators a page declares for its rows. Each entry is
47271
47313
  * `{ id, field, equals?, in?, icon, text, tooltip? }`: `field` is a
@@ -47792,6 +47834,16 @@ var script$82 = {
47792
47834
  },
47793
47835
 
47794
47836
  computed: {
47837
+ /**
47838
+ * The leading icon tile's tone, one the board look draws.
47839
+ *
47840
+ * @return {string} `primary`, `teal` or `neutral`.
47841
+ * @spec openspec/changes/board-row-icon-tile/specs/data-table/spec.md#requirement-a-board-tables-leading-icon-sits-on-a-tile
47842
+ */
47843
+ iconTone() {
47844
+ return ['primary', 'teal', 'neutral'].includes(this.rowIconTone) ? this.rowIconTone : 'primary'
47845
+ },
47846
+
47795
47847
  /**
47796
47848
  * Whether the table is drawn as the board look's white card.
47797
47849
  *
@@ -49435,15 +49487,15 @@ const _hoisted_21$18 = {
49435
49487
  class: "cn-table-header__filter-dot",
49436
49488
  "data-testid": "cn-table-header-filter-dot"
49437
49489
  };
49438
- const _hoisted_22$16 = {
49490
+ const _hoisted_22$17 = {
49439
49491
  key: 2,
49440
49492
  class: "cn-table-col--actions"
49441
49493
  };
49442
- const _hoisted_23$X = {
49494
+ const _hoisted_23$Y = {
49443
49495
  key: 0,
49444
49496
  class: "hidden-visually"
49445
49497
  };
49446
- const _hoisted_24$S = {
49498
+ const _hoisted_24$T = {
49447
49499
  key: 0,
49448
49500
  class: "cn-table-empty",
49449
49501
  "data-testid": "cn-object-list-empty"
@@ -49704,9 +49756,9 @@ function render$81(_ctx, _cache, $props, $setup, $data, $options) {
49704
49756
  ], 46 /* CLASS, STYLE, PROPS, NEED_HYDRATION */, _hoisted_15$21))
49705
49757
  }), 128 /* KEYED_FRAGMENT */)),
49706
49758
  (_ctx.$slots['row-actions'])
49707
- ? (vue$1.openBlock(), vue$1.createElementBlock("th", _hoisted_22$16, [
49759
+ ? (vue$1.openBlock(), vue$1.createElementBlock("th", _hoisted_22$17, [
49708
49760
  ($options.isBoardLook)
49709
- ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_23$X, vue$1.toDisplayString($options.actionsColumnLabel), 1 /* TEXT */))
49761
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_23$Y, vue$1.toDisplayString($options.actionsColumnLabel), 1 /* TEXT */))
49710
49762
  : vue$1.createCommentVNode("v-if", true),
49711
49763
  vue$1.renderSlot(_ctx.$slots, "actions-header")
49712
49764
  ]))
@@ -49715,7 +49767,7 @@ function render$81(_ctx, _cache, $props, $setup, $data, $options) {
49715
49767
  ])),
49716
49768
  vue$1.createElementVNode("tbody", null, [
49717
49769
  ($options.effectiveRows.length === 0)
49718
- ? (vue$1.openBlock(), vue$1.createElementBlock("tr", _hoisted_24$S, [
49770
+ ? (vue$1.openBlock(), vue$1.createElementBlock("tr", _hoisted_24$T, [
49719
49771
  vue$1.createElementVNode("td", { colspan: $options.totalColumns }, [
49720
49772
  vue$1.renderSlot(_ctx.$slots, "empty", {}, () => [
49721
49773
  ($options.lensReasonText)
@@ -49770,7 +49822,7 @@ function render$81(_ctx, _cache, $props, $setup, $data, $options) {
49770
49822
  ($props.rowIcon)
49771
49823
  ? (vue$1.openBlock(), vue$1.createElementBlock("td", {
49772
49824
  key: 1,
49773
- class: vue$1.normalizeClass(["cn-table-col--icon", $options.pinClass($props.selectable ? 1 : 0)]),
49825
+ class: vue$1.normalizeClass(["cn-table-col--icon", [$options.pinClass($props.selectable ? 1 : 0), 'cn-table-col--icon-' + $options.iconTone]]),
49774
49826
  style: vue$1.normalizeStyle($options.pinStyle($props.selectable ? 1 : 0))
49775
49827
  }, [
49776
49828
  vue$1.createVNode(_component_CnIcon, {
@@ -85550,13 +85602,13 @@ const _hoisted_21$17 = {
85550
85602
  key: 0,
85551
85603
  class: "cn-icon-browser-panel__hint"
85552
85604
  };
85553
- const _hoisted_22$15 = {
85605
+ const _hoisted_22$16 = {
85554
85606
  key: 1,
85555
85607
  class: "cn-icon-browser-panel__error",
85556
85608
  role: "alert"
85557
85609
  };
85558
- const _hoisted_23$W = ["placeholder", "aria-label"];
85559
- const _hoisted_24$R = {
85610
+ const _hoisted_23$X = ["placeholder", "aria-label"];
85611
+ const _hoisted_24$S = {
85560
85612
  key: 3,
85561
85613
  class: "cn-icon-browser-panel__grid"
85562
85614
  };
@@ -85741,7 +85793,7 @@ function render$7S(_ctx, _cache, $props, $setup, $data, $options) {
85741
85793
  ($data.groupLoading[group.key])
85742
85794
  ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_21$17, vue$1.toDisplayString($options.t('nextcloud-vue', 'Loading icon set…')), 1 /* TEXT */))
85743
85795
  : ($data.groupError[group.key])
85744
- ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_22$15, vue$1.toDisplayString($data.groupError[group.key]), 1 /* TEXT */))
85796
+ ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_22$16, vue$1.toDisplayString($data.groupError[group.key]), 1 /* TEXT */))
85745
85797
  : vue$1.createCommentVNode("v-if", true),
85746
85798
  (group.icons.length > $props.maxResults)
85747
85799
  ? vue$1.withDirectives((vue$1.openBlock(), vue$1.createElementBlock("input", {
@@ -85751,12 +85803,12 @@ function render$7S(_ctx, _cache, $props, $setup, $data, $options) {
85751
85803
  class: "cn-icon-browser-panel__search",
85752
85804
  placeholder: $options.t('nextcloud-vue', 'Search icons…'),
85753
85805
  "aria-label": $options.t('nextcloud-vue', 'Search icons')
85754
- }, null, 8 /* PROPS */, _hoisted_23$W)), [
85806
+ }, null, 8 /* PROPS */, _hoisted_23$X)), [
85755
85807
  [vue$1.vModelText, $data.customQuery]
85756
85808
  ])
85757
85809
  : vue$1.createCommentVNode("v-if", true),
85758
85810
  ($options.customVisibleIcons.length > 0)
85759
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_24$R, [
85811
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_24$S, [
85760
85812
  (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($options.customVisibleIcons, (icon) => {
85761
85813
  return (vue$1.openBlock(), vue$1.createElementBlock("button", {
85762
85814
  key: icon.id || icon.url,
@@ -88435,7 +88487,9 @@ const SEARCH_DELAY$3 = 250;
88435
88487
  * Organisatie · KvK …"). Built on CnSearchSelect.
88436
88488
  *
88437
88489
  * `sources`: each `{ id, register, schema, label?, title?, secondary?,
88438
- * limit?, icon? }` searches OpenRegister with `_search`; `title` and
88490
+ * limit?, icon?, lookup? }` searches OpenRegister with `_search`; `lookup`
88491
+ * (`{ url, when }`) sends a term matching `when` to the app's exact lookup
88492
+ * instead (a POST of `{ term }` answering `{ results }`); `title` and
88439
88493
  * `secondary` are templates filled from the row (default `{name}` / none),
88440
88494
  * `label` names the kind ("Resident") and starts the second line; `label`
88441
88495
  * and `secondary` go through the host translate, so a manifest can write
@@ -88667,6 +88721,10 @@ var script$7Q = {
88667
88721
  const { data } = await axios$1.post(router.generateUrl('/contactsmenu/contacts'), { filter: term });
88668
88722
  return (data && Array.isArray(data.contacts) ? data.contacts : []).map((c) => this.contactOptionOf(source, c))
88669
88723
  }
88724
+ if (this.isExactLookup(source, term) === true) {
88725
+ const { data } = await axios$1.post(router.generateUrl(source.lookup.url), { term });
88726
+ return (data && Array.isArray(data.results) ? data.results : []).map((row) => this.optionOf(source, row))
88727
+ }
88670
88728
  if (!source.register || !source.schema) {
88671
88729
  return []
88672
88730
  }
@@ -88694,6 +88752,30 @@ var script$7Q = {
88694
88752
  this.searching = false;
88695
88753
  },
88696
88754
 
88755
+ /**
88756
+ * Whether a typed term goes to the source's exact lookup instead of the
88757
+ * free-text search: `source.lookup` is `{ url, when }`, `when` a regular
88758
+ * expression the whole term must match ("^[0-9]{9}$" for a BSN). The
88759
+ * lookup is a POST, so the term never sits in a URL or a server log line,
88760
+ * and the app behind it may check and record who looked whom up.
88761
+ *
88762
+ * @param {object} source The source.
88763
+ * @param {string} term The typed term.
88764
+ * @return {boolean}
88765
+ * @spec openspec/changes/party-picker-exact-lookup/specs/party-picker/spec.md#requirement-a-party-source-can-look-a-term-up-exactly
88766
+ */
88767
+ isExactLookup(source, term) {
88768
+ const lookup = source && source.lookup;
88769
+ if (!lookup || typeof lookup.url !== 'string' || lookup.url === '' || typeof lookup.when !== 'string') {
88770
+ return false
88771
+ }
88772
+ try {
88773
+ return new RegExp(lookup.when).test(term)
88774
+ } catch {
88775
+ return false
88776
+ }
88777
+ },
88778
+
88697
88779
  /**
88698
88780
  * Name a value the list does not hold: the first OpenRegister source
88699
88781
  * that has an object with that id.
@@ -88775,7 +88857,7 @@ function render$7P(_ctx, _cache, $props, $setup, $data, $options) {
88775
88857
  }
88776
88858
 
88777
88859
  script$7Q.render = render$7P;
88778
- script$7Q.__scopeId = "data-v-44764194";
88860
+ script$7Q.__scopeId = "data-v-1b4d732c";
88779
88861
  const __sfc_main$7K = script$7Q;
88780
88862
 
88781
88863
  const BASE = '/apps/integriq/api/property-sources';
@@ -95713,7 +95795,7 @@ const _hoisted_18$1n = { class: "cn-activity-card__day-count" };
95713
95795
  const _hoisted_19$1h = { class: "cn-activity-card__list" };
95714
95796
  const _hoisted_20$18 = { class: "cn-activity-card__row-main" };
95715
95797
  const _hoisted_21$16 = { class: "cn-activity-card__row-subject" };
95716
- const _hoisted_22$14 = { class: "cn-activity-card__row-meta" };
95798
+ const _hoisted_22$15 = { class: "cn-activity-card__row-meta" };
95717
95799
 
95718
95800
  function render$7I(_ctx, _cache, $props, $setup, $data, $options) {
95719
95801
  const _component_NcLoadingIcon = vue$1.resolveComponent("NcLoadingIcon");
@@ -95793,7 +95875,7 @@ function render$7I(_ctx, _cache, $props, $setup, $data, $options) {
95793
95875
  (vue$1.openBlock(), vue$1.createBlock(vue$1.resolveDynamicComponent($options.iconFor(entry)), { size: 14 })),
95794
95876
  vue$1.createElementVNode("div", _hoisted_20$18, [
95795
95877
  vue$1.createElementVNode("span", _hoisted_21$16, vue$1.toDisplayString($options.subjectFor(entry)), 1 /* TEXT */),
95796
- vue$1.createElementVNode("span", _hoisted_22$14, vue$1.toDisplayString($options.actorFor(entry)) + " · " + vue$1.toDisplayString($options.relativeTime(entry)), 1 /* TEXT */),
95878
+ vue$1.createElementVNode("span", _hoisted_22$15, vue$1.toDisplayString($options.actorFor(entry)) + " · " + vue$1.toDisplayString($options.relativeTime(entry)), 1 /* TEXT */),
95797
95879
  ($props.showVisibility)
95798
95880
  ? (vue$1.openBlock(), vue$1.createBlock(_component_CnVisibilityChip, {
95799
95881
  key: 0,
@@ -96702,12 +96784,12 @@ const _hoisted_21$15 = {
96702
96784
  class: "cn-activity-tab__visibility-fixed",
96703
96785
  "data-testid": "cn-activity-visibility-fixed"
96704
96786
  };
96705
- const _hoisted_22$13 = {
96787
+ const _hoisted_22$14 = {
96706
96788
  key: 1,
96707
96789
  class: "cn-activity-tab__filter"
96708
96790
  };
96709
- const _hoisted_23$V = { class: "cn-activity-tab__filter-label" };
96710
- const _hoisted_24$Q = { value: "" };
96791
+ const _hoisted_23$W = { class: "cn-activity-tab__filter-label" };
96792
+ const _hoisted_24$R = { value: "" };
96711
96793
  const _hoisted_25$J = { value: "internal" };
96712
96794
  const _hoisted_26$D = { value: "public" };
96713
96795
  const _hoisted_27$u = {
@@ -96905,15 +96987,15 @@ function render$7H(_ctx, _cache, $props, $setup, $data, $options) {
96905
96987
  vue$1.createVNode(_component_CnVisibilityChip, { visibility: "public" }),
96906
96988
  vue$1.createTextVNode(" " + vue$1.toDisplayString($data.publicOnlyLabel), 1 /* TEXT */)
96907
96989
  ]))
96908
- : (vue$1.openBlock(), vue$1.createElementBlock("label", _hoisted_22$13, [
96909
- vue$1.createElementVNode("span", _hoisted_23$V, vue$1.toDisplayString($data.visibilityLabel), 1 /* TEXT */),
96990
+ : (vue$1.openBlock(), vue$1.createElementBlock("label", _hoisted_22$14, [
96991
+ vue$1.createElementVNode("span", _hoisted_23$W, vue$1.toDisplayString($data.visibilityLabel), 1 /* TEXT */),
96910
96992
  vue$1.withDirectives(vue$1.createElementVNode("select", {
96911
96993
  "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => (($data.selectedVisibility) = $event)),
96912
96994
  class: "cn-activity-tab__select",
96913
96995
  "data-testid": "cn-activity-visibility-select",
96914
96996
  onChange: _cache[6] || (_cache[6] = (...args) => ($options.resetAndFetch && $options.resetAndFetch(...args)))
96915
96997
  }, [
96916
- vue$1.createElementVNode("option", _hoisted_24$Q, vue$1.toDisplayString($data.allVisibilityLabel), 1 /* TEXT */),
96998
+ vue$1.createElementVNode("option", _hoisted_24$R, vue$1.toDisplayString($data.allVisibilityLabel), 1 /* TEXT */),
96917
96999
  vue$1.createElementVNode("option", _hoisted_25$J, vue$1.toDisplayString($data.internalLabel), 1 /* TEXT */),
96918
97000
  vue$1.createElementVNode("option", _hoisted_26$D, vue$1.toDisplayString($data.publicLabel), 1 /* TEXT */)
96919
97001
  ], 544 /* NEED_HYDRATION, NEED_PATCH */), [
@@ -100140,7 +100222,7 @@ const _hoisted_20$16 = {
100140
100222
  class: "cn-audit-details__values"
100141
100223
  };
100142
100224
  const _hoisted_21$14 = { class: "cn-audit-details__mono" };
100143
- const _hoisted_22$12 = {
100225
+ const _hoisted_22$13 = {
100144
100226
  key: 2,
100145
100227
  class: "cn-sidebar-tab__empty"
100146
100228
  };
@@ -100324,7 +100406,7 @@ function render$7z(_ctx, _cache, $props, $setup, $data, $options) {
100324
100406
  : vue$1.createCommentVNode("v-if", true)
100325
100407
  ]))
100326
100408
  ], 64 /* STABLE_FRAGMENT */))
100327
- : (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_22$12, vue$1.toDisplayString($props.noAuditTrailLabel), 1 /* TEXT */)),
100409
+ : (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_22$13, vue$1.toDisplayString($props.noAuditTrailLabel), 1 /* TEXT */)),
100328
100410
  ($data.restoreEntry)
100329
100411
  ? (vue$1.openBlock(), vue$1.createBlock(_component_CnConfirmDialog, {
100330
100412
  key: 3,
@@ -100632,12 +100714,12 @@ const _hoisted_18$1j = {
100632
100714
  const _hoisted_19$1d = { class: "cn-bookmarks-card__row-icon" };
100633
100715
  const _hoisted_20$15 = ["src", "onError"];
100634
100716
  const _hoisted_21$13 = { class: "cn-bookmarks-card__row-main" };
100635
- const _hoisted_22$11 = ["href"];
100636
- const _hoisted_23$U = {
100717
+ const _hoisted_22$12 = ["href"];
100718
+ const _hoisted_23$V = {
100637
100719
  key: 1,
100638
100720
  class: "cn-bookmarks-card__title"
100639
100721
  };
100640
- const _hoisted_24$P = {
100722
+ const _hoisted_24$Q = {
100641
100723
  key: 3,
100642
100724
  class: "cn-bookmarks-card__view-all"
100643
100725
  };
@@ -100765,14 +100847,14 @@ function render$7y(_ctx, _cache, $props, $setup, $data, $options) {
100765
100847
  target: "_blank",
100766
100848
  rel: "noopener noreferrer",
100767
100849
  class: "cn-bookmarks-card__title"
100768
- }, vue$1.toDisplayString($options.bookmarkTitle(bookmark)), 9 /* TEXT, PROPS */, _hoisted_22$11))
100769
- : (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_23$U, vue$1.toDisplayString($options.bookmarkTitle(bookmark)), 1 /* TEXT */))
100850
+ }, vue$1.toDisplayString($options.bookmarkTitle(bookmark)), 9 /* TEXT, PROPS */, _hoisted_22$12))
100851
+ : (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_23$V, vue$1.toDisplayString($options.bookmarkTitle(bookmark)), 1 /* TEXT */))
100770
100852
  ])
100771
100853
  ]))
100772
100854
  }), 128 /* KEYED_FRAGMENT */))
100773
100855
  ])),
100774
100856
  ($data.bookmarks.length > $data.COMPACT_LIMIT)
100775
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_24$P, [
100857
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_24$Q, [
100776
100858
  vue$1.createElementVNode("a", {
100777
100859
  href: $props.bookmarksAppUrl,
100778
100860
  target: "_blank",
@@ -102728,12 +102810,12 @@ const _hoisted_21$12 = {
102728
102810
  key: 1,
102729
102811
  class: "cn-calendar-tab__group"
102730
102812
  };
102731
- const _hoisted_22$10 = { class: "cn-calendar-tab__group-title cn-calendar-tab__group-title--past" };
102732
- const _hoisted_23$T = {
102813
+ const _hoisted_22$11 = { class: "cn-calendar-tab__group-title cn-calendar-tab__group-title--past" };
102814
+ const _hoisted_23$U = {
102733
102815
  class: "cn-calendar-tab__date cn-calendar-tab__date--past",
102734
102816
  "aria-hidden": "true"
102735
102817
  };
102736
- const _hoisted_24$O = { class: "cn-calendar-tab__date-month" };
102818
+ const _hoisted_24$P = { class: "cn-calendar-tab__date-month" };
102737
102819
  const _hoisted_25$H = { class: "cn-calendar-tab__date-day" };
102738
102820
  const _hoisted_26$C = { class: "cn-calendar-tab__main" };
102739
102821
  const _hoisted_27$t = { class: "cn-calendar-tab__title-row" };
@@ -102908,14 +102990,14 @@ function render$7t(_ctx, _cache, $props, $setup, $data, $options) {
102908
102990
  : vue$1.createCommentVNode("v-if", true),
102909
102991
  ($options.pastEvents.length > 0)
102910
102992
  ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_21$12, [
102911
- vue$1.createElementVNode("h4", _hoisted_22$10, vue$1.toDisplayString($props.pastLabel), 1 /* TEXT */),
102993
+ vue$1.createElementVNode("h4", _hoisted_22$11, vue$1.toDisplayString($props.pastLabel), 1 /* TEXT */),
102912
102994
  (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($options.pastEvents, (ev) => {
102913
102995
  return (vue$1.openBlock(), vue$1.createElementBlock("div", {
102914
102996
  key: $options.rowKey(ev),
102915
102997
  class: "cn-calendar-tab__event cn-calendar-tab__event--past"
102916
102998
  }, [
102917
- vue$1.createElementVNode("div", _hoisted_23$T, [
102918
- vue$1.createElementVNode("span", _hoisted_24$O, vue$1.toDisplayString($options.monthOf(ev)), 1 /* TEXT */),
102999
+ vue$1.createElementVNode("div", _hoisted_23$U, [
103000
+ vue$1.createElementVNode("span", _hoisted_24$P, vue$1.toDisplayString($options.monthOf(ev)), 1 /* TEXT */),
102919
103001
  vue$1.createElementVNode("span", _hoisted_25$H, vue$1.toDisplayString($options.dayOf(ev)), 1 /* TEXT */)
102920
103002
  ]),
102921
103003
  vue$1.createElementVNode("div", _hoisted_26$C, [
@@ -103429,12 +103511,12 @@ const _hoisted_20$13 = {
103429
103511
  "aria-hidden": true
103430
103512
  };
103431
103513
  const _hoisted_21$11 = { class: "cn-collectives-card__row-main" };
103432
- const _hoisted_22$$ = ["href"];
103433
- const _hoisted_23$S = {
103514
+ const _hoisted_22$10 = ["href"];
103515
+ const _hoisted_23$T = {
103434
103516
  key: 0,
103435
103517
  class: "cn-collectives-card__row-collective"
103436
103518
  };
103437
- const _hoisted_24$N = {
103519
+ const _hoisted_24$O = {
103438
103520
  key: 3,
103439
103521
  class: "cn-collectives-card__view-all"
103440
103522
  };
@@ -103557,16 +103639,16 @@ function render$7s(_ctx, _cache, $props, $setup, $data, $options) {
103557
103639
  target: "_blank",
103558
103640
  rel: "noopener",
103559
103641
  class: "cn-collectives-card__title"
103560
- }, vue$1.toDisplayString($options.pageTitle(page)), 9 /* TEXT, PROPS */, _hoisted_22$$),
103642
+ }, vue$1.toDisplayString($options.pageTitle(page)), 9 /* TEXT, PROPS */, _hoisted_22$10),
103561
103643
  ($options.collectiveLabel(page))
103562
- ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_23$S, vue$1.toDisplayString($options.collectiveLabel(page)), 1 /* TEXT */))
103644
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_23$T, vue$1.toDisplayString($options.collectiveLabel(page)), 1 /* TEXT */))
103563
103645
  : vue$1.createCommentVNode("v-if", true)
103564
103646
  ])
103565
103647
  ]))
103566
103648
  }), 128 /* KEYED_FRAGMENT */))
103567
103649
  ])),
103568
103650
  ($data.pages.length > $data.COMPACT_LIMIT)
103569
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_24$N, [
103651
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_24$O, [
103570
103652
  vue$1.createElementVNode("a", {
103571
103653
  href: $props.collectivesAppUrl,
103572
103654
  target: "_blank",
@@ -106144,6 +106226,8 @@ var script$7k = {
106144
106226
  name: 'CnContactsTab',
106145
106227
 
106146
106228
  components: {
106229
+ NcActionButton: vue.NcActionButton,
106230
+ NcActions: vue.NcActions,
106147
106231
  NcAvatar: vue.NcAvatar,
106148
106232
  NcButton: vue.NcButton,
106149
106233
  NcEmptyContent: vue.NcEmptyContent,
@@ -106234,6 +106318,11 @@ var script$7k = {
106234
106318
  },
106235
106319
 
106236
106320
  computed: {
106321
+ /** @return {boolean} The board look draws the rows as DqZaakBetrokkenen does. */
106322
+ isBoard() {
106323
+ return normalizeLook(this.cnLook) === 'board'
106324
+ },
106325
+
106237
106326
  /**
106238
106327
  * The two header buttons in the order and weight of the look: the
106239
106328
  * board look draws Link contact then New contact, both outlined; the
@@ -106526,6 +106615,49 @@ var script$7k = {
106526
106615
  *
106527
106616
  * @return {string} The window.
106528
106617
  */
106618
+ /**
106619
+ * A person's details on one line for the board look: e-mail, phone,
106620
+ * organisation and note, separated by middots.
106621
+ *
106622
+ * @param {object} contact The contact link.
106623
+ * @return {string}
106624
+ */
106625
+ boardMetaOf(contact) {
106626
+ return [contact?.email, contact?.phone, contact?.org, contact?.note].filter((part) => typeof part === 'string' && part !== '').join(' · ')
106627
+ },
106628
+
106629
+ /**
106630
+ * The validity window in the board's short dates: "Vanaf 3 okt",
106631
+ * "Tot 31 okt" or "3 okt – 31 okt".
106632
+ *
106633
+ * @param {object} contact The contact link.
106634
+ * @return {string}
106635
+ */
106636
+ boardPeriodOf(contact) {
106637
+ const from = formatBoardDate(contact?.validFrom || '');
106638
+ const until = formatBoardDate(contact?.validUntil || '');
106639
+ if (from === '' && until === '') {
106640
+ return ''
106641
+ }
106642
+ if (until === '') {
106643
+ return l10n.translate('nextcloud-vue', 'From {date}', { date: from })
106644
+ }
106645
+ if (from === '') {
106646
+ return l10n.translate('nextcloud-vue', 'Until {date}', { date: until })
106647
+ }
106648
+ return `${from} – ${until}`
106649
+ },
106650
+
106651
+ /**
106652
+ * The label of a person's actions menu.
106653
+ *
106654
+ * @param {object} contact The contact link.
106655
+ * @return {string}
106656
+ */
106657
+ moreActionsLabel(contact) {
106658
+ return l10n.translate('nextcloud-vue', 'More actions for {name}', { name: contact?.displayName || this.unknownLabel })
106659
+ },
106660
+
106529
106661
  periodOf(contact) {
106530
106662
  const from = contact?.validFrom || '';
106531
106663
  const until = contact?.validUntil || '';
@@ -106633,7 +106765,10 @@ const _hoisted_3$68 = {
106633
106765
  class: "cn-contacts-tab__groups"
106634
106766
  };
106635
106767
  const _hoisted_4$5z = { class: "cn-contacts-tab__group-title" };
106636
- const _hoisted_5$4V = { class: "cn-contacts-tab__group-count" };
106768
+ const _hoisted_5$4V = {
106769
+ key: 0,
106770
+ class: "cn-contacts-tab__group-count"
106771
+ };
106637
106772
  const _hoisted_6$4x = { class: "cn-contacts-tab__list" };
106638
106773
  const _hoisted_7$46 = ["onClick"];
106639
106774
  const _hoisted_8$3H = { class: "cn-contacts-tab__avatar" };
@@ -106641,32 +106776,42 @@ const _hoisted_9$3n = { class: "cn-contacts-tab__details" };
106641
106776
  const _hoisted_10$2$ = { class: "cn-contacts-tab__name-row" };
106642
106777
  const _hoisted_11$2G = { class: "cn-contacts-tab__name" };
106643
106778
  const _hoisted_12$2p = {
106779
+ key: 0,
106780
+ class: "cn-contacts-tab__meta",
106781
+ "data-testid": "cn-contacts-tab-meta"
106782
+ };
106783
+ const _hoisted_13$29 = {
106644
106784
  key: 0,
106645
106785
  class: "cn-contacts-tab__sub"
106646
106786
  };
106647
- const _hoisted_13$29 = { class: "cn-contacts-tab__sub-text" };
106648
- const _hoisted_14$22 = {
106787
+ const _hoisted_14$22 = { class: "cn-contacts-tab__sub-text" };
106788
+ const _hoisted_15$1T = {
106649
106789
  key: 1,
106650
106790
  class: "cn-contacts-tab__sub"
106651
106791
  };
106652
- const _hoisted_15$1T = { class: "cn-contacts-tab__sub-text" };
106653
- const _hoisted_16$1H = {
106792
+ const _hoisted_16$1H = { class: "cn-contacts-tab__sub-text" };
106793
+ const _hoisted_17$1u = {
106654
106794
  key: 2,
106655
106795
  class: "cn-contacts-tab__sub"
106656
106796
  };
106657
- const _hoisted_17$1u = { class: "cn-contacts-tab__sub-text" };
106658
- const _hoisted_18$1g = {
106797
+ const _hoisted_18$1g = { class: "cn-contacts-tab__sub-text" };
106798
+ const _hoisted_19$1a = {
106659
106799
  key: 3,
106660
106800
  class: "cn-contacts-tab__sub",
106661
106801
  "data-testid": "cn-contacts-tab-period"
106662
106802
  };
106663
- const _hoisted_19$1a = { class: "cn-contacts-tab__sub-text" };
106664
- const _hoisted_20$12 = {
106803
+ const _hoisted_20$12 = { class: "cn-contacts-tab__sub-text" };
106804
+ const _hoisted_21$10 = {
106665
106805
  key: 4,
106666
106806
  class: "cn-contacts-tab__sub"
106667
106807
  };
106668
- const _hoisted_21$10 = { class: "cn-contacts-tab__sub-text" };
106669
- const _hoisted_22$_ = ["aria-label", "title", "onClick"];
106808
+ const _hoisted_22$$ = { class: "cn-contacts-tab__sub-text" };
106809
+ const _hoisted_23$S = {
106810
+ key: 0,
106811
+ class: "cn-contacts-tab__period",
106812
+ "data-testid": "cn-contacts-tab-board-period"
106813
+ };
106814
+ const _hoisted_24$N = ["aria-label", "title", "onClick"];
106670
106815
 
106671
106816
  function render$7j(_ctx, _cache, $props, $setup, $data, $options) {
106672
106817
  const _component_LinkVariant = vue$1.resolveComponent("LinkVariant");
@@ -106684,6 +106829,8 @@ function render$7j(_ctx, _cache, $props, $setup, $data, $options) {
106684
106829
  const _component_OfficeBuildingOutline = vue$1.resolveComponent("OfficeBuildingOutline");
106685
106830
  const _component_CalendarRange = vue$1.resolveComponent("CalendarRange");
106686
106831
  const _component_Close = vue$1.resolveComponent("Close");
106832
+ const _component_NcActionButton = vue$1.resolveComponent("NcActionButton");
106833
+ const _component_NcActions = vue$1.resolveComponent("NcActions");
106687
106834
  const _component_CnContactPicker = vue$1.resolveComponent("CnContactPicker");
106688
106835
  const _component_CnContactCreate = vue$1.resolveComponent("CnContactCreate");
106689
106836
 
@@ -106752,7 +106899,9 @@ function render$7j(_ctx, _cache, $props, $setup, $data, $options) {
106752
106899
  }, [
106753
106900
  vue$1.createElementVNode("h3", _hoisted_4$5z, [
106754
106901
  vue$1.createTextVNode(vue$1.toDisplayString(group.label) + " ", 1 /* TEXT */),
106755
- vue$1.createElementVNode("span", _hoisted_5$4V, vue$1.toDisplayString(group.items.length), 1 /* TEXT */)
106902
+ (!$options.isBoard)
106903
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_5$4V, vue$1.toDisplayString(group.items.length), 1 /* TEXT */))
106904
+ : vue$1.createCommentVNode("v-if", true)
106756
106905
  ]),
106757
106906
  vue$1.createElementVNode("ul", _hoisted_6$4x, [
106758
106907
  (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList(group.items, (contact) => {
@@ -106763,7 +106912,7 @@ function render$7j(_ctx, _cache, $props, $setup, $data, $options) {
106763
106912
  }, [
106764
106913
  vue$1.createElementVNode("div", _hoisted_8$3H, [
106765
106914
  vue$1.createVNode(_component_NcAvatar, {
106766
- size: 40,
106915
+ size: $options.isBoard ? 32 : 40,
106767
106916
  displayName: contact.displayName || $props.unknownLabel,
106768
106917
  user: $options.avatarSeed(contact),
106769
106918
  url: contact.avatarUrl || undefined,
@@ -106771,12 +106920,12 @@ function render$7j(_ctx, _cache, $props, $setup, $data, $options) {
106771
106920
  disableMenu: true,
106772
106921
  disableTooltip: true,
106773
106922
  hideStatus: ""
106774
- }, null, 8 /* PROPS */, ["displayName", "user", "url"])
106923
+ }, null, 8 /* PROPS */, ["size", "displayName", "user", "url"])
106775
106924
  ]),
106776
106925
  vue$1.createElementVNode("div", _hoisted_9$3n, [
106777
106926
  vue$1.createElementVNode("div", _hoisted_10$2$, [
106778
106927
  vue$1.createElementVNode("span", _hoisted_11$2G, vue$1.toDisplayString(contact.displayName || $props.unknownLabel), 1 /* TEXT */),
106779
- (contact.role)
106928
+ (contact.role && !$options.isBoard)
106780
106929
  ? (vue$1.openBlock(), vue$1.createBlock(_component_CnStatusBadge, {
106781
106930
  key: 0,
106782
106931
  label: $options.roleLabel(contact),
@@ -106794,57 +106943,94 @@ function render$7j(_ctx, _cache, $props, $setup, $data, $options) {
106794
106943
  }, null, 8 /* PROPS */, ["label"]))
106795
106944
  : vue$1.createCommentVNode("v-if", true)
106796
106945
  ]),
106797
- (contact.email)
106798
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_12$2p, [
106799
- vue$1.createVNode(_component_Email, {
106800
- size: 14,
106801
- class: "cn-contacts-tab__sub-icon"
106802
- }),
106803
- vue$1.createElementVNode("span", _hoisted_13$29, vue$1.toDisplayString(contact.email), 1 /* TEXT */)
106804
- ]))
106805
- : vue$1.createCommentVNode("v-if", true),
106806
- (contact.phone)
106807
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_14$22, [
106808
- vue$1.createVNode(_component_Phone, {
106809
- size: 14,
106810
- class: "cn-contacts-tab__sub-icon"
106811
- }),
106812
- vue$1.createElementVNode("span", _hoisted_15$1T, vue$1.toDisplayString(contact.phone), 1 /* TEXT */)
106813
- ]))
106814
- : vue$1.createCommentVNode("v-if", true),
106815
- (contact.org)
106816
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_16$1H, [
106817
- vue$1.createVNode(_component_OfficeBuildingOutline, {
106818
- size: 14,
106819
- class: "cn-contacts-tab__sub-icon"
106820
- }),
106821
- vue$1.createElementVNode("span", _hoisted_17$1u, vue$1.toDisplayString(contact.org), 1 /* TEXT */)
106822
- ]))
106823
- : vue$1.createCommentVNode("v-if", true),
106824
- ($options.periodOf(contact))
106825
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_18$1g, [
106826
- vue$1.createVNode(_component_CalendarRange, {
106827
- size: 14,
106828
- class: "cn-contacts-tab__sub-icon"
106829
- }),
106830
- vue$1.createElementVNode("span", _hoisted_19$1a, vue$1.toDisplayString($options.periodOf(contact)), 1 /* TEXT */)
106831
- ]))
106946
+ ($options.isBoard && $options.boardMetaOf(contact))
106947
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_12$2p, vue$1.toDisplayString($options.boardMetaOf(contact)), 1 /* TEXT */))
106832
106948
  : vue$1.createCommentVNode("v-if", true),
106833
- (contact.note)
106834
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_20$12, [
106835
- vue$1.createElementVNode("span", _hoisted_21$10, vue$1.toDisplayString(contact.note), 1 /* TEXT */)
106836
- ]))
106949
+ (!$options.isBoard)
106950
+ ? (vue$1.openBlock(), vue$1.createElementBlock(vue$1.Fragment, { key: 1 }, [
106951
+ (contact.email)
106952
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_13$29, [
106953
+ vue$1.createVNode(_component_Email, {
106954
+ size: 14,
106955
+ class: "cn-contacts-tab__sub-icon"
106956
+ }),
106957
+ vue$1.createElementVNode("span", _hoisted_14$22, vue$1.toDisplayString(contact.email), 1 /* TEXT */)
106958
+ ]))
106959
+ : vue$1.createCommentVNode("v-if", true),
106960
+ (contact.phone)
106961
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_15$1T, [
106962
+ vue$1.createVNode(_component_Phone, {
106963
+ size: 14,
106964
+ class: "cn-contacts-tab__sub-icon"
106965
+ }),
106966
+ vue$1.createElementVNode("span", _hoisted_16$1H, vue$1.toDisplayString(contact.phone), 1 /* TEXT */)
106967
+ ]))
106968
+ : vue$1.createCommentVNode("v-if", true),
106969
+ (contact.org)
106970
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_17$1u, [
106971
+ vue$1.createVNode(_component_OfficeBuildingOutline, {
106972
+ size: 14,
106973
+ class: "cn-contacts-tab__sub-icon"
106974
+ }),
106975
+ vue$1.createElementVNode("span", _hoisted_18$1g, vue$1.toDisplayString(contact.org), 1 /* TEXT */)
106976
+ ]))
106977
+ : vue$1.createCommentVNode("v-if", true),
106978
+ ($options.periodOf(contact))
106979
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_19$1a, [
106980
+ vue$1.createVNode(_component_CalendarRange, {
106981
+ size: 14,
106982
+ class: "cn-contacts-tab__sub-icon"
106983
+ }),
106984
+ vue$1.createElementVNode("span", _hoisted_20$12, vue$1.toDisplayString($options.periodOf(contact)), 1 /* TEXT */)
106985
+ ]))
106986
+ : vue$1.createCommentVNode("v-if", true),
106987
+ (contact.note)
106988
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_21$10, [
106989
+ vue$1.createElementVNode("span", _hoisted_22$$, vue$1.toDisplayString(contact.note), 1 /* TEXT */)
106990
+ ]))
106991
+ : vue$1.createCommentVNode("v-if", true)
106992
+ ], 64 /* STABLE_FRAGMENT */))
106837
106993
  : vue$1.createCommentVNode("v-if", true)
106838
106994
  ]),
106839
- vue$1.createElementVNode("button", {
106840
- type: "button",
106841
- class: "cn-contacts-tab__unlink",
106842
- "aria-label": $props.unlinkLabel,
106843
- title: $props.unlinkLabel,
106844
- onClick: vue$1.withModifiers($event => ($options.unlink(contact)), ["stop"])
106845
- }, [
106846
- vue$1.createVNode(_component_Close, { size: 18 })
106847
- ], 8 /* PROPS */, _hoisted_22$_)
106995
+ ($options.isBoard)
106996
+ ? (vue$1.openBlock(), vue$1.createElementBlock(vue$1.Fragment, { key: 0 }, [
106997
+ ($options.boardPeriodOf(contact))
106998
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_23$S, vue$1.toDisplayString($options.boardPeriodOf(contact)), 1 /* TEXT */))
106999
+ : vue$1.createCommentVNode("v-if", true),
107000
+ vue$1.createVNode(_component_NcActions, {
107001
+ class: "cn-contacts-tab__menu",
107002
+ variant: "secondary",
107003
+ "aria-label": $options.moreActionsLabel(contact),
107004
+ "data-testid": "cn-contacts-tab-menu",
107005
+ onClick: _cache[0] || (_cache[0] = vue$1.withModifiers(() => {}, ["stop"]))
107006
+ }, {
107007
+ default: vue$1.withCtx(() => [
107008
+ vue$1.createVNode(_component_NcActionButton, {
107009
+ closeAfterClick: true,
107010
+ onClick: $event => ($options.unlink(contact))
107011
+ }, {
107012
+ icon: vue$1.withCtx(() => [
107013
+ vue$1.createVNode(_component_Close, { size: 20 })
107014
+ ]),
107015
+ default: vue$1.withCtx(() => [
107016
+ vue$1.createTextVNode(" " + vue$1.toDisplayString($props.unlinkLabel), 1 /* TEXT */)
107017
+ ]),
107018
+ _: 1 /* STABLE */
107019
+ }, 8 /* PROPS */, ["onClick"])
107020
+ ]),
107021
+ _: 2 /* DYNAMIC */
107022
+ }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["aria-label"])
107023
+ ], 64 /* STABLE_FRAGMENT */))
107024
+ : (vue$1.openBlock(), vue$1.createElementBlock("button", {
107025
+ key: 1,
107026
+ type: "button",
107027
+ class: "cn-contacts-tab__unlink",
107028
+ "aria-label": $props.unlinkLabel,
107029
+ title: $props.unlinkLabel,
107030
+ onClick: vue$1.withModifiers($event => ($options.unlink(contact)), ["stop"])
107031
+ }, [
107032
+ vue$1.createVNode(_component_Close, { size: 18 })
107033
+ ], 8 /* PROPS */, _hoisted_24$N))
106848
107034
  ], 8 /* PROPS */, _hoisted_7$46))
106849
107035
  }), 128 /* KEYED_FRAGMENT */))
106850
107036
  ])
@@ -106857,7 +107043,7 @@ function render$7j(_ctx, _cache, $props, $setup, $data, $options) {
106857
107043
  apiBase: $props.apiBase
106858
107044
  }, $options.pickerRoleOptions, {
106859
107045
  onLink: $options.onPickerLink,
106860
- onClose: _cache[0] || (_cache[0] = $event => ($data.showLinkDialog = false))
107046
+ onClose: _cache[1] || (_cache[1] = $event => ($data.showLinkDialog = false))
106861
107047
  }), null, 16 /* FULL_PROPS */, ["apiBase", "onLink"]))
106862
107048
  : vue$1.createCommentVNode("v-if", true),
106863
107049
  ($data.showCreateDialog)
@@ -106865,14 +107051,14 @@ function render$7j(_ctx, _cache, $props, $setup, $data, $options) {
106865
107051
  key: 5,
106866
107052
  loading: $data.createLoading,
106867
107053
  onCreate: $options.onCreateSubmit,
106868
- onClose: _cache[1] || (_cache[1] = $event => ($data.showCreateDialog = false))
107054
+ onClose: _cache[2] || (_cache[2] = $event => ($data.showCreateDialog = false))
106869
107055
  }, null, 8 /* PROPS */, ["loading", "onCreate"]))
106870
107056
  : vue$1.createCommentVNode("v-if", true)
106871
107057
  ]))
106872
107058
  }
106873
107059
 
106874
107060
  script$7k.render = render$7j;
106875
- script$7k.__scopeId = "data-v-ec6b0008";
107061
+ script$7k.__scopeId = "data-v-a0d90184";
106876
107062
  const __sfc_main$7e = script$7k;
106877
107063
 
106878
107064
  /**
@@ -108377,422 +108563,7 @@ const cospendIntegration = {
108377
108563
  defaultSize: { w: 3, h: 3 },
108378
108564
  };
108379
108565
 
108380
- const VALID_SURFACES$e = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'];
108381
-
108382
- /**
108383
- * CnDeckCard — bespoke surface-aware widget for the `deck` integration.
108384
- *
108385
- * Renders Deck-aware metadata across all four surfaces. See the
108386
- * file-level docblock for surface-by-surface behaviour.
108387
- */
108388
108566
  var script$7f = {
108389
- name: 'CnDeckCard',
108390
-
108391
- components: { CnDetailCard: __sfc_main$7F, NcLoadingIcon: vue.NcLoadingIcon, ViewColumnOutline },
108392
-
108393
- props: {
108394
- /** Stable integration id (forwarded from the registry — always `'deck'`). */
108395
- integrationId: { type: String, default: 'deck' },
108396
- /** OpenRegister register id (slug or uuid). */
108397
- register: { type: String, required: true },
108398
- /** OpenRegister schema id (slug or uuid). */
108399
- schema: { type: String, required: true },
108400
- /** Parent object id. */
108401
- objectId: { type: String, required: true },
108402
- /** Rendering surface (AD-19). */
108403
- surface: {
108404
- type: String,
108405
- default: 'detail-page',
108406
- validator: (s) => VALID_SURFACES$e.includes(s),
108407
- },
108408
-
108409
- /** Optional single-entity reference (card id). */
108410
- value: { type: [String, Number], default: '' },
108411
- /** Pre-translated card title. */
108412
- title: { type: String, default: () => l10n.translate('nextcloud-vue', 'Cards') },
108413
- /** Optional Material Design Icon component. */
108414
- icon: { type: Object, default: () => ViewColumnOutline },
108415
- /** Base API URL. */
108416
- apiBase: { type: String, default: '/apps/openregister/api' },
108417
- /** Whether the card body is collapsible. */
108418
- collapsible: { type: Boolean, default: true },
108419
- /** Pre-translated empty-state label. */
108420
- emptyLabel: { type: String, default: () => l10n.translate('nextcloud-vue', 'No cards linked yet') },
108421
- /** Pre-translated unavailable label. */
108422
- unavailableLabel: { type: String, default: () => l10n.translate('nextcloud-vue', 'NC Deck is currently unavailable.') },
108423
- /** URL of the NC Deck app entry. */
108424
- deckAppUrl: { type: String, default: '/index.php/apps/deck' },
108425
- },
108426
-
108427
- data() {
108428
- return {
108429
- cards: [],
108430
- entity: null,
108431
- loading: false,
108432
- degraded: '',
108433
- }
108434
- },
108435
-
108436
- computed: {
108437
- cardHeaderTitle() {
108438
- return this.title || this.integrationId
108439
- },
108440
-
108441
- cardIcon() {
108442
- return this.icon
108443
- },
108444
-
108445
- /**
108446
- * Cards grouped by stack for both the mini-kanban (detail-page)
108447
- * and the stack-distribution list (dashboard surfaces).
108448
- *
108449
- * @return {Array<{key: string, label: string, cards: Array}>}
108450
- */
108451
- columns() {
108452
- const groups = new Map();
108453
- for (const card of this.cards) {
108454
- const stackId = card.stackId ?? card.stack?.id ?? 'unstacked';
108455
- const stackLabel = card.stackTitle
108456
- ?? card.stack?.title
108457
- ?? (stackId === 'unstacked'
108458
- ? l10n.translate('nextcloud-vue', 'No stack')
108459
- : l10n.translate('nextcloud-vue', 'Stack {n}', { n: stackId }));
108460
- const key = String(stackId);
108461
- if (!groups.has(key)) {
108462
- groups.set(key, { key, label: stackLabel, cards: [] });
108463
- }
108464
- groups.get(key).cards.push(card);
108465
- }
108466
- return Array.from(groups.values())
108467
- },
108468
-
108469
- /**
108470
- * Up to three columns for the mini-kanban view on `detail-page`.
108471
- *
108472
- * Per AD-2 (design.md): when the linked card has its own stack
108473
- * we always include that stack; otherwise the first three stacks
108474
- * by first-seen order are surfaced.
108475
- *
108476
- * @return {Array<{key: string, label: string, cards: Array}>}
108477
- */
108478
- miniColumns() {
108479
- return this.columns.slice(0, 3)
108480
- },
108481
-
108482
- stackDistribution() {
108483
- return this.columns.map((col) => ({
108484
- key: col.key,
108485
- label: col.label,
108486
- count: col.cards.length,
108487
- }))
108488
- },
108489
-
108490
- countHeadline() {
108491
- const total = this.cards.length;
108492
- const stackCount = this.columns.length;
108493
- if (stackCount <= 1) {
108494
- return l10n.translatePlural('nextcloud-vue', '{count} card', '{count} cards', total, { count: total })
108495
- }
108496
- const cardFragment = l10n.translatePlural('nextcloud-vue', '{count} card', '{count} cards', total, { count: total });
108497
- const stacksFragment = l10n.translatePlural('nextcloud-vue', 'across {count} stack', 'across {count} stacks', stackCount, { count: stackCount });
108498
- return `${cardFragment} ${stacksFragment}`
108499
- },
108500
-
108501
- mostRecent() {
108502
- if (this.cards.length === 0) {
108503
- return null
108504
- }
108505
- const sorted = [...this.cards].sort((a, b) => {
108506
- const ta = Date.parse(a.linkedAt ?? a.lastActivity ?? '') || 0;
108507
- const tb = Date.parse(b.linkedAt ?? b.lastActivity ?? '') || 0;
108508
- return tb - ta
108509
- });
108510
- return sorted[0]
108511
- },
108512
- },
108513
-
108514
- watch: {
108515
- objectId: { immediate: true, handler() {
108516
- this.fetch();
108517
- } },
108518
-
108519
- surface() {
108520
- this.fetch();
108521
- },
108522
-
108523
- value() {
108524
- if (this.surface === 'single-entity') {
108525
- this.fetchSingle();
108526
- }
108527
- },
108528
- },
108529
-
108530
- methods: {
108531
- baseUrl() {
108532
- return `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`
108533
- },
108534
-
108535
- cardKey(card) {
108536
- return card.cardId ?? card.id ?? ''
108537
- },
108538
-
108539
- cardTitle(card) {
108540
- return card.cardTitle ?? card.title ?? this.cardKey(card)
108541
- },
108542
-
108543
- cardUrl(card) {
108544
- if (card.url && safeHref(card.url) !== '#') {
108545
- return card.url
108546
- }
108547
- const boardId = card.boardId ?? card.board?.id ?? '';
108548
- const cardId = card.cardId ?? card.id ?? '';
108549
- if (boardId && cardId) {
108550
- return `/index.php/apps/deck/board/${boardId}/card/${cardId}`
108551
- }
108552
- return this.deckAppUrl
108553
- },
108554
-
108555
- stackTitle(card) {
108556
- return card.stackTitle ?? card.stack?.title ?? ''
108557
- },
108558
-
108559
- isLinked(card) {
108560
- if (!this.value) {
108561
- return false
108562
- }
108563
- return String(this.cardKey(card)) === String(this.value)
108564
- },
108565
-
108566
- chipSubtitle(card) {
108567
- return this.stackTitle(card) || this.cardTitle(card)
108568
- },
108569
-
108570
- fetch() {
108571
- if (this.surface === 'single-entity') {
108572
- this.fetchSingle();
108573
- return
108574
- }
108575
- this.fetchList();
108576
- },
108577
-
108578
- async fetchList() {
108579
- if (!this.register || !this.schema || !this.objectId) {
108580
- return
108581
- }
108582
- this.loading = true;
108583
- this.degraded = '';
108584
- try {
108585
- const response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() });
108586
- if (response.ok) {
108587
- const data = await response.json();
108588
- this.cards = data.results || data.items || (Array.isArray(data) ? data : []) || [];
108589
- } else if (response.status === 503) {
108590
- this.cards = [];
108591
- this.degraded = this.unavailableLabel;
108592
- } else {
108593
- this.cards = [];
108594
- }
108595
- } catch (err) {
108596
- // eslint-disable-next-line no-console
108597
- console.error('[CnDeckCard] failed to fetch cards', err);
108598
- this.cards = [];
108599
- } finally {
108600
- this.loading = false;
108601
- }
108602
- },
108603
-
108604
- async fetchSingle() {
108605
- if (!this.value || !this.register || !this.schema || !this.objectId) {
108606
- this.entity = null;
108607
- return
108608
- }
108609
- this.loading = true;
108610
- this.degraded = '';
108611
- try {
108612
- const response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() });
108613
- if (response.ok) {
108614
- this.entity = await response.json();
108615
- } else if (response.status === 503) {
108616
- this.entity = null;
108617
- this.degraded = this.unavailableLabel;
108618
- } else {
108619
- this.entity = null;
108620
- }
108621
- } catch (err) {
108622
- // eslint-disable-next-line no-console
108623
- console.error('[CnDeckCard] failed to fetch single card', err);
108624
- this.entity = null;
108625
- } finally {
108626
- this.loading = false;
108627
- }
108628
- },
108629
- },
108630
- };
108631
-
108632
- const _hoisted_1$6Y = ["title"];
108633
- const _hoisted_2$6K = ["href"];
108634
- const _hoisted_3$64 = {
108635
- key: 0,
108636
- class: "cn-deck-card__chip-stack"
108637
- };
108638
- const _hoisted_4$5v = {
108639
- key: 1,
108640
- class: "cn-deck-card__empty"
108641
- };
108642
- const _hoisted_5$4R = {
108643
- key: 0,
108644
- class: "cn-deck-card__empty"
108645
- };
108646
- const _hoisted_6$4t = {
108647
- key: 1,
108648
- class: "cn-deck-card__empty"
108649
- };
108650
- const _hoisted_7$42 = {
108651
- key: 2,
108652
- class: "cn-deck-card__headline"
108653
- };
108654
- const _hoisted_8$3E = { class: "cn-deck-card__headline-line" };
108655
- const _hoisted_9$3k = {
108656
- key: 0,
108657
- class: "cn-deck-card__distribution"
108658
- };
108659
- const _hoisted_10$2Y = { class: "cn-deck-card__distribution-label" };
108660
- const _hoisted_11$2D = { class: "cn-deck-card__distribution-count" };
108661
- const _hoisted_12$2m = {
108662
- key: 1,
108663
- class: "cn-deck-card__headline-recent"
108664
- };
108665
- const _hoisted_13$26 = ["href"];
108666
- const _hoisted_14$1$ = {
108667
- key: 0,
108668
- class: "cn-deck-card__empty"
108669
- };
108670
- const _hoisted_15$1R = {
108671
- key: 1,
108672
- class: "cn-deck-card__empty"
108673
- };
108674
- const _hoisted_16$1F = {
108675
- key: 2,
108676
- class: "cn-deck-card__kanban"
108677
- };
108678
- const _hoisted_17$1s = { class: "cn-deck-card__column-header" };
108679
- const _hoisted_18$1f = { class: "cn-deck-card__column-title" };
108680
- const _hoisted_19$19 = { class: "cn-deck-card__column-count" };
108681
- const _hoisted_20$11 = { class: "cn-deck-card__column-list" };
108682
- const _hoisted_21$$ = ["href"];
108683
-
108684
- function render$7e(_ctx, _cache, $props, $setup, $data, $options) {
108685
- const _component_NcLoadingIcon = vue$1.resolveComponent("NcLoadingIcon");
108686
- const _component_ViewColumnOutline = vue$1.resolveComponent("ViewColumnOutline");
108687
- const _component_CnDetailCard = vue$1.resolveComponent("CnDetailCard");
108688
-
108689
- return (vue$1.openBlock(), vue$1.createBlock(_component_CnDetailCard, {
108690
- title: $options.cardHeaderTitle,
108691
- icon: $options.cardIcon,
108692
- collapsible: $props.collapsible
108693
- }, {
108694
- default: vue$1.withCtx(() => [
108695
- ($data.loading)
108696
- ? (vue$1.openBlock(), vue$1.createBlock(_component_NcLoadingIcon, { key: 0 }))
108697
- : ($props.surface === 'single-entity')
108698
- ? (vue$1.openBlock(), vue$1.createElementBlock(vue$1.Fragment, { key: 1 }, [
108699
- ($data.entity)
108700
- ? (vue$1.openBlock(), vue$1.createElementBlock("span", {
108701
- key: 0,
108702
- class: "cn-deck-card__chip",
108703
- title: $options.chipSubtitle($data.entity)
108704
- }, [
108705
- vue$1.createVNode(_component_ViewColumnOutline, { size: 14 }),
108706
- vue$1.createElementVNode("a", {
108707
- href: $options.cardUrl($data.entity),
108708
- target: "_blank",
108709
- rel: "noopener"
108710
- }, vue$1.toDisplayString($options.cardTitle($data.entity)), 9 /* TEXT, PROPS */, _hoisted_2$6K),
108711
- ($options.stackTitle($data.entity))
108712
- ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_3$64, " · " + vue$1.toDisplayString($options.stackTitle($data.entity)), 1 /* TEXT */))
108713
- : vue$1.createCommentVNode("v-if", true)
108714
- ], 8 /* PROPS */, _hoisted_1$6Y))
108715
- : (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_4$5v, vue$1.toDisplayString($props.emptyLabel), 1 /* TEXT */))
108716
- ], 64 /* STABLE_FRAGMENT */))
108717
- : ($props.surface === 'user-dashboard' || $props.surface === 'app-dashboard')
108718
- ? (vue$1.openBlock(), vue$1.createElementBlock(vue$1.Fragment, { key: 2 }, [
108719
- ($data.degraded)
108720
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_5$4R, vue$1.toDisplayString($data.degraded), 1 /* TEXT */))
108721
- : ($data.cards.length === 0)
108722
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_6$4t, vue$1.toDisplayString($props.emptyLabel), 1 /* TEXT */))
108723
- : (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_7$42, [
108724
- vue$1.createElementVNode("div", _hoisted_8$3E, [
108725
- vue$1.createElementVNode("strong", null, vue$1.toDisplayString($options.countHeadline), 1 /* TEXT */)
108726
- ]),
108727
- ($options.stackDistribution.length > 0)
108728
- ? (vue$1.openBlock(), vue$1.createElementBlock("ul", _hoisted_9$3k, [
108729
- (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($options.stackDistribution, (bucket) => {
108730
- return (vue$1.openBlock(), vue$1.createElementBlock("li", {
108731
- key: bucket.key,
108732
- class: "cn-deck-card__distribution-row"
108733
- }, [
108734
- vue$1.createElementVNode("span", _hoisted_10$2Y, vue$1.toDisplayString(bucket.label), 1 /* TEXT */),
108735
- vue$1.createElementVNode("span", _hoisted_11$2D, vue$1.toDisplayString(bucket.count), 1 /* TEXT */)
108736
- ]))
108737
- }), 128 /* KEYED_FRAGMENT */))
108738
- ]))
108739
- : vue$1.createCommentVNode("v-if", true),
108740
- ($options.mostRecent)
108741
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_12$2m, [
108742
- vue$1.createVNode(_component_ViewColumnOutline, { size: 14 }),
108743
- vue$1.createElementVNode("a", {
108744
- href: $options.cardUrl($options.mostRecent),
108745
- target: "_blank",
108746
- rel: "noopener"
108747
- }, vue$1.toDisplayString($options.cardTitle($options.mostRecent)), 9 /* TEXT, PROPS */, _hoisted_13$26)
108748
- ]))
108749
- : vue$1.createCommentVNode("v-if", true)
108750
- ]))
108751
- ], 64 /* STABLE_FRAGMENT */))
108752
- : (vue$1.openBlock(), vue$1.createElementBlock(vue$1.Fragment, { key: 3 }, [
108753
- ($data.degraded)
108754
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_14$1$, vue$1.toDisplayString($data.degraded), 1 /* TEXT */))
108755
- : ($data.cards.length === 0)
108756
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_15$1R, vue$1.toDisplayString($props.emptyLabel), 1 /* TEXT */))
108757
- : (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_16$1F, [
108758
- (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($options.miniColumns, (stack) => {
108759
- return (vue$1.openBlock(), vue$1.createElementBlock("section", {
108760
- key: stack.key,
108761
- class: "cn-deck-card__column"
108762
- }, [
108763
- vue$1.createElementVNode("header", _hoisted_17$1s, [
108764
- vue$1.createElementVNode("span", _hoisted_18$1f, vue$1.toDisplayString(stack.label), 1 /* TEXT */),
108765
- vue$1.createElementVNode("span", _hoisted_19$19, vue$1.toDisplayString(stack.cards.length), 1 /* TEXT */)
108766
- ]),
108767
- vue$1.createElementVNode("ul", _hoisted_20$11, [
108768
- (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList(stack.cards, (card) => {
108769
- return (vue$1.openBlock(), vue$1.createElementBlock("li", {
108770
- key: $options.cardKey(card),
108771
- class: vue$1.normalizeClass(["cn-deck-card__row", { 'cn-deck-card__row--highlight': $options.isLinked(card) }])
108772
- }, [
108773
- vue$1.createElementVNode("a", {
108774
- href: $options.cardUrl(card),
108775
- target: "_blank",
108776
- rel: "noopener",
108777
- class: "cn-deck-card__title"
108778
- }, vue$1.toDisplayString($options.cardTitle(card)), 9 /* TEXT, PROPS */, _hoisted_21$$)
108779
- ], 2 /* CLASS */))
108780
- }), 128 /* KEYED_FRAGMENT */))
108781
- ])
108782
- ]))
108783
- }), 128 /* KEYED_FRAGMENT */))
108784
- ]))
108785
- ], 64 /* STABLE_FRAGMENT */))
108786
- ]),
108787
- _: 1 /* STABLE */
108788
- }, 8 /* PROPS */, ["title", "icon", "collapsible"]))
108789
- }
108790
-
108791
- script$7f.render = render$7e;
108792
- script$7f.__scopeId = "data-v-13f7eff5";
108793
- const __sfc_main$79 = script$7f;
108794
-
108795
- var script$7e = {
108796
108567
  name: 'CnDeckCardCreate',
108797
108568
 
108798
108569
  components: { NcButton: vue.NcButton, NcDialog: vue.NcDialog, NcNoteCard: vue.NcNoteCard, NcSelect: vue.NcSelect, NcTextArea: vue.NcTextArea, NcTextField: vue.NcTextField },
@@ -108924,10 +108695,10 @@ var script$7e = {
108924
108695
  },
108925
108696
  };
108926
108697
 
108927
- const _hoisted_1$6X = { class: "cn-deck-card-create__date" };
108928
- const _hoisted_2$6J = { class: "cn-deck-card-create__date-label" };
108698
+ const _hoisted_1$6Y = { class: "cn-deck-card-create__date" };
108699
+ const _hoisted_2$6K = { class: "cn-deck-card-create__date-label" };
108929
108700
 
108930
- function render$7d(_ctx, _cache, $props, $setup, $data, $options) {
108701
+ function render$7e(_ctx, _cache, $props, $setup, $data, $options) {
108931
108702
  const _component_NcNoteCard = vue$1.resolveComponent("NcNoteCard");
108932
108703
  const _component_NcSelect = vue$1.resolveComponent("NcSelect");
108933
108704
  const _component_NcTextField = vue$1.resolveComponent("NcTextField");
@@ -109013,8 +108784,8 @@ function render$7d(_ctx, _cache, $props, $setup, $data, $options) {
109013
108784
  maxlength: 4000,
109014
108785
  rows: 4
109015
108786
  }, null, 8 /* PROPS */, ["modelValue", "label"]),
109016
- vue$1.createElementVNode("label", _hoisted_1$6X, [
109017
- vue$1.createElementVNode("span", _hoisted_2$6J, vue$1.toDisplayString($options.t('nextcloud-vue', 'Due date (optional)')), 1 /* TEXT */),
108787
+ vue$1.createElementVNode("label", _hoisted_1$6Y, [
108788
+ vue$1.createElementVNode("span", _hoisted_2$6K, vue$1.toDisplayString($options.t('nextcloud-vue', 'Due date (optional)')), 1 /* TEXT */),
109018
108789
  vue$1.withDirectives(vue$1.createElementVNode("input", {
109019
108790
  "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => (($data.duedate) = $event)),
109020
108791
  type: "date",
@@ -109029,11 +108800,11 @@ function render$7d(_ctx, _cache, $props, $setup, $data, $options) {
109029
108800
  }, 8 /* PROPS */, ["name", "onClosing"]))
109030
108801
  }
109031
108802
 
109032
- script$7e.render = render$7d;
109033
- script$7e.__scopeId = "data-v-7b2335ba";
109034
- const __sfc_main$78 = script$7e;
108803
+ script$7f.render = render$7e;
108804
+ script$7f.__scopeId = "data-v-7b2335ba";
108805
+ const __sfc_main$79 = script$7f;
109035
108806
 
109036
- var script$7d = {
108807
+ var script$7e = {
109037
108808
  name: 'CnDeckCardPicker',
109038
108809
 
109039
108810
  components: { NcButton: vue.NcButton, NcDialog: vue.NcDialog, NcEmptyContent: vue.NcEmptyContent, NcLoadingIcon: vue.NcLoadingIcon, NcNoteCard: vue.NcNoteCard, ViewColumnOutline },
@@ -109221,40 +108992,40 @@ var script$7d = {
109221
108992
  },
109222
108993
  };
109223
108994
 
109224
- const _hoisted_1$6W = { class: "cn-deck-card-picker" };
109225
- const _hoisted_2$6I = { class: "cn-deck-card-picker__steps" };
109226
- const _hoisted_3$63 = {
108995
+ const _hoisted_1$6X = { class: "cn-deck-card-picker" };
108996
+ const _hoisted_2$6J = { class: "cn-deck-card-picker__steps" };
108997
+ const _hoisted_3$64 = {
109227
108998
  key: 1,
109228
108999
  class: "cn-deck-card-picker__panel"
109229
109000
  };
109230
- const _hoisted_4$5u = {
109001
+ const _hoisted_4$5v = {
109231
109002
  key: 2,
109232
109003
  class: "cn-deck-card-picker__list"
109233
109004
  };
109234
- const _hoisted_5$4Q = ["onClick"];
109235
- const _hoisted_6$4s = { class: "cn-deck-card-picker__row-label" };
109236
- const _hoisted_7$41 = {
109005
+ const _hoisted_5$4R = ["onClick"];
109006
+ const _hoisted_6$4t = { class: "cn-deck-card-picker__row-label" };
109007
+ const _hoisted_7$42 = {
109237
109008
  key: 2,
109238
109009
  class: "cn-deck-card-picker__panel"
109239
109010
  };
109240
- const _hoisted_8$3D = {
109011
+ const _hoisted_8$3E = {
109241
109012
  key: 2,
109242
109013
  class: "cn-deck-card-picker__list"
109243
109014
  };
109244
- const _hoisted_9$3j = ["onClick"];
109245
- const _hoisted_10$2X = { class: "cn-deck-card-picker__row-label" };
109246
- const _hoisted_11$2C = {
109015
+ const _hoisted_9$3k = ["onClick"];
109016
+ const _hoisted_10$2Y = { class: "cn-deck-card-picker__row-label" };
109017
+ const _hoisted_11$2D = {
109247
109018
  key: 3,
109248
109019
  class: "cn-deck-card-picker__panel"
109249
109020
  };
109250
- const _hoisted_12$2l = {
109021
+ const _hoisted_12$2m = {
109251
109022
  key: 2,
109252
109023
  class: "cn-deck-card-picker__list"
109253
109024
  };
109254
- const _hoisted_13$25 = ["onClick"];
109255
- const _hoisted_14$1_ = { class: "cn-deck-card-picker__row-label" };
109025
+ const _hoisted_13$26 = ["onClick"];
109026
+ const _hoisted_14$1$ = { class: "cn-deck-card-picker__row-label" };
109256
109027
 
109257
- function render$7c(_ctx, _cache, $props, $setup, $data, $options) {
109028
+ function render$7d(_ctx, _cache, $props, $setup, $data, $options) {
109258
109029
  const _component_NcNoteCard = vue$1.resolveComponent("NcNoteCard");
109259
109030
  const _component_NcLoadingIcon = vue$1.resolveComponent("NcLoadingIcon");
109260
109031
  const _component_NcEmptyContent = vue$1.resolveComponent("NcEmptyContent");
@@ -109301,8 +109072,8 @@ function render$7c(_ctx, _cache, $props, $setup, $data, $options) {
109301
109072
  : vue$1.createCommentVNode("v-if", true)
109302
109073
  ]),
109303
109074
  default: vue$1.withCtx(() => [
109304
- vue$1.createElementVNode("div", _hoisted_1$6W, [
109305
- vue$1.createElementVNode("ol", _hoisted_2$6I, [
109075
+ vue$1.createElementVNode("div", _hoisted_1$6X, [
109076
+ vue$1.createElementVNode("ol", _hoisted_2$6J, [
109306
109077
  vue$1.createElementVNode("li", {
109307
109078
  class: vue$1.normalizeClass($options.stepClass(1))
109308
109079
  }, vue$1.toDisplayString($options.t('nextcloud-vue', '1. Board')), 3 /* TEXT, CLASS */),
@@ -109326,7 +109097,7 @@ function render$7c(_ctx, _cache, $props, $setup, $data, $options) {
109326
109097
  }))
109327
109098
  : vue$1.createCommentVNode("v-if", true),
109328
109099
  ($data.step === 1)
109329
- ? (vue$1.openBlock(), vue$1.createElementBlock("section", _hoisted_3$63, [
109100
+ ? (vue$1.openBlock(), vue$1.createElementBlock("section", _hoisted_3$64, [
109330
109101
  ($data.loading)
109331
109102
  ? (vue$1.openBlock(), vue$1.createBlock(_component_NcLoadingIcon, { key: 0 }))
109332
109103
  : ($data.boards.length === 0)
@@ -109335,7 +109106,7 @@ function render$7c(_ctx, _cache, $props, $setup, $data, $options) {
109335
109106
  name: $options.t('nextcloud-vue', 'No boards available'),
109336
109107
  description: $options.t('nextcloud-vue', 'NC Deck has no boards visible to you yet.')
109337
109108
  }, null, 8 /* PROPS */, ["name", "description"]))
109338
- : (vue$1.openBlock(), vue$1.createElementBlock("ul", _hoisted_4$5u, [
109109
+ : (vue$1.openBlock(), vue$1.createElementBlock("ul", _hoisted_4$5v, [
109339
109110
  (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($data.boards, (board) => {
109340
109111
  return (vue$1.openBlock(), vue$1.createElementBlock("li", {
109341
109112
  key: board.id,
@@ -109347,14 +109118,14 @@ function render$7c(_ctx, _cache, $props, $setup, $data, $options) {
109347
109118
  onClick: $event => ($options.pickBoard(board))
109348
109119
  }, [
109349
109120
  vue$1.createVNode(_component_ViewColumnOutline, { size: 20 }),
109350
- vue$1.createElementVNode("span", _hoisted_6$4s, vue$1.toDisplayString(board.title), 1 /* TEXT */)
109351
- ], 8 /* PROPS */, _hoisted_5$4Q)
109121
+ vue$1.createElementVNode("span", _hoisted_6$4t, vue$1.toDisplayString(board.title), 1 /* TEXT */)
109122
+ ], 8 /* PROPS */, _hoisted_5$4R)
109352
109123
  ], 2 /* CLASS */))
109353
109124
  }), 128 /* KEYED_FRAGMENT */))
109354
109125
  ]))
109355
109126
  ]))
109356
109127
  : ($data.step === 2)
109357
- ? (vue$1.openBlock(), vue$1.createElementBlock("section", _hoisted_7$41, [
109128
+ ? (vue$1.openBlock(), vue$1.createElementBlock("section", _hoisted_7$42, [
109358
109129
  ($data.loading)
109359
109130
  ? (vue$1.openBlock(), vue$1.createBlock(_component_NcLoadingIcon, { key: 0 }))
109360
109131
  : ($data.stacks.length === 0)
@@ -109363,7 +109134,7 @@ function render$7c(_ctx, _cache, $props, $setup, $data, $options) {
109363
109134
  name: $options.t('nextcloud-vue', 'No stacks on this board'),
109364
109135
  description: $options.t('nextcloud-vue', 'Pick a different board or add a stack in NC Deck.')
109365
109136
  }, null, 8 /* PROPS */, ["name", "description"]))
109366
- : (vue$1.openBlock(), vue$1.createElementBlock("ul", _hoisted_8$3D, [
109137
+ : (vue$1.openBlock(), vue$1.createElementBlock("ul", _hoisted_8$3E, [
109367
109138
  (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($data.stacks, (stack) => {
109368
109139
  return (vue$1.openBlock(), vue$1.createElementBlock("li", {
109369
109140
  key: stack.id,
@@ -109374,14 +109145,14 @@ function render$7c(_ctx, _cache, $props, $setup, $data, $options) {
109374
109145
  class: "cn-deck-card-picker__row-button",
109375
109146
  onClick: $event => ($options.pickStack(stack))
109376
109147
  }, [
109377
- vue$1.createElementVNode("span", _hoisted_10$2X, vue$1.toDisplayString(stack.title), 1 /* TEXT */)
109378
- ], 8 /* PROPS */, _hoisted_9$3j)
109148
+ vue$1.createElementVNode("span", _hoisted_10$2Y, vue$1.toDisplayString(stack.title), 1 /* TEXT */)
109149
+ ], 8 /* PROPS */, _hoisted_9$3k)
109379
109150
  ], 2 /* CLASS */))
109380
109151
  }), 128 /* KEYED_FRAGMENT */))
109381
109152
  ]))
109382
109153
  ]))
109383
109154
  : ($data.step === 3)
109384
- ? (vue$1.openBlock(), vue$1.createElementBlock("section", _hoisted_11$2C, [
109155
+ ? (vue$1.openBlock(), vue$1.createElementBlock("section", _hoisted_11$2D, [
109385
109156
  ($data.loading)
109386
109157
  ? (vue$1.openBlock(), vue$1.createBlock(_component_NcLoadingIcon, { key: 0 }))
109387
109158
  : ($data.cards.length === 0)
@@ -109390,7 +109161,7 @@ function render$7c(_ctx, _cache, $props, $setup, $data, $options) {
109390
109161
  name: $options.t('nextcloud-vue', 'No cards on this stack'),
109391
109162
  description: $options.t('nextcloud-vue', 'Pick a different stack or create a card in NC Deck.')
109392
109163
  }, null, 8 /* PROPS */, ["name", "description"]))
109393
- : (vue$1.openBlock(), vue$1.createElementBlock("ul", _hoisted_12$2l, [
109164
+ : (vue$1.openBlock(), vue$1.createElementBlock("ul", _hoisted_12$2m, [
109394
109165
  (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($data.cards, (card) => {
109395
109166
  return (vue$1.openBlock(), vue$1.createElementBlock("li", {
109396
109167
  key: card.id,
@@ -109401,8 +109172,8 @@ function render$7c(_ctx, _cache, $props, $setup, $data, $options) {
109401
109172
  class: "cn-deck-card-picker__row-button",
109402
109173
  onClick: $event => ($options.pickCard(card))
109403
109174
  }, [
109404
- vue$1.createElementVNode("span", _hoisted_14$1_, vue$1.toDisplayString(card.title), 1 /* TEXT */)
109405
- ], 8 /* PROPS */, _hoisted_13$25)
109175
+ vue$1.createElementVNode("span", _hoisted_14$1$, vue$1.toDisplayString(card.title), 1 /* TEXT */)
109176
+ ], 8 /* PROPS */, _hoisted_13$26)
109406
109177
  ], 2 /* CLASS */))
109407
109178
  }), 128 /* KEYED_FRAGMENT */))
109408
109179
  ]))
@@ -109414,8 +109185,563 @@ function render$7c(_ctx, _cache, $props, $setup, $data, $options) {
109414
109185
  }, 8 /* PROPS */, ["name", "onClosing"]))
109415
109186
  }
109416
109187
 
109188
+ script$7e.render = render$7d;
109189
+ script$7e.__scopeId = "data-v-7861e746";
109190
+ const __sfc_main$78 = script$7e;
109191
+
109192
+ const VALID_SURFACES$e = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'];
109193
+
109194
+ /**
109195
+ * CnDeckCard — bespoke surface-aware widget for the `deck` integration.
109196
+ *
109197
+ * Renders Deck-aware metadata across all four surfaces. See the
109198
+ * file-level docblock for surface-by-surface behaviour.
109199
+ */
109200
+ var script$7d = {
109201
+ name: 'CnDeckCard',
109202
+
109203
+ components: { CnDeckCardCreate: __sfc_main$79, CnDeckCardPicker: __sfc_main$78, CnDetailCard: __sfc_main$7F, LinkVariant, NcButton: vue.NcButton, NcLoadingIcon: vue.NcLoadingIcon, Plus, ViewColumnOutline },
109204
+
109205
+ props: {
109206
+ /** Stable integration id (forwarded from the registry — always `'deck'`). */
109207
+ integrationId: { type: String, default: 'deck' },
109208
+ /** OpenRegister register id (slug or uuid). */
109209
+ register: { type: String, required: true },
109210
+ /** OpenRegister schema id (slug or uuid). */
109211
+ schema: { type: String, required: true },
109212
+ /** Parent object id. */
109213
+ objectId: { type: String, required: true },
109214
+ /** Rendering surface (AD-19). */
109215
+ surface: {
109216
+ type: String,
109217
+ default: 'detail-page',
109218
+ validator: (s) => VALID_SURFACES$e.includes(s),
109219
+ },
109220
+
109221
+ /** Optional single-entity reference (card id). */
109222
+ value: { type: [String, Number], default: '' },
109223
+ /** Pre-translated card title. */
109224
+ title: { type: String, default: () => l10n.translate('nextcloud-vue', 'Cards') },
109225
+ /** Optional Material Design Icon component. */
109226
+ icon: { type: Object, default: () => ViewColumnOutline },
109227
+ /** Base API URL. */
109228
+ apiBase: { type: String, default: '/apps/openregister/api' },
109229
+ /** Whether the card body is collapsible. */
109230
+ collapsible: { type: Boolean, default: true },
109231
+ /** Pre-translated empty-state label. */
109232
+ emptyLabel: { type: String, default: () => l10n.translate('nextcloud-vue', 'No cards linked yet') },
109233
+ /** Pre-translated unavailable label. */
109234
+ unavailableLabel: { type: String, default: () => l10n.translate('nextcloud-vue', 'NC Deck is currently unavailable.') },
109235
+ /** URL of the NC Deck app entry. */
109236
+ deckAppUrl: { type: String, default: '/index.php/apps/deck' },
109237
+ /**
109238
+ * Buttons in the card's title row on a detail page: `link` links an
109239
+ * existing Deck card, `create` makes a new one linked to the object.
109240
+ * In the order given; none by default.
109241
+ */
109242
+ headerActions: { type: Array, default: () => [] },
109243
+ /** Label of the `link` button. */
109244
+ linkLabel: { type: String, default: () => l10n.translate('nextcloud-vue', 'Link an existing card') },
109245
+ /** Label of the `create` button. */
109246
+ createLabel: { type: String, default: () => l10n.translate('nextcloud-vue', 'New card') },
109247
+ },
109248
+
109249
+ data() {
109250
+ return {
109251
+ cards: [],
109252
+ entity: null,
109253
+ loading: false,
109254
+ degraded: '',
109255
+ pickerOpen: false,
109256
+ createOpen: false,
109257
+ actionError: '',
109258
+ }
109259
+ },
109260
+
109261
+ computed: {
109262
+ /**
109263
+ * The title-row buttons to draw: only on a detail page with an
109264
+ * object, only the known ones, in the order given.
109265
+ *
109266
+ * @return {Array<string>}
109267
+ */
109268
+ shownHeaderActions() {
109269
+ if (this.surface !== 'detail-page' || !this.objectId || !Array.isArray(this.headerActions)) {
109270
+ return []
109271
+ }
109272
+ return this.headerActions.filter((action, index, all) => ['create', 'link'].includes(action) && all.indexOf(action) === index)
109273
+ },
109274
+
109275
+ cardHeaderTitle() {
109276
+ return this.title || this.integrationId
109277
+ },
109278
+
109279
+ cardIcon() {
109280
+ return this.icon
109281
+ },
109282
+
109283
+ /**
109284
+ * Cards grouped by stack for both the mini-kanban (detail-page)
109285
+ * and the stack-distribution list (dashboard surfaces).
109286
+ *
109287
+ * @return {Array<{key: string, label: string, cards: Array}>}
109288
+ */
109289
+ columns() {
109290
+ const groups = new Map();
109291
+ for (const card of this.cards) {
109292
+ const stackId = card.stackId ?? card.stack?.id ?? 'unstacked';
109293
+ const stackLabel = card.stackTitle
109294
+ ?? card.stack?.title
109295
+ ?? (stackId === 'unstacked'
109296
+ ? l10n.translate('nextcloud-vue', 'No stack')
109297
+ : l10n.translate('nextcloud-vue', 'Stack {n}', { n: stackId }));
109298
+ const key = String(stackId);
109299
+ if (!groups.has(key)) {
109300
+ groups.set(key, { key, label: stackLabel, cards: [] });
109301
+ }
109302
+ groups.get(key).cards.push(card);
109303
+ }
109304
+ return Array.from(groups.values())
109305
+ },
109306
+
109307
+ /**
109308
+ * Up to three columns for the mini-kanban view on `detail-page`.
109309
+ *
109310
+ * Per AD-2 (design.md): when the linked card has its own stack
109311
+ * we always include that stack; otherwise the first three stacks
109312
+ * by first-seen order are surfaced.
109313
+ *
109314
+ * @return {Array<{key: string, label: string, cards: Array}>}
109315
+ */
109316
+ miniColumns() {
109317
+ return this.columns.slice(0, 3)
109318
+ },
109319
+
109320
+ stackDistribution() {
109321
+ return this.columns.map((col) => ({
109322
+ key: col.key,
109323
+ label: col.label,
109324
+ count: col.cards.length,
109325
+ }))
109326
+ },
109327
+
109328
+ countHeadline() {
109329
+ const total = this.cards.length;
109330
+ const stackCount = this.columns.length;
109331
+ if (stackCount <= 1) {
109332
+ return l10n.translatePlural('nextcloud-vue', '{count} card', '{count} cards', total, { count: total })
109333
+ }
109334
+ const cardFragment = l10n.translatePlural('nextcloud-vue', '{count} card', '{count} cards', total, { count: total });
109335
+ const stacksFragment = l10n.translatePlural('nextcloud-vue', 'across {count} stack', 'across {count} stacks', stackCount, { count: stackCount });
109336
+ return `${cardFragment} ${stacksFragment}`
109337
+ },
109338
+
109339
+ mostRecent() {
109340
+ if (this.cards.length === 0) {
109341
+ return null
109342
+ }
109343
+ const sorted = [...this.cards].sort((a, b) => {
109344
+ const ta = Date.parse(a.linkedAt ?? a.lastActivity ?? '') || 0;
109345
+ const tb = Date.parse(b.linkedAt ?? b.lastActivity ?? '') || 0;
109346
+ return tb - ta
109347
+ });
109348
+ return sorted[0]
109349
+ },
109350
+ },
109351
+
109352
+ watch: {
109353
+ objectId: { immediate: true, handler() {
109354
+ this.fetch();
109355
+ } },
109356
+
109357
+ surface() {
109358
+ this.fetch();
109359
+ },
109360
+
109361
+ value() {
109362
+ if (this.surface === 'single-entity') {
109363
+ this.fetchSingle();
109364
+ }
109365
+ },
109366
+ },
109367
+
109368
+ methods: {
109369
+ /** @return {string} The object's Deck endpoint the link and create flows post to. */
109370
+ deckEndpoint() {
109371
+ return `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/deck`
109372
+ },
109373
+
109374
+ /**
109375
+ * Link the picked card, then read the list again.
109376
+ *
109377
+ * @param {object} payload `{ cardId }` from the picker.
109378
+ */
109379
+ async onLink(payload) {
109380
+ this.pickerOpen = false;
109381
+ await this.post(this.deckEndpoint(), payload, l10n.translate('nextcloud-vue', 'Could not link card.'));
109382
+ },
109383
+
109384
+ /**
109385
+ * Create the card linked to the object, then read the list again.
109386
+ *
109387
+ * @param {object} payload `{ boardId, stackId, title, ... }` from the form.
109388
+ */
109389
+ async onCreate(payload) {
109390
+ this.createOpen = false;
109391
+ await this.post(`${this.deckEndpoint()}/new`, payload, l10n.translate('nextcloud-vue', 'Could not create card.'));
109392
+ },
109393
+
109394
+ /**
109395
+ * POST one flow's payload and reload on success.
109396
+ *
109397
+ * @param {string} url The endpoint.
109398
+ * @param {object} payload The body.
109399
+ * @param {string} failure The sentence shown when it fails.
109400
+ */
109401
+ async post(url, payload, failure) {
109402
+ this.actionError = '';
109403
+ try {
109404
+ const response = await fetch(prefixUrl(url), {
109405
+ method: 'POST',
109406
+ headers: { ...buildHeaders(), 'Content-Type': 'application/json' },
109407
+ body: JSON.stringify(payload),
109408
+ });
109409
+ if (response.ok) {
109410
+ await this.fetchList();
109411
+ } else if (response.status === 409) {
109412
+ this.actionError = l10n.translate('nextcloud-vue', 'This card is already linked.');
109413
+ } else {
109414
+ this.actionError = failure;
109415
+ }
109416
+ } catch {
109417
+ this.actionError = failure;
109418
+ }
109419
+ },
109420
+
109421
+ baseUrl() {
109422
+ return `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`
109423
+ },
109424
+
109425
+ cardKey(card) {
109426
+ return card.cardId ?? card.id ?? ''
109427
+ },
109428
+
109429
+ cardTitle(card) {
109430
+ return card.cardTitle ?? card.title ?? this.cardKey(card)
109431
+ },
109432
+
109433
+ cardUrl(card) {
109434
+ if (card.url && safeHref(card.url) !== '#') {
109435
+ return card.url
109436
+ }
109437
+ const boardId = card.boardId ?? card.board?.id ?? '';
109438
+ const cardId = card.cardId ?? card.id ?? '';
109439
+ if (boardId && cardId) {
109440
+ return `/index.php/apps/deck/board/${boardId}/card/${cardId}`
109441
+ }
109442
+ return this.deckAppUrl
109443
+ },
109444
+
109445
+ stackTitle(card) {
109446
+ return card.stackTitle ?? card.stack?.title ?? ''
109447
+ },
109448
+
109449
+ isLinked(card) {
109450
+ if (!this.value) {
109451
+ return false
109452
+ }
109453
+ return String(this.cardKey(card)) === String(this.value)
109454
+ },
109455
+
109456
+ chipSubtitle(card) {
109457
+ return this.stackTitle(card) || this.cardTitle(card)
109458
+ },
109459
+
109460
+ fetch() {
109461
+ if (this.surface === 'single-entity') {
109462
+ this.fetchSingle();
109463
+ return
109464
+ }
109465
+ this.fetchList();
109466
+ },
109467
+
109468
+ async fetchList() {
109469
+ if (!this.register || !this.schema || !this.objectId) {
109470
+ return
109471
+ }
109472
+ this.loading = true;
109473
+ this.degraded = '';
109474
+ try {
109475
+ const response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() });
109476
+ if (response.ok) {
109477
+ const data = await response.json();
109478
+ this.cards = data.results || data.items || (Array.isArray(data) ? data : []) || [];
109479
+ } else if (response.status === 503) {
109480
+ this.cards = [];
109481
+ this.degraded = this.unavailableLabel;
109482
+ } else {
109483
+ this.cards = [];
109484
+ }
109485
+ } catch (err) {
109486
+ // eslint-disable-next-line no-console
109487
+ console.error('[CnDeckCard] failed to fetch cards', err);
109488
+ this.cards = [];
109489
+ } finally {
109490
+ this.loading = false;
109491
+ }
109492
+ },
109493
+
109494
+ async fetchSingle() {
109495
+ if (!this.value || !this.register || !this.schema || !this.objectId) {
109496
+ this.entity = null;
109497
+ return
109498
+ }
109499
+ this.loading = true;
109500
+ this.degraded = '';
109501
+ try {
109502
+ const response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() });
109503
+ if (response.ok) {
109504
+ this.entity = await response.json();
109505
+ } else if (response.status === 503) {
109506
+ this.entity = null;
109507
+ this.degraded = this.unavailableLabel;
109508
+ } else {
109509
+ this.entity = null;
109510
+ }
109511
+ } catch (err) {
109512
+ // eslint-disable-next-line no-console
109513
+ console.error('[CnDeckCard] failed to fetch single card', err);
109514
+ this.entity = null;
109515
+ } finally {
109516
+ this.loading = false;
109517
+ }
109518
+ },
109519
+ },
109520
+ };
109521
+
109522
+ const _hoisted_1$6W = ["title"];
109523
+ const _hoisted_2$6I = ["href"];
109524
+ const _hoisted_3$63 = {
109525
+ key: 0,
109526
+ class: "cn-deck-card__chip-stack"
109527
+ };
109528
+ const _hoisted_4$5u = {
109529
+ key: 1,
109530
+ class: "cn-deck-card__empty"
109531
+ };
109532
+ const _hoisted_5$4Q = {
109533
+ key: 0,
109534
+ class: "cn-deck-card__empty"
109535
+ };
109536
+ const _hoisted_6$4s = {
109537
+ key: 1,
109538
+ class: "cn-deck-card__empty"
109539
+ };
109540
+ const _hoisted_7$41 = {
109541
+ key: 2,
109542
+ class: "cn-deck-card__headline"
109543
+ };
109544
+ const _hoisted_8$3D = { class: "cn-deck-card__headline-line" };
109545
+ const _hoisted_9$3j = {
109546
+ key: 0,
109547
+ class: "cn-deck-card__distribution"
109548
+ };
109549
+ const _hoisted_10$2X = { class: "cn-deck-card__distribution-label" };
109550
+ const _hoisted_11$2C = { class: "cn-deck-card__distribution-count" };
109551
+ const _hoisted_12$2l = {
109552
+ key: 1,
109553
+ class: "cn-deck-card__headline-recent"
109554
+ };
109555
+ const _hoisted_13$25 = ["href"];
109556
+ const _hoisted_14$1_ = {
109557
+ key: 0,
109558
+ class: "cn-deck-card__empty"
109559
+ };
109560
+ const _hoisted_15$1R = {
109561
+ key: 1,
109562
+ class: "cn-deck-card__empty"
109563
+ };
109564
+ const _hoisted_16$1F = {
109565
+ key: 2,
109566
+ class: "cn-deck-card__kanban"
109567
+ };
109568
+ const _hoisted_17$1s = { class: "cn-deck-card__column-header" };
109569
+ const _hoisted_18$1f = { class: "cn-deck-card__column-title" };
109570
+ const _hoisted_19$19 = { class: "cn-deck-card__column-count" };
109571
+ const _hoisted_20$11 = { class: "cn-deck-card__column-list" };
109572
+ const _hoisted_21$$ = ["href"];
109573
+ const _hoisted_22$_ = {
109574
+ key: 4,
109575
+ class: "cn-deck-card__action-error",
109576
+ role: "alert"
109577
+ };
109578
+
109579
+ function render$7c(_ctx, _cache, $props, $setup, $data, $options) {
109580
+ const _component_Plus = vue$1.resolveComponent("Plus");
109581
+ const _component_LinkVariant = vue$1.resolveComponent("LinkVariant");
109582
+ const _component_NcButton = vue$1.resolveComponent("NcButton");
109583
+ const _component_NcLoadingIcon = vue$1.resolveComponent("NcLoadingIcon");
109584
+ const _component_ViewColumnOutline = vue$1.resolveComponent("ViewColumnOutline");
109585
+ const _component_CnDeckCardPicker = vue$1.resolveComponent("CnDeckCardPicker");
109586
+ const _component_CnDeckCardCreate = vue$1.resolveComponent("CnDeckCardCreate");
109587
+ const _component_CnDetailCard = vue$1.resolveComponent("CnDetailCard");
109588
+
109589
+ return (vue$1.openBlock(), vue$1.createBlock(_component_CnDetailCard, {
109590
+ title: $options.cardHeaderTitle,
109591
+ icon: $options.cardIcon,
109592
+ collapsible: $props.collapsible
109593
+ }, vue$1.createSlots({
109594
+ default: vue$1.withCtx(() => [
109595
+ ($data.loading)
109596
+ ? (vue$1.openBlock(), vue$1.createBlock(_component_NcLoadingIcon, { key: 0 }))
109597
+ : ($props.surface === 'single-entity')
109598
+ ? (vue$1.openBlock(), vue$1.createElementBlock(vue$1.Fragment, { key: 1 }, [
109599
+ ($data.entity)
109600
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", {
109601
+ key: 0,
109602
+ class: "cn-deck-card__chip",
109603
+ title: $options.chipSubtitle($data.entity)
109604
+ }, [
109605
+ vue$1.createVNode(_component_ViewColumnOutline, { size: 14 }),
109606
+ vue$1.createElementVNode("a", {
109607
+ href: $options.cardUrl($data.entity),
109608
+ target: "_blank",
109609
+ rel: "noopener"
109610
+ }, vue$1.toDisplayString($options.cardTitle($data.entity)), 9 /* TEXT, PROPS */, _hoisted_2$6I),
109611
+ ($options.stackTitle($data.entity))
109612
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_3$63, " · " + vue$1.toDisplayString($options.stackTitle($data.entity)), 1 /* TEXT */))
109613
+ : vue$1.createCommentVNode("v-if", true)
109614
+ ], 8 /* PROPS */, _hoisted_1$6W))
109615
+ : (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_4$5u, vue$1.toDisplayString($props.emptyLabel), 1 /* TEXT */))
109616
+ ], 64 /* STABLE_FRAGMENT */))
109617
+ : ($props.surface === 'user-dashboard' || $props.surface === 'app-dashboard')
109618
+ ? (vue$1.openBlock(), vue$1.createElementBlock(vue$1.Fragment, { key: 2 }, [
109619
+ ($data.degraded)
109620
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_5$4Q, vue$1.toDisplayString($data.degraded), 1 /* TEXT */))
109621
+ : ($data.cards.length === 0)
109622
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_6$4s, vue$1.toDisplayString($props.emptyLabel), 1 /* TEXT */))
109623
+ : (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_7$41, [
109624
+ vue$1.createElementVNode("div", _hoisted_8$3D, [
109625
+ vue$1.createElementVNode("strong", null, vue$1.toDisplayString($options.countHeadline), 1 /* TEXT */)
109626
+ ]),
109627
+ ($options.stackDistribution.length > 0)
109628
+ ? (vue$1.openBlock(), vue$1.createElementBlock("ul", _hoisted_9$3j, [
109629
+ (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($options.stackDistribution, (bucket) => {
109630
+ return (vue$1.openBlock(), vue$1.createElementBlock("li", {
109631
+ key: bucket.key,
109632
+ class: "cn-deck-card__distribution-row"
109633
+ }, [
109634
+ vue$1.createElementVNode("span", _hoisted_10$2X, vue$1.toDisplayString(bucket.label), 1 /* TEXT */),
109635
+ vue$1.createElementVNode("span", _hoisted_11$2C, vue$1.toDisplayString(bucket.count), 1 /* TEXT */)
109636
+ ]))
109637
+ }), 128 /* KEYED_FRAGMENT */))
109638
+ ]))
109639
+ : vue$1.createCommentVNode("v-if", true),
109640
+ ($options.mostRecent)
109641
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_12$2l, [
109642
+ vue$1.createVNode(_component_ViewColumnOutline, { size: 14 }),
109643
+ vue$1.createElementVNode("a", {
109644
+ href: $options.cardUrl($options.mostRecent),
109645
+ target: "_blank",
109646
+ rel: "noopener"
109647
+ }, vue$1.toDisplayString($options.cardTitle($options.mostRecent)), 9 /* TEXT, PROPS */, _hoisted_13$25)
109648
+ ]))
109649
+ : vue$1.createCommentVNode("v-if", true)
109650
+ ]))
109651
+ ], 64 /* STABLE_FRAGMENT */))
109652
+ : (vue$1.openBlock(), vue$1.createElementBlock(vue$1.Fragment, { key: 3 }, [
109653
+ ($data.degraded)
109654
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_14$1_, vue$1.toDisplayString($data.degraded), 1 /* TEXT */))
109655
+ : ($data.cards.length === 0)
109656
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_15$1R, vue$1.toDisplayString($props.emptyLabel), 1 /* TEXT */))
109657
+ : (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_16$1F, [
109658
+ (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($options.miniColumns, (stack) => {
109659
+ return (vue$1.openBlock(), vue$1.createElementBlock("section", {
109660
+ key: stack.key,
109661
+ class: "cn-deck-card__column"
109662
+ }, [
109663
+ vue$1.createElementVNode("header", _hoisted_17$1s, [
109664
+ vue$1.createElementVNode("span", _hoisted_18$1f, vue$1.toDisplayString(stack.label), 1 /* TEXT */),
109665
+ vue$1.createElementVNode("span", _hoisted_19$19, vue$1.toDisplayString(stack.cards.length), 1 /* TEXT */)
109666
+ ]),
109667
+ vue$1.createElementVNode("ul", _hoisted_20$11, [
109668
+ (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList(stack.cards, (card) => {
109669
+ return (vue$1.openBlock(), vue$1.createElementBlock("li", {
109670
+ key: $options.cardKey(card),
109671
+ class: vue$1.normalizeClass(["cn-deck-card__row", { 'cn-deck-card__row--highlight': $options.isLinked(card) }])
109672
+ }, [
109673
+ vue$1.createElementVNode("a", {
109674
+ href: $options.cardUrl(card),
109675
+ target: "_blank",
109676
+ rel: "noopener",
109677
+ class: "cn-deck-card__title"
109678
+ }, vue$1.toDisplayString($options.cardTitle(card)), 9 /* TEXT, PROPS */, _hoisted_21$$)
109679
+ ], 2 /* CLASS */))
109680
+ }), 128 /* KEYED_FRAGMENT */))
109681
+ ])
109682
+ ]))
109683
+ }), 128 /* KEYED_FRAGMENT */))
109684
+ ]))
109685
+ ], 64 /* STABLE_FRAGMENT */)),
109686
+ ($data.actionError)
109687
+ ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_22$_, vue$1.toDisplayString($data.actionError), 1 /* TEXT */))
109688
+ : vue$1.createCommentVNode("v-if", true),
109689
+ ($data.pickerOpen)
109690
+ ? (vue$1.openBlock(), vue$1.createBlock(_component_CnDeckCardPicker, {
109691
+ key: 5,
109692
+ apiBase: $props.apiBase,
109693
+ onClose: _cache[0] || (_cache[0] = $event => ($data.pickerOpen = false)),
109694
+ onLink: $options.onLink
109695
+ }, null, 8 /* PROPS */, ["apiBase", "onLink"]))
109696
+ : vue$1.createCommentVNode("v-if", true),
109697
+ ($data.createOpen)
109698
+ ? (vue$1.openBlock(), vue$1.createBlock(_component_CnDeckCardCreate, {
109699
+ key: 6,
109700
+ apiBase: $props.apiBase,
109701
+ onClose: _cache[1] || (_cache[1] = $event => ($data.createOpen = false)),
109702
+ onCreate: $options.onCreate
109703
+ }, null, 8 /* PROPS */, ["apiBase", "onCreate"]))
109704
+ : vue$1.createCommentVNode("v-if", true)
109705
+ ]),
109706
+ _: 2 /* DYNAMIC */
109707
+ }, [
109708
+ ($options.shownHeaderActions.length > 0)
109709
+ ? {
109710
+ name: "actions",
109711
+ fn: vue$1.withCtx(() => [
109712
+ (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($options.shownHeaderActions, (action) => {
109713
+ return (vue$1.openBlock(), vue$1.createBlock(_component_NcButton, {
109714
+ key: action,
109715
+ variant: "secondary",
109716
+ "data-testid": `cn-deck-card-${action}`,
109717
+ onClick: vue$1.withModifiers($event => (action === 'create' ? ($data.createOpen = true) : ($data.pickerOpen = true)), ["stop"])
109718
+ }, {
109719
+ icon: vue$1.withCtx(() => [
109720
+ (action === 'create')
109721
+ ? (vue$1.openBlock(), vue$1.createBlock(_component_Plus, {
109722
+ key: 0,
109723
+ size: 20
109724
+ }))
109725
+ : (vue$1.openBlock(), vue$1.createBlock(_component_LinkVariant, {
109726
+ key: 1,
109727
+ size: 20
109728
+ }))
109729
+ ]),
109730
+ default: vue$1.withCtx(() => [
109731
+ vue$1.createTextVNode(" " + vue$1.toDisplayString(action === 'create' ? $props.createLabel : $props.linkLabel), 1 /* TEXT */)
109732
+ ]),
109733
+ _: 2 /* DYNAMIC */
109734
+ }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["data-testid", "onClick"]))
109735
+ }), 128 /* KEYED_FRAGMENT */))
109736
+ ]),
109737
+ key: "0"
109738
+ }
109739
+ : undefined
109740
+ ]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["title", "icon", "collapsible"]))
109741
+ }
109742
+
109417
109743
  script$7d.render = render$7c;
109418
- script$7d.__scopeId = "data-v-7861e746";
109744
+ script$7d.__scopeId = "data-v-d8cff1c2";
109419
109745
  const __sfc_main$77 = script$7d;
109420
109746
 
109421
109747
  /** Maximum assignee avatars shown before collapsing the rest into a +N overflow badge. */
@@ -109440,8 +109766,8 @@ var script$7c = {
109440
109766
  LinkVariant,
109441
109767
  Plus,
109442
109768
  ViewColumnOutline,
109443
- CnDeckCardPicker: __sfc_main$77,
109444
- CnDeckCardCreate: __sfc_main$78,
109769
+ CnDeckCardPicker: __sfc_main$78,
109770
+ CnDeckCardCreate: __sfc_main$79,
109445
109771
  CnStatusBadge: __sfc_main$8g,
109446
109772
  },
109447
109773
 
@@ -110083,7 +110409,7 @@ const deckIntegration = {
110083
110409
  group: 'workflow',
110084
110410
  referenceType: 'deck',
110085
110411
  tab: __sfc_main$76,
110086
- widget: __sfc_main$79,
110412
+ widget: __sfc_main$77,
110087
110413
  defaultSize: { w: 4, h: 3 },
110088
110414
  // CnIntegrationWidget contract — exemplar accent (NC Deck brand blue).
110089
110415
  // `appName` / `docsUrl` default from id+label in the registry.
@@ -116836,10 +117162,22 @@ var script$71 = {
116836
117162
  * @spec openspec/changes/files-drop-panel/specs/files-browser/spec.md#requirement-the-drop-state-fills-the-panel-and-says-where-the-files-go
116837
117163
  */
116838
117164
  dropDetail: {
116839
- type: String,
117165
+ type: [String, Array],
116840
117166
  default: '',
116841
117167
  },
116842
117168
 
117169
+ /**
117170
+ * Named values filled into `dropDetail` as `{name}`, such as the case
117171
+ * number (`{ "case": "@object.identifier" }` in a detail page's props).
117172
+ * While a value is unknown the line is left out.
117173
+ *
117174
+ * @spec openspec/changes/files-drop-detail-values/specs/files-drop-detail-values/spec.md#requirement-the-drop-detail-line-can-name-the-record
117175
+ */
117176
+ dropDetailValues: {
117177
+ type: Object,
117178
+ default: null,
117179
+ },
117180
+
116843
117181
  /** Show a line under the list saying files can be dropped on it. */
116844
117182
  dropHint: {
116845
117183
  type: Boolean,
@@ -117132,6 +117470,22 @@ var script$71 = {
117132
117470
  },
117133
117471
 
117134
117472
  computed: {
117473
+ /**
117474
+ * The `dropDetail` text: the string, or the text of the first variant
117475
+ * `{ when, text }` whose `when` is `true` or that has none.
117476
+ *
117477
+ * @return {string}
117478
+ * @spec openspec/changes/files-drop-detail-values/specs/files-drop-detail-values/spec.md#requirement-the-drop-detail-line-can-name-the-record
117479
+ */
117480
+ dropDetailTemplate() {
117481
+ if (typeof this.dropDetail === 'string') {
117482
+ return this.dropDetail
117483
+ }
117484
+ const variants = Array.isArray(this.dropDetail) ? this.dropDetail : [];
117485
+ const chosen = variants.find((item) => item && typeof item.text === 'string' && (!('when' in item) || item.when === true));
117486
+ return chosen ? chosen.text : ''
117487
+ },
117488
+
117135
117489
  /**
117136
117490
  * `dropDetail` through the host translate with `{count}` filled, '' when
117137
117491
  * there is none or the count it needs is unknown.
@@ -117140,10 +117494,22 @@ var script$71 = {
117140
117494
  * @return {string}
117141
117495
  */
117142
117496
  dropDetailText() {
117143
- if (!this.dropDetail) {
117497
+ const template = this.dropDetailTemplate;
117498
+ if (!template) {
117144
117499
  return ''
117145
117500
  }
117146
- const text = this.cnTranslate(this.dropDetail);
117501
+ let text = this.cnTranslate(template);
117502
+ // `dropDetailValues`: named values such as the case number; a value
117503
+ // still unknown (an unresolved `@` token, or empty) leaves the line out.
117504
+ // @spec openspec/changes/files-drop-detail-values/specs/files-drop-detail-values/spec.md#requirement-the-drop-detail-line-can-name-the-record
117505
+ const values = this.dropDetailValues && typeof this.dropDetailValues === 'object' ? this.dropDetailValues : {};
117506
+ for (const [name, value] of Object.entries(values)) {
117507
+ const shown = value === null || value === undefined ? '' : String(value);
117508
+ if (shown === '' || shown.startsWith('@')) {
117509
+ return ''
117510
+ }
117511
+ text = text.split(`{${name}}`).join(shown);
117512
+ }
117147
117513
  if (!text.includes('{count}')) {
117148
117514
  return text
117149
117515
  }
@@ -119043,7 +119409,7 @@ function render$70(_ctx, _cache, $props, $setup, $data, $options) {
119043
119409
  const _component_NcDialog = vue$1.resolveComponent("NcDialog");
119044
119410
 
119045
119411
  return (vue$1.openBlock(), vue$1.createElementBlock("div", {
119046
- class: vue$1.normalizeClass(["cn-files-browser", { 'cn-files-browser--dragging': $data.dragging, 'cn-files-browser--drop-panel': $props.dropOverlay && $props.dropDetail !== '' }]),
119412
+ class: vue$1.normalizeClass(["cn-files-browser", { 'cn-files-browser--dragging': $data.dragging, 'cn-files-browser--drop-panel': $props.dropOverlay && $options.dropDetailTemplate !== '' }]),
119047
119413
  "data-testid": "cn-files-browser",
119048
119414
  onDragenter: _cache[21] || (_cache[21] = vue$1.withModifiers((...args) => ($options.onDragEnter && $options.onDragEnter(...args)), ["prevent"])),
119049
119415
  onDragover: _cache[22] || (_cache[22] = vue$1.withModifiers((...args) => ($options.onDragOver && $options.onDragOver(...args)), ["prevent"])),
@@ -120079,7 +120445,7 @@ function render$70(_ctx, _cache, $props, $setup, $data, $options) {
120079
120445
  }
120080
120446
 
120081
120447
  script$71.render = render$70;
120082
- script$71.__scopeId = "data-v-3a771a01";
120448
+ script$71.__scopeId = "data-v-ebaa2c0e";
120083
120449
  const __sfc_main$6X = script$71;
120084
120450
 
120085
120451
  /**
@@ -120423,7 +120789,9 @@ var script$70 = {
120423
120789
  /** Forwarded to CnFilesBrowser: the drop state's line ("Laat los om toe te voegen"); the library's own without it. */
120424
120790
  dropLabel: { type: String, default: undefined },
120425
120791
  /** Forwarded to CnFilesBrowser: the drop state's second line, `{count}` the files dragged; it also makes the overlay fill the panel. */
120426
- dropDetail: { type: String, default: '' },
120792
+ dropDetail: { type: [String, Array], default: '' },
120793
+ /** Forwarded to CnFilesBrowser: named values filled into `dropDetail`. */
120794
+ dropDetailValues: { type: Object, default: null },
120427
120795
  /** Forwarded to CnFilesBrowser: "Or drag files onto this list." under the list. */
120428
120796
  dropHint: { type: Boolean, default: false },
120429
120797
  /**
@@ -121212,6 +121580,7 @@ function render$6$(_ctx, _cache, $props, $setup, $data, $options) {
121212
121580
  dropOverlay: $props.dropOverlay,
121213
121581
  dropLabel: $props.dropLabel,
121214
121582
  dropDetail: $props.dropDetail,
121583
+ dropDetailValues: $props.dropDetailValues,
121215
121584
  dropHint: $props.dropHint,
121216
121585
  rowActions: $props.rowActions,
121217
121586
  newActions: $props.newActions,
@@ -121235,7 +121604,7 @@ function render$6$(_ctx, _cache, $props, $setup, $data, $options) {
121235
121604
  groupBy: $props.groupBy,
121236
121605
  facets: $props.facets,
121237
121606
  onChanged: $options.onBrowserChanged
121238
- }, null, 8 /* PROPS */, ["rootPath", "rootLabel", "source", "deleteLabel", "uploadButton", "newMenu", "folderButton", "dropOverlay", "dropLabel", "dropDetail", "dropHint", "rowActions", "newActions", "headerActions", "rowButton", "reviewSummary", "footnote", "nameSecondary", "secondLine", "linkedItems", "columns", "rowData", "preferenceApp", "preferenceKey", "bulkActions", "selectionLabel", "selectionLabelOne", "showClearSelection", "sideCards", "sideCardsPlacement", "groupBy", "facets", "onChanged"]))
121607
+ }, null, 8 /* PROPS */, ["rootPath", "rootLabel", "source", "deleteLabel", "uploadButton", "newMenu", "folderButton", "dropOverlay", "dropLabel", "dropDetail", "dropDetailValues", "dropHint", "rowActions", "newActions", "headerActions", "rowButton", "reviewSummary", "footnote", "nameSecondary", "secondLine", "linkedItems", "columns", "rowData", "preferenceApp", "preferenceKey", "bulkActions", "selectionLabel", "selectionLabelOne", "showClearSelection", "sideCards", "sideCardsPlacement", "groupBy", "facets", "onChanged"]))
121239
121608
  : (vue$1.openBlock(), vue$1.createElementBlock(vue$1.Fragment, { key: 1 }, [
121240
121609
  ($data.uploadError)
121241
121610
  ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_2$6w, vue$1.toDisplayString($data.uploadError), 1 /* TEXT */))
@@ -121486,7 +121855,7 @@ function render$6$(_ctx, _cache, $props, $setup, $data, $options) {
121486
121855
  }
121487
121856
 
121488
121857
  script$70.render = render$6$;
121489
- script$70.__scopeId = "data-v-89d80864";
121858
+ script$70.__scopeId = "data-v-569603d2";
121490
121859
  const __sfc_main$6W = script$70;
121491
121860
 
121492
121861
  /**
@@ -236927,6 +237296,8 @@ var script$48 = {
236927
237296
  cnDispatchAction: { default: null },
236928
237297
  /** The page's object sidebar channel; a `link.sidebarTab` opens one of its tabs. */
236929
237298
  objectSidebarState: { default: null },
237299
+ /** The records the detail page reads beside the record (`@context.<name>`), for a `labelPath`. */
237300
+ cnDetailVisibilityRows: { default: null },
236930
237301
  },
236931
237302
 
236932
237303
  props: {
@@ -236943,13 +237314,14 @@ var script$48 = {
236943
237314
  * `route` (`{ name, params?, query? }`, `@source.<path>` in a param) on a `title` or a `row` links its value to that route; a param that does not fill leaves it plain.
236944
237315
  * `format: 'user'` shows a Nextcloud user id as the user's display name, read once per id; the entry stays empty until the name is known.
236945
237316
  * `template` ("{available} {unit}") fills its `{path}` placeholders from the record instead of reading `path`; it is empty when every placeholder is. `suffix` (" kg") is added to a value that is not empty.
237317
+ * `labelPath` ("@context.caseType.handlerLabel") names a value on the record or the page's `@context` records that labels the row when it holds text; `label` is the fallback.
236946
237318
  * `emptyValue` ("No") is shown, translated, when the entry has no value, so the entry stays drawn whatever `hideEmpty` says (an override that was never set reads "No").
236947
237319
  * - `action` — an action drawn as a button under the facts; `headerAction` — one drawn in the card header (reported through the `header` event).
236948
237320
  * - `actions` — action buttons under the facts in the `headerActions` shape (`api-call`, `open-form`, `navigate`, `open-modal`, ...; `confirm`, `variant`). `@source.<field>` names a field of the record shown, and an action whose token names an empty field is not drawn. `visibleWhen` reads the record shown; `adminOnly: true` draws the action for an instance admin only (visibility, never authorisation). An api-call or a saved form refreshes the page, and the facts read their source again.
236949
237321
  * - `rowsPath` — a path to a list of `{ label, value }` the record holds, drawn as rows after `fields` (a preview the server builds).
236950
237322
  * - `headerPill` — a status pill in the card header (reported through the `header` event): `{ path?, text?, map?, variant?, variantMap?, colorMap? }`, read from the record like a `pill` entry (DqBezwaar "Advies ontvangen"); nothing shows while the value is empty.
236951
237323
  * - `notice` — `{ text, variant? }`, a note band inside the card under the facts (`info` by default; `success`, `warning` or `error`); the text is translated.
236952
- * `format` is `text` (default), `date` (board short date), `date-long` (always with the year), `list` (an array joined by ", ", each item read at `itemPath`), `bytes` (a size, "1.5 MB"), `number` (in the reader's language, `decimals` fraction digits when set) or `currency` (a money amount in the reader's language, "€ 1.234,50": the code in `currency`, else the record's `currencyField`, else the app's reporting currency, else EUR; `decimals` defaults to 2).
237324
+ * `format` is `text` (default), `date` (board short date), `date-long` (always with the year), `longdate` (the month written out, the year only outside this year: "3 oktober"), `list` (an array joined by ", ", each item read at `itemPath`), `bytes` (a size, "1.5 MB"), `number` (in the reader's language, `decimals` fraction digits when set) or `currency` (a money amount in the reader's language, "€ 1.234,50": the code in `currency`, else the record's `currencyField`, else the app's reporting currency, else EUR; `decimals` defaults to 2).
236953
237325
  * `map` turns a raw value into a label (translated). `parts` (on a `line`) lists paths, or `{ path, label?, format? }`, joined by `separator` (default " · "); empty parts are dropped.
236954
237326
  * - `source` — `{ register, schema, id }` or `{ url }`; omitted = the current object. `{ register, schema, filter, sort? }` without `id` reads the first object that matches `filter` in `sort` order (`{ field, dir }`): the latest count of a shift. An entry with `from: "object"` reads the current object even then (a client card that also shows the ticket's channel).
236955
237327
  * - `endpointSource` — `{ url, method?, params?, responsePath? }`, the block a stat or chart takes: the facts read an app endpoint, shared and cached like those.
@@ -237233,7 +237605,7 @@ var script$48 = {
237233
237605
  }
237234
237606
  if (kind === 'row') {
237235
237607
  const last = out[out.length - 1];
237236
- const row = { key, label: this.tr(field.label || ''), value, ...this.rowExtras(record, field, value) };
237608
+ const row = { key, label: this.labelOf(record, field), value, ...this.rowExtras(record, field, value) };
237237
237609
  if (field.route && !row.href) {
237238
237610
  row.to = this.resolveRoute(field.route, record);
237239
237611
  }
@@ -237917,6 +238289,30 @@ var script$48 = {
237917
238289
  }
237918
238290
  },
237919
238291
 
238292
+ /**
238293
+ * A row's label: the value at `labelPath` (the record, or the page's
238294
+ * `@context.<name>` records) when it holds text, else `label`, both
238295
+ * through the host translate. A case type can name its handler
238296
+ * ("Regisseur") while another keeps "Behandelaar".
238297
+ *
238298
+ * @param {object} record The record shown.
238299
+ * @param {object} field The entry.
238300
+ * @return {string}
238301
+ * @spec openspec/changes/facts-label-from-path/specs/facts-widget/spec.md#requirement-a-facts-row-can-take-its-label-from-the-record-or-its-context
238302
+ */
238303
+ labelOf(record, field) {
238304
+ if (typeof field.labelPath === 'string' && field.labelPath !== '') {
238305
+ const rows = this.cnDetailVisibilityRows && typeof this.cnDetailVisibilityRows === 'object' && 'value' in this.cnDetailVisibilityRows
238306
+ ? this.cnDetailVisibilityRows.value
238307
+ : this.cnDetailVisibilityRows;
238308
+ const named = readPath$8({ ...(record || {}), '@context': rows || {} }, field.labelPath);
238309
+ if (typeof named === 'string' && named.trim() !== '') {
238310
+ return this.tr(named.trim())
238311
+ }
238312
+ }
238313
+ return this.tr(field.label || '')
238314
+ },
238315
+
237920
238316
  /**
237921
238317
  * Copy a row's value to the clipboard.
237922
238318
  *
@@ -238286,6 +238682,10 @@ var script$48 = {
238286
238682
  .filter((item) => item !== '');
238287
238683
  return items.join(', ')
238288
238684
  }
238685
+ // The month written out, the year only outside this year: "3 oktober".
238686
+ if (format === 'longdate') {
238687
+ return formatLongDate(raw)
238688
+ }
238289
238689
  if (format === 'date' || format === 'date-long') {
238290
238690
  const date = parseDateValue(raw);
238291
238691
  if (date === null) {
@@ -238792,7 +239192,7 @@ function render$47(_ctx, _cache, $props, $setup, $data, $options) {
238792
239192
  }
238793
239193
 
238794
239194
  script$48.render = render$47;
238795
- script$48.__scopeId = "data-v-1b8f8e6f";
239195
+ script$48.__scopeId = "data-v-b1b0c69e";
238796
239196
  const __sfc_main$48 = script$48;
238797
239197
 
238798
239198
  /**
@@ -343271,15 +343671,15 @@ function buildViewTree({ views = [], labelFilter = '', maxDepth = 3 } = {}) {
343271
343671
  }
343272
343672
  }
343273
343673
 
343274
- // Seeded roots first, then the reader's own, each alphabetically. A
343275
- // dropdown whose order changes between two openings is a dropdown nobody
343276
- // can build a habit with.
343674
+ // Seeded roots first, in the order the app declares them, then the
343675
+ // reader's own in the order they were saved (DqFilterBeheren). Both are
343676
+ // stable between two openings, so a person can build a habit with them.
343277
343677
  const seededRoots = roots.filter(({ view }) => view?.seeded === true);
343278
343678
  const ownRoots = roots.filter(({ view }) => view?.seeded !== true);
343279
- for (const { view, orphaned } of sortRootsByName(seededRoots)) {
343679
+ for (const { view, orphaned } of seededRoots) {
343280
343680
  emit(view, 0, orphaned);
343281
343681
  }
343282
- for (const { view, orphaned } of sortRootsByName(ownRoots)) {
343682
+ for (const { view, orphaned } of sortRootsBySaved(ownRoots)) {
343283
343683
  emit(view, 0, orphaned);
343284
343684
  }
343285
343685
 
@@ -343335,13 +343735,28 @@ function sortByName(views) {
343335
343735
  }
343336
343736
 
343337
343737
  /**
343338
- * Root entries by the name of the view they carry.
343738
+ * Root entries in the order their views were saved (`created`, oldest
343739
+ * first); views without a saved time follow, by name.
343339
343740
  *
343340
343741
  * @param {Array<object>} roots - The root entries.
343341
343742
  * @return {Array<object>} The same entries, sorted.
343342
343743
  */
343343
- function sortRootsByName(roots) {
343344
- return [...roots].sort((left, right) => String(left.view?.name ?? '').localeCompare(String(right.view?.name ?? '')))
343744
+ function sortRootsBySaved(roots) {
343745
+ const savedAt = (entry) => (typeof entry.view?.created === 'string' ? entry.view.created : '');
343746
+ return [...roots].sort((left, right) => {
343747
+ const a = savedAt(left);
343748
+ const b = savedAt(right);
343749
+ if (a !== b) {
343750
+ if (a === '') {
343751
+ return 1
343752
+ }
343753
+ if (b === '') {
343754
+ return -1
343755
+ }
343756
+ return a < b ? -1 : 1
343757
+ }
343758
+ return String(left.view?.name ?? '').localeCompare(String(right.view?.name ?? ''))
343759
+ })
343345
343760
  }
343346
343761
 
343347
343762
  /**
@@ -351913,6 +352328,18 @@ function useSelfFetchList(props, instance, inject, extras = {}) {
351913
352328
  // a reactive getter so a runtime flip attaches/detaches accordingly.
351914
352329
  // Stores without live-updates support (no `subscribe` action) are a
351915
352330
  // silent no-op inside the composable, keeping this fully inert.
352331
+ // An address carrying `@myTeam` (a shared link) waits for the team, as a
352332
+ // saved view on it does: once read, the token becomes the team, or goes
352333
+ // for a reader in none.
352334
+ const carriesMyTeam = Object.values(initialActiveFilters || {})
352335
+ .some((value) => value === MY_TEAM_TOKEN || (Array.isArray(value) && value.includes(MY_TEAM_TOKEN)));
352336
+ if (carriesMyTeam) {
352337
+ loadMyTeam().then(() => {
352338
+ list.activeFilters.value = dropOptionalUnresolved(resolveFilterTokens(list.activeFilters.value, tokenCtx()));
352339
+ list.refresh(1);
352340
+ });
352341
+ }
352342
+
351916
352343
  useObjectSubscription(objectStore, () => objectType.value, null, {
351917
352344
  enabled: () => props.subscribe !== false,
351918
352345
  });
@@ -353579,6 +354006,17 @@ var script$20 = {
353579
354006
  default: null,
353580
354007
  },
353581
354008
 
354009
+ /**
354010
+ * The leading icon's tile tone in the board look (`primary`, `teal`,
354011
+ * `neutral`), from `pages[].config.rowIconTone`. Forwarded to CnDataTable.
354012
+ *
354013
+ * @type {string}
354014
+ */
354015
+ rowIconTone: {
354016
+ type: String,
354017
+ default: 'primary',
354018
+ },
354019
+
353582
354020
  /** Columns to exclude in schema mode */
353583
354021
  excludeColumns: {
353584
354022
  type: Array,
@@ -356816,6 +357254,7 @@ var script$20 = {
356816
357254
  r: this.register,
356817
357255
  s: this.exportSchemaSlug,
356818
357256
  f: this.filter || null,
357257
+ a: this.effectiveActiveFilters || null,
356819
357258
  e: this.countEntries,
356820
357259
  // The store's total, not the error override in effectivePagination,
356821
357260
  // so a failed fetch and its recovery do not refetch the counts.
@@ -360406,7 +360845,15 @@ var script$20 = {
360406
360845
  const counts = await fetchFilterCounts({
360407
360846
  register: this.register,
360408
360847
  schema,
360409
- entries: this.countEntries.map((entry) => ({ key: entry.key, filter: resolveFilterMap(entry.filter, params, ctx) })),
360848
+ // A quick filter counts within the list as filtered (the
360849
+ // active filters, its own field its own); a saved view counts
360850
+ // its own query.
360851
+ entries: this.countEntries.map((entry) => ({
360852
+ key: entry.key,
360853
+ filter: entry.key.startsWith('tab:')
360854
+ ? { ...(this.effectiveActiveFilters || {}), ...resolveFilterMap(entry.filter, params, ctx) }
360855
+ : resolveFilterMap(entry.filter, params, ctx),
360856
+ })),
360410
360857
  baseFilter: resolveFilterMap(this.filter, params, ctx),
360411
360858
  ctx,
360412
360859
  });
@@ -365902,6 +366349,7 @@ function render$1$(_ctx, _cache, $props, $setup, $data, $options) {
365902
366349
  columns: $options.renderedColumns,
365903
366350
  pinnedCount: $options.pinnedColumnCount,
365904
366351
  rowIcon: $props.rowIcon,
366352
+ rowIconTone: $props.rowIconTone,
365905
366353
  rowIndicators: $props.rowIndicators,
365906
366354
  rowIndicatorCap: $props.rowIndicatorCap,
365907
366355
  rows: $options.displayObjects,
@@ -366049,7 +366497,7 @@ function render$1$(_ctx, _cache, $props, $setup, $data, $options) {
366049
366497
  key: "2"
366050
366498
  }
366051
366499
  : undefined
366052
- ]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["schema", "filterChips", "columns", "pinnedCount", "rowIcon", "rowIndicators", "rowIndicatorCap", "rows", "sortKey", "sortOrder", "sortKeys", "selectable", "hideHeader", "rowClickToView", "rowClickRoute", "selectedIds", "rowKey", "emptyText", "excludeColumns", "includeColumns", "columnOverrides", "rowClass", "rowTitle", "showUnread", "filterable", "filterSchema", "activeFilters", "filterRegister", "onColumnFilter", "onSort", "onSelect", "onRowClick", "onRowAuxClick", "onRowContextMenu"]))
366500
+ ]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["schema", "filterChips", "columns", "pinnedCount", "rowIcon", "rowIconTone", "rowIndicators", "rowIndicatorCap", "rows", "sortKey", "sortOrder", "sortKeys", "selectable", "hideHeader", "rowClickToView", "rowClickRoute", "selectedIds", "rowKey", "emptyText", "excludeColumns", "includeColumns", "columnOverrides", "rowClass", "rowTitle", "showUnread", "filterable", "filterSchema", "activeFilters", "filterRegister", "onColumnFilter", "onSort", "onSelect", "onRowClick", "onRowAuxClick", "onRowContextMenu"]))
366053
366501
  : ($data.currentViewMode === 'map' && $options.mapListEnabled)
366054
366502
  ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_42$3, [
366055
366503
  vue$1.createVNode(_component_CnMapWidget, {
@@ -477735,8 +478183,8 @@ exports.CnDataTable = __sfc_main$7Y;
477735
478183
  exports.CnDateAxisView = __sfc_main$2x;
477736
478184
  exports.CnDateRangePicker = __sfc_main$2e;
477737
478185
  exports.CnDecisionListWidget = __sfc_main$5x;
477738
- exports.CnDeckCardCreate = __sfc_main$78;
477739
- exports.CnDeckCardPicker = __sfc_main$77;
478186
+ exports.CnDeckCardCreate = __sfc_main$79;
478187
+ exports.CnDeckCardPicker = __sfc_main$78;
477740
478188
  exports.CnDeleteDialog = __sfc_main$2g;
477741
478189
  exports.CnDeltaWidget = __sfc_main$4q;
477742
478190
  exports.CnDeltaWidgetForm = __sfc_main$4r;