Skip to content

Instantly share code, notes, and snippets.

@nip10
Last active July 28, 2025 10:22
Show Gist options
  • Select an option

  • Save nip10/edabc99d4f93b0ea4481526ef27126fc to your computer and use it in GitHub Desktop.

Select an option

Save nip10/edabc99d4f93b0ea4481526ef27126fc to your computer and use it in GitHub Desktop.
vwo spec
<template>
<div class="step">
<label>
Symptoms:
<textarea name="symptoms" :value="value.symptoms" @input="$emit('input', { ...value, symptoms: $event.target.value })"></textarea>
</label>
<label>
Notes:
<textarea name="notes" :value="value.notes" @input="$emit('input', { ...value, notes: $event.target.value })"></textarea>
</label>
</div>
</template>
<script>
export default {
name: 'StepFive',
props: ['value'],
};
</script>
<style scoped>
.step {
display: flex;
flex-direction: column;
gap: 1rem;
}
</style>
<template>
<div class="step">
<label>
Height (cm):
<input type="number" name="height" :value="value.height" @input="$emit('input', { ...value, height: $event.target.value })">
</label>
<label>
Weight (kg):
<input type="number" name="weight" :value="value.weight" @input="$emit('input', { ...value, weight: $event.target.value })">
</label>
</div>
</template>
<script>
export default {
name: 'StepFour',
props: ['value'],
};
</script>
<style scoped>
.step {
display: flex;
flex-direction: column;
gap: 1rem;
}
</style>
<template>
<div class="step">
<label>
First Name:
<input type="text" name="first_name" :value="value.first_name" @input="$emit('input', { ...value, first_name: $event.target.value })">
</label>
<label>
Last Name:
<input type="text" name="last_name" :value="value.last_name" @input="$emit('input', { ...value, last_name: $event.target.value })">
</label>
</div>
</template>
<script>
export default {
name: 'StepOne',
props: ['value'],
};
</script>
<style scoped>
.step {
display: flex;
flex-direction: column;
gap: 1rem;
}
</style>
<template>
<div class="step">
<label>
Age:
<input type="number" name="age" :value="value.age" @input="$emit('input', { ...value, age: $event.target.value })">
</label>
<label>
Gender:
<select name="gender" :value="value.gender" @change="$emit('input', { ...value, gender: $event.target.value })">
<option disabled value="">Select</option>
<option>Male</option>
<option>Female</option>
<option>Other</option>
</select>
</label>
</div>
</template>
<script>
export default {
name: 'StepThree',
props: ['value'],
};
</script>
<style scoped>
.step {
display: flex;
flex-direction: column;
gap: 1rem;
}
</style>
<template>
<div class="step">
<label>
Email:
<input type="email" name="email" :value="value.email" @input="$emit('input', { ...value, email: $event.target.value })">
</label>
<label>
Phone:
<input type="tel" name="phone" :value="value.phone" @input="$emit('input', { ...value, phone: $event.target.value })">
</label>
</div>
</template>
<script>
export default {
name: 'StepTwo',
props: ['value'],
};
</script>
<style scoped>
.step {
display: flex;
flex-direction: column;
gap: 1rem;
}
</style>
<template>
<form id="clinical-form" class="form-wrapper wrapper" @submit.prevent>
<component
:is="steps[stepIndex]"
v-model="formData[stepIndex]"
/>
<div class="actions">
<button :disabled="stepIndex === 0" @click="prevStep">
Previous
</button>
<button @click="nextStep">
{{ stepIndex === steps.length - 1 ? 'Submit' : 'Next' }}
</button>
</div>
</form>
</template>
<script>
import StepOne from './steps/StepOne.vue';
import StepTwo from './steps/StepTwo.vue';
import StepThree from './steps/StepThree.vue';
import StepFour from './steps/StepFour.vue';
import StepFive from './steps/StepFive.vue';
export default {
name: 'DynamicForm',
components: {
StepOne,
StepTwo,
StepThree,
StepFour,
StepFive,
},
data() {
return {
stepIndex: 0,
steps: ['StepOne', 'StepTwo', 'StepThree', 'StepFour', 'StepFive'],
formData: [
{ first_name: '', last_name: '' },
{ email: '', phone: '' },
{ age: '', gender: '' },
{ height: '', weight: '' },
{ symptoms: '', notes: '' },
],
};
},
computed: {
currentComponent() {
return this.steps[this.stepIndex];
},
},
watch: {
stepIndex(newStep) {
this.$nextTick(() => {
const formInstance = document.getElementById('clinical-form');
if (formInstance && window.VWO?.nls?.formAnalysis?.handleTracking) {
console.log(`[VWO] Tracking step ${newStep + 1} via handleTracking()`);
window.VWO.nls.formAnalysis.handleTracking();
} else {
console.warn('[VWO] handleTracking failed — form or method not available');
}
});
},
},
mounted() {
this.$nextTick(() => {
const formInstance = document.getElementById('clinical-form');
if (formInstance && window.VWO?.nls?.formAnalysis?.handleTracking) {
console.log('[VWO] Initial tracking of step 1 via handleTracking()');
window.VWO.nls.formAnalysis.handleTracking();
} else {
console.warn('[VWO] Initial handleTracking failed — form or method not available');
}
});
},
methods: {
nextStep() {
if (this.stepIndex < this.steps.length - 1) {
this.stepIndex += 1;
} else {
this.submitForm();
}
},
prevStep() {
if (this.stepIndex > 0) {
this.stepIndex -= 1;
}
},
submitForm() {
const mergedData = Object.assign({}, ...this.formData);
console.log('[Submit] Form submitted with data:', mergedData);
const formInstance = document.getElementById('clinical-form');
// Ensure VWO object exists
window.VWO = window.VWO || [];
if (formInstance) {
console.log('[Submit] Found form element:', formInstance);
} else {
console.warn('[Submit] Could not find form element with ID "clinical-form"');
}
if (typeof window.VWO.push === 'function') {
console.log('[VWO] Calling markSuccess for goal ID 1');
window.VWO.push(['nls.formAnalysis.markSuccess', formInstance, 1]);
} else {
console.warn('[VWO] VWO.push is not a function — VWO may not be initialized');
}
},
},
};
</script>
<style scoped>
#clinical-form {
margin-top: 4rem;
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
}
.actions {
display: flex;
gap: 1rem;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment