@conduction/nextcloud-vue 2.66.0 → 2.67.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/CnActionsBar/CnActionsBar.vue2.js +19 -0
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +10 -3
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +2 -2
- 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/CnDataTable/CnDataTable.vue2.js +4 -4
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +31 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +335 -313
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +5 -11
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/composables/useDependencyCheck.js +44 -1
- package/dist/esm/composables/useDependencyCheck.js.map +1 -1
- package/dist/esm/composables/useListView.js +43 -1
- package/dist/esm/composables/useListView.js.map +1 -1
- package/dist/esm/composables/useSetupStatus.js +26 -3
- package/dist/esm/composables/useSetupStatus.js.map +1 -1
- package/dist/esm/l10n/en.json.js +4 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +4 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +20 -2
- package/dist/esm/store/plugins/liveUpdates.js +19 -9
- package/dist/esm/store/plugins/liveUpdates.js.map +1 -1
- package/dist/esm/store/useObjectStore.js +20 -9
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +857 -652
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +20 -2
- package/l10n/en.json +4 -1
- package/l10n/nl.json +4 -1
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.md +2 -1
- package/src/components/CnActionsBar/CnActionsBar.vue +25 -1
- package/src/components/CnAppNav/CnAppNav.vue +2 -2
- package/src/components/CnDataTable/CnDataTable.md +1 -1
- package/src/components/CnDataTable/CnDataTable.vue +4 -4
- package/src/components/CnIndexPage/CnIndexPage.md +2 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +47 -1
- package/src/components/CnSetupWizard/CnSetupWizard.vue +5 -11
- package/src/composables/useDependencyCheck.js +43 -0
- package/src/composables/useListView.js +43 -1
- package/src/composables/useSetupStatus.js +26 -3
- package/src/css/actions-bar.css +20 -2
- package/src/schemas/app-manifest-v2.schema.json +3 -3
- package/src/schemas/app-manifest.schema.json +1 -1
- package/src/store/plugins/liveUpdates.js +19 -9
- package/src/store/useObjectStore.js +19 -8
- package/src/utils/validateManifestV2.compiled.js +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnSetupWizard.vue3.js","sources":["../../../../src/components/CnSetupWizard/CnSetupWizard.vue?vue&type=template&id=75b2078b&lang.js"],"sourcesContent":["<!-- SPDX-License-Identifier: EUPL-1.2 -->\n<!-- SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl> -->\n<template>\n\t<CnWizardDialog\n\t\tref=\"wizard\"\n\t\t:steps=\"wizardSteps\"\n\t\t:dialogTitle=\"resolvedDialogTitle\"\n\t\t:submitLabel=\"submitLabel\"\n\t\t:cancelLabel=\"cancelLabel\"\n\t\t:nextLabel=\"nextLabel\"\n\t\t:backLabel=\"backLabel\"\n\t\t:successText=\"successText\"\n\t\t:validate=\"validateStep\"\n\t\t:cancellable=\"cancellable\"\n\t\t:initialStep=\"dependencyBlocked ? '' : initialStepId\"\n\t\t:nextDisabled=\"dependencyBlocked\"\n\t\t@stepChange=\"onStepChange\"\n\t\t@submit=\"onSubmit\"\n\t\t@close=\"onClose\">\n\t\t<!-- This `<template v-for>` defines dynamic SLOTS, not a rendered list.\n\t\t `@vue/compiler-sfc` DISCARDS a `:key` on a slot-defining\n\t\t `<template>` (verified against the generated `createSlots` call)\n\t\t and honours it only on the child, so the rule's advice is inverted\n\t\t here — moving the key up would throw it away. -->\n\t\t<!-- A required app is missing: this is the only step, and Next stays\n\t\t disabled until the app is there. -->\n\t\t<template v-if=\"dependencyBlocked\" #[dependencyStepSlot]>\n\t\t\t<div class=\"cn-setup-step\" data-step-type=\"dependencies\" data-testid=\"cn-setup-dependencies\">\n\t\t\t\t<NcNoteCard type=\"warning\">\n\t\t\t\t\t{{ dependencyBlockedText }}\n\t\t\t\t</NcNoteCard>\n\t\t\t\t<CnLeafDependencySettings\n\t\t\t\t\t:appId=\"appId\"\n\t\t\t\t\t:dependencies=\"dependencyEntries\"\n\t\t\t\t\t:isAdmin=\"isAdmin\"\n\t\t\t\t\t:sectionName=\"dependencySectionName\"\n\t\t\t\t\tsectionDescription=\"\"\n\t\t\t\t\t@installed=\"onDependencyInstalled\" />\n\t\t\t</div>\n\t\t</template>\n\t\t<!-- eslint-disable vue/no-v-for-template-key-on-child -->\n\t\t<template v-for=\"step in setupSteps\" #[stepSlot(step)]=\"scope\">\n\t\t\t<div :key=\"step.id\" class=\"cn-setup-step\" :data-step-type=\"step.type\">\n\t\t\t\t<!-- Optional apps that are missing never block, but the admin\n\t\t\t\t reads about them once, on the first step. -->\n\t\t\t\t<NcNoteCard\n\t\t\t\t\tv-if=\"step.id === firstStepId && missingOptional.length > 0\"\n\t\t\t\t\ttype=\"info\"\n\t\t\t\t\tdata-testid=\"cn-setup-optional-dependencies\">\n\t\t\t\t\t{{ optionalMissingText }}\n\t\t\t\t</NcNoteCard>\n\t\t\t\t<!-- @slot step-{id} Override a step's body (for `component` steps or\n\t\t\t\t any bespoke step). Scope: the CnWizardDialog step scope plus\n\t\t\t\t `{ step, runAction, saveConfig }`. -->\n\t\t\t\t<slot\n\t\t\t\t\tv-if=\"hasCustomSlot(step.id)\"\n\t\t\t\t\t:name=\"stepSlot(step)\"\n\t\t\t\t\tv-bind=\"scope\"\n\t\t\t\t\t:step=\"step\"\n\t\t\t\t\t:runAction=\"() => runAction(step)\"\n\t\t\t\t\t:saveConfig=\"(patch) => saveConfig(patch)\" />\n\n\t\t\t\t<!-- info -->\n\t\t\t\t<template v-else-if=\"step.type === 'info'\">\n\t\t\t\t\t<NcNoteCard type=\"info\" :heading=\"stepTitle(step)\">\n\t\t\t\t\t\t{{ stepBody(step) }}\n\t\t\t\t\t</NcNoteCard>\n\t\t\t\t</template>\n\n\t\t\t\t<!-- choice -->\n\t\t\t\t<template v-else-if=\"step.type === 'choice'\">\n\t\t\t\t\t<NcNoteCard v-if=\"step.body\" type=\"info\">\n\t\t\t\t\t\t{{ stepBody(step) }}\n\t\t\t\t\t</NcNoteCard>\n\t\t\t\t\t<CnChoiceCards\n\t\t\t\t\t\tv-if=\"isCardChoice(step)\"\n\t\t\t\t\t\t:label=\"stepTitle(step)\"\n\t\t\t\t\t\t:options=\"optionsFor(step)\"\n\t\t\t\t\t\t:multiple=\"step.multiple === true\"\n\t\t\t\t\t\t:disabled=\"isChoiceDisabled(step)\"\n\t\t\t\t\t\t:loading=\"isOptionsLoading(step)\"\n\t\t\t\t\t\t:modelValue=\"cardModel(step)\"\n\t\t\t\t\t\t@update:modelValue=\"(v) => onChoice(step, v)\">\n\t\t\t\t\t\t<!-- `loadAction`: every dataset card except \"none\" loads\n\t\t\t\t\t\t itself, so no separate run-action step is needed. -->\n\t\t\t\t\t\t<template v-if=\"step.loadAction\" #option-actions=\"{ option }\">\n\t\t\t\t\t\t\t<template v-if=\"isLoadableOption(step, option)\">\n\t\t\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\t\t\t:disabled=\"isDatasetLoading(step, option)\"\n\t\t\t\t\t\t\t\t\t:aria-label=\"loadAriaLabel(option)\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-setup-load-dataset\"\n\t\t\t\t\t\t\t\t\t@click=\"loadDataset(step, option)\">\n\t\t\t\t\t\t\t\t\t<template v-if=\"isDatasetLoading(step, option)\" #icon>\n\t\t\t\t\t\t\t\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t\t{{ loadButtonLabel(step, option) }}\n\t\t\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\tv-if=\"datasetResultFor(step, option)\"\n\t\t\t\t\t\t\t\t\trole=\"status\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-setup-load-result\"\n\t\t\t\t\t\t\t\t\t:class=\"datasetResultFor(step, option).success ? 'cn-setup-load-result--success' : 'cn-setup-load-result--error'\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-setup-load-result\">\n\t\t\t\t\t\t\t\t\t{{ datasetResultFor(step, option).message }}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</CnChoiceCards>\n\t\t\t\t\t<NcSelect\n\t\t\t\t\t\tv-else\n\t\t\t\t\t\t:inputLabel=\"stepTitle(step)\"\n\t\t\t\t\t\t:options=\"optionsFor(step)\"\n\t\t\t\t\t\t:multiple=\"step.multiple === true\"\n\t\t\t\t\t\t:disabled=\"isChoiceDisabled(step)\"\n\t\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t\t:modelValue=\"choiceModel[step.id]\"\n\t\t\t\t\t\t@update:modelValue=\"(v) => onChoice(step, v)\" />\n\t\t\t\t\t<NcNoteCard v-if=\"isChoiceDisabled(step)\" type=\"warning\">\n\t\t\t\t\t\t{{ dependsOnHint(step) }}\n\t\t\t\t\t</NcNoteCard>\n\t\t\t\t</template>\n\n\t\t\t\t<!-- config-fields -->\n\t\t\t\t<template v-else-if=\"step.type === 'config-fields'\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-for=\"field in fieldsFor(step)\"\n\t\t\t\t\t\t:key=\"field.key\"\n\t\t\t\t\t\tclass=\"cn-setup-field\">\n\t\t\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\t\t\tv-if=\"field.widget === 'checkbox'\"\n\t\t\t\t\t\t\t:modelValue=\"!!configModel[field.key]\"\n\t\t\t\t\t\t\t@update:modelValue=\"(v) => configModel[field.key] = v\">\n\t\t\t\t\t\t\t{{ field.label }}\n\t\t\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t\tv-else-if=\"field.widget === 'select'\"\n\t\t\t\t\t\t\t:inputLabel=\"field.label\"\n\t\t\t\t\t\t\t:options=\"field.enum || []\"\n\t\t\t\t\t\t\t:modelValue=\"configModel[field.key]\"\n\t\t\t\t\t\t\t@update:modelValue=\"(v) => configModel[field.key] = v\" />\n\t\t\t\t\t\t<NcTextField\n\t\t\t\t\t\t\tv-else\n\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t:type=\"field.widget === 'number' ? 'number' : 'text'\"\n\t\t\t\t\t\t\t:modelValue=\"configModel[field.key] != null ? String(configModel[field.key]) : ''\"\n\t\t\t\t\t\t\t@update:modelValue=\"(v) => configModel[field.key] = v\" />\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\n\t\t\t\t<!-- run-action -->\n\t\t\t\t<template v-else-if=\"step.type === 'run-action'\">\n\t\t\t\t\t<NcNoteCard v-if=\"step.body\" type=\"info\">\n\t\t\t\t\t\t{{ stepBody(step) }}\n\t\t\t\t\t</NcNoteCard>\n\t\t\t\t\t<!-- The action starts automatically on entering this step\n\t\t\t\t\t (see `maybeAutoRunStep`), so the default state is the\n\t\t\t\t\t spinner, not an idle button. -->\n\t\t\t\t\t<div v-if=\"running[step.id]\" class=\"cn-setup-step__running\">\n\t\t\t\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t\t\t\t<span>{{ runningLabel }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t<NcNoteCard\n\t\t\t\t\t\t\tv-if=\"actionResult[step.id]\"\n\t\t\t\t\t\t\t:type=\"actionResult[step.id].success ? 'success' : 'error'\">\n\t\t\t\t\t\t\t{{ actionResult[step.id].message }}\n\t\t\t\t\t\t</NcNoteCard>\n\t\t\t\t\t\t<div class=\"cn-setup-step__nav\">\n\t\t\t\t\t\t\t<NcButton :variant=\"actionResult[step.id] ? 'secondary' : 'primary'\" @click=\"runAction(step)\">\n\t\t\t\t\t\t\t\t{{ actionResult[step.id] ? rerunLabel : runLabel }}\n\t\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</template>\n\t\t\t\t</template>\n\n\t\t\t\t<!-- summary -->\n\t\t\t\t<template v-else-if=\"step.type === 'summary'\">\n\t\t\t\t\t<NcNoteCard v-if=\"step.body\" type=\"info\">\n\t\t\t\t\t\t{{ stepBody(step) }}\n\t\t\t\t\t</NcNoteCard>\n\t\t\t\t\t<ul class=\"cn-setup-summary\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"item in summaryItems\"\n\t\t\t\t\t\t\t:key=\"item.id\"\n\t\t\t\t\t\t\tclass=\"cn-setup-summary__item\"\n\t\t\t\t\t\t\t:class=\"{ 'cn-setup-summary__item--done': item.done, 'cn-setup-summary__item--not-run': item.notRun }\">\n\t\t\t\t\t\t\t<span class=\"cn-setup-summary__mark\">{{ item.done ? '✓' : '○' }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-setup-summary__label\">{{ item.title }}</span>\n\t\t\t\t\t\t\t<span v-if=\"item.value\" class=\"cn-setup-summary__value\">{{ item.value }}</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t</template>\n\t\t<!-- eslint-enable vue/no-v-for-template-key-on-child -->\n\t</CnWizardDialog>\n</template>\n\n<script>\nimport { getCurrentUser } from '@nextcloud/auth'\nimport { loadState } from '@nextcloud/initial-state'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcLoadingIcon, NcNoteCard, NcSelect, NcTextField } from '@nextcloud/vue'\nimport CnChoiceCards from '../CnChoiceCards/CnChoiceCards.vue'\nimport CnLeafDependencySettings from '../CnLeafDependencySettings/CnLeafDependencySettings.vue'\nimport CnWizardDialog from '../CnWizardDialog/CnWizardDialog.vue'\nimport { checkDependencies, isDependencyResolved } from '../../composables/useDependencyCheck.js'\nimport { useSetupStatus } from '../../composables/useSetupStatus.js'\nimport { fieldsFromSchema } from '../../utils/schema.js'\n\n/** Id of the synthetic step shown while a required app is missing. */\nconst DEPENDENCY_STEP_ID = 'cn-setup-dependencies'\n\n/** The dataset value that means \"seed nothing\"; it never gets a Load button. */\nconst NONE_DATASET = 'none'\n\n/**\n * CnSetupWizard — abstract, manifest-driven first-time setup wizard (ADR-042).\n *\n * Wraps {@link CnWizardDialog} and renders a `manifest.setup.steps[]` array by\n * each step's `type`: `info` (note), `config-fields` (fields from a JSON Schema\n * via `fieldsFromSchema`, the same field shapes the admin pages use), `choice`\n * (`NcSelect` persisting to an app-config key), `run-action` (POST a privileged\n * server action), `summary` (recap), and `component` (a parent-provided\n * `#step-<id>` slot). It NEVER writes OpenRegister objects from the browser —\n * all persistence goes through the per-app setup contract\n * (`POST /apps/{appId}/api/setup/config` and `/api/setup/action/{actionId}`).\n *\n * Mount it gated by `CnAppRoot`'s setup phase, or open it on demand from\n * `CnAdminSettingsShell`.\n *\n * ```vue\n * <CnSetupWizard\n * :app-id=\"'procest'\"\n * :steps=\"manifest.setup.steps\"\n * @complete=\"onSetupComplete\"\n * @close=\"showWizard = false\" />\n * ```\n */\nexport default {\n\tname: 'CnSetupWizard',\n\n\tcomponents: {\n\t\tCnWizardDialog,\n\t\tCnChoiceCards,\n\t\tCnLeafDependencySettings,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tNcSelect,\n\t\tNcTextField,\n\t\tNcCheckboxRadioSwitch,\n\t\tNcLoadingIcon,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Consumer translation function, provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). Step field\n\t\t * labels come from schema property titles, authored in English as the\n\t\t * canonical source; the visible label is resolved through this function\n\t\t * so it follows the user's language. Defaults to identity when used\n\t\t * standalone (no CnAppRoot ancestor).\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/**\n\t\t * The app manifest, provided by CnAppRoot. Read only for its\n\t\t * `dependencies` when the `dependencies` prop is empty.\n\t\t */\n\t\tcnManifest: { default: null },\n\t},\n\n\tprops: {\n\t\t/** The Nextcloud app id; used to build the `/apps/{appId}/api/setup/*` URLs. */\n\t\tappId: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** The `manifest.setup.steps` array to render. */\n\t\tsteps: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Dialog title. Overridden by `appName` when that prop is set. */\n\t\tdialogTitle: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Set up this app'),\n\t\t},\n\n\t\t/**\n\t\t * The app's human-readable display name. When set, the dialog title\n\t\t * becomes \"Set up {appName}\" instead of the generic `dialogTitle`\n\t\t * default — pass the same display name CnAppRoot already resolves\n\t\t * (`appDisplayName || manifest.name || appId`).\n\t\t */\n\t\tappName: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Final-step submit button label. */\n\t\tsubmitLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Finish'),\n\t\t},\n\n\t\t/** Cancel button label. */\n\t\tcancelLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Cancel'),\n\t\t},\n\n\t\t/** Next button label. */\n\t\tnextLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Next'),\n\t\t},\n\n\t\t/** Back button label. */\n\t\tbackLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Back'),\n\t\t},\n\n\t\t/** Run-action button label (shown before the first automatic run). */\n\t\trunLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Run'),\n\t\t},\n\n\t\t/** Run-action button label offered after a run already finished. */\n\t\trerunLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Run again'),\n\t\t},\n\n\t\t/** Label shown next to the spinner while a run-action step is running. */\n\t\trunningLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Loading…'),\n\t\t},\n\n\t\t/** Result-phase success text. */\n\t\tsuccessText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Setup complete.'),\n\t\t},\n\n\t\t/**\n\t\t * Whether the wizard can be dismissed before finishing (Cancel\n\t\t * button, ESC, backdrop click). Pass `false` when a REQUIRED step\n\t\t * is unmet and the host is gating its shell behind this wizard\n\t\t * (ADR-042) — an offered-but-non-functional Cancel would be\n\t\t * misleading. Optional-only setup stays cancellable (default).\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tcancellable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/**\n\t\t * Ids of steps the SERVER already reports as done (e.g.\n\t\t * `useSetupStatus(...).steps` filtered to `done === true`), passed by\n\t\t * the host so a freshly (re)mounted wizard doesn't re-ask something\n\t\t * already persisted in an earlier session — this component's own\n\t\t * `localDone`/`choiceModel` only track the CURRENT session and reset\n\t\t * blank on every mount. Drives both the initial step (`initialStepId`)\n\t\t * and the summary page's done markers.\n\t\t *\n\t\t * @type {Array<string>}\n\t\t */\n\t\tcompletedStepIds: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The app's dependencies, in the manifest's own shape (app-id strings\n\t\t * or `{ id, name?, required? }`). Falls back to the injected manifest's\n\t\t * `dependencies`. A missing REQUIRED one replaces every step with the\n\t\t * dependency list and disables Next; a missing optional one is listed\n\t\t * on the first step and does not block.\n\t\t *\n\t\t * @type {Array<string|object>}\n\t\t * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step\n\t\t */\n\t\tdependencies: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Load button label on a dataset card (`loadAction`). */\n\t\tloadLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Load'),\n\t\t},\n\n\t\t/** Load button label once that dataset loaded in this session. */\n\t\treloadLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Load again'),\n\t\t},\n\t},\n\n\temits: ['complete', 'close', 'step-change', 'action-result'],\n\n\t/**\n\t * Live option lists come from the app's own setup status document, so a\n\t * step that offers a choice of things the SERVER ships never has to\n\t * restate them in the manifest. `useSetupStatus` caches per app id, so the\n\t * host's own status fetch (CnAppRoot's setup gate) is the one that runs —\n\t * this reads the same refs rather than asking again.\n\t *\n\t * Only called when a step actually declares `optionsSource`: a wizard with\n\t * none must not start making requests it never made before.\n\t *\n\t * @param {object} props The component props.\n\t * @return {object} `setupStatus` / `setupStatusLoading` for the option resolver.\n\t */\n\tsetup(props) {\n\t\tconst wantsSource = (props.steps || []).some((s) => s && s.optionsSource)\n\t\tif (!wantsSource) {\n\t\t\treturn {}\n\t\t}\n\t\tconst { status, loading } = useSetupStatus(props.appId, { setup: { steps: props.steps } })\n\t\treturn { setupStatus: status, setupStatusLoading: loading }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tchoiceModel: {},\n\t\t\tchoiceValues: {},\n\t\t\tconfigModel: {},\n\t\t\trunning: {},\n\t\t\tactionResult: {},\n\t\t\tlocalDone: {},\n\t\t\tuserTouched: {},\n\t\t\t// Per-card load state for `loadAction` steps, keyed by `loadKey()`.\n\t\t\tdatasetRunning: {},\n\t\t\tdatasetResult: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The title actually rendered by CnWizardDialog: \"Set up {appName}\"\n\t\t * when a display name was passed, otherwise whatever `dialogTitle`\n\t\t * resolves to (a caller override, or its own generic default).\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedDialogTitle() {\n\t\t\treturn this.appName\n\t\t\t\t? t('nextcloud-vue', 'Set up {app}', { app: this.appName })\n\t\t\t\t: this.dialogTitle\n\t\t},\n\n\t\t/**\n\t\t * Every well-formed step, before `requires` is applied.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tdeclaredSteps() {\n\t\t\treturn (this.steps || []).filter((s) => s && s.id && s.type)\n\t\t},\n\n\t\t/**\n\t\t * Steps that declare `requires` on an app that is not installed and\n\t\t * enabled. They are not offered; the summary names them as skipped.\n\t\t *\n\t\t * @return {Array<{ step: object, missing: Array<string> }>}\n\t\t * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step\n\t\t */\n\t\tskippedSteps() {\n\t\t\treturn this.declaredSteps\n\t\t\t\t.filter((s) => Array.isArray(s.requires) && s.requires.length > 0)\n\t\t\t\t.map((step) => {\n\t\t\t\t\tconst known = Object.fromEntries(this.dependencyRows.map((d) => [d.id, d]))\n\t\t\t\t\tconst missing = checkDependencies(step.requires, this.serverAppStatuses)\n\t\t\t\t\t\t.map((row) => known[row.id] || row)\n\t\t\t\t\t\t.filter((row) => !isDependencyResolved(row))\n\t\t\t\t\t\t.map((row) => row.name || row.id)\n\t\t\t\t\treturn { step, missing }\n\t\t\t\t})\n\t\t\t\t.filter((entry) => entry.missing.length > 0)\n\t\t},\n\n\t\tsetupSteps() {\n\t\t\tconst skipped = new Set(this.skippedSteps.map((entry) => entry.step.id))\n\t\t\treturn this.declaredSteps.filter((s) => !skipped.has(s.id))\n\t\t},\n\n\t\t/**\n\t\t * Id of the first offered step: where the optional-apps note shows.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tfirstStepId() {\n\t\t\treturn this.setupSteps.length > 0 ? this.setupSteps[0].id : ''\n\t\t},\n\n\t\t/**\n\t\t * The dependency declarations to check: the prop, else the manifest's.\n\t\t *\n\t\t * @return {Array<string|object>}\n\t\t */\n\t\tdependencyEntries() {\n\t\t\tif (Array.isArray(this.dependencies) && this.dependencies.length > 0) {\n\t\t\t\treturn this.dependencies\n\t\t\t}\n\t\t\tconst fromManifest = this.cnManifest && this.cnManifest.dependencies\n\t\t\treturn Array.isArray(fromManifest) ? fromManifest : []\n\t\t},\n\n\t\t/**\n\t\t * The `dependency_statuses` initial state an app may inject from PHP.\n\t\t * Same source CnAppRoot reads; `{}` when the app injects none.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tserverAppStatuses() {\n\t\t\ttry {\n\t\t\t\treturn loadState(this.appId, 'dependency_statuses', {}) || {}\n\t\t\t} catch {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The declared dependencies with their live status.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tdependencyRows() {\n\t\t\treturn checkDependencies(this.dependencyEntries, this.serverAppStatuses)\n\t\t},\n\n\t\t/** @return {Array<object>} Required dependencies that are missing or disabled. */\n\t\tmissingRequired() {\n\t\t\treturn this.dependencyRows.filter((d) => d.required && !isDependencyResolved(d))\n\t\t},\n\n\t\t/** @return {Array<object>} Optional dependencies that are missing or disabled. */\n\t\tmissingOptional() {\n\t\t\treturn this.dependencyRows.filter((d) => !d.required && !isDependencyResolved(d))\n\t\t},\n\n\t\t/**\n\t\t * Whether a required app is missing, so the wizard shows the\n\t\t * dependency list instead of its steps and refuses to continue.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step\n\t\t */\n\t\tdependencyBlocked() {\n\t\t\treturn this.missingRequired.length > 0\n\t\t},\n\n\t\tdependencyStepSlot() {\n\t\t\treturn 'step-' + DEPENDENCY_STEP_ID\n\t\t},\n\n\t\tdependencySectionName() {\n\t\t\treturn t('nextcloud-vue', 'Apps this app needs')\n\t\t},\n\n\t\tdependencyBlockedText() {\n\t\t\treturn t('nextcloud-vue', 'Setup can start once these apps are installed and enabled: {apps}.', {\n\t\t\t\tapps: this.missingRequired.map((d) => d.name).join(', '),\n\t\t\t})\n\t\t},\n\n\t\toptionalMissingText() {\n\t\t\treturn t('nextcloud-vue', 'Optional, not installed: {apps}. You can add them later.', {\n\t\t\t\tapps: this.missingOptional.map((d) => d.name).join(', '),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Whether the current user is a Nextcloud admin; only admins get the\n\t\t * install and enable buttons.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisAdmin() {\n\t\t\ttry {\n\t\t\t\tconst user = getCurrentUser()\n\t\t\t\treturn !!(user && user.isAdmin)\n\t\t\t} catch {\n\t\t\t\treturn false\n\t\t\t}\n\t\t},\n\n\t\twizardSteps() {\n\t\t\tif (this.dependencyBlocked) {\n\t\t\t\treturn [{\n\t\t\t\t\tid: DEPENDENCY_STEP_ID,\n\t\t\t\t\tlabel: t('nextcloud-vue', 'Required apps'),\n\t\t\t\t\toptional: false,\n\t\t\t\t}]\n\t\t\t}\n\t\t\treturn this.setupSteps.map((s) => ({\n\t\t\t\tid: s.id,\n\t\t\t\tlabel: this.stepTitle(s),\n\t\t\t\toptional: s.required !== true,\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Recap rows for the `summary` step — one per non-summary step, with\n\t\t * the value the user selected (choice label / config field values) and\n\t\t * a done marker (info steps always done; choice steps done when picked;\n\t\t * run-action steps done when the action succeeded).\n\t\t *\n\t\t * An on-demand step (`onDemand: true`) is the exception: it is done\n\t\t * only when its action succeeded in THIS session. Apps report such a\n\t\t * step as `done` on purpose so it never auto-runs (learniq's \"Remove\n\t\t * the example data\"), and reading that flag here ticked a removal\n\t\t * nobody ran. When it did not run it gets `notRun: true` and the value\n\t\t * \"Not run\" instead.\n\t\t *\n\t\t * @return {Array<{ id: string, title: string, value: string, done: boolean, notRun: boolean }>}\n\t\t */\n\t\tsummaryItems() {\n\t\t\tconst skipped = this.skippedSteps\n\t\t\t\t.filter((entry) => entry.step.type !== 'summary' && entry.step.type !== 'info')\n\t\t\t\t.map((entry) => ({\n\t\t\t\t\tid: entry.step.id,\n\t\t\t\t\ttitle: this.stepTitle(entry.step),\n\t\t\t\t\tvalue: t('nextcloud-vue', 'Skipped, needs {apps}', { apps: entry.missing.join(', ') }),\n\t\t\t\t\tdone: false,\n\t\t\t\t\tnotRun: true,\n\t\t\t\t}))\n\t\t\treturn this.setupSteps\n\t\t\t\t.filter((s) => s.type !== 'summary')\n\t\t\t\t.map((step) => {\n\t\t\t\t\tlet value = ''\n\t\t\t\t\tif (step.type === 'choice') {\n\t\t\t\t\t\tconst raw = this.choiceModel[step.id]\n\t\t\t\t\t\tif (Array.isArray(raw)) {\n\t\t\t\t\t\t\tvalue = raw.map((o) => this.choiceLabel(step, o)).join(', ')\n\t\t\t\t\t\t} else if (raw !== null && raw !== undefined && raw !== '') {\n\t\t\t\t\t\t\tvalue = this.choiceLabel(step, raw)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (step.type === 'config-fields') {\n\t\t\t\t\t\tvalue = this.fieldsFor(step)\n\t\t\t\t\t\t\t.map((f) => `${f.label}: ${this.configModel[f.key] !== null && this.configModel[f.key] !== undefined ? this.configModel[f.key] : ''}`)\n\t\t\t\t\t\t\t.join(', ')\n\t\t\t\t\t}\n\t\t\t\t\tif (this.isOnDemand(step)) {\n\t\t\t\t\t\tconst ran = this.ranThisSession(step.id)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tid: step.id,\n\t\t\t\t\t\t\ttitle: this.stepTitle(step),\n\t\t\t\t\t\t\tvalue: ran ? '' : t('nextcloud-vue', 'Not run'),\n\t\t\t\t\t\t\tdone: ran,\n\t\t\t\t\t\t\tnotRun: !ran,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tlet done\n\t\t\t\t\tif (step.type === 'info') {\n\t\t\t\t\t\tdone = true\n\t\t\t\t\t} else if (step.type === 'choice') {\n\t\t\t\t\t\tdone = this.hasChoice(step) || this.isServerDone(step.id)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdone = this.isStepDone(step.id)\n\t\t\t\t\t}\n\t\t\t\t\treturn { id: step.id, title: this.stepTitle(step), value, done, notRun: false }\n\t\t\t\t})\n\t\t\t\t.concat(skipped)\n\t\t},\n\n\t\t/**\n\t\t * Id of the step the wizard should open on when RESUMING: the first\n\t\t * non-`info`/`summary` step not already done, per the CURRENT session's\n\t\t * local state OR the server-reported `completedStepIds` (a prior\n\t\t * session). Falls back to `''` (CnWizardDialog's own first-step default)\n\t\t * on a fresh setup and when every actionable step is already done.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tinitialStepId() {\n\t\t\t// Fresh setup — nothing done anywhere yet, so there is nothing to\n\t\t\t// resume past. Start at step one so a leading `info`/welcome step is\n\t\t\t// actually seen; resuming is only for returning sessions.\n\t\t\tif (this.completedStepIds.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\t// An on-demand step is never outstanding work, so it is never the\n\t\t\t// step to resume on.\n\t\t\tconst actionable = this.setupSteps.filter((s) => s.type !== 'info' && s.type !== 'summary' && !this.isOnDemand(s))\n\t\t\tconst firstUnmetIndex = actionable.findIndex((s) => {\n\t\t\t\tif (s.type === 'choice') {\n\t\t\t\t\treturn !(this.hasChoice(s) || this.isServerDone(s.id))\n\t\t\t\t}\n\t\t\t\treturn !this.isStepDone(s.id)\n\t\t\t})\n\t\t\t// Done is not the same as seen: an app that pre-satisfies a step at\n\t\t\t// install time (buildiq seeds templates from a repair step) reports it\n\t\t\t// done before anyone opens the wizard. Only finished work BEFORE the\n\t\t\t// outstanding step means there is something to resume past.\n\t\t\tif (firstUnmetIndex <= 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn actionable[firstUnmetIndex].id\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// Covers a RESUMED wizard opening straight onto its run-action step\n\t\t// (`initialStepId`) — `onStepChange` only fires on later navigation,\n\t\t// so a step already active at mount time needs its own trigger.\n\t\tthis.maybeAutoRunStep(this.initialStepId || (this.setupSteps[0] && this.setupSteps[0].id))\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Start a `run-action` step's action automatically the moment it\n\t\t * becomes current, instead of waiting for a manual \"Run\" click —\n\t\t * the step has nothing else for the user to decide by then (the\n\t\t * choice it acts on was already made on an earlier step), so\n\t\t * requiring a click before showing progress just adds a stall.\n\t\t * No-ops for any other step type, or one already run/running, and\n\t\t * never fires an on-demand step (`onDemand: true`): those run only\n\t\t * when the user clicks Run.\n\t\t *\n\t\t * @param {string} stepId The step id becoming current.\n\t\t * @return {void}\n\t\t */\n\t\tmaybeAutoRunStep(stepId) {\n\t\t\t// Nothing runs while a required app is missing: every action\n\t\t\t// would fail with an error that does not name the app.\n\t\t\tif (this.dependencyBlocked) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst step = this.setupSteps.find((s) => s.id === stepId)\n\t\t\tif (step && step.type === 'run-action' && !this.isOnDemand(step) && !this.isStepDone(step.id) && !this.running[step.id]) {\n\t\t\t\tthis.runAction(step)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The visible title for a step, resolved through the host app's\n\t\t * translation function.\n\t\t *\n\t\t * `manifest.setup.steps[].title` is authored in English as the\n\t\t * canonical source, exactly like the schema property titles this\n\t\t * component already routes through `cnTranslate` for field labels\n\t\t * (see `fieldsFromSchema` below). The step titles were the one\n\t\t * remaining place rendering the manifest string verbatim, so an\n\t\t * nl-locale user saw a wizard whose field labels were translated and\n\t\t * whose step headings were not.\n\t\t *\n\t\t * Falls back to the step id when a step declares no title, which is\n\t\t * the same fallback the raw bindings used.\n\t\t *\n\t\t * @param {object} step A `manifest.setup.steps[]` entry.\n\t\t * @return {string} The translated title, or the step id.\n\t\t */\n\t\tstepTitle(step) {\n\t\t\tconst raw = (step && step.title) || (step && step.id) || ''\n\t\t\treturn raw === '' ? '' : this.cnTranslate(raw)\n\t\t},\n\n\t\t/**\n\t\t * The visible body for a step, resolved through the host app's\n\t\t * translation function.\n\t\t *\n\t\t * 🔴 THIS USED TO RENDER THE MANIFEST STRING VERBATIM, IN FOUR PLACES.\n\t\t * `stepTitle()` was routed through `cnTranslate` and the body was not,\n\t\t * so a wizard on a Dutch instance showed a translated heading over an\n\t\t * English paragraph, with translated Cancel/Next buttons underneath.\n\t\t * That reads as a bug rather than as a missing string, and the app could\n\t\t * not fix it: decidiq shipped correct Dutch for its setup copy in\n\t\t * `l10n/nl.json`, and the component never asked for it.\n\t\t *\n\t\t * @param {object} step The step declaration.\n\t\t * @return {string} The translated body, or ''.\n\t\t */\n\t\tstepBody(step) {\n\t\t\tconst raw = (step && step.body) || ''\n\t\t\treturn raw === '' ? '' : this.cnTranslate(raw)\n\t\t},\n\n\t\tstepSlot(step) {\n\t\t\treturn 'step-' + step.id\n\t\t},\n\n\t\thasCustomSlot(id) {\n\t\t\treturn !!this.$slots['step-' + id] || !!this.$slots['step-' + id]\n\t\t},\n\n\t\tfieldsFor(step) {\n\t\t\tif (step.schema && typeof step.schema === 'object') {\n\t\t\t\treturn fieldsFromSchema(step.schema, { translate: this.cnTranslate })\n\t\t\t}\n\t\t\t// Fallback: a plain text field per declared config key.\n\t\t\treturn (step.configKeys || []).map((key) => ({ key, label: key, widget: 'text' }))\n\t\t},\n\n\t\t/**\n\t\t * Options for a `choice` step. When the step declares `dependsOn`\n\t\t * (a parent step's configKey) + `optionsByParent`, the option list\n\t\t * is the entry keyed by the parent's currently-selected value — this\n\t\t * is the country → organisation-type cascade. Otherwise the static\n\t\t * `options` array.\n\t\t *\n\t\t * @param {object} step The choice step definition.\n\t\t * @return {Array} The option list to render.\n\t\t */\n\t\toptionsFor(step) {\n\t\t\tlet options = step.options || []\n\t\t\tif (step.optionsSource) {\n\t\t\t\toptions = this.sourcedOptions(step)\n\t\t\t} else if (step.dependsOn && step.optionsByParent) {\n\t\t\t\tconst parentValue = this.choiceValues[step.dependsOn]\n\t\t\t\toptions = (parentValue === null || parentValue === undefined || parentValue === '')\n\t\t\t\t\t? []\n\t\t\t\t\t: (step.optionsByParent[parentValue] || [])\n\t\t\t}\n\t\t\t// The LABEL is translated, the VALUE never is. `scalarChoice()` reads\n\t\t\t// `.value` and that is what reaches `POST /api/setup/config`, so a\n\t\t\t// translated label cannot change what gets stored — while an\n\t\t\t// untranslated one leaves the choice itself in English on a\n\t\t\t// localised instance, which is the half-translated screen this fix\n\t\t\t// exists to remove.\n\t\t\treturn options.map((option) => {\n\t\t\t\tif (!option || typeof option !== 'object' || !option.label) {\n\t\t\t\t\treturn option\n\t\t\t\t}\n\t\t\t\tconst translated = { ...option, label: this.cnTranslate(option.label) }\n\t\t\t\tif (option.description) {\n\t\t\t\t\ttranslated.description = this.cnTranslate(option.description)\n\t\t\t\t}\n\t\t\t\treturn translated\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The option list a `choice` step declares `optionsSource` for, read\n\t\t * from the setup status document the app already serves.\n\t\t *\n\t\t * `optionsSource` names a key in that document (dots walk into nested\n\t\t * objects), and the entries are normalised to the option shape: `id` is\n\t\t * accepted for `value`, `name` for `label`, and a bare `objectCount`\n\t\t * becomes the one stat a card shows. A server that already sends\n\t\t * `stats` keeps them.\n\t\t *\n\t\t * @param {object} step The choice step declaring `optionsSource`.\n\t\t * @return {Array<object>} The options, or [] while the status is loading.\n\t\t */\n\t\tsourcedOptions(step) {\n\t\t\tlet node = this.setupStatus\n\t\t\tfor (const key of String(step.optionsSource).split('.')) {\n\t\t\t\tif (node === null || node === undefined || typeof node !== 'object') {\n\t\t\t\t\treturn []\n\t\t\t\t}\n\t\t\t\tnode = node[key]\n\t\t\t}\n\t\t\tif (!Array.isArray(node)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn node\n\t\t\t\t.filter((entry) => entry && typeof entry === 'object')\n\t\t\t\t.map((entry) => {\n\t\t\t\t\tconst value = entry.value !== undefined ? entry.value : entry.id\n\t\t\t\t\tconst option = {\n\t\t\t\t\t\t...entry,\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tlabel: entry.label || entry.name || String(value),\n\t\t\t\t\t}\n\t\t\t\t\tif (!option.stats && typeof entry.objectCount === 'number' && entry.objectCount > 0) {\n\t\t\t\t\t\toption.stats = [{ label: t('nextcloud-vue', 'Objects'), value: entry.objectCount }]\n\t\t\t\t\t}\n\t\t\t\t\treturn option\n\t\t\t\t})\n\t\t\t\t.filter((option) => option.value !== undefined)\n\t\t},\n\n\t\t/**\n\t\t * Whether a choice step renders as a card grid rather than a dropdown.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @return {boolean} True for `display: \"cards\"`.\n\t\t */\n\t\tisCardChoice(step) {\n\t\t\treturn step.display === 'cards'\n\t\t},\n\n\t\t/**\n\t\t * Whether this step is still waiting for its live option list.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @return {boolean} True while the status document is in flight.\n\t\t */\n\t\tisOptionsLoading(step) {\n\t\t\treturn !!step.optionsSource && this.setupStatusLoading === true\n\t\t},\n\n\t\t/**\n\t\t * The card grid's model: plain values, not the option objects NcSelect\n\t\t * binds. `scalarChoice` already reduces either shape to values, so both\n\t\t * renderers can share one `choiceModel` entry.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @return {unknown} The selected value, or [] / null when nothing is picked.\n\t\t */\n\t\tcardModel(step) {\n\t\t\tconst value = this.scalarChoice(step)\n\t\t\tif (step.multiple === true) {\n\t\t\t\treturn Array.isArray(value) ? value : []\n\t\t\t}\n\t\t\treturn value === undefined ? null : value\n\t\t},\n\n\t\tisChoiceDisabled(step) {\n\t\t\tif (!step.dependsOn) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst parentValue = this.choiceValues[step.dependsOn]\n\t\t\treturn parentValue === null || parentValue === undefined || parentValue === ''\n\t\t},\n\n\t\tdependsOnHint(step) {\n\t\t\tconst parent = this.setupSteps.find((s) => s.configKey === step.dependsOn)\n\t\t\tconst label = parent ? (parent.title || parent.id) : step.dependsOn\n\t\t\treturn t('nextcloud-vue', 'Select \"{step}\" first.', { step: label })\n\t\t},\n\n\t\t/**\n\t\t * The visible label for one selected choice entry.\n\t\t *\n\t\t * A card grid stores plain VALUES where NcSelect stores option objects,\n\t\t * so the recap looked one up and printed the other: the summary read\n\t\t * \"municipality\" instead of \"Municipality\". Resolves a bare value back\n\t\t * to its option, and falls back to the value when the option list is\n\t\t * not (yet) available.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @param {unknown} entry One selected entry: an option object or a value.\n\t\t * @return {string} The label to show.\n\t\t */\n\t\tchoiceLabel(step, entry) {\n\t\t\tif (entry && typeof entry === 'object' && entry.label) {\n\t\t\t\treturn entry.label\n\t\t\t}\n\t\t\tconst match = this.optionsFor(step).find((o) => o && String(o.value) === String(entry))\n\t\t\treturn (match && match.label) ? match.label : String(entry)\n\t\t},\n\n\t\thasChoice(step) {\n\t\t\tconst v = this.choiceModel[step.id]\n\t\t\tif (step.multiple === true) {\n\t\t\t\treturn Array.isArray(v) && v.length > 0\n\t\t\t}\n\t\t\treturn v !== null && v !== undefined && v !== ''\n\t\t},\n\n\t\tscalarChoice(step) {\n\t\t\tconst raw = this.choiceModel[step.id]\n\t\t\tif (step.multiple === true) {\n\t\t\t\treturn (raw || []).map((o) => (o && o.value !== undefined ? o.value : o))\n\t\t\t}\n\t\t\treturn raw && raw.value !== undefined ? raw.value : raw\n\t\t},\n\n\t\tonChoice(step, value) {\n\t\t\tthis.userTouched[step.id] = true\n\t\t\tthis.choiceModel[step.id] = value\n\t\t\tif (step.configKey) {\n\t\t\t\tthis.choiceValues[step.configKey] = this.scalarChoice(step)\n\t\t\t}\n\t\t\t// Reset any dependent child choices when the parent changes.\n\t\t\tfor (const child of this.setupSteps) {\n\t\t\t\tif (child.dependsOn && child.dependsOn === step.configKey) {\n\t\t\t\t\tthis.choiceModel[child.id] = child.multiple === true ? [] : null\n\t\t\t\t\tthis.userTouched[child.id] = false\n\t\t\t\t\tif (child.configKey) {\n\t\t\t\t\t\tthis.choiceValues[child.configKey] = ''\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-apply auto-suggestions for steps that derive a default from this one.\n\t\t\tfor (const dep of this.setupSteps) {\n\t\t\t\tif (dep.suggestFrom && dep.suggestFrom === step.configKey) {\n\t\t\t\t\tthis.applySuggestion(dep)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Auto-fill a `choice` step's suggested default from an earlier choice\n\t\t * (its `suggestFrom` configKey → `suggestMap[parentValue]`). No-op once\n\t\t * the user has manually picked here, so a suggestion never clobbers an\n\t\t * explicit choice.\n\t\t *\n\t\t * @param {object} step The dependent choice step.\n\t\t * @return {void}\n\t\t */\n\t\tapplySuggestion(step) {\n\t\t\tif (this.userTouched[step.id] === true) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst parentValue = this.choiceValues[step.suggestFrom]\n\t\t\tif (parentValue === null || parentValue === undefined || parentValue === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst wanted = (step.suggestMap || {})[parentValue]\n\t\t\tif (wanted === null || wanted === undefined) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst opt = this.optionsFor(step).find((o) => o.value === wanted) || { value: wanted, label: String(wanted) }\n\t\t\tthis.choiceModel[step.id] = opt\n\t\t\tif (step.configKey) {\n\t\t\t\tthis.choiceValues[step.configKey] = wanted\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * CnWizardDialog `validate` hook — intercepts Next/Submit to persist\n\t\t * the active step before advancing. `choice`/`config-fields` steps\n\t\t * POST their value via the setup contract; required `choice` steps\n\t\t * block advance until a value is picked; required `run-action` steps\n\t\t * block until the action has succeeded. Returns `true` to advance or\n\t\t * an error string to surface above the step and block navigation.\n\t\t *\n\t\t * @param {string} stepId The active step id.\n\t\t * @return {Promise<boolean|string>} True to advance, or an error message.\n\t\t */\n\t\tasync validateStep(stepId) {\n\t\t\tconst step = this.setupSteps.find((s) => s.id === stepId)\n\t\t\tif (!step) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif (step.type === 'choice') {\n\t\t\t\tif (!this.hasChoice(step)) {\n\t\t\t\t\t// Already persisted server-side and the user only back-navigated\n\t\t\t\t\t// onto it — `choiceModel` is blank because it's session-local, not\n\t\t\t\t\t// because nothing is set. Don't force a re-pick, and don't re-POST.\n\t\t\t\t\tif (this.isServerDone(step.id)) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\treturn step.required === true\n\t\t\t\t\t\t? t('nextcloud-vue', 'Please make a selection to continue.')\n\t\t\t\t\t\t: true\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait this.saveConfig({ [step.configKey]: this.scalarChoice(step) })\n\t\t\t\t\tthis.localDone[step.id] = true\n\t\t\t\t\treturn true\n\t\t\t\t} catch (err) {\n\t\t\t\t\treturn this.errorMessage(err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.type === 'config-fields') {\n\t\t\t\ttry {\n\t\t\t\t\tconst patch = {}\n\t\t\t\t\tfor (const field of this.fieldsFor(step)) {\n\t\t\t\t\t\tpatch[field.key] = this.configModel[field.key]\n\t\t\t\t\t}\n\t\t\t\t\tawait this.saveConfig(patch)\n\t\t\t\t\tthis.localDone[step.id] = true\n\t\t\t\t\treturn true\n\t\t\t\t} catch (err) {\n\t\t\t\t\treturn this.errorMessage(err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.type === 'run-action' && step.required === true && !this.isStepDone(step.id)) {\n\t\t\t\treturn t('nextcloud-vue', 'Please run this step before continuing.')\n\t\t\t}\n\t\t\treturn true\n\t\t},\n\n\t\t/**\n\t\t * Persist one or more app-config values via the setup contract.\n\t\t *\n\t\t * @param {object} patch `{ <configKey>: value }` to write.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync saveConfig(patch) {\n\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\timport('@nextcloud/router'),\n\t\t\t])\n\t\t\tawait axios.post(generateUrl(`/apps/${this.appId}/api/setup/config`), patch)\n\t\t},\n\n\t\tasync runAction(step) {\n\t\t\tthis.running[step.id] = true\n\t\t\tthis.actionResult[step.id] = null\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 { data } = await axios.post(generateUrl(`/apps/${this.appId}/api/setup/action/${step.action}`))\n\t\t\t\tconst 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\tthis.actionResult[step.id] = result\n\t\t\t\tif (result.success) {\n\t\t\t\t\tthis.localDone[step.id] = true\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event action-result Emitted when a `run-action` step finishes.\n\t\t\t\t * @type {{ stepId: string, action: string, success: boolean, message: string }}\n\t\t\t\t */\n\t\t\t\tthis.$emit('action-result', { stepId: step.id, action: step.action, ...result })\n\t\t\t} catch (err) {\n\t\t\t\tthis.actionResult[step.id] = { success: false, message: this.errorMessage(err) }\n\t\t\t\tthis.$emit('action-result', { stepId: step.id, action: step.action, success: false, message: this.errorMessage(err) })\n\t\t\t} finally {\n\t\t\t\tthis.running[step.id] = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Key for one card's load state.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @param {object} option The card's option.\n\t\t * @return {string}\n\t\t */\n\t\tloadKey(step, option) {\n\t\t\treturn step.id + '::' + String(option && option.value)\n\t\t},\n\n\t\t/**\n\t\t * Whether a card gets a Load button: the step declares `loadAction`\n\t\t * and the option is not the \"seed nothing\" answer.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @param {object} option The card's option.\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-a-dataset-card-loads-itself\n\t\t */\n\t\tisLoadableOption(step, option) {\n\t\t\treturn !!(step && step.loadAction) && !!option && String(option.value) !== NONE_DATASET\n\t\t},\n\n\t\tisDatasetLoading(step, option) {\n\t\t\treturn this.datasetRunning[this.loadKey(step, option)] === true\n\t\t},\n\n\t\tdatasetResultFor(step, option) {\n\t\t\treturn this.datasetResult[this.loadKey(step, option)] || null\n\t\t},\n\n\t\tloadButtonLabel(step, option) {\n\t\t\tif (this.isDatasetLoading(step, option)) {\n\t\t\t\treturn this.runningLabel\n\t\t\t}\n\t\t\tconst result = this.datasetResultFor(step, option)\n\t\t\treturn (result && result.success) ? this.reloadLabel : this.loadLabel\n\t\t},\n\n\t\tloadAriaLabel(option) {\n\t\t\treturn t('nextcloud-vue', 'Load {name}', { name: option.label })\n\t\t},\n\n\t\t/**\n\t\t * Load one dataset from its card: POST `{ dataset }` to the step's\n\t\t * `loadAction`, spinner on the button while it runs, the result on the\n\t\t * card after. A successful load also selects the card, so the summary\n\t\t * and the stored choice name what was loaded.\n\t\t *\n\t\t * @param {object} step The choice step declaring `loadAction`.\n\t\t * @param {object} option The card's option.\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-a-dataset-card-loads-itself\n\t\t */\n\t\tasync loadDataset(step, option) {\n\t\t\tif (!this.isLoadableOption(step, option) || this.isDatasetLoading(step, option)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst key = this.loadKey(step, option)\n\t\t\tthis.datasetRunning[key] = true\n\t\t\tthis.datasetResult[key] = null\n\t\t\tlet result\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 { data } = await axios.post(\n\t\t\t\t\tgenerateUrl(`/apps/${this.appId}/api/setup/action/${step.loadAction}`),\n\t\t\t\t\t{ dataset: option.value },\n\t\t\t\t)\n\t\t\t\tresult = {\n\t\t\t\t\tsuccess: !!data && data.success !== false,\n\t\t\t\t\tmessage: (data && data.message) || t('nextcloud-vue', 'Loaded.'),\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tresult = { success: false, message: this.errorMessage(err) }\n\t\t\t} finally {\n\t\t\t\tthis.datasetRunning[key] = false\n\t\t\t}\n\t\t\tthis.datasetResult[key] = result\n\t\t\tif (result.success) {\n\t\t\t\tthis.selectLoadedOption(step, option)\n\t\t\t\tthis.localDone[step.id] = true\n\t\t\t}\n\t\t\tthis.$emit('action-result', {\n\t\t\t\tstepId: step.id,\n\t\t\t\taction: step.loadAction,\n\t\t\t\tdataset: option.value,\n\t\t\t\t...result,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Select a card that just loaded, adding to (never replacing) the\n\t\t * selection of a `multiple` step.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @param {object} option The loaded option.\n\t\t * @return {void}\n\t\t */\n\t\tselectLoadedOption(step, option) {\n\t\t\tif (step.multiple === true) {\n\t\t\t\tconst current = this.cardModel(step)\n\t\t\t\tif (current.some((v) => String(v) === String(option.value))) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.onChoice(step, current.concat([option.value]))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onChoice(step, option.value)\n\t\t},\n\n\t\t/**\n\t\t * A required app was installed from the dependency list. A new app's\n\t\t * scripts and its `OC.appswebroots` entry exist only after a full page\n\t\t * load, and `useAppStatus` caches per page, so reload.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonDependencyInstalled() {\n\t\t\twindow.location.reload()\n\t\t},\n\n\t\t/**\n\t\t * Whether a step is marked on-demand (`onDemand: true`): it runs only\n\t\t * when the user asks, never automatically, and the summary ticks it\n\t\t * only when it ran in this session.\n\t\t *\n\t\t * @param {object} step The manifest step.\n\t\t * @return {boolean}\n\t\t */\n\t\tisOnDemand(step) {\n\t\t\treturn !!step && step.onDemand === true\n\t\t},\n\n\t\t/**\n\t\t * Whether a step's action succeeded in the CURRENT session, ignoring\n\t\t * whatever the server reports.\n\t\t *\n\t\t * @param {string} id Step id.\n\t\t * @return {boolean}\n\t\t */\n\t\tranThisSession(id) {\n\t\t\treturn !!(this.actionResult[id] && this.actionResult[id].success)\n\t\t},\n\n\t\tisStepDone(id) {\n\t\t\treturn this.isServerDone(id) || this.localDone[id] === true || (this.actionResult[id] && this.actionResult[id].success)\n\t\t},\n\n\t\t/**\n\t\t * Whether the server already reported this step done in a prior\n\t\t * session, via the `completedStepIds` prop.\n\t\t *\n\t\t * @param {string} id Step id.\n\t\t * @return {boolean}\n\t\t */\n\t\tisServerDone(id) {\n\t\t\treturn this.completedStepIds.includes(id)\n\t\t},\n\n\t\tonSubmit() {\n\t\t\tif (this.$refs.wizard && this.$refs.wizard.setResult) {\n\t\t\t\tthis.$refs.wizard.setResult({ success: true, message: this.successText })\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event complete Emitted when the last step is submitted (setup finished).\n\t\t\t */\n\t\t\tthis.$emit('complete')\n\t\t},\n\n\t\tonStepChange(payload) {\n\t\t\t// Pre-fill a suggested default when entering a step that derives one.\n\t\t\tconst step = this.setupSteps.find((s) => s.id === payload.stepId)\n\t\t\tif (step && step.suggestFrom) {\n\t\t\t\tthis.applySuggestion(step)\n\t\t\t}\n\t\t\tthis.maybeAutoRunStep(payload.stepId)\n\t\t\t/**\n\t\t\t * @event step-change Emitted when the active step changes.\n\t\t\t * @type {{ stepId: string, stepIndex: number, direction: string }}\n\t\t\t */\n\t\t\tthis.$emit('step-change', payload)\n\t\t},\n\n\t\tonClose() {\n\t\t\t/**\n\t\t\t * @event close Emitted when the dialog should close.\n\t\t\t */\n\t\t\tthis.$emit('close')\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-setup-step {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-setup-step__nav {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tgap: 8px;\n\tmargin-top: 8px;\n}\n\n.cn-setup-step__running {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\tpadding: 24px 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-setup-load-result {\n\tmargin: 0;\n\tfont-size: var(--font-size-small, 13px);\n}\n\n.cn-setup-load-result--success {\n\tcolor: var(--color-success-text, var(--color-success));\n}\n\n.cn-setup-load-result--error {\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-setup-field {\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-setup-summary {\n\tlist-style: none;\n\tpadding: 0;\n\tmargin: 0;\n}\n\n.cn-setup-summary__item {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 4px 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-setup-summary__item--done {\n\tcolor: var(--color-main-text);\n}\n\n.cn-setup-summary__mark {\n\tfont-weight: bold;\n\tcolor: var(--color-success);\n}\n\n.cn-setup-summary__label {\n\tflex: 0 0 auto;\n}\n\n.cn-setup-summary__value {\n\tmargin-inline-start: auto;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n/* An on-demand step that did not run says so quietly: it is a choice the\n user did not make, not a failure. */\n.cn-setup-summary__item--not-run .cn-setup-summary__value {\n\tfont-weight: normal;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["_createBlock","_createVNode","_renderList","_withCtx","_createElementBlock","_renderSlot","_mergeProps","_createTextVNode","_toDisplayString","_Fragment","_normalizeClass","_openBlock","_createElementVNode"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnSetupWizard.vue3.js","sources":["../../../../src/components/CnSetupWizard/CnSetupWizard.vue?vue&type=template&id=75b2078b&lang.js"],"sourcesContent":["<!-- SPDX-License-Identifier: EUPL-1.2 -->\n<!-- SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl> -->\n<template>\n\t<CnWizardDialog\n\t\tref=\"wizard\"\n\t\t:steps=\"wizardSteps\"\n\t\t:dialogTitle=\"resolvedDialogTitle\"\n\t\t:submitLabel=\"submitLabel\"\n\t\t:cancelLabel=\"cancelLabel\"\n\t\t:nextLabel=\"nextLabel\"\n\t\t:backLabel=\"backLabel\"\n\t\t:successText=\"successText\"\n\t\t:validate=\"validateStep\"\n\t\t:cancellable=\"cancellable\"\n\t\t:initialStep=\"dependencyBlocked ? '' : initialStepId\"\n\t\t:nextDisabled=\"dependencyBlocked\"\n\t\t@stepChange=\"onStepChange\"\n\t\t@submit=\"onSubmit\"\n\t\t@close=\"onClose\">\n\t\t<!-- This `<template v-for>` defines dynamic SLOTS, not a rendered list.\n\t\t `@vue/compiler-sfc` DISCARDS a `:key` on a slot-defining\n\t\t `<template>` (verified against the generated `createSlots` call)\n\t\t and honours it only on the child, so the rule's advice is inverted\n\t\t here — moving the key up would throw it away. -->\n\t\t<!-- A required app is missing: this is the only step, and Next stays\n\t\t disabled until the app is there. -->\n\t\t<template v-if=\"dependencyBlocked\" #[dependencyStepSlot]>\n\t\t\t<div class=\"cn-setup-step\" data-step-type=\"dependencies\" data-testid=\"cn-setup-dependencies\">\n\t\t\t\t<NcNoteCard type=\"warning\">\n\t\t\t\t\t{{ dependencyBlockedText }}\n\t\t\t\t</NcNoteCard>\n\t\t\t\t<CnLeafDependencySettings\n\t\t\t\t\t:appId=\"appId\"\n\t\t\t\t\t:dependencies=\"dependencyEntries\"\n\t\t\t\t\t:isAdmin=\"isAdmin\"\n\t\t\t\t\t:sectionName=\"dependencySectionName\"\n\t\t\t\t\tsectionDescription=\"\"\n\t\t\t\t\t@installed=\"onDependencyInstalled\" />\n\t\t\t</div>\n\t\t</template>\n\t\t<!-- eslint-disable vue/no-v-for-template-key-on-child -->\n\t\t<template v-for=\"step in setupSteps\" #[stepSlot(step)]=\"scope\">\n\t\t\t<div :key=\"step.id\" class=\"cn-setup-step\" :data-step-type=\"step.type\">\n\t\t\t\t<!-- Optional apps that are missing never block, but the admin\n\t\t\t\t reads about them once, on the first step. -->\n\t\t\t\t<NcNoteCard\n\t\t\t\t\tv-if=\"step.id === firstStepId && missingOptional.length > 0\"\n\t\t\t\t\ttype=\"info\"\n\t\t\t\t\tdata-testid=\"cn-setup-optional-dependencies\">\n\t\t\t\t\t{{ optionalMissingText }}\n\t\t\t\t</NcNoteCard>\n\t\t\t\t<!-- @slot step-{id} Override a step's body (for `component` steps or\n\t\t\t\t any bespoke step). Scope: the CnWizardDialog step scope plus\n\t\t\t\t `{ step, runAction, saveConfig }`. -->\n\t\t\t\t<slot\n\t\t\t\t\tv-if=\"hasCustomSlot(step.id)\"\n\t\t\t\t\t:name=\"stepSlot(step)\"\n\t\t\t\t\tv-bind=\"scope\"\n\t\t\t\t\t:step=\"step\"\n\t\t\t\t\t:runAction=\"() => runAction(step)\"\n\t\t\t\t\t:saveConfig=\"(patch) => saveConfig(patch)\" />\n\n\t\t\t\t<!-- info -->\n\t\t\t\t<template v-else-if=\"step.type === 'info'\">\n\t\t\t\t\t<NcNoteCard type=\"info\" :heading=\"stepTitle(step)\">\n\t\t\t\t\t\t{{ stepBody(step) }}\n\t\t\t\t\t</NcNoteCard>\n\t\t\t\t</template>\n\n\t\t\t\t<!-- choice -->\n\t\t\t\t<template v-else-if=\"step.type === 'choice'\">\n\t\t\t\t\t<NcNoteCard v-if=\"step.body\" type=\"info\">\n\t\t\t\t\t\t{{ stepBody(step) }}\n\t\t\t\t\t</NcNoteCard>\n\t\t\t\t\t<CnChoiceCards\n\t\t\t\t\t\tv-if=\"isCardChoice(step)\"\n\t\t\t\t\t\t:label=\"stepTitle(step)\"\n\t\t\t\t\t\t:options=\"optionsFor(step)\"\n\t\t\t\t\t\t:multiple=\"step.multiple === true\"\n\t\t\t\t\t\t:disabled=\"isChoiceDisabled(step)\"\n\t\t\t\t\t\t:loading=\"isOptionsLoading(step)\"\n\t\t\t\t\t\t:modelValue=\"cardModel(step)\"\n\t\t\t\t\t\t@update:modelValue=\"(v) => onChoice(step, v)\">\n\t\t\t\t\t\t<!-- `loadAction`: every dataset card except \"none\" loads\n\t\t\t\t\t\t itself, so no separate run-action step is needed. -->\n\t\t\t\t\t\t<template v-if=\"step.loadAction\" #option-actions=\"{ option }\">\n\t\t\t\t\t\t\t<template v-if=\"isLoadableOption(step, option)\">\n\t\t\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\t\t\t:disabled=\"isDatasetLoading(step, option)\"\n\t\t\t\t\t\t\t\t\t:aria-label=\"loadAriaLabel(option)\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-setup-load-dataset\"\n\t\t\t\t\t\t\t\t\t@click=\"loadDataset(step, option)\">\n\t\t\t\t\t\t\t\t\t<template v-if=\"isDatasetLoading(step, option)\" #icon>\n\t\t\t\t\t\t\t\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t\t{{ loadButtonLabel(step, option) }}\n\t\t\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\tv-if=\"datasetResultFor(step, option)\"\n\t\t\t\t\t\t\t\t\trole=\"status\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-setup-load-result\"\n\t\t\t\t\t\t\t\t\t:class=\"datasetResultFor(step, option).success ? 'cn-setup-load-result--success' : 'cn-setup-load-result--error'\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-setup-load-result\">\n\t\t\t\t\t\t\t\t\t{{ datasetResultFor(step, option).message }}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</CnChoiceCards>\n\t\t\t\t\t<NcSelect\n\t\t\t\t\t\tv-else\n\t\t\t\t\t\t:inputLabel=\"stepTitle(step)\"\n\t\t\t\t\t\t:options=\"optionsFor(step)\"\n\t\t\t\t\t\t:multiple=\"step.multiple === true\"\n\t\t\t\t\t\t:disabled=\"isChoiceDisabled(step)\"\n\t\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t\t:modelValue=\"choiceModel[step.id]\"\n\t\t\t\t\t\t@update:modelValue=\"(v) => onChoice(step, v)\" />\n\t\t\t\t\t<NcNoteCard v-if=\"isChoiceDisabled(step)\" type=\"warning\">\n\t\t\t\t\t\t{{ dependsOnHint(step) }}\n\t\t\t\t\t</NcNoteCard>\n\t\t\t\t</template>\n\n\t\t\t\t<!-- config-fields -->\n\t\t\t\t<template v-else-if=\"step.type === 'config-fields'\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-for=\"field in fieldsFor(step)\"\n\t\t\t\t\t\t:key=\"field.key\"\n\t\t\t\t\t\tclass=\"cn-setup-field\">\n\t\t\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\t\t\tv-if=\"field.widget === 'checkbox'\"\n\t\t\t\t\t\t\t:modelValue=\"!!configModel[field.key]\"\n\t\t\t\t\t\t\t@update:modelValue=\"(v) => configModel[field.key] = v\">\n\t\t\t\t\t\t\t{{ field.label }}\n\t\t\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t\tv-else-if=\"field.widget === 'select'\"\n\t\t\t\t\t\t\t:inputLabel=\"field.label\"\n\t\t\t\t\t\t\t:options=\"field.enum || []\"\n\t\t\t\t\t\t\t:modelValue=\"configModel[field.key]\"\n\t\t\t\t\t\t\t@update:modelValue=\"(v) => configModel[field.key] = v\" />\n\t\t\t\t\t\t<NcTextField\n\t\t\t\t\t\t\tv-else\n\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t:type=\"field.widget === 'number' ? 'number' : 'text'\"\n\t\t\t\t\t\t\t:modelValue=\"configModel[field.key] != null ? String(configModel[field.key]) : ''\"\n\t\t\t\t\t\t\t@update:modelValue=\"(v) => configModel[field.key] = v\" />\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\n\t\t\t\t<!-- run-action -->\n\t\t\t\t<template v-else-if=\"step.type === 'run-action'\">\n\t\t\t\t\t<NcNoteCard v-if=\"step.body\" type=\"info\">\n\t\t\t\t\t\t{{ stepBody(step) }}\n\t\t\t\t\t</NcNoteCard>\n\t\t\t\t\t<!-- The action starts automatically on entering this step\n\t\t\t\t\t (see `maybeAutoRunStep`), so the default state is the\n\t\t\t\t\t spinner, not an idle button. -->\n\t\t\t\t\t<div v-if=\"running[step.id]\" class=\"cn-setup-step__running\">\n\t\t\t\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t\t\t\t<span>{{ runningLabel }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t<NcNoteCard\n\t\t\t\t\t\t\tv-if=\"actionResult[step.id]\"\n\t\t\t\t\t\t\t:type=\"actionResult[step.id].success ? 'success' : 'error'\">\n\t\t\t\t\t\t\t{{ actionResult[step.id].message }}\n\t\t\t\t\t\t</NcNoteCard>\n\t\t\t\t\t\t<div class=\"cn-setup-step__nav\">\n\t\t\t\t\t\t\t<NcButton :variant=\"actionResult[step.id] ? 'secondary' : 'primary'\" @click=\"runAction(step)\">\n\t\t\t\t\t\t\t\t{{ actionResult[step.id] ? rerunLabel : runLabel }}\n\t\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</template>\n\t\t\t\t</template>\n\n\t\t\t\t<!-- summary -->\n\t\t\t\t<template v-else-if=\"step.type === 'summary'\">\n\t\t\t\t\t<NcNoteCard v-if=\"step.body\" type=\"info\">\n\t\t\t\t\t\t{{ stepBody(step) }}\n\t\t\t\t\t</NcNoteCard>\n\t\t\t\t\t<ul class=\"cn-setup-summary\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"item in summaryItems\"\n\t\t\t\t\t\t\t:key=\"item.id\"\n\t\t\t\t\t\t\tclass=\"cn-setup-summary__item\"\n\t\t\t\t\t\t\t:class=\"{ 'cn-setup-summary__item--done': item.done, 'cn-setup-summary__item--not-run': item.notRun }\">\n\t\t\t\t\t\t\t<span class=\"cn-setup-summary__mark\">{{ item.done ? '✓' : '○' }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-setup-summary__label\">{{ item.title }}</span>\n\t\t\t\t\t\t\t<span v-if=\"item.value\" class=\"cn-setup-summary__value\">{{ item.value }}</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t</template>\n\t\t<!-- eslint-enable vue/no-v-for-template-key-on-child -->\n\t</CnWizardDialog>\n</template>\n\n<script>\nimport { getCurrentUser } from '@nextcloud/auth'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcLoadingIcon, NcNoteCard, NcSelect, NcTextField } from '@nextcloud/vue'\nimport CnChoiceCards from '../CnChoiceCards/CnChoiceCards.vue'\nimport CnLeafDependencySettings from '../CnLeafDependencySettings/CnLeafDependencySettings.vue'\nimport CnWizardDialog from '../CnWizardDialog/CnWizardDialog.vue'\nimport { checkDependencies, isDependencyResolved, missingRequiredApps, readServerAppStatuses } from '../../composables/useDependencyCheck.js'\nimport { useSetupStatus } from '../../composables/useSetupStatus.js'\nimport { fieldsFromSchema } from '../../utils/schema.js'\n\n/** Id of the synthetic step shown while a required app is missing. */\nconst DEPENDENCY_STEP_ID = 'cn-setup-dependencies'\n\n/** The dataset value that means \"seed nothing\"; it never gets a Load button. */\nconst NONE_DATASET = 'none'\n\n/**\n * CnSetupWizard — abstract, manifest-driven first-time setup wizard (ADR-042).\n *\n * Wraps {@link CnWizardDialog} and renders a `manifest.setup.steps[]` array by\n * each step's `type`: `info` (note), `config-fields` (fields from a JSON Schema\n * via `fieldsFromSchema`, the same field shapes the admin pages use), `choice`\n * (`NcSelect` persisting to an app-config key), `run-action` (POST a privileged\n * server action), `summary` (recap), and `component` (a parent-provided\n * `#step-<id>` slot). It NEVER writes OpenRegister objects from the browser —\n * all persistence goes through the per-app setup contract\n * (`POST /apps/{appId}/api/setup/config` and `/api/setup/action/{actionId}`).\n *\n * Mount it gated by `CnAppRoot`'s setup phase, or open it on demand from\n * `CnAdminSettingsShell`.\n *\n * ```vue\n * <CnSetupWizard\n * :app-id=\"'procest'\"\n * :steps=\"manifest.setup.steps\"\n * @complete=\"onSetupComplete\"\n * @close=\"showWizard = false\" />\n * ```\n */\nexport default {\n\tname: 'CnSetupWizard',\n\n\tcomponents: {\n\t\tCnWizardDialog,\n\t\tCnChoiceCards,\n\t\tCnLeafDependencySettings,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tNcSelect,\n\t\tNcTextField,\n\t\tNcCheckboxRadioSwitch,\n\t\tNcLoadingIcon,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Consumer translation function, provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). Step field\n\t\t * labels come from schema property titles, authored in English as the\n\t\t * canonical source; the visible label is resolved through this function\n\t\t * so it follows the user's language. Defaults to identity when used\n\t\t * standalone (no CnAppRoot ancestor).\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/**\n\t\t * The app manifest, provided by CnAppRoot. Read only for its\n\t\t * `dependencies` when the `dependencies` prop is empty.\n\t\t */\n\t\tcnManifest: { default: null },\n\t},\n\n\tprops: {\n\t\t/** The Nextcloud app id; used to build the `/apps/{appId}/api/setup/*` URLs. */\n\t\tappId: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** The `manifest.setup.steps` array to render. */\n\t\tsteps: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Dialog title. Overridden by `appName` when that prop is set. */\n\t\tdialogTitle: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Set up this app'),\n\t\t},\n\n\t\t/**\n\t\t * The app's human-readable display name. When set, the dialog title\n\t\t * becomes \"Set up {appName}\" instead of the generic `dialogTitle`\n\t\t * default — pass the same display name CnAppRoot already resolves\n\t\t * (`appDisplayName || manifest.name || appId`).\n\t\t */\n\t\tappName: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Final-step submit button label. */\n\t\tsubmitLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Finish'),\n\t\t},\n\n\t\t/** Cancel button label. */\n\t\tcancelLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Cancel'),\n\t\t},\n\n\t\t/** Next button label. */\n\t\tnextLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Next'),\n\t\t},\n\n\t\t/** Back button label. */\n\t\tbackLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Back'),\n\t\t},\n\n\t\t/** Run-action button label (shown before the first automatic run). */\n\t\trunLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Run'),\n\t\t},\n\n\t\t/** Run-action button label offered after a run already finished. */\n\t\trerunLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Run again'),\n\t\t},\n\n\t\t/** Label shown next to the spinner while a run-action step is running. */\n\t\trunningLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Loading…'),\n\t\t},\n\n\t\t/** Result-phase success text. */\n\t\tsuccessText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Setup complete.'),\n\t\t},\n\n\t\t/**\n\t\t * Whether the wizard can be dismissed before finishing (Cancel\n\t\t * button, ESC, backdrop click). Pass `false` when a REQUIRED step\n\t\t * is unmet and the host is gating its shell behind this wizard\n\t\t * (ADR-042) — an offered-but-non-functional Cancel would be\n\t\t * misleading. Optional-only setup stays cancellable (default).\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tcancellable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/**\n\t\t * Ids of steps the SERVER already reports as done (e.g.\n\t\t * `useSetupStatus(...).steps` filtered to `done === true`), passed by\n\t\t * the host so a freshly (re)mounted wizard doesn't re-ask something\n\t\t * already persisted in an earlier session — this component's own\n\t\t * `localDone`/`choiceModel` only track the CURRENT session and reset\n\t\t * blank on every mount. Drives both the initial step (`initialStepId`)\n\t\t * and the summary page's done markers.\n\t\t *\n\t\t * @type {Array<string>}\n\t\t */\n\t\tcompletedStepIds: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The app's dependencies, in the manifest's own shape (app-id strings\n\t\t * or `{ id, name?, required? }`). Falls back to the injected manifest's\n\t\t * `dependencies`. A missing REQUIRED one replaces every step with the\n\t\t * dependency list and disables Next; a missing optional one is listed\n\t\t * on the first step and does not block.\n\t\t *\n\t\t * @type {Array<string|object>}\n\t\t * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step\n\t\t */\n\t\tdependencies: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Load button label on a dataset card (`loadAction`). */\n\t\tloadLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Load'),\n\t\t},\n\n\t\t/** Load button label once that dataset loaded in this session. */\n\t\treloadLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Load again'),\n\t\t},\n\t},\n\n\temits: ['complete', 'close', 'step-change', 'action-result'],\n\n\t/**\n\t * Live option lists come from the app's own setup status document, so a\n\t * step that offers a choice of things the SERVER ships never has to\n\t * restate them in the manifest. `useSetupStatus` caches per app id, so the\n\t * host's own status fetch (CnAppRoot's setup gate) is the one that runs —\n\t * this reads the same refs rather than asking again.\n\t *\n\t * Only called when a step actually declares `optionsSource`: a wizard with\n\t * none must not start making requests it never made before.\n\t *\n\t * @param {object} props The component props.\n\t * @return {object} `setupStatus` / `setupStatusLoading` for the option resolver.\n\t */\n\tsetup(props) {\n\t\tconst wantsSource = (props.steps || []).some((s) => s && s.optionsSource)\n\t\tif (!wantsSource) {\n\t\t\treturn {}\n\t\t}\n\t\tconst { status, loading } = useSetupStatus(props.appId, { setup: { steps: props.steps } })\n\t\treturn { setupStatus: status, setupStatusLoading: loading }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tchoiceModel: {},\n\t\t\tchoiceValues: {},\n\t\t\tconfigModel: {},\n\t\t\trunning: {},\n\t\t\tactionResult: {},\n\t\t\tlocalDone: {},\n\t\t\tuserTouched: {},\n\t\t\t// Per-card load state for `loadAction` steps, keyed by `loadKey()`.\n\t\t\tdatasetRunning: {},\n\t\t\tdatasetResult: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The title actually rendered by CnWizardDialog: \"Set up {appName}\"\n\t\t * when a display name was passed, otherwise whatever `dialogTitle`\n\t\t * resolves to (a caller override, or its own generic default).\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedDialogTitle() {\n\t\t\treturn this.appName\n\t\t\t\t? t('nextcloud-vue', 'Set up {app}', { app: this.appName })\n\t\t\t\t: this.dialogTitle\n\t\t},\n\n\t\t/**\n\t\t * Every well-formed step, before `requires` is applied.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tdeclaredSteps() {\n\t\t\treturn (this.steps || []).filter((s) => s && s.id && s.type)\n\t\t},\n\n\t\t/**\n\t\t * Steps that declare `requires` on an app that is not installed and\n\t\t * enabled. They are not offered; the summary names them as skipped.\n\t\t *\n\t\t * @return {Array<{ step: object, missing: Array<string> }>}\n\t\t * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step\n\t\t * @spec openspec/changes/optional-step-requires/specs/cn-setup-wizard/spec.md#requirement-a-step-whose-required-apps-are-absent-is-not-applicable\n\t\t */\n\t\tskippedSteps() {\n\t\t\treturn this.declaredSteps\n\t\t\t\t.filter((s) => Array.isArray(s.requires) && s.requires.length > 0)\n\t\t\t\t.map((step) => {\n\t\t\t\t\tconst known = Object.fromEntries(this.dependencyRows.map((d) => [d.id, d]))\n\t\t\t\t\tconst missing = missingRequiredApps(step.requires, this.serverAppStatuses)\n\t\t\t\t\t\t.map((row) => (known[row.id] || row).name || row.id)\n\t\t\t\t\treturn { step, missing }\n\t\t\t\t})\n\t\t\t\t.filter((entry) => entry.missing.length > 0)\n\t\t},\n\n\t\tsetupSteps() {\n\t\t\tconst skipped = new Set(this.skippedSteps.map((entry) => entry.step.id))\n\t\t\treturn this.declaredSteps.filter((s) => !skipped.has(s.id))\n\t\t},\n\n\t\t/**\n\t\t * Id of the first offered step: where the optional-apps note shows.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tfirstStepId() {\n\t\t\treturn this.setupSteps.length > 0 ? this.setupSteps[0].id : ''\n\t\t},\n\n\t\t/**\n\t\t * The dependency declarations to check: the prop, else the manifest's.\n\t\t *\n\t\t * @return {Array<string|object>}\n\t\t */\n\t\tdependencyEntries() {\n\t\t\tif (Array.isArray(this.dependencies) && this.dependencies.length > 0) {\n\t\t\t\treturn this.dependencies\n\t\t\t}\n\t\t\tconst fromManifest = this.cnManifest && this.cnManifest.dependencies\n\t\t\treturn Array.isArray(fromManifest) ? fromManifest : []\n\t\t},\n\n\t\t/**\n\t\t * The `dependency_statuses` initial state an app may inject from PHP.\n\t\t * Same source CnAppRoot reads; `{}` when the app injects none.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tserverAppStatuses() {\n\t\t\treturn readServerAppStatuses(this.appId)\n\t\t},\n\n\t\t/**\n\t\t * The declared dependencies with their live status.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tdependencyRows() {\n\t\t\treturn checkDependencies(this.dependencyEntries, this.serverAppStatuses)\n\t\t},\n\n\t\t/** @return {Array<object>} Required dependencies that are missing or disabled. */\n\t\tmissingRequired() {\n\t\t\treturn this.dependencyRows.filter((d) => d.required && !isDependencyResolved(d))\n\t\t},\n\n\t\t/** @return {Array<object>} Optional dependencies that are missing or disabled. */\n\t\tmissingOptional() {\n\t\t\treturn this.dependencyRows.filter((d) => !d.required && !isDependencyResolved(d))\n\t\t},\n\n\t\t/**\n\t\t * Whether a required app is missing, so the wizard shows the\n\t\t * dependency list instead of its steps and refuses to continue.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step\n\t\t */\n\t\tdependencyBlocked() {\n\t\t\treturn this.missingRequired.length > 0\n\t\t},\n\n\t\tdependencyStepSlot() {\n\t\t\treturn 'step-' + DEPENDENCY_STEP_ID\n\t\t},\n\n\t\tdependencySectionName() {\n\t\t\treturn t('nextcloud-vue', 'Apps this app needs')\n\t\t},\n\n\t\tdependencyBlockedText() {\n\t\t\treturn t('nextcloud-vue', 'Setup can start once these apps are installed and enabled: {apps}.', {\n\t\t\t\tapps: this.missingRequired.map((d) => d.name).join(', '),\n\t\t\t})\n\t\t},\n\n\t\toptionalMissingText() {\n\t\t\treturn t('nextcloud-vue', 'Optional, not installed: {apps}. You can add them later.', {\n\t\t\t\tapps: this.missingOptional.map((d) => d.name).join(', '),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Whether the current user is a Nextcloud admin; only admins get the\n\t\t * install and enable buttons.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisAdmin() {\n\t\t\ttry {\n\t\t\t\tconst user = getCurrentUser()\n\t\t\t\treturn !!(user && user.isAdmin)\n\t\t\t} catch {\n\t\t\t\treturn false\n\t\t\t}\n\t\t},\n\n\t\twizardSteps() {\n\t\t\tif (this.dependencyBlocked) {\n\t\t\t\treturn [{\n\t\t\t\t\tid: DEPENDENCY_STEP_ID,\n\t\t\t\t\tlabel: t('nextcloud-vue', 'Required apps'),\n\t\t\t\t\toptional: false,\n\t\t\t\t}]\n\t\t\t}\n\t\t\treturn this.setupSteps.map((s) => ({\n\t\t\t\tid: s.id,\n\t\t\t\tlabel: this.stepTitle(s),\n\t\t\t\toptional: s.required !== true,\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Recap rows for the `summary` step — one per non-summary step, with\n\t\t * the value the user selected (choice label / config field values) and\n\t\t * a done marker (info steps always done; choice steps done when picked;\n\t\t * run-action steps done when the action succeeded).\n\t\t *\n\t\t * An on-demand step (`onDemand: true`) is the exception: it is done\n\t\t * only when its action succeeded in THIS session. Apps report such a\n\t\t * step as `done` on purpose so it never auto-runs (learniq's \"Remove\n\t\t * the example data\"), and reading that flag here ticked a removal\n\t\t * nobody ran. When it did not run it gets `notRun: true` and the value\n\t\t * \"Not run\" instead.\n\t\t *\n\t\t * @return {Array<{ id: string, title: string, value: string, done: boolean, notRun: boolean }>}\n\t\t */\n\t\tsummaryItems() {\n\t\t\tconst skipped = this.skippedSteps\n\t\t\t\t.filter((entry) => entry.step.type !== 'summary' && entry.step.type !== 'info')\n\t\t\t\t.map((entry) => ({\n\t\t\t\t\tid: entry.step.id,\n\t\t\t\t\ttitle: this.stepTitle(entry.step),\n\t\t\t\t\tvalue: t('nextcloud-vue', 'Skipped, needs {apps}', { apps: entry.missing.join(', ') }),\n\t\t\t\t\tdone: false,\n\t\t\t\t\tnotRun: true,\n\t\t\t\t}))\n\t\t\treturn this.setupSteps\n\t\t\t\t.filter((s) => s.type !== 'summary')\n\t\t\t\t.map((step) => {\n\t\t\t\t\tlet value = ''\n\t\t\t\t\tif (step.type === 'choice') {\n\t\t\t\t\t\tconst raw = this.choiceModel[step.id]\n\t\t\t\t\t\tif (Array.isArray(raw)) {\n\t\t\t\t\t\t\tvalue = raw.map((o) => this.choiceLabel(step, o)).join(', ')\n\t\t\t\t\t\t} else if (raw !== null && raw !== undefined && raw !== '') {\n\t\t\t\t\t\t\tvalue = this.choiceLabel(step, raw)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (step.type === 'config-fields') {\n\t\t\t\t\t\tvalue = this.fieldsFor(step)\n\t\t\t\t\t\t\t.map((f) => `${f.label}: ${this.configModel[f.key] !== null && this.configModel[f.key] !== undefined ? this.configModel[f.key] : ''}`)\n\t\t\t\t\t\t\t.join(', ')\n\t\t\t\t\t}\n\t\t\t\t\tif (this.isOnDemand(step)) {\n\t\t\t\t\t\tconst ran = this.ranThisSession(step.id)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tid: step.id,\n\t\t\t\t\t\t\ttitle: this.stepTitle(step),\n\t\t\t\t\t\t\tvalue: ran ? '' : t('nextcloud-vue', 'Not run'),\n\t\t\t\t\t\t\tdone: ran,\n\t\t\t\t\t\t\tnotRun: !ran,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tlet done\n\t\t\t\t\tif (step.type === 'info') {\n\t\t\t\t\t\tdone = true\n\t\t\t\t\t} else if (step.type === 'choice') {\n\t\t\t\t\t\tdone = this.hasChoice(step) || this.isServerDone(step.id)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdone = this.isStepDone(step.id)\n\t\t\t\t\t}\n\t\t\t\t\treturn { id: step.id, title: this.stepTitle(step), value, done, notRun: false }\n\t\t\t\t})\n\t\t\t\t.concat(skipped)\n\t\t},\n\n\t\t/**\n\t\t * Id of the step the wizard should open on when RESUMING: the first\n\t\t * non-`info`/`summary` step not already done, per the CURRENT session's\n\t\t * local state OR the server-reported `completedStepIds` (a prior\n\t\t * session). Falls back to `''` (CnWizardDialog's own first-step default)\n\t\t * on a fresh setup and when every actionable step is already done.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tinitialStepId() {\n\t\t\t// Fresh setup — nothing done anywhere yet, so there is nothing to\n\t\t\t// resume past. Start at step one so a leading `info`/welcome step is\n\t\t\t// actually seen; resuming is only for returning sessions.\n\t\t\tif (this.completedStepIds.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\t// An on-demand step is never outstanding work, so it is never the\n\t\t\t// step to resume on.\n\t\t\tconst actionable = this.setupSteps.filter((s) => s.type !== 'info' && s.type !== 'summary' && !this.isOnDemand(s))\n\t\t\tconst firstUnmetIndex = actionable.findIndex((s) => {\n\t\t\t\tif (s.type === 'choice') {\n\t\t\t\t\treturn !(this.hasChoice(s) || this.isServerDone(s.id))\n\t\t\t\t}\n\t\t\t\treturn !this.isStepDone(s.id)\n\t\t\t})\n\t\t\t// Done is not the same as seen: an app that pre-satisfies a step at\n\t\t\t// install time (buildiq seeds templates from a repair step) reports it\n\t\t\t// done before anyone opens the wizard. Only finished work BEFORE the\n\t\t\t// outstanding step means there is something to resume past.\n\t\t\tif (firstUnmetIndex <= 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn actionable[firstUnmetIndex].id\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// Covers a RESUMED wizard opening straight onto its run-action step\n\t\t// (`initialStepId`) — `onStepChange` only fires on later navigation,\n\t\t// so a step already active at mount time needs its own trigger.\n\t\tthis.maybeAutoRunStep(this.initialStepId || (this.setupSteps[0] && this.setupSteps[0].id))\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Start a `run-action` step's action automatically the moment it\n\t\t * becomes current, instead of waiting for a manual \"Run\" click —\n\t\t * the step has nothing else for the user to decide by then (the\n\t\t * choice it acts on was already made on an earlier step), so\n\t\t * requiring a click before showing progress just adds a stall.\n\t\t * No-ops for any other step type, or one already run/running, and\n\t\t * never fires an on-demand step (`onDemand: true`): those run only\n\t\t * when the user clicks Run.\n\t\t *\n\t\t * @param {string} stepId The step id becoming current.\n\t\t * @return {void}\n\t\t */\n\t\tmaybeAutoRunStep(stepId) {\n\t\t\t// Nothing runs while a required app is missing: every action\n\t\t\t// would fail with an error that does not name the app.\n\t\t\tif (this.dependencyBlocked) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst step = this.setupSteps.find((s) => s.id === stepId)\n\t\t\tif (step && step.type === 'run-action' && !this.isOnDemand(step) && !this.isStepDone(step.id) && !this.running[step.id]) {\n\t\t\t\tthis.runAction(step)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The visible title for a step, resolved through the host app's\n\t\t * translation function.\n\t\t *\n\t\t * `manifest.setup.steps[].title` is authored in English as the\n\t\t * canonical source, exactly like the schema property titles this\n\t\t * component already routes through `cnTranslate` for field labels\n\t\t * (see `fieldsFromSchema` below). The step titles were the one\n\t\t * remaining place rendering the manifest string verbatim, so an\n\t\t * nl-locale user saw a wizard whose field labels were translated and\n\t\t * whose step headings were not.\n\t\t *\n\t\t * Falls back to the step id when a step declares no title, which is\n\t\t * the same fallback the raw bindings used.\n\t\t *\n\t\t * @param {object} step A `manifest.setup.steps[]` entry.\n\t\t * @return {string} The translated title, or the step id.\n\t\t */\n\t\tstepTitle(step) {\n\t\t\tconst raw = (step && step.title) || (step && step.id) || ''\n\t\t\treturn raw === '' ? '' : this.cnTranslate(raw)\n\t\t},\n\n\t\t/**\n\t\t * The visible body for a step, resolved through the host app's\n\t\t * translation function.\n\t\t *\n\t\t * 🔴 THIS USED TO RENDER THE MANIFEST STRING VERBATIM, IN FOUR PLACES.\n\t\t * `stepTitle()` was routed through `cnTranslate` and the body was not,\n\t\t * so a wizard on a Dutch instance showed a translated heading over an\n\t\t * English paragraph, with translated Cancel/Next buttons underneath.\n\t\t * That reads as a bug rather than as a missing string, and the app could\n\t\t * not fix it: decidiq shipped correct Dutch for its setup copy in\n\t\t * `l10n/nl.json`, and the component never asked for it.\n\t\t *\n\t\t * @param {object} step The step declaration.\n\t\t * @return {string} The translated body, or ''.\n\t\t */\n\t\tstepBody(step) {\n\t\t\tconst raw = (step && step.body) || ''\n\t\t\treturn raw === '' ? '' : this.cnTranslate(raw)\n\t\t},\n\n\t\tstepSlot(step) {\n\t\t\treturn 'step-' + step.id\n\t\t},\n\n\t\thasCustomSlot(id) {\n\t\t\treturn !!this.$slots['step-' + id] || !!this.$slots['step-' + id]\n\t\t},\n\n\t\tfieldsFor(step) {\n\t\t\tif (step.schema && typeof step.schema === 'object') {\n\t\t\t\treturn fieldsFromSchema(step.schema, { translate: this.cnTranslate })\n\t\t\t}\n\t\t\t// Fallback: a plain text field per declared config key.\n\t\t\treturn (step.configKeys || []).map((key) => ({ key, label: key, widget: 'text' }))\n\t\t},\n\n\t\t/**\n\t\t * Options for a `choice` step. When the step declares `dependsOn`\n\t\t * (a parent step's configKey) + `optionsByParent`, the option list\n\t\t * is the entry keyed by the parent's currently-selected value — this\n\t\t * is the country → organisation-type cascade. Otherwise the static\n\t\t * `options` array.\n\t\t *\n\t\t * @param {object} step The choice step definition.\n\t\t * @return {Array} The option list to render.\n\t\t */\n\t\toptionsFor(step) {\n\t\t\tlet options = step.options || []\n\t\t\tif (step.optionsSource) {\n\t\t\t\toptions = this.sourcedOptions(step)\n\t\t\t} else if (step.dependsOn && step.optionsByParent) {\n\t\t\t\tconst parentValue = this.choiceValues[step.dependsOn]\n\t\t\t\toptions = (parentValue === null || parentValue === undefined || parentValue === '')\n\t\t\t\t\t? []\n\t\t\t\t\t: (step.optionsByParent[parentValue] || [])\n\t\t\t}\n\t\t\t// The LABEL is translated, the VALUE never is. `scalarChoice()` reads\n\t\t\t// `.value` and that is what reaches `POST /api/setup/config`, so a\n\t\t\t// translated label cannot change what gets stored — while an\n\t\t\t// untranslated one leaves the choice itself in English on a\n\t\t\t// localised instance, which is the half-translated screen this fix\n\t\t\t// exists to remove.\n\t\t\treturn options.map((option) => {\n\t\t\t\tif (!option || typeof option !== 'object' || !option.label) {\n\t\t\t\t\treturn option\n\t\t\t\t}\n\t\t\t\tconst translated = { ...option, label: this.cnTranslate(option.label) }\n\t\t\t\tif (option.description) {\n\t\t\t\t\ttranslated.description = this.cnTranslate(option.description)\n\t\t\t\t}\n\t\t\t\treturn translated\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The option list a `choice` step declares `optionsSource` for, read\n\t\t * from the setup status document the app already serves.\n\t\t *\n\t\t * `optionsSource` names a key in that document (dots walk into nested\n\t\t * objects), and the entries are normalised to the option shape: `id` is\n\t\t * accepted for `value`, `name` for `label`, and a bare `objectCount`\n\t\t * becomes the one stat a card shows. A server that already sends\n\t\t * `stats` keeps them.\n\t\t *\n\t\t * @param {object} step The choice step declaring `optionsSource`.\n\t\t * @return {Array<object>} The options, or [] while the status is loading.\n\t\t */\n\t\tsourcedOptions(step) {\n\t\t\tlet node = this.setupStatus\n\t\t\tfor (const key of String(step.optionsSource).split('.')) {\n\t\t\t\tif (node === null || node === undefined || typeof node !== 'object') {\n\t\t\t\t\treturn []\n\t\t\t\t}\n\t\t\t\tnode = node[key]\n\t\t\t}\n\t\t\tif (!Array.isArray(node)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn node\n\t\t\t\t.filter((entry) => entry && typeof entry === 'object')\n\t\t\t\t.map((entry) => {\n\t\t\t\t\tconst value = entry.value !== undefined ? entry.value : entry.id\n\t\t\t\t\tconst option = {\n\t\t\t\t\t\t...entry,\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tlabel: entry.label || entry.name || String(value),\n\t\t\t\t\t}\n\t\t\t\t\tif (!option.stats && typeof entry.objectCount === 'number' && entry.objectCount > 0) {\n\t\t\t\t\t\toption.stats = [{ label: t('nextcloud-vue', 'Objects'), value: entry.objectCount }]\n\t\t\t\t\t}\n\t\t\t\t\treturn option\n\t\t\t\t})\n\t\t\t\t.filter((option) => option.value !== undefined)\n\t\t},\n\n\t\t/**\n\t\t * Whether a choice step renders as a card grid rather than a dropdown.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @return {boolean} True for `display: \"cards\"`.\n\t\t */\n\t\tisCardChoice(step) {\n\t\t\treturn step.display === 'cards'\n\t\t},\n\n\t\t/**\n\t\t * Whether this step is still waiting for its live option list.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @return {boolean} True while the status document is in flight.\n\t\t */\n\t\tisOptionsLoading(step) {\n\t\t\treturn !!step.optionsSource && this.setupStatusLoading === true\n\t\t},\n\n\t\t/**\n\t\t * The card grid's model: plain values, not the option objects NcSelect\n\t\t * binds. `scalarChoice` already reduces either shape to values, so both\n\t\t * renderers can share one `choiceModel` entry.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @return {unknown} The selected value, or [] / null when nothing is picked.\n\t\t */\n\t\tcardModel(step) {\n\t\t\tconst value = this.scalarChoice(step)\n\t\t\tif (step.multiple === true) {\n\t\t\t\treturn Array.isArray(value) ? value : []\n\t\t\t}\n\t\t\treturn value === undefined ? null : value\n\t\t},\n\n\t\tisChoiceDisabled(step) {\n\t\t\tif (!step.dependsOn) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst parentValue = this.choiceValues[step.dependsOn]\n\t\t\treturn parentValue === null || parentValue === undefined || parentValue === ''\n\t\t},\n\n\t\tdependsOnHint(step) {\n\t\t\tconst parent = this.setupSteps.find((s) => s.configKey === step.dependsOn)\n\t\t\tconst label = parent ? (parent.title || parent.id) : step.dependsOn\n\t\t\treturn t('nextcloud-vue', 'Select \"{step}\" first.', { step: label })\n\t\t},\n\n\t\t/**\n\t\t * The visible label for one selected choice entry.\n\t\t *\n\t\t * A card grid stores plain VALUES where NcSelect stores option objects,\n\t\t * so the recap looked one up and printed the other: the summary read\n\t\t * \"municipality\" instead of \"Municipality\". Resolves a bare value back\n\t\t * to its option, and falls back to the value when the option list is\n\t\t * not (yet) available.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @param {unknown} entry One selected entry: an option object or a value.\n\t\t * @return {string} The label to show.\n\t\t */\n\t\tchoiceLabel(step, entry) {\n\t\t\tif (entry && typeof entry === 'object' && entry.label) {\n\t\t\t\treturn entry.label\n\t\t\t}\n\t\t\tconst match = this.optionsFor(step).find((o) => o && String(o.value) === String(entry))\n\t\t\treturn (match && match.label) ? match.label : String(entry)\n\t\t},\n\n\t\thasChoice(step) {\n\t\t\tconst v = this.choiceModel[step.id]\n\t\t\tif (step.multiple === true) {\n\t\t\t\treturn Array.isArray(v) && v.length > 0\n\t\t\t}\n\t\t\treturn v !== null && v !== undefined && v !== ''\n\t\t},\n\n\t\tscalarChoice(step) {\n\t\t\tconst raw = this.choiceModel[step.id]\n\t\t\tif (step.multiple === true) {\n\t\t\t\treturn (raw || []).map((o) => (o && o.value !== undefined ? o.value : o))\n\t\t\t}\n\t\t\treturn raw && raw.value !== undefined ? raw.value : raw\n\t\t},\n\n\t\tonChoice(step, value) {\n\t\t\tthis.userTouched[step.id] = true\n\t\t\tthis.choiceModel[step.id] = value\n\t\t\tif (step.configKey) {\n\t\t\t\tthis.choiceValues[step.configKey] = this.scalarChoice(step)\n\t\t\t}\n\t\t\t// Reset any dependent child choices when the parent changes.\n\t\t\tfor (const child of this.setupSteps) {\n\t\t\t\tif (child.dependsOn && child.dependsOn === step.configKey) {\n\t\t\t\t\tthis.choiceModel[child.id] = child.multiple === true ? [] : null\n\t\t\t\t\tthis.userTouched[child.id] = false\n\t\t\t\t\tif (child.configKey) {\n\t\t\t\t\t\tthis.choiceValues[child.configKey] = ''\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-apply auto-suggestions for steps that derive a default from this one.\n\t\t\tfor (const dep of this.setupSteps) {\n\t\t\t\tif (dep.suggestFrom && dep.suggestFrom === step.configKey) {\n\t\t\t\t\tthis.applySuggestion(dep)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Auto-fill a `choice` step's suggested default from an earlier choice\n\t\t * (its `suggestFrom` configKey → `suggestMap[parentValue]`). No-op once\n\t\t * the user has manually picked here, so a suggestion never clobbers an\n\t\t * explicit choice.\n\t\t *\n\t\t * @param {object} step The dependent choice step.\n\t\t * @return {void}\n\t\t */\n\t\tapplySuggestion(step) {\n\t\t\tif (this.userTouched[step.id] === true) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst parentValue = this.choiceValues[step.suggestFrom]\n\t\t\tif (parentValue === null || parentValue === undefined || parentValue === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst wanted = (step.suggestMap || {})[parentValue]\n\t\t\tif (wanted === null || wanted === undefined) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst opt = this.optionsFor(step).find((o) => o.value === wanted) || { value: wanted, label: String(wanted) }\n\t\t\tthis.choiceModel[step.id] = opt\n\t\t\tif (step.configKey) {\n\t\t\t\tthis.choiceValues[step.configKey] = wanted\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * CnWizardDialog `validate` hook — intercepts Next/Submit to persist\n\t\t * the active step before advancing. `choice`/`config-fields` steps\n\t\t * POST their value via the setup contract; required `choice` steps\n\t\t * block advance until a value is picked; required `run-action` steps\n\t\t * block until the action has succeeded. Returns `true` to advance or\n\t\t * an error string to surface above the step and block navigation.\n\t\t *\n\t\t * @param {string} stepId The active step id.\n\t\t * @return {Promise<boolean|string>} True to advance, or an error message.\n\t\t */\n\t\tasync validateStep(stepId) {\n\t\t\tconst step = this.setupSteps.find((s) => s.id === stepId)\n\t\t\tif (!step) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif (step.type === 'choice') {\n\t\t\t\tif (!this.hasChoice(step)) {\n\t\t\t\t\t// Already persisted server-side and the user only back-navigated\n\t\t\t\t\t// onto it — `choiceModel` is blank because it's session-local, not\n\t\t\t\t\t// because nothing is set. Don't force a re-pick, and don't re-POST.\n\t\t\t\t\tif (this.isServerDone(step.id)) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\treturn step.required === true\n\t\t\t\t\t\t? t('nextcloud-vue', 'Please make a selection to continue.')\n\t\t\t\t\t\t: true\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait this.saveConfig({ [step.configKey]: this.scalarChoice(step) })\n\t\t\t\t\tthis.localDone[step.id] = true\n\t\t\t\t\treturn true\n\t\t\t\t} catch (err) {\n\t\t\t\t\treturn this.errorMessage(err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.type === 'config-fields') {\n\t\t\t\ttry {\n\t\t\t\t\tconst patch = {}\n\t\t\t\t\tfor (const field of this.fieldsFor(step)) {\n\t\t\t\t\t\tpatch[field.key] = this.configModel[field.key]\n\t\t\t\t\t}\n\t\t\t\t\tawait this.saveConfig(patch)\n\t\t\t\t\tthis.localDone[step.id] = true\n\t\t\t\t\treturn true\n\t\t\t\t} catch (err) {\n\t\t\t\t\treturn this.errorMessage(err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.type === 'run-action' && step.required === true && !this.isStepDone(step.id)) {\n\t\t\t\treturn t('nextcloud-vue', 'Please run this step before continuing.')\n\t\t\t}\n\t\t\treturn true\n\t\t},\n\n\t\t/**\n\t\t * Persist one or more app-config values via the setup contract.\n\t\t *\n\t\t * @param {object} patch `{ <configKey>: value }` to write.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync saveConfig(patch) {\n\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\timport('@nextcloud/router'),\n\t\t\t])\n\t\t\tawait axios.post(generateUrl(`/apps/${this.appId}/api/setup/config`), patch)\n\t\t},\n\n\t\tasync runAction(step) {\n\t\t\tthis.running[step.id] = true\n\t\t\tthis.actionResult[step.id] = null\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 { data } = await axios.post(generateUrl(`/apps/${this.appId}/api/setup/action/${step.action}`))\n\t\t\t\tconst 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\tthis.actionResult[step.id] = result\n\t\t\t\tif (result.success) {\n\t\t\t\t\tthis.localDone[step.id] = true\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event action-result Emitted when a `run-action` step finishes.\n\t\t\t\t * @type {{ stepId: string, action: string, success: boolean, message: string }}\n\t\t\t\t */\n\t\t\t\tthis.$emit('action-result', { stepId: step.id, action: step.action, ...result })\n\t\t\t} catch (err) {\n\t\t\t\tthis.actionResult[step.id] = { success: false, message: this.errorMessage(err) }\n\t\t\t\tthis.$emit('action-result', { stepId: step.id, action: step.action, success: false, message: this.errorMessage(err) })\n\t\t\t} finally {\n\t\t\t\tthis.running[step.id] = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Key for one card's load state.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @param {object} option The card's option.\n\t\t * @return {string}\n\t\t */\n\t\tloadKey(step, option) {\n\t\t\treturn step.id + '::' + String(option && option.value)\n\t\t},\n\n\t\t/**\n\t\t * Whether a card gets a Load button: the step declares `loadAction`\n\t\t * and the option is not the \"seed nothing\" answer.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @param {object} option The card's option.\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-a-dataset-card-loads-itself\n\t\t */\n\t\tisLoadableOption(step, option) {\n\t\t\treturn !!(step && step.loadAction) && !!option && String(option.value) !== NONE_DATASET\n\t\t},\n\n\t\tisDatasetLoading(step, option) {\n\t\t\treturn this.datasetRunning[this.loadKey(step, option)] === true\n\t\t},\n\n\t\tdatasetResultFor(step, option) {\n\t\t\treturn this.datasetResult[this.loadKey(step, option)] || null\n\t\t},\n\n\t\tloadButtonLabel(step, option) {\n\t\t\tif (this.isDatasetLoading(step, option)) {\n\t\t\t\treturn this.runningLabel\n\t\t\t}\n\t\t\tconst result = this.datasetResultFor(step, option)\n\t\t\treturn (result && result.success) ? this.reloadLabel : this.loadLabel\n\t\t},\n\n\t\tloadAriaLabel(option) {\n\t\t\treturn t('nextcloud-vue', 'Load {name}', { name: option.label })\n\t\t},\n\n\t\t/**\n\t\t * Load one dataset from its card: POST `{ dataset }` to the step's\n\t\t * `loadAction`, spinner on the button while it runs, the result on the\n\t\t * card after. A successful load also selects the card, so the summary\n\t\t * and the stored choice name what was loaded.\n\t\t *\n\t\t * @param {object} step The choice step declaring `loadAction`.\n\t\t * @param {object} option The card's option.\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-a-dataset-card-loads-itself\n\t\t */\n\t\tasync loadDataset(step, option) {\n\t\t\tif (!this.isLoadableOption(step, option) || this.isDatasetLoading(step, option)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst key = this.loadKey(step, option)\n\t\t\tthis.datasetRunning[key] = true\n\t\t\tthis.datasetResult[key] = null\n\t\t\tlet result\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 { data } = await axios.post(\n\t\t\t\t\tgenerateUrl(`/apps/${this.appId}/api/setup/action/${step.loadAction}`),\n\t\t\t\t\t{ dataset: option.value },\n\t\t\t\t)\n\t\t\t\tresult = {\n\t\t\t\t\tsuccess: !!data && data.success !== false,\n\t\t\t\t\tmessage: (data && data.message) || t('nextcloud-vue', 'Loaded.'),\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tresult = { success: false, message: this.errorMessage(err) }\n\t\t\t} finally {\n\t\t\t\tthis.datasetRunning[key] = false\n\t\t\t}\n\t\t\tthis.datasetResult[key] = result\n\t\t\tif (result.success) {\n\t\t\t\tthis.selectLoadedOption(step, option)\n\t\t\t\tthis.localDone[step.id] = true\n\t\t\t}\n\t\t\tthis.$emit('action-result', {\n\t\t\t\tstepId: step.id,\n\t\t\t\taction: step.loadAction,\n\t\t\t\tdataset: option.value,\n\t\t\t\t...result,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Select a card that just loaded, adding to (never replacing) the\n\t\t * selection of a `multiple` step.\n\t\t *\n\t\t * @param {object} step The choice step.\n\t\t * @param {object} option The loaded option.\n\t\t * @return {void}\n\t\t */\n\t\tselectLoadedOption(step, option) {\n\t\t\tif (step.multiple === true) {\n\t\t\t\tconst current = this.cardModel(step)\n\t\t\t\tif (current.some((v) => String(v) === String(option.value))) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.onChoice(step, current.concat([option.value]))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onChoice(step, option.value)\n\t\t},\n\n\t\t/**\n\t\t * A required app was installed from the dependency list. A new app's\n\t\t * scripts and its `OC.appswebroots` entry exist only after a full page\n\t\t * load, and `useAppStatus` caches per page, so reload.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonDependencyInstalled() {\n\t\t\twindow.location.reload()\n\t\t},\n\n\t\t/**\n\t\t * Whether a step is marked on-demand (`onDemand: true`): it runs only\n\t\t * when the user asks, never automatically, and the summary ticks it\n\t\t * only when it ran in this session.\n\t\t *\n\t\t * @param {object} step The manifest step.\n\t\t * @return {boolean}\n\t\t */\n\t\tisOnDemand(step) {\n\t\t\treturn !!step && step.onDemand === true\n\t\t},\n\n\t\t/**\n\t\t * Whether a step's action succeeded in the CURRENT session, ignoring\n\t\t * whatever the server reports.\n\t\t *\n\t\t * @param {string} id Step id.\n\t\t * @return {boolean}\n\t\t */\n\t\tranThisSession(id) {\n\t\t\treturn !!(this.actionResult[id] && this.actionResult[id].success)\n\t\t},\n\n\t\tisStepDone(id) {\n\t\t\treturn this.isServerDone(id) || this.localDone[id] === true || (this.actionResult[id] && this.actionResult[id].success)\n\t\t},\n\n\t\t/**\n\t\t * Whether the server already reported this step done in a prior\n\t\t * session, via the `completedStepIds` prop.\n\t\t *\n\t\t * @param {string} id Step id.\n\t\t * @return {boolean}\n\t\t */\n\t\tisServerDone(id) {\n\t\t\treturn this.completedStepIds.includes(id)\n\t\t},\n\n\t\tonSubmit() {\n\t\t\tif (this.$refs.wizard && this.$refs.wizard.setResult) {\n\t\t\t\tthis.$refs.wizard.setResult({ success: true, message: this.successText })\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event complete Emitted when the last step is submitted (setup finished).\n\t\t\t */\n\t\t\tthis.$emit('complete')\n\t\t},\n\n\t\tonStepChange(payload) {\n\t\t\t// Pre-fill a suggested default when entering a step that derives one.\n\t\t\tconst step = this.setupSteps.find((s) => s.id === payload.stepId)\n\t\t\tif (step && step.suggestFrom) {\n\t\t\t\tthis.applySuggestion(step)\n\t\t\t}\n\t\t\tthis.maybeAutoRunStep(payload.stepId)\n\t\t\t/**\n\t\t\t * @event step-change Emitted when the active step changes.\n\t\t\t * @type {{ stepId: string, stepIndex: number, direction: string }}\n\t\t\t */\n\t\t\tthis.$emit('step-change', payload)\n\t\t},\n\n\t\tonClose() {\n\t\t\t/**\n\t\t\t * @event close Emitted when the dialog should close.\n\t\t\t */\n\t\t\tthis.$emit('close')\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-setup-step {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-setup-step__nav {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tgap: 8px;\n\tmargin-top: 8px;\n}\n\n.cn-setup-step__running {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\tpadding: 24px 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-setup-load-result {\n\tmargin: 0;\n\tfont-size: var(--font-size-small, 13px);\n}\n\n.cn-setup-load-result--success {\n\tcolor: var(--color-success-text, var(--color-success));\n}\n\n.cn-setup-load-result--error {\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-setup-field {\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-setup-summary {\n\tlist-style: none;\n\tpadding: 0;\n\tmargin: 0;\n}\n\n.cn-setup-summary__item {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 4px 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-setup-summary__item--done {\n\tcolor: var(--color-main-text);\n}\n\n.cn-setup-summary__mark {\n\tfont-weight: bold;\n\tcolor: var(--color-success);\n}\n\n.cn-setup-summary__label {\n\tflex: 0 0 auto;\n}\n\n.cn-setup-summary__value {\n\tmargin-inline-start: auto;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n/* An on-demand step that did not run says so quietly: it is a choice the\n user did not make, not a failure. */\n.cn-setup-summary__item--not-run .cn-setup-summary__value {\n\tfont-weight: normal;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["_createBlock","_createVNode","_renderList","_withCtx","_createElementBlock","_renderSlot","_mergeProps","_createTextVNode","_toDisplayString","_Fragment","_normalizeClass","_openBlock","_createElementVNode"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { loadState } from '@nextcloud/initial-state';
|
|
1
2
|
import { useAppStatus } from './useAppStatus.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -61,5 +62,47 @@ function isDependencyResolved(row) {
|
|
|
61
62
|
return !!row && row.installed === true && row.enabled === true
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Read the `dependency_statuses` initial state an app may inject from PHP.
|
|
67
|
+
*
|
|
68
|
+
* Keyed by app id: `{ installed, enabled }`. Returns `{}` when the app injects
|
|
69
|
+
* none or the state cannot be read, so callers fall back to `useAppStatus`.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} appId The app whose initial state to read.
|
|
72
|
+
* @return {object} Server-reported statuses keyed by app id.
|
|
73
|
+
*
|
|
74
|
+
* @spec openspec/changes/optional-step-requires/specs/cn-setup-wizard/spec.md#requirement-a-step-whose-required-apps-are-absent-is-not-applicable
|
|
75
|
+
*/
|
|
76
|
+
function readServerAppStatuses(appId) {
|
|
77
|
+
try {
|
|
78
|
+
const statuses = loadState(appId, 'dependency_statuses', {});
|
|
79
|
+
return (statuses && typeof statuses === 'object') ? statuses : {}
|
|
80
|
+
} catch {
|
|
81
|
+
return {}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The apps in a setup step's `requires` list that are not installed and
|
|
87
|
+
* enabled.
|
|
88
|
+
*
|
|
89
|
+
* This is the one resolution every setup surface uses: `CnSetupWizard` skips a
|
|
90
|
+
* step when it is non-empty, and `useSetupStatus` then counts that step as not
|
|
91
|
+
* applicable rather than unmet. Keeping both on one function is what stops the
|
|
92
|
+
* wizard and the status from disagreeing about the same step.
|
|
93
|
+
*
|
|
94
|
+
* @param {Array<string>} requires The step's `requires` app ids.
|
|
95
|
+
* @param {object} [serverStatuses] Server-reported statuses keyed by app id.
|
|
96
|
+
* @return {Array<{id: string, name: string, required: boolean, installed: boolean, enabled: boolean}>} The missing apps.
|
|
97
|
+
*
|
|
98
|
+
* @spec openspec/changes/optional-step-requires/specs/cn-setup-wizard/spec.md#requirement-a-step-whose-required-apps-are-absent-is-not-applicable
|
|
99
|
+
*/
|
|
100
|
+
function missingRequiredApps(requires, serverStatuses = {}) {
|
|
101
|
+
if (!Array.isArray(requires) || requires.length === 0) {
|
|
102
|
+
return []
|
|
103
|
+
}
|
|
104
|
+
return checkDependencies(requires, serverStatuses).filter((row) => !isDependencyResolved(row))
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export { checkDependencies, isDependencyResolved, missingRequiredApps, readServerAppStatuses };
|
|
65
108
|
//# sourceMappingURL=useDependencyCheck.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDependencyCheck.js","sources":["../../../src/composables/useDependencyCheck.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\n\nimport { useAppStatus } from './useAppStatus.js'\n\n/**\n * Resolve `manifest.dependencies` entries to their live status.\n *\n * Accepts the manifest's own shape: an app-id string (a REQUIRED dependency)\n * or `{ id, name?, required? }` where `required` defaults to true. Each entry\n * becomes `{ id, name, required, installed, enabled }`.\n *\n * `serverStatuses` is the optional `dependency_statuses` initial state an app\n * injects from PHP, keyed by app id: `{ installed, enabled }`. It wins over\n * the browser heuristic, because only the server can tell \"installed but\n * disabled\" from \"not installed\". Without it, `useAppStatus` answers, which\n * is the same resolution `CnAppRoot` uses for its dependency-missing phase.\n *\n * @param {Array<string|{id: string, name?: string, required?: boolean}>} entries The declared dependencies.\n * @param {object} [serverStatuses] Server-reported statuses keyed by app id.\n * @return {Array<{id: string, name: string, required: boolean, installed: boolean, enabled: boolean}>} One row per valid entry.\n *\n * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step\n *\n * @example\n * const missing = checkDependencies(manifest.dependencies)\n * .filter((d) => d.required && !(d.installed && d.enabled))\n */\nexport function checkDependencies(entries, serverStatuses = {}) {\n\tconst list = Array.isArray(entries) ? entries : []\n\tconst server = (serverStatuses && typeof serverStatuses === 'object') ? serverStatuses : {}\n\treturn list\n\t\t.map((entry) => {\n\t\t\tconst isObject = entry !== null && typeof entry === 'object'\n\t\t\tconst id = isObject ? entry.id : entry\n\t\t\tif (typeof id !== 'string' || id === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst required = isObject ? entry.required !== false : true\n\t\t\tconst name = (isObject && entry.name) || id\n\t\t\tconst reported = server[id]\n\t\t\tif (reported !== undefined && reported !== null) {\n\t\t\t\treturn { id, name, required, installed: !!reported.installed, enabled: !!reported.enabled }\n\t\t\t}\n\t\t\tconst status = useAppStatus(id)\n\t\t\treturn { id, name, required, installed: !!status.installed.value, enabled: !!status.enabled.value }\n\t\t})\n\t\t.filter((row) => row !== null)\n}\n\n/**\n * Whether a resolved dependency row is usable: installed AND enabled.\n *\n * @param {{installed: boolean, enabled: boolean}} row A row from `checkDependencies`.\n * @return {boolean} True when the app can be used.\n */\nexport function isDependencyResolved(row) {\n\treturn !!row && row.installed === true && row.enabled === true\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useDependencyCheck.js","sources":["../../../src/composables/useDependencyCheck.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\n\nimport { loadState } from '@nextcloud/initial-state'\nimport { useAppStatus } from './useAppStatus.js'\n\n/**\n * Resolve `manifest.dependencies` entries to their live status.\n *\n * Accepts the manifest's own shape: an app-id string (a REQUIRED dependency)\n * or `{ id, name?, required? }` where `required` defaults to true. Each entry\n * becomes `{ id, name, required, installed, enabled }`.\n *\n * `serverStatuses` is the optional `dependency_statuses` initial state an app\n * injects from PHP, keyed by app id: `{ installed, enabled }`. It wins over\n * the browser heuristic, because only the server can tell \"installed but\n * disabled\" from \"not installed\". Without it, `useAppStatus` answers, which\n * is the same resolution `CnAppRoot` uses for its dependency-missing phase.\n *\n * @param {Array<string|{id: string, name?: string, required?: boolean}>} entries The declared dependencies.\n * @param {object} [serverStatuses] Server-reported statuses keyed by app id.\n * @return {Array<{id: string, name: string, required: boolean, installed: boolean, enabled: boolean}>} One row per valid entry.\n *\n * @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step\n *\n * @example\n * const missing = checkDependencies(manifest.dependencies)\n * .filter((d) => d.required && !(d.installed && d.enabled))\n */\nexport function checkDependencies(entries, serverStatuses = {}) {\n\tconst list = Array.isArray(entries) ? entries : []\n\tconst server = (serverStatuses && typeof serverStatuses === 'object') ? serverStatuses : {}\n\treturn list\n\t\t.map((entry) => {\n\t\t\tconst isObject = entry !== null && typeof entry === 'object'\n\t\t\tconst id = isObject ? entry.id : entry\n\t\t\tif (typeof id !== 'string' || id === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst required = isObject ? entry.required !== false : true\n\t\t\tconst name = (isObject && entry.name) || id\n\t\t\tconst reported = server[id]\n\t\t\tif (reported !== undefined && reported !== null) {\n\t\t\t\treturn { id, name, required, installed: !!reported.installed, enabled: !!reported.enabled }\n\t\t\t}\n\t\t\tconst status = useAppStatus(id)\n\t\t\treturn { id, name, required, installed: !!status.installed.value, enabled: !!status.enabled.value }\n\t\t})\n\t\t.filter((row) => row !== null)\n}\n\n/**\n * Whether a resolved dependency row is usable: installed AND enabled.\n *\n * @param {{installed: boolean, enabled: boolean}} row A row from `checkDependencies`.\n * @return {boolean} True when the app can be used.\n */\nexport function isDependencyResolved(row) {\n\treturn !!row && row.installed === true && row.enabled === true\n}\n\n/**\n * Read the `dependency_statuses` initial state an app may inject from PHP.\n *\n * Keyed by app id: `{ installed, enabled }`. Returns `{}` when the app injects\n * none or the state cannot be read, so callers fall back to `useAppStatus`.\n *\n * @param {string} appId The app whose initial state to read.\n * @return {object} Server-reported statuses keyed by app id.\n *\n * @spec openspec/changes/optional-step-requires/specs/cn-setup-wizard/spec.md#requirement-a-step-whose-required-apps-are-absent-is-not-applicable\n */\nexport function readServerAppStatuses(appId) {\n\ttry {\n\t\tconst statuses = loadState(appId, 'dependency_statuses', {})\n\t\treturn (statuses && typeof statuses === 'object') ? statuses : {}\n\t} catch {\n\t\treturn {}\n\t}\n}\n\n/**\n * The apps in a setup step's `requires` list that are not installed and\n * enabled.\n *\n * This is the one resolution every setup surface uses: `CnSetupWizard` skips a\n * step when it is non-empty, and `useSetupStatus` then counts that step as not\n * applicable rather than unmet. Keeping both on one function is what stops the\n * wizard and the status from disagreeing about the same step.\n *\n * @param {Array<string>} requires The step's `requires` app ids.\n * @param {object} [serverStatuses] Server-reported statuses keyed by app id.\n * @return {Array<{id: string, name: string, required: boolean, installed: boolean, enabled: boolean}>} The missing apps.\n *\n * @spec openspec/changes/optional-step-requires/specs/cn-setup-wizard/spec.md#requirement-a-step-whose-required-apps-are-absent-is-not-applicable\n */\nexport function missingRequiredApps(requires, serverStatuses = {}) {\n\tif (!Array.isArray(requires) || requires.length === 0) {\n\t\treturn []\n\t}\n\treturn checkDependencies(requires, serverStatuses).filter((row) => !isDependencyResolved(row))\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -94,6 +94,13 @@ function useListView(objectTypeOrOptions, options) {
|
|
|
94
94
|
// field (a folder pane grouping by it, say) reads them here rather than
|
|
95
95
|
// deriving them from `objects`, which is only the current page.
|
|
96
96
|
const facets = computed(() => objectStore.facets[objectType] || {});
|
|
97
|
+
// Outcome of the latest `refresh()`: the store's error for it, or null once
|
|
98
|
+
// it succeeded or a later fetch stored new rows. The store keeps the previous
|
|
99
|
+
// rows on a failed fetch, so this is how a consumer tells stale rows from
|
|
100
|
+
// current ones.
|
|
101
|
+
const error = ref(null);
|
|
102
|
+
let refreshSeq = 0;
|
|
103
|
+
let pendingRefreshes = 0;
|
|
97
104
|
|
|
98
105
|
let searchTimeout = null;
|
|
99
106
|
|
|
@@ -178,14 +185,48 @@ function useListView(objectTypeOrOptions, options) {
|
|
|
178
185
|
|
|
179
186
|
/**
|
|
180
187
|
* Fetch the collection using current state params and update sidebar facet data.
|
|
188
|
+
* Only the most recently started refresh sets `error`, so an older response
|
|
189
|
+
* settling late cannot override a newer one's outcome.
|
|
181
190
|
*
|
|
182
191
|
* @param {number} [page] Page to fetch
|
|
183
192
|
* @return {Promise<void>}
|
|
184
193
|
*/
|
|
185
194
|
async function refresh(page = 1) {
|
|
186
|
-
|
|
195
|
+
const seq = ++refreshSeq;
|
|
196
|
+
// This call's own result. `errors[objectType]` is shared by every call
|
|
197
|
+
// for the type and is not cleared on success, so it cannot say which
|
|
198
|
+
// request failed; it is only the fallback for a store that ignores
|
|
199
|
+
// `options.outcome`.
|
|
200
|
+
const outcome = {};
|
|
201
|
+
pendingRefreshes++;
|
|
202
|
+
try {
|
|
203
|
+
await objectStore.fetchCollection(objectType, buildParams(page), { outcome });
|
|
204
|
+
} catch (e) {
|
|
205
|
+
if (seq === refreshSeq) {
|
|
206
|
+
error.value = e;
|
|
207
|
+
}
|
|
208
|
+
throw e
|
|
209
|
+
} finally {
|
|
210
|
+
pendingRefreshes--;
|
|
211
|
+
}
|
|
212
|
+
if (seq === refreshSeq) {
|
|
213
|
+
error.value = ('error' in outcome) ? outcome.error : (objectStore.errors?.[objectType] || null);
|
|
214
|
+
}
|
|
187
215
|
}
|
|
188
216
|
|
|
217
|
+
// A fetch outside `refresh()` (e.g. the live-updates refetch) that stores
|
|
218
|
+
// new rows recovers the list from an earlier failure. Rows written while
|
|
219
|
+
// a refresh is pending are left to that refresh, which decides `error`.
|
|
220
|
+
watch(
|
|
221
|
+
() => objectStore.collections[objectType],
|
|
222
|
+
() => {
|
|
223
|
+
if (pendingRefreshes === 0) {
|
|
224
|
+
error.value = null;
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
{ flush: 'sync' },
|
|
228
|
+
);
|
|
229
|
+
|
|
189
230
|
// ── Event handlers ───────────────────────────────────────────────────
|
|
190
231
|
|
|
191
232
|
/**
|
|
@@ -323,6 +364,7 @@ function useListView(objectTypeOrOptions, options) {
|
|
|
323
364
|
loading,
|
|
324
365
|
pagination,
|
|
325
366
|
facets,
|
|
367
|
+
error,
|
|
326
368
|
// Local state
|
|
327
369
|
searchTerm,
|
|
328
370
|
sortKey,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListView.js","sources":["../../../src/composables/useListView.js"],"sourcesContent":["import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { useObjectStore } from '../store/index.js'\n\n/**\n * Composable for managing list view state with full objectStore integration.\n *\n * When called with an `objectType` string, connects to the objectStore and handles\n * schema loading, collection fetching, sidebar wiring, and all event handlers\n * automatically. Everything a `CnIndexPage`-based list view needs is returned\n * directly — no additional computed properties or methods required in the component.\n *\n * Backward-compatible: existing `useListView(options)` and `useListView()` calls\n * continue to work without modification.\n *\n * @param {string|object} [objectTypeOrOptions] Object type slug (new API) or legacy options object\n * @param {object} [options] Options (new API only)\n * @param {object|null} [options.objectStore] Custom object store instance (from createObjectStore). When provided, uses this store instead of the default useObjectStore(). Required when the app uses createObjectStore with a custom store ID.\n * @param {object|null} [options.sidebarState] Sidebar state object from `inject('sidebarState')`. When provided, the composable wires and unwires the sidebar automatically on mount/unmount.\n * @param {number} [options.defaultPageSize] Default `_limit` sent to the API\n * @param {number} [options.debounceMs] Search debounce in milliseconds\n * @param {object} [options.defaultSort] Default sort applied on mount e.g. `{ key: 'createdAt', order: 'desc' }`\n * @param {Array<{key: string, order: 'asc'|'desc'}>} [options.defaultSortKeys] Default MULTI-column sort applied on mount (ordered priority list). Takes precedence over `defaultSort` when both are given; used e.g. to restore a sort persisted in the route query.\n * @param {Array<string>|null} [options.defaultVisibleColumns] Initial visible-column key set. Seeds the sidebar's Columns tab with the curated default (e.g. a manifest `columns` list) so toggles add/remove from it. Omit (or `null`) for schema-driven tables where every column starts visible.\n * @param {object} [options.defaultActiveFilters] Initial facet-filter map, e.g. to restore filters persisted in the route query on mount.\n * @param {string} [options.defaultSearchTerm] Initial search term, e.g. to restore `_search` from the route query on mount.\n * @param {object|(() => object)} [options.fixedFilters] A filter map (or getter returning one) merged into every fetch AFTER the user's facet filters, so the fixed entries always win. Used e.g. by `CnIndexPage` to apply a route-param-scoped `pages[].config.filter`. Default `{}` — omitting it is behaviourally identical to before.\n * @param {Array<string>|(() => Array<string>)} [options.extend] OpenRegister `_extend[]` values (or a getter returning them) forwarded on every fetch, e.g. `['calculations']` so a schema's `materialise: false` calculations are evaluated and can be shown as columns. Default `[]` — omitting it sends no `_extend` at all, exactly as before.\n * @return {object} Reactive state and event handlers\n *\n * @example\n * // New API — minimal\n * const { schema, objects, loading, pagination,\n * onSearch, onSort, onFilterChange, onPageChange, refresh } = useListView('client')\n *\n * @example\n * // New API — with sidebar wiring\n * const list = useListView('client', {\n * sidebarState: inject('sidebarState', null),\n * })\n *\n * @example\n * // Legacy API — still works\n * const { searchTerm, filters, onSearchInput, toggleSort } = useListView({\n * objectType: 'client',\n * fetchFn: (type, params) => objectStore.fetchCollection(type, params),\n * })\n */\nexport function useListView(objectTypeOrOptions, options) {\n\t// Backward compat: if first arg is an object or absent, delegate to legacy implementation\n\tif (!objectTypeOrOptions || typeof objectTypeOrOptions === 'object') {\n\t\treturn useLegacyListView(objectTypeOrOptions || {})\n\t}\n\n\t// ── New API ──────────────────────────────────────────────────────────\n\tconst objectType = objectTypeOrOptions\n\tconst opts = options || {}\n\tconst sidebarState = opts.sidebarState || null\n\n\tconst objectStore = opts.objectStore || useObjectStore()\n\n\t// ── State refs ───────────────────────────────────────────────────────\n\tconst schema = ref(null)\n\tconst searchTerm = ref(opts.defaultSearchTerm || '')\n\t// `sortKeys` is the source of truth (ordered multi-column priority list);\n\t// `sortKey`/`sortOrder` are kept as a primary-entry mirror so every\n\t// existing reader of the pre-multi-sort refs keeps working unchanged.\n\tconst initialSortKeys = Array.isArray(opts.defaultSortKeys) && opts.defaultSortKeys.length > 0\n\t\t? opts.defaultSortKeys\n\t\t: (opts.defaultSort?.key ? [{ key: opts.defaultSort.key, order: opts.defaultSort.order || 'asc' }] : [])\n\tconst sortKeys = ref(initialSortKeys)\n\tconst sortKey = ref(initialSortKeys[0]?.key || null)\n\tconst sortOrder = ref(initialSortKeys[0]?.order || 'asc')\n\tconst activeFilters = ref((opts.defaultActiveFilters && typeof opts.defaultActiveFilters === 'object') ? opts.defaultActiveFilters : {})\n\t// Seed the visible-column set to the configured columns so the sidebar's\n\t// Columns tab reflects the curated default and toggles add/remove from it.\n\t// `null` (no seed) means \"all columns visible\" (schema-driven tables).\n\tconst visibleColumns = ref(opts.defaultVisibleColumns ?? null)\n\tconst pageSize = ref(opts.defaultPageSize || 20)\n\n\t// ── Computed refs from the store ─────────────────────────────────────\n\tconst objects = computed(() => objectStore.collections[objectType] || [])\n\t// True from creation until the mount sequence below has issued its first\n\t// fetch. The store's own flag only goes up once `fetchCollection` runs, and\n\t// `onMounted` awaits `fetchSchema()` first — so for the length of that round\n\t// trip the list was \"not loading\" with zero rows, and every consumer\n\t// rendered its empty state (\"No log entries to show\") before any data had\n\t// been asked for. Folded into `loading` rather than exposed separately so\n\t// existing consumers get the fix without touching their templates.\n\tconst bootstrapping = ref(true)\n\tconst loading = computed(() => bootstrapping.value || objectStore.loading[objectType] || false)\n\tconst pagination = computed(() => objectStore.pagination[objectType] || { total: 0, page: 1, pages: 1, limit: 20 })\n\t// Facets are computed by the platform over the whole query rather than the\n\t// loaded page, so anything that needs the complete set of values for a\n\t// field (a folder pane grouping by it, say) reads them here rather than\n\t// deriving them from `objects`, which is only the current page.\n\tconst facets = computed(() => objectStore.facets[objectType] || {})\n\n\tlet searchTimeout = null\n\n\t// ── Param construction ───────────────────────────────────────────────\n\n\t/**\n\t * Resolve `opts.fixedFilters` to a plain map. Accepts a plain object OR a\n\t * function/getter returning one (so callers can derive it from reactive\n\t * sources — e.g. route params — and have it re-read on every fetch).\n\t *\n\t * @return {object} The fixed-filter map (may be empty).\n\t */\n\tfunction resolveFixedFilters() {\n\t\tconst f = typeof opts.fixedFilters === 'function' ? opts.fixedFilters() : opts.fixedFilters\n\t\treturn (f && typeof f === 'object') ? f : {}\n\t}\n\n\t/**\n\t * Resolve `opts.extend` to a list of non-empty strings.\n\t *\n\t * Accepts an array or a getter returning one, so a caller can re-scope it\n\t * between fetches the way `fixedFilters` allows.\n\t *\n\t * @return {Array<string>} The `_extend[]` values, possibly empty.\n\t */\n\tfunction resolveExtend() {\n\t\tconst e = typeof opts.extend === 'function' ? opts.extend() : opts.extend\n\t\tif (!Array.isArray(e)) {\n\t\t\treturn []\n\t\t}\n\t\treturn e.filter((v) => typeof v === 'string' && v !== '')\n\t}\n\n\t/**\n\t * Build API fetch params from current reactive state.\n\t *\n\t * @param {number} page Page number to request\n\t * @return {object} Params object ready to pass to fetchCollection\n\t */\n\tfunction buildParams(page) {\n\t\tconst params = { _limit: pageSize.value, _page: page }\n\n\t\tif (searchTerm.value) {\n\t\t\tparams._search = searchTerm.value\n\t\t}\n\n\t\tif (sortKeys.value.length > 0) {\n\t\t\t// Object key insertion order = sort priority order, matching\n\t\t\t// OpenRegister's `_order` format exactly (`ObjectsController::\n\t\t\t// normalizeOrderParameter` / `MagicMapper`'s ordering loop). A\n\t\t\t// single active key produces byte-identical output to before.\n\t\t\tparams._order = Object.fromEntries(sortKeys.value.map((k) => [k.key, k.order]))\n\t\t}\n\n\t\tfor (const [key, values] of Object.entries(activeFilters.value)) {\n\t\t\tif (values && values.length > 0) {\n\t\t\t\t// Single-value arrays are unwrapped to scalar params\n\t\t\t\tparams[key] = values.length === 1 ? values[0] : values\n\t\t\t}\n\t\t}\n\n\t\t// Fixed filters (e.g. a route-param-scoped `pages[].config.filter`) are\n\t\t// merged LAST so they always win over a colliding facet `activeFilter`.\n\t\t// Repeated `_extend[]`, serialized by the shared query builder. Set\n\t\t// before the fixed filters so an explicit `_extend` in a filter map\n\t\t// still wins, matching every other key's precedence here.\n\t\tconst extend = resolveExtend()\n\t\tif (extend.length > 0) {\n\t\t\tparams._extend = extend\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(resolveFixedFilters())) {\n\t\t\tif (value !== undefined && value !== null && value !== '') {\n\t\t\t\tparams[key] = value\n\t\t\t}\n\t\t}\n\n\t\treturn params\n\t}\n\n\t// ── Fetch ────────────────────────────────────────────────────────────\n\n\t/**\n\t * Fetch the collection using current state params and update sidebar facet data.\n\t *\n\t * @param {number} [page] Page to fetch\n\t * @return {Promise<void>}\n\t */\n\tasync function refresh(page = 1) {\n\t\tawait objectStore.fetchCollection(objectType, buildParams(page))\n\t}\n\n\t// ── Event handlers ───────────────────────────────────────────────────\n\n\t/**\n\t * Handle search input. Debounced by `options.debounceMs` (default 300 ms).\n\t *\n\t * @param {string} value New search string\n\t */\n\tfunction onSearch(value) {\n\t\tsearchTerm.value = value\n\t\tclearTimeout(searchTimeout)\n\t\tsearchTimeout = setTimeout(() => refresh(1), opts.debounceMs || 300)\n\t}\n\n\t/**\n\t * Handle sort change. Updates sort state and triggers refresh.\n\t *\n\t * Accepts CnDataTable's extended `sort` event payload: `keys` (the full\n\t * ordered multi-column list) is used when present; a legacy caller\n\t * passing only `{ key, order }` (no `keys`) still works exactly as\n\t * before — it's normalised to a single-entry `sortKeys` list.\n\t *\n\t * @param {{key: string|null, order: string|null, keys?: Array<{key: string, order: string}>}} sort New sort definition.\n\t */\n\tfunction onSort({ key, order, keys }) {\n\t\tconst nextKeys = Array.isArray(keys) ? keys : (key ? [{ key, order: order || 'asc' }] : [])\n\t\tsortKeys.value = nextKeys\n\t\tsortKey.value = nextKeys[0]?.key ?? null\n\t\tsortOrder.value = nextKeys[0]?.order ?? 'asc'\n\t\trefresh(1)\n\t}\n\n\t/**\n\t * Handle filter change for a single key. Empty arrays remove the key.\n\t *\n\t * @param {string} key Filter key (maps to API param name)\n\t * @param {Array} values Selected filter values\n\t */\n\tfunction onFilterChange(key, values) {\n\t\tif (!values || values.length === 0) {\n\t\t\tconst updated = { ...activeFilters.value }\n\t\t\tdelete updated[key]\n\t\t\tactiveFilters.value = updated\n\t\t} else {\n\t\t\tactiveFilters.value = { ...activeFilters.value, [key]: values }\n\t\t}\n\t\trefresh(1)\n\t}\n\n\t/**\n\t * Handle page navigation.\n\t *\n\t * @param {number} page Page number to navigate to\n\t */\n\tfunction onPageChange(page) {\n\t\trefresh(page)\n\t}\n\n\t/**\n\t * Handle page-size change. Resets to page 1.\n\t *\n\t * @param {number} size New page size\n\t */\n\tfunction onPageSizeChange(size) {\n\t\tpageSize.value = size\n\t\trefresh(1)\n\t}\n\n\t// ── Sidebar wiring ───────────────────────────────────────────────────\n\n\tfunction setupSidebar() {\n\t\tif (!sidebarState) {\n\t\t\treturn\n\t\t}\n\t\tsidebarState.active = true\n\t\tsidebarState.schema = schema.value\n\t\tsidebarState.searchValue = searchTerm.value\n\t\tsidebarState.activeFilters = {}\n\t\tsidebarState.onSearch = onSearch\n\t\tsidebarState.onColumnsChange = (cols) => {\n\t\t\tvisibleColumns.value = cols\n\t\t}\n\t\tsidebarState.onFilterChange = ({ key, values }) => onFilterChange(key, values)\n\t}\n\n\tfunction teardownSidebar() {\n\t\tif (!sidebarState) {\n\t\t\treturn\n\t\t}\n\t\tsidebarState.active = false\n\t\tsidebarState.schema = null\n\t\tsidebarState.activeFilters = {}\n\t\tsidebarState.facetData = {}\n\t\tsidebarState.onSearch = null\n\t\tsidebarState.onColumnsChange = null\n\t\tsidebarState.onFilterChange = null\n\t}\n\n\t// Push facet data to sidebar after each store update\n\tif (sidebarState) {\n\t\twatch(\n\t\t\t() => objectStore.facets[objectType],\n\t\t\t(facets) => {\n\t\t\t\tsidebarState.facetData = facets || {}\n\t\t\t},\n\t\t)\n\t}\n\n\t// ── Lifecycle ────────────────────────────────────────────────────────\n\n\tonMounted(async () => {\n\t\ttry {\n\t\t\tschema.value = await objectStore.fetchSchema(objectType)\n\t\t\tif (sidebarState) {\n\t\t\t\tsetupSidebar()\n\t\t\t}\n\t\t\tawait refresh(1)\n\t\t} finally {\n\t\t\t// Hand the loading state back to the store either way: a schema\n\t\t\t// fetch that rejects must not leave the list spinning forever.\n\t\t\tbootstrapping.value = false\n\t\t}\n\t})\n\n\tonBeforeUnmount(() => {\n\t\tclearTimeout(searchTimeout)\n\t\tteardownSidebar()\n\t})\n\n\t// ── Return value ─────────────────────────────────────────────────────\n\n\treturn {\n\t\t// Store-derived\n\t\tschema,\n\t\tobjects,\n\t\tloading,\n\t\tpagination,\n\t\tfacets,\n\t\t// Local state\n\t\tsearchTerm,\n\t\tsortKey,\n\t\tsortOrder,\n\t\tsortKeys,\n\t\tactiveFilters,\n\t\tvisibleColumns,\n\t\tpageSize,\n\t\t// Event handlers\n\t\tonSearch,\n\t\tonSort,\n\t\tonFilterChange,\n\t\tonPageChange,\n\t\tonPageSizeChange,\n\t\t// Explicit fetch\n\t\trefresh,\n\t}\n}\n\n// ── Legacy implementation ─────────────────────────────────────────────────────\n\n/**\n * Legacy `useListView(options)` implementation.\n * Preserved verbatim for backward compatibility.\n *\n * @param {object} options Legacy options object\n * @param {string} [options.objectType] The registered object type slug\n * @param {(type: string, params: object) => Promise<Array<object>>} [options.fetchFn] Function to call: (type, params) => Promise<Array>\n * @param {number} [options.debounceMs] Search debounce in milliseconds\n * @param {number} [options.pageSize] Default page size\n * @param {object} [options.defaultSort] Default sort: { key: string, order: 'asc'|'desc' }\n * @return {object} Reactive state and methods\n */\nfunction useLegacyListView(options) {\n\tconst searchTerm = ref('')\n\tconst filters = ref({})\n\tconst sortKey = ref(options.defaultSort?.key || null)\n\tconst sortOrder = ref(options.defaultSort?.order || 'asc')\n\tconst currentPage = ref(1)\n\tconst pageSize = ref(options.pageSize || 20)\n\n\tlet searchTimeout = null\n\n\tfunction buildFetchParams() {\n\t\tconst params = {\n\t\t\t_limit: pageSize.value,\n\t\t\t_page: currentPage.value,\n\t\t}\n\n\t\tif (searchTerm.value) {\n\t\t\tparams._search = searchTerm.value\n\t\t}\n\n\t\tif (sortKey.value) {\n\t\t\tparams._order = { [sortKey.value]: sortOrder.value }\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(filters.value)) {\n\t\t\tif (value !== null && value !== '' && value !== false) {\n\t\t\t\tparams[key] = value\n\t\t\t}\n\t\t}\n\n\t\treturn params\n\t}\n\n\tasync function fetchData(page) {\n\t\tif (page !== undefined) {\n\t\t\tcurrentPage.value = page\n\t\t}\n\t\tconst params = buildFetchParams()\n\t\tif (options.fetchFn) {\n\t\t\treturn options.fetchFn(options.objectType, params)\n\t\t}\n\t}\n\n\tfunction onSearchInput(value) {\n\t\tsearchTerm.value = value\n\t\tclearTimeout(searchTimeout)\n\t\tsearchTimeout = setTimeout(() => fetchData(1), options.debounceMs || 300)\n\t}\n\n\tfunction toggleSort(key) {\n\t\tif (sortKey.value === key) {\n\t\t\tif (sortOrder.value === 'asc') {\n\t\t\t\tsortOrder.value = 'desc'\n\t\t\t} else {\n\t\t\t\tsortKey.value = null\n\t\t\t\tsortOrder.value = 'asc'\n\t\t\t}\n\t\t} else {\n\t\t\tsortKey.value = key\n\t\t\tsortOrder.value = 'asc'\n\t\t}\n\t\tfetchData(1)\n\t}\n\n\tfunction setFilter(key, value) {\n\t\tfilters.value = { ...filters.value, [key]: value }\n\t\tfetchData(1)\n\t}\n\n\tfunction clearAllFilters() {\n\t\tsearchTerm.value = ''\n\t\tfilters.value = {}\n\t\tsortKey.value = options.defaultSort?.key || null\n\t\tsortOrder.value = options.defaultSort?.order || 'asc'\n\t\tfetchData(1)\n\t}\n\n\tfunction goToPage(page) {\n\t\tcurrentPage.value = page\n\t\tfetchData()\n\t}\n\n\tonBeforeUnmount(() => clearTimeout(searchTimeout))\n\n\treturn {\n\t\tsearchTerm,\n\t\tfilters,\n\t\tsortKey,\n\t\tsortOrder,\n\t\tcurrentPage,\n\t\tpageSize,\n\t\tonSearchInput,\n\t\ttoggleSort,\n\t\tsetFilter,\n\t\tclearAllFilters,\n\t\tgoToPage,\n\t\tfetch: fetchData,\n\t\tbuildFetchParams,\n\t}\n}\n"],"names":[],"mappings":";;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,WAAW,CAAC,mBAAmB,EAAE,OAAO,EAAE;AAC1D;AACA,CAAC,IAAI,CAAC,mBAAmB,IAAI,OAAO,mBAAmB,KAAK,QAAQ,EAAE;AACtE,EAAE,OAAO,iBAAiB,CAAC,mBAAmB,IAAI,EAAE,CAAC;AACrD,EAAE;AACF;AACA;AACA,CAAC,MAAM,UAAU,GAAG,oBAAmB;AACvC,CAAC,MAAM,IAAI,GAAG,OAAO,IAAI,GAAE;AAC3B,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,KAAI;AAC/C;AACA,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,cAAc,GAAE;AACzD;AACA;AACA,CAAC,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAC;AACzB,CAAC,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,CAAC,iBAAiB,IAAI,EAAE,EAAC;AACrD;AACA;AACA;AACA,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC;AAC/F,IAAI,IAAI,CAAC,eAAe;AACxB,KAAK,IAAI,CAAC,WAAW,EAAE,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,KAAK,IAAI,KAAK,EAAE,CAAC,GAAG,EAAE,EAAC;AAC1G,CAAC,MAAM,QAAQ,GAAG,GAAG,CAAC,eAAe,EAAC;AACtC,CAAC,MAAM,OAAO,GAAG,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,IAAI,EAAC;AACrD,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,KAAK,EAAC;AAC1D,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,oBAAoB,IAAI,OAAO,IAAI,CAAC,oBAAoB,KAAK,QAAQ,IAAI,IAAI,CAAC,oBAAoB,GAAG,EAAE,EAAC;AACzI;AACA;AACA;AACA,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC,IAAI,CAAC,qBAAqB,IAAI,IAAI,EAAC;AAC/D,CAAC,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,EAAC;AACjD;AACA;AACA,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,WAAW,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,EAAE,EAAC;AAC1E;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC,IAAI,EAAC;AAChC,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,aAAa,CAAC,KAAK,IAAI,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,KAAK,EAAC;AAChG,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,WAAW,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAC;AACpH;AACA;AACA;AACA;AACA,CAAC,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,EAAC;AACpE;AACA,CAAC,IAAI,aAAa,GAAG,KAAI;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,SAAS,mBAAmB,GAAG;AAChC,EAAE,MAAM,CAAC,GAAG,OAAO,IAAI,CAAC,YAAY,KAAK,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,aAAY;AAC7F,EAAE,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,GAAG,EAAE;AAC9C,EAAE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,SAAS,aAAa,GAAG;AAC1B,EAAE,MAAM,CAAC,GAAG,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,OAAM;AAC3E,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;AACzB,GAAG,OAAO,EAAE;AACZ,GAAG;AACH,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;AAC3D,EAAE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,SAAS,WAAW,CAAC,IAAI,EAAE;AAC5B,EAAE,MAAM,MAAM,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,GAAE;AACxD;AACA,EAAE,IAAI,UAAU,CAAC,KAAK,EAAE;AACxB,GAAG,MAAM,CAAC,OAAO,GAAG,UAAU,CAAC,MAAK;AACpC,GAAG;AACH;AACA,EAAE,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;AACjC;AACA;AACA;AACA;AACA,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAC;AAClF,GAAG;AACH;AACA,EAAE,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE;AACnE,GAAG,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;AACpC;AACA,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,OAAM;AAC1D,IAAI;AACJ,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,MAAM,MAAM,GAAG,aAAa,GAAE;AAChC,EAAE,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;AACzB,GAAG,MAAM,CAAC,OAAO,GAAG,OAAM;AAC1B,GAAG;AACH;AACA,EAAE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC,EAAE;AACpE,GAAG,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE;AAC9D,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,MAAK;AACvB,IAAI;AACJ,GAAG;AACH;AACA,EAAE,OAAO,MAAM;AACf,EAAE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,eAAe,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE;AAClC,EAAE,MAAM,WAAW,CAAC,eAAe,CAAC,UAAU,EAAE,WAAW,CAAC,IAAI,CAAC,EAAC;AAClE,EAAE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,SAAS,QAAQ,CAAC,KAAK,EAAE;AAC1B,EAAE,UAAU,CAAC,KAAK,GAAG,MAAK;AAC1B,EAAE,YAAY,CAAC,aAAa,EAAC;AAC7B,EAAE,aAAa,GAAG,UAAU,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,IAAI,GAAG,EAAC;AACtE,EAAE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;AACvC,EAAE,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,IAAI,KAAK,EAAE,CAAC,GAAG,EAAE,EAAC;AAC7F,EAAE,QAAQ,CAAC,KAAK,GAAG,SAAQ;AAC3B,EAAE,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,KAAI;AAC1C,EAAE,SAAS,CAAC,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,MAAK;AAC/C,EAAE,OAAO,CAAC,CAAC,EAAC;AACZ,EAAE;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,SAAS,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE;AACtC,EAAE,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;AACtC,GAAG,MAAM,OAAO,GAAG,EAAE,GAAG,aAAa,CAAC,KAAK,GAAE;AAC7C,GAAG,OAAO,OAAO,CAAC,GAAG,EAAC;AACtB,GAAG,aAAa,CAAC,KAAK,GAAG,QAAO;AAChC,GAAG,MAAM;AACT,GAAG,aAAa,CAAC,KAAK,GAAG,EAAE,GAAG,aAAa,CAAC,KAAK,EAAE,CAAC,GAAG,GAAG,MAAM,GAAE;AAClE,GAAG;AACH,EAAE,OAAO,CAAC,CAAC,EAAC;AACZ,EAAE;AACF;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,SAAS,YAAY,CAAC,IAAI,EAAE;AAC7B,EAAE,OAAO,CAAC,IAAI,EAAC;AACf,EAAE;AACF;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,SAAS,gBAAgB,CAAC,IAAI,EAAE;AACjC,EAAE,QAAQ,CAAC,KAAK,GAAG,KAAI;AACvB,EAAE,OAAO,CAAC,CAAC,EAAC;AACZ,EAAE;AACF;AACA;AACA;AACA,CAAC,SAAS,YAAY,GAAG;AACzB,EAAE,IAAI,CAAC,YAAY,EAAE;AACrB,GAAG,MAAM;AACT,GAAG;AACH,EAAE,YAAY,CAAC,MAAM,GAAG,KAAI;AAC5B,EAAE,YAAY,CAAC,MAAM,GAAG,MAAM,CAAC,MAAK;AACpC,EAAE,YAAY,CAAC,WAAW,GAAG,UAAU,CAAC,MAAK;AAC7C,EAAE,YAAY,CAAC,aAAa,GAAG,GAAE;AACjC,EAAE,YAAY,CAAC,QAAQ,GAAG,SAAQ;AAClC,EAAE,YAAY,CAAC,eAAe,GAAG,CAAC,IAAI,KAAK;AAC3C,GAAG,cAAc,CAAC,KAAK,GAAG,KAAI;AAC9B,IAAG;AACH,EAAE,YAAY,CAAC,cAAc,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,cAAc,CAAC,GAAG,EAAE,MAAM,EAAC;AAChF,EAAE;AACF;AACA,CAAC,SAAS,eAAe,GAAG;AAC5B,EAAE,IAAI,CAAC,YAAY,EAAE;AACrB,GAAG,MAAM;AACT,GAAG;AACH,EAAE,YAAY,CAAC,MAAM,GAAG,MAAK;AAC7B,EAAE,YAAY,CAAC,MAAM,GAAG,KAAI;AAC5B,EAAE,YAAY,CAAC,aAAa,GAAG,GAAE;AACjC,EAAE,YAAY,CAAC,SAAS,GAAG,GAAE;AAC7B,EAAE,YAAY,CAAC,QAAQ,GAAG,KAAI;AAC9B,EAAE,YAAY,CAAC,eAAe,GAAG,KAAI;AACrC,EAAE,YAAY,CAAC,cAAc,GAAG,KAAI;AACpC,EAAE;AACF;AACA;AACA,CAAC,IAAI,YAAY,EAAE;AACnB,EAAE,KAAK;AACP,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC;AACvC,GAAG,CAAC,MAAM,KAAK;AACf,IAAI,YAAY,CAAC,SAAS,GAAG,MAAM,IAAI,GAAE;AACzC,IAAI;AACJ,IAAG;AACH,EAAE;AACF;AACA;AACA;AACA,CAAC,SAAS,CAAC,YAAY;AACvB,EAAE,IAAI;AACN,GAAG,MAAM,CAAC,KAAK,GAAG,MAAM,WAAW,CAAC,WAAW,CAAC,UAAU,EAAC;AAC3D,GAAG,IAAI,YAAY,EAAE;AACrB,IAAI,YAAY,GAAE;AAClB,IAAI;AACJ,GAAG,MAAM,OAAO,CAAC,CAAC,EAAC;AACnB,GAAG,SAAS;AACZ;AACA;AACA,GAAG,aAAa,CAAC,KAAK,GAAG,MAAK;AAC9B,GAAG;AACH,EAAE,EAAC;AACH;AACA,CAAC,eAAe,CAAC,MAAM;AACvB,EAAE,YAAY,CAAC,aAAa,EAAC;AAC7B,EAAE,eAAe,GAAE;AACnB,EAAE,EAAC;AACH;AACA;AACA;AACA,CAAC,OAAO;AACR;AACA,EAAE,MAAM;AACR,EAAE,OAAO;AACT,EAAE,OAAO;AACT,EAAE,UAAU;AACZ,EAAE,MAAM;AACR;AACA,EAAE,UAAU;AACZ,EAAE,OAAO;AACT,EAAE,SAAS;AACX,EAAE,QAAQ;AACV,EAAE,aAAa;AACf,EAAE,cAAc;AAChB,EAAE,QAAQ;AACV;AACA,EAAE,QAAQ;AACV,EAAE,MAAM;AACR,EAAE,cAAc;AAChB,EAAE,YAAY;AACd,EAAE,gBAAgB;AAClB;AACA,EAAE,OAAO;AACT,EAAE;AACF,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,iBAAiB,CAAC,OAAO,EAAE;AACpC,CAAC,MAAM,UAAU,GAAG,GAAG,CAAC,EAAE,EAAC;AAC3B,CAAC,MAAM,OAAO,GAAG,GAAG,CAAC,EAAE,EAAC;AACxB,CAAC,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,IAAI,EAAC;AACtD,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,IAAI,KAAK,EAAC;AAC3D,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC,CAAC,EAAC;AAC3B,CAAC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,EAAC;AAC7C;AACA,CAAC,IAAI,aAAa,GAAG,KAAI;AACzB;AACA,CAAC,SAAS,gBAAgB,GAAG;AAC7B,EAAE,MAAM,MAAM,GAAG;AACjB,GAAG,MAAM,EAAE,QAAQ,CAAC,KAAK;AACzB,GAAG,KAAK,EAAE,WAAW,CAAC,KAAK;AAC3B,IAAG;AACH;AACA,EAAE,IAAI,UAAU,CAAC,KAAK,EAAE;AACxB,GAAG,MAAM,CAAC,OAAO,GAAG,UAAU,CAAC,MAAK;AACpC,GAAG;AACH;AACA,EAAE,IAAI,OAAO,CAAC,KAAK,EAAE;AACrB,GAAG,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,KAAK,GAAE;AACvD,GAAG;AACH;AACA,EAAE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;AAC5D,GAAG,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,IAAI,KAAK,KAAK,KAAK,EAAE;AAC1D,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,MAAK;AACvB,IAAI;AACJ,GAAG;AACH;AACA,EAAE,OAAO,MAAM;AACf,EAAE;AACF;AACA,CAAC,eAAe,SAAS,CAAC,IAAI,EAAE;AAChC,EAAE,IAAI,IAAI,KAAK,SAAS,EAAE;AAC1B,GAAG,WAAW,CAAC,KAAK,GAAG,KAAI;AAC3B,GAAG;AACH,EAAE,MAAM,MAAM,GAAG,gBAAgB,GAAE;AACnC,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE;AACvB,GAAG,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC;AACrD,GAAG;AACH,EAAE;AACF;AACA,CAAC,SAAS,aAAa,CAAC,KAAK,EAAE;AAC/B,EAAE,UAAU,CAAC,KAAK,GAAG,MAAK;AAC1B,EAAE,YAAY,CAAC,aAAa,EAAC;AAC7B,EAAE,aAAa,GAAG,UAAU,CAAC,MAAM,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,IAAI,GAAG,EAAC;AAC3E,EAAE;AACF;AACA,CAAC,SAAS,UAAU,CAAC,GAAG,EAAE;AAC1B,EAAE,IAAI,OAAO,CAAC,KAAK,KAAK,GAAG,EAAE;AAC7B,GAAG,IAAI,SAAS,CAAC,KAAK,KAAK,KAAK,EAAE;AAClC,IAAI,SAAS,CAAC,KAAK,GAAG,OAAM;AAC5B,IAAI,MAAM;AACV,IAAI,OAAO,CAAC,KAAK,GAAG,KAAI;AACxB,IAAI,SAAS,CAAC,KAAK,GAAG,MAAK;AAC3B,IAAI;AACJ,GAAG,MAAM;AACT,GAAG,OAAO,CAAC,KAAK,GAAG,IAAG;AACtB,GAAG,SAAS,CAAC,KAAK,GAAG,MAAK;AAC1B,GAAG;AACH,EAAE,SAAS,CAAC,CAAC,EAAC;AACd,EAAE;AACF;AACA,CAAC,SAAS,SAAS,CAAC,GAAG,EAAE,KAAK,EAAE;AAChC,EAAE,OAAO,CAAC,KAAK,GAAG,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,GAAG,GAAG,KAAK,GAAE;AACpD,EAAE,SAAS,CAAC,CAAC,EAAC;AACd,EAAE;AACF;AACA,CAAC,SAAS,eAAe,GAAG;AAC5B,EAAE,UAAU,CAAC,KAAK,GAAG,GAAE;AACvB,EAAE,OAAO,CAAC,KAAK,GAAG,GAAE;AACpB,EAAE,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,KAAI;AAClD,EAAE,SAAS,CAAC,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,KAAK,IAAI,MAAK;AACvD,EAAE,SAAS,CAAC,CAAC,EAAC;AACd,EAAE;AACF;AACA,CAAC,SAAS,QAAQ,CAAC,IAAI,EAAE;AACzB,EAAE,WAAW,CAAC,KAAK,GAAG,KAAI;AAC1B,EAAE,SAAS,GAAE;AACb,EAAE;AACF;AACA,CAAC,eAAe,CAAC,MAAM,YAAY,CAAC,aAAa,CAAC,EAAC;AACnD;AACA,CAAC,OAAO;AACR,EAAE,UAAU;AACZ,EAAE,OAAO;AACT,EAAE,OAAO;AACT,EAAE,SAAS;AACX,EAAE,WAAW;AACb,EAAE,QAAQ;AACV,EAAE,aAAa;AACf,EAAE,UAAU;AACZ,EAAE,SAAS;AACX,EAAE,eAAe;AACjB,EAAE,QAAQ;AACV,EAAE,KAAK,EAAE,SAAS;AAClB,EAAE,gBAAgB;AAClB,EAAE;AACF;;;;"}
|
|
1
|
+
{"version":3,"file":"useListView.js","sources":["../../../src/composables/useListView.js"],"sourcesContent":["import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { useObjectStore } from '../store/index.js'\n\n/**\n * Composable for managing list view state with full objectStore integration.\n *\n * When called with an `objectType` string, connects to the objectStore and handles\n * schema loading, collection fetching, sidebar wiring, and all event handlers\n * automatically. Everything a `CnIndexPage`-based list view needs is returned\n * directly — no additional computed properties or methods required in the component.\n *\n * Backward-compatible: existing `useListView(options)` and `useListView()` calls\n * continue to work without modification.\n *\n * @param {string|object} [objectTypeOrOptions] Object type slug (new API) or legacy options object\n * @param {object} [options] Options (new API only)\n * @param {object|null} [options.objectStore] Custom object store instance (from createObjectStore). When provided, uses this store instead of the default useObjectStore(). Required when the app uses createObjectStore with a custom store ID.\n * @param {object|null} [options.sidebarState] Sidebar state object from `inject('sidebarState')`. When provided, the composable wires and unwires the sidebar automatically on mount/unmount.\n * @param {number} [options.defaultPageSize] Default `_limit` sent to the API\n * @param {number} [options.debounceMs] Search debounce in milliseconds\n * @param {object} [options.defaultSort] Default sort applied on mount e.g. `{ key: 'createdAt', order: 'desc' }`\n * @param {Array<{key: string, order: 'asc'|'desc'}>} [options.defaultSortKeys] Default MULTI-column sort applied on mount (ordered priority list). Takes precedence over `defaultSort` when both are given; used e.g. to restore a sort persisted in the route query.\n * @param {Array<string>|null} [options.defaultVisibleColumns] Initial visible-column key set. Seeds the sidebar's Columns tab with the curated default (e.g. a manifest `columns` list) so toggles add/remove from it. Omit (or `null`) for schema-driven tables where every column starts visible.\n * @param {object} [options.defaultActiveFilters] Initial facet-filter map, e.g. to restore filters persisted in the route query on mount.\n * @param {string} [options.defaultSearchTerm] Initial search term, e.g. to restore `_search` from the route query on mount.\n * @param {object|(() => object)} [options.fixedFilters] A filter map (or getter returning one) merged into every fetch AFTER the user's facet filters, so the fixed entries always win. Used e.g. by `CnIndexPage` to apply a route-param-scoped `pages[].config.filter`. Default `{}` — omitting it is behaviourally identical to before.\n * @param {Array<string>|(() => Array<string>)} [options.extend] OpenRegister `_extend[]` values (or a getter returning them) forwarded on every fetch, e.g. `['calculations']` so a schema's `materialise: false` calculations are evaluated and can be shown as columns. Default `[]` — omitting it sends no `_extend` at all, exactly as before.\n * @return {object} Reactive state and event handlers\n *\n * @example\n * // New API — minimal\n * const { schema, objects, loading, pagination,\n * onSearch, onSort, onFilterChange, onPageChange, refresh } = useListView('client')\n *\n * @example\n * // New API — with sidebar wiring\n * const list = useListView('client', {\n * sidebarState: inject('sidebarState', null),\n * })\n *\n * @example\n * // Legacy API — still works\n * const { searchTerm, filters, onSearchInput, toggleSort } = useListView({\n * objectType: 'client',\n * fetchFn: (type, params) => objectStore.fetchCollection(type, params),\n * })\n */\nexport function useListView(objectTypeOrOptions, options) {\n\t// Backward compat: if first arg is an object or absent, delegate to legacy implementation\n\tif (!objectTypeOrOptions || typeof objectTypeOrOptions === 'object') {\n\t\treturn useLegacyListView(objectTypeOrOptions || {})\n\t}\n\n\t// ── New API ──────────────────────────────────────────────────────────\n\tconst objectType = objectTypeOrOptions\n\tconst opts = options || {}\n\tconst sidebarState = opts.sidebarState || null\n\n\tconst objectStore = opts.objectStore || useObjectStore()\n\n\t// ── State refs ───────────────────────────────────────────────────────\n\tconst schema = ref(null)\n\tconst searchTerm = ref(opts.defaultSearchTerm || '')\n\t// `sortKeys` is the source of truth (ordered multi-column priority list);\n\t// `sortKey`/`sortOrder` are kept as a primary-entry mirror so every\n\t// existing reader of the pre-multi-sort refs keeps working unchanged.\n\tconst initialSortKeys = Array.isArray(opts.defaultSortKeys) && opts.defaultSortKeys.length > 0\n\t\t? opts.defaultSortKeys\n\t\t: (opts.defaultSort?.key ? [{ key: opts.defaultSort.key, order: opts.defaultSort.order || 'asc' }] : [])\n\tconst sortKeys = ref(initialSortKeys)\n\tconst sortKey = ref(initialSortKeys[0]?.key || null)\n\tconst sortOrder = ref(initialSortKeys[0]?.order || 'asc')\n\tconst activeFilters = ref((opts.defaultActiveFilters && typeof opts.defaultActiveFilters === 'object') ? opts.defaultActiveFilters : {})\n\t// Seed the visible-column set to the configured columns so the sidebar's\n\t// Columns tab reflects the curated default and toggles add/remove from it.\n\t// `null` (no seed) means \"all columns visible\" (schema-driven tables).\n\tconst visibleColumns = ref(opts.defaultVisibleColumns ?? null)\n\tconst pageSize = ref(opts.defaultPageSize || 20)\n\n\t// ── Computed refs from the store ─────────────────────────────────────\n\tconst objects = computed(() => objectStore.collections[objectType] || [])\n\t// True from creation until the mount sequence below has issued its first\n\t// fetch. The store's own flag only goes up once `fetchCollection` runs, and\n\t// `onMounted` awaits `fetchSchema()` first — so for the length of that round\n\t// trip the list was \"not loading\" with zero rows, and every consumer\n\t// rendered its empty state (\"No log entries to show\") before any data had\n\t// been asked for. Folded into `loading` rather than exposed separately so\n\t// existing consumers get the fix without touching their templates.\n\tconst bootstrapping = ref(true)\n\tconst loading = computed(() => bootstrapping.value || objectStore.loading[objectType] || false)\n\tconst pagination = computed(() => objectStore.pagination[objectType] || { total: 0, page: 1, pages: 1, limit: 20 })\n\t// Facets are computed by the platform over the whole query rather than the\n\t// loaded page, so anything that needs the complete set of values for a\n\t// field (a folder pane grouping by it, say) reads them here rather than\n\t// deriving them from `objects`, which is only the current page.\n\tconst facets = computed(() => objectStore.facets[objectType] || {})\n\t// Outcome of the latest `refresh()`: the store's error for it, or null once\n\t// it succeeded or a later fetch stored new rows. The store keeps the previous\n\t// rows on a failed fetch, so this is how a consumer tells stale rows from\n\t// current ones.\n\tconst error = ref(null)\n\tlet refreshSeq = 0\n\tlet pendingRefreshes = 0\n\n\tlet searchTimeout = null\n\n\t// ── Param construction ───────────────────────────────────────────────\n\n\t/**\n\t * Resolve `opts.fixedFilters` to a plain map. Accepts a plain object OR a\n\t * function/getter returning one (so callers can derive it from reactive\n\t * sources — e.g. route params — and have it re-read on every fetch).\n\t *\n\t * @return {object} The fixed-filter map (may be empty).\n\t */\n\tfunction resolveFixedFilters() {\n\t\tconst f = typeof opts.fixedFilters === 'function' ? opts.fixedFilters() : opts.fixedFilters\n\t\treturn (f && typeof f === 'object') ? f : {}\n\t}\n\n\t/**\n\t * Resolve `opts.extend` to a list of non-empty strings.\n\t *\n\t * Accepts an array or a getter returning one, so a caller can re-scope it\n\t * between fetches the way `fixedFilters` allows.\n\t *\n\t * @return {Array<string>} The `_extend[]` values, possibly empty.\n\t */\n\tfunction resolveExtend() {\n\t\tconst e = typeof opts.extend === 'function' ? opts.extend() : opts.extend\n\t\tif (!Array.isArray(e)) {\n\t\t\treturn []\n\t\t}\n\t\treturn e.filter((v) => typeof v === 'string' && v !== '')\n\t}\n\n\t/**\n\t * Build API fetch params from current reactive state.\n\t *\n\t * @param {number} page Page number to request\n\t * @return {object} Params object ready to pass to fetchCollection\n\t */\n\tfunction buildParams(page) {\n\t\tconst params = { _limit: pageSize.value, _page: page }\n\n\t\tif (searchTerm.value) {\n\t\t\tparams._search = searchTerm.value\n\t\t}\n\n\t\tif (sortKeys.value.length > 0) {\n\t\t\t// Object key insertion order = sort priority order, matching\n\t\t\t// OpenRegister's `_order` format exactly (`ObjectsController::\n\t\t\t// normalizeOrderParameter` / `MagicMapper`'s ordering loop). A\n\t\t\t// single active key produces byte-identical output to before.\n\t\t\tparams._order = Object.fromEntries(sortKeys.value.map((k) => [k.key, k.order]))\n\t\t}\n\n\t\tfor (const [key, values] of Object.entries(activeFilters.value)) {\n\t\t\tif (values && values.length > 0) {\n\t\t\t\t// Single-value arrays are unwrapped to scalar params\n\t\t\t\tparams[key] = values.length === 1 ? values[0] : values\n\t\t\t}\n\t\t}\n\n\t\t// Fixed filters (e.g. a route-param-scoped `pages[].config.filter`) are\n\t\t// merged LAST so they always win over a colliding facet `activeFilter`.\n\t\t// Repeated `_extend[]`, serialized by the shared query builder. Set\n\t\t// before the fixed filters so an explicit `_extend` in a filter map\n\t\t// still wins, matching every other key's precedence here.\n\t\tconst extend = resolveExtend()\n\t\tif (extend.length > 0) {\n\t\t\tparams._extend = extend\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(resolveFixedFilters())) {\n\t\t\tif (value !== undefined && value !== null && value !== '') {\n\t\t\t\tparams[key] = value\n\t\t\t}\n\t\t}\n\n\t\treturn params\n\t}\n\n\t// ── Fetch ────────────────────────────────────────────────────────────\n\n\t/**\n\t * Fetch the collection using current state params and update sidebar facet data.\n\t * Only the most recently started refresh sets `error`, so an older response\n\t * settling late cannot override a newer one's outcome.\n\t *\n\t * @param {number} [page] Page to fetch\n\t * @return {Promise<void>}\n\t */\n\tasync function refresh(page = 1) {\n\t\tconst seq = ++refreshSeq\n\t\t// This call's own result. `errors[objectType]` is shared by every call\n\t\t// for the type and is not cleared on success, so it cannot say which\n\t\t// request failed; it is only the fallback for a store that ignores\n\t\t// `options.outcome`.\n\t\tconst outcome = {}\n\t\tpendingRefreshes++\n\t\ttry {\n\t\t\tawait objectStore.fetchCollection(objectType, buildParams(page), { outcome })\n\t\t} catch (e) {\n\t\t\tif (seq === refreshSeq) {\n\t\t\t\terror.value = e\n\t\t\t}\n\t\t\tthrow e\n\t\t} finally {\n\t\t\tpendingRefreshes--\n\t\t}\n\t\tif (seq === refreshSeq) {\n\t\t\terror.value = ('error' in outcome) ? outcome.error : (objectStore.errors?.[objectType] || null)\n\t\t}\n\t}\n\n\t// A fetch outside `refresh()` (e.g. the live-updates refetch) that stores\n\t// new rows recovers the list from an earlier failure. Rows written while\n\t// a refresh is pending are left to that refresh, which decides `error`.\n\twatch(\n\t\t() => objectStore.collections[objectType],\n\t\t() => {\n\t\t\tif (pendingRefreshes === 0) {\n\t\t\t\terror.value = null\n\t\t\t}\n\t\t},\n\t\t{ flush: 'sync' },\n\t)\n\n\t// ── Event handlers ───────────────────────────────────────────────────\n\n\t/**\n\t * Handle search input. Debounced by `options.debounceMs` (default 300 ms).\n\t *\n\t * @param {string} value New search string\n\t */\n\tfunction onSearch(value) {\n\t\tsearchTerm.value = value\n\t\tclearTimeout(searchTimeout)\n\t\tsearchTimeout = setTimeout(() => refresh(1), opts.debounceMs || 300)\n\t}\n\n\t/**\n\t * Handle sort change. Updates sort state and triggers refresh.\n\t *\n\t * Accepts CnDataTable's extended `sort` event payload: `keys` (the full\n\t * ordered multi-column list) is used when present; a legacy caller\n\t * passing only `{ key, order }` (no `keys`) still works exactly as\n\t * before — it's normalised to a single-entry `sortKeys` list.\n\t *\n\t * @param {{key: string|null, order: string|null, keys?: Array<{key: string, order: string}>}} sort New sort definition.\n\t */\n\tfunction onSort({ key, order, keys }) {\n\t\tconst nextKeys = Array.isArray(keys) ? keys : (key ? [{ key, order: order || 'asc' }] : [])\n\t\tsortKeys.value = nextKeys\n\t\tsortKey.value = nextKeys[0]?.key ?? null\n\t\tsortOrder.value = nextKeys[0]?.order ?? 'asc'\n\t\trefresh(1)\n\t}\n\n\t/**\n\t * Handle filter change for a single key. Empty arrays remove the key.\n\t *\n\t * @param {string} key Filter key (maps to API param name)\n\t * @param {Array} values Selected filter values\n\t */\n\tfunction onFilterChange(key, values) {\n\t\tif (!values || values.length === 0) {\n\t\t\tconst updated = { ...activeFilters.value }\n\t\t\tdelete updated[key]\n\t\t\tactiveFilters.value = updated\n\t\t} else {\n\t\t\tactiveFilters.value = { ...activeFilters.value, [key]: values }\n\t\t}\n\t\trefresh(1)\n\t}\n\n\t/**\n\t * Handle page navigation.\n\t *\n\t * @param {number} page Page number to navigate to\n\t */\n\tfunction onPageChange(page) {\n\t\trefresh(page)\n\t}\n\n\t/**\n\t * Handle page-size change. Resets to page 1.\n\t *\n\t * @param {number} size New page size\n\t */\n\tfunction onPageSizeChange(size) {\n\t\tpageSize.value = size\n\t\trefresh(1)\n\t}\n\n\t// ── Sidebar wiring ───────────────────────────────────────────────────\n\n\tfunction setupSidebar() {\n\t\tif (!sidebarState) {\n\t\t\treturn\n\t\t}\n\t\tsidebarState.active = true\n\t\tsidebarState.schema = schema.value\n\t\tsidebarState.searchValue = searchTerm.value\n\t\tsidebarState.activeFilters = {}\n\t\tsidebarState.onSearch = onSearch\n\t\tsidebarState.onColumnsChange = (cols) => {\n\t\t\tvisibleColumns.value = cols\n\t\t}\n\t\tsidebarState.onFilterChange = ({ key, values }) => onFilterChange(key, values)\n\t}\n\n\tfunction teardownSidebar() {\n\t\tif (!sidebarState) {\n\t\t\treturn\n\t\t}\n\t\tsidebarState.active = false\n\t\tsidebarState.schema = null\n\t\tsidebarState.activeFilters = {}\n\t\tsidebarState.facetData = {}\n\t\tsidebarState.onSearch = null\n\t\tsidebarState.onColumnsChange = null\n\t\tsidebarState.onFilterChange = null\n\t}\n\n\t// Push facet data to sidebar after each store update\n\tif (sidebarState) {\n\t\twatch(\n\t\t\t() => objectStore.facets[objectType],\n\t\t\t(facets) => {\n\t\t\t\tsidebarState.facetData = facets || {}\n\t\t\t},\n\t\t)\n\t}\n\n\t// ── Lifecycle ────────────────────────────────────────────────────────\n\n\tonMounted(async () => {\n\t\ttry {\n\t\t\tschema.value = await objectStore.fetchSchema(objectType)\n\t\t\tif (sidebarState) {\n\t\t\t\tsetupSidebar()\n\t\t\t}\n\t\t\tawait refresh(1)\n\t\t} finally {\n\t\t\t// Hand the loading state back to the store either way: a schema\n\t\t\t// fetch that rejects must not leave the list spinning forever.\n\t\t\tbootstrapping.value = false\n\t\t}\n\t})\n\n\tonBeforeUnmount(() => {\n\t\tclearTimeout(searchTimeout)\n\t\tteardownSidebar()\n\t})\n\n\t// ── Return value ─────────────────────────────────────────────────────\n\n\treturn {\n\t\t// Store-derived\n\t\tschema,\n\t\tobjects,\n\t\tloading,\n\t\tpagination,\n\t\tfacets,\n\t\terror,\n\t\t// Local state\n\t\tsearchTerm,\n\t\tsortKey,\n\t\tsortOrder,\n\t\tsortKeys,\n\t\tactiveFilters,\n\t\tvisibleColumns,\n\t\tpageSize,\n\t\t// Event handlers\n\t\tonSearch,\n\t\tonSort,\n\t\tonFilterChange,\n\t\tonPageChange,\n\t\tonPageSizeChange,\n\t\t// Explicit fetch\n\t\trefresh,\n\t}\n}\n\n// ── Legacy implementation ─────────────────────────────────────────────────────\n\n/**\n * Legacy `useListView(options)` implementation.\n * Preserved verbatim for backward compatibility.\n *\n * @param {object} options Legacy options object\n * @param {string} [options.objectType] The registered object type slug\n * @param {(type: string, params: object) => Promise<Array<object>>} [options.fetchFn] Function to call: (type, params) => Promise<Array>\n * @param {number} [options.debounceMs] Search debounce in milliseconds\n * @param {number} [options.pageSize] Default page size\n * @param {object} [options.defaultSort] Default sort: { key: string, order: 'asc'|'desc' }\n * @return {object} Reactive state and methods\n */\nfunction useLegacyListView(options) {\n\tconst searchTerm = ref('')\n\tconst filters = ref({})\n\tconst sortKey = ref(options.defaultSort?.key || null)\n\tconst sortOrder = ref(options.defaultSort?.order || 'asc')\n\tconst currentPage = ref(1)\n\tconst pageSize = ref(options.pageSize || 20)\n\n\tlet searchTimeout = null\n\n\tfunction buildFetchParams() {\n\t\tconst params = {\n\t\t\t_limit: pageSize.value,\n\t\t\t_page: currentPage.value,\n\t\t}\n\n\t\tif (searchTerm.value) {\n\t\t\tparams._search = searchTerm.value\n\t\t}\n\n\t\tif (sortKey.value) {\n\t\t\tparams._order = { [sortKey.value]: sortOrder.value }\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(filters.value)) {\n\t\t\tif (value !== null && value !== '' && value !== false) {\n\t\t\t\tparams[key] = value\n\t\t\t}\n\t\t}\n\n\t\treturn params\n\t}\n\n\tasync function fetchData(page) {\n\t\tif (page !== undefined) {\n\t\t\tcurrentPage.value = page\n\t\t}\n\t\tconst params = buildFetchParams()\n\t\tif (options.fetchFn) {\n\t\t\treturn options.fetchFn(options.objectType, params)\n\t\t}\n\t}\n\n\tfunction onSearchInput(value) {\n\t\tsearchTerm.value = value\n\t\tclearTimeout(searchTimeout)\n\t\tsearchTimeout = setTimeout(() => fetchData(1), options.debounceMs || 300)\n\t}\n\n\tfunction toggleSort(key) {\n\t\tif (sortKey.value === key) {\n\t\t\tif (sortOrder.value === 'asc') {\n\t\t\t\tsortOrder.value = 'desc'\n\t\t\t} else {\n\t\t\t\tsortKey.value = null\n\t\t\t\tsortOrder.value = 'asc'\n\t\t\t}\n\t\t} else {\n\t\t\tsortKey.value = key\n\t\t\tsortOrder.value = 'asc'\n\t\t}\n\t\tfetchData(1)\n\t}\n\n\tfunction setFilter(key, value) {\n\t\tfilters.value = { ...filters.value, [key]: value }\n\t\tfetchData(1)\n\t}\n\n\tfunction clearAllFilters() {\n\t\tsearchTerm.value = ''\n\t\tfilters.value = {}\n\t\tsortKey.value = options.defaultSort?.key || null\n\t\tsortOrder.value = options.defaultSort?.order || 'asc'\n\t\tfetchData(1)\n\t}\n\n\tfunction goToPage(page) {\n\t\tcurrentPage.value = page\n\t\tfetchData()\n\t}\n\n\tonBeforeUnmount(() => clearTimeout(searchTimeout))\n\n\treturn {\n\t\tsearchTerm,\n\t\tfilters,\n\t\tsortKey,\n\t\tsortOrder,\n\t\tcurrentPage,\n\t\tpageSize,\n\t\tonSearchInput,\n\t\ttoggleSort,\n\t\tsetFilter,\n\t\tclearAllFilters,\n\t\tgoToPage,\n\t\tfetch: fetchData,\n\t\tbuildFetchParams,\n\t}\n}\n"],"names":[],"mappings":"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"}
|