diff --git a/api/app/Enums/FieldDisplayWidth.php b/api/app/Enums/FieldDisplayWidth.php index 4fcfabde..014bcd52 100644 --- a/api/app/Enums/FieldDisplayWidth.php +++ b/api/app/Enums/FieldDisplayWidth.php @@ -32,7 +32,8 @@ enum FieldDisplayWidth: string RegistrationFieldType::RADIO, RegistrationFieldType::CHECKBOX, RegistrationFieldType::MULTISELECT, - RegistrationFieldType::TAG_PICKER => self::FULL, + RegistrationFieldType::TAG_PICKER, + RegistrationFieldType::HEADING => self::FULL, }; } } diff --git a/api/app/Enums/RegistrationFieldType.php b/api/app/Enums/RegistrationFieldType.php index 64ce3df2..819b393c 100644 --- a/api/app/Enums/RegistrationFieldType.php +++ b/api/app/Enums/RegistrationFieldType.php @@ -15,6 +15,7 @@ enum RegistrationFieldType: string case BOOLEAN = 'boolean'; case NUMBER = 'number'; case TAG_PICKER = 'tag_picker'; + case HEADING = 'heading'; public function isMultiValue(): bool { @@ -28,6 +29,11 @@ enum RegistrationFieldType: string public function prohibitsOptions(): bool { - return in_array($this, [self::TEXT, self::TEXTAREA, self::BOOLEAN, self::NUMBER, self::TAG_PICKER], true); + return in_array($this, [self::TEXT, self::TEXTAREA, self::BOOLEAN, self::NUMBER, self::TAG_PICKER, self::HEADING], true); + } + + public function isStructural(): bool + { + return $this === self::HEADING; } } diff --git a/api/app/Http/Controllers/Api/V1/PublicRegistrationDataController.php b/api/app/Http/Controllers/Api/V1/PublicRegistrationDataController.php index 06ac0c21..f2467204 100644 --- a/api/app/Http/Controllers/Api/V1/PublicRegistrationDataController.php +++ b/api/app/Http/Controllers/Api/V1/PublicRegistrationDataController.php @@ -100,7 +100,6 @@ final class PublicRegistrationDataController extends Controller 'normalized_options' => $field->normalized_options, 'tag_category' => $field->tag_category, 'is_required' => $field->is_required, - 'section' => $field->section, 'help_text' => $field->help_text, 'display_width' => $field->display_width->value, ]; diff --git a/api/app/Http/Requests/Api/V1/StoreRegistrationFieldTemplateRequest.php b/api/app/Http/Requests/Api/V1/StoreRegistrationFieldTemplateRequest.php index ef319c8c..747b9a10 100644 --- a/api/app/Http/Requests/Api/V1/StoreRegistrationFieldTemplateRequest.php +++ b/api/app/Http/Requests/Api/V1/StoreRegistrationFieldTemplateRequest.php @@ -22,9 +22,19 @@ final class StoreRegistrationFieldTemplateRequest extends FormRequest $fieldType = $this->input('field_type'); $type = RegistrationFieldType::tryFrom($fieldType); - return [ + $rules = [ 'label' => ['required', 'string', 'max:255'], 'field_type' => ['required', Rule::in(array_column(RegistrationFieldType::cases(), 'value'))], + 'help_text' => ['nullable', 'string', 'max:5000'], + 'sort_order' => ['nullable', 'integer', 'min:0'], + 'display_width' => ['sometimes', Rule::in(array_column(FieldDisplayWidth::cases(), 'value'))], + ]; + + if ($type?->isStructural()) { + return $rules; + } + + return array_merge($rules, [ 'options' => [ $type?->requiresOptions() ? 'required' : 'nullable', $type?->prohibitsOptions() ? 'prohibited' : 'nullable', @@ -42,10 +52,6 @@ final class StoreRegistrationFieldTemplateRequest extends FormRequest 'is_filterable' => ['nullable', 'boolean'], 'is_portal_visible' => ['nullable', 'boolean'], 'is_admin_only' => ['nullable', 'boolean'], - 'section' => ['nullable', 'string', 'max:100'], - 'help_text' => ['nullable', 'string', 'max:5000'], - 'sort_order' => ['nullable', 'integer', 'min:0'], - 'display_width' => ['sometimes', Rule::in(array_column(FieldDisplayWidth::cases(), 'value'))], - ]; + ]); } } diff --git a/api/app/Http/Requests/Api/V1/StoreRegistrationFormFieldRequest.php b/api/app/Http/Requests/Api/V1/StoreRegistrationFormFieldRequest.php index d17e26d3..43aad059 100644 --- a/api/app/Http/Requests/Api/V1/StoreRegistrationFormFieldRequest.php +++ b/api/app/Http/Requests/Api/V1/StoreRegistrationFormFieldRequest.php @@ -22,9 +22,19 @@ final class StoreRegistrationFormFieldRequest extends FormRequest $fieldType = $this->input('field_type'); $type = RegistrationFieldType::tryFrom($fieldType); - return [ + $rules = [ 'label' => ['required', 'string', 'max:255'], 'field_type' => ['required', Rule::in(array_column(RegistrationFieldType::cases(), 'value'))], + 'help_text' => ['nullable', 'string', 'max:5000'], + 'sort_order' => ['nullable', 'integer', 'min:0'], + 'display_width' => ['sometimes', Rule::in(array_column(FieldDisplayWidth::cases(), 'value'))], + ]; + + if ($type?->isStructural()) { + return $rules; + } + + return array_merge($rules, [ 'options' => [ $type?->requiresOptions() ? 'required' : 'nullable', $type?->prohibitsOptions() ? 'prohibited' : 'nullable', @@ -42,10 +52,6 @@ final class StoreRegistrationFormFieldRequest extends FormRequest 'is_portal_visible' => ['nullable', 'boolean'], 'is_admin_only' => ['nullable', 'boolean'], 'is_filterable' => ['nullable', 'boolean'], - 'section' => ['nullable', 'string', 'max:100'], - 'help_text' => ['nullable', 'string', 'max:5000'], - 'sort_order' => ['nullable', 'integer', 'min:0'], - 'display_width' => ['sometimes', Rule::in(array_column(FieldDisplayWidth::cases(), 'value'))], - ]; + ]); } } diff --git a/api/app/Http/Requests/Api/V1/UpdateRegistrationFieldTemplateRequest.php b/api/app/Http/Requests/Api/V1/UpdateRegistrationFieldTemplateRequest.php index bac5249c..af0d85a9 100644 --- a/api/app/Http/Requests/Api/V1/UpdateRegistrationFieldTemplateRequest.php +++ b/api/app/Http/Requests/Api/V1/UpdateRegistrationFieldTemplateRequest.php @@ -30,7 +30,6 @@ final class UpdateRegistrationFieldTemplateRequest extends FormRequest 'is_filterable' => ['nullable', 'boolean'], 'is_portal_visible' => ['nullable', 'boolean'], 'is_admin_only' => ['nullable', 'boolean'], - 'section' => ['nullable', 'string', 'max:100'], 'help_text' => ['nullable', 'string', 'max:5000'], 'sort_order' => ['nullable', 'integer', 'min:0'], 'display_width' => ['sometimes', Rule::in(array_column(FieldDisplayWidth::cases(), 'value'))], diff --git a/api/app/Http/Requests/Api/V1/UpdateRegistrationFormFieldRequest.php b/api/app/Http/Requests/Api/V1/UpdateRegistrationFormFieldRequest.php index 3e8339ea..adaa48e6 100644 --- a/api/app/Http/Requests/Api/V1/UpdateRegistrationFormFieldRequest.php +++ b/api/app/Http/Requests/Api/V1/UpdateRegistrationFormFieldRequest.php @@ -29,7 +29,6 @@ final class UpdateRegistrationFormFieldRequest extends FormRequest 'is_portal_visible' => ['nullable', 'boolean'], 'is_admin_only' => ['nullable', 'boolean'], 'is_filterable' => ['nullable', 'boolean'], - 'section' => ['nullable', 'string', 'max:100'], 'help_text' => ['nullable', 'string', 'max:5000'], 'sort_order' => ['nullable', 'integer', 'min:0'], 'display_width' => ['sometimes', Rule::in(array_column(FieldDisplayWidth::cases(), 'value'))], diff --git a/api/app/Http/Resources/Api/V1/RegistrationFieldTemplateResource.php b/api/app/Http/Resources/Api/V1/RegistrationFieldTemplateResource.php index f35ff6c6..fd880671 100644 --- a/api/app/Http/Resources/Api/V1/RegistrationFieldTemplateResource.php +++ b/api/app/Http/Resources/Api/V1/RegistrationFieldTemplateResource.php @@ -24,7 +24,6 @@ final class RegistrationFieldTemplateResource extends JsonResource 'is_filterable' => $this->is_filterable, 'is_portal_visible' => $this->is_portal_visible, 'is_admin_only' => $this->is_admin_only, - 'section' => $this->section, 'help_text' => $this->help_text, 'sort_order' => $this->sort_order, 'display_width' => $this->display_width->value, diff --git a/api/app/Http/Resources/Api/V1/RegistrationFormFieldResource.php b/api/app/Http/Resources/Api/V1/RegistrationFormFieldResource.php index c7fac0d5..50a51e58 100644 --- a/api/app/Http/Resources/Api/V1/RegistrationFormFieldResource.php +++ b/api/app/Http/Resources/Api/V1/RegistrationFormFieldResource.php @@ -26,7 +26,6 @@ final class RegistrationFormFieldResource extends JsonResource 'is_portal_visible' => $this->is_portal_visible, 'is_admin_only' => $this->is_admin_only, 'is_filterable' => $this->is_filterable, - 'section' => $this->section, 'help_text' => $this->help_text, 'sort_order' => $this->sort_order, 'display_width' => $this->display_width->value, diff --git a/api/app/Models/RegistrationFieldTemplate.php b/api/app/Models/RegistrationFieldTemplate.php index cb822edf..3d82820b 100644 --- a/api/app/Models/RegistrationFieldTemplate.php +++ b/api/app/Models/RegistrationFieldTemplate.php @@ -34,7 +34,6 @@ final class RegistrationFieldTemplate extends Model 'is_filterable', 'is_portal_visible', 'is_admin_only', - 'section', 'help_text', 'sort_order', 'display_width', diff --git a/api/app/Models/RegistrationFormField.php b/api/app/Models/RegistrationFormField.php index f4e4e0aa..372dc5ad 100644 --- a/api/app/Models/RegistrationFormField.php +++ b/api/app/Models/RegistrationFormField.php @@ -38,7 +38,6 @@ final class RegistrationFormField extends Model 'is_portal_visible', 'is_admin_only', 'is_filterable', - 'section', 'help_text', 'sort_order', 'display_width', diff --git a/api/app/Services/RegistrationFieldTemplateService.php b/api/app/Services/RegistrationFieldTemplateService.php index 3cfe2ed3..ef23ebf7 100644 --- a/api/app/Services/RegistrationFieldTemplateService.php +++ b/api/app/Services/RegistrationFieldTemplateService.php @@ -115,7 +115,6 @@ final class RegistrationFieldTemplateService 'is_portal_visible' => $template->is_portal_visible, 'is_admin_only' => $template->is_admin_only, 'is_filterable' => $template->is_filterable, - 'section' => $template->section, 'help_text' => $template->help_text, 'sort_order' => $maxOrder + 1, 'display_width' => $template->display_width, @@ -140,21 +139,26 @@ final class RegistrationFieldTemplateService public static function seedSystemTemplates(Organisation $organisation): void { $templates = [ - ['label' => 'Shirtmaat', 'field_type' => 'select', 'options' => ['XS', 'S', 'M', 'L', 'XL', 'XXL', 'XXXL'], 'is_filterable' => true, 'display_width' => 'half', 'sort_order' => 1], - ['label' => 'Dieetwensen', 'field_type' => 'multiselect', 'options' => ['Vegetarisch', 'Veganistisch', 'Halal', 'Glutenvrij', 'Lactosevrij', 'Geen pinda\'s', 'Geen noten'], 'is_filterable' => true, 'display_width' => 'full', 'sort_order' => 2], - ['label' => 'Vergoeding', 'field_type' => 'radio', 'options' => [ + ['label' => 'Persoonlijke voorkeuren', 'field_type' => 'heading', 'help_text' => 'Vertel ons wat we over jou moeten weten', 'display_width' => 'full', 'sort_order' => 1], + ['label' => 'Shirtmaat', 'field_type' => 'select', 'options' => ['XS', 'S', 'M', 'L', 'XL', 'XXL', 'XXXL'], 'is_filterable' => true, 'display_width' => 'half', 'sort_order' => 2], + ['label' => 'Dieetwensen', 'field_type' => 'multiselect', 'options' => ['Vegetarisch', 'Veganistisch', 'Halal', 'Glutenvrij', 'Lactosevrij', 'Geen pinda\'s', 'Geen noten'], 'is_filterable' => true, 'display_width' => 'half', 'sort_order' => 3], + ['label' => 'Vergoeding', 'field_type' => 'heading', 'help_text' => 'Kies hoe je wilt worden bedankt voor je inzet', 'display_width' => 'full', 'sort_order' => 4], + ['label' => 'Vergoedingstype', 'field_type' => 'radio', 'options' => [ ['label' => 'Pro Deo', 'description' => 'Je werkt als vrijwilliger zonder financiële vergoeding'], ['label' => 'Entreeticket', 'description' => 'Je ontvangt een gratis festivalticket als dank voor je inzet'], ['label' => 'Vrijwilligersvergoeding', 'description' => 'Je ontvangt een vergoeding conform de vrijwilligersregeling'], - ], 'section' => 'Vergoeding', 'display_width' => 'full', 'sort_order' => 3], - ['label' => 'Toestemming gegevensverwerking', 'field_type' => 'boolean', 'is_required' => true, 'section' => 'Toestemming', 'help_text' => 'Ik geef toestemming voor de verwerking van mijn persoonsgegevens ten behoeve van de organisatie van dit evenement, conform de Algemene Verordening Gegevensbescherming (AVG).', 'display_width' => 'full', 'sort_order' => 4], - ['label' => 'Noodcontact naam', 'field_type' => 'text', 'section' => 'Noodcontact', 'display_width' => 'half', 'sort_order' => 5], - ['label' => 'Noodcontact telefoon', 'field_type' => 'text', 'section' => 'Noodcontact', 'display_width' => 'half', 'sort_order' => 6], - ['label' => 'EHBO / BHV diploma', 'field_type' => 'boolean', 'is_filterable' => true, 'display_width' => 'half', 'sort_order' => 7], - ['label' => 'Rijbewijs', 'field_type' => 'boolean', 'is_filterable' => true, 'display_width' => 'half', 'sort_order' => 8], - ['label' => 'Eerder vrijwilliger geweest', 'field_type' => 'boolean', 'is_filterable' => true, 'display_width' => 'half', 'sort_order' => 9], - ['label' => 'Certificaten & vaardigheden', 'field_type' => 'tag_picker', 'tag_category' => null, 'is_filterable' => true, 'display_width' => 'full', 'sort_order' => 10], - ['label' => 'Opmerkingen', 'field_type' => 'textarea', 'display_width' => 'full', 'sort_order' => 11], + ], 'is_required' => true, 'display_width' => 'full', 'sort_order' => 5], + ['label' => 'Noodcontact', 'field_type' => 'heading', 'help_text' => 'Wie kunnen we bereiken bij calamiteiten?', 'display_width' => 'full', 'sort_order' => 6], + ['label' => 'Noodcontact naam', 'field_type' => 'text', 'display_width' => 'half', 'sort_order' => 7], + ['label' => 'Noodcontact telefoon', 'field_type' => 'text', 'display_width' => 'half', 'sort_order' => 8], + ['label' => 'Ervaring & vaardigheden', 'field_type' => 'heading', 'help_text' => 'Welke diploma\'s en skills heb je?', 'display_width' => 'full', 'sort_order' => 9], + ['label' => 'EHBO / BHV diploma', 'field_type' => 'boolean', 'is_filterable' => true, 'display_width' => 'half', 'sort_order' => 10], + ['label' => 'Rijbewijs', 'field_type' => 'boolean', 'is_filterable' => true, 'display_width' => 'half', 'sort_order' => 11], + ['label' => 'Eerder vrijwilliger geweest', 'field_type' => 'boolean', 'is_filterable' => true, 'display_width' => 'half', 'sort_order' => 12], + ['label' => 'Certificaten & vaardigheden', 'field_type' => 'tag_picker', 'tag_category' => null, 'is_filterable' => true, 'display_width' => 'full', 'sort_order' => 13], + ['label' => 'Aanvullende informatie', 'field_type' => 'heading', 'display_width' => 'full', 'sort_order' => 14], + ['label' => 'Toestemming gegevensverwerking', 'field_type' => 'boolean', 'is_required' => true, 'help_text' => 'Ik geef toestemming voor de verwerking van mijn persoonsgegevens ten behoeve van de organisatie van dit evenement, conform de Algemene Verordening Gegevensbescherming (AVG).', 'display_width' => 'full', 'sort_order' => 15], + ['label' => 'Opmerkingen', 'field_type' => 'textarea', 'display_width' => 'full', 'sort_order' => 16], ]; foreach ($templates as $data) { diff --git a/api/app/Services/RegistrationFormFieldService.php b/api/app/Services/RegistrationFormFieldService.php index 5d2fffe6..8188a439 100644 --- a/api/app/Services/RegistrationFormFieldService.php +++ b/api/app/Services/RegistrationFormFieldService.php @@ -111,7 +111,7 @@ final class RegistrationFormFieldService DB::transaction(function () use ($person, $values, $fields): void { foreach ($values as $slug => $rawValue) { $field = $fields->get($slug); - if ($field === null) { + if ($field === null || $field->field_type->isStructural()) { continue; } @@ -176,7 +176,6 @@ final class RegistrationFormFieldService 'is_portal_visible' => $sourceField->is_portal_visible, 'is_admin_only' => $sourceField->is_admin_only, 'is_filterable' => $sourceField->is_filterable, - 'section' => $sourceField->section, 'help_text' => $sourceField->help_text, 'sort_order' => ++$maxOrder, 'display_width' => $sourceField->display_width, diff --git a/api/database/factories/RegistrationFieldTemplateFactory.php b/api/database/factories/RegistrationFieldTemplateFactory.php index fe2a768a..256f1619 100644 --- a/api/database/factories/RegistrationFieldTemplateFactory.php +++ b/api/database/factories/RegistrationFieldTemplateFactory.php @@ -32,7 +32,6 @@ final class RegistrationFieldTemplateFactory extends Factory 'is_filterable' => false, 'is_portal_visible' => true, 'is_admin_only' => false, - 'section' => null, 'help_text' => null, 'sort_order' => fake()->numberBetween(0, 20), 'display_width' => FieldDisplayWidth::FULL, @@ -84,4 +83,15 @@ final class RegistrationFieldTemplateFactory extends Factory 'display_width' => FieldDisplayWidth::FULL, ]); } + + public function headingField(): static + { + return $this->state(fn () => [ + 'label' => 'Persoonlijke voorkeuren', + 'slug' => 'persoonlijke-voorkeuren', + 'field_type' => RegistrationFieldType::HEADING, + 'help_text' => 'Vertel ons wat we over jou moeten weten', + 'display_width' => FieldDisplayWidth::FULL, + ]); + } } diff --git a/api/database/factories/RegistrationFormFieldFactory.php b/api/database/factories/RegistrationFormFieldFactory.php index 2dc591ec..9a00c82f 100644 --- a/api/database/factories/RegistrationFormFieldFactory.php +++ b/api/database/factories/RegistrationFormFieldFactory.php @@ -32,7 +32,6 @@ final class RegistrationFormFieldFactory extends Factory 'is_portal_visible' => true, 'is_admin_only' => false, 'is_filterable' => false, - 'section' => null, 'help_text' => null, 'sort_order' => fake()->numberBetween(0, 20), 'display_width' => FieldDisplayWidth::FULL, @@ -45,7 +44,6 @@ final class RegistrationFormFieldFactory extends Factory 'label' => 'Noodcontact naam', 'slug' => 'noodcontact-naam', 'field_type' => RegistrationFieldType::TEXT, - 'section' => 'Noodcontact', 'display_width' => FieldDisplayWidth::HALF, ]); } @@ -81,7 +79,6 @@ final class RegistrationFormFieldFactory extends Factory 'slug' => 'toestemming-gegevensverwerking', 'field_type' => RegistrationFieldType::BOOLEAN, 'is_required' => true, - 'section' => 'Toestemming', 'help_text' => 'Ik geef toestemming voor de verwerking van mijn persoonsgegevens conform de AVG.', 'display_width' => FieldDisplayWidth::FULL, ]); @@ -109,7 +106,6 @@ final class RegistrationFormFieldFactory extends Factory ['label' => 'Entreeticket', 'description' => 'Je ontvangt een gratis festivalticket als dank voor je inzet'], ['label' => 'Vrijwilligersvergoeding', 'description' => 'Je ontvangt een vergoeding conform de vrijwilligersregeling'], ], - 'section' => 'Vergoeding', 'display_width' => FieldDisplayWidth::FULL, ]); } @@ -123,4 +119,15 @@ final class RegistrationFormFieldFactory extends Factory 'display_width' => FieldDisplayWidth::FULL, ]); } + + public function headingField(): static + { + return $this->state(fn () => [ + 'label' => 'Persoonlijke voorkeuren', + 'slug' => 'persoonlijke-voorkeuren', + 'field_type' => RegistrationFieldType::HEADING, + 'help_text' => 'Vertel ons wat we over jou moeten weten', + 'display_width' => FieldDisplayWidth::FULL, + ]); + } } diff --git a/api/database/migrations/2026_04_16_142626_remove_section_from_registration_fields_tables.php b/api/database/migrations/2026_04_16_142626_remove_section_from_registration_fields_tables.php new file mode 100644 index 00000000..3f587f23 --- /dev/null +++ b/api/database/migrations/2026_04_16_142626_remove_section_from_registration_fields_tables.php @@ -0,0 +1,32 @@ +dropColumn('section'); + }); + + Schema::table('registration_form_fields', function (Blueprint $table) { + $table->dropColumn('section'); + }); + } + + public function down(): void + { + Schema::table('registration_field_templates', function (Blueprint $table) { + $table->string('section', 100)->nullable()->after('is_admin_only'); + }); + + Schema::table('registration_form_fields', function (Blueprint $table) { + $table->string('section', 100)->nullable()->after('is_filterable'); + }); + } +}; diff --git a/api/database/seeders/DevSeeder.php b/api/database/seeders/DevSeeder.php index 1edc59ac..a029b033 100644 --- a/api/database/seeders/DevSeeder.php +++ b/api/database/seeders/DevSeeder.php @@ -156,7 +156,7 @@ class DevSeeder extends Seeder \App\Services\RegistrationFieldTemplateService::seedSystemTemplates($this->org); - $this->command->info(' Organisation, 8 users, 6 companies, 7 crowd types, 10 person tags, 11 registration templates created'); + $this->command->info(' Organisation, 8 users, 6 companies, 7 crowd types, 10 person tags, 16 registration templates created'); }); } diff --git a/api/tests/Feature/RegistrationFieldTemplate/RegistrationFieldTemplateTest.php b/api/tests/Feature/RegistrationFieldTemplate/RegistrationFieldTemplateTest.php index 037a6086..1560973f 100644 --- a/api/tests/Feature/RegistrationFieldTemplate/RegistrationFieldTemplateTest.php +++ b/api/tests/Feature/RegistrationFieldTemplate/RegistrationFieldTemplateTest.php @@ -152,7 +152,7 @@ class RegistrationFieldTemplateTest extends TestCase ->where('is_system', true) ->count(); - $this->assertEquals(11, $count); + $this->assertEquals(16, $count); } public function test_create_field_from_template(): void diff --git a/api/tests/Feature/RegistrationFormField/RegistrationFormFieldTest.php b/api/tests/Feature/RegistrationFormField/RegistrationFormFieldTest.php index f0873c10..88c4230b 100644 --- a/api/tests/Feature/RegistrationFormField/RegistrationFormFieldTest.php +++ b/api/tests/Feature/RegistrationFormField/RegistrationFormFieldTest.php @@ -492,4 +492,58 @@ class RegistrationFormFieldTest extends TestCase $this->assertEquals('full', $field['display_width']); $this->assertNotNull($field['normalized_options']); } + + public function test_store_heading_field_without_options(): void + { + Sanctum::actingAs($this->orgAdmin); + + $response = $this->postJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/registration-fields", [ + 'label' => 'Persoonlijke voorkeuren', + 'field_type' => 'heading', + 'help_text' => 'Vertel ons wat we over jou moeten weten', + ]); + + $response->assertCreated() + ->assertJsonPath('data.field_type', 'heading') + ->assertJsonPath('data.label', 'Persoonlijke voorkeuren') + ->assertJsonPath('data.help_text', 'Vertel ons wat we over jou moeten weten') + ->assertJsonPath('data.display_width', 'full'); + } + + public function test_heading_field_skips_options_validation(): void + { + Sanctum::actingAs($this->orgAdmin); + + $response = $this->postJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/registration-fields", [ + 'label' => 'Sectiekop', + 'field_type' => 'heading', + ]); + + $response->assertCreated(); + } + + public function test_heading_field_display_width_defaults_to_full(): void + { + Sanctum::actingAs($this->orgAdmin); + + $response = $this->postJson("/api/v1/organisations/{$this->organisation->id}/events/{$this->event->id}/registration-fields", [ + 'label' => 'Noodcontact', + 'field_type' => 'heading', + ]); + + $response->assertCreated() + ->assertJsonPath('data.display_width', 'full'); + } + + public function test_seeder_creates_heading_fields(): void + { + \App\Services\RegistrationFieldTemplateService::seedSystemTemplates($this->organisation); + + $headingCount = \App\Models\RegistrationFieldTemplate::where('organisation_id', $this->organisation->id) + ->where('is_system', true) + ->where('field_type', 'heading') + ->count(); + + $this->assertEquals(5, $headingCount); + } } diff --git a/apps/app/src/components/event/RegistrationFieldCard.vue b/apps/app/src/components/event/RegistrationFieldCard.vue index e3066d26..c2550ede 100644 --- a/apps/app/src/components/event/RegistrationFieldCard.vue +++ b/apps/app/src/components/event/RegistrationFieldCard.vue @@ -21,7 +21,55 @@ function formatOptions(options: NormalizedOption[] | null): string { - -