@conduction/nextcloud-vue 2.82.0 → 2.84.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 (178) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +2 -2
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
  4. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +24 -0
  5. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  6. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +9 -4
  7. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  8. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
  9. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +1 -1
  10. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
  11. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
  12. package/dist/esm/components/CnBodySections/CnBodySections.vue.js +1 -1
  13. package/dist/esm/components/CnBodySections/CnBodySections.vue2.js +22 -1
  14. package/dist/esm/components/CnBodySections/CnBodySections.vue2.js.map +1 -1
  15. package/dist/esm/components/CnBodySections/CnBodySections.vue3.js.map +1 -1
  16. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue.js +1 -1
  17. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js +47 -4
  18. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js.map +1 -1
  19. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue3.js.map +1 -1
  20. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue.js +1 -1
  21. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +125 -3
  22. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
  23. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js +3 -3
  24. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
  25. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  26. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +14 -0
  27. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  28. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  29. package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue.js +1 -1
  30. package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue2.js +1 -1
  31. package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue2.js.map +1 -1
  32. package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue3.js.map +1 -1
  33. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +203 -4
  34. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  35. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  36. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +1 -1
  37. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +38 -1
  38. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
  39. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +45 -5
  40. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
  41. package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue.js +1 -1
  42. package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue2.js +1 -1
  43. package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue2.js.map +1 -1
  44. package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue3.js.map +1 -1
  45. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +96 -12
  46. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  47. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +95 -50
  48. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  49. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js +29 -6
  50. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
  51. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
  52. package/dist/esm/components/CnMassActionBar/CnMassActionBar.vue2.js +2 -2
  53. package/dist/esm/components/CnMassActionBar/CnMassActionBar.vue2.js.map +1 -1
  54. package/dist/esm/components/CnMassImportDialog/CnMassImportDialog.vue2.js +2 -2
  55. package/dist/esm/components/CnMassImportDialog/CnMassImportDialog.vue2.js.map +1 -1
  56. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
  57. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +31 -2
  58. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
  59. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
  60. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js +1 -1
  61. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js +16 -2
  62. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js.map +1 -1
  63. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js +3 -3
  64. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js.map +1 -1
  65. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  66. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +37 -2
  67. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  68. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  69. package/dist/esm/components/CnReportsPage/CnReportsPage.vue.js +1 -1
  70. package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js +176 -0
  71. package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js.map +1 -1
  72. package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js +181 -52
  73. package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js.map +1 -1
  74. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js +1 -1
  75. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js +70 -5
  76. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js.map +1 -1
  77. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js +61 -35
  78. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js.map +1 -1
  79. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue.js +1 -1
  80. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +72 -5
  81. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
  82. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js +33 -8
  83. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
  84. package/dist/esm/components/CnStatWidget/CnStatWidget.vue.js +1 -1
  85. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +4 -2
  86. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  87. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  88. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue.js +1 -1
  89. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +72 -5
  90. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
  91. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +88 -71
  92. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
  93. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js +1 -1
  94. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js +18 -4
  95. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js.map +1 -1
  96. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js.map +1 -1
  97. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
  98. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +33 -3
  99. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  100. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  101. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue.js +1 -1
  102. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +16 -0
  103. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
  104. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
  105. package/dist/esm/dialogs/CnTransitionInputDialog.vue.js +1 -1
  106. package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js +68 -6
  107. package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js.map +1 -1
  108. package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js +133 -105
  109. package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js.map +1 -1
  110. package/dist/esm/l10n/en.json.js +16 -1
  111. package/dist/esm/l10n/en.json.js.map +1 -1
  112. package/dist/esm/l10n/nl.json.js +41 -4
  113. package/dist/esm/l10n/nl.json.js.map +1 -1
  114. package/dist/esm/library-style.js +1 -1
  115. package/dist/esm/nextcloud-vue.css +900 -537
  116. package/dist/esm/utils/buildManifest.js +37 -3
  117. package/dist/esm/utils/buildManifest.js.map +1 -1
  118. package/dist/esm/utils/detailWidgetVisibility.js +149 -0
  119. package/dist/esm/utils/detailWidgetVisibility.js.map +1 -0
  120. package/dist/esm/utils/fetchAggregate.js +25 -2
  121. package/dist/esm/utils/fetchAggregate.js.map +1 -1
  122. package/dist/esm/utils/formatMetric.js +39 -3
  123. package/dist/esm/utils/formatMetric.js.map +1 -1
  124. package/dist/esm/utils/resolveManifestTokens.js +1 -0
  125. package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
  126. package/dist/esm/utils/sentinelTokens.js +9 -0
  127. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  128. package/dist/esm/utils/switchValues.js +62 -0
  129. package/dist/esm/utils/switchValues.js.map +1 -0
  130. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  131. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  132. package/dist/nextcloud-vue.cjs.js +36711 -34909
  133. package/dist/nextcloud-vue.cjs.js.map +2 -2
  134. package/dist/nextcloud-vue.css +900 -537
  135. package/l10n/en.json +16 -1
  136. package/l10n/nl.json +41 -4
  137. package/package.json +1 -1
  138. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +31 -2
  139. package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +1 -1
  140. package/src/components/CnBodySections/CnBodySections.vue +22 -1
  141. package/src/components/CnCountdownWidget/CnCountdownWidget.vue +47 -4
  142. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +128 -6
  143. package/src/components/CnDashboardPage/CnDashboardPage.vue +14 -0
  144. package/src/components/CnDeltaWidget/CnDeltaWidget.vue +1 -1
  145. package/src/components/CnDetailPage/CnDetailPage.md +2 -0
  146. package/src/components/CnDetailPage/CnDetailPage.vue +203 -4
  147. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +63 -1
  148. package/src/components/CnGaugeWidget/CnGaugeWidget.vue +1 -1
  149. package/src/components/CnIndexPage/CnIndexPage.vue +125 -15
  150. package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +29 -6
  151. package/src/components/CnObjectDataWidget/CnObjectDataWidget.md +1 -1
  152. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +31 -2
  153. package/src/components/CnRadioCardsWidget/CnRadioCardsWidget.vue +54 -3
  154. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +37 -2
  155. package/src/components/CnReportsPage/CnReportsPage.vue +263 -5
  156. package/src/components/CnSettingsFormWidget/CnSettingsFormWidget.vue +110 -18
  157. package/src/components/CnStagesWidget/CnStagesWidget.vue +103 -6
  158. package/src/components/CnStatWidget/CnStatWidget.vue +4 -2
  159. package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +125 -7
  160. package/src/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue +18 -4
  161. package/src/components/CnTabsWidget/CnTabsWidget.vue +33 -3
  162. package/src/components/CnWalkthrough/CnWalkthrough.vue +16 -0
  163. package/src/css/form-field.css +14 -0
  164. package/src/css/form-widget.css +11 -0
  165. package/src/css/index-page.css +11 -0
  166. package/src/css/index.css +1 -0
  167. package/src/css/look-board-reports.css +175 -0
  168. package/src/css/look-board.css +44 -1
  169. package/src/dialogs/CnTransitionInputDialog.vue +87 -7
  170. package/src/schemas/app-manifest-v2.schema.json +24 -3
  171. package/src/utils/buildManifest.js +36 -2
  172. package/src/utils/detailWidgetVisibility.js +145 -0
  173. package/src/utils/fetchAggregate.js +24 -1
  174. package/src/utils/formatMetric.js +38 -2
  175. package/src/utils/resolveManifestTokens.js +1 -0
  176. package/src/utils/sentinelTokens.js +9 -0
  177. package/src/utils/switchValues.js +59 -0
  178. package/src/utils/validateManifestV2.compiled.js +3 -3
package/l10n/en.json CHANGED
@@ -1,5 +1,16 @@
1
1
  {
2
2
  "translations": {
3
+ "Channel": "Channel",
4
+ "Outcome": "Outcome",
5
+ "Chat": "Chat",
6
+ "Counter": "Counter",
7
+ "Social media": "Social media",
8
+ "Letter": "Letter",
9
+ "Save contact moment": "Save contact moment",
10
+ "Saving…": "Saving…",
11
+ "Subject is required": "Subject is required",
12
+ "Cannot save right now": "Cannot save right now",
13
+ "Failed to save interaction": "Failed to save interaction",
3
14
  "You cannot see these files": "You cannot see these files",
4
15
  "Delete {name}? This cannot be undone here.": "Delete {name}? This cannot be undone here.",
5
16
  "Linked items": "Linked items",
@@ -326,6 +337,9 @@
326
337
  "Constant": "Constant",
327
338
  "Copy": "Copy",
328
339
  "Copied": "Copied",
340
+ "Copy the name": "Copy the name",
341
+ "Copy the value": "Copy the value",
342
+ "Copying did not work. Select the text and copy it yourself.": "Copying did not work. Select the text and copy it yourself.",
329
343
  "Show": "Show",
330
344
  "Copy {label}": "Copy {label}",
331
345
  "Show {label}": "Show {label}",
@@ -1403,7 +1417,8 @@
1403
1417
  "This answer was refused.": "This answer was refused.",
1404
1418
  "No value ({count})": "No value ({count})",
1405
1419
  "{start} to {end}": "{start} to {end}",
1406
- "Breadcrumb": "Breadcrumb"
1420
+ "Breadcrumb": "Breadcrumb",
1421
+ "Moving to {stage}": "Moving to {stage}"
1407
1422
  },
1408
1423
  "plurals": {
1409
1424
  "{count} selected": [
package/l10n/nl.json CHANGED
@@ -1,5 +1,16 @@
1
1
  {
2
2
  "translations": {
3
+ "Channel": "Kanaal",
4
+ "Outcome": "Uitkomst",
5
+ "Chat": "Chat",
6
+ "Counter": "Balie",
7
+ "Social media": "Sociale media",
8
+ "Letter": "Brief",
9
+ "Save contact moment": "Contactmoment opslaan",
10
+ "Saving…": "Opslaan…",
11
+ "Subject is required": "Onderwerp is verplicht",
12
+ "Cannot save right now": "Opslaan kan nu niet",
13
+ "Failed to save interaction": "Het contactmoment is niet opgeslagen",
3
14
  "You cannot see these files": "Je kunt deze bestanden niet zien",
4
15
  "Delete {name}? This cannot be undone here.": "{name} verwijderen? Dit kun je hier niet ongedaan maken.",
5
16
  "Linked items": "Gekoppelde items",
@@ -348,6 +359,9 @@
348
359
  "Constant": "Constante",
349
360
  "Copy": "Kopiëren",
350
361
  "Copied": "Gekopieerd",
362
+ "Copy the name": "Naam kopiëren",
363
+ "Copy the value": "Waarde kopiëren",
364
+ "Copying did not work. Select the text and copy it yourself.": "Kopiëren lukte niet. Selecteer de tekst en kopieer hem zelf.",
351
365
  "Show": "Tonen",
352
366
  "Copy {label}": "{label} kopiëren",
353
367
  "Show {label}": "{label} tonen",
@@ -355,7 +369,7 @@
355
369
  "Copy items": "Items kopiëren",
356
370
  "Copy of {name}": "Kopie van {name}",
357
371
  "Copy property": "Eigenschap kopiëren",
358
- "Copy selected": "Geselecteerde kopiëren",
372
+ "Copy selected": "Selectie kopiëren",
359
373
  "Copy {key}": "Kopieer {key}",
360
374
  "Create": "Aanmaken",
361
375
  "Create (C)": "Aanmaken (C)",
@@ -386,7 +400,7 @@
386
400
  "Delete note": "Notitie verwijderen",
387
401
  "Delete objects": "Objecten verwijderen",
388
402
  "Delete property": "Eigenschap verwijderen",
389
- "Delete selected": "Geselecteerde verwijderen",
403
+ "Delete selected": "Selectie verwijderen",
390
404
  "Delete {key}": "Verwijder {key}",
391
405
  "Deprecated": "Verouderd",
392
406
  "Description": "Beschrijving",
@@ -1148,7 +1162,7 @@
1148
1162
  "Could not load entries.": "Kon registraties niet laden.",
1149
1163
  "Could not create client.": "Kon klant niet aanmaken.",
1150
1164
  "{count} selected": "{count} geselecteerd",
1151
- "Clear selection": "Selectie wissen",
1165
+ "Clear selection": "Selectie opheffen",
1152
1166
  "Overdue by 1 day": "1 dag over tijd",
1153
1167
  "Overdue by {days} days": "{days} dagen over tijd",
1154
1168
  "Overdue": "Over tijd",
@@ -1547,7 +1561,30 @@
1547
1561
  "This answer was refused.": "Dit antwoord is geweigerd.",
1548
1562
  "No value ({count})": "Geen waarde ({count})",
1549
1563
  "{start} to {end}": "{start} tot {end} uur",
1550
- "Breadcrumb": "Kruimelpad"
1564
+ "Breadcrumb": "Kruimelpad",
1565
+ "Cannot move: this record has no id, so the move would create a duplicate instead of updating it.": "Verplaatsen kan niet: dit record heeft geen id, dus de verplaatsing zou een kopie maken in plaats van het record bij te werken.",
1566
+ "Could not check whether this stage can be reached": "Kon niet controleren of deze fase bereikbaar is",
1567
+ "Could not check which stages can be reached": "Kon niet controleren welke fases bereikbaar zijn",
1568
+ "Could not load the stages": "De fases konden niet worden geladen",
1569
+ "Just a moment, the last move is still running": "Een moment, de vorige verplaatsing loopt nog",
1570
+ "Loading stages": "Fases laden",
1571
+ "Move to {stage}": "Naar {stage} verplaatsen",
1572
+ "Moved to {stage}": "Verplaatst naar {stage}",
1573
+ "No stages to show": "Geen fases om te tonen",
1574
+ "Not reachable from the current stage": "Niet bereikbaar vanuit de huidige fase",
1575
+ "Stages": "Fases",
1576
+ "The move could not be made": "De verplaatsing is niet gelukt",
1577
+ "The record is not loaded yet": "Het record is nog niet geladen",
1578
+ "The stages cannot be read for this record yet": "De fases van dit record zijn nog niet te lezen",
1579
+ "This move is not possible right now": "Deze verplaatsing kan nu niet",
1580
+ "This stage cannot be chosen right now": "Deze fase kan nu niet worden gekozen",
1581
+ "Are you sure you want to \"{action}\"?": "Weet je zeker dat je \"{action}\" wilt uitvoeren?",
1582
+ "Moved to {state}": "Verplaatst naar {state}",
1583
+ "Moved {title} to {state}": "{title} verplaatst naar {state}",
1584
+ "Transition failed": "De overgang is mislukt",
1585
+ "Closed": "Afgesloten",
1586
+ "Skip to {action}": "Ga naar {action}",
1587
+ "Moving to {stage}": "Bezig met verplaatsen naar {stage}"
1551
1588
  },
1552
1589
  "plurals": {
1553
1590
  "{count} selected": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/nextcloud-vue",
3
- "version": "2.82.0",
3
+ "version": "2.84.0",
4
4
  "description": "Shared Vue component library for Conduction Nextcloud apps — complements @nextcloud/vue with higher-level components, OpenRegister integration, and NL Design System support",
5
5
  "homepage": "https://github.com/ConductionNL/nextcloud-vue#readme",
6
6
  "bugs": {
@@ -56,6 +56,11 @@
56
56
  </div>
57
57
  </div>
58
58
 
59
+ <!-- `versionCardPosition: "end"`: the app's sections come first, and the
60
+ version card and the credential broker follow them (board DcBeheer
61
+ starts with the app's own card). The same slot, rendered once. -->
62
+ <slot v-if="versionCardAtEnd" />
63
+
59
64
  <!-- Version information card (version, up-to-date check, re-import, support footer) -->
60
65
  <CnVersionInfoCard
61
66
  v-if="showVersionCard"
@@ -151,8 +156,8 @@
151
156
  :appCredentials="appCredentials"
152
157
  data-testid="cn-admin-organisation-credentials" />
153
158
 
154
- <!-- @slot default The app's own settings sections, rendered below the version card. -->
155
- <slot />
159
+ <!-- @slot default The app's own settings sections, rendered below the version card, or above it with `versionCardPosition: "end"`. -->
160
+ <slot v-if="!versionCardAtEnd" />
156
161
 
157
162
  <!-- First-time setup wizard, opened from the admin page (ADR-042). -->
158
163
  <CnSetupWizard
@@ -358,6 +363,20 @@ export default {
358
363
  default: true,
359
364
  },
360
365
 
366
+ /**
367
+ * Where the version card (and the organisation credential broker) sits:
368
+ * `start`, above the app's sections (the default), or `end`, after them,
369
+ * for a page whose board starts with the app's own card. The keyboard
370
+ * order follows, because the slot moves in the markup.
371
+ *
372
+ * @spec openspec/changes/screens-admin-settings-version-card-end/specs/settings-board-look/spec.md#requirement-the-version-card-can-follow-the-apps-sections
373
+ */
374
+ versionCardPosition: {
375
+ type: String,
376
+ default: 'start',
377
+ validator: (value) => ['start', 'end'].includes(value),
378
+ },
379
+
361
380
  /** Version card heading. */
362
381
  versionTitle: {
363
382
  type: String,
@@ -482,6 +501,16 @@ export default {
482
501
  },
483
502
 
484
503
  computed: {
504
+ /**
505
+ * Whether the app's sections come before the version card.
506
+ *
507
+ * @return {boolean}
508
+ * @spec openspec/changes/screens-admin-settings-version-card-end/specs/settings-board-look/spec.md#requirement-the-version-card-can-follow-the-apps-sections
509
+ */
510
+ versionCardAtEnd() {
511
+ return this.versionCardPosition === 'end'
512
+ },
513
+
485
514
  /**
486
515
  * The crumbs that have a label.
487
516
  *
@@ -154,7 +154,7 @@ export default {
154
154
  if (!this.register || !this.schema || !this.objectId) {
155
155
  return ''
156
156
  }
157
- return `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/audit-trail?${params.toString()}`
157
+ return `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/audit-trails?${params.toString()}`
158
158
  },
159
159
  },
160
160
 
@@ -82,6 +82,8 @@ export default {
82
82
  cnRegistry: { default: () => ({}) },
83
83
  /** Legacy custom-component map provided by CnAppRoot. */
84
84
  cnCustomComponents: { default: () => ({}) },
85
+ /** The host's translate function for manifest-authored strings, provided by CnAppRoot. */
86
+ cnTranslate: { default: null },
85
87
  },
86
88
 
87
89
  props: {
@@ -201,7 +203,7 @@ export default {
201
203
  return {
202
204
  id,
203
205
  name,
204
- title: (section && section.title) || '',
206
+ title: this.translateTitle((section && section.title) || ''),
205
207
  card: !!(section && section.card),
206
208
  colSpan: section && section.colSpan,
207
209
  placement: section && section.placement,
@@ -213,6 +215,25 @@ export default {
213
215
  },
214
216
 
215
217
  methods: {
218
+ /**
219
+ * A manifest-authored section title in the reader's language.
220
+ *
221
+ * The title is written in the manifest, so it belongs to the host
222
+ * app's catalogue, the same as every other manifest title the detail
223
+ * page shows. A section heading printed raw read "Asked about" to a
224
+ * Dutch handler on a pipelinq ticket (Zuiddrecht live round 3).
225
+ *
226
+ * @param {string} title The title as written in the manifest.
227
+ * @return {string} The translated title, or '' when there is none.
228
+ * @spec openspec/changes/detail-page-strings-dutch/specs/detail-page-strings-dutch/spec.md#requirement-the-detail-page-speaks-the-readers-language
229
+ */
230
+ translateTitle(title) {
231
+ if (title === '' || typeof this.cnTranslate !== 'function') {
232
+ return title
233
+ }
234
+ return this.cnTranslate(title)
235
+ },
236
+
216
237
  /**
217
238
  * Resolve a section's host-app component by registry name. Precedence
218
239
  * matches CnPageRenderer.resolveCustomComponent: the v2 `cnRegistry`
@@ -69,6 +69,18 @@ const VARIANT_COLORS = Object.freeze({
69
69
  * negative number: "-3 days left" is a puzzle, and an overdue case is the one
70
70
  * state a handler must not have to decode.
71
71
  *
72
+ * ## A settled record
73
+ *
74
+ * `settledField` names a property on the record that is truthy once the work
75
+ * the date belongs to is done, such as a case in its final status. A settled
76
+ * record reads `settledLabel` (default "Closed") in no threshold colour, never
77
+ * "overdue": a closed case missed nothing that can still be acted on, and a red
78
+ * count of days on it sends a handler after a case that needs nobody.
79
+ *
80
+ * ```js
81
+ * content: { field: 'deadline', settledField: 'isFinalStatus', settledLabel: 'Handled' }
82
+ * ```
83
+ *
72
84
  * ## An unset date
73
85
  *
74
86
  * Renders a dash and no threshold colour. A case with no deadline is not urgent
@@ -91,8 +103,10 @@ export default {
91
103
  * - `icon` — MDI icon name.
92
104
  * - `thresholds` — `{ warn, danger }` in days remaining.
93
105
  * - `showDate` — render the absolute date underneath (default true).
106
+ * - `settledField` — property on the record that is truthy once the work is done; the tile then reads `settledLabel` and never counts overdue.
107
+ * - `settledLabel` — the headline of a settled record, run through the host's translate function (default "Closed").
94
108
  *
95
- * @type {{ field?: string, label?: string, icon?: string, thresholds?: {warn?: number, danger?: number}, showDate?: boolean }}
109
+ * @type {{ field?: string, label?: string, icon?: string, thresholds?: {warn?: number, danger?: number}, showDate?: boolean, settledField?: string, settledLabel?: string }}
96
110
  */
97
111
  content: {
98
112
  type: Object,
@@ -115,6 +129,7 @@ export default {
115
129
  return {
116
130
  cnObjectContextInjected: inject('cnObjectContext', null),
117
131
  cnDetailObjectContextInjected: inject('cnDetailObjectContext', null),
132
+ cnTranslate: inject('cnTranslate', null),
118
133
  }
119
134
  },
120
135
 
@@ -166,17 +181,44 @@ export default {
166
181
  return Math.round((startOfDay(this.targetDate) - startOfDay(new Date())) / MS_PER_DAY)
167
182
  },
168
183
 
169
- /** @return {boolean} true once the date has passed. */
184
+ /**
185
+ * Whether the record says its work is done (`settledField` is truthy).
186
+ *
187
+ * @return {boolean} True for a settled record.
188
+ * @spec openspec/changes/countdown-settled-record/specs/countdown-settled-record/spec.md#requirement-a-settled-record-never-reads-as-overdue
189
+ */
190
+ isSettled() {
191
+ const field = this.content?.settledField
192
+ if (!this.record || !field) {
193
+ return false
194
+ }
195
+ return Boolean(getByPath(this.record, field))
196
+ },
197
+
198
+ /**
199
+ * Whether the date has passed on a record still open.
200
+ *
201
+ * @return {boolean} True when overdue.
202
+ * @spec openspec/changes/countdown-settled-record/specs/countdown-settled-record/spec.md#requirement-a-settled-record-never-reads-as-overdue
203
+ */
170
204
  isOverdue() {
171
- return this.daysRemaining !== null && this.daysRemaining < 0
205
+ return !this.isSettled && this.daysRemaining !== null && this.daysRemaining < 0
172
206
  },
173
207
 
174
208
  /**
175
209
  * The headline: the remaining time in words.
176
210
  *
177
211
  * @return {string} The headline text.
212
+ * @spec openspec/changes/countdown-settled-record/specs/countdown-settled-record/spec.md#requirement-a-settled-record-never-reads-as-overdue
178
213
  */
179
214
  headline() {
215
+ if (this.isSettled) {
216
+ const label = this.content?.settledLabel
217
+ if (label) {
218
+ return typeof this.cnTranslate === 'function' ? this.cnTranslate(label) : label
219
+ }
220
+ return t('nextcloud-vue', 'Closed')
221
+ }
180
222
  const days = this.daysRemaining
181
223
  if (days === null) {
182
224
  return '—'
@@ -213,10 +255,11 @@ export default {
213
255
  * The threshold band this date falls in.
214
256
  *
215
257
  * @return {string} One of default / success / warning / error.
258
+ * @spec openspec/changes/countdown-settled-record/specs/countdown-settled-record/spec.md#requirement-a-settled-record-never-reads-as-overdue
216
259
  */
217
260
  variant() {
218
261
  const days = this.daysRemaining
219
- if (days === null) {
262
+ if (days === null || this.isSettled) {
220
263
  return 'default'
221
264
  }
222
265
  if (days < 0) {
@@ -38,11 +38,11 @@
38
38
  :tabindex="itemTabindex"
39
39
  :gs-id="item.id"
40
40
  :gs-x="item.gridX"
41
- :gs-y="item.gridY"
41
+ :gs-y="scaledRow(item.gridY)"
42
42
  :gs-w="item.gridWidth"
43
- :gs-h="item.gridHeight"
43
+ :gs-h="scaledRow(item.gridHeight)"
44
44
  :gs-min-w="minWidth"
45
- :gs-min-h="minHeight"
45
+ :gs-min-h="minRows(item)"
46
46
  :gs-size-to-content="sizeToContentAttr(item)"
47
47
  @keydown="onItemKeydown($event, item)">
48
48
  <div class="grid-stack-item-content">
@@ -99,6 +99,24 @@ let domIdCounter = 0
99
99
  */
100
100
  const MENU_ANCHOR_OFFSET = 8
101
101
 
102
+ /**
103
+ * How many GridStack rows a layout row is drawn as on a grid with
104
+ * size-to-content cells (`wantsRowSplit`): 80px rows become 10px rows.
105
+ */
106
+ const ROW_SPLIT = 8
107
+
108
+ /**
109
+ * Whether a layout holds a size-to-content cell (`sizeToContent` true or a
110
+ * positive row maximum).
111
+ *
112
+ * @param {Array<object>} layout The layout items.
113
+ * @return {boolean} True when one does.
114
+ */
115
+ function hasFitCell(layout) {
116
+ return Array.isArray(layout) && layout.some((item) => item
117
+ && (item.sizeToContent === true || (Number.isFinite(item.sizeToContent) && item.sizeToContent > 0)))
118
+ }
119
+
102
120
  /**
103
121
  * CnDashboardGrid — Low-level grid layout engine powered by GridStack.
104
122
  *
@@ -304,10 +322,38 @@ export default {
304
322
  * on that.
305
323
  */
306
324
  reflowed: false,
325
+ /**
326
+ * How many GridStack rows one layout row is drawn as: 1, or
327
+ * ROW_SPLIT while `wantsRowSplit` holds. Set before the first render,
328
+ * since GridStack reads the items' places from their attributes,
329
+ * and again when the grid is rebuilt.
330
+ */
331
+ rowSplit: (!this.editable && !this.float && hasFitCell(this.layout)) ? ROW_SPLIT : 1,
307
332
  }
308
333
  },
309
334
 
310
335
  computed: {
336
+ /**
337
+ * Whether the grid draws each layout row as ROW_SPLIT finer rows. A
338
+ * size-to-content cell can only be a whole number of rows tall, so on
339
+ * 80px rows it kept up to a row of empty space under its content and
340
+ * a packing grid (`float: false`) could not close it (pipelinq and
341
+ * dossiq Mijn werk, decidiq Mijn acties). Split into 10px rows the
342
+ * cell ends within 10px of its content and the widgets below move up.
343
+ *
344
+ * Only in view mode, only on a packing grid with a size-to-content
345
+ * cell: every other grid is built exactly as before. Item positions
346
+ * and sizes scale with the rows, so every widget keeps its pixel
347
+ * place and size until a cell shrinks; the layout itself, what edit
348
+ * mode shows and what `layout-change` reports, stays in layout rows.
349
+ *
350
+ * @spec openspec/changes/dashboard-fit-cells-close-up/specs/dashboard-grid/spec.md#requirement-a-size-to-content-cell-ends-at-its-content
351
+ * @return {boolean} True to split the rows.
352
+ */
353
+ wantsRowSplit() {
354
+ return !this.editable && !this.float && hasFitCell(this.layout)
355
+ },
356
+
311
357
  /**
312
358
  * @return {boolean} Whether the keyboard lane is live — items are
313
359
  * focusable and the arrow keys reposition. Requires the opt-in prop,
@@ -374,6 +420,12 @@ export default {
374
420
  this.syncInteractivity()
375
421
  },
376
422
 
423
+ wantsRowSplit(wants) {
424
+ if (this.grid && (wants ? ROW_SPLIT : 1) !== this.rowSplit) {
425
+ this.rebuildGrid()
426
+ }
427
+ },
428
+
377
429
  layout: {
378
430
  deep: true,
379
431
  handler(newLayout) {
@@ -433,11 +485,75 @@ export default {
433
485
  }
434
486
  // A number is GridStack's "size to content, but never taller than N rows" (a maximum).
435
487
  if (Number.isFinite(v) && v > 0) {
436
- return String(v)
488
+ return String(v * this.rowSplit)
437
489
  }
438
490
  return undefined
439
491
  },
440
492
 
493
+ /**
494
+ * A layout row coordinate or height in GridStack rows (`rowSplit`).
495
+ *
496
+ * @spec openspec/changes/dashboard-fit-cells-close-up/specs/dashboard-grid/spec.md#requirement-a-size-to-content-cell-ends-at-its-content
497
+ * @param {number|undefined} value The value in layout rows.
498
+ * @return {number|undefined} The value in grid rows, or undefined when none is set.
499
+ */
500
+ scaledRow(value) {
501
+ return Number.isFinite(value) ? value * this.rowSplit : undefined
502
+ },
503
+
504
+ /**
505
+ * The smallest height an item may take, in grid rows: `minHeight`
506
+ * layout rows, except for a size-to-content cell on split rows, which
507
+ * may shrink to its content down to one grid row. The minimum is for
508
+ * resizing by hand, and that only happens on whole rows in edit mode.
509
+ *
510
+ * @spec openspec/changes/dashboard-fit-cells-close-up/specs/dashboard-grid/spec.md#requirement-a-size-to-content-cell-ends-at-its-content
511
+ * @param {object} item The layout item.
512
+ * @return {number} The minimum height in grid rows.
513
+ */
514
+ minRows(item) {
515
+ if (this.rowSplit !== 1 && this.sizeToContentAttr(item) !== undefined) {
516
+ return 1
517
+ }
518
+ return this.minHeight * this.rowSplit
519
+ },
520
+
521
+ /**
522
+ * Build the grid again with the row split `wantsRowSplit` asks for, as
523
+ * edit mode turns on or off: the items go back to their layout places in
524
+ * the new rows, and the size-to-content cells are measured again.
525
+ *
526
+ * @spec openspec/changes/dashboard-fit-cells-close-up/specs/dashboard-grid/spec.md#requirement-a-size-to-content-cell-ends-at-its-content
527
+ * @return {void}
528
+ */
529
+ rebuildGrid() {
530
+ this.disconnectContentObserver()
531
+ this.grid.destroy(false)
532
+ this.grid = null
533
+ this.rowSplit = this.wantsRowSplit ? ROW_SPLIT : 1
534
+ this.$nextTick(() => {
535
+ if (!this.$refs.gridContainer) {
536
+ return
537
+ }
538
+ // GridStack wrote its own places onto the items; put the layout's back.
539
+ for (const item of this.layout) {
540
+ const el = this.$refs.gridContainer.querySelector(`[gs-id="${item.id}"]`)
541
+ if (!el) {
542
+ continue
543
+ }
544
+ for (const [name, value] of [['gs-y', this.scaledRow(item.gridY)], ['gs-h', this.scaledRow(item.gridHeight)], ['gs-min-h', this.minRows(item)], ['gs-size-to-content', this.sizeToContentAttr(item)]]) {
545
+ if (value === undefined) {
546
+ el.removeAttribute(name)
547
+ } else {
548
+ el.setAttribute(name, String(value))
549
+ }
550
+ }
551
+ }
552
+ this.initGrid()
553
+ this.observeContentSizes()
554
+ })
555
+ },
556
+
441
557
  /**
442
558
  * Build the accessible name for a grid item. A custom `itemLabel` wins
443
559
  * outright; otherwise the first human-readable field on the item is used
@@ -499,7 +615,7 @@ export default {
499
615
  const el = this.$refs.gridContainer.querySelector('.grid-stack')
500
616
  this.grid = GridStack.init({
501
617
  column: this.columns,
502
- cellHeight: this.cellHeight,
618
+ cellHeight: this.cellHeight / this.rowSplit,
503
619
  margin: this.margin,
504
620
  float: this.float,
505
621
  animate: true,
@@ -586,6 +702,12 @@ export default {
586
702
  if (this.isReflowed()) {
587
703
  return
588
704
  }
705
+ // A grid on split rows is a view-mode grid: what changes in it is
706
+ // packing and size-to-content measuring, never a user's
707
+ // arrangement, and it is not reported as one.
708
+ if (this.rowSplit !== 1) {
709
+ return
710
+ }
589
711
  if (!items || items.length === 0) {
590
712
  return
591
713
  }
@@ -986,7 +1108,7 @@ export default {
986
1108
  // grid's own position back, which matches and does
987
1109
  // nothing here.
988
1110
  const x = item.gridX ?? node.x
989
- const y = item.gridY ?? node.y
1111
+ const y = Number.isFinite(item.gridY) ? item.gridY * this.rowSplit : node.y
990
1112
  if (x !== node.x || y !== node.y) {
991
1113
  // The layout said where it goes; GridStack's `change`
992
1114
  // for it is not a user's arrangement and is not
@@ -4578,6 +4578,20 @@ export default {
4578
4578
  // because the dispatcher only forwards `content` — honour it with a
4579
4579
  // one-time deprecation warning so those cards render while authors
4580
4580
  // migrate the key under `content`.
4581
+ // Grid layout (`layout: "grid"`): stat tiles in columns.
4582
+ const stats = content.stats !== undefined ? content.stats : props.stats
4583
+ if (Array.isArray(stats)) {
4584
+ out.stats = stats
4585
+ }
4586
+ const columns = content.columns !== undefined ? content.columns : props.columns
4587
+ if (columns !== undefined) {
4588
+ out.columns = Number(columns)
4589
+ }
4590
+ // `tiles: "box"`: each grid tile on a surface of its own.
4591
+ const tiles = content.tiles !== undefined ? content.tiles : props.tiles
4592
+ if (typeof tiles === 'string') {
4593
+ out.tiles = tiles
4594
+ }
4581
4595
  const contentEntries = content.entries !== undefined ? content.entries : props.entries
4582
4596
  if (contentEntries !== undefined) {
4583
4597
  out.entries = contentEntries
@@ -511,7 +511,7 @@ export default {
511
511
  * @return {string} The formatted string.
512
512
  */
513
513
  formatNumber(value) {
514
- return formatMetricValue(value, this.resolvedFormat, unwrapAppConfig(this.cnAppConfig))
514
+ return formatMetricValue(value, this.resolvedFormat, unwrapAppConfig(this.cnAppConfig), null, this.effectiveTranslate)
515
515
  },
516
516
 
517
517
  /**
@@ -446,6 +446,8 @@ When the schema-driven fetch answers 404 the page shows a not-found state whose
446
446
 
447
447
  `headerCard` (default `false`) draws the header as a bordered card. `headerWidget` (default `''`) names a widget in `widgets` to render inside the header under the title, without a card of its own and out of the body grid: the stages bars of the board's case card.
448
448
 
449
+ `visibilityContext` (default `{}`) names records the page reads beside the loaded one, `{ <name>: { field, register, schema } }`. A grid widget's `visibleWhen` is read against the record, and against those records as `@context.<name>.<path>`, so a case page can show a widget for some case types only (`{ "field": "@context.caseType.category", "op": "in", "value": ["Sociaal domein"] }`). A widget without `visibleWhen` shows as before; a conditional one stays out of the grid until the record and the named records have loaded.
450
+
449
451
  `showWidgetActions` (Boolean, default `true`): `false` (manifest `config.showWidgetActions: false`) drops the overflow Actions menu from the cards of the body grid and the side column, unless a widget definition sets `showActions: true`.
450
452
 
451
453
  ### Favourite and follow