@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.
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js +10 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js.map +1 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js +189 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js.map +1 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js +72 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js.map +1 -0
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +99 -9
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +39 -18
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +53 -37
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -2
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js +10 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js +293 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js +189 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +216 -8
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +118 -50
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +2 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js +3 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +234 -9
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +96 -57
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +51 -2
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +5 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/selfModeActions.js +3 -3
- package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +36 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js +121 -12
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js.map +1 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js +3 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +326 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +112 -26
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +10 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +337 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +102 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js +34 -0
- package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js.map +1 -0
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +131 -8
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js +40 -38
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +8 -6
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js +19 -0
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js.map +1 -1
- package/dist/esm/composables/useDependencyCheck.js +65 -0
- package/dist/esm/composables/useDependencyCheck.js.map +1 -0
- package/dist/esm/composables/useWalkthrough.js +218 -6
- package/dist/esm/composables/useWalkthrough.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +43 -0
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +43 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +449 -0
- package/dist/esm/store/useObjectStore.js +8 -0
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/columnFilters.js +311 -0
- package/dist/esm/utils/columnFilters.js.map +1 -0
- package/dist/esm/utils/groupAutocomplete.js +123 -0
- package/dist/esm/utils/groupAutocomplete.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +2 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/pickerOptions.js +198 -0
- package/dist/esm/utils/pickerOptions.js.map +1 -0
- package/dist/esm/utils/schema.js +83 -7
- package/dist/esm/utils/schema.js.map +1 -1
- package/dist/esm/utils/timelineEvents.js +174 -0
- package/dist/esm/utils/timelineEvents.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/walkthroughSignals.js +88 -0
- package/dist/esm/utils/walkthroughSignals.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +9098 -5161
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +449 -0
- package/l10n/en.json +43 -0
- package/l10n/nl.json +43 -0
- package/package.json +1 -1
- package/src/components/CnAdminActionCard/CnAdminActionCard.vue +234 -0
- package/src/components/CnAdminActionCard/index.js +4 -0
- package/src/components/CnAppRoot/CnAppRoot.vue +129 -15
- package/src/components/CnChoiceCards/CnChoiceCards.vue +56 -31
- package/src/components/CnDashboardPage/CnDashboardPage.vue +2 -2
- package/src/components/CnDataTable/CnColumnFilterPopover.vue +524 -0
- package/src/components/CnDataTable/CnDataTable.md +3 -0
- package/src/components/CnDataTable/CnDataTable.vue +260 -4
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +2 -2
- package/src/components/CnFieldHelper/CnFieldHelper.vue +4 -2
- package/src/components/CnFormDialog/CnFormDialog.vue +290 -31
- package/src/components/CnIndexPage/CnIndexPage.vue +55 -2
- package/src/components/CnIndexPage/selfModeActions.js +3 -3
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +39 -1
- package/src/components/CnResourceSelect/CnResourceSelect.vue +123 -12
- package/src/components/CnSetupWizard/CnSetupWizard.vue +392 -3
- package/src/components/CnTimelineWidget/CnTimelineWidget.vue +477 -0
- package/src/components/CnTimelineWidget/dashboardRegistration.js +32 -0
- package/src/components/CnTimelineWidget/index.js +4 -0
- package/src/components/CnWalkthrough/CnWalkthrough.vue +143 -10
- package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/src/components/CnWizardDialog/CnWizardDialog.vue +20 -1
- package/src/components/index.js +2 -0
- package/src/composables/useDependencyCheck.js +61 -0
- package/src/composables/useWalkthrough.js +217 -5
- package/src/css/dashboard.css +49 -0
- package/src/css/detail-page.css +19 -0
- package/src/css/table.css +81 -0
- package/src/index.js +2 -0
- package/src/schemas/app-manifest-v2.schema.json +3 -1
- package/src/store/useObjectStore.js +8 -0
- package/src/utils/columnFilters.js +324 -0
- package/src/utils/groupAutocomplete.js +119 -0
- package/src/utils/libraryWidgetKeys.js +2 -0
- package/src/utils/pickerOptions.js +194 -0
- package/src/utils/schema.js +83 -7
- package/src/utils/timelineEvents.js +171 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
- 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 (
|
|
1890
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|