2023-12-09 15:47:03 +01:00
|
|
|
<template>
|
|
|
|
|
<div v-if="isAutoSubmit">
|
|
|
|
|
<p class="text-center p-4">
|
2023-12-11 11:56:21 +01:00
|
|
|
<Loader class="h-6 w-6 text-nt-blue mx-auto" />
|
2023-12-09 15:47:03 +01:00
|
|
|
</p>
|
|
|
|
|
</div>
|
2024-04-15 19:39:03 +02:00
|
|
|
<form
|
|
|
|
|
v-else-if="dataForm"
|
2024-08-08 12:00:36 +02:00
|
|
|
:style="computedStyle"
|
2024-04-15 19:39:03 +02:00
|
|
|
@submit.prevent=""
|
|
|
|
|
>
|
2024-09-19 10:11:34 +02:00
|
|
|
<FormTimer
|
|
|
|
|
ref="form-timer"
|
|
|
|
|
:form="form"
|
|
|
|
|
/>
|
2024-04-15 19:39:03 +02:00
|
|
|
<template v-if="form.show_progress_bar">
|
|
|
|
|
<div
|
|
|
|
|
v-if="isIframe"
|
|
|
|
|
class="mb-4 p-2"
|
|
|
|
|
>
|
|
|
|
|
<div class="w-full h-2 bg-gray-200 dark:bg-gray-600 relative border rounded-full overflow-hidden">
|
|
|
|
|
<div
|
|
|
|
|
class="h-full transition-all duration-300 rounded-r-full"
|
|
|
|
|
:class="{ 'w-0': formProgress === 0 }"
|
|
|
|
|
:style="{ width: formProgress + '%', background: form.color }"
|
2024-03-18 16:28:01 +01:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-04-15 19:39:03 +02:00
|
|
|
<div
|
|
|
|
|
v-else
|
|
|
|
|
class="fixed top-0 left-0 right-0 z-50"
|
|
|
|
|
>
|
|
|
|
|
<div class="w-full h-[0.2rem] bg-gray-200 dark:bg-gray-600 relative overflow-hidden">
|
|
|
|
|
<div
|
|
|
|
|
class="h-full transition-all duration-300"
|
|
|
|
|
:class="{ 'w-0': formProgress === 0 }"
|
|
|
|
|
:style="{ width: formProgress + '%', background: form.color }"
|
2024-03-18 16:28:01 +01:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
2024-04-15 19:39:03 +02:00
|
|
|
<transition
|
|
|
|
|
name="fade"
|
|
|
|
|
mode="out-in"
|
|
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
:key="currentFieldGroupIndex"
|
|
|
|
|
class="form-group flex flex-wrap w-full"
|
|
|
|
|
>
|
|
|
|
|
<draggable
|
2024-05-13 14:47:59 +02:00
|
|
|
:list="currentFields"
|
|
|
|
|
group="form-elements"
|
2024-04-15 19:39:03 +02:00
|
|
|
item-key="id"
|
2024-05-14 17:17:28 +02:00
|
|
|
class="grid grid-cols-12 relative transition-all w-full"
|
2024-05-13 14:47:59 +02:00
|
|
|
:class="{'rounded-md bg-blue-50':draggingNewBlock}"
|
2024-04-15 19:39:03 +02:00
|
|
|
ghost-class="ghost-item"
|
|
|
|
|
:animation="200"
|
2024-05-13 14:47:59 +02:00
|
|
|
:disabled="!adminPreview"
|
|
|
|
|
handle=".handle"
|
|
|
|
|
@change="handleDragDropped"
|
2023-12-09 15:47:03 +01:00
|
|
|
>
|
|
|
|
|
<template #item="{element}">
|
|
|
|
|
<open-form-field
|
|
|
|
|
:field="element"
|
|
|
|
|
:show-hidden="showHidden"
|
|
|
|
|
:form="form"
|
|
|
|
|
:data-form="dataForm"
|
|
|
|
|
:data-form-value="dataFormValue"
|
|
|
|
|
:theme="theme"
|
2024-04-15 15:19:37 +02:00
|
|
|
:dark-mode="darkMode"
|
2023-12-09 15:47:03 +01:00
|
|
|
:admin-preview="adminPreview"
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
</draggable>
|
|
|
|
|
</div>
|
|
|
|
|
</transition>
|
|
|
|
|
|
|
|
|
|
<!-- Captcha -->
|
|
|
|
|
<template v-if="form.use_captcha && isLastPage">
|
|
|
|
|
<div class="mb-3 px-2 mt-2 mx-auto w-max">
|
2024-04-15 19:39:03 +02:00
|
|
|
<vue-hcaptcha
|
|
|
|
|
ref="hcaptcha"
|
|
|
|
|
:sitekey="hCaptchaSiteKey"
|
|
|
|
|
:theme="darkMode?'dark':'light'"
|
2024-08-08 12:00:36 +02:00
|
|
|
@opened="setMinHeight(500)"
|
|
|
|
|
@closed="setMinHeight(0)"
|
2024-04-15 19:39:03 +02:00
|
|
|
/>
|
|
|
|
|
<has-error
|
|
|
|
|
:form="dataForm"
|
|
|
|
|
field="h-captcha-response"
|
|
|
|
|
/>
|
2023-12-09 15:47:03 +01:00
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- Submit, Next and previous buttons -->
|
|
|
|
|
<div class="flex flex-wrap justify-center w-full">
|
2024-04-15 19:39:03 +02:00
|
|
|
<open-form-button
|
|
|
|
|
v-if="currentFieldGroupIndex>0 && previousFieldsPageBreak && !loading"
|
|
|
|
|
native-type="button"
|
|
|
|
|
:color="form.color"
|
|
|
|
|
:theme="theme"
|
|
|
|
|
class="mt-2 px-8 mx-1"
|
|
|
|
|
@click="previousPage"
|
2023-12-09 15:47:03 +01:00
|
|
|
>
|
|
|
|
|
{{ previousFieldsPageBreak.previous_btn_text }}
|
|
|
|
|
</open-form-button>
|
|
|
|
|
|
2024-04-15 19:39:03 +02:00
|
|
|
<slot
|
|
|
|
|
v-if="isLastPage"
|
|
|
|
|
name="submit-btn"
|
|
|
|
|
:submit-form="submitForm"
|
|
|
|
|
/>
|
|
|
|
|
<open-form-button
|
|
|
|
|
v-else-if="currentFieldsPageBreak"
|
|
|
|
|
native-type="button"
|
|
|
|
|
:color="form.color"
|
|
|
|
|
:theme="theme"
|
|
|
|
|
class="mt-2 px-8 mx-1"
|
2024-06-24 11:08:54 +02:00
|
|
|
:loading="dataForm.busy"
|
2024-07-15 23:20:56 +02:00
|
|
|
@click.stop="nextPage"
|
2023-12-09 15:47:03 +01:00
|
|
|
>
|
|
|
|
|
{{ currentFieldsPageBreak.next_btn_text }}
|
|
|
|
|
</open-form-button>
|
|
|
|
|
<div v-if="!currentFieldsPageBreak && !isLastPage">
|
|
|
|
|
Something is wrong with this form structure. If you're the form owner please contact us.
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import clonedeep from 'clone-deep'
|
|
|
|
|
import draggable from 'vuedraggable'
|
2023-12-24 20:19:59 +01:00
|
|
|
import OpenFormButton from './OpenFormButton.vue'
|
|
|
|
|
import VueHcaptcha from "@hcaptcha/vue3-hcaptcha"
|
|
|
|
|
import OpenFormField from './OpenFormField.vue'
|
2024-01-16 15:00:22 +01:00
|
|
|
import {pendingSubmission} from "~/composables/forms/pendingSubmission.js"
|
|
|
|
|
import FormLogicPropertyResolver from "~/lib/forms/FormLogicPropertyResolver.js"
|
2024-05-13 14:47:59 +02:00
|
|
|
import {computed} from "vue"
|
2024-07-10 12:31:33 +02:00
|
|
|
import CachedDefaultTheme from "~/lib/forms/themes/CachedDefaultTheme.js"
|
2024-09-19 10:11:34 +02:00
|
|
|
import FormTimer from './FormTimer.vue'
|
2023-12-09 15:47:03 +01:00
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: 'OpenForm',
|
2024-09-19 10:11:34 +02:00
|
|
|
components: {draggable, OpenFormField, OpenFormButton, VueHcaptcha, FormTimer},
|
2023-12-09 15:47:03 +01:00
|
|
|
props: {
|
|
|
|
|
form: {
|
|
|
|
|
type: Object,
|
|
|
|
|
required: true
|
|
|
|
|
},
|
|
|
|
|
theme: {
|
2024-07-24 09:50:42 +02:00
|
|
|
type: Object, default: () => {
|
|
|
|
|
const theme = inject("theme", null)
|
|
|
|
|
if (theme) {
|
|
|
|
|
return theme.value
|
|
|
|
|
}
|
|
|
|
|
return CachedDefaultTheme.getInstance()
|
2024-07-10 12:31:33 +02:00
|
|
|
}
|
2024-07-24 09:50:42 +02:00
|
|
|
},
|
2023-12-09 15:47:03 +01:00
|
|
|
loading: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
required: true
|
|
|
|
|
},
|
|
|
|
|
showHidden: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false
|
|
|
|
|
},
|
|
|
|
|
fields: {
|
|
|
|
|
type: Array,
|
|
|
|
|
required: true
|
|
|
|
|
},
|
2024-05-13 14:47:59 +02:00
|
|
|
defaultDataForm: {},
|
|
|
|
|
adminPreview: {type: Boolean, default: false}, // If used in FormEditorPreview
|
2024-07-26 11:44:38 +02:00
|
|
|
urlPrefillPreview: {type: Boolean, default: false}, // If used in UrlFormPrefill
|
2024-04-15 15:19:37 +02:00
|
|
|
darkMode: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false
|
|
|
|
|
}
|
2023-12-09 15:47:03 +01:00
|
|
|
},
|
|
|
|
|
|
2024-05-13 14:47:59 +02:00
|
|
|
setup(props) {
|
2023-12-09 15:47:03 +01:00
|
|
|
const recordsStore = useRecordsStore()
|
|
|
|
|
const workingFormStore = useWorkingFormStore()
|
2023-12-24 20:19:59 +01:00
|
|
|
const dataForm = ref(useForm())
|
|
|
|
|
|
2023-12-09 15:47:03 +01:00
|
|
|
return {
|
2023-12-24 20:19:59 +01:00
|
|
|
dataForm,
|
2023-12-09 15:47:03 +01:00
|
|
|
recordsStore,
|
2023-12-19 13:46:55 +01:00
|
|
|
workingFormStore,
|
2024-08-08 12:00:36 +02:00
|
|
|
isIframe: useIsIframe(),
|
2024-05-13 14:47:59 +02:00
|
|
|
draggingNewBlock: computed(() => workingFormStore.draggingNewBlock),
|
2023-12-24 20:19:59 +01:00
|
|
|
pendingSubmission: pendingSubmission(props.form)
|
2023-12-09 15:47:03 +01:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2024-05-13 14:47:59 +02:00
|
|
|
data() {
|
2023-12-09 15:47:03 +01:00
|
|
|
return {
|
|
|
|
|
currentFieldGroupIndex: 0,
|
|
|
|
|
/**
|
|
|
|
|
* Used to force refresh components by changing their keys
|
|
|
|
|
*/
|
|
|
|
|
isAutoSubmit: false,
|
2024-08-08 12:00:36 +02:00
|
|
|
minHeight: 0
|
2023-12-09 15:47:03 +01:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
computed: {
|
2024-05-13 14:47:59 +02:00
|
|
|
hCaptchaSiteKey() {
|
2024-01-03 17:38:11 +01:00
|
|
|
return useRuntimeConfig().public.hCaptchaSiteKey
|
|
|
|
|
},
|
2023-12-09 15:47:03 +01:00
|
|
|
/**
|
|
|
|
|
* Create field groups (or Page) using page breaks if any
|
|
|
|
|
*/
|
2024-05-13 14:47:59 +02:00
|
|
|
fieldGroups() {
|
2023-12-09 15:47:03 +01:00
|
|
|
if (!this.fields) return []
|
|
|
|
|
const groups = []
|
|
|
|
|
let currentGroup = []
|
|
|
|
|
this.fields.forEach((field) => {
|
|
|
|
|
if (field.type === 'nf-page-break' && this.isFieldHidden(field)) return
|
|
|
|
|
currentGroup.push(field)
|
|
|
|
|
if (field.type === 'nf-page-break') {
|
|
|
|
|
groups.push(currentGroup)
|
|
|
|
|
currentGroup = []
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
groups.push(currentGroup)
|
|
|
|
|
return groups
|
|
|
|
|
},
|
2024-05-13 14:47:59 +02:00
|
|
|
formProgress() {
|
2024-03-18 16:28:01 +01:00
|
|
|
const requiredFields = this.fields.filter(field => field.required)
|
|
|
|
|
if (requiredFields.length === 0) {
|
|
|
|
|
return 100
|
|
|
|
|
}
|
|
|
|
|
const completedFields = requiredFields.filter(field => ![null, undefined, ''].includes(this.dataFormValue[field.id]))
|
|
|
|
|
const progress = (completedFields.length / requiredFields.length) * 100
|
|
|
|
|
return Math.round(progress)
|
|
|
|
|
},
|
2023-12-09 15:47:03 +01:00
|
|
|
currentFields: {
|
2024-05-13 14:47:59 +02:00
|
|
|
get() {
|
2023-12-09 15:47:03 +01:00
|
|
|
return this.fieldGroups[this.currentFieldGroupIndex]
|
|
|
|
|
},
|
2024-05-13 14:47:59 +02:00
|
|
|
set(val) {
|
2023-12-09 15:47:03 +01:00
|
|
|
// On re-order from the form, set the new order
|
|
|
|
|
// Add the previous groups and next to val, and set the properties on working form
|
|
|
|
|
const newFields = []
|
|
|
|
|
this.fieldGroups.forEach((group, index) => {
|
|
|
|
|
if (index < this.currentFieldGroupIndex) {
|
|
|
|
|
newFields.push(...group)
|
|
|
|
|
} else if (index === this.currentFieldGroupIndex) {
|
|
|
|
|
newFields.push(...val)
|
|
|
|
|
} else {
|
|
|
|
|
newFields.push(...group)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
// set the properties on working_form store
|
|
|
|
|
this.workingFormStore.setProperties(newFields)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
/**
|
|
|
|
|
* Returns the page break block for the current group of fields
|
|
|
|
|
*/
|
2024-05-13 14:47:59 +02:00
|
|
|
currentFieldsPageBreak() {
|
2024-01-17 12:43:52 +01:00
|
|
|
// Last block from current group
|
2024-02-10 12:20:45 +01:00
|
|
|
if (!this.currentFields?.length) return null
|
2023-12-09 15:47:03 +01:00
|
|
|
const block = this.currentFields[this.currentFields.length - 1]
|
|
|
|
|
if (block && block.type === 'nf-page-break') return block
|
|
|
|
|
return null
|
|
|
|
|
},
|
2024-05-13 14:47:59 +02:00
|
|
|
previousFieldsPageBreak() {
|
2023-12-09 15:47:03 +01:00
|
|
|
if (this.currentFieldGroupIndex === 0) return null
|
|
|
|
|
const previousFields = this.fieldGroups[this.currentFieldGroupIndex - 1]
|
|
|
|
|
const block = previousFields[previousFields.length - 1]
|
|
|
|
|
if (block && block.type === 'nf-page-break') return block
|
|
|
|
|
return null
|
|
|
|
|
},
|
|
|
|
|
/**
|
|
|
|
|
* Returns true if we're on the last page
|
|
|
|
|
* @returns {boolean}xs
|
|
|
|
|
*/
|
2024-05-13 14:47:59 +02:00
|
|
|
isLastPage() {
|
2023-12-09 15:47:03 +01:00
|
|
|
return this.currentFieldGroupIndex === (this.fieldGroups.length - 1)
|
|
|
|
|
},
|
2024-05-13 14:47:59 +02:00
|
|
|
isPublicFormPage() {
|
2023-12-24 20:19:59 +01:00
|
|
|
return this.$route.name === 'forms-slug'
|
2023-12-09 15:47:03 +01:00
|
|
|
},
|
2024-05-13 14:47:59 +02:00
|
|
|
dataFormValue() {
|
2023-12-09 15:47:03 +01:00
|
|
|
// For get values instead of Id for select/multi select options
|
|
|
|
|
const data = this.dataForm.data()
|
|
|
|
|
const selectionFields = this.fields.filter((field) => {
|
|
|
|
|
return ['select', 'multi_select'].includes(field.type)
|
|
|
|
|
})
|
|
|
|
|
selectionFields.forEach((field) => {
|
|
|
|
|
if (data[field.id] !== undefined && data[field.id] !== null && Array.isArray(data[field.id])) {
|
|
|
|
|
data[field.id] = data[field.id].map(option_nfid => {
|
|
|
|
|
const tmpop = field[field.type].options.find((op) => {
|
|
|
|
|
return (op.id === option_nfid)
|
|
|
|
|
})
|
|
|
|
|
return (tmpop) ? tmpop.name : option_nfid
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
return data
|
2024-08-08 12:00:36 +02:00
|
|
|
},
|
|
|
|
|
computedStyle() {
|
|
|
|
|
return {
|
|
|
|
|
...this.minHeight ? {minHeight: this.minHeight + 'px'} : {}
|
|
|
|
|
}
|
2023-12-09 15:47:03 +01:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
watch: {
|
|
|
|
|
form: {
|
|
|
|
|
deep: true,
|
2024-05-13 14:47:59 +02:00
|
|
|
handler() {
|
2023-12-09 15:47:03 +01:00
|
|
|
this.initForm()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
fields: {
|
|
|
|
|
deep: true,
|
2024-05-13 14:47:59 +02:00
|
|
|
handler() {
|
2023-12-09 15:47:03 +01:00
|
|
|
this.initForm()
|
|
|
|
|
}
|
|
|
|
|
},
|
2023-12-24 20:19:59 +01:00
|
|
|
dataFormValue: {
|
2023-12-09 15:47:03 +01:00
|
|
|
deep: true,
|
2024-05-13 14:47:59 +02:00
|
|
|
handler() {
|
2023-12-24 20:19:59 +01:00
|
|
|
if (this.isPublicFormPage && this.form && this.form.auto_save) {
|
|
|
|
|
this.pendingSubmission.set(this.dataFormValue)
|
2023-12-09 15:47:03 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2024-05-13 14:47:59 +02:00
|
|
|
mounted() {
|
2023-12-09 15:47:03 +01:00
|
|
|
this.initForm()
|
2024-09-19 10:11:34 +02:00
|
|
|
this.$refs['form-timer'].startTimer()
|
2024-03-28 17:59:41 +01:00
|
|
|
if (import.meta.client && window.location.href.includes('auto_submit=true')) {
|
2023-12-09 15:47:03 +01:00
|
|
|
this.isAutoSubmit = true
|
|
|
|
|
this.submitForm()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
methods: {
|
2024-05-13 14:47:59 +02:00
|
|
|
submitForm() {
|
2023-12-09 15:47:03 +01:00
|
|
|
if (this.currentFieldGroupIndex !== this.fieldGroups.length - 1) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-28 17:59:41 +01:00
|
|
|
if (this.form.use_captcha && import.meta.client) {
|
2023-12-09 15:47:03 +01:00
|
|
|
this.dataForm['h-captcha-response'] = document.getElementsByName('h-captcha-response')[0].value
|
|
|
|
|
this.$refs.hcaptcha.reset()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.form.editable_submissions && this.form.submission_id) {
|
|
|
|
|
this.dataForm.submission_id = this.form.submission_id
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-19 10:11:34 +02:00
|
|
|
this.$refs['form-timer'].stopTimer()
|
|
|
|
|
this.dataForm.completion_time = this.$refs['form-timer'].completionTime
|
|
|
|
|
|
2023-12-09 15:47:03 +01:00
|
|
|
this.$emit('submit', this.dataForm, this.onSubmissionFailure)
|
|
|
|
|
},
|
|
|
|
|
/**
|
|
|
|
|
* If more than one page, show first page with error
|
|
|
|
|
*/
|
2024-05-13 14:47:59 +02:00
|
|
|
onSubmissionFailure() {
|
2024-09-19 10:11:34 +02:00
|
|
|
this.$refs['form-timer'].startTimer()
|
2023-12-09 15:47:03 +01:00
|
|
|
this.isAutoSubmit = false
|
|
|
|
|
if (this.fieldGroups.length > 1) {
|
|
|
|
|
// Find first mistake and show page
|
|
|
|
|
let pageChanged = false
|
|
|
|
|
this.fieldGroups.forEach((group, groupIndex) => {
|
|
|
|
|
group.forEach((field) => {
|
|
|
|
|
if (pageChanged) return
|
|
|
|
|
|
|
|
|
|
if (!pageChanged && this.dataForm.errors.has(field.id)) {
|
|
|
|
|
this.currentFieldGroupIndex = groupIndex
|
|
|
|
|
pageChanged = true
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Scroll to error
|
2024-03-28 17:59:41 +01:00
|
|
|
if (import.meta.server) return
|
2023-12-09 15:47:03 +01:00
|
|
|
const elements = document.getElementsByClassName('has-error')
|
|
|
|
|
if (elements.length > 0) {
|
|
|
|
|
window.scroll({
|
|
|
|
|
top: window.scrollY + elements[0].getBoundingClientRect().top - 60,
|
|
|
|
|
behavior: 'smooth'
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
},
|
2024-05-13 14:47:59 +02:00
|
|
|
async getSubmissionData() {
|
2023-12-09 15:47:03 +01:00
|
|
|
if (!this.form || !this.form.editable_submissions || !this.form.submission_id) {
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
await this.recordsStore.loadRecord(
|
2024-01-02 13:09:41 +01:00
|
|
|
opnFetch('/forms/' + this.form.slug + '/submissions/' + this.form.submission_id).then((data) => {
|
2024-05-13 14:47:59 +02:00
|
|
|
return {submission_id: this.form.submission_id, id: this.form.submission_id, ...data.data}
|
2023-12-09 15:47:03 +01:00
|
|
|
})
|
|
|
|
|
)
|
2024-02-03 12:50:57 +01:00
|
|
|
return this.recordsStore.getByKey(this.form.submission_id)
|
2023-12-09 15:47:03 +01:00
|
|
|
},
|
2024-05-13 14:47:59 +02:00
|
|
|
async initForm() {
|
|
|
|
|
if (this.defaultDataForm) {
|
2024-02-03 12:50:57 +01:00
|
|
|
this.dataForm = useForm(this.defaultDataForm)
|
2024-04-15 19:39:03 +02:00
|
|
|
return
|
2024-02-03 12:50:57 +01:00
|
|
|
}
|
|
|
|
|
|
2023-12-09 15:47:03 +01:00
|
|
|
if (this.isPublicFormPage && this.form.editable_submissions) {
|
2024-04-15 15:09:19 +02:00
|
|
|
if (useRoute().query?.submission_id) {
|
|
|
|
|
this.form.submission_id = useRoute().query?.submission_id
|
2023-12-09 15:47:03 +01:00
|
|
|
const data = await this.getSubmissionData()
|
|
|
|
|
if (data !== null && data) {
|
2023-12-24 20:19:59 +01:00
|
|
|
this.dataForm = useForm(data)
|
2023-12-09 15:47:03 +01:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (this.isPublicFormPage && this.form.auto_save) {
|
2024-04-15 19:39:03 +02:00
|
|
|
const pendingData = this.pendingSubmission.get()
|
2024-01-25 06:11:00 +01:00
|
|
|
if (pendingData !== null && pendingData && Object.keys(this.pendingSubmission.get()).length !== 0) {
|
2023-12-09 15:47:03 +01:00
|
|
|
this.fields.forEach((field) => {
|
|
|
|
|
if (field.type === 'date' && field.prefill_today === true) { // For Prefill with 'today'
|
2024-04-15 15:09:19 +02:00
|
|
|
pendingData[field.id] = new Date().toISOString()
|
2023-12-09 15:47:03 +01:00
|
|
|
}
|
|
|
|
|
})
|
2023-12-24 20:19:59 +01:00
|
|
|
this.dataForm = useForm(pendingData)
|
2023-12-09 15:47:03 +01:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const formData = clonedeep(this.dataForm ? this.dataForm.data() : {})
|
|
|
|
|
let urlPrefill = null
|
|
|
|
|
if (this.isPublicFormPage) {
|
|
|
|
|
urlPrefill = new URLSearchParams(window.location.search)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.fields.forEach((field) => {
|
|
|
|
|
if (field.type.startsWith('nf-')) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (urlPrefill && urlPrefill.has(field.id)) {
|
|
|
|
|
// Url prefills
|
|
|
|
|
if (field.type === 'checkbox') {
|
|
|
|
|
if (urlPrefill.get(field.id) === 'false' || urlPrefill.get(field.id) === '0') {
|
|
|
|
|
formData[field.id] = false
|
|
|
|
|
} else if (urlPrefill.get(field.id) === 'true' || urlPrefill.get(field.id) === '1') {
|
|
|
|
|
formData[field.id] = true
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
formData[field.id] = urlPrefill.get(field.id)
|
|
|
|
|
}
|
|
|
|
|
} else if (urlPrefill && urlPrefill.has(field.id + '[]')) {
|
|
|
|
|
// Array url prefills
|
|
|
|
|
formData[field.id] = urlPrefill.getAll(field.id + '[]')
|
|
|
|
|
} else if (field.type === 'date' && field.prefill_today === true) { // For Prefill with 'today'
|
2024-04-15 15:09:19 +02:00
|
|
|
formData[field.id] = new Date().toISOString()
|
2024-08-23 15:28:21 +02:00
|
|
|
} else if (field.type === 'matrix') {
|
|
|
|
|
formData[field.id] = {...field.prefill}
|
2023-12-09 15:47:03 +01:00
|
|
|
} else { // Default prefill if any
|
|
|
|
|
formData[field.id] = field.prefill
|
|
|
|
|
}
|
|
|
|
|
})
|
2023-12-24 20:19:59 +01:00
|
|
|
this.dataForm = useForm(formData)
|
2023-12-09 15:47:03 +01:00
|
|
|
},
|
2024-05-13 14:47:59 +02:00
|
|
|
previousPage() {
|
2023-12-09 15:47:03 +01:00
|
|
|
this.currentFieldGroupIndex -= 1
|
2024-05-13 14:47:59 +02:00
|
|
|
window.scrollTo({top: 0, behavior: 'smooth'})
|
2023-12-09 15:47:03 +01:00
|
|
|
return false
|
|
|
|
|
},
|
2024-05-13 14:47:59 +02:00
|
|
|
nextPage() {
|
2024-07-26 11:44:38 +02:00
|
|
|
if (this.adminPreview || this.urlPrefillPreview) {
|
2024-06-26 10:43:06 +02:00
|
|
|
this.currentFieldGroupIndex += 1
|
2024-08-08 12:00:36 +02:00
|
|
|
window.scrollTo({top: 0, behavior: 'smooth'})
|
2024-06-26 10:43:06 +02:00
|
|
|
return false
|
|
|
|
|
}
|
2024-06-24 11:08:54 +02:00
|
|
|
const fieldsToValidate = this.currentFields.map(f => f.id)
|
|
|
|
|
this.dataForm.busy = true
|
|
|
|
|
this.dataForm.validate('POST', '/forms/' + this.form.slug + '/answer', {}, fieldsToValidate)
|
|
|
|
|
.then((data) => {
|
|
|
|
|
this.currentFieldGroupIndex += 1
|
|
|
|
|
this.dataForm.busy = false
|
|
|
|
|
window.scrollTo({top: 0, behavior: 'smooth'})
|
2024-07-15 23:20:56 +02:00
|
|
|
}).catch(error => {
|
2024-08-08 12:00:36 +02:00
|
|
|
console.error(error)
|
|
|
|
|
if (error && error.data && error.data.message) {
|
|
|
|
|
useAlert().error(error.data.message)
|
|
|
|
|
}
|
|
|
|
|
this.dataForm.busy = false
|
|
|
|
|
})
|
2024-06-26 10:43:06 +02:00
|
|
|
return false
|
2023-12-09 15:47:03 +01:00
|
|
|
},
|
2024-05-13 14:47:59 +02:00
|
|
|
isFieldHidden(field) {
|
2023-12-09 15:47:03 +01:00
|
|
|
return (new FormLogicPropertyResolver(field, this.dataFormValue)).isHidden()
|
|
|
|
|
},
|
2024-08-08 12:00:36 +02:00
|
|
|
getTargetFieldIndex(currentFieldPageIndex) {
|
2024-05-14 17:17:28 +02:00
|
|
|
let targetIndex = 0
|
2024-08-08 12:00:36 +02:00
|
|
|
if (this.currentFieldGroupIndex > 0) {
|
|
|
|
|
for (let i = 0; i < this.currentFieldGroupIndex; i++) {
|
|
|
|
|
targetIndex += this.fieldGroups[i].length
|
2024-05-13 14:47:59 +02:00
|
|
|
}
|
2024-08-08 12:00:36 +02:00
|
|
|
targetIndex += currentFieldPageIndex
|
|
|
|
|
} else {
|
|
|
|
|
targetIndex = currentFieldPageIndex
|
|
|
|
|
}
|
|
|
|
|
return targetIndex
|
2023-12-09 15:47:03 +01:00
|
|
|
},
|
2024-05-13 14:47:59 +02:00
|
|
|
handleDragDropped(data) {
|
|
|
|
|
if (data.added) {
|
|
|
|
|
const targetIndex = this.getTargetFieldIndex(data.added.newIndex)
|
|
|
|
|
this.workingFormStore.addBlock(data.added.element, targetIndex)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.moved) {
|
|
|
|
|
const oldTargetIndex = this.getTargetFieldIndex(data.moved.oldIndex)
|
|
|
|
|
const newTargetIndex = this.getTargetFieldIndex(data.moved.newIndex)
|
|
|
|
|
this.workingFormStore.moveField(oldTargetIndex, newTargetIndex)
|
|
|
|
|
}
|
2024-08-08 12:00:36 +02:00
|
|
|
},
|
|
|
|
|
setMinHeight(minHeight) {
|
|
|
|
|
if (!this.isIframe) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
this.minHeight = minHeight
|
|
|
|
|
// Trigger window iframe resize
|
|
|
|
|
try {
|
|
|
|
|
window.parentIFrame.size()
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error(e)
|
|
|
|
|
}
|
2023-12-09 15:47:03 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang='scss' scoped>
|
|
|
|
|
.ghost-item {
|
|
|
|
|
@apply bg-blue-100 dark:bg-blue-900 rounded-md;
|
|
|
|
|
}
|
|
|
|
|
</style>
|