zu fabricjs gewechselt, noch nicht funktionsfähig
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (Schema::hasTable('task_collections') && ! Schema::hasColumn('task_collections', 'source_collection_id')) {
|
||||
Schema::table('task_collections', function (Blueprint $table) {
|
||||
$table->foreignId('source_collection_id')
|
||||
->nullable()
|
||||
->after('event_type_id')
|
||||
->constrained('task_collections')
|
||||
->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
if (Schema::hasTable('tasks')) {
|
||||
Schema::table('tasks', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('tasks', 'source_collection_id')) {
|
||||
$table->foreignId('source_collection_id')
|
||||
->nullable()
|
||||
->after('collection_id')
|
||||
->constrained('task_collections')
|
||||
->nullOnDelete();
|
||||
}
|
||||
|
||||
if (! Schema::hasColumn('tasks', 'source_task_id')) {
|
||||
$table->foreignId('source_task_id')
|
||||
->nullable()
|
||||
->after('tenant_id')
|
||||
->constrained('tasks')
|
||||
->nullOnDelete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
if (Schema::hasTable('tasks')) {
|
||||
Schema::table('tasks', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('tasks', 'source_collection_id')) {
|
||||
$table->dropForeign(['source_collection_id']);
|
||||
$table->dropColumn('source_collection_id');
|
||||
}
|
||||
|
||||
if (Schema::hasColumn('tasks', 'source_task_id')) {
|
||||
$table->dropForeign(['source_task_id']);
|
||||
$table->dropColumn('source_task_id');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (Schema::hasTable('task_collections') && Schema::hasColumn('task_collections', 'source_collection_id')) {
|
||||
Schema::table('task_collections', function (Blueprint $table) {
|
||||
$table->dropForeign(['source_collection_id']);
|
||||
$table->dropColumn('source_collection_id');
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -24,7 +24,7 @@ class InviteLayoutSeeder extends Seeder
|
||||
'text' => $layout['text'] ?? null,
|
||||
'badge' => $layout['badge'] ?? null,
|
||||
'qr' => $layout['qr'] ?? ['size_px' => 500],
|
||||
'svg' => $layout['svg'] ?? ['width' => 1080, 'height' => 1520],
|
||||
'svg' => $layout['svg'] ?? ['width' => 1240, 'height' => 1754],
|
||||
];
|
||||
|
||||
$options = [
|
||||
@@ -35,7 +35,7 @@ class InviteLayoutSeeder extends Seeder
|
||||
'cta_caption' => $layout['cta_caption'] ?? 'Scan mich & starte direkt',
|
||||
'link_label' => $layout['link_label'] ?? null,
|
||||
'logo_url' => $layout['logo_url'] ?? null,
|
||||
'formats' => $layout['formats'] ?? ['pdf', 'svg'],
|
||||
'formats' => $layout['formats'] ?? ['pdf', 'png'],
|
||||
];
|
||||
|
||||
InviteLayout::updateOrCreate(
|
||||
|
||||
@@ -7,250 +7,546 @@ use App\Models\EventType;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskCollection;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class TaskCollectionsSeeder extends Seeder
|
||||
{
|
||||
private const MIN_TASKS_PER_EVENT_TYPE = 50;
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$collections = [
|
||||
[
|
||||
'slug' => 'wedding-classics',
|
||||
'event_type' => [
|
||||
'slug' => 'wedding',
|
||||
'name' => [
|
||||
'de' => 'Hochzeit',
|
||||
'en' => 'Wedding',
|
||||
],
|
||||
'icon' => 'lucide-heart',
|
||||
],
|
||||
'name' => [
|
||||
'de' => 'Hochzeitsklassiker',
|
||||
'en' => 'Wedding Classics',
|
||||
],
|
||||
'description' => [
|
||||
'de' => 'Kuratierte Aufgaben rund um Trauung, Emotionen und besondere Momente.',
|
||||
'en' => 'Curated prompts for vows, emotions, and memorable wedding highlights.',
|
||||
],
|
||||
'is_default' => true,
|
||||
'position' => 10,
|
||||
'tasks' => [
|
||||
[
|
||||
'slug' => 'wedding-first-look',
|
||||
'title' => [
|
||||
'de' => 'Erster Blick des Brautpaares festhalten',
|
||||
'en' => 'Capture the couple’s first look',
|
||||
],
|
||||
'description' => [
|
||||
'de' => 'Halte den Moment fest, in dem sich Braut und Bräutigam zum ersten Mal sehen.',
|
||||
'en' => 'Capture the moment when the bride and groom see each other for the first time.',
|
||||
],
|
||||
'example' => [
|
||||
'de' => 'Fotografiere die Reaktionen aus verschiedenen Blickwinkeln.',
|
||||
'en' => 'Photograph their reactions from different angles.',
|
||||
],
|
||||
'emotion' => [
|
||||
'name' => [
|
||||
'de' => 'Romantik',
|
||||
'en' => 'Romance',
|
||||
],
|
||||
'icon' => 'lucide-heart',
|
||||
'color' => '#ec4899',
|
||||
'sort_order' => 10,
|
||||
],
|
||||
'difficulty' => 'easy',
|
||||
'sort_order' => 10,
|
||||
],
|
||||
[
|
||||
'slug' => 'wedding-family-hug',
|
||||
'title' => [
|
||||
'de' => 'Familienumarmung organisieren',
|
||||
'en' => 'Organise a family group hug',
|
||||
],
|
||||
'description' => [
|
||||
'de' => 'Bitte die wichtigsten Menschen, das Paar gleichzeitig zu umarmen.',
|
||||
'en' => 'Ask the closest friends and family to hug the couple at the same time.',
|
||||
],
|
||||
'example' => [
|
||||
'de' => 'Kombiniere die Umarmung mit einem Toast.',
|
||||
'en' => 'Combine the hug with a heartfelt toast.',
|
||||
],
|
||||
'emotion' => [
|
||||
'name' => [
|
||||
'de' => 'Freude',
|
||||
'en' => 'Joy',
|
||||
],
|
||||
'icon' => 'lucide-smile',
|
||||
'color' => '#f59e0b',
|
||||
'sort_order' => 20,
|
||||
],
|
||||
'difficulty' => 'medium',
|
||||
'sort_order' => 20,
|
||||
],
|
||||
[
|
||||
'slug' => 'wedding-midnight-sparkler',
|
||||
'title' => [
|
||||
'de' => 'Mitternachtsfunkeln mit Wunderkerzen',
|
||||
'en' => 'Midnight sparkler moment',
|
||||
],
|
||||
'description' => [
|
||||
'de' => 'Verteile Wunderkerzen und schafft ein leuchtendes Spalier für das Paar.',
|
||||
'en' => 'Hand out sparklers and form a glowing aisle for the couple.',
|
||||
],
|
||||
'example' => [
|
||||
'de' => 'Koordiniere die Musik und kündige den Countdown an.',
|
||||
'en' => 'Coordinate music and announce a countdown.',
|
||||
],
|
||||
'emotion' => [
|
||||
'name' => [
|
||||
'de' => 'Ekstase',
|
||||
'en' => 'Euphoria',
|
||||
],
|
||||
'icon' => 'lucide-stars',
|
||||
'color' => '#6366f1',
|
||||
'sort_order' => 30,
|
||||
],
|
||||
'difficulty' => 'medium',
|
||||
'sort_order' => 30,
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'slug' => 'birthday-celebration',
|
||||
'event_type' => [
|
||||
'slug' => 'birthday',
|
||||
'name' => [
|
||||
'de' => 'Geburtstag',
|
||||
'en' => 'Birthday',
|
||||
],
|
||||
'icon' => 'lucide-cake',
|
||||
],
|
||||
'name' => [
|
||||
'de' => 'Geburtstags-Highlights',
|
||||
'en' => 'Birthday Highlights',
|
||||
],
|
||||
'description' => [
|
||||
'de' => 'Aufgaben für Überraschungen, Gratulationen und gemeinsames Feiern.',
|
||||
'en' => 'Prompts covering surprises, wishes, and shared celebrations.',
|
||||
],
|
||||
'is_default' => false,
|
||||
'position' => 20,
|
||||
'tasks' => [
|
||||
[
|
||||
'slug' => 'birthday-surprise-wall',
|
||||
'title' => [
|
||||
'de' => 'Überraschungswand mit Polaroids gestalten',
|
||||
'en' => 'Create a surprise wall filled with instant photos',
|
||||
],
|
||||
'description' => [
|
||||
'de' => 'Sammle Schnappschüsse der Gäste und befestige sie als Fotowand.',
|
||||
'en' => 'Collect snapshots from guests and mount them on a photo wall.',
|
||||
],
|
||||
'example' => [
|
||||
'de' => 'Schreibe zu jedem Bild einen kurzen Gruß.',
|
||||
'en' => 'Add a short message to each picture.',
|
||||
],
|
||||
'emotion' => [
|
||||
'name' => [
|
||||
'de' => 'Nostalgie',
|
||||
'en' => 'Nostalgia',
|
||||
],
|
||||
'icon' => 'lucide-images',
|
||||
'color' => '#f97316',
|
||||
'sort_order' => 40,
|
||||
],
|
||||
'difficulty' => 'easy',
|
||||
'sort_order' => 10,
|
||||
],
|
||||
[
|
||||
'slug' => 'birthday-toast-circle',
|
||||
'title' => [
|
||||
'de' => 'Gratulationskreis mit kurzen Toasts',
|
||||
'en' => 'Circle of toasts',
|
||||
],
|
||||
'description' => [
|
||||
'de' => 'Bildet einen Kreis und bittet jede Person um einen 10-Sekunden-Toast.',
|
||||
'en' => 'Form a circle and ask everyone for a 10-second toast.',
|
||||
],
|
||||
'example' => [
|
||||
'de' => 'Nimm die Reaktionen als Video auf.',
|
||||
'en' => 'Record the reactions on video.',
|
||||
],
|
||||
'emotion' => [
|
||||
'name' => [
|
||||
'de' => 'Dankbarkeit',
|
||||
'en' => 'Gratitude',
|
||||
],
|
||||
'icon' => 'lucide-hands',
|
||||
'color' => '#22c55e',
|
||||
'sort_order' => 50,
|
||||
],
|
||||
'difficulty' => 'easy',
|
||||
'sort_order' => 20,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$definitions = $this->definitions();
|
||||
$collectionMap = [];
|
||||
|
||||
DB::transaction(function () use ($collections) {
|
||||
foreach ($collections as $definition) {
|
||||
DB::transaction(function () use ($definitions, &$collectionMap) {
|
||||
foreach ($definitions as $eventTypeSlug => $definition) {
|
||||
$eventType = $this->ensureEventType($definition['event_type']);
|
||||
|
||||
$collection = TaskCollection::updateOrCreate(
|
||||
['slug' => $definition['slug']],
|
||||
['slug' => $definition['collection']['slug']],
|
||||
[
|
||||
'tenant_id' => null,
|
||||
'event_type_id' => $eventType->id,
|
||||
'name_translations' => $definition['name'],
|
||||
'description_translations' => $definition['description'],
|
||||
'is_default' => $definition['is_default'] ?? false,
|
||||
'position' => $definition['position'] ?? 0,
|
||||
'name_translations' => $definition['collection']['name'],
|
||||
'description_translations' => $definition['collection']['description'],
|
||||
'is_default' => $definition['collection']['is_default'] ?? false,
|
||||
'position' => $definition['collection']['position'] ?? 0,
|
||||
'source_collection_id' => null,
|
||||
]
|
||||
);
|
||||
|
||||
$syncPayload = [];
|
||||
|
||||
foreach ($definition['tasks'] as $taskDefinition) {
|
||||
$emotion = $this->ensureEmotion($taskDefinition['emotion'] ?? [], $eventType->id);
|
||||
|
||||
$task = Task::updateOrCreate(
|
||||
['slug' => $taskDefinition['slug']],
|
||||
[
|
||||
'tenant_id' => null,
|
||||
'event_type_id' => $eventType->id,
|
||||
'collection_id' => $collection->id,
|
||||
'emotion_id' => $emotion?->id,
|
||||
'title' => $taskDefinition['title'],
|
||||
'description' => $taskDefinition['description'] ?? null,
|
||||
'example_text' => $taskDefinition['example'] ?? null,
|
||||
'difficulty' => $taskDefinition['difficulty'] ?? 'easy',
|
||||
'priority' => 'medium',
|
||||
'sort_order' => $taskDefinition['sort_order'] ?? 0,
|
||||
'is_active' => true,
|
||||
'is_completed' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$syncPayload[$task->id] = ['sort_order' => $taskDefinition['sort_order'] ?? 0];
|
||||
foreach ($definition['base_tasks'] as $index => $taskDefinition) {
|
||||
$sortOrder = $taskDefinition['sort_order'] ?? (($index + 1) * 10);
|
||||
$task = $this->upsertTask($collection, $eventType, $taskDefinition, $sortOrder);
|
||||
$syncPayload[$task->id] = ['sort_order' => $sortOrder];
|
||||
}
|
||||
|
||||
if (! empty($syncPayload)) {
|
||||
$collection->tasks()->sync($syncPayload);
|
||||
}
|
||||
$this->ensureMinimumTasks(
|
||||
$collection,
|
||||
$eventType,
|
||||
$syncPayload,
|
||||
count($syncPayload),
|
||||
self::MIN_TASKS_PER_EVENT_TYPE,
|
||||
$eventTypeSlug
|
||||
);
|
||||
|
||||
$collection->tasks()->sync($syncPayload);
|
||||
$collectionMap[$eventType->id] = $collection;
|
||||
}
|
||||
});
|
||||
|
||||
$this->assignOrphanTasks($collectionMap);
|
||||
}
|
||||
|
||||
private function definitions(): array
|
||||
{
|
||||
return [
|
||||
'wedding' => [
|
||||
'event_type' => [
|
||||
'slug' => 'wedding',
|
||||
'name' => ['de' => 'Hochzeit', 'en' => 'Wedding'],
|
||||
'icon' => 'lucide-heart',
|
||||
],
|
||||
'collection' => [
|
||||
'slug' => 'wedding-classics',
|
||||
'name' => [
|
||||
'de' => 'Hochzeitsklassiker',
|
||||
'en' => 'Wedding Classics',
|
||||
],
|
||||
'description' => [
|
||||
'de' => 'Kuratierte Aufgaben rund um Trauung, Emotionen und besondere Momente.',
|
||||
'en' => 'Curated prompts for vows, emotions, and memorable wedding highlights.',
|
||||
],
|
||||
'is_default' => true,
|
||||
'position' => 10,
|
||||
],
|
||||
'base_tasks' => [
|
||||
$this->taskDefinition(
|
||||
'wedding-first-look',
|
||||
['de' => 'Erster Blick des Brautpaares festhalten', 'en' => 'Capture the couple’s first look'],
|
||||
['de' => 'Halte den Moment fest, in dem sich Braut und Bräutigam zum ersten Mal sehen.', 'en' => 'Capture the instant when the couple sees each other for the first time.'],
|
||||
['de' => 'Fotografiere die Reaktionen aus verschiedenen Blickwinkeln.', 'en' => 'Shoot reactions from multiple angles.'],
|
||||
['name' => ['de' => 'Romantik', 'en' => 'Romance'], 'icon' => 'lucide-heart', 'color' => '#ec4899', 'sort_order' => 10],
|
||||
'easy',
|
||||
10
|
||||
),
|
||||
$this->taskDefinition(
|
||||
'wedding-family-hug',
|
||||
['de' => 'Familienumarmung organisieren', 'en' => 'Organise a family group hug'],
|
||||
['de' => 'Bitte die wichtigsten Menschen, das Paar gleichzeitig zu umarmen.', 'en' => 'Ask the closest friends and family to hug the couple all at once.'],
|
||||
['de' => 'Kombiniere die Umarmung mit einem Toast.', 'en' => 'Combine the hug with a heartfelt toast.'],
|
||||
['name' => ['de' => 'Freude', 'en' => 'Joy'], 'icon' => 'lucide-smile', 'color' => '#f59e0b', 'sort_order' => 20],
|
||||
'medium',
|
||||
20
|
||||
),
|
||||
$this->taskDefinition(
|
||||
'wedding-midnight-sparkler',
|
||||
['de' => 'Mitternachtsfunkeln mit Wunderkerzen', 'en' => 'Midnight sparkler moment'],
|
||||
['de' => 'Verteile Wunderkerzen und schafft ein leuchtendes Spalier für das Paar.', 'en' => 'Hand out sparklers and form a glowing aisle for the couple.'],
|
||||
['de' => 'Koordiniere die Musik und kündige den Countdown an.', 'en' => 'Coordinate music and announce a countdown.'],
|
||||
['name' => ['de' => 'Ekstase', 'en' => 'Euphoria'], 'icon' => 'lucide-stars', 'color' => '#6366f1', 'sort_order' => 30],
|
||||
'medium',
|
||||
30
|
||||
),
|
||||
$this->taskDefinition(
|
||||
'wedding-vow-whisper',
|
||||
['de' => 'Flüstert die liebsten Gelübde erneut', 'en' => 'Whisper your vows again'],
|
||||
['de' => 'Lasst das Paar sich gegenseitig einen Satz aus den Gelübden zuflüstern.', 'en' => 'Let the couple whisper a favourite line from their vows to each other.'],
|
||||
['de' => 'Nahaufnahme der Gesichter, während sie lächeln.', 'en' => 'Capture a close-up of the smiles while they whisper.'],
|
||||
['name' => ['de' => 'Verbundenheit', 'en' => 'Connection'], 'icon' => 'lucide-infinity', 'color' => '#a855f7', 'sort_order' => 35],
|
||||
'easy',
|
||||
40
|
||||
),
|
||||
$this->taskDefinition(
|
||||
'wedding-generations-portrait',
|
||||
['de' => 'Generationenportrait', 'en' => 'Generations portrait'],
|
||||
['de' => 'Bringe drei Generationen gleichzeitig aufs Foto – vom jüngsten zum ältesten Familienmitglied.', 'en' => 'Bring three generations into a single photograph – youngest to oldest.'],
|
||||
['de' => 'Stellt sie gestaffelt auf einer Treppe auf.', 'en' => 'Arrange them on steps for a layered look.'],
|
||||
['name' => ['de' => 'Familie', 'en' => 'Family'], 'icon' => 'lucide-users', 'color' => '#0ea5e9', 'sort_order' => 45],
|
||||
'medium',
|
||||
50
|
||||
),
|
||||
],
|
||||
],
|
||||
'birthday' => [
|
||||
'event_type' => [
|
||||
'slug' => 'birthday',
|
||||
'name' => ['de' => 'Geburtstag', 'en' => 'Birthday'],
|
||||
'icon' => 'lucide-cake',
|
||||
],
|
||||
'collection' => [
|
||||
'slug' => 'birthday-celebration',
|
||||
'name' => [
|
||||
'de' => 'Geburtstags-Highlights',
|
||||
'en' => 'Birthday Highlights',
|
||||
],
|
||||
'description' => [
|
||||
'de' => 'Aufgaben für Überraschungen, Gratulationen und gemeinsames Feiern.',
|
||||
'en' => 'Prompts covering surprises, wishes, and shared celebrations.',
|
||||
],
|
||||
'is_default' => false,
|
||||
'position' => 20,
|
||||
],
|
||||
'base_tasks' => [
|
||||
$this->taskDefinition(
|
||||
'birthday-surprise-wall',
|
||||
['de' => 'Überraschungswand mit Polaroids gestalten', 'en' => 'Create a surprise wall filled with instant photos'],
|
||||
['de' => 'Sammle Schnappschüsse der Gäste und befestige sie als Fotowand.', 'en' => 'Collect snapshots from guests and display them as a photo wall.'],
|
||||
['de' => 'Bitte jeden Gast um einen kurzen Gruß zum Bild.', 'en' => 'Invite each guest to add a short note to the photo.'],
|
||||
['name' => ['de' => 'Nostalgie', 'en' => 'Nostalgia'], 'icon' => 'lucide-images', 'color' => '#f97316', 'sort_order' => 40],
|
||||
'easy',
|
||||
10
|
||||
),
|
||||
$this->taskDefinition(
|
||||
'birthday-toast-circle',
|
||||
['de' => 'Gratulationskreis mit kurzen Toasts', 'en' => 'Circle of toasts'],
|
||||
['de' => 'Bildet einen Kreis und bittet jede Person um einen 10-Sekunden-Toast.', 'en' => 'Gather in a circle and ask each guest for a 10-second toast.'],
|
||||
['de' => 'Nimm die Lautstärke nach jedem Toast auf – wer jubelt am lautesten?', 'en' => 'Capture the volume of each cheer—who is the loudest?'],
|
||||
['name' => ['de' => 'Dankbarkeit', 'en' => 'Gratitude'], 'icon' => 'lucide-hands', 'color' => '#22c55e', 'sort_order' => 50],
|
||||
'easy',
|
||||
20
|
||||
),
|
||||
$this->taskDefinition(
|
||||
'birthday-memory-lane',
|
||||
['de' => 'Memory-Lane Foto-Story', 'en' => 'Memory lane photo story'],
|
||||
['de' => 'Suche drei Gegenstände, die zum Geburtstagsgast passen, und erzähle eine Mini-Story mit Fotos.', 'en' => 'Find three objects that represent the guest of honour and photograph them in a mini story.'],
|
||||
['de' => 'Nutze unterschiedliche Bildausschnitte für die Story.', 'en' => 'Use different framings to tell the story.'],
|
||||
['name' => ['de' => 'Erinnerung', 'en' => 'Reminiscence'], 'icon' => 'lucide-book-open', 'color' => '#6366f1', 'sort_order' => 55],
|
||||
'medium',
|
||||
30
|
||||
),
|
||||
],
|
||||
],
|
||||
'christmas' => [
|
||||
'event_type' => [
|
||||
'slug' => 'christmas',
|
||||
'name' => ['de' => 'Weihnachten', 'en' => 'Christmas'],
|
||||
'icon' => 'lucide-sparkles',
|
||||
],
|
||||
'collection' => [
|
||||
'slug' => 'christmas-magic',
|
||||
'name' => [
|
||||
'de' => 'Festliche Highlights',
|
||||
'en' => 'Festive Highlights',
|
||||
],
|
||||
'description' => [
|
||||
'de' => 'Wärmende Aufgaben für Glühwein-Momente und gemeinsames Staunen.',
|
||||
'en' => 'Cozy prompts for mulled wine moments and shared wonder.',
|
||||
],
|
||||
'is_default' => false,
|
||||
'position' => 30,
|
||||
],
|
||||
'base_tasks' => [
|
||||
$this->taskDefinition(
|
||||
'christmas-light-hunt',
|
||||
['de' => 'Finde das schönste Lichterdekor', 'en' => 'Spot the brightest decoration'],
|
||||
['de' => 'Halte das stimmungsvollste Licht im Raum fest.', 'en' => 'Capture the coziest lights around you.'],
|
||||
['de' => 'Zeige die Lichtquelle plus Reaktion eines Gastes.', 'en' => 'Show the light source together with a guest reacting to it.'],
|
||||
['name' => ['de' => 'Besinnlichkeit', 'en' => 'Serenity'], 'icon' => 'lucide-candle', 'color' => '#f97316', 'sort_order' => 20],
|
||||
'easy',
|
||||
10
|
||||
),
|
||||
$this->taskDefinition(
|
||||
'christmas-cookie-cheers',
|
||||
['de' => 'Plätzchen-Toast', 'en' => 'Cookie cheers'],
|
||||
['de' => 'Stoßt mit Lieblingsplätzchen an und haltet das gemeinsame Lachen fest.', 'en' => 'Clink your favourite cookies together and capture the laughter.'],
|
||||
['de' => 'Fotografiere in einer Reihe – Hände vorne, Gesichter dahinter.', 'en' => 'Frame hands with cookies in front, smiling faces behind.'],
|
||||
['name' => ['de' => 'Wärme', 'en' => 'Warmth'], 'icon' => 'lucide-mug-hot', 'color' => '#ef4444', 'sort_order' => 30],
|
||||
'easy',
|
||||
20
|
||||
),
|
||||
$this->taskDefinition(
|
||||
'christmas-snow-story',
|
||||
['de' => 'Schneegestöber im Innenraum', 'en' => 'Indoor snow story'],
|
||||
['de' => 'Nutze Deko oder Papier, um Schneeflocken zu simulieren, und halte den Zauber fest.', 'en' => 'Use props or paper to fake snow and capture the magic.'],
|
||||
['de' => 'Lass die Schneeflocken im Vordergrund unscharf erscheinen.', 'en' => 'Keep the fake snow in the foreground out of focus for a dreamy look.'],
|
||||
['name' => ['de' => 'Staunen', 'en' => 'Wonder'], 'icon' => 'lucide-sparkles', 'color' => '#22d3ee', 'sort_order' => 35],
|
||||
'medium',
|
||||
30
|
||||
),
|
||||
],
|
||||
],
|
||||
'corporate' => [
|
||||
'event_type' => [
|
||||
'slug' => 'corporate',
|
||||
'name' => ['de' => 'Firma', 'en' => 'Corporate'],
|
||||
'icon' => 'lucide-briefcase',
|
||||
],
|
||||
'collection' => [
|
||||
'slug' => 'corporate-connect',
|
||||
'name' => [
|
||||
'de' => 'Team-Verbindungen',
|
||||
'en' => 'Team Connections',
|
||||
],
|
||||
'description' => [
|
||||
'de' => 'Interaktive Aufgaben für produktive Offsites und Firmenfeiern.',
|
||||
'en' => 'Interactive prompts for productive offsites and company celebrations.',
|
||||
],
|
||||
'is_default' => false,
|
||||
'position' => 40,
|
||||
],
|
||||
'base_tasks' => [
|
||||
$this->taskDefinition(
|
||||
'corporate-high-five-chain',
|
||||
['de' => 'High-Five-Kette starten', 'en' => 'Start a high-five chain'],
|
||||
['de' => 'Platziert euch im Kreis und gebt reihum High-Fives – haltet den Moment fest.', 'en' => 'Form a circle and pass on high-fives – capture the energy.'],
|
||||
['de' => 'Nutze Serienaufnahmen, um alle Bewegungen einzufangen.', 'en' => 'Use burst mode to catch every high-five.'],
|
||||
['name' => ['de' => 'Teamgeist', 'en' => 'Team Spirit'], 'icon' => 'lucide-users', 'color' => '#0ea5e9', 'sort_order' => 10],
|
||||
'easy',
|
||||
10
|
||||
),
|
||||
$this->taskDefinition(
|
||||
'corporate-goal-toast',
|
||||
['de' => 'Ziele feiern', 'en' => 'Celebrate milestones'],
|
||||
['de' => 'Ruft gemeinsam ein erreichte Ziel in die Kamera und haltet die Euphorie fest.', 'en' => 'Shout a recent team achievement into the camera together.'],
|
||||
['de' => 'Lasst Konfetti oder Luftballons in den Rahmen fallen.', 'en' => 'Drop confetti or balloons into the frame.'],
|
||||
['name' => ['de' => 'Stolz', 'en' => 'Pride'], 'icon' => 'lucide-trophy', 'color' => '#22c55e', 'sort_order' => 20],
|
||||
'medium',
|
||||
20
|
||||
),
|
||||
$this->taskDefinition(
|
||||
'corporate-coffee-pose',
|
||||
['de' => 'Kaffee-Powerpose', 'en' => 'Coffee power pose'],
|
||||
['de' => 'Lass das Team mit Kaffeebechern posieren – jede Person zeigt eine andere Emotion.', 'en' => 'Have the team pose with coffee mugs, each showing a different emotion.'],
|
||||
['de' => 'Nutze eine niedrige Perspektive, um Power auszudrücken.', 'en' => 'Shoot from a low angle to emphasise power.'],
|
||||
['name' => ['de' => 'Motivation', 'en' => 'Motivation'], 'icon' => 'lucide-rocket', 'color' => '#8b5cf6', 'sort_order' => 30],
|
||||
'easy',
|
||||
30
|
||||
),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function autoTaskSeeds(): array
|
||||
{
|
||||
return [
|
||||
'wedding' => [
|
||||
$this->autoSeed('dancefloor',
|
||||
['de' => 'Dancefloor-Glück #{n}', 'en' => 'Dancefloor joy #{n}'],
|
||||
['de' => 'Fange eine wilde Tanzszene ein, die die Energie der Gäste zeigt.', 'en' => 'Capture a wild dance scene packed with guest energy.'],
|
||||
['de' => 'Nutze eine lange Belichtungszeit für Lichtstreifen.', 'en' => 'Use a longer exposure to create light trails.'],
|
||||
['name' => ['de' => 'Ekstase', 'en' => 'Euphoria'], 'icon' => 'lucide-stars', 'color' => '#a855f7'],
|
||||
'medium'
|
||||
),
|
||||
$this->autoSeed('sparkler',
|
||||
['de' => 'Wunderkerzen-Story #{n}', 'en' => 'Sparkler story #{n}'],
|
||||
['de' => 'Erzähle die Geschichte eines funkelnden Moments mit mindestens drei Bildern.', 'en' => 'Tell the story of a sparkling moment in three frames.'],
|
||||
['de' => 'Variiere zwischen Detailaufnahme, Halbtotaler und Reaktion.', 'en' => 'Mix a detail shot, a medium frame, and a reaction close-up.'],
|
||||
['name' => ['de' => 'Staunen', 'en' => 'Awe'], 'icon' => 'lucide-wand', 'color' => '#f97316'],
|
||||
'medium'
|
||||
),
|
||||
$this->autoSeed('first-dance-detail',
|
||||
['de' => 'Detail beim Hochzeitstanz #{n}', 'en' => 'First-dance detail #{n}'],
|
||||
['de' => 'Suche ein kleines Detail während des Hochzeitstanzes – Hände, Schuhe, Accessoires.', 'en' => 'Spot a tiny detail during the first dance – hands, shoes or accessories.'],
|
||||
['de' => 'Nutze starkes Bokeh für den Hintergrund.', 'en' => 'Use heavy bokeh to blur the background.'],
|
||||
['name' => ['de' => 'Zärtlichkeit', 'en' => 'Tenderness'], 'icon' => 'lucide-feather', 'color' => '#fb7185'],
|
||||
'easy'
|
||||
),
|
||||
$this->autoSeed('guest-story',
|
||||
['de' => 'Gästegeschichte #{n}', 'en' => 'Guest story #{n}'],
|
||||
['de' => 'Dokumentiere eine Gruppe von Gästen, wie sie miteinander interagiert.', 'en' => 'Document a group of guests interacting naturally.'],
|
||||
['de' => 'Fotografiere die Gruppe aus zwei Perspektiven.', 'en' => 'Capture the group from two perspectives.'],
|
||||
['name' => ['de' => 'Freundschaft', 'en' => 'Friendship'], 'icon' => 'lucide-users', 'color' => '#38bdf8'],
|
||||
'easy'
|
||||
),
|
||||
],
|
||||
'birthday' => [
|
||||
$this->autoSeed('candle-wish',
|
||||
['de' => 'Kerzenwunsch #{n}', 'en' => 'Candle wish #{n}'],
|
||||
['de' => 'Halte den Moment fest, in dem der Wunsch ausgesprochen wird.', 'en' => 'Capture the second the wish is made.'],
|
||||
['de' => 'Lass das Motiv in den Kerzenlichtschein eintauchen.', 'en' => 'Bathe the scene in candlelight glow.'],
|
||||
['name' => ['de' => 'Freude', 'en' => 'Joy'], 'icon' => 'lucide-sparkles', 'color' => '#facc15'],
|
||||
'easy'
|
||||
),
|
||||
$this->autoSeed('gift-reaction',
|
||||
['de' => 'Geschenk-Reaktion #{n}', 'en' => 'Gift reaction #{n}'],
|
||||
['de' => 'Fange die spontanste Reaktion auf ein Geschenk ein.', 'en' => 'Capture the most spontaneous gift reaction.'],
|
||||
['de' => 'Versuche eine Serienaufnahme für echte Emotionen.', 'en' => 'Use burst mode for authentic emotion.'],
|
||||
['name' => ['de' => 'Überraschung', 'en' => 'Surprise'], 'icon' => 'lucide-gift', 'color' => '#f472b6'],
|
||||
'easy'
|
||||
),
|
||||
$this->autoSeed('birthday-anthem',
|
||||
['de' => 'Geburtstagshymne #{n}', 'en' => 'Birthday anthem #{n}'],
|
||||
['de' => 'Dokumentiere das Mitsingen eines Liedes inklusive Stimmung im Raum.', 'en' => 'Document everyone singing a song and the overall mood.'],
|
||||
['de' => 'Fokussiere auf die Person, die am lautesten singt.', 'en' => 'Focus on whoever sings the loudest.'],
|
||||
['name' => ['de' => 'Lebensfreude', 'en' => 'Delight'], 'icon' => 'lucide-music', 'color' => '#60a5fa'],
|
||||
'medium'
|
||||
),
|
||||
],
|
||||
'christmas' => [
|
||||
$this->autoSeed('ornament-closeup',
|
||||
['de' => 'Ornament-Kunstwerk #{n}', 'en' => 'Ornament art #{n}'],
|
||||
['de' => 'Inszeniere ein besonderes Ornament in Nahaufnahme.', 'en' => 'Stage a special ornament in a moody close-up.'],
|
||||
['de' => 'Lass warme Lichter im Hintergrund bokeh-artig wirken.', 'en' => 'Let warm lights blur softly in the background.'],
|
||||
['name' => ['de' => 'Staunen', 'en' => 'Wonder'], 'icon' => 'lucide-sparkles', 'color' => '#38bdf8'],
|
||||
'easy'
|
||||
),
|
||||
$this->autoSeed('carol-chorus',
|
||||
['de' => 'Singender Chor #{n}', 'en' => 'Carolling chorus #{n}'],
|
||||
['de' => 'Zeige eine Gruppe beim Singen eines Weihnachtsliedes.', 'en' => 'Highlight a group singing a seasonal song.'],
|
||||
['de' => 'Nutze eine Panorama-Aufnahme für alle Beteiligten.', 'en' => 'Use a panorama framing to include everyone.'],
|
||||
['name' => ['de' => 'Gemeinschaft', 'en' => 'Togetherness'], 'icon' => 'lucide-users', 'color' => '#22c55e'],
|
||||
'medium'
|
||||
),
|
||||
$this->autoSeed('wishlist-doodle',
|
||||
['de' => 'Wunschlisten-Doodle #{n}', 'en' => 'Wishlist doodle #{n}'],
|
||||
['de' => 'Fotografiere eine improvisierte Wunschliste inklusive Zeichnungen.', 'en' => 'Photograph an improvised wishlist complete with doodles.'],
|
||||
['de' => 'Lege die Liste auf Holz- oder Stoffoberflächen für Wärme.', 'en' => 'Place the list on wood or fabric for extra warmth.'],
|
||||
['name' => ['de' => 'Hoffnung', 'en' => 'Hope'], 'icon' => 'lucide-heart', 'color' => '#f97316'],
|
||||
'easy'
|
||||
),
|
||||
],
|
||||
'corporate' => [
|
||||
$this->autoSeed('team-brainstorm',
|
||||
['de' => 'Brainstorm-Blitzlicht #{n}', 'en' => 'Brainstorm spotlight #{n}'],
|
||||
['de' => 'Halte eine kreative Idee auf einem Whiteboard plus das Team dahinter fest.', 'en' => 'Capture a clever whiteboard idea with the team behind it.'],
|
||||
['de' => 'Arbeite mit Spiegelungen im Glas oder Bildschirm.', 'en' => 'Play with reflections on glass or screens.'],
|
||||
['name' => ['de' => 'Innovation', 'en' => 'Innovation'], 'icon' => 'lucide-lightbulb', 'color' => '#facc15'],
|
||||
'medium'
|
||||
),
|
||||
$this->autoSeed('watercooler-moment',
|
||||
['de' => 'Watercooler-Moment #{n}', 'en' => 'Watercooler moment #{n}'],
|
||||
['de' => 'Zeige Kollegen beim lockeren Austausch abseits des Meetings.', 'en' => 'Show teammates chatting casually away from the meeting table.'],
|
||||
['de' => 'Fokussiere auf Gestik und Körpersprache.', 'en' => 'Focus on gestures and body language.'],
|
||||
['name' => ['de' => 'Leichtigkeit', 'en' => 'Ease'], 'icon' => 'lucide-coffee', 'color' => '#60a5fa'],
|
||||
'easy'
|
||||
),
|
||||
$this->autoSeed('celebration-confetti',
|
||||
['de' => 'Konfetti-Erfolg #{n}', 'en' => 'Confetti success #{n}'],
|
||||
['de' => 'Inszeniere einen kleinen Konfettiwurf als Symbol für Team-Erfolg.', 'en' => 'Stage a mini confetti throw to celebrate team success.'],
|
||||
['de' => 'Arbeite mit schneller Verschlusszeit für eingefrorene Partikel.', 'en' => 'Use a fast shutter to freeze the confetti.'],
|
||||
['name' => ['de' => 'Erfolg', 'en' => 'Success'], 'icon' => 'lucide-flag', 'color' => '#a855f7'],
|
||||
'medium'
|
||||
),
|
||||
],
|
||||
'default' => [
|
||||
$this->autoSeed('story-fragments',
|
||||
['de' => 'Story-Fragmente #{n}', 'en' => 'Story fragments #{n}'],
|
||||
['de' => 'Finde drei kleine Details, die zusammen eine Geschichte ergeben.', 'en' => 'Find three small details that build one story.'],
|
||||
['de' => 'Kombiniere Nahaufnahmen mit einem Übersichtsfoto.', 'en' => 'Combine close-ups with one establishing shot.'],
|
||||
['name' => ['de' => 'Neugier', 'en' => 'Curiosity'], 'icon' => 'lucide-eye', 'color' => '#38bdf8'],
|
||||
'easy'
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function taskDefinition(
|
||||
string $slug,
|
||||
array $title,
|
||||
array $description,
|
||||
array $example,
|
||||
?array $emotion,
|
||||
string $difficulty,
|
||||
int $sortOrder
|
||||
): array {
|
||||
return [
|
||||
'slug' => $slug,
|
||||
'title' => $title,
|
||||
'description' => $description,
|
||||
'example' => $example,
|
||||
'emotion' => $emotion,
|
||||
'difficulty' => $difficulty,
|
||||
'sort_order' => $sortOrder,
|
||||
];
|
||||
}
|
||||
|
||||
private function autoSeed(
|
||||
string $slug,
|
||||
array $title,
|
||||
array $description,
|
||||
array $example,
|
||||
?array $emotion,
|
||||
string $difficulty
|
||||
): array {
|
||||
return [
|
||||
'slug' => $slug,
|
||||
'title' => $title,
|
||||
'description' => $description,
|
||||
'example' => $example,
|
||||
'emotion' => $emotion,
|
||||
'difficulty' => $difficulty,
|
||||
];
|
||||
}
|
||||
|
||||
private function upsertTask(TaskCollection $collection, EventType $eventType, array $definition, int $sortOrder): Task
|
||||
{
|
||||
$emotion = $this->ensureEmotion($definition['emotion'] ?? [], $eventType->id);
|
||||
|
||||
$task = Task::updateOrCreate(
|
||||
['slug' => $definition['slug']],
|
||||
[
|
||||
'tenant_id' => null,
|
||||
'event_type_id' => $eventType->id,
|
||||
'collection_id' => $collection->id,
|
||||
'emotion_id' => $emotion?->id,
|
||||
'title' => $definition['title'],
|
||||
'description' => $definition['description'] ?? null,
|
||||
'example_text' => $definition['example'] ?? null,
|
||||
'difficulty' => $definition['difficulty'] ?? 'easy',
|
||||
'priority' => $definition['priority'] ?? 'medium',
|
||||
'sort_order' => $definition['sort_order'] ?? $sortOrder,
|
||||
'is_active' => true,
|
||||
'is_completed' => false,
|
||||
]
|
||||
);
|
||||
|
||||
if ($task->collection_id !== $collection->id) {
|
||||
$task->collection_id = $collection->id;
|
||||
$task->save();
|
||||
}
|
||||
|
||||
return $task;
|
||||
}
|
||||
|
||||
private function ensureMinimumTasks(
|
||||
TaskCollection $collection,
|
||||
EventType $eventType,
|
||||
array &$syncPayload,
|
||||
int $existingCount,
|
||||
int $minimum,
|
||||
string $eventTypeSlug
|
||||
): void {
|
||||
$needed = $minimum - $existingCount;
|
||||
if ($needed <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$seeds = $this->autoTaskSeeds()[$eventTypeSlug] ?? $this->autoTaskSeeds()['default'];
|
||||
if (empty($seeds)) {
|
||||
$seeds = $this->autoTaskSeeds()['default'];
|
||||
}
|
||||
|
||||
$baseCount = $existingCount;
|
||||
|
||||
for ($i = 0; $i < $needed; $i++) {
|
||||
$seed = $seeds[$i % count($seeds)];
|
||||
$sequence = $baseCount + $i + 1;
|
||||
|
||||
$slug = Str::slug(sprintf('%s-%s-%s', $eventType->slug ?? $eventTypeSlug, $seed['slug'], $sequence));
|
||||
$taskDefinition = [
|
||||
'slug' => $slug,
|
||||
'title' => [
|
||||
'de' => str_replace('{n}', (string) $sequence, $seed['title']['de']),
|
||||
'en' => str_replace('{n}', (string) $sequence, $seed['title']['en']),
|
||||
],
|
||||
'description' => [
|
||||
'de' => str_replace('{n}', (string) $sequence, $seed['description']['de']),
|
||||
'en' => str_replace('{n}', (string) $sequence, $seed['description']['en']),
|
||||
],
|
||||
'example' => [
|
||||
'de' => str_replace('{n}', (string) $sequence, $seed['example']['de']),
|
||||
'en' => str_replace('{n}', (string) $sequence, $seed['example']['en']),
|
||||
],
|
||||
'emotion' => $seed['emotion'] ?? null,
|
||||
'difficulty' => $seed['difficulty'] ?? 'easy',
|
||||
'priority' => $seed['priority'] ?? 'medium',
|
||||
'sort_order' => 500 + ($sequence * 5),
|
||||
];
|
||||
|
||||
$task = $this->upsertTask($collection, $eventType, $taskDefinition, $taskDefinition['sort_order']);
|
||||
$syncPayload[$task->id] = ['sort_order' => $taskDefinition['sort_order']];
|
||||
}
|
||||
}
|
||||
|
||||
private function assignOrphanTasks(array $collectionMap): void
|
||||
{
|
||||
if (empty($collectionMap)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Task::whereNull('collection_id')
|
||||
->orderBy('id')
|
||||
->chunkById(100, function ($tasks) use ($collectionMap) {
|
||||
foreach ($tasks as $task) {
|
||||
$collection = $collectionMap[$task->event_type_id] ?? Arr::first($collectionMap);
|
||||
if (! $collection) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$task->collection_id = $collection->id;
|
||||
$task->save();
|
||||
|
||||
$collection->tasks()->syncWithoutDetaching([
|
||||
$task->id => ['sort_order' => $task->sort_order ?? 0],
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected function ensureEventType(array $definition): EventType
|
||||
{
|
||||
$payload = [
|
||||
'name' => $definition['name'],
|
||||
'icon' => $definition['icon'] ?? null,
|
||||
];
|
||||
|
||||
return EventType::updateOrCreate(
|
||||
['slug' => $definition['slug']],
|
||||
$payload
|
||||
[
|
||||
'name' => $definition['name'],
|
||||
'icon' => $definition['icon'] ?? null,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -261,7 +557,6 @@ class TaskCollectionsSeeder extends Seeder
|
||||
}
|
||||
|
||||
$query = Emotion::query();
|
||||
|
||||
$name = $definition['name'] ?? [];
|
||||
|
||||
if (isset($name['en'])) {
|
||||
|
||||
Reference in New Issue
Block a user