import { computed } from 'vue' import type { ComputedRef, Ref } from 'vue' import { evaluateConditionalLogic } from './useConditionalLogic' import { FormFieldType } from '../types/formBuilder' import type { FormValues, PublicFormField, PublicFormSchema } from '../types/formBuilder' import { isFieldValueEmpty } from '../utils/formValidation' export type StepKind = 'submitter' | 'section' | 'heading_group' | 'flat' | 'review' export interface FormStep { key: string kind: StepKind title: string subtitle?: string fields: PublicFormField[] } function partitionByHeading(fields: PublicFormField[]): FormStep[] { if (fields.length === 0) return [] const out: FormStep[] = [] let current: FormStep | null = null let index = 0 for (const field of fields) { if (field.field_type === FormFieldType.HEADING) { current = { key: `heading-${field.id}`, kind: 'heading_group', title: field.label, fields: [field], } out.push(current) index++ continue } if (!current) { current = { key: `group-${index}`, kind: 'heading_group', title: 'Vragen', fields: [], } out.push(current) index++ } current.fields.push(field) } return out } export function useFormSteps(schema: Ref): ComputedRef { return computed(() => { const s = schema.value const submitterStep: FormStep = { key: 'submitter', kind: 'submitter', title: 'Contactgegevens', subtitle: 'Zo kunnen we contact met je opnemen', fields: [], } const reviewStep: FormStep = { key: 'review', kind: 'review', title: 'Controleer en versturen', subtitle: 'Check je antwoorden en verstuur het formulier', fields: [], } if (!s) return [submitterStep, reviewStep] const sorted = [...s.fields].sort((a, b) => a.sort_order - b.sort_order) const steps: FormStep[] = [submitterStep] if (s.sections.length > 0 && s.section_level_submit === false) { const sectionsSorted = [...s.sections].sort((a, b) => a.sort_order - b.sort_order) for (const section of sectionsSorted) { const fields = sorted.filter(f => f.form_schema_section_id === section.id) steps.push({ key: `section-${section.id}`, kind: 'section', title: section.name, subtitle: section.description ?? undefined, fields, }) } const loose = sorted.filter(f => f.form_schema_section_id === null) if (loose.length > 0) { steps.push({ key: 'section-loose', kind: 'section', title: 'Overig', fields: loose, }) } } else if (sorted.some(f => f.field_type === FormFieldType.HEADING)) { steps.push(...partitionByHeading(sorted)) } else { steps.push({ key: 'all-fields', kind: 'flat', title: 'Vragen', fields: sorted, }) } steps.push(reviewStep) return steps }) } /** * Returns true when all visible required fields in `step` have a * non-empty value. Hidden fields (failing conditional logic) are * skipped. HEADING/PARAGRAPH fields carry no value and are skipped. */ export function isStepValid( step: FormStep, values: FormValues, submitterValid: boolean, ): boolean { if (step.kind === 'submitter') return submitterValid if (step.kind === 'review') return true for (const field of step.fields) { if (field.field_type === FormFieldType.HEADING || field.field_type === FormFieldType.PARAGRAPH) continue if (!field.is_required) continue if (!evaluateConditionalLogic(field.conditional_logic, values)) continue if (isFieldValueEmpty(values[field.slug])) return false } return true }