definitions(); $collectionMap = []; DB::transaction(function () use ($definitions, &$collectionMap) { foreach ($definitions as $eventTypeSlug => $definition) { $eventType = $this->ensureEventType($definition['event_type']); $collection = TaskCollection::updateOrCreate( ['slug' => $definition['collection']['slug']], [ 'tenant_id' => null, 'event_type_id' => $eventType->id, '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['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]; } $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 { return EventType::updateOrCreate( ['slug' => $definition['slug']], [ 'name' => $definition['name'], 'icon' => $definition['icon'] ?? null, ] ); } protected function ensureEmotion(array $definition, ?int $eventTypeId): ?Emotion { if (empty($definition)) { return null; } $query = Emotion::query(); $name = $definition['name'] ?? []; if (isset($name['en'])) { $query->orWhere('name->en', $name['en']); } if (isset($name['de'])) { $query->orWhere('name->de', $name['de']); } $emotion = $query->first(); if (! $emotion) { $emotion = Emotion::create([ 'name' => $name, 'icon' => $definition['icon'] ?? 'lucide-smile', 'color' => $definition['color'] ?? '#6366f1', 'description' => $definition['description'] ?? null, 'sort_order' => $definition['sort_order'] ?? 0, 'is_active' => true, ]); } if ($eventTypeId && ! $emotion->eventTypes()->where('event_type_id', $eventTypeId)->exists()) { $emotion->eventTypes()->attach($eventTypeId); } return $emotion; } }