@conduction/nextcloud-vue 2.62.0 → 2.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/CnAppNav/CnAppNav.vue2.js +271 -32
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- 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 +9369 -5193
- 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/CnAppNav/CnAppNav.vue +271 -32
- 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 +23 -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
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
:successText="successText"
|
|
13
13
|
:validate="validateStep"
|
|
14
14
|
:cancellable="cancellable"
|
|
15
|
-
:initialStep="initialStepId"
|
|
15
|
+
:initialStep="dependencyBlocked ? '' : initialStepId"
|
|
16
|
+
:nextDisabled="dependencyBlocked"
|
|
16
17
|
@stepChange="onStepChange"
|
|
17
18
|
@submit="onSubmit"
|
|
18
19
|
@close="onClose">
|
|
@@ -21,9 +22,33 @@
|
|
|
21
22
|
`<template>` (verified against the generated `createSlots` call)
|
|
22
23
|
and honours it only on the child, so the rule's advice is inverted
|
|
23
24
|
here — moving the key up would throw it away. -->
|
|
25
|
+
<!-- A required app is missing: this is the only step, and Next stays
|
|
26
|
+
disabled until the app is there. -->
|
|
27
|
+
<template v-if="dependencyBlocked" #[dependencyStepSlot]>
|
|
28
|
+
<div class="cn-setup-step" data-step-type="dependencies" data-testid="cn-setup-dependencies">
|
|
29
|
+
<NcNoteCard type="warning">
|
|
30
|
+
{{ dependencyBlockedText }}
|
|
31
|
+
</NcNoteCard>
|
|
32
|
+
<CnLeafDependencySettings
|
|
33
|
+
:appId="appId"
|
|
34
|
+
:dependencies="dependencyEntries"
|
|
35
|
+
:isAdmin="isAdmin"
|
|
36
|
+
:sectionName="dependencySectionName"
|
|
37
|
+
sectionDescription=""
|
|
38
|
+
@installed="onDependencyInstalled" />
|
|
39
|
+
</div>
|
|
40
|
+
</template>
|
|
24
41
|
<!-- eslint-disable vue/no-v-for-template-key-on-child -->
|
|
25
42
|
<template v-for="step in setupSteps" #[stepSlot(step)]="scope">
|
|
26
43
|
<div :key="step.id" class="cn-setup-step" :data-step-type="step.type">
|
|
44
|
+
<!-- Optional apps that are missing never block, but the admin
|
|
45
|
+
reads about them once, on the first step. -->
|
|
46
|
+
<NcNoteCard
|
|
47
|
+
v-if="step.id === firstStepId && missingOptional.length > 0"
|
|
48
|
+
type="info"
|
|
49
|
+
data-testid="cn-setup-optional-dependencies">
|
|
50
|
+
{{ optionalMissingText }}
|
|
51
|
+
</NcNoteCard>
|
|
27
52
|
<!-- @slot step-{id} Override a step's body (for `component` steps or
|
|
28
53
|
any bespoke step). Scope: the CnWizardDialog step scope plus
|
|
29
54
|
`{ step, runAction, saveConfig }`. -->
|
|
@@ -55,7 +80,33 @@
|
|
|
55
80
|
:disabled="isChoiceDisabled(step)"
|
|
56
81
|
:loading="isOptionsLoading(step)"
|
|
57
82
|
:modelValue="cardModel(step)"
|
|
58
|
-
@update:modelValue="(v) => onChoice(step, v)"
|
|
83
|
+
@update:modelValue="(v) => onChoice(step, v)">
|
|
84
|
+
<!-- `loadAction`: every dataset card except "none" loads
|
|
85
|
+
itself, so no separate run-action step is needed. -->
|
|
86
|
+
<template v-if="step.loadAction" #option-actions="{ option }">
|
|
87
|
+
<template v-if="isLoadableOption(step, option)">
|
|
88
|
+
<NcButton
|
|
89
|
+
variant="secondary"
|
|
90
|
+
:disabled="isDatasetLoading(step, option)"
|
|
91
|
+
:aria-label="loadAriaLabel(option)"
|
|
92
|
+
data-testid="cn-setup-load-dataset"
|
|
93
|
+
@click="loadDataset(step, option)">
|
|
94
|
+
<template v-if="isDatasetLoading(step, option)" #icon>
|
|
95
|
+
<NcLoadingIcon :size="20" />
|
|
96
|
+
</template>
|
|
97
|
+
{{ loadButtonLabel(step, option) }}
|
|
98
|
+
</NcButton>
|
|
99
|
+
<p
|
|
100
|
+
v-if="datasetResultFor(step, option)"
|
|
101
|
+
role="status"
|
|
102
|
+
class="cn-setup-load-result"
|
|
103
|
+
:class="datasetResultFor(step, option).success ? 'cn-setup-load-result--success' : 'cn-setup-load-result--error'"
|
|
104
|
+
data-testid="cn-setup-load-result">
|
|
105
|
+
{{ datasetResultFor(step, option).message }}
|
|
106
|
+
</p>
|
|
107
|
+
</template>
|
|
108
|
+
</template>
|
|
109
|
+
</CnChoiceCards>
|
|
59
110
|
<NcSelect
|
|
60
111
|
v-else
|
|
61
112
|
:inputLabel="stepTitle(step)"
|
|
@@ -147,13 +198,23 @@
|
|
|
147
198
|
</template>
|
|
148
199
|
|
|
149
200
|
<script>
|
|
201
|
+
import { getCurrentUser } from '@nextcloud/auth'
|
|
202
|
+
import { loadState } from '@nextcloud/initial-state'
|
|
150
203
|
import { translate as t } from '@nextcloud/l10n'
|
|
151
204
|
import { NcButton, NcCheckboxRadioSwitch, NcLoadingIcon, NcNoteCard, NcSelect, NcTextField } from '@nextcloud/vue'
|
|
152
205
|
import CnChoiceCards from '../CnChoiceCards/CnChoiceCards.vue'
|
|
206
|
+
import CnLeafDependencySettings from '../CnLeafDependencySettings/CnLeafDependencySettings.vue'
|
|
153
207
|
import CnWizardDialog from '../CnWizardDialog/CnWizardDialog.vue'
|
|
208
|
+
import { checkDependencies, isDependencyResolved } from '../../composables/useDependencyCheck.js'
|
|
154
209
|
import { useSetupStatus } from '../../composables/useSetupStatus.js'
|
|
155
210
|
import { fieldsFromSchema } from '../../utils/schema.js'
|
|
156
211
|
|
|
212
|
+
/** Id of the synthetic step shown while a required app is missing. */
|
|
213
|
+
const DEPENDENCY_STEP_ID = 'cn-setup-dependencies'
|
|
214
|
+
|
|
215
|
+
/** The dataset value that means "seed nothing"; it never gets a Load button. */
|
|
216
|
+
const NONE_DATASET = 'none'
|
|
217
|
+
|
|
157
218
|
/**
|
|
158
219
|
* CnSetupWizard — abstract, manifest-driven first-time setup wizard (ADR-042).
|
|
159
220
|
*
|
|
@@ -183,6 +244,7 @@ export default {
|
|
|
183
244
|
components: {
|
|
184
245
|
CnWizardDialog,
|
|
185
246
|
CnChoiceCards,
|
|
247
|
+
CnLeafDependencySettings,
|
|
186
248
|
NcButton,
|
|
187
249
|
NcNoteCard,
|
|
188
250
|
NcSelect,
|
|
@@ -201,6 +263,11 @@ export default {
|
|
|
201
263
|
* standalone (no CnAppRoot ancestor).
|
|
202
264
|
*/
|
|
203
265
|
cnTranslate: { default: () => (key) => key },
|
|
266
|
+
/**
|
|
267
|
+
* The app manifest, provided by CnAppRoot. Read only for its
|
|
268
|
+
* `dependencies` when the `dependencies` prop is empty.
|
|
269
|
+
*/
|
|
270
|
+
cnManifest: { default: null },
|
|
204
271
|
},
|
|
205
272
|
|
|
206
273
|
props: {
|
|
@@ -310,6 +377,33 @@ export default {
|
|
|
310
377
|
type: Array,
|
|
311
378
|
default: () => [],
|
|
312
379
|
},
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* The app's dependencies, in the manifest's own shape (app-id strings
|
|
383
|
+
* or `{ id, name?, required? }`). Falls back to the injected manifest's
|
|
384
|
+
* `dependencies`. A missing REQUIRED one replaces every step with the
|
|
385
|
+
* dependency list and disables Next; a missing optional one is listed
|
|
386
|
+
* on the first step and does not block.
|
|
387
|
+
*
|
|
388
|
+
* @type {Array<string|object>}
|
|
389
|
+
* @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step
|
|
390
|
+
*/
|
|
391
|
+
dependencies: {
|
|
392
|
+
type: Array,
|
|
393
|
+
default: () => [],
|
|
394
|
+
},
|
|
395
|
+
|
|
396
|
+
/** Load button label on a dataset card (`loadAction`). */
|
|
397
|
+
loadLabel: {
|
|
398
|
+
type: String,
|
|
399
|
+
default: () => t('nextcloud-vue', 'Load'),
|
|
400
|
+
},
|
|
401
|
+
|
|
402
|
+
/** Load button label once that dataset loaded in this session. */
|
|
403
|
+
reloadLabel: {
|
|
404
|
+
type: String,
|
|
405
|
+
default: () => t('nextcloud-vue', 'Load again'),
|
|
406
|
+
},
|
|
313
407
|
},
|
|
314
408
|
|
|
315
409
|
emits: ['complete', 'close', 'step-change', 'action-result'],
|
|
@@ -345,6 +439,9 @@ export default {
|
|
|
345
439
|
actionResult: {},
|
|
346
440
|
localDone: {},
|
|
347
441
|
userTouched: {},
|
|
442
|
+
// Per-card load state for `loadAction` steps, keyed by `loadKey()`.
|
|
443
|
+
datasetRunning: {},
|
|
444
|
+
datasetResult: {},
|
|
348
445
|
}
|
|
349
446
|
},
|
|
350
447
|
|
|
@@ -362,11 +459,150 @@ export default {
|
|
|
362
459
|
: this.dialogTitle
|
|
363
460
|
},
|
|
364
461
|
|
|
365
|
-
|
|
462
|
+
/**
|
|
463
|
+
* Every well-formed step, before `requires` is applied.
|
|
464
|
+
*
|
|
465
|
+
* @return {Array<object>}
|
|
466
|
+
*/
|
|
467
|
+
declaredSteps() {
|
|
366
468
|
return (this.steps || []).filter((s) => s && s.id && s.type)
|
|
367
469
|
},
|
|
368
470
|
|
|
471
|
+
/**
|
|
472
|
+
* Steps that declare `requires` on an app that is not installed and
|
|
473
|
+
* enabled. They are not offered; the summary names them as skipped.
|
|
474
|
+
*
|
|
475
|
+
* @return {Array<{ step: object, missing: Array<string> }>}
|
|
476
|
+
* @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step
|
|
477
|
+
*/
|
|
478
|
+
skippedSteps() {
|
|
479
|
+
return this.declaredSteps
|
|
480
|
+
.filter((s) => Array.isArray(s.requires) && s.requires.length > 0)
|
|
481
|
+
.map((step) => {
|
|
482
|
+
const known = Object.fromEntries(this.dependencyRows.map((d) => [d.id, d]))
|
|
483
|
+
const missing = checkDependencies(step.requires, this.serverAppStatuses)
|
|
484
|
+
.map((row) => known[row.id] || row)
|
|
485
|
+
.filter((row) => !isDependencyResolved(row))
|
|
486
|
+
.map((row) => row.name || row.id)
|
|
487
|
+
return { step, missing }
|
|
488
|
+
})
|
|
489
|
+
.filter((entry) => entry.missing.length > 0)
|
|
490
|
+
},
|
|
491
|
+
|
|
492
|
+
setupSteps() {
|
|
493
|
+
const skipped = new Set(this.skippedSteps.map((entry) => entry.step.id))
|
|
494
|
+
return this.declaredSteps.filter((s) => !skipped.has(s.id))
|
|
495
|
+
},
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Id of the first offered step: where the optional-apps note shows.
|
|
499
|
+
*
|
|
500
|
+
* @return {string}
|
|
501
|
+
*/
|
|
502
|
+
firstStepId() {
|
|
503
|
+
return this.setupSteps.length > 0 ? this.setupSteps[0].id : ''
|
|
504
|
+
},
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* The dependency declarations to check: the prop, else the manifest's.
|
|
508
|
+
*
|
|
509
|
+
* @return {Array<string|object>}
|
|
510
|
+
*/
|
|
511
|
+
dependencyEntries() {
|
|
512
|
+
if (Array.isArray(this.dependencies) && this.dependencies.length > 0) {
|
|
513
|
+
return this.dependencies
|
|
514
|
+
}
|
|
515
|
+
const fromManifest = this.cnManifest && this.cnManifest.dependencies
|
|
516
|
+
return Array.isArray(fromManifest) ? fromManifest : []
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* The `dependency_statuses` initial state an app may inject from PHP.
|
|
521
|
+
* Same source CnAppRoot reads; `{}` when the app injects none.
|
|
522
|
+
*
|
|
523
|
+
* @return {object}
|
|
524
|
+
*/
|
|
525
|
+
serverAppStatuses() {
|
|
526
|
+
try {
|
|
527
|
+
return loadState(this.appId, 'dependency_statuses', {}) || {}
|
|
528
|
+
} catch {
|
|
529
|
+
return {}
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* The declared dependencies with their live status.
|
|
535
|
+
*
|
|
536
|
+
* @return {Array<object>}
|
|
537
|
+
*/
|
|
538
|
+
dependencyRows() {
|
|
539
|
+
return checkDependencies(this.dependencyEntries, this.serverAppStatuses)
|
|
540
|
+
},
|
|
541
|
+
|
|
542
|
+
/** @return {Array<object>} Required dependencies that are missing or disabled. */
|
|
543
|
+
missingRequired() {
|
|
544
|
+
return this.dependencyRows.filter((d) => d.required && !isDependencyResolved(d))
|
|
545
|
+
},
|
|
546
|
+
|
|
547
|
+
/** @return {Array<object>} Optional dependencies that are missing or disabled. */
|
|
548
|
+
missingOptional() {
|
|
549
|
+
return this.dependencyRows.filter((d) => !d.required && !isDependencyResolved(d))
|
|
550
|
+
},
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Whether a required app is missing, so the wizard shows the
|
|
554
|
+
* dependency list instead of its steps and refuses to continue.
|
|
555
|
+
*
|
|
556
|
+
* @return {boolean}
|
|
557
|
+
* @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step
|
|
558
|
+
*/
|
|
559
|
+
dependencyBlocked() {
|
|
560
|
+
return this.missingRequired.length > 0
|
|
561
|
+
},
|
|
562
|
+
|
|
563
|
+
dependencyStepSlot() {
|
|
564
|
+
return 'step-' + DEPENDENCY_STEP_ID
|
|
565
|
+
},
|
|
566
|
+
|
|
567
|
+
dependencySectionName() {
|
|
568
|
+
return t('nextcloud-vue', 'Apps this app needs')
|
|
569
|
+
},
|
|
570
|
+
|
|
571
|
+
dependencyBlockedText() {
|
|
572
|
+
return t('nextcloud-vue', 'Setup can start once these apps are installed and enabled: {apps}.', {
|
|
573
|
+
apps: this.missingRequired.map((d) => d.name).join(', '),
|
|
574
|
+
})
|
|
575
|
+
},
|
|
576
|
+
|
|
577
|
+
optionalMissingText() {
|
|
578
|
+
return t('nextcloud-vue', 'Optional, not installed: {apps}. You can add them later.', {
|
|
579
|
+
apps: this.missingOptional.map((d) => d.name).join(', '),
|
|
580
|
+
})
|
|
581
|
+
},
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Whether the current user is a Nextcloud admin; only admins get the
|
|
585
|
+
* install and enable buttons.
|
|
586
|
+
*
|
|
587
|
+
* @return {boolean}
|
|
588
|
+
*/
|
|
589
|
+
isAdmin() {
|
|
590
|
+
try {
|
|
591
|
+
const user = getCurrentUser()
|
|
592
|
+
return !!(user && user.isAdmin)
|
|
593
|
+
} catch {
|
|
594
|
+
return false
|
|
595
|
+
}
|
|
596
|
+
},
|
|
597
|
+
|
|
369
598
|
wizardSteps() {
|
|
599
|
+
if (this.dependencyBlocked) {
|
|
600
|
+
return [{
|
|
601
|
+
id: DEPENDENCY_STEP_ID,
|
|
602
|
+
label: t('nextcloud-vue', 'Required apps'),
|
|
603
|
+
optional: false,
|
|
604
|
+
}]
|
|
605
|
+
}
|
|
370
606
|
return this.setupSteps.map((s) => ({
|
|
371
607
|
id: s.id,
|
|
372
608
|
label: this.stepTitle(s),
|
|
@@ -390,6 +626,15 @@ export default {
|
|
|
390
626
|
* @return {Array<{ id: string, title: string, value: string, done: boolean, notRun: boolean }>}
|
|
391
627
|
*/
|
|
392
628
|
summaryItems() {
|
|
629
|
+
const skipped = this.skippedSteps
|
|
630
|
+
.filter((entry) => entry.step.type !== 'summary' && entry.step.type !== 'info')
|
|
631
|
+
.map((entry) => ({
|
|
632
|
+
id: entry.step.id,
|
|
633
|
+
title: this.stepTitle(entry.step),
|
|
634
|
+
value: t('nextcloud-vue', 'Skipped, needs {apps}', { apps: entry.missing.join(', ') }),
|
|
635
|
+
done: false,
|
|
636
|
+
notRun: true,
|
|
637
|
+
}))
|
|
393
638
|
return this.setupSteps
|
|
394
639
|
.filter((s) => s.type !== 'summary')
|
|
395
640
|
.map((step) => {
|
|
@@ -426,6 +671,7 @@ export default {
|
|
|
426
671
|
}
|
|
427
672
|
return { id: step.id, title: this.stepTitle(step), value, done, notRun: false }
|
|
428
673
|
})
|
|
674
|
+
.concat(skipped)
|
|
429
675
|
},
|
|
430
676
|
|
|
431
677
|
/**
|
|
@@ -486,6 +732,11 @@ export default {
|
|
|
486
732
|
* @return {void}
|
|
487
733
|
*/
|
|
488
734
|
maybeAutoRunStep(stepId) {
|
|
735
|
+
// Nothing runs while a required app is missing: every action
|
|
736
|
+
// would fail with an error that does not name the app.
|
|
737
|
+
if (this.dependencyBlocked) {
|
|
738
|
+
return
|
|
739
|
+
}
|
|
489
740
|
const step = this.setupSteps.find((s) => s.id === stepId)
|
|
490
741
|
if (step && step.type === 'run-action' && !this.isOnDemand(step) && !this.isStepDone(step.id) && !this.running[step.id]) {
|
|
491
742
|
this.runAction(step)
|
|
@@ -868,6 +1119,131 @@ export default {
|
|
|
868
1119
|
}
|
|
869
1120
|
},
|
|
870
1121
|
|
|
1122
|
+
/**
|
|
1123
|
+
* Key for one card's load state.
|
|
1124
|
+
*
|
|
1125
|
+
* @param {object} step The choice step.
|
|
1126
|
+
* @param {object} option The card's option.
|
|
1127
|
+
* @return {string}
|
|
1128
|
+
*/
|
|
1129
|
+
loadKey(step, option) {
|
|
1130
|
+
return step.id + '::' + String(option && option.value)
|
|
1131
|
+
},
|
|
1132
|
+
|
|
1133
|
+
/**
|
|
1134
|
+
* Whether a card gets a Load button: the step declares `loadAction`
|
|
1135
|
+
* and the option is not the "seed nothing" answer.
|
|
1136
|
+
*
|
|
1137
|
+
* @param {object} step The choice step.
|
|
1138
|
+
* @param {object} option The card's option.
|
|
1139
|
+
* @return {boolean}
|
|
1140
|
+
* @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-a-dataset-card-loads-itself
|
|
1141
|
+
*/
|
|
1142
|
+
isLoadableOption(step, option) {
|
|
1143
|
+
return !!(step && step.loadAction) && !!option && String(option.value) !== NONE_DATASET
|
|
1144
|
+
},
|
|
1145
|
+
|
|
1146
|
+
isDatasetLoading(step, option) {
|
|
1147
|
+
return this.datasetRunning[this.loadKey(step, option)] === true
|
|
1148
|
+
},
|
|
1149
|
+
|
|
1150
|
+
datasetResultFor(step, option) {
|
|
1151
|
+
return this.datasetResult[this.loadKey(step, option)] || null
|
|
1152
|
+
},
|
|
1153
|
+
|
|
1154
|
+
loadButtonLabel(step, option) {
|
|
1155
|
+
if (this.isDatasetLoading(step, option)) {
|
|
1156
|
+
return this.runningLabel
|
|
1157
|
+
}
|
|
1158
|
+
const result = this.datasetResultFor(step, option)
|
|
1159
|
+
return (result && result.success) ? this.reloadLabel : this.loadLabel
|
|
1160
|
+
},
|
|
1161
|
+
|
|
1162
|
+
loadAriaLabel(option) {
|
|
1163
|
+
return t('nextcloud-vue', 'Load {name}', { name: option.label })
|
|
1164
|
+
},
|
|
1165
|
+
|
|
1166
|
+
/**
|
|
1167
|
+
* Load one dataset from its card: POST `{ dataset }` to the step's
|
|
1168
|
+
* `loadAction`, spinner on the button while it runs, the result on the
|
|
1169
|
+
* card after. A successful load also selects the card, so the summary
|
|
1170
|
+
* and the stored choice name what was loaded.
|
|
1171
|
+
*
|
|
1172
|
+
* @param {object} step The choice step declaring `loadAction`.
|
|
1173
|
+
* @param {object} option The card's option.
|
|
1174
|
+
* @return {Promise<void>}
|
|
1175
|
+
* @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-a-dataset-card-loads-itself
|
|
1176
|
+
*/
|
|
1177
|
+
async loadDataset(step, option) {
|
|
1178
|
+
if (!this.isLoadableOption(step, option) || this.isDatasetLoading(step, option)) {
|
|
1179
|
+
return
|
|
1180
|
+
}
|
|
1181
|
+
const key = this.loadKey(step, option)
|
|
1182
|
+
this.datasetRunning[key] = true
|
|
1183
|
+
this.datasetResult[key] = null
|
|
1184
|
+
let result
|
|
1185
|
+
try {
|
|
1186
|
+
const [{ default: axios }, { generateUrl }] = await Promise.all([
|
|
1187
|
+
import('@nextcloud/axios'),
|
|
1188
|
+
import('@nextcloud/router'),
|
|
1189
|
+
])
|
|
1190
|
+
const { data } = await axios.post(
|
|
1191
|
+
generateUrl(`/apps/${this.appId}/api/setup/action/${step.loadAction}`),
|
|
1192
|
+
{ dataset: option.value },
|
|
1193
|
+
)
|
|
1194
|
+
result = {
|
|
1195
|
+
success: !!data && data.success !== false,
|
|
1196
|
+
message: (data && data.message) || t('nextcloud-vue', 'Loaded.'),
|
|
1197
|
+
}
|
|
1198
|
+
} catch (err) {
|
|
1199
|
+
result = { success: false, message: this.errorMessage(err) }
|
|
1200
|
+
} finally {
|
|
1201
|
+
this.datasetRunning[key] = false
|
|
1202
|
+
}
|
|
1203
|
+
this.datasetResult[key] = result
|
|
1204
|
+
if (result.success) {
|
|
1205
|
+
this.selectLoadedOption(step, option)
|
|
1206
|
+
this.localDone[step.id] = true
|
|
1207
|
+
}
|
|
1208
|
+
this.$emit('action-result', {
|
|
1209
|
+
stepId: step.id,
|
|
1210
|
+
action: step.loadAction,
|
|
1211
|
+
dataset: option.value,
|
|
1212
|
+
...result,
|
|
1213
|
+
})
|
|
1214
|
+
},
|
|
1215
|
+
|
|
1216
|
+
/**
|
|
1217
|
+
* Select a card that just loaded, adding to (never replacing) the
|
|
1218
|
+
* selection of a `multiple` step.
|
|
1219
|
+
*
|
|
1220
|
+
* @param {object} step The choice step.
|
|
1221
|
+
* @param {object} option The loaded option.
|
|
1222
|
+
* @return {void}
|
|
1223
|
+
*/
|
|
1224
|
+
selectLoadedOption(step, option) {
|
|
1225
|
+
if (step.multiple === true) {
|
|
1226
|
+
const current = this.cardModel(step)
|
|
1227
|
+
if (current.some((v) => String(v) === String(option.value))) {
|
|
1228
|
+
return
|
|
1229
|
+
}
|
|
1230
|
+
this.onChoice(step, current.concat([option.value]))
|
|
1231
|
+
return
|
|
1232
|
+
}
|
|
1233
|
+
this.onChoice(step, option.value)
|
|
1234
|
+
},
|
|
1235
|
+
|
|
1236
|
+
/**
|
|
1237
|
+
* A required app was installed from the dependency list. A new app's
|
|
1238
|
+
* scripts and its `OC.appswebroots` entry exist only after a full page
|
|
1239
|
+
* load, and `useAppStatus` caches per page, so reload.
|
|
1240
|
+
*
|
|
1241
|
+
* @return {void}
|
|
1242
|
+
*/
|
|
1243
|
+
onDependencyInstalled() {
|
|
1244
|
+
window.location.reload()
|
|
1245
|
+
},
|
|
1246
|
+
|
|
871
1247
|
/**
|
|
872
1248
|
* Whether a step is marked on-demand (`onDemand: true`): it runs only
|
|
873
1249
|
* when the user asks, never automatically, and the summary ticks it
|
|
@@ -971,6 +1347,19 @@ export default {
|
|
|
971
1347
|
color: var(--color-text-maxcontrast);
|
|
972
1348
|
}
|
|
973
1349
|
|
|
1350
|
+
.cn-setup-load-result {
|
|
1351
|
+
margin: 0;
|
|
1352
|
+
font-size: var(--font-size-small, 13px);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
.cn-setup-load-result--success {
|
|
1356
|
+
color: var(--color-success-text, var(--color-success));
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
.cn-setup-load-result--error {
|
|
1360
|
+
color: var(--color-error-text, var(--color-error));
|
|
1361
|
+
}
|
|
1362
|
+
|
|
974
1363
|
.cn-setup-field {
|
|
975
1364
|
display: flex;
|
|
976
1365
|
flex-direction: column;
|