refactor(FormTimer.vue): change timer variable from let to ref for reactivity

refactor(OpenCompleteForm.vue): remove FormTimer component and related logic to simplify form submission
feat(OpenForm.vue): reintroduce FormTimer component and manage timer start/stop on form events for better user experience
This commit is contained in:
Julien Nahum 2024-09-19 10:11:34 +02:00
parent f65de634ee
commit 5020f9fa74
3 changed files with 20 additions and 34 deletions

View File

@ -1,5 +1,4 @@
<script setup> <script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { pendingSubmission as pendingSubmissionFun } from "~/composables/forms/pendingSubmission.js" import { pendingSubmission as pendingSubmissionFun } from "~/composables/forms/pendingSubmission.js"
const props = defineProps({ const props = defineProps({
@ -9,7 +8,7 @@ const props = defineProps({
const pendingSubmission = pendingSubmissionFun(props.form) const pendingSubmission = pendingSubmissionFun(props.form)
const startTime = ref(null) const startTime = ref(null)
const completionTime = ref(parseInt(pendingSubmission.getTimer() ?? null)) const completionTime = ref(parseInt(pendingSubmission.getTimer() ?? null))
let timer = null const timer = ref(null)
watch(() => completionTime.value, () => { watch(() => completionTime.value, () => {
if (completionTime.value) { if (completionTime.value) {
@ -21,16 +20,16 @@ const startTimer = () => {
if (!startTime.value) { if (!startTime.value) {
startTime.value = parseInt(pendingSubmission.getTimer() ?? 1) startTime.value = parseInt(pendingSubmission.getTimer() ?? 1)
completionTime.value = startTime.value completionTime.value = startTime.value
timer = setInterval(() => { timer.value = setInterval(() => {
completionTime.value += 1 completionTime.value += 1
}, 1000) }, 1000)
} }
} }
const stopTimer = () => { const stopTimer = () => {
if (timer) { if (timer.value) {
clearInterval(timer) clearInterval(timer.value)
timer = null timer.value = null
startTime.value = null startTime.value = null
} }
} }
@ -40,17 +39,9 @@ const resetTimer = () => {
completionTime.value = null completionTime.value = null
} }
onMounted(() => {
document.addEventListener('input', startTimer)
})
onUnmounted(() => {
document.removeEventListener('input', startTimer)
stopTimer()
})
defineExpose({ defineExpose({
completionTime, completionTime,
startTimer,
stopTimer, stopTimer,
resetTimer resetTimer
}) })

View File

@ -4,11 +4,6 @@
class="open-complete-form" class="open-complete-form"
:style="{ '--font-family': form.font_family }" :style="{ '--font-family': form.font_family }"
> >
<FormTimer
ref="formTimer"
:form="form"
/>
<link <link
v-if="adminPreview && form.font_family" v-if="adminPreview && form.font_family"
rel="stylesheet" rel="stylesheet"
@ -205,7 +200,6 @@
<script> <script>
import OpenForm from './OpenForm.vue' import OpenForm from './OpenForm.vue'
import OpenFormButton from './OpenFormButton.vue' import OpenFormButton from './OpenFormButton.vue'
import FormTimer from './FormTimer.vue'
import VButton from '~/components/global/VButton.vue' import VButton from '~/components/global/VButton.vue'
import FormCleanings from '../../pages/forms/show/FormCleanings.vue' import FormCleanings from '../../pages/forms/show/FormCleanings.vue'
import VTransition from '~/components/global/transitions/VTransition.vue' import VTransition from '~/components/global/transitions/VTransition.vue'
@ -214,7 +208,7 @@ import clonedeep from "clone-deep"
import ThemeBuilder from "~/lib/forms/themes/ThemeBuilder.js" import ThemeBuilder from "~/lib/forms/themes/ThemeBuilder.js"
export default { export default {
components: { VTransition, VButton, OpenFormButton, OpenForm, FormCleanings, FormTimer }, components: { VTransition, VButton, OpenFormButton, OpenForm, FormCleanings },
props: { props: {
form: { type: Object, required: true }, form: { type: Object, required: true },
@ -281,14 +275,7 @@ export default {
if (form.busy) return if (form.busy) return
this.loading = true this.loading = true
// Stop the timer and get the completion time form.post('/forms/' + this.form.slug + '/answer').then((data) => {
this.$refs.formTimer.stopTimer()
const completionTime = this.$refs.formTimer.completionTime
form.completion_time = completionTime
// this.closeAlert()
form.post('/forms/' + this.form.slug + '/answer')
.then((data) => {
useAmplitude().logEvent('form_submission', { useAmplitude().logEvent('form_submission', {
workspace_id: this.form.workspace_id, workspace_id: this.form.workspace_id,
form_id: this.form.id form_id: this.form.id
@ -302,7 +289,7 @@ export default {
redirect_target_url: (this.form.is_pro && data.redirect && data.redirect_url) ? data.redirect_url : null redirect_target_url: (this.form.is_pro && data.redirect && data.redirect_url) ? data.redirect_url : null
}, },
submission_data: form.data(), submission_data: form.data(),
completion_time: completionTime completion_time: form['completion_time']
}) })
if (this.isIframe) { if (this.isIframe) {
@ -334,14 +321,12 @@ export default {
useAlert().error(error.data.message) useAlert().error(error.data.message)
} }
this.loading = false this.loading = false
this.$refs.formTimer.startTimer()
onFailure() onFailure()
}) })
}, },
restart () { restart () {
this.submitted = false this.submitted = false
this.$emit('restarted', true) this.$emit('restarted', true)
this.$refs.formTimer.resetTimer() // Reset the timer
}, },
passwordEntered () { passwordEntered () {
if (this.passwordForm.password !== '' && this.passwordForm.password !== null) { if (this.passwordForm.password !== '' && this.passwordForm.password !== null) {

View File

@ -9,6 +9,10 @@
:style="computedStyle" :style="computedStyle"
@submit.prevent="" @submit.prevent=""
> >
<FormTimer
ref="form-timer"
:form="form"
/>
<template v-if="form.show_progress_bar"> <template v-if="form.show_progress_bar">
<div <div
v-if="isIframe" v-if="isIframe"
@ -134,10 +138,11 @@ import {pendingSubmission} from "~/composables/forms/pendingSubmission.js"
import FormLogicPropertyResolver from "~/lib/forms/FormLogicPropertyResolver.js" import FormLogicPropertyResolver from "~/lib/forms/FormLogicPropertyResolver.js"
import {computed} from "vue" import {computed} from "vue"
import CachedDefaultTheme from "~/lib/forms/themes/CachedDefaultTheme.js" import CachedDefaultTheme from "~/lib/forms/themes/CachedDefaultTheme.js"
import FormTimer from './FormTimer.vue'
export default { export default {
name: 'OpenForm', name: 'OpenForm',
components: {draggable, OpenFormField, OpenFormButton, VueHcaptcha}, components: {draggable, OpenFormField, OpenFormButton, VueHcaptcha, FormTimer},
props: { props: {
form: { form: {
type: Object, type: Object,
@ -328,6 +333,7 @@ export default {
mounted() { mounted() {
this.initForm() this.initForm()
this.$refs['form-timer'].startTimer()
if (import.meta.client && window.location.href.includes('auto_submit=true')) { if (import.meta.client && window.location.href.includes('auto_submit=true')) {
this.isAutoSubmit = true this.isAutoSubmit = true
this.submitForm() this.submitForm()
@ -349,12 +355,16 @@ export default {
this.dataForm.submission_id = this.form.submission_id this.dataForm.submission_id = this.form.submission_id
} }
this.$refs['form-timer'].stopTimer()
this.dataForm.completion_time = this.$refs['form-timer'].completionTime
this.$emit('submit', this.dataForm, this.onSubmissionFailure) this.$emit('submit', this.dataForm, this.onSubmissionFailure)
}, },
/** /**
* If more than one page, show first page with error * If more than one page, show first page with error
*/ */
onSubmissionFailure() { onSubmissionFailure() {
this.$refs['form-timer'].startTimer()
this.isAutoSubmit = false this.isAutoSubmit = false
if (this.fieldGroups.length > 1) { if (this.fieldGroups.length > 1) {
// Find first mistake and show page // Find first mistake and show page