mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-19 05:58:34 +02:00
202 lines
5.2 KiB
Vue
202 lines
5.2 KiB
Vue
<template>
|
|
<div class="d-flex justify-center inner fill-height">
|
|
<v-stepper
|
|
v-model="currentStep"
|
|
:mobile="!smAndDown"
|
|
class="vertical-overflow"
|
|
>
|
|
<ConfirmDialog ref="confirm" />
|
|
<v-stepper-header class="align-center px-10">
|
|
<v-img :src="logoPath" max-width="190" class="hidden-sm-and-down" />
|
|
<v-stepper-item
|
|
v-for="(step, index) in steps"
|
|
:key="step.name"
|
|
color="primary"
|
|
:value="index + 1"
|
|
:complete="currentStep > index + 1"
|
|
>
|
|
{{ step.title }}
|
|
</v-stepper-item>
|
|
<v-stepper-item :value="steps.length + 1">
|
|
{{ $gettext('Summary') }}
|
|
</v-stepper-item>
|
|
<v-btn
|
|
icon="mdi-close"
|
|
color="primary"
|
|
:size="smAndDown ? 'small' : 'x-large'"
|
|
variant="text"
|
|
@click="close"
|
|
>
|
|
</v-btn>
|
|
</v-stepper-header>
|
|
<v-stepper-window class="mt-4 d-flex justify-center">
|
|
<v-stepper-window-item
|
|
v-for="(step, index) in steps"
|
|
:key="step.name"
|
|
:value="index + 1"
|
|
class="flex-grow-1 ma-10"
|
|
>
|
|
<v-container fluid>
|
|
<v-row align="center" justify="center">
|
|
<v-col cols="8" align="start">
|
|
<div class="mb-5 text-h5">
|
|
<span class="text-grey-darken-1">{{ title }}</span>
|
|
/
|
|
{{ step.title }}
|
|
</div>
|
|
<slot
|
|
:name="`form.${step.name}`"
|
|
:step="step"
|
|
:current-step="index + 1"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
</v-container>
|
|
<div class="d-flex justify-end mt-4">
|
|
<v-btn
|
|
v-if="index + 1 > 1"
|
|
class="mr-10"
|
|
variant="text"
|
|
@click="currentStep = index"
|
|
>
|
|
{{ $gettext('Back') }}
|
|
</v-btn>
|
|
<v-btn
|
|
color="primary"
|
|
size="large"
|
|
@click="goToNextStep(index + 1, index + 2)"
|
|
>
|
|
{{ $gettext('Next') }}
|
|
</v-btn>
|
|
</div>
|
|
</v-stepper-window-item>
|
|
<v-stepper-window-item :value="steps.length + 1" class="flex-grow-0">
|
|
<div class="text-center text-h3">
|
|
{{ $gettext('Summary') }}
|
|
</div>
|
|
<CreationSummary
|
|
:sections="summarySections"
|
|
@modify-step="(val) => (currentStep = val)"
|
|
>
|
|
<template v-for="(_, slot) of $slots" #[slot]="scope">
|
|
<slot :name="slot" v-bind="scope" />
|
|
</template>
|
|
</CreationSummary>
|
|
<div class="d-flex justify-center mt-8">
|
|
<v-btn
|
|
color="primary"
|
|
size="large"
|
|
:loading="working"
|
|
@click="create"
|
|
>
|
|
{{ $gettext('Confirm and create') }}
|
|
</v-btn>
|
|
</div>
|
|
</v-stepper-window-item>
|
|
</v-stepper-window>
|
|
</v-stepper>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="js">
|
|
import ConfirmDialog from './ConfirmDialog'
|
|
import CreationSummary from './CreationSummary.vue'
|
|
import { computed, ref } from 'vue'
|
|
import { useGettext } from 'vue3-gettext'
|
|
import { useDisplay } from 'vuetify'
|
|
import { useGlobalConfig } from '@/main'
|
|
|
|
const { smAndDown } = useDisplay()
|
|
const globalConfig = useGlobalConfig()
|
|
|
|
const props = defineProps({
|
|
title: { type: String, default: '' },
|
|
steps: { type: Array, default: () => [] },
|
|
summarySections: { type: Array, default: () => [] },
|
|
getVFormRef: { type: Function, default: null },
|
|
formGetter: { type: Function, default: null },
|
|
validateObject: { type: Function, default: null },
|
|
})
|
|
|
|
const currentStep = ref(1)
|
|
const working = ref(false)
|
|
const confirm = ref()
|
|
|
|
const logoPath = computed(() => {
|
|
if (globalConfig.CREATION_FORM_LOGO_PATH) {
|
|
return globalConfig.CREATION_FORM_LOGO_PATH
|
|
}
|
|
const imgUrl = new URL('@/assets/Modoboa_RVB-BLEU-SANS.png', import.meta.url)
|
|
.href
|
|
return imgUrl
|
|
})
|
|
|
|
const emit = defineEmits(['close', 'create', 'validationError'])
|
|
|
|
const { $gettext } = useGettext()
|
|
|
|
async function close() {
|
|
const confirmed = await confirm.value.open(
|
|
$gettext('Warning'),
|
|
$gettext(
|
|
"If you close this form now, your modifications won't be saved. Do you confirm?"
|
|
),
|
|
{
|
|
color: 'warning',
|
|
agreeLabel: $gettext('Yes'),
|
|
cancelLabel: $gettext('No'),
|
|
}
|
|
)
|
|
if (!confirmed) {
|
|
return
|
|
}
|
|
emit('close')
|
|
}
|
|
async function goToNextStep(current, next) {
|
|
const vform = props.getVFormRef(props.steps[current - 1])
|
|
if (vform !== undefined) {
|
|
const { valid } = await vform.validate()
|
|
if (!valid) {
|
|
return
|
|
}
|
|
if (props.validateObject) {
|
|
try {
|
|
await props.validateObject(current)
|
|
} catch (error) {
|
|
emit('validationError', current, error.response.data)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
currentStep.value = next
|
|
}
|
|
|
|
function create() {
|
|
working.value = true
|
|
emit('create')
|
|
}
|
|
|
|
defineExpose({
|
|
working,
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.inner {
|
|
background-color: #fff;
|
|
}
|
|
.v-stepper {
|
|
width: 100%;
|
|
overflow: auto;
|
|
}
|
|
.vertical-overflow {
|
|
overflow-y: auto !important;
|
|
}
|
|
|
|
.v-window {
|
|
&__container {
|
|
width: 60%;
|
|
}
|
|
}
|
|
</style>
|