135 lines
4.8 KiB
JavaScript
135 lines
4.8 KiB
JavaScript
import { computed, toValue } from 'vue'
|
|
|
|
/**
|
|
* @fileoverview Composable for handling form validation logic.
|
|
*/
|
|
export function useFormValidation(formConfig, form, managerState) {
|
|
|
|
const formRef = computed(() => toValue(form)) // Ensure reactivity with the form instance
|
|
const configRef = computed(() => toValue(formConfig)) // Ensure reactivity with config
|
|
|
|
/**
|
|
* Validates specific fields using the vForm instance's validate method.
|
|
* @param {Array<String>} fieldIds - Array of field IDs to validate.
|
|
* @param {String} [httpMethod='POST'] - HTTP method for the validation request.
|
|
* @returns {Promise<boolean>} Resolves true if validation passes, rejects with errors if fails.
|
|
*/
|
|
const validateFields = async (fieldIds, httpMethod = 'POST') => {
|
|
if (!fieldIds || fieldIds.length === 0) {
|
|
return true // Nothing to validate
|
|
}
|
|
|
|
const config = configRef.value
|
|
const validationUrl = `/forms/${config.slug}/answer` // Use reactive config
|
|
|
|
// Use the reactive formRef
|
|
await formRef.value.validate(httpMethod, validationUrl, {}, fieldIds)
|
|
return true // Validation passed
|
|
}
|
|
|
|
/**
|
|
* Validates fields on the current page based on the form mode strategy.
|
|
* Excludes payment fields.
|
|
* @param {Array<Object>} currentPageFields - Array of field objects for the current page.
|
|
* @param {Object} formModeStrategy - The strategy object for the current mode.
|
|
* @returns {Promise<boolean>} Resolves true if validation passes or isn't required, rejects otherwise.
|
|
*/
|
|
const validateCurrentPage = async (currentPageFields, formModeStrategy) => {
|
|
if (!formModeStrategy?.validation?.validateOnNextPage) {
|
|
return true
|
|
}
|
|
|
|
const fieldIdsToValidate = currentPageFields
|
|
.filter(field => field && field.id && field.type !== 'payment')
|
|
.map(field => field.id)
|
|
|
|
if (fieldIdsToValidate.length === 0) {
|
|
return true
|
|
}
|
|
|
|
await validateFields(fieldIdsToValidate)
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* Validates all form fields before final submission, *unless* currently on the last page.
|
|
* @param {Array<Object>} allFields - Array of all field objects in the form.
|
|
* @param {Object} formModeStrategy - The strategy object for the current mode.
|
|
* @param {boolean} isCurrentlyLastPage - Boolean indicating if the *current* state is the last page.
|
|
* @returns {Promise<boolean>} Resolves true if validation passes or isn't required/skipped, rejects otherwise.
|
|
*/
|
|
const validateSubmissionIfNotLastPage = async (allFields, formModeStrategy, isCurrentlyLastPage) => {
|
|
// Skip validation if we are already determined to be on the last page
|
|
if (isCurrentlyLastPage) {
|
|
return true
|
|
}
|
|
|
|
// Skip validation if strategy doesn't require it
|
|
if (!formModeStrategy?.validation?.validateOnSubmit) {
|
|
return true
|
|
}
|
|
|
|
const fieldIdsToValidate = allFields
|
|
.filter(field => field && field.id && field.type !== 'payment')
|
|
.map(field => field.id)
|
|
|
|
if (fieldIdsToValidate.length === 0) {
|
|
return true
|
|
}
|
|
|
|
await validateFields(fieldIdsToValidate)
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* Finds the index of the first page containing validation errors.
|
|
* @param {Array<Array<Object>>} fieldGroups - Nested array of fields per page (from useFormStructure).
|
|
* @returns {number} Index of the first page with errors, or -1 if no errors found.
|
|
*/
|
|
const findFirstPageWithError = (fieldGroups) => {
|
|
const errors = formRef.value.errors
|
|
if (!errors || !errors.any()) {
|
|
return -1 // No errors exist
|
|
}
|
|
if (!fieldGroups) return -1 // No groups to check
|
|
|
|
for (let i = 0; i < fieldGroups.length; i++) {
|
|
const pageHasError = fieldGroups[i]?.some(field => field && field.id && errors.has(field.id))
|
|
if (pageHasError) {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
/**
|
|
* Actions to perform on validation failure (e.g., during submit or page change).
|
|
* @param {Object} context - Object containing { fieldGroups, timerService, setPageIndexCallback }.
|
|
*/
|
|
const onValidationFailure = (context) => {
|
|
const { fieldGroups, timerService, setPageIndexCallback } = context
|
|
|
|
// Restart timer using the timer composable instance
|
|
if (timerService && typeof timerService.start === 'function') {
|
|
timerService.start()
|
|
}
|
|
|
|
// Find and navigate to the first page with an error
|
|
if (fieldGroups && fieldGroups.length > 1 && typeof setPageIndexCallback === 'function') {
|
|
const errorPageIndex = findFirstPageWithError(fieldGroups)
|
|
if (errorPageIndex !== -1 && errorPageIndex !== managerState?.currentPage) {
|
|
setPageIndexCallback(errorPageIndex)
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Exposed API ---
|
|
return {
|
|
// Methods
|
|
validateFields,
|
|
validateCurrentPage,
|
|
validateSubmissionIfNotLastPage,
|
|
findFirstPageWithError,
|
|
onValidationFailure,
|
|
}
|
|
}
|