Pre-existing breakage on main since WS-5b's validation_rules
canonicalisation renamed max_priorities → max_selected. Component
was migrated; the spec fixtures were not.
Four occurrences in
apps/portal/tests/components/public-form/FieldSectionPriority.spec.ts:
- line 182, 253, 260: max_priorities used in fixture, the
component's max_selected read returned undefined → test
assertions on rendered max-cap behaviour failed (2 tests red)
- line 220: also used max_priorities; test was accidentally
passing because the value (99) was ignored and the component
fell back to HARD_CAP = 5 which happened to match the
"5 / 5" assertion. Now passes via the correct path (99 clamped
to HARD_CAP via Math.min).
No component-side changes. No new test helpers. Pure fixture
key-rename matching ARCH-CONSOLIDATION-ADDENDUM-2026-04-24
WS-5b Uitvoering: "max_priorities → rule_type = max_selected:
semantically equivalent; two enum cases for one semantic = rot."
Pre: 111/113 passing, 2 failing.
Post: 113/113 passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Moves formBuilder types, formValidation, useConditionalLogic, useFormSteps,
and formatFieldValue from apps/portal/src to packages/form-schema/src.
Adds @form-schema path alias to both apps/portal and apps/app.
Vue field components remain per-app to allow independent visual evolution.
Behavior-neutral: all 35 Vitest tests green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Extract formatFieldValue helper for shared use between review step
and confirmation page — one source of truth for TAG_PICKER,
AVAILABILITY_PICKER, and SECTION_PRIORITY display, so the raw-ID
and [object Object] leaks from two parallel stringifiers can't
regress on either side.
- TAG_PICKER: lookup via field.available_tags (server-inlined).
- AVAILABILITY_PICKER: lookup via usePublicFormTimeSlots, strip
seconds. "Laden…" while the cache warms.
- SECTION_PRIORITY: defensive shape-guard prevents [object Object]
leaks, sorted priority-prefixed rendering ("1. Bar, 2. Hospitality").
- Subtle primary-tinted hover (4% primary, primary border) replacing
the near-black Vuetify default overlay on unranked section cards.
- Explicit ghost-class / drag-class / chosen-class on vuedraggable
with solid drag-clone + elevation shadow and a 30%-opacity silhouette
at the origin, so mid-drag text no longer overlaps.
- 17 new formatFieldValue unit assertions + 2 new FieldSectionPriority
assertions locking in the draggable classes and the disabled-card
toggle at max.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- FieldTagPicker: VAutocomplete multiple with grouped category slots,
empty/null category normalised to "Overig", empty-state info alert
when the server delivers no tags.
- FieldAvailabilityPicker: date-grouped checkbox list, festival-aware
via usePublicFormTimeSlots. Event-name subheaders only surface when
the time-slots span multiple events. Time format strips seconds.
- FieldSectionPriority: tap-to-rank + drag-to-reorder via vuedraggable
for desktop; mobile tap-only. Renumbers priorities on every mutation.
Self-heals malformed modelValue. UI soft cap via
validation_rules.max_priorities clamped to the backend hard cap of 5.
- FieldRenderer: three new types removed from isStubbed.
- publicFormInjection: page-level provide/inject for the public token.
- IdentityMatchBanner: prefers backend-provided Dutch copy with
frontend defaults as defensive fallback.
- FormConfirmation wires the banner inline.
- usePublicFormTimeSlots and usePublicFormSections TanStack composables.
- 40 new Vitest assertions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>