@conduction/nextcloud-vue 2.56.0-beta.10 → 2.56.0-beta.11

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 (150) hide show
  1. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js +10 -0
  2. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js.map +1 -0
  3. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js +189 -0
  4. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js.map +1 -0
  5. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js +72 -0
  6. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js.map +1 -0
  7. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +99 -9
  8. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +39 -18
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  11. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
  12. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +53 -37
  13. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
  14. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  15. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -2
  16. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  17. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js +10 -0
  18. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js.map +1 -0
  19. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js +293 -0
  20. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js.map +1 -0
  21. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js +189 -0
  22. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js.map +1 -0
  23. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +216 -8
  24. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +118 -50
  26. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  27. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  28. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +2 -1
  29. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  30. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js +3 -1
  31. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js.map +1 -1
  32. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js +1 -1
  33. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js.map +1 -1
  34. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +234 -9
  35. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  36. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +96 -57
  37. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  38. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +51 -2
  39. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  40. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +5 -1
  41. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  42. package/dist/esm/components/CnIndexPage/selfModeActions.js +3 -3
  43. package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
  44. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +36 -1
  45. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  46. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -1
  47. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  48. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js +121 -12
  49. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js.map +1 -1
  50. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js +3 -1
  51. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js.map +1 -1
  52. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +326 -1
  53. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  54. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +112 -26
  55. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  56. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +10 -0
  57. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js.map +1 -0
  58. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +337 -0
  59. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -0
  60. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +102 -0
  61. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -0
  62. package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js +34 -0
  63. package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js.map +1 -0
  64. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +131 -8
  65. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
  66. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js +40 -38
  67. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
  68. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +8 -6
  69. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  70. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  71. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  72. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js +19 -0
  73. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js.map +1 -1
  74. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js +1 -1
  75. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js.map +1 -1
  76. package/dist/esm/composables/useDependencyCheck.js +65 -0
  77. package/dist/esm/composables/useDependencyCheck.js.map +1 -0
  78. package/dist/esm/composables/useWalkthrough.js +218 -6
  79. package/dist/esm/composables/useWalkthrough.js.map +1 -1
  80. package/dist/esm/index.js +2 -0
  81. package/dist/esm/index.js.map +1 -1
  82. package/dist/esm/l10n/en.json.js +43 -0
  83. package/dist/esm/l10n/en.json.js.map +1 -1
  84. package/dist/esm/l10n/nl.json.js +43 -0
  85. package/dist/esm/l10n/nl.json.js.map +1 -1
  86. package/dist/esm/nextcloud-vue.css +449 -0
  87. package/dist/esm/store/useObjectStore.js +8 -0
  88. package/dist/esm/store/useObjectStore.js.map +1 -1
  89. package/dist/esm/utils/columnFilters.js +311 -0
  90. package/dist/esm/utils/columnFilters.js.map +1 -0
  91. package/dist/esm/utils/groupAutocomplete.js +123 -0
  92. package/dist/esm/utils/groupAutocomplete.js.map +1 -0
  93. package/dist/esm/utils/libraryWidgetKeys.js +2 -0
  94. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  95. package/dist/esm/utils/pickerOptions.js +198 -0
  96. package/dist/esm/utils/pickerOptions.js.map +1 -0
  97. package/dist/esm/utils/schema.js +83 -7
  98. package/dist/esm/utils/schema.js.map +1 -1
  99. package/dist/esm/utils/timelineEvents.js +174 -0
  100. package/dist/esm/utils/timelineEvents.js.map +1 -0
  101. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  102. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  103. package/dist/esm/utils/walkthroughSignals.js +88 -0
  104. package/dist/esm/utils/walkthroughSignals.js.map +1 -0
  105. package/dist/nextcloud-vue.cjs.js +9098 -5161
  106. package/dist/nextcloud-vue.cjs.js.map +2 -2
  107. package/dist/nextcloud-vue.css +449 -0
  108. package/l10n/en.json +43 -0
  109. package/l10n/nl.json +43 -0
  110. package/package.json +1 -1
  111. package/src/components/CnAdminActionCard/CnAdminActionCard.vue +234 -0
  112. package/src/components/CnAdminActionCard/index.js +4 -0
  113. package/src/components/CnAppRoot/CnAppRoot.vue +129 -15
  114. package/src/components/CnChoiceCards/CnChoiceCards.vue +56 -31
  115. package/src/components/CnDashboardPage/CnDashboardPage.vue +2 -2
  116. package/src/components/CnDataTable/CnColumnFilterPopover.vue +524 -0
  117. package/src/components/CnDataTable/CnDataTable.md +3 -0
  118. package/src/components/CnDataTable/CnDataTable.vue +260 -4
  119. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +2 -2
  120. package/src/components/CnFieldHelper/CnFieldHelper.vue +4 -2
  121. package/src/components/CnFormDialog/CnFormDialog.vue +290 -31
  122. package/src/components/CnIndexPage/CnIndexPage.vue +55 -2
  123. package/src/components/CnIndexPage/selfModeActions.js +3 -3
  124. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +39 -1
  125. package/src/components/CnResourceSelect/CnResourceSelect.vue +123 -12
  126. package/src/components/CnSetupWizard/CnSetupWizard.vue +392 -3
  127. package/src/components/CnTimelineWidget/CnTimelineWidget.vue +477 -0
  128. package/src/components/CnTimelineWidget/dashboardRegistration.js +32 -0
  129. package/src/components/CnTimelineWidget/index.js +4 -0
  130. package/src/components/CnWalkthrough/CnWalkthrough.vue +143 -10
  131. package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
  132. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  133. package/src/components/CnWizardDialog/CnWizardDialog.vue +20 -1
  134. package/src/components/index.js +2 -0
  135. package/src/composables/useDependencyCheck.js +61 -0
  136. package/src/composables/useWalkthrough.js +217 -5
  137. package/src/css/dashboard.css +49 -0
  138. package/src/css/detail-page.css +19 -0
  139. package/src/css/table.css +81 -0
  140. package/src/index.js +2 -0
  141. package/src/schemas/app-manifest-v2.schema.json +3 -1
  142. package/src/store/useObjectStore.js +8 -0
  143. package/src/utils/columnFilters.js +324 -0
  144. package/src/utils/groupAutocomplete.js +119 -0
  145. package/src/utils/libraryWidgetKeys.js +2 -0
  146. package/src/utils/pickerOptions.js +194 -0
  147. package/src/utils/schema.js +83 -7
  148. package/src/utils/timelineEvents.js +171 -0
  149. package/src/utils/validateManifestV2.compiled.js +2 -2
  150. package/src/utils/walkthroughSignals.js +94 -0
@@ -0,0 +1,10 @@
1
+ import script from './CnAdminActionCard.vue2.js';
2
+ import { render } from './CnAdminActionCard.vue3.js';
3
+
4
+ script.render = render;
5
+ script.__scopeId = "data-v-57907ee5";
6
+ script.__file = "src/components/CnAdminActionCard/CnAdminActionCard.vue";
7
+ const __sfc_main = script;
8
+
9
+ export { __sfc_main as default };
10
+ //# sourceMappingURL=CnAdminActionCard.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnAdminActionCard.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
@@ -0,0 +1,189 @@
1
+ import { translate } from '@nextcloud/l10n';
2
+ import { NcButton, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue';
3
+ import __sfc_main from '../CnConfigurationCard/CnConfigurationCard.vue.js';
4
+
5
+ /**
6
+ * CnAdminActionCard — one maintenance action on an admin settings page.
7
+ *
8
+ * A card with a title, a short explanation and one button. The click POSTs
9
+ * an app action, the button spins while it runs, and the server's message
10
+ * shows below it as success or error. Use it for work an admin starts by
11
+ * hand, such as repairing the register or re-importing schemas. That work
12
+ * never belongs in the setup wizard.
13
+ *
14
+ * By default it posts to the app's setup action contract,
15
+ * `/apps/{appId}/api/setup/action/{action}`, the same endpoint the setup
16
+ * wizard uses. Pass `url` to post somewhere else.
17
+ *
18
+ * ```vue
19
+ * <CnAdminActionCard
20
+ * app-id="pipelinq"
21
+ * action="provision"
22
+ * :title="t('pipelinq', 'Repair the register')"
23
+ * :description="t('pipelinq', 'Creates missing schemas again. Your data stays as it is.')"
24
+ * :button-label="t('pipelinq', 'Repair')" />
25
+ * ```
26
+ *
27
+ * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-an-admin-action-card-runs-one-server-action
28
+ */
29
+ var script = {
30
+ name: 'CnAdminActionCard',
31
+
32
+ components: {
33
+ CnConfigurationCard: __sfc_main,
34
+ NcButton,
35
+ NcLoadingIcon,
36
+ NcNoteCard,
37
+ },
38
+
39
+ props: {
40
+ /** Card heading. Pass it translated. */
41
+ title: {
42
+ type: String,
43
+ required: true,
44
+ },
45
+
46
+ /** What the action does, in one or two short sentences. Pass it translated. */
47
+ description: {
48
+ type: String,
49
+ default: '',
50
+ },
51
+
52
+ /** The Nextcloud app id; builds the default action URL. */
53
+ appId: {
54
+ type: String,
55
+ default: '',
56
+ },
57
+
58
+ /** The action id, posted to `/apps/{appId}/api/setup/action/{action}`. */
59
+ action: {
60
+ type: String,
61
+ default: '',
62
+ },
63
+
64
+ /**
65
+ * An app path to POST to instead of the setup action contract, e.g.
66
+ * `/apps/pipelinq/api/settings/repair`. Passed through `generateUrl`.
67
+ */
68
+ url: {
69
+ type: String,
70
+ default: '',
71
+ },
72
+
73
+ /** Request body to send with the POST. */
74
+ payload: {
75
+ type: Object,
76
+ default: () => ({}),
77
+ },
78
+
79
+ /** Button label. Defaults to "Run". */
80
+ buttonLabel: {
81
+ type: String,
82
+ default: '',
83
+ },
84
+
85
+ /** Label next to the spinner while the action runs. */
86
+ runningLabel: {
87
+ type: String,
88
+ default: () => translate('nextcloud-vue', 'Running…'),
89
+ },
90
+
91
+ /** NcButton variant for the action button. */
92
+ buttonVariant: {
93
+ type: String,
94
+ default: 'secondary',
95
+ },
96
+
97
+ /** Disable the button, e.g. while a precondition is not met. */
98
+ disabled: {
99
+ type: Boolean,
100
+ default: false,
101
+ },
102
+ },
103
+
104
+ emits: ['result'],
105
+
106
+ data() {
107
+ return {
108
+ running: false,
109
+ /** @type {{ success: boolean, message: string }|null} */
110
+ result: null,
111
+ }
112
+ },
113
+
114
+ computed: {
115
+ resolvedButtonLabel() {
116
+ return this.buttonLabel || translate('nextcloud-vue', 'Run')
117
+ },
118
+
119
+ /**
120
+ * The app path the button posts to: `url` when set, else the setup
121
+ * action contract. Empty when neither is configured.
122
+ *
123
+ * @return {string}
124
+ */
125
+ actionPath() {
126
+ if (this.url) {
127
+ return this.url
128
+ }
129
+ if (this.appId && this.action) {
130
+ return `/apps/${this.appId}/api/setup/action/${this.action}`
131
+ }
132
+ return ''
133
+ },
134
+ },
135
+
136
+ methods: {
137
+ /**
138
+ * Run the action: POST, spin while it runs, show the result.
139
+ *
140
+ * @return {Promise<void>}
141
+ * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-an-admin-action-card-runs-one-server-action
142
+ */
143
+ async run() {
144
+ if (this.running) {
145
+ return
146
+ }
147
+ if (!this.actionPath) {
148
+ this.result = { success: false, message: translate('nextcloud-vue', 'This action is not configured.') };
149
+ return
150
+ }
151
+ this.running = true;
152
+ this.result = null;
153
+ let data;
154
+ try {
155
+ const [{ default: axios }, { generateUrl }] = await Promise.all([
156
+ import('@nextcloud/axios'),
157
+ import('@nextcloud/router'),
158
+ ]);
159
+ const response = await axios.post(generateUrl(this.actionPath), this.payload);
160
+ data = response && response.data;
161
+ this.result = {
162
+ success: !!data && data.success !== false,
163
+ message: (data && data.message) || translate('nextcloud-vue', 'Done.'),
164
+ };
165
+ } catch (err) {
166
+ data = err && err.response ? err.response.data : null;
167
+ this.result = { success: false, message: this.errorMessage(err) };
168
+ } finally {
169
+ this.running = false;
170
+ }
171
+ /**
172
+ * @event result The action finished.
173
+ * @type {{ success: boolean, message: string, data: object|null }}
174
+ */
175
+ this.$emit('result', { ...this.result, data });
176
+ },
177
+
178
+ errorMessage(err) {
179
+ const data = err && err.response && err.response.data;
180
+ if (data && (data.message || data.error)) {
181
+ return data.message || data.error
182
+ }
183
+ return (err && err.message) || translate('nextcloud-vue', 'Something went wrong.')
184
+ },
185
+ },
186
+ };
187
+
188
+ export { script as default };
189
+ //# sourceMappingURL=CnAdminActionCard.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnAdminActionCard.vue2.js","sources":["../../../../src/components/CnAdminActionCard/CnAdminActionCard.vue"],"sourcesContent":["<!-- SPDX-License-Identifier: EUPL-1.2 -->\n<!-- SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl> -->\n<template>\n\t<CnConfigurationCard class=\"cn-admin-action-card\" :title=\"title\" data-testid=\"cn-admin-action-card\">\n\t\t<p v-if=\"description\" class=\"cn-admin-action-card__description\">\n\t\t\t{{ description }}\n\t\t</p>\n\t\t<!-- @slot default Extra content between the explanation and the button. -->\n\t\t<slot />\n\t\t<div class=\"cn-admin-action-card__footer\">\n\t\t\t<NcButton\n\t\t\t\t:variant=\"buttonVariant\"\n\t\t\t\t:disabled=\"running || disabled\"\n\t\t\t\tdata-testid=\"cn-admin-action-card-run\"\n\t\t\t\t@click=\"run\">\n\t\t\t\t<template v-if=\"running\" #icon>\n\t\t\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ running ? runningLabel : resolvedButtonLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<!-- role=status so a screen reader hears the outcome without moving focus. -->\n\t\t<div role=\"status\" aria-live=\"polite\">\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"result\"\n\t\t\t\t:type=\"result.success ? 'success' : 'error'\"\n\t\t\t\tdata-testid=\"cn-admin-action-card-result\">\n\t\t\t\t{{ result.message }}\n\t\t\t</NcNoteCard>\n\t\t</div>\n\t</CnConfigurationCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport CnConfigurationCard from '../CnConfigurationCard/CnConfigurationCard.vue'\n\n/**\n * CnAdminActionCard — one maintenance action on an admin settings page.\n *\n * A card with a title, a short explanation and one button. The click POSTs\n * an app action, the button spins while it runs, and the server's message\n * shows below it as success or error. Use it for work an admin starts by\n * hand, such as repairing the register or re-importing schemas. That work\n * never belongs in the setup wizard.\n *\n * By default it posts to the app's setup action contract,\n * `/apps/{appId}/api/setup/action/{action}`, the same endpoint the setup\n * wizard uses. Pass `url` to post somewhere else.\n *\n * ```vue\n * <CnAdminActionCard\n * app-id=\"pipelinq\"\n * action=\"provision\"\n * :title=\"t('pipelinq', 'Repair the register')\"\n * :description=\"t('pipelinq', 'Creates missing schemas again. Your data stays as it is.')\"\n * :button-label=\"t('pipelinq', 'Repair')\" />\n * ```\n *\n * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-an-admin-action-card-runs-one-server-action\n */\nexport default {\n\tname: 'CnAdminActionCard',\n\n\tcomponents: {\n\t\tCnConfigurationCard,\n\t\tNcButton,\n\t\tNcLoadingIcon,\n\t\tNcNoteCard,\n\t},\n\n\tprops: {\n\t\t/** Card heading. Pass it translated. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** What the action does, in one or two short sentences. Pass it translated. */\n\t\tdescription: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The Nextcloud app id; builds the default action URL. */\n\t\tappId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The action id, posted to `/apps/{appId}/api/setup/action/{action}`. */\n\t\taction: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * An app path to POST to instead of the setup action contract, e.g.\n\t\t * `/apps/pipelinq/api/settings/repair`. Passed through `generateUrl`.\n\t\t */\n\t\turl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Request body to send with the POST. */\n\t\tpayload: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Button label. Defaults to \"Run\". */\n\t\tbuttonLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Label next to the spinner while the action runs. */\n\t\trunningLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Running…'),\n\t\t},\n\n\t\t/** NcButton variant for the action button. */\n\t\tbuttonVariant: {\n\t\t\ttype: String,\n\t\t\tdefault: 'secondary',\n\t\t},\n\n\t\t/** Disable the button, e.g. while a precondition is not met. */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: ['result'],\n\n\tdata() {\n\t\treturn {\n\t\t\trunning: false,\n\t\t\t/** @type {{ success: boolean, message: string }|null} */\n\t\t\tresult: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tresolvedButtonLabel() {\n\t\t\treturn this.buttonLabel || t('nextcloud-vue', 'Run')\n\t\t},\n\n\t\t/**\n\t\t * The app path the button posts to: `url` when set, else the setup\n\t\t * action contract. Empty when neither is configured.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tactionPath() {\n\t\t\tif (this.url) {\n\t\t\t\treturn this.url\n\t\t\t}\n\t\t\tif (this.appId && this.action) {\n\t\t\t\treturn `/apps/${this.appId}/api/setup/action/${this.action}`\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Run the action: POST, spin while it runs, show the result.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-an-admin-action-card-runs-one-server-action\n\t\t */\n\t\tasync run() {\n\t\t\tif (this.running) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.actionPath) {\n\t\t\t\tthis.result = { success: false, message: t('nextcloud-vue', 'This action is not configured.') }\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.running = true\n\t\t\tthis.result = null\n\t\t\tlet data\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst response = await axios.post(generateUrl(this.actionPath), this.payload)\n\t\t\t\tdata = response && response.data\n\t\t\t\tthis.result = {\n\t\t\t\t\tsuccess: !!data && data.success !== false,\n\t\t\t\t\tmessage: (data && data.message) || t('nextcloud-vue', 'Done.'),\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tdata = err && err.response ? err.response.data : null\n\t\t\t\tthis.result = { success: false, message: this.errorMessage(err) }\n\t\t\t} finally {\n\t\t\t\tthis.running = false\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event result The action finished.\n\t\t\t * @type {{ success: boolean, message: string, data: object|null }}\n\t\t\t */\n\t\t\tthis.$emit('result', { ...this.result, data })\n\t\t},\n\n\t\terrorMessage(err) {\n\t\t\tconst data = err && err.response && err.response.data\n\t\t\tif (data && (data.message || data.error)) {\n\t\t\t\treturn data.message || data.error\n\t\t\t}\n\t\t\treturn (err && err.message) || t('nextcloud-vue', 'Something went wrong.')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-admin-action-card__description {\n\tmargin: 0 0 12px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-admin-action-card__footer {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-block: 8px;\n}\n</style>\n"],"names":["CnConfigurationCard","t"],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { resolveComponent, openBlock, createBlock, withCtx, createElementBlock, toDisplayString, createCommentVNode, renderSlot, createElementVNode, createVNode, createSlots, createTextVNode } from 'vue';
2
+
3
+ const _hoisted_1 = {
4
+ key: 0,
5
+ class: "cn-admin-action-card__description"
6
+ };
7
+ const _hoisted_2 = { class: "cn-admin-action-card__footer" };
8
+ const _hoisted_3 = {
9
+ role: "status",
10
+ "aria-live": "polite"
11
+ };
12
+
13
+ function render(_ctx, _cache, $props, $setup, $data, $options) {
14
+ const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
15
+ const _component_NcButton = resolveComponent("NcButton");
16
+ const _component_NcNoteCard = resolveComponent("NcNoteCard");
17
+ const _component_CnConfigurationCard = resolveComponent("CnConfigurationCard");
18
+
19
+ return (openBlock(), createBlock(_component_CnConfigurationCard, {
20
+ class: "cn-admin-action-card",
21
+ title: $props.title,
22
+ "data-testid": "cn-admin-action-card"
23
+ }, {
24
+ default: withCtx(() => [
25
+ ($props.description)
26
+ ? (openBlock(), createElementBlock("p", _hoisted_1, toDisplayString($props.description), 1 /* TEXT */))
27
+ : createCommentVNode("v-if", true),
28
+ renderSlot(_ctx.$slots, "default"),
29
+ createElementVNode("div", _hoisted_2, [
30
+ createVNode(_component_NcButton, {
31
+ variant: $props.buttonVariant,
32
+ disabled: $data.running || $props.disabled,
33
+ "data-testid": "cn-admin-action-card-run",
34
+ onClick: $options.run
35
+ }, createSlots({
36
+ default: withCtx(() => [
37
+ createTextVNode(" " + toDisplayString($data.running ? $props.runningLabel : $options.resolvedButtonLabel), 1 /* TEXT */)
38
+ ]),
39
+ _: 2 /* DYNAMIC */
40
+ }, [
41
+ ($data.running)
42
+ ? {
43
+ name: "icon",
44
+ fn: withCtx(() => [
45
+ createVNode(_component_NcLoadingIcon, { size: 20 })
46
+ ]),
47
+ key: "0"
48
+ }
49
+ : undefined
50
+ ]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["variant", "disabled", "onClick"])
51
+ ]),
52
+ createElementVNode("div", _hoisted_3, [
53
+ ($data.result)
54
+ ? (openBlock(), createBlock(_component_NcNoteCard, {
55
+ key: 0,
56
+ type: $data.result.success ? 'success' : 'error',
57
+ "data-testid": "cn-admin-action-card-result"
58
+ }, {
59
+ default: withCtx(() => [
60
+ createTextVNode(toDisplayString($data.result.message), 1 /* TEXT */)
61
+ ]),
62
+ _: 1 /* STABLE */
63
+ }, 8 /* PROPS */, ["type"]))
64
+ : createCommentVNode("v-if", true)
65
+ ])
66
+ ]),
67
+ _: 3 /* FORWARDED */
68
+ }, 8 /* PROPS */, ["title"]))
69
+ }
70
+
71
+ export { render };
72
+ //# sourceMappingURL=CnAdminActionCard.vue3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnAdminActionCard.vue3.js","sources":["../../../../src/components/CnAdminActionCard/CnAdminActionCard.vue?vue&type=template&id=57907ee5&lang.js"],"sourcesContent":["<!-- SPDX-License-Identifier: EUPL-1.2 -->\n<!-- SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl> -->\n<template>\n\t<CnConfigurationCard class=\"cn-admin-action-card\" :title=\"title\" data-testid=\"cn-admin-action-card\">\n\t\t<p v-if=\"description\" class=\"cn-admin-action-card__description\">\n\t\t\t{{ description }}\n\t\t</p>\n\t\t<!-- @slot default Extra content between the explanation and the button. -->\n\t\t<slot />\n\t\t<div class=\"cn-admin-action-card__footer\">\n\t\t\t<NcButton\n\t\t\t\t:variant=\"buttonVariant\"\n\t\t\t\t:disabled=\"running || disabled\"\n\t\t\t\tdata-testid=\"cn-admin-action-card-run\"\n\t\t\t\t@click=\"run\">\n\t\t\t\t<template v-if=\"running\" #icon>\n\t\t\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ running ? runningLabel : resolvedButtonLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<!-- role=status so a screen reader hears the outcome without moving focus. -->\n\t\t<div role=\"status\" aria-live=\"polite\">\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"result\"\n\t\t\t\t:type=\"result.success ? 'success' : 'error'\"\n\t\t\t\tdata-testid=\"cn-admin-action-card-result\">\n\t\t\t\t{{ result.message }}\n\t\t\t</NcNoteCard>\n\t\t</div>\n\t</CnConfigurationCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport CnConfigurationCard from '../CnConfigurationCard/CnConfigurationCard.vue'\n\n/**\n * CnAdminActionCard — one maintenance action on an admin settings page.\n *\n * A card with a title, a short explanation and one button. The click POSTs\n * an app action, the button spins while it runs, and the server's message\n * shows below it as success or error. Use it for work an admin starts by\n * hand, such as repairing the register or re-importing schemas. That work\n * never belongs in the setup wizard.\n *\n * By default it posts to the app's setup action contract,\n * `/apps/{appId}/api/setup/action/{action}`, the same endpoint the setup\n * wizard uses. Pass `url` to post somewhere else.\n *\n * ```vue\n * <CnAdminActionCard\n * app-id=\"pipelinq\"\n * action=\"provision\"\n * :title=\"t('pipelinq', 'Repair the register')\"\n * :description=\"t('pipelinq', 'Creates missing schemas again. Your data stays as it is.')\"\n * :button-label=\"t('pipelinq', 'Repair')\" />\n * ```\n *\n * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-an-admin-action-card-runs-one-server-action\n */\nexport default {\n\tname: 'CnAdminActionCard',\n\n\tcomponents: {\n\t\tCnConfigurationCard,\n\t\tNcButton,\n\t\tNcLoadingIcon,\n\t\tNcNoteCard,\n\t},\n\n\tprops: {\n\t\t/** Card heading. Pass it translated. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** What the action does, in one or two short sentences. Pass it translated. */\n\t\tdescription: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The Nextcloud app id; builds the default action URL. */\n\t\tappId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The action id, posted to `/apps/{appId}/api/setup/action/{action}`. */\n\t\taction: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * An app path to POST to instead of the setup action contract, e.g.\n\t\t * `/apps/pipelinq/api/settings/repair`. Passed through `generateUrl`.\n\t\t */\n\t\turl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Request body to send with the POST. */\n\t\tpayload: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Button label. Defaults to \"Run\". */\n\t\tbuttonLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Label next to the spinner while the action runs. */\n\t\trunningLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Running…'),\n\t\t},\n\n\t\t/** NcButton variant for the action button. */\n\t\tbuttonVariant: {\n\t\t\ttype: String,\n\t\t\tdefault: 'secondary',\n\t\t},\n\n\t\t/** Disable the button, e.g. while a precondition is not met. */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: ['result'],\n\n\tdata() {\n\t\treturn {\n\t\t\trunning: false,\n\t\t\t/** @type {{ success: boolean, message: string }|null} */\n\t\t\tresult: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tresolvedButtonLabel() {\n\t\t\treturn this.buttonLabel || t('nextcloud-vue', 'Run')\n\t\t},\n\n\t\t/**\n\t\t * The app path the button posts to: `url` when set, else the setup\n\t\t * action contract. Empty when neither is configured.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tactionPath() {\n\t\t\tif (this.url) {\n\t\t\t\treturn this.url\n\t\t\t}\n\t\t\tif (this.appId && this.action) {\n\t\t\t\treturn `/apps/${this.appId}/api/setup/action/${this.action}`\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Run the action: POST, spin while it runs, show the result.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-an-admin-action-card-runs-one-server-action\n\t\t */\n\t\tasync run() {\n\t\t\tif (this.running) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.actionPath) {\n\t\t\t\tthis.result = { success: false, message: t('nextcloud-vue', 'This action is not configured.') }\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.running = true\n\t\t\tthis.result = null\n\t\t\tlet data\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst response = await axios.post(generateUrl(this.actionPath), this.payload)\n\t\t\t\tdata = response && response.data\n\t\t\t\tthis.result = {\n\t\t\t\t\tsuccess: !!data && data.success !== false,\n\t\t\t\t\tmessage: (data && data.message) || t('nextcloud-vue', 'Done.'),\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tdata = err && err.response ? err.response.data : null\n\t\t\t\tthis.result = { success: false, message: this.errorMessage(err) }\n\t\t\t} finally {\n\t\t\t\tthis.running = false\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event result The action finished.\n\t\t\t * @type {{ success: boolean, message: string, data: object|null }}\n\t\t\t */\n\t\t\tthis.$emit('result', { ...this.result, data })\n\t\t},\n\n\t\terrorMessage(err) {\n\t\t\tconst data = err && err.response && err.response.data\n\t\t\tif (data && (data.message || data.error)) {\n\t\t\t\treturn data.message || data.error\n\t\t\t}\n\t\t\treturn (err && err.message) || t('nextcloud-vue', 'Something went wrong.')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-admin-action-card__description {\n\tmargin: 0 0 12px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-admin-action-card__footer {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-block: 8px;\n}\n</style>\n"],"names":["_createBlock","_createElementBlock","_toDisplayString","_renderSlot","_createTextVNode","_createVNode"],"mappings":";;;;AAMK,EAAA,KAAA,EAAA,mCAAA;;AAKE,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,8BAAc,GAAA;;AAapB,EAAA,IAAI,EAAC,QAAI;AAAA,EAAA,WAAA,EAAA,QAAA;;;;;;;;;uBAnBbA;AAAgB,IAAA,KAAA,EAAA,sBAAA;AAAA,IAAA,KAAA,EAAA,MAAA,CAAA,KAAA;AAAA,IAAA,aAAA,EAAA,sBAAA;;qBACjB,MAEI;OAFD,MAAA,CAAA,WAAA;wBAAHC,kBAEI,CAAA,GAAA,EAFJ,UAEI,EAAAC,eAAA,CADC,MAAU,CAAA,WAAA,CAAA,EAAA,CAAA,YAAA;;MAGdC,UAAO,CAAA,IAAA,CAAA,MAAA,EAAA,SAAA,CAAA;yBAYF,iBAAA,EAAA;mBADM,CAAA,mBAAA,EAAA;AART,UAAA,OAAO,EAAE,MAAa,CAAA,aAAA;UACtB,QAAQ,EAAE,KAAC,CAAA,OAAA,IAAA,MAAA,CAAA,QAAA;AACZ,UAAA,aAAW,EAAC,0BAAkB;AAC7B,UAAA,OAAK,EAAE,QAAG,CAAA,GAAA;;2BAGF,MACT;AADS,YAAAC,eAAA,CAAA,GACT,GAAGF,eAAA,CAAA,KAAA,CAAA,OAAA,GAAA,MAAA,CAAA,YAAA,GAAA,QAAA,CAAA,mBAAA,CAAA,EAAA,CAAA,YAAA;;;;WAHO,KAAA,CAAA,OAAA;;sBAAA,MAAA;4BACT,MAA4B;kBAA5BG,WAA4B,CAAA,wBAAA,EAAA,EAAZ,IAAI,EAAE,EAAG,EAAA,CAAA;;;;;;;;SAQpB,KAAM,CAAA,MAAA;0BADZL,WAKY,CAAA,qBAAA,EAAA;;cAHX,IAAI,EAAE,KAAM,CAAA,MAAA,CAAC,OAAK,GAAA,SAAA,GAAA,OAAA;AACnB,cAAA,aAAW,EAAA,6BAAA;;+BACX,MAAG;AAAA,gBAAAI,eAAA,CAAAF,eAAA,CAAA,KAAA,CAAA,MAAA,CAAA,OAAA,CAAA,EAAA,CAAA,YAAA;;;;;;;;;;;;;"}
@@ -1,11 +1,13 @@
1
1
  import { getCurrentUser } from '@nextcloud/auth';
2
2
  import axios__default from '@nextcloud/axios';
3
3
  import { loadState } from '@nextcloud/initial-state';
4
+ import { translate } from '@nextcloud/l10n';
4
5
  import { generateUrl } from '@nextcloud/router';
5
6
  import { NcAppContent, NcAppSettingsDialog, NcAppSettingsSection, NcButton, NcContent, NcEmptyContent, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue';
6
7
  import { shallowRef, watch, computed, reactive } from 'vue';
7
8
  import DatabaseSearchOutline from 'vue-material-design-icons/DatabaseSearchOutline.vue';
8
9
  import OpenInNew from 'vue-material-design-icons/OpenInNew.vue';
10
+ import Play from 'vue-material-design-icons/Play.vue';
9
11
  import Restart from 'vue-material-design-icons/Restart.vue';
10
12
  import __sfc_main$5 from '../CnAiCompanion/CnAiCompanion.vue.js';
11
13
  import __sfc_main$1 from '../CnAppLoading/CnAppLoading.vue.js';
@@ -29,7 +31,7 @@ import { useSetupStatus } from '../../composables/useSetupStatus.js';
29
31
  import { useSupportDialog } from '../../composables/useSupportDialog.js';
30
32
  import { provideTenantContext } from '../../composables/useTenantContext.js';
31
33
  import { useUserPreferences } from '../../composables/useUserPreferences.js';
32
- import { useWalkthrough, readLocalWalkthroughSeenVersion, loadWalkthroughSeenVersion, normaliseSeenVersion, persistWalkthroughSeenVersion } from '../../composables/useWalkthrough.js';
34
+ import { useWalkthrough, readLocalWalkthroughSeenVersion, readLocalWalkthroughProgress, loadWalkthroughSeenVersion, loadWalkthroughProgress, normaliseSeenVersion, persistWalkthroughSeenVersion, persistWalkthroughProgress } from '../../composables/useWalkthrough.js';
33
35
  import { RegistryKindError } from '../../errors/RegistryKindError.js';
34
36
  import { isAppInstalled } from '../../utils/appInstalled.js';
35
37
  import { BUILT_IN_FORMATTERS } from '../../utils/builtInFormatters.js';
@@ -106,6 +108,7 @@ var script = {
106
108
  NcNoteCard,
107
109
  DatabaseSearchOutline,
108
110
  OpenInNew,
111
+ Play,
109
112
  Restart,
110
113
  CnAppNav: __sfc_main,
111
114
  CnAppLoading: __sfc_main$1,
@@ -1133,6 +1136,15 @@ var script = {
1133
1136
  * @type {string}
1134
1137
  */
1135
1138
  walkthroughSeenVersionValue: readLocalWalkthroughSeenVersion(this.appId),
1139
+ /**
1140
+ * Where the user is in an unfinished tour (`{ tourId, stepId,
1141
+ * index, version }`), or null. Seeded from the local mirror, then
1142
+ * replaced by the per-user `<completionConfigKey>-progress`
1143
+ * preference. The tour resumes at this step on the next visit.
1144
+ *
1145
+ * @type {object|null}
1146
+ */
1147
+ walkthroughProgressValue: readLocalWalkthroughProgress(this.appId),
1136
1148
  /**
1137
1149
  * Whether `walkthroughSeenVersionValue` is settled. Starts `true`
1138
1150
  * when the manifest declares no `completionConfigKey` (the local
@@ -1886,13 +1898,15 @@ var script = {
1886
1898
  try {
1887
1899
  const p = new URLSearchParams(window.location.search);
1888
1900
  const tourId = p.get('cn_resume_tour');
1889
- if (!tourId) {
1890
- return null
1901
+ if (tourId) {
1902
+ return { tourId, stepId: p.get('cn_resume_step') || '' }
1891
1903
  }
1892
- return { tourId, stepId: p.get('cn_resume_step') || '' }
1893
1904
  } catch {
1894
- return null
1905
+ // No URL token; fall through to the remembered progress.
1895
1906
  }
1907
+ // An unfinished tour continues at the step the user reached.
1908
+ const progress = this.walkthroughProgressValue;
1909
+ return progress ? { tourId: progress.tourId, stepId: progress.stepId } : null
1896
1910
  },
1897
1911
 
1898
1912
  phase() {
@@ -2135,6 +2149,24 @@ var script = {
2135
2149
  return this.translate('Restart walkthrough')
2136
2150
  },
2137
2151
 
2152
+ /**
2153
+ * Label for continuing an unfinished tour at the remembered step.
2154
+ *
2155
+ * @return {string}
2156
+ */
2157
+ continueWalkthroughLabel() {
2158
+ return translate('nextcloud-vue', 'Continue where you left off')
2159
+ },
2160
+
2161
+ /**
2162
+ * Label for restarting an unfinished tour from the first step.
2163
+ *
2164
+ * @return {string}
2165
+ */
2166
+ startOverWalkthroughLabel() {
2167
+ return translate('nextcloud-vue', 'Start over')
2168
+ },
2169
+
2138
2170
  /**
2139
2171
  * Resolve the active modal's Vue component from the registry.
2140
2172
  * Returns null when no modal is open or the key no longer resolves.
@@ -2638,10 +2670,12 @@ var script = {
2638
2670
  return
2639
2671
  }
2640
2672
  try {
2641
- this.walkthroughSeenVersionValue = await loadWalkthroughSeenVersion(
2642
- this.appId,
2643
- this.walkthroughConfigKey,
2644
- );
2673
+ const [seen, progress] = await Promise.all([
2674
+ loadWalkthroughSeenVersion(this.appId, this.walkthroughConfigKey),
2675
+ loadWalkthroughProgress(this.appId, this.walkthroughConfigKey),
2676
+ ]);
2677
+ this.walkthroughSeenVersionValue = seen;
2678
+ this.walkthroughProgressValue = progress;
2645
2679
  } finally {
2646
2680
  this.walkthroughSeenResolved = true;
2647
2681
  }
@@ -2665,12 +2699,68 @@ var script = {
2665
2699
  // Fire-and-forget: the loader never rejects and the local mirror is
2666
2700
  // already written, so a failed PUT must not surface as an error.
2667
2701
  persistWalkthroughSeenVersion(this.appId, this.walkthroughConfigKey, v);
2702
+ // A finished or skipped tour has nothing to continue.
2703
+ if (this.walkthroughProgressValue) {
2704
+ this.walkthroughProgressValue = null;
2705
+ persistWalkthroughProgress(this.appId, this.walkthroughConfigKey, null);
2706
+ }
2668
2707
  /**
2669
2708
  * @event walkthrough-complete Emitted when the walkthrough finishes or is dismissed.
2670
2709
  */
2671
2710
  this.$emit('walkthrough-complete');
2672
2711
  },
2673
2712
 
2713
+ /**
2714
+ * Remember where the user is in the tour, so a reload or a restart
2715
+ * entry continues there (local mirror + per-user preference).
2716
+ *
2717
+ * @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md
2718
+ * @param {object} progress `{ tourId, stepId, index }` from CnWalkthrough.
2719
+ * @return {void}
2720
+ */
2721
+ onWalkthroughProgress(progress) {
2722
+ if (!progress || !progress.tourId) {
2723
+ return
2724
+ }
2725
+ // The first step of a fresh tour is where a restart lands anyway,
2726
+ // so it costs no request.
2727
+ if (progress.index === 0 && !this.walkthroughProgressValue) {
2728
+ return
2729
+ }
2730
+ const value = {
2731
+ tourId: progress.tourId,
2732
+ stepId: progress.stepId || '',
2733
+ index: progress.index || 0,
2734
+ version: String((this.manifest && this.manifest.version) || ''),
2735
+ };
2736
+ this.walkthroughProgressValue = value;
2737
+ persistWalkthroughProgress(this.appId, this.walkthroughConfigKey, value);
2738
+ },
2739
+
2740
+ /**
2741
+ * Continue an unfinished tour from the user-settings dialog: show a
2742
+ * paused tour again, or start the remembered tour at its step.
2743
+ *
2744
+ * @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md
2745
+ * @return {void}
2746
+ */
2747
+ continueWalkthroughFromSettings() {
2748
+ this.userSettingsOpen = false;
2749
+ if (!this.walkthroughEnabled) {
2750
+ return
2751
+ }
2752
+ setTimeout(() => {
2753
+ const wt = useWalkthrough(this.appId, this.manifest);
2754
+ if (wt.resumePaused()) {
2755
+ return
2756
+ }
2757
+ const progress = this.walkthroughProgressValue;
2758
+ if (progress) {
2759
+ wt.resumeAt(progress.tourId, progress.stepId);
2760
+ }
2761
+ }, 50);
2762
+ },
2763
+
2674
2764
  /**
2675
2765
  * Replay the product tour from the user-settings dialog. Closes the
2676
2766
  * dialog first, then restarts the first declared tour on the next tick