diff --git a/dev-docs/BACKLOG.md b/dev-docs/BACKLOG.md index cd38ae25..037bda84 100644 --- a/dev-docs/BACKLOG.md +++ b/dev-docs/BACKLOG.md @@ -489,22 +489,6 @@ apps/portal om drift te voorkomen. --- -### TECH-DEBT-01 — @form-schema transitive dep op @core/utils/validators - -**Aanleiding:** `packages/form-schema/src/utils/formValidation.ts` -importeert uit `@core/utils/validators`, dat per-app resolvt naar -`apps/*/src/@core/utils/validators.ts`. Beide app-kopieën zijn op dit -moment identiek (Vuexy template-code), maar drift tussen de twee -zou `@form-schema` per consumer verschillend laten werken. -**Wat:** Kopieer de specifieke validators die `formValidation.ts` -gebruikt naar `packages/form-schema/src/utils/` zodra het package -een tweede echte consumer krijgt (PR-c of later). Ontdekt tijdens -PR-a (commit dda60ed5). -**Prioriteit:** Laag — latent; triggert pas bij consumer-drift of -wanneer het package buiten de huidige Vuexy-twin apps wordt geladen. - ---- - ### ~~TECH-02 — scopeForFestival helper op Event model~~ ✅ OPGELOST --- @@ -517,6 +501,10 @@ wanneer het package buiten de huidige Vuexy-twin apps wordt geladen. --- +### ~~TECH-07 — @form-schema transitive dep op @core/utils/validators~~ ✅ OPGELOST — resolved in PR-a1 + +--- + ## Opgeloste items (april 2026) De volgende items zijn geïmplementeerd en afgerond (673+ tests): diff --git a/dev-docs/dev-guide.md b/dev-docs/dev-guide.md index bf8b1199..0f9e7ac4 100644 --- a/dev-docs/dev-guide.md +++ b/dev-docs/dev-guide.md @@ -381,7 +381,7 @@ Altijd in deze volgorde. Nooit stappen overslaan — later toevoegen kost meer t | Fase 3 — Advancing & Show Day | Artist advancing + portaal, Timetable, Mission Control, Formulierbouwer, Post-festival evaluatie, PDF allocatiesheet, Campagnes (email + WhatsApp via Zender) | | Fase 4 — Differentiators | Real-time WebSockets, Show Day Mode, Vrijwilligersprofiel + festival-paspoort, Shift swap & wachtlijst, Retrospectief rapport, Leveranciersportaal uitgebreid | -## Shared frontend packages +## Gedeelde frontend packages `apps/portal/` en `apps/app/` delen een klein aantal schema-gedreven modules. Deze sectie legt vast wat wel en niet in die gedeelde laag hoort, en hoe je de aliases opzet. diff --git a/packages/form-schema/src/utils/formValidation.ts b/packages/form-schema/src/utils/formValidation.ts index 8594ce53..7bb4fae2 100644 --- a/packages/form-schema/src/utils/formValidation.ts +++ b/packages/form-schema/src/utils/formValidation.ts @@ -1,4 +1,4 @@ -import { emailValidator, regexValidator, requiredValidator, urlValidator } from '@core/utils/validators' +import { emailValidator, regexValidator, requiredValidator, urlValidator } from './validators' import { FormFieldType } from '../types/formBuilder' import type { PublicFormField } from '../types/formBuilder' diff --git a/packages/form-schema/src/utils/validators.ts b/packages/form-schema/src/utils/validators.ts new file mode 100644 index 00000000..158fade5 --- /dev/null +++ b/packages/form-schema/src/utils/validators.ts @@ -0,0 +1,54 @@ +// Pure boolean validators used as gates by formValidation.ts. Error +// messages (in Dutch) are produced by the caller, not by these validators. +// This file deliberately has no external imports — the package must be +// standalone and free of cross-app coupling. + +function isNullOrUndefined(value: unknown): boolean { + return value === null || value === undefined +} + +function isEmptyArray(value: unknown): boolean { + return Array.isArray(value) && value.length === 0 +} + +function isEmpty(value: unknown): boolean { + if (isNullOrUndefined(value)) return true + if (typeof value === 'string') return value.trim() === '' + if (Array.isArray(value)) return value.length === 0 + + return false +} + +export function requiredValidator(value: unknown): boolean { + if (isNullOrUndefined(value) || isEmptyArray(value) || value === false) return false + + return String(value).trim().length > 0 +} + +export function emailValidator(value: unknown): boolean { + if (isEmpty(value)) return true + + const re = /^(?:[^<>()[\]\\.,;:\s@"]+(?:\.[^<>()[\]\\.,;:\s@"]+)*|".+")@(?:\[\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\]|(?:[a-z\-\d]+\.)+[a-z]{2,})$/i + + if (Array.isArray(value)) return value.every(val => re.test(String(val))) + + return re.test(String(value)) +} + +export function urlValidator(value: unknown): boolean { + if (isEmpty(value)) return true + + const re = /^https?:\/\/[^\s$.?#].\S*$/ + + return re.test(String(value)) +} + +export function regexValidator(value: unknown, regex: RegExp | string): boolean { + if (isEmpty(value)) return true + + const regEx = typeof regex === 'string' ? new RegExp(regex) : regex + + if (Array.isArray(value)) return value.every(val => regexValidator(val, regEx)) + + return regEx.test(String(value)) +}