Files
crewli-old/apps/app/src/composables/forms/utils/formValidation.ts
bert.hausmans 79954aace6 refactor(forms): move packages/form-schema → apps/app/src/composables/forms
Inlines the form-schema source folder (no package.json, alias-only)
into apps/app/src/composables/forms. Drops the @form-schema alias
from apps/app/vite.config.ts (replaced by @/composables/forms via
the existing @ alias). apps/portal vite + vitest configs keep
@form-schema as a temporary alias pointing at the new location so
portal tests/build keep working until apps/portal is removed at the
end of this PR. Two pure-logic form-schema tests moved alongside.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 18:50:52 +02:00

150 lines
4.4 KiB
TypeScript

import { emailValidator, regexValidator, requiredValidator, urlValidator } from './validators'
import { FormFieldType } from '../types/formBuilder'
import type { PublicFormField } from '../types/formBuilder'
export type Validator = (value: unknown) => true | string
/**
* Build a list of client-side validators for a public form field.
* Runs in VTextField `:rules` and the stepper "current step valid"
* gate. Mirrors (a subset of) the backend relaxed rule set so the
* submitter gets feedback before the submit round-trip.
*/
export function getValidatorsForField(field: PublicFormField): Validator[] {
const rules: Validator[] = []
const v = field.validation_rules ?? {}
if (field.is_required) {
rules.push(value => {
const ok = requiredValidator(value)
return ok === true ? true : 'Dit veld is verplicht.'
})
}
switch (field.field_type) {
case FormFieldType.EMAIL:
rules.push(value => {
const ok = emailValidator(value)
return ok === true ? true : 'Vul een geldig e-mailadres in.'
})
break
case FormFieldType.URL:
rules.push(value => {
const ok = urlValidator(value)
return ok === true ? true : 'Vul een geldige URL in (beginnend met http:// of https://).'
})
break
case FormFieldType.PHONE:
rules.push(value => {
if (value === null || value === undefined || value === '') return true
const s = String(value).replace(/\s+/g, '')
return /^\+?[\d()-]{6,}$/.test(s) || 'Vul een geldig telefoonnummer in.'
})
break
case FormFieldType.NUMBER:
rules.push(value => {
if (value === null || value === undefined || value === '') return true
const n = Number(value)
return Number.isFinite(n) || 'Vul een geldig getal in.'
})
if (typeof v.min === 'number') {
const min = v.min
rules.push(value => {
if (value === null || value === undefined || value === '') return true
const n = Number(value)
return Number.isFinite(n) && n >= min ? true : `Minimaal ${min}.`
})
}
if (typeof v.max === 'number') {
const max = v.max
rules.push(value => {
if (value === null || value === undefined || value === '') return true
const n = Number(value)
return Number.isFinite(n) && n <= max ? true : `Maximaal ${max}.`
})
}
break
case FormFieldType.TEXT:
case FormFieldType.TEXTAREA:
if (typeof v.min === 'number') {
const min = v.min
rules.push(value => {
if (value === null || value === undefined || value === '') return true
return String(value).length >= min ? true : `Minimaal ${min} tekens.`
})
}
if (typeof v.max === 'number') {
const max = v.max
rules.push(value => {
if (value === null || value === undefined || value === '') return true
return String(value).length <= max ? true : `Maximaal ${max} tekens.`
})
}
if (typeof v.pattern === 'string' && v.pattern.length > 0) {
const pattern = v.pattern
rules.push(value => {
if (value === null || value === undefined || value === '') return true
const ok = regexValidator(value, pattern)
return ok === true ? true : 'Ongeldige invoer.'
})
}
break
case FormFieldType.MULTISELECT:
case FormFieldType.CHECKBOX_LIST:
if (typeof v.min_selections === 'number') {
const min = v.min_selections
rules.push(value => {
const arr = Array.isArray(value) ? value : []
return arr.length >= min ? true : `Kies er minimaal ${min}.`
})
}
if (typeof v.max_selections === 'number') {
const max = v.max_selections
rules.push(value => {
const arr = Array.isArray(value) ? value : []
return arr.length <= max ? true : `Kies er maximaal ${max}.`
})
}
break
default:
break
}
return rules
}
export function runValidators(rules: Validator[], value: unknown): string | true {
for (const rule of rules) {
const r = rule(value)
if (r !== true) return r
}
return true
}
export function isFieldValueEmpty(value: unknown): boolean {
if (value === null || value === undefined) return true
if (typeof value === 'string') return value.trim() === ''
if (Array.isArray(value)) return value.length === 0
return false
}