Files
fotospiel-app/database/seeders/TaskCollectionsSeeder.php
2025-10-31 20:19:09 +01:00

590 lines
31 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace Database\Seeders;
use App\Models\Emotion;
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
{
$definitions = $this->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 couples 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;
}
}