feat(timetable): ArtistAdvanceDefault seeder + bootstrap
Seeds 5 default sections per RFC v0.2 D15 (General Info, Contacts, Production, Technical Rider, Hospitality) on a per-organisation artist_advance FormSchema with section_level_submit=true. Each section ships with 3-4 illustrative form_fields; organisations customise via the FormBuilder UI later. Wired into org-creation via the new OrganisationObserver so new tenants receive the schema automatically. Existing orgs get coverage via the new artist:seed-advance-default artisan command (idempotent — orgs that already own a schema are skipped). Note: introduces a new production-grade default-seeder convention. Prior FormBuilder defaults were dev-only via FormBuilderDevSeeder called from DevSeeder::run(). This is the first non-dev path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
181
api/app/FormBuilder/Defaults/ArtistAdvanceDefault.php
Normal file
181
api/app/FormBuilder/Defaults/ArtistAdvanceDefault.php
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\FormBuilder\Defaults;
|
||||
|
||||
use App\Enums\Artist\AdvanceSectionType;
|
||||
use App\Enums\FormBuilder\FormFieldType;
|
||||
use App\Enums\FormBuilder\FormPurpose;
|
||||
use App\Enums\FormBuilder\FormSchemaSnapshotMode;
|
||||
use App\Enums\FormBuilder\FormSubmissionMode;
|
||||
use App\Models\FormBuilder\FormField;
|
||||
use App\Models\FormBuilder\FormSchema;
|
||||
use App\Models\FormBuilder\FormSchemaSection;
|
||||
use App\Models\Organisation;
|
||||
use App\Models\Scopes\OrganisationScope;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Default `artist_advance` FormSchema bootstrap per RFC-TIMETABLE
|
||||
* v0.2 D15. One schema per organisation, with five sections mapped
|
||||
* to AdvanceSectionType:
|
||||
*
|
||||
* - General Info → Custom
|
||||
* - Contacts → Contacts
|
||||
* - Production → Production
|
||||
* - Technical Rider → Production
|
||||
* - Hospitality → Custom
|
||||
*
|
||||
* Each section carries 3-4 illustrative fields. Organisations
|
||||
* customise via the FormBuilder UI later. The schema is published
|
||||
* and uses section_level_submit per ARCH-FORM-BUILDER §3.2.5.
|
||||
*
|
||||
* Idempotent: if an organisation already owns an artist_advance
|
||||
* schema (any one), the seeder no-ops and returns the existing row.
|
||||
*
|
||||
* Bridge to per-engagement AdvanceSection rows: FormSchemaSection
|
||||
* slug matches AdvanceSectionType::value (where applicable). Sections
|
||||
* carrying type=Custom use a stable slug per row name.
|
||||
*/
|
||||
final class ArtistAdvanceDefault
|
||||
{
|
||||
public static function seedFor(Organisation $organisation): FormSchema
|
||||
{
|
||||
$existing = FormSchema::query()
|
||||
->withoutGlobalScope(OrganisationScope::class)
|
||||
->where('organisation_id', $organisation->id)
|
||||
->where('purpose', FormPurpose::ARTIST_ADVANCE->value)
|
||||
->first();
|
||||
|
||||
if ($existing instanceof FormSchema) {
|
||||
return $existing;
|
||||
}
|
||||
|
||||
return DB::transaction(static function () use ($organisation): FormSchema {
|
||||
$schema = FormSchema::create([
|
||||
'organisation_id' => $organisation->id,
|
||||
'owner_type' => 'organisation',
|
||||
'owner_id' => $organisation->id,
|
||||
'name' => 'Artiest advance',
|
||||
'slug' => 'artiest-advance',
|
||||
'purpose' => FormPurpose::ARTIST_ADVANCE->value,
|
||||
'description' => 'Standaard advance-formulier voor artiesten. Pas de secties en velden aan via de FormBuilder.',
|
||||
'is_published' => true,
|
||||
'submission_mode' => FormSubmissionMode::DRAFT_SINGLE->value,
|
||||
'locale' => 'nl',
|
||||
'snapshot_mode' => FormSchemaSnapshotMode::ON_SUBMIT->value,
|
||||
'freeze_on_submit' => false,
|
||||
'section_level_submit' => true,
|
||||
'auto_save_enabled' => true,
|
||||
'version' => 1,
|
||||
]);
|
||||
|
||||
foreach (self::sectionDefinitions() as $sortOrder => $def) {
|
||||
$section = FormSchemaSection::create([
|
||||
'form_schema_id' => $schema->id,
|
||||
'slug' => $def['slug'],
|
||||
'name' => $def['name'],
|
||||
'sort_order' => $sortOrder + 1,
|
||||
'submit_independent' => true,
|
||||
'required_for_schema_submit' => true,
|
||||
]);
|
||||
|
||||
foreach ($def['fields'] as $fieldOrder => $field) {
|
||||
FormField::create([
|
||||
'form_schema_id' => $schema->id,
|
||||
'form_schema_section_id' => $section->id,
|
||||
'field_type' => $field['type']->value,
|
||||
'slug' => $field['slug'],
|
||||
'label' => $field['label'],
|
||||
'help_text' => $field['help_text'] ?? null,
|
||||
'is_required' => $field['is_required'] ?? false,
|
||||
'is_filterable' => false,
|
||||
'is_portal_visible' => true,
|
||||
'is_admin_only' => false,
|
||||
'is_pii' => $field['is_pii'] ?? false,
|
||||
'display_width' => $field['display_width'] ?? 'full',
|
||||
'value_storage_hint' => ($field['type']->recommendedValueStorageHint())->value,
|
||||
'sort_order' => $fieldOrder + 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $schema->refresh();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{
|
||||
* slug: string,
|
||||
* name: string,
|
||||
* advance_type: AdvanceSectionType,
|
||||
* fields: array<int, array{
|
||||
* type: FormFieldType,
|
||||
* slug: string,
|
||||
* label: string,
|
||||
* help_text?: string,
|
||||
* is_required?: bool,
|
||||
* is_pii?: bool,
|
||||
* display_width?: string,
|
||||
* }>
|
||||
* }>
|
||||
*/
|
||||
private static function sectionDefinitions(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'slug' => 'general-info',
|
||||
'name' => 'Algemeen',
|
||||
'advance_type' => AdvanceSectionType::Custom,
|
||||
'fields' => [
|
||||
['type' => FormFieldType::DATETIME, 'slug' => 'arrival-datetime', 'label' => 'Aankomsttijd', 'is_required' => true, 'display_width' => 'half'],
|
||||
['type' => FormFieldType::DATETIME, 'slug' => 'departure-datetime', 'label' => 'Vertrektijd', 'is_required' => true, 'display_width' => 'half'],
|
||||
['type' => FormFieldType::TEXTAREA, 'slug' => 'general-notes', 'label' => 'Opmerkingen'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'slug' => 'contacts',
|
||||
'name' => 'Contactpersonen',
|
||||
'advance_type' => AdvanceSectionType::Contacts,
|
||||
'fields' => [
|
||||
['type' => FormFieldType::TEXT, 'slug' => 'tour-manager-name', 'label' => 'Tour manager', 'is_required' => true, 'is_pii' => true, 'display_width' => 'full'],
|
||||
['type' => FormFieldType::EMAIL, 'slug' => 'tour-manager-email', 'label' => 'E-mail tour manager', 'is_required' => true, 'is_pii' => true, 'display_width' => 'half'],
|
||||
['type' => FormFieldType::PHONE, 'slug' => 'tour-manager-phone', 'label' => 'Telefoon tour manager', 'is_pii' => true, 'display_width' => 'half'],
|
||||
['type' => FormFieldType::TABLE_ROWS, 'slug' => 'additional-contacts', 'label' => 'Aanvullende contactpersonen', 'is_pii' => true],
|
||||
],
|
||||
],
|
||||
[
|
||||
'slug' => 'production',
|
||||
'name' => 'Productie',
|
||||
'advance_type' => AdvanceSectionType::Production,
|
||||
'fields' => [
|
||||
['type' => FormFieldType::FILE_UPLOAD, 'slug' => 'stage-plot', 'label' => 'Stage plot'],
|
||||
['type' => FormFieldType::TEXTAREA, 'slug' => 'monitor-needs', 'label' => 'Monitorwensen'],
|
||||
['type' => FormFieldType::TEXTAREA, 'slug' => 'special-equipment', 'label' => 'Specifieke apparatuur'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'slug' => 'technical-rider',
|
||||
'name' => 'Technische rider',
|
||||
'advance_type' => AdvanceSectionType::Production,
|
||||
'fields' => [
|
||||
['type' => FormFieldType::FILE_UPLOAD, 'slug' => 'input-list', 'label' => 'Input list'],
|
||||
['type' => FormFieldType::TEXTAREA, 'slug' => 'microphone-preferences', 'label' => 'Microfoonvoorkeuren'],
|
||||
['type' => FormFieldType::TEXTAREA, 'slug' => 'backline-requirements', 'label' => 'Backline'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'slug' => 'hospitality',
|
||||
'name' => 'Hospitality',
|
||||
'advance_type' => AdvanceSectionType::Custom,
|
||||
'fields' => [
|
||||
['type' => FormFieldType::TEXTAREA, 'slug' => 'dressing-room-requirements', 'label' => 'Kleedkamer'],
|
||||
['type' => FormFieldType::TEXTAREA, 'slug' => 'food-preferences', 'label' => 'Cateringvoorkeuren'],
|
||||
['type' => FormFieldType::TEXTAREA, 'slug' => 'drinks', 'label' => 'Drankvoorkeuren'],
|
||||
['type' => FormFieldType::TEXT, 'slug' => 'allergies', 'label' => 'Allergieën', 'is_pii' => true],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user