feat: registration form field display_width and option descriptions

Add configurable column widths (full/half) and optional descriptions
for radio/select/checkbox options on registration form fields.

- Migration adds display_width column to both tables
- FieldDisplayWidth enum with smart defaults per field type
- normalized_options accessor for backwards-compatible option format
- Portal form renderer uses display_width for VRow/VCol grid layout
- Radio/select/checkbox options render with descriptions
- Admin field editor supports display_width toggle and description input
- System templates updated with appropriate widths and descriptions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-16 07:46:36 +02:00
parent c4a23b6763
commit 9718e27029
25 changed files with 634 additions and 84 deletions

View File

@@ -1584,7 +1584,7 @@ $effectiveDate = $shift->end_date ?? $shift->timeSlot->date;
| `label` | string | Display label, e.g. "Heb je voedselallergiëen?" |
| `slug` | string(100) | Auto-generated from label, used as stable key |
| `field_type` | enum | `text\|textarea\|select\|multiselect\|checkbox\|radio\|boolean\|number\|tag_picker` |
| `options` | JSON nullable | For select/multiselect/radio/checkbox: array of option strings. NULL for tag_picker (options come from person_tags). JSON OK: opaque config. |
| `options` | JSON nullable | For select/multiselect/radio/checkbox: array of option strings OR option objects `{label, description?}`. NULL for tag_picker (options come from person_tags). JSON OK: opaque config. Both formats accepted; `normalized_options` accessor always returns objects. |
| `tag_category` | string(50) null | Only for tag_picker: filter tags by this category. NULL = show all active tags. |
| `is_required` | bool | Field must be filled in |
| `is_portal_visible`| bool | Shown to person in registration form |
@@ -1593,6 +1593,7 @@ $effectiveDate = $shift->end_date ?? $shift->timeSlot->date;
| `section` | string(100) null | Form section grouping (e.g. "Vergoeding", "Toestemming") |
| `help_text` | text nullable | Explanatory text shown below the field |
| `sort_order` | int | Display order in form |
| `display_width` | string(10) | `full` (default) or `half` — controls form layout width |
| `created_at` | timestamp | |
| `updated_at` | timestamp | |