@conduction/nextcloud-vue 2.56.0-beta.46 → 2.56.0-beta.48

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 (199) hide show
  1. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js +1 -1
  2. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +2 -1
  4. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
  5. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
  6. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +28 -8
  7. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  8. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +37 -2
  9. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +57 -4
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +44 -17
  13. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  14. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue.js +1 -1
  15. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js +44 -2
  16. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
  17. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +140 -59
  18. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
  19. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue.js +1 -1
  20. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js +139 -3
  21. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js.map +1 -1
  22. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue3.js +60 -12
  23. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue3.js.map +1 -1
  24. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  25. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +111 -42
  26. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  27. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +117 -73
  28. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  29. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +34 -39
  30. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  31. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +7 -7
  32. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  33. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +5 -3
  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/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  37. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +6 -4
  38. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  39. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  40. package/dist/esm/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue2.js +46 -0
  41. package/dist/esm/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue2.js.map +1 -1
  42. package/dist/esm/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue3.js +6 -2
  43. package/dist/esm/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue3.js.map +1 -1
  44. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue.js +1 -1
  45. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js +82 -9
  46. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  47. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js +104 -90
  48. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  49. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue.js +9 -0
  50. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue.js.map +1 -0
  51. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js +355 -0
  52. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js.map +1 -0
  53. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js +462 -0
  54. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js.map +1 -0
  55. package/dist/esm/components/CnFormDialog/CnFormDialog.vue.js +1 -1
  56. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +21 -0
  57. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  58. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +402 -391
  59. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  60. package/dist/esm/components/CnIcon/semanticIcons.js +2 -2
  61. package/dist/esm/components/CnIcon/semanticIcons.js.map +1 -1
  62. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +141 -7
  63. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  64. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +5 -2
  65. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  66. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue.js +1 -1
  67. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js +22 -1
  68. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js.map +1 -1
  69. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js +20 -14
  70. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js.map +1 -1
  71. package/dist/esm/components/CnNotificationPreferences/CnNotificationPreferences.vue.js +1 -1
  72. package/dist/esm/components/CnNotificationPreferences/CnNotificationPreferences.vue2.js +2 -0
  73. package/dist/esm/components/CnNotificationPreferences/CnNotificationPreferences.vue2.js.map +1 -1
  74. package/dist/esm/components/CnNotificationPreferences/CnNotificationPreferences.vue3.js +15 -5
  75. package/dist/esm/components/CnNotificationPreferences/CnNotificationPreferences.vue3.js.map +1 -1
  76. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue.js +8 -0
  77. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue.js.map +1 -0
  78. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue2.js +114 -0
  79. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue2.js.map +1 -0
  80. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue3.js +45 -0
  81. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue3.js.map +1 -0
  82. package/dist/esm/components/CnPageHeader/CnPageHeader.vue2.js +38 -2
  83. package/dist/esm/components/CnPageHeader/CnPageHeader.vue2.js.map +1 -1
  84. package/dist/esm/components/CnPageHeader/CnPageHeader.vue3.js +12 -2
  85. package/dist/esm/components/CnPageHeader/CnPageHeader.vue3.js.map +1 -1
  86. package/dist/esm/components/CnPagination/CnPagination.vue2.js.map +1 -1
  87. package/dist/esm/components/CnPagination/CnPagination.vue3.js +56 -51
  88. package/dist/esm/components/CnPagination/CnPagination.vue3.js.map +1 -1
  89. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  90. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +3 -3
  91. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  92. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  93. package/dist/esm/components/CnRoadmapTab/CnRoadmapTab.vue.js +1 -1
  94. package/dist/esm/components/CnRoadmapTab/CnRoadmapTab.vue2.js +8 -30
  95. package/dist/esm/components/CnRoadmapTab/CnRoadmapTab.vue2.js.map +1 -1
  96. package/dist/esm/components/CnRoadmapTab/CnRoadmapTab.vue3.js.map +1 -1
  97. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue.js +1 -1
  98. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js +24 -0
  99. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js.map +1 -1
  100. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js +3 -1
  101. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js.map +1 -1
  102. package/dist/esm/components/CnTabs/CnTabs.vue.js +1 -1
  103. package/dist/esm/components/CnTabs/CnTabs.vue2.js +39 -2
  104. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  105. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  106. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
  107. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +85 -8
  108. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  109. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +37 -9
  110. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  111. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue.js +9 -0
  112. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue.js.map +1 -0
  113. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js +164 -0
  114. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js.map +1 -0
  115. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js +93 -0
  116. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js.map +1 -0
  117. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue.js +1 -1
  118. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js.map +1 -1
  119. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js +4 -3
  120. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js.map +1 -1
  121. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue.js +1 -1
  122. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +32 -0
  123. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
  124. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js +31 -20
  125. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
  126. package/dist/esm/composables/indexSources.js +9 -0
  127. package/dist/esm/composables/indexSources.js.map +1 -1
  128. package/dist/esm/l10n/en.json.js +24 -1
  129. package/dist/esm/l10n/en.json.js.map +1 -1
  130. package/dist/esm/l10n/nl.json.js +24 -1
  131. package/dist/esm/l10n/nl.json.js.map +1 -1
  132. package/dist/esm/library-style.js +1 -1
  133. package/dist/esm/nextcloud-vue.css +1190 -410
  134. package/dist/esm/utils/dueText.js +49 -8
  135. package/dist/esm/utils/dueText.js.map +1 -1
  136. package/dist/esm/utils/featureCatalog.js +135 -0
  137. package/dist/esm/utils/featureCatalog.js.map +1 -0
  138. package/dist/esm/utils/fillTemplate.js +74 -3
  139. package/dist/esm/utils/fillTemplate.js.map +1 -1
  140. package/dist/esm/utils/headerMeta.js +62 -12
  141. package/dist/esm/utils/headerMeta.js.map +1 -1
  142. package/dist/esm/utils/roadmapIssues.js +86 -0
  143. package/dist/esm/utils/roadmapIssues.js.map +1 -0
  144. package/dist/esm/utils/userDisplayName.js +47 -0
  145. package/dist/esm/utils/userDisplayName.js.map +1 -0
  146. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  147. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  148. package/dist/nextcloud-vue.cjs.js +16229 -13423
  149. package/dist/nextcloud-vue.cjs.js.map +2 -2
  150. package/dist/nextcloud-vue.css +1190 -410
  151. package/l10n/en.json +24 -1
  152. package/l10n/nl.json +24 -1
  153. package/package.json +1 -1
  154. package/src/components/CnActionsMenu/CnActionsMenu.vue +3 -1
  155. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +39 -1
  156. package/src/components/CnAppRoot/CnAppRoot.md +2 -0
  157. package/src/components/CnAppRoot/CnAppRoot.vue +66 -5
  158. package/src/components/CnChoiceCards/CnChoiceCards.md +27 -0
  159. package/src/components/CnChoiceCards/CnChoiceCards.vue +168 -3
  160. package/src/components/CnCountdownWidget/CnCountdownWidget.vue +222 -6
  161. package/src/components/CnDashboardPage/CnDashboardPage.vue +141 -8
  162. package/src/components/CnDataTable/CnDataTable.md +1 -0
  163. package/src/components/CnDataTable/CnDataTable.vue +41 -46
  164. package/src/components/CnDetailPage/CnDetailPage.vue +5 -3
  165. package/src/components/CnFactsWidget/CnFactsWidget.vue +6 -4
  166. package/src/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue +51 -1
  167. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +98 -13
  168. package/src/components/CnFeaturesBoard/CnFeaturesBoard.vue +1053 -0
  169. package/src/components/CnFormDialog/CnFormDialog.vue +606 -562
  170. package/src/components/CnIndexPage/CnIndexPage.vue +144 -7
  171. package/src/components/CnNcWidgetWidget/CnNcWidgetWidget.vue +23 -2
  172. package/src/components/CnNotificationPreferences/CnNotificationPreferences.vue +5 -0
  173. package/src/components/CnPageHeader/CnHeaderTrail.vue +140 -0
  174. package/src/components/CnPageHeader/CnPageHeader.md +11 -0
  175. package/src/components/CnPageHeader/CnPageHeader.vue +42 -0
  176. package/src/components/CnPagination/CnPagination.vue +10 -2
  177. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +3 -3
  178. package/src/components/CnRoadmapTab/CnRoadmapTab.vue +6 -28
  179. package/src/components/CnSettingsPage/CnSettingsPage.vue +26 -0
  180. package/src/components/CnTabs/CnTabs.vue +39 -2
  181. package/src/components/CnTabsWidget/CnTabsWidget.vue +106 -13
  182. package/src/components/CnUserSettingsDialog/CnUserSettingsDialog.vue +305 -0
  183. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +4 -3
  184. package/src/components/CnWidgetWrapper/CnWidgetWrapper.md +8 -0
  185. package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +43 -2
  186. package/src/composables/indexSources.js +9 -0
  187. package/src/css/dialog.css +61 -0
  188. package/src/css/look-board-detail.css +8 -0
  189. package/src/css/look-board-index.css +27 -1
  190. package/src/css/look-board.css +60 -0
  191. package/src/css/page-header.css +16 -0
  192. package/src/schemas/app-manifest-v2.schema.json +30 -17
  193. package/src/utils/dueText.js +49 -8
  194. package/src/utils/featureCatalog.js +132 -0
  195. package/src/utils/fillTemplate.js +73 -2
  196. package/src/utils/headerMeta.js +62 -12
  197. package/src/utils/roadmapIssues.js +82 -0
  198. package/src/utils/userDisplayName.js +54 -0
  199. package/src/utils/validateManifestV2.compiled.js +3 -3
package/l10n/en.json CHANGED
@@ -1494,7 +1494,30 @@
1494
1494
  "{on} of %n are in the menu"
1495
1495
  ],
1496
1496
  "Put it in the menu:": "Put it in the menu:",
1497
- "Admin settings": "Admin settings"
1497
+ "Admin settings": "Admin settings",
1498
+ "Support this app": "Support this app",
1499
+ "No features with this status": "No features with this status",
1500
+ "Learn more about {title}": "Learn more about {title}",
1501
+ "Learn more": "Learn more",
1502
+ "Next on the roadmap": "Next on the roadmap",
1503
+ "Coming next": "Coming next",
1504
+ "The most requested items from the issue tracker.": "The most requested items from the issue tracker.",
1505
+ "Opened {date}": "Opened {date}",
1506
+ "See the whole roadmap": "See the whole roadmap",
1507
+ "Features and roadmap": "Features and roadmap",
1508
+ "Beta": "Beta",
1509
+ "Planned": "Planned",
1510
+ "{status}, {expected}": "{status}, {expected}",
1511
+ "Build on this app": "Build on this app",
1512
+ "AI skills": "AI skills",
1513
+ "%n feature": [
1514
+ "%n feature",
1515
+ "%n features"
1516
+ ],
1517
+ "Your account": "Your account",
1518
+ "Only for you. Colleagues keep their own settings.": "Only for you. Colleagues keep their own settings.",
1519
+ "Sections of the settings": "Sections of the settings",
1520
+ "Changes are saved straight away": "Changes are saved straight away"
1498
1521
  },
1499
1522
  "plurals": {
1500
1523
  "{count} selected": [
package/l10n/nl.json CHANGED
@@ -1680,7 +1680,30 @@
1680
1680
  "{on} van %n staat in het menu",
1681
1681
  "{on} van %n staan in het menu"
1682
1682
  ],
1683
- "Put it in the menu:": "In het menu zetten:"
1683
+ "Put it in the menu:": "In het menu zetten:",
1684
+ "Support this app": "Steun deze app",
1685
+ "No features with this status": "Geen functies met deze status",
1686
+ "Learn more about {title}": "Lees meer over {title}",
1687
+ "Learn more": "Lees meer",
1688
+ "Next on the roadmap": "Hierna op de roadmap",
1689
+ "Coming next": "Hierna",
1690
+ "The most requested items from the issue tracker.": "De meest gevraagde items uit de issuetracker.",
1691
+ "Opened {date}": "Geopend {date}",
1692
+ "See the whole roadmap": "Bekijk de hele roadmap",
1693
+ "Features and roadmap": "Functies en roadmap",
1694
+ "Beta": "Beta",
1695
+ "Planned": "Gepland",
1696
+ "{status}, {expected}": "{status}, {expected}",
1697
+ "Build on this app": "Bouw verder op deze app",
1698
+ "AI skills": "AI-vaardigheden",
1699
+ "%n feature": [
1700
+ "%n functie",
1701
+ "%n functies"
1702
+ ],
1703
+ "Your account": "Uw account",
1704
+ "Only for you. Colleagues keep their own settings.": "Alleen voor jou. Collega's houden hun eigen instellingen.",
1705
+ "Sections of the settings": "Onderdelen van de instellingen",
1706
+ "Changes are saved straight away": "Wijzigingen worden meteen bewaard"
1684
1707
  },
1685
1708
  "plurals": {
1686
1709
  "{count} selected": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/nextcloud-vue",
3
- "version": "2.56.0-beta.46",
3
+ "version": "2.56.0-beta.48",
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": {
@@ -32,6 +32,8 @@
32
32
  <template #icon>
33
33
  <DotsHorizontal :size="20" />
34
34
  </template>
35
+ <!-- @slot lead-items Items rendered at the very top of the menu, above Refresh: the tabs a board strip lists here instead of drawing them (CnTabsWidget). The caller owns any separator after them. -->
36
+ <slot name="lead-items" />
35
37
  <!-- Stays open while refreshing; onRefreshClick closes the menu once the refresh settles. -->
36
38
  <NcActionButton
37
39
  v-if="showRefresh"
@@ -569,7 +571,7 @@ export default {
569
571
  if (this.showRequestFeature) {
570
572
  return true
571
573
  }
572
- if (this.hasPrimaryItems || this.$slots['primary-items']) {
574
+ if (this.hasPrimaryItems || this.$slots['primary-items'] || this.$slots['lead-items']) {
573
575
  return true
574
576
  }
575
577
  return Boolean(this.$slots['action-items']) || Boolean(this.$slots && this.$slots['action-items'])
@@ -139,7 +139,7 @@
139
139
  <template #footer>
140
140
  <!-- @slot footer Replaces the default support/SLA footer inside the version card. -->
141
141
  <slot name="footer">
142
- <div v-if="showSupport" class="cn-support-info">
142
+ <div v-if="showSupport && !supportAtEnd" class="cn-support-info">
143
143
  <h4>{{ supportTitle }}</h4>
144
144
  <p>
145
145
  {{ supportLine }}
@@ -185,6 +185,24 @@
185
185
  <!-- @slot default The app's own settings sections, rendered below the version card, or above it with `versionCardPosition: "end"`. With `tabs`, only while the first tab is open. -->
186
186
  <slot v-if="!versionCardAtEnd && onFirstTab" />
187
187
 
188
+ <!-- `supportPosition: "end"`: the support lines as their own wide card,
189
+ last on the page (board PqBeheer), not in the version card. -->
190
+ <CnSettingsSection
191
+ v-if="showSupport && supportAtEnd && onFirstTab"
192
+ class="cn-admin-settings-shell__support"
193
+ :name="supportTitle"
194
+ :wide="true"
195
+ data-testid="cn-admin-settings-support">
196
+ <p class="cn-admin-settings-shell__support-line">
197
+ {{ supportLine }}
198
+ <a :href="`mailto:${supportEmail}`">{{ supportEmail }}</a>
199
+ </p>
200
+ <p v-if="slaEmail" class="cn-admin-settings-shell__support-line">
201
+ {{ slaLine }}
202
+ <a :href="`mailto:${slaEmail}`">{{ slaEmail }}</a>
203
+ </p>
204
+ </CnSettingsSection>
205
+
188
206
  <!-- First-time setup wizard, opened from the admin page (ADR-042). -->
189
207
  <CnSetupWizard
190
208
  v-if="showSetup && setupWizardOpen"
@@ -216,6 +234,7 @@ import { normalizeLook, useLook } from '../../composables/useLook.js'
216
234
  import { ensureTranslations } from '../../l10n/index.js'
217
235
  import { buildFeatureRequestUrl } from '../../utils/forge.js'
218
236
  import { prefixUrl } from '../../utils/headers.js'
237
+ import { CnSettingsSection } from '../CnSettingsSection/index.js'
219
238
  import { CnTab, CnTabs } from '../CnTabs/index.js'
220
239
  import { CnVersionInfoCard } from '../CnVersionInfoCard/index.js'
221
240
 
@@ -327,6 +346,7 @@ export default {
327
346
  BookOpenVariant,
328
347
  NcSettingsSection,
329
348
  CnCredentials,
349
+ CnSettingsSection,
330
350
  CnVersionInfoCard,
331
351
  CnSetupWizard,
332
352
  CnTab,
@@ -616,6 +636,19 @@ export default {
616
636
  default: true,
617
637
  },
618
638
 
639
+ /**
640
+ * Where the support lines go: `card`, in the version card's footer (the
641
+ * default), or `end`, as their own wide card after everything else on
642
+ * the first tab (board PqBeheer).
643
+ *
644
+ * @spec openspec/changes/admin-shell-support-end/specs/settings-board-look/spec.md#requirement-support-can-be-its-own-card-at-the-end
645
+ */
646
+ supportPosition: {
647
+ type: String,
648
+ default: 'card',
649
+ validator: (value) => ['card', 'end'].includes(value),
650
+ },
651
+
619
652
  /** Support contact email. */
620
653
  supportEmail: {
621
654
  type: String,
@@ -723,6 +756,11 @@ export default {
723
756
  return this.versionCardPosition === 'end'
724
757
  },
725
758
 
759
+ /** @return {boolean} Whether the support lines are their own card at the end. */
760
+ supportAtEnd() {
761
+ return this.supportPosition === 'end'
762
+ },
763
+
726
764
  /**
727
765
  * The crumbs that have a label.
728
766
  *
@@ -85,6 +85,8 @@ With loading state, custom components, translate, permissions, and custom page t
85
85
  | `permissions` | Array | No | `[]` | Permission strings for the current user; forwarded to `CnAppNav` for menu filtering |
86
86
  | `pageTypes` | Object | No | `null` | Page-type registry map (`type → component`). Falls back to the library's `defaultPageTypes`. Extend with `{ ...defaultPageTypes, report: MyReportPage }` |
87
87
  | `userSettingsTitle` | String | No | `''` | Title shown at the top of the hosted `NcAppSettingsDialog`. Empty (the default) resolves to "User settings" from the library catalogue ("Persoonlijke instellingen" in Dutch), then the app's `translate`. Override per app to brand the modal (e.g. `'Decidiq preferences'`). |
88
+ | `userSettingsEyebrow` | String | No | `''` | Board look: the line above the user-settings title. Empty uses "Your account" ("Uw account"). Goes through the app's `translate`. |
89
+ | `userSettingsSubtitle` | String | No | `''` | Board look: the sentence under the user-settings title. Empty uses "Only for you. Colleagues keep their own settings." Goes through the app's `translate`. |
88
90
  | `userSettingsSections` | Array | No | `null` | The user-settings sections to draw, in order: `app` (the app's slot content), `notifications`, `credentials`, `walkthrough`, `integrations`. A key left out is not drawn. `null` keeps the defaults: every section after `#user-settings-sections` content, or only the `#user-settings` content and the integrations section. |
89
91
  | `adminSettingsTitle` | String | No | `''` | Title shown at the top of the admin-settings `NcAppSettingsDialog`. Empty (the default) resolves to `translate('Administration')`. Override per app (e.g. `'Pipelinq administration'`). |
90
92
  | `requiresApps` | Array | No | `['openregister']` | App ids that MUST be installed for the host app to function. Checked against the OCS capabilities API on mount. When any required app is missing, CnAppRoot renders the empty state from the `or-missing` slot (default: `<NcEmptyContent>`) instead of the renderer. Pass `[]` to opt out (e.g. launchpad, the docs/styleguide app) — useful for any consumer that does not depend on OpenRegister. |
@@ -550,10 +550,9 @@
550
550
  integrations section are drawn, unless `userSettingsSections`
551
551
  names more.
552
552
  -->
553
- <NcAppSettingsDialog
554
- :open="userSettingsOpen"
555
- :showNavigation="true"
556
- :name="resolvedUserSettingsTitle"
553
+ <component
554
+ :is="resolvedLook === 'board' ? 'CnUserSettingsDialog' : 'NcAppSettingsDialog'"
555
+ v-bind="userSettingsDialogProps"
557
556
  @update:open="userSettingsOpen = $event">
558
557
  <template v-for="section in userSettingsSectionOrder()" :key="section">
559
558
  <template v-if="section === 'app'">
@@ -573,6 +572,9 @@
573
572
  <NcAppSettingsSection v-else-if="section === 'credentials' && userSettingsOpen"
574
573
  id="credentials"
575
574
  :name="libraryCopy('Credentials')">
575
+ <template v-if="resolvedLook === 'board'" #icon>
576
+ <KeyOutline :size="18" />
577
+ </template>
576
578
  <CnCredentials
577
579
  scope="personal"
578
580
  :appId="appId"
@@ -587,6 +589,9 @@
587
589
  <NcAppSettingsSection v-else-if="section === 'walkthrough' && walkthroughEnabled"
588
590
  id="cn-walkthrough"
589
591
  :name="restartWalkthroughSectionName">
592
+ <template v-if="resolvedLook === 'board'" #icon>
593
+ <Play :size="18" />
594
+ </template>
590
595
  <p class="cn-app-root__walkthrough-hint">
591
596
  {{ restartWalkthroughHint }}
592
597
  </p>
@@ -628,6 +633,9 @@
628
633
  <NcAppSettingsSection v-else-if="section === 'integrations' && userSettingsOpen && integrationEntries.length"
629
634
  id="cn-integrations"
630
635
  :name="translate('Integrations')">
636
+ <template v-if="resolvedLook === 'board'" #icon>
637
+ <OpenInNew :size="18" />
638
+ </template>
631
639
  <p class="cn-app-root__integrations-hint">
632
640
  {{ libraryCopy('These open in another app.') }}
633
641
  </p>
@@ -653,7 +661,7 @@
653
661
  </ul>
654
662
  </NcAppSettingsSection>
655
663
  </template>
656
- </NcAppSettingsDialog>
664
+ </component>
657
665
 
658
666
  <!--
659
667
  ADR-079 Step 2: the generic admin-settings modal has been REMOVED.
@@ -693,6 +701,7 @@ import { generateUrl } from '@nextcloud/router'
693
701
  import { NcAppContent, NcAppSettingsDialog, NcAppSettingsSection, NcButton, NcContent, NcEmptyContent, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'
694
702
  import { computed, reactive, shallowRef, watch } from 'vue'
695
703
  import DatabaseSearchOutline from 'vue-material-design-icons/DatabaseSearchOutline.vue'
704
+ import KeyOutline from 'vue-material-design-icons/KeyOutline.vue'
696
705
  import OpenInNew from 'vue-material-design-icons/OpenInNew.vue'
697
706
  import Play from 'vue-material-design-icons/Play.vue'
698
707
  import Restart from 'vue-material-design-icons/Restart.vue'
@@ -709,6 +718,7 @@ import CnObjectSidebar from '../CnObjectSidebar/CnObjectSidebar.vue'
709
718
  import CnSetupWizard from '../CnSetupWizard/CnSetupWizard.vue'
710
719
  import CnSupportDialog from '../CnSupportDialog/CnSupportDialog.vue'
711
720
  import CnTenantBadge from '../CnTenantBadge/CnTenantBadge.vue'
721
+ import CnUserSettingsDialog from '../CnUserSettingsDialog/CnUserSettingsDialog.vue'
712
722
  import CnWalkthrough from '../CnWalkthrough/CnWalkthrough.vue'
713
723
  import { DEFAULT_CHAT_APP_ID } from '../../composables/aiChatConfig.js'
714
724
  import { useAppInstaller } from '../../composables/useAppInstaller.js'
@@ -818,6 +828,7 @@ export default {
818
828
  NcLoadingIcon,
819
829
  NcNoteCard,
820
830
  DatabaseSearchOutline,
831
+ KeyOutline,
821
832
  OpenInNew,
822
833
  Play,
823
834
  Restart,
@@ -832,6 +843,7 @@ export default {
832
843
  CnObjectSidebar,
833
844
  CnSupportDialog,
834
845
  CnNotificationPreferences,
846
+ CnUserSettingsDialog,
835
847
  CnCredentials,
836
848
  CnTenantBadge,
837
849
  },
@@ -1648,6 +1660,29 @@ export default {
1648
1660
  default: '',
1649
1661
  },
1650
1662
 
1663
+ /**
1664
+ * Board look: the line above the user-settings title. Empty uses
1665
+ * the library's "Your account".
1666
+ *
1667
+ * @type {string}
1668
+ */
1669
+ userSettingsEyebrow: {
1670
+ type: String,
1671
+ default: '',
1672
+ },
1673
+
1674
+ /**
1675
+ * Board look: the sentence under the user-settings title. Empty
1676
+ * uses the library's "Only for you. Colleagues keep their own
1677
+ * settings."
1678
+ *
1679
+ * @type {string}
1680
+ */
1681
+ userSettingsSubtitle: {
1682
+ type: String,
1683
+ default: '',
1684
+ },
1685
+
1651
1686
  /**
1652
1687
  * The sections of the user-settings modal, in the order they are
1653
1688
  * drawn. Keys: `app` (the content of the `#user-settings-sections`
@@ -3065,6 +3100,32 @@ export default {
3065
3100
  return this.userSettingsTitle || this.libraryCopy('User settings')
3066
3101
  },
3067
3102
 
3103
+ /**
3104
+ * The props of the user-settings dialog: `NcAppSettingsDialog`'s, or
3105
+ * under the board look CnUserSettingsDialog's (eyebrow, subtitle and
3106
+ * the app's name for the footer note).
3107
+ *
3108
+ * @spec openspec/changes/user-settings-board-dialog/specs/user-settings-board-dialog/spec.md#requirement-the-user-settings-dialog-takes-the-board-look
3109
+ * @return {object}
3110
+ */
3111
+ userSettingsDialogProps() {
3112
+ if (this.resolvedLook !== 'board') {
3113
+ return { open: this.userSettingsOpen, showNavigation: true, name: this.resolvedUserSettingsTitle }
3114
+ }
3115
+ const props = {
3116
+ open: this.userSettingsOpen,
3117
+ title: this.resolvedUserSettingsTitle,
3118
+ appName: this.appDisplayName || (this.manifest && this.manifest.name) || this.appId || '',
3119
+ }
3120
+ if (this.userSettingsEyebrow) {
3121
+ props.eyebrow = this.translate(this.userSettingsEyebrow)
3122
+ }
3123
+ if (this.userSettingsSubtitle) {
3124
+ props.subtitle = this.translate(this.userSettingsSubtitle)
3125
+ }
3126
+ return props
3127
+ },
3128
+
3068
3129
  /**
3069
3130
  * Title for the admin-settings modal. Prop override, else the
3070
3131
  * translated "Administration". Mirrors `resolvedUserSettingsTitle`.
@@ -61,3 +61,30 @@ four lines `description-lines` shows by default:
61
61
  ]" />
62
62
  </div>
63
63
  ```
64
+
65
+ One option per row with `layout="rows"`, an option that cannot be picked yet
66
+ (`disabled` with a `disabledReason`), and fields for the chosen option right
67
+ under it through the `option-actions` slot (scope `{ option, selected }`):
68
+
69
+ ```vue
70
+ <div style="max-width: 560px;">
71
+ <CnChoiceCards
72
+ label="Next step"
73
+ layout="rows"
74
+ :model-value="'decide'"
75
+ :options="[
76
+ { value: 'decide', label: 'Take a decision',
77
+ description: 'Record the decision and inform the applicant.' },
78
+ { value: 'advice', label: 'Ask for advice',
79
+ description: 'Send the case to the advisory committee.' },
80
+ { value: 'close', label: 'Close the case', disabled: true,
81
+ disabledReason: 'A decision has to be taken first.' },
82
+ ]">
83
+ <template #option-actions="{ option, selected }">
84
+ <p v-if="selected && option.value === 'decide'" style="margin: 0 0 0 32px;">Decision fields go here.</p>
85
+ </template>
86
+ </CnChoiceCards>
87
+ </div>
88
+ ```
89
+
90
+ `columns` fixes the card grid's column count; `:columns="1"` stacks the cards in one column.
@@ -14,6 +14,45 @@
14
14
  {{ emptyText }}
15
15
  </NcNoteCard>
16
16
 
17
+ <!-- Rows: one full-width option per row, the input on the left and the
18
+ title over a muted description, no card chrome. -->
19
+ <div v-else-if="layout === 'rows'" class="cn-choice-cards__rows" data-testid="cn-choice-cards-rows">
20
+ <div
21
+ v-for="option in normalizedOptions"
22
+ :key="String(option.value)"
23
+ class="cn-choice-cards__cell">
24
+ <label
25
+ class="cn-choice-cards__row"
26
+ :class="{ 'cn-choice-cards__row--selected': isSelected(option), 'cn-choice-cards__row--disabled': isDisabled(option) }">
27
+ <input
28
+ class="cn-choice-cards__row-input"
29
+ :type="multiple ? 'checkbox' : 'radio'"
30
+ :name="groupName"
31
+ :value="String(option.value)"
32
+ :checked="isSelected(option)"
33
+ :disabled="isDisabled(option)"
34
+ :aria-describedby="option.disabled && option.disabledReason ? reasonId(option) : null"
35
+ @change="onToggle(option)">
36
+ <CnIcon v-if="option.icon"
37
+ :name="option.icon"
38
+ :size="20"
39
+ class="cn-choice-cards__row-icon" />
40
+ <span class="cn-choice-cards__row-text">
41
+ <span class="cn-choice-cards__row-title">{{ option.label }}</span>
42
+ <span v-if="option.description" class="cn-choice-cards__row-description">{{ option.description }}</span>
43
+ <span
44
+ v-if="option.disabled && option.disabledReason"
45
+ :id="reasonId(option)"
46
+ class="cn-choice-cards__reason"
47
+ data-testid="cn-choice-cards-reason">{{ option.disabledReason }}</span>
48
+ </span>
49
+ </label>
50
+ <div v-if="$slots['option-actions']" class="cn-choice-cards__actions">
51
+ <slot name="option-actions" :option="option" :selected="isSelected(option)" />
52
+ </div>
53
+ </div>
54
+ </div>
55
+
17
56
  <div v-else class="cn-choice-cards__grid" :style="gridStyle">
18
57
  <!-- One cell per option: the card's label, and below it whatever the
19
58
  `#option-actions` slot adds. The slot sits OUTSIDE the label on
@@ -37,7 +76,8 @@
37
76
  :name="groupName"
38
77
  :value="String(option.value)"
39
78
  :checked="isSelected(option)"
40
- :disabled="disabled"
79
+ :disabled="isDisabled(option)"
80
+ :aria-describedby="option.disabled && option.disabledReason ? reasonId(option) : null"
41
81
  @change="onToggle(option)">
42
82
  <CnCard
43
83
  class="cn-choice-cards__card"
@@ -53,6 +93,11 @@
53
93
  <CnIcon :name="option.icon" :size="20" />
54
94
  </template>
55
95
  </CnCard>
96
+ <span
97
+ v-if="option.disabled && option.disabledReason"
98
+ :id="reasonId(option)"
99
+ class="cn-choice-cards__reason cn-choice-cards__reason--card"
100
+ data-testid="cn-choice-cards-reason">{{ option.disabledReason }}</span>
56
101
  </label>
57
102
  <div v-if="$slots['option-actions']" class="cn-choice-cards__actions">
58
103
  <!-- @slot option-actions Content below one card, outside its label
@@ -109,7 +154,9 @@ export default {
109
154
  props: {
110
155
  /**
111
156
  * The options to offer. Each: `{ value, label, description?, stats?,
112
- * tags?, icon? }` — `icon` is a PascalCase MDI name resolved by CnIcon.
157
+ * tags?, icon?, disabled?, disabledReason? }` — `icon` is a PascalCase
158
+ * MDI name resolved by CnIcon; a `disabled` option is greyed out and
159
+ * cannot be picked, with `disabledReason` shown under it.
113
160
  */
114
161
  options: {
115
162
  type: Array,
@@ -156,6 +203,23 @@ export default {
156
203
  default: () => t('nextcloud-vue', 'Nothing to choose from here.'),
157
204
  },
158
205
 
206
+ /**
207
+ * `cards`: a grid of cards with stats and tags. `rows`: one option per
208
+ * row, full width, the input on the left and the title over a muted
209
+ * description; with `multiple` and one option it is a checkbox card.
210
+ */
211
+ layout: {
212
+ type: String,
213
+ default: 'cards',
214
+ validator: (value) => ['cards', 'rows'].includes(value),
215
+ },
216
+
217
+ /** Columns of the card grid; 0 lets the grid fit as many as the width allows. */
218
+ columns: {
219
+ type: Number,
220
+ default: 0,
221
+ },
222
+
159
223
  /** Lines of description shown before clamping. */
160
224
  descriptionLines: {
161
225
  type: Number,
@@ -226,6 +290,9 @@ export default {
226
290
  * @return {object} A `gridTemplateColumns` override, or `{}`.
227
291
  */
228
292
  gridStyle() {
293
+ if (this.columns > 0) {
294
+ return { gridTemplateColumns: `repeat(${this.columns}, minmax(0, 1fr))` }
295
+ }
229
296
  const count = this.normalizedOptions.length
230
297
  if (count > 0 && count <= 2) {
231
298
  return { gridTemplateColumns: `repeat(${count}, minmax(0, 1fr))` }
@@ -244,6 +311,26 @@ export default {
244
311
  * @param {object} option The normalised option.
245
312
  * @return {boolean} True when selected.
246
313
  */
314
+ /**
315
+ * Whether an option cannot be picked: the whole group is disabled, or the option is.
316
+ *
317
+ * @param {object} option The normalised option.
318
+ * @return {boolean} True when disabled.
319
+ */
320
+ isDisabled(option) {
321
+ return this.disabled || option.disabled === true
322
+ },
323
+
324
+ /**
325
+ * Id of an option's disabled reason, for `aria-describedby`.
326
+ *
327
+ * @param {object} option The normalised option.
328
+ * @return {string} The id.
329
+ */
330
+ reasonId(option) {
331
+ return `${this.groupName}-reason-${String(option.value).replace(/[^A-Za-z0-9_-]/g, '-')}`
332
+ },
333
+
247
334
  isSelected(option) {
248
335
  return this.selectedValues.some((v) => String(v) === String(option.value))
249
336
  },
@@ -255,7 +342,7 @@ export default {
255
342
  * @return {void}
256
343
  */
257
344
  onToggle(option) {
258
- if (this.disabled) {
345
+ if (this.isDisabled(option)) {
259
346
  return
260
347
  }
261
348
  if (!this.multiple) {
@@ -368,4 +455,82 @@ export default {
368
455
  .cn-choice-cards__input:disabled + :deep(.cn-card) {
369
456
  opacity: 0.6;
370
457
  }
458
+
459
+ .cn-choice-cards__rows {
460
+ display: flex;
461
+ flex-direction: column;
462
+ gap: 8px;
463
+ }
464
+
465
+ .cn-choice-cards__row {
466
+ display: flex;
467
+ align-items: flex-start;
468
+ gap: 12px;
469
+ padding: 14px 16px;
470
+ border: 1px solid var(--color-border);
471
+ border-radius: var(--border-radius-large, 8px);
472
+ background: var(--color-main-background);
473
+ cursor: pointer;
474
+ }
475
+
476
+ .cn-choice-cards__row:hover {
477
+ border-color: var(--color-border-dark);
478
+ }
479
+
480
+ .cn-choice-cards__row--selected,
481
+ .cn-choice-cards__row--selected:hover {
482
+ border-color: var(--color-primary-element);
483
+ /* The second pixel of the 2px selected border, without moving the content. */
484
+ box-shadow: inset 0 0 0 1px var(--color-primary-element);
485
+ background: var(--color-primary-element-light);
486
+ }
487
+
488
+ .cn-choice-cards__row--disabled {
489
+ cursor: default;
490
+ opacity: 0.6;
491
+ }
492
+
493
+ .cn-choice-cards__row:has(.cn-choice-cards__row-input:focus-visible) {
494
+ outline: 2px solid var(--color-primary-element);
495
+ outline-offset: 2px;
496
+ }
497
+
498
+ .cn-choice-cards__row-input {
499
+ flex: none;
500
+ margin: 2px 0 0;
501
+ accent-color: var(--color-primary-element);
502
+ cursor: inherit;
503
+ }
504
+
505
+ .cn-choice-cards__row-icon {
506
+ flex: none;
507
+ }
508
+
509
+ .cn-choice-cards__row-text {
510
+ display: flex;
511
+ flex-direction: column;
512
+ gap: 2px;
513
+ min-width: 0;
514
+ }
515
+
516
+ .cn-choice-cards__row-title {
517
+ font-size: 15px;
518
+ font-weight: 600;
519
+ }
520
+
521
+ .cn-choice-cards__row-description {
522
+ font-size: 13px;
523
+ color: var(--color-text-maxcontrast);
524
+ }
525
+
526
+ .cn-choice-cards__reason {
527
+ font-size: 13px;
528
+ color: var(--color-text-maxcontrast);
529
+ font-style: italic;
530
+ }
531
+
532
+ .cn-choice-cards__reason--card {
533
+ display: block;
534
+ margin-block-start: 4px;
535
+ }
371
536
  </style>