CnWizardDialog
Multi-step modal with per-step slots, back/next navigation, optional
per-step validation, and a single result phase. Each step is declared
in the steps[] prop ({ id, label, optional?, icon? }) and rendered
via a #step-{id} named slot with the navigation API exposed through
the slot scope.
Use CnFormDialog for single-step forms,
CnRichSubmitDialog for single-step
submit-with-files flows, and CnExportWizard
for the specific scope+format+delivery export-trigger shape.
Try it
<template>
<div>
<NcButton @click="show = true">Bulk enrol</NcButton>
<CnWizardDialog
v-if="show"
ref="wizard"
dialog-title="Bulk enrol"
:steps="steps"
:validate="validateStep"
@submit="onSubmit"
@close="show = false">
<template #step-audience="{ stepData, setStepData }">
<CohortPicker :value="stepData.cohort" @input="v => setStepData({ cohort: v })" />
</template>
<template #step-course="{ stepData, setStepData }">
<CoursePicker :value="stepData.course" @input="v => setStepData({ course: v })" />
</template>
<template #step-confirm="{ stepData }">
<p>Enrol {{ stepData.cohort?.size }} learners into {{ stepData.course?.name }}?</p>
</template>
</CnWizardDialog>
</div>
</template>
<script>
import { CnWizardDialog } from '@conduction/nextcloud-vue'
export default {
components: { CnWizardDialog },
data() {
return {
show: false,
steps: [
{ id: 'audience', label: 'Audience' },
{ id: 'course', label: 'Course' },
{ id: 'confirm', label: 'Confirm' },
],
}
},
methods: {
validateStep(stepId, stepData) {
if (stepId === 'audience' && !stepData.cohort) return 'Pick a cohort first.'
if (stepId === 'course' && !stepData.course) return 'Pick a course first.'
return true
},
async onSubmit(payload) {
try {
const { data } = await axios.post('/api/enrolments/bulk', payload)
this.$refs.wizard.setResult({ success: true, message: `Enrolled ${data.count} learners.` })
} catch (e) {
this.$refs.wizard.setResult({ error: e.message })
}
},
},
}
</script>
Slot scope
Each #step-{id} slot receives:
| Field | Type | Description |
|---|---|---|
next | () => Promise<void> | Run validation + advance to the next step. |
back | () => void | Step back. No validation. |
jumpTo | (stepId) => void | Jump to any step. No validation. |
submit | () => Promise<void> | Run validation + emit @submit. |
currentStep | Object | The current step definition. |
stepIndex | number | Zero-based index of the current step. |
totalSteps | number | Total number of declared steps. |
stepData | Object | Shared cross-step data. |
setStepData | (partial: Object) => void | Merge partial into stepData. |
isFirst | boolean | True on the first step. |
isLast | boolean | True on the last step (Next → Submit label flip). |
Props
| Prop | Type | Default | Description |
|---|---|---|---|
steps | Array<{id,label,optional?,icon?}> | — (required) | Step declarations. Order is significant. |
dialogTitle | String | 'Wizard' | Dialog header. |
initialStep | String | '' | Step id to start on; defaults to the first step. |
validate | (stepId, stepData) => Promise<boolean|string> | null | Per-step validator. Return true to advance, a string to show as an error banner + block navigation. |
allowJumpBack | Boolean | true | Allow clicking a completed-step dot to jump back. Forward jumps via the progress indicator are never allowed. |
defaults | Object | {} | Seed values for stepData. |
cancelLabel | String | 'Cancel' | Cancel-button label. |
backLabel | String | 'Back' | Back-button label. |
nextLabel | String | 'Next' | Next-button label. |
submitLabel | String | 'Submit' | Final-step submit-button label. |
closeLabel | String | 'Close' | Close-button label (result phase). |
successText | String | 'Done.' | Default success-banner text when result.message is empty. |
Events
| Event | Payload | Description |
|---|---|---|
submit | stepData | User reached the final step and confirmed. |
step-change | { stepId, stepIndex, direction } | After every step navigation (next, back, jump). |
close | — | Dialog was dismissed. |
Public methods
| Method | Signature | Description |
|---|---|---|
setResult(result) | ({ success?, message?, error? }) | Switch into the result phase + clear loading. |
Slots
#step-{id}— body for each declared step (see scope table above).#result-extra— extra content rendered below the result-phase banner. Scope:{ result }.
See also
CnFormDialog— single-step schema-driven form.CnRichSubmitDialog— single-step rich submit (reason + files + notes).CnExportWizard— pre-built export-trigger wizard.