PluginProbe
The Innovative Form Builder – IvyForms / 0.1.2
The Innovative Form Builder – IvyForms v0.1.2
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / frontend / src / views / public / FormRender.vue

FormRender.vue in The Innovative Form Builder – IvyForms 0.1.2, at frontend/src/views/public/FormRender.vue

294 lines 8.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <template>
2 <div
3 ref="formWrapperRef"
4 :class="['ivyforms-form-wrapper', 'ivyforms-form-wrapper-' + formData.id]"
5 >
6 <div v-if="showSuccessMessage" class="ivyforms-success-message">
7 {{ successMessage || getLabel('thank_you') }}
8 </div>
9 <!-- Form title-->
10 <template v-else>
11 <h2
12 v-if="formData.showTitle"
13 :class="['ivyforms-form-title', 'ivyforms-form-title-' + formData.id]"
14 >
15 {{ formData.name }}
16 </h2>
17 <!-- Form description-->
18 <p
19 v-if="formData.showDescription"
20 :class="['ivyforms-form-description', 'ivyforms-form-description-' + formData.id]"
21 >
22 {{ formData.description }}
23 </p>
24
25 <IvyForm
26 :id="'ivyforms-form-' + formData.id"
27 ref="formRef"
28 :class="'ivyforms-form ivyforms-form-' + formData.id"
29 :data-form-id="formData.id"
30 :rules="formRules"
31 :model="formValues"
32 @submit.prevent="submitForm"
33 >
34 <template v-for="(field, index) in formFields" :key="index">
35 <component
36 :is="formFieldsTemplates[field.type]"
37 ref="formFieldsCollectorRefs"
38 v-model="formValues[field.type + '_' + field.fieldIndex]"
39 :field="field"
40 :error="fieldErrors[field.type + '_' + field.fieldIndex]"
41 ></component>
42 </template>
43 <!-- Add hidden inputs fields for referer, post id ... -->
44 <div class="ivyforms-form-submit">
45 <IvyButtonAction
46 :id="'ivyforms-form-submit-button-' + formData.id"
47 priority="tertiary"
48 type="border"
49 @click.prevent.stop="validateAndSubmit"
50 >
51 {{ getLabel('submit') }}
52 </IvyButtonAction>
53 </div>
54 </IvyForm>
55 </template>
56 </div>
57 </template>
58
59 <script setup lang="ts">
60 import { ref, onMounted, markRaw, reactive } from 'vue'
61 import axios from 'axios'
62
63 // * Form Field templates
64 import IvyTextFormField from '../public/fields/TextFormField.vue'
65 import IvyParagraphFormField from '../public/fields/ParagraphFormField.vue'
66 import IvyEmailFormField from '../public/fields/EmailFormField.vue'
67 import IvyNumberFormField from '../public/fields/NumberFormField.vue'
68 import IvyPhoneFormField from '../public/fields/PhoneFormField.vue'
69 import type { Field } from '@/types/field'
70 import type { Confirmation } from '@/types/confirmations'
71 import { useLabels } from '@/composables/useLabels'
72 const { getLabel } = useLabels()
73
74 // Form data
75 const formFields = ref([])
76 const formValues = ref({})
77 const formRules = ref({})
78 const fieldErrors = ref({})
79 const isLoading = ref(true)
80 const formRef = ref(null)
81 const formWrapperRef = ref(null)
82 const formNonce = ref(null)
83 const formConfirmation = ref([])
84 const showSuccessMessage = ref(false)
85 const successMessage = ref('')
86
87 interface FormData {
88 id: string | number
89 name?: string
90 description?: string
91 showTitle?: boolean
92 showDescription?: boolean
93 defaultValue?: string
94 counter?: number
95 fields?: Field[]
96 confirmations?: Confirmation[]
97 }
98 // Update your refs with proper typing
99 const formData = ref<FormData>({
100 id: '',
101 name: '',
102 description: '',
103 showTitle: true,
104 showDescription: false,
105 defaultValue: '',
106 counter: 0,
107 fields: [],
108 confirmations: [],
109 })
110 // * Form Fields Object
111 const formFieldsTemplates = reactive({
112 text: markRaw(IvyTextFormField),
113 email: markRaw(IvyEmailFormField),
114 textarea: markRaw(IvyParagraphFormField),
115 number: markRaw(IvyNumberFormField),
116 phone: markRaw(IvyPhoneFormField),
117 })
118
119 // Initialize from global variable
120 onMounted(async () => {
121 if (typeof window !== 'undefined' && window.wpIvyFormDataList) {
122 const counter = formWrapperRef.value.closest('.ivyforms-frontend').dataset.counter
123 const formsData = window.wpIvyFormDataList[counter]
124 // Check if formData is an object and has the required properties
125 if (typeof formsData === 'object' && formsData !== null && formsData.id) {
126 // Assign the form data to the reactive variable
127 formData.value = formsData.form
128 formNonce.value = formsData.nonce
129 formFields.value = formsData.fields || []
130 formConfirmation.value = formsData.confirmations || []
131
132 // Initialize form values with defaults or empty strings
133 formFields.value.forEach((field) => {
134 // Initialize field errors
135 const key = field.type + '_' + field.fieldIndex
136 formValues.value[key] = field.defaultValue || ''
137
138 const rules = []
139
140 // Required rule
141 if (field.required) {
142 rules.push({
143 required: true,
144 message: getRequiredMessage(field),
145 trigger: 'submit',
146 })
147 }
148
149 // Format validations
150 if (field.type === 'email') {
151 rules.push({
152 type: 'email',
153 message: getLabel('enter_email_address'),
154 trigger: ['submit'],
155 })
156 }
157
158 if (field.type === 'phone') {
159 rules.push({
160 pattern: /^[+\d]?(?:[\d\s.-]{7,15})$/,
161 message: getLabel('enter_phone_number'),
162 trigger: ['submit'],
163 })
164 }
165
166 formRules.value[key] = rules
167 })
168 // Form is loaded, remove loader
169 isLoading.value = false
170 // add vanila js remove class from loader
171 const loader = document.querySelector('.ivyforms-skeleton-' + counter)
172 if (loader) {
173 loader.remove()
174 }
175 } else {
176 console.error(getLabel('invalid_form_data'), formData)
177 }
178 } else {
179 console.error(getLabel('form_data_not_found'))
180 // Even with error, don't show loader anymore
181 isLoading.value = false
182 }
183 })
184
185 function getRequiredMessage(field: Field): string {
186 return field.label?.trim() ? `${field.label} ${getLabel('this_field')}` : getLabel('required')
187 }
188
189 // Validate form and submit
190 const validateAndSubmit = async () => {
191 if (!formRef.value) return
192
193 await formRef.value.validate((valid: boolean, errors: Record<string, { message: string }[]>) => {
194 if (valid) {
195 submitForm()
196 } else {
197 fieldErrors.value = {} // Clear previous
198 Object.keys(errors).forEach((fieldKey) => {
199 fieldErrors.value[fieldKey] = errors[fieldKey][0]?.message || getLabel('invalid_input')
200 })
201 }
202 })
203 }
204
205 // Submit form
206 const submitForm = async () => {
207 try {
208 const response = await axios.post(
209 // eslint-disable-next-line no-undef
210 `${wpIvyApiSettings.root}ivyforms/v1/form/submission/`,
211 {
212 formId: formData.value.id,
213 values: formValues.value,
214 nonce: formNonce.value,
215 },
216 {
217 headers: {
218 'Content-Type': 'application/json',
219 // eslint-disable-next-line no-undef
220 'X-WP-Nonce': wpIvyApiSettings.nonce,
221 },
222 },
223 )
224 // Handle response
225 if (response.status !== 200) {
226 console.error(getLabel('error_submitting_form'), response)
227 }
228
229 console.log(getLabel('form_submitted'))
230 // Reset form or show success message
231 formValues.value = {}
232 const type = formConfirmation.value[0].type
233 const status = formConfirmation.value[0].status
234
235 if (!status) return
236
237 if (type === 'successMessage') {
238 showSuccessMessage.value = true
239 successMessage.value = formConfirmation.value[0].message || getLabel('thank_you')
240 return
241 } else if (type === 'redirectToPage' && formConfirmation.value[0].page) {
242 const pageId = formConfirmation.value[0].page
243 if (pageId) {
244 const pageUrl = `${window.location.origin}/?p=${pageId}`
245 window.location.href = pageUrl
246 }
247 } else if (type === 'redirectToCustomUrl' && formConfirmation.value[0].url) {
248 window.location.href = formConfirmation.value[0].url
249 }
250 } catch (error) {
251 console.error(getLabel('error_submitting_form'), error)
252 // Show error message
253 }
254 }
255 </script>
256
257 <style lang="scss" scoped>
258 .ivyforms-form-wrapper {
259 width: 100%;
260 max-width: 720px;
261 margin: 0 auto;
262
263 .ivyforms-form-title {
264 font-size: 24px;
265 font-weight: 600;
266 margin-bottom: 12px;
267 color: var(--map-base-text-0);
268 }
269
270 .ivyforms-form-description {
271 font-size: 16px;
272 margin-bottom: 24px;
273 color: var(--map-base-text-1);
274 }
275
276 .ivyforms-form-field {
277 margin-bottom: 16px;
278 }
279
280 .ivyforms-form-submit {
281 margin-top: 24px;
282 }
283 ::v-deep(.ivyforms-success-message) {
284 font-size: 20px;
285 color: var(--map-base-brand-symbol-0);
286 padding: 20px;
287 background-color: var(--map-base-brand-o05);
288 border: 1px solid var(--map-base-brand-symbol-0);
289 border-radius: 8px;
290 text-align: center;
291 }
292 }
293 </style>
294