Initialize repo and add session changes (2025-09-08)

This commit is contained in:
Auto Commit
2025-09-08 14:03:43 +02:00
commit 44ab0a534b
327 changed files with 40952 additions and 0 deletions

1
database/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.sqlite*

View File

@@ -0,0 +1,44 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* The current password being used by the factory.
*/
protected static ?string $password;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}

View File

@@ -0,0 +1,49 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('sessions');
}
};

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('cache', function (Blueprint $table) {
$table->string('key')->primary();
$table->mediumText('value');
$table->integer('expiration');
});
Schema::create('cache_locks', function (Blueprint $table) {
$table->string('key')->primary();
$table->string('owner');
$table->integer('expiration');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('cache');
Schema::dropIfExists('cache_locks');
}
};

View File

@@ -0,0 +1,57 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('jobs', function (Blueprint $table) {
$table->id();
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
Schema::create('job_batches', function (Blueprint $table) {
$table->string('id')->primary();
$table->string('name');
$table->integer('total_jobs');
$table->integer('pending_jobs');
$table->integer('failed_jobs');
$table->longText('failed_job_ids');
$table->mediumText('options')->nullable();
$table->integer('cancelled_at')->nullable();
$table->integer('created_at');
$table->integer('finished_at')->nullable();
});
Schema::create('failed_jobs', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jobs');
Schema::dropIfExists('job_batches');
Schema::dropIfExists('failed_jobs');
}
};

View File

@@ -0,0 +1,25 @@
<?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
{
Schema::create('event_types', function (Blueprint $table) {
$table->id();
$table->json('name');
$table->string('slug')->unique();
$table->string('icon')->nullable();
$table->json('settings')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('event_types');
}
};

View File

@@ -0,0 +1,34 @@
<?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
{
Schema::create('emotions', function (Blueprint $table) {
$table->id();
$table->json('name');
$table->string('icon', 50);
$table->string('color', 7);
$table->json('description')->nullable();
$table->integer('sort_order')->default(0);
$table->boolean('is_active')->default(true);
$table->timestamps();
});
Schema::create('emotion_event_type', function (Blueprint $table) {
$table->unsignedBigInteger('emotion_id');
$table->unsignedBigInteger('event_type_id');
$table->primary(['emotion_id', 'event_type_id']);
});
}
public function down(): void
{
Schema::dropIfExists('emotion_event_type');
Schema::dropIfExists('emotions');
}
};

View File

@@ -0,0 +1,29 @@
<?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
{
Schema::create('events', function (Blueprint $table) {
$table->id();
$table->json('name');
$table->json('description')->nullable();
$table->date('date');
$table->string('slug')->unique();
$table->json('settings')->nullable();
$table->unsignedBigInteger('event_type_id');
$table->boolean('is_active')->default(true);
$table->string('default_locale', 5)->default('de');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('events');
}
};

View File

@@ -0,0 +1,29 @@
<?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
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('emotion_id');
$table->unsignedBigInteger('event_type_id')->nullable();
$table->json('title');
$table->json('description');
$table->json('example_text')->nullable();
$table->enum('difficulty', ['easy','medium','hard'])->default('easy');
$table->integer('sort_order')->default(0);
$table->boolean('is_active')->default(true);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('tasks');
}
};

View File

@@ -0,0 +1,40 @@
<?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
{
Schema::create('photos', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('event_id');
$table->unsignedBigInteger('emotion_id');
$table->unsignedBigInteger('task_id')->nullable();
$table->string('guest_name');
$table->string('file_path');
$table->string('thumbnail_path');
$table->integer('likes_count')->default(0);
$table->boolean('is_featured')->default(false);
$table->json('metadata')->nullable();
$table->timestamps();
});
Schema::create('photo_likes', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('photo_id');
$table->string('guest_name');
$table->string('ip_address', 45)->nullable();
$table->timestamp('created_at')->useCurrent();
$table->unique(['photo_id','guest_name','ip_address']);
});
}
public function down(): void
{
Schema::dropIfExists('photo_likes');
Schema::dropIfExists('photos');
}
};

View File

@@ -0,0 +1,29 @@
<?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
{
Schema::create('legal_pages', function (Blueprint $table) {
$table->id();
$table->string('slug', 32);
$table->json('title');
$table->json('body_markdown');
$table->string('locale_fallback', 5)->default('de');
$table->integer('version')->default(1);
$table->timestamp('effective_from')->nullable();
$table->boolean('is_published')->default(false);
$table->timestamps();
$table->unique(['slug','version']);
});
}
public function down(): void
{
Schema::dropIfExists('legal_pages');
}
};

View File

@@ -0,0 +1,22 @@
<?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
{
Schema::table('users', function (Blueprint $table) {
$table->string('role')->default('super_admin')->after('password');
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('role');
});
}
};

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('personal_access_tokens', function (Blueprint $table) {
$table->id();
$table->morphs('tokenable');
$table->text('name');
$table->string('token', 64)->unique();
$table->text('abilities')->nullable();
$table->timestamp('last_used_at')->nullable();
$table->timestamp('expires_at')->nullable()->index();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('personal_access_tokens');
}
};

View File

@@ -0,0 +1,29 @@
<?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
{
Schema::create('task_collections', function (Blueprint $table) {
$table->id();
$table->json('name');
$table->json('description')->nullable();
$table->timestamps();
});
Schema::create('task_collection_task', function (Blueprint $table) {
$table->unsignedBigInteger('task_collection_id');
$table->unsignedBigInteger('task_id');
$table->primary(['task_collection_id','task_id']);
});
}
public function down(): void
{
Schema::dropIfExists('task_collection_task');
Schema::dropIfExists('task_collections');
}
};

View File

@@ -0,0 +1,28 @@
<?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
{
Schema::create('task_imports', function (Blueprint $table) {
$table->id();
$table->string('disk')->default('local');
$table->string('path');
$table->string('source_filename');
$table->enum('status', ['pending','processing','completed','failed'])->default('pending');
$table->unsignedInteger('total_rows')->default(0);
$table->unsignedInteger('imported_rows')->default(0);
$table->json('errors')->nullable();
$table->unsignedBigInteger('created_by')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('task_imports');
}
};

View File

@@ -0,0 +1,28 @@
<?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
{
Schema::table('users', function (Blueprint $table) {
$table->text('two_factor_secret')->nullable();
$table->text('two_factor_recovery_codes')->nullable();
$table->timestamp('two_factor_confirmed_at')->nullable();
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn([
'two_factor_secret',
'two_factor_recovery_codes',
'two_factor_confirmed_at',
]);
});
}
};

View File

@@ -0,0 +1,42 @@
<?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
{
Schema::create('tenants', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->string('domain')->nullable()->unique();
$table->string('contact_name')->nullable();
$table->string('contact_email')->nullable();
$table->string('contact_phone')->nullable();
// Simple event-credit based monetization (MVP)
$table->integer('event_credits_balance')->default(1);
$table->timestamp('free_event_granted_at')->nullable();
// Limits & quotas
$table->integer('max_photos_per_event')->default(500);
$table->integer('max_storage_mb')->default(1024);
// Feature flags & misc
$table->json('features')->nullable();
$table->timestamp('last_activity_at')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('tenants');
}
};

View File

@@ -0,0 +1,38 @@
<?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
{
Schema::table('users', function (Blueprint $table) {
if (! Schema::hasColumn('users', 'tenant_id')) {
$table->foreignId('tenant_id')->nullable()->after('id')
->constrained('tenants')->nullOnDelete();
}
if (! Schema::hasColumn('users', 'role')) {
$table->string('role', 32)->default('tenant_user')->after('password')->index();
}
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
if (Schema::hasColumn('users', 'tenant_id')) {
// Drop FK first if the driver supports it
try { $table->dropConstrainedForeignId('tenant_id'); } catch (\Throwable $e) {
try { $table->dropForeign(['tenant_id']); } catch (\Throwable $e2) {}
$table->dropColumn('tenant_id');
}
}
if (Schema::hasColumn('users', 'role')) {
$table->dropColumn('role');
}
});
}
};

View File

@@ -0,0 +1,36 @@
<?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('events')) return;
Schema::table('events', function (Blueprint $table) {
if (! Schema::hasColumn('events', 'tenant_id')) {
$table->foreignId('tenant_id')->nullable()->after('id')->constrained('tenants')->nullOnDelete();
}
if (Schema::hasColumn('events', 'slug')) {
// Optional: ensure index exists
try { $table->index('slug'); } catch (\Throwable $e) {}
}
});
}
public function down(): void
{
if (! Schema::hasTable('events')) return;
Schema::table('events', function (Blueprint $table) {
if (Schema::hasColumn('events', 'tenant_id')) {
try { $table->dropConstrainedForeignId('tenant_id'); } catch (\Throwable $e) {
try { $table->dropForeign(['tenant_id']); } catch (\Throwable $e2) {}
$table->dropColumn('tenant_id');
}
}
});
}
};

View File

@@ -0,0 +1,31 @@
<?php
namespace Database\Seeders;
use App\Models\User;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
// Seed core demo data for frontend previews
$this->call([
EventTypesSeeder::class,
EmotionsSeeder::class,
DemoEventSeeder::class,
TasksSeeder::class,
DemoAchievementsSeeder::class,
]);
// Optional: demo user
User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
]);
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use App\Models\{Event, Emotion, Task, Photo};
class DemoAchievementsSeeder extends Seeder
{
public function run(): void
{
$event = Event::where('slug', 'demo-wedding-2025')->first();
if (!$event) {
$event = Event::create([
'slug' => 'demo-wedding-2025',
'name' => ['de' => 'Demo Hochzeit 2025', 'en' => 'Demo Wedding 2025'],
'description' => ['de' => 'Demo-Event', 'en' => 'Demo event'],
'date' => now()->toDateString(),
'event_type_id' => null,
'is_active' => true,
'settings' => [],
'default_locale' => 'de',
]);
}
$emotions = Emotion::query()->take(6)->get();
if (Task::count() === 0 && $emotions->isNotEmpty()) {
foreach (range(1, 10) as $i) {
$emo = $emotions->random();
Task::create([
'title' => ['de' => "Aufgabe #$i", 'en' => "Task #$i"],
'description' => ['de' => 'Kurzbeschreibung', 'en' => 'Short description'],
'emotion_id' => $emo->id,
'is_active' => true,
]);
}
}
$tasks = Task::inRandomOrder()->take(10)->get();
if ($tasks->isEmpty()) {
return; // nothing to seed
}
// Simple placeholder PNG (100x100)
$png = base64_decode('iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAGXRFWHRTb2Z0d2FyZQBwYWludC5uZXQgNC4xLjM2qefiAAABVklEQVR4Xu3UQQrCMBRF0cK1J3YQyF5z6jYv3Q0W3gQ6b8IYc3ov2Jf6n8A0Yq1nG2mZfE8y2GQkAAAAAAAAAANhK0ZL8b3xP2m5b4+0O8S9I3o9b3r8CwV8u0aH3bX8wE4WqgX3m4v3zO2KJ6l4yT4xvCw0b1q2c2w8bqQO3vFf0u8wUo5L3a8b0n2l5yq9Kf4zvCw0f1q2s2w0bpQO7PFv0s8wco4b3a8b0n2k5yq9Kf4zvCw0f1q2s2w0bpQO7PFv0s8wYgAAAAAAAAAAAACw9wG0qN2b2l3cMQAAAABJRU5ErkJggg==');
$guests = ['Alex', 'Marie', 'Lukas', 'Lena', 'Tom', 'Sophie', 'Jonas', 'Mia'];
foreach (range(1, 24) as $i) {
$task = $tasks->random();
$fileName = 'photo_demo_'.Str::random(6).'.png';
$thumbName = 'thumb_demo_'.Str::random(6).'.png';
Storage::disk('public')->put('photos/'.$fileName, $png);
Storage::disk('public')->put('thumbnails/'.$thumbName, $png);
Photo::create([
'event_id' => $event->id,
'emotion_id' => $task->emotion_id,
'task_id' => $task->id,
'guest_name' => $guests[array_rand($guests)],
'file_path' => 'photos/'.$fileName,
'thumbnail_path' => 'thumbnails/'.$thumbName,
'likes_count' => rand(0, 7),
'metadata' => ['seeded' => true],
'created_at' => now()->subMinutes(rand(1, 180)),
'updated_at' => now(),
]);
}
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\{Event, EventType};
class DemoEventSeeder extends Seeder
{
public function run(): void
{
$type = EventType::where('slug','wedding')->first();
if(!$type){ return; }
Event::updateOrCreate(['slug'=>'demo-wedding-2025'], [
'name' => ['de'=>'Demo Hochzeit 2025','en'=>'Demo Wedding 2025'],
'description' => ['de'=>'Demo-Event','en'=>'Demo event'],
'date' => now()->addMonths(3)->toDateString(),
'event_type_id' => $type->id,
'is_active' => true,
'settings' => [],
'default_locale' => 'de',
]);
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Emotion;
use App\Models\EventType;
use Illuminate\Support\Facades\DB;
class EmotionsSeeder extends Seeder
{
public function run(): void
{
$emotionData = [
['name'=>['de'=>'Liebe','en'=>'Love'], 'icon'=>'💖', 'color'=>'#ff6b9d', 'description'=>['de'=>'Romantische Momente','en'=>'Romantic moments'], 'sort_order'=>1],
['name'=>['de'=>'Freude','en'=>'Joy'], 'icon'=>'😊', 'color'=>'#ffd93d', 'description'=>['de'=>'Fröhliche Augenblicke','en'=>'Happy moments'], 'sort_order'=>2],
['name'=>['de'=>'Rührung','en'=>'Touched'], 'icon'=>'🥹', 'color'=>'#6bcf7f', 'description'=>['de'=>'Berührende Szenen','en'=>'Touching scenes'], 'sort_order'=>3],
['name'=>['de'=>'Nostalgie','en'=>'Nostalgia'], 'icon'=>'🕰️', 'color'=>'#a78bfa', 'description'=>['de'=>'Erinnerungen','en'=>'Memories'], 'sort_order'=>4],
['name'=>['de'=>'Überraschung','en'=>'Surprise'], 'icon'=>'😲', 'color'=>'#fb7185', 'description'=>['de'=>'Unerwartete Momente','en'=>'Unexpected moments'], 'sort_order'=>5],
['name'=>['de'=>'Stolz','en'=>'Pride'], 'icon'=>'🏆', 'color'=>'#34d399', 'description'=>['de'=>'Triumphale Augenblicke','en'=>'Triumphal moments'], 'sort_order'=>6],
['name'=>['de'=>'Teamgeist','en'=>'Team Spirit'], 'icon'=>'🤝', 'color'=>'#38bdf8', 'description'=>['de'=>'Zusammenhalt','en'=>'Team bonding'], 'sort_order'=>7],
['name'=>['de'=>'Besinnlichkeit','en'=>'Contemplation'], 'icon'=>'🕯️', 'color'=>'#22c55e', 'description'=>['de'=>'Feierliche Stimmung','en'=>'Festive calm'], 'sort_order'=>8],
];
$typeIds = EventType::pluck('id','slug');
foreach ($emotionData as $e) {
$emotion = Emotion::updateOrCreate(['name->de' => $e['name']['de']], $e);
// Link to types
$links = ['wedding','birthday','corporate','christmas'];
if ($e['name']['de'] === 'Teamgeist') { $links = ['corporate']; }
if ($e['name']['de'] === 'Besinnlichkeit') { $links = ['christmas']; }
foreach ($links as $slug) {
if (isset($typeIds[$slug])) {
DB::table('emotion_event_type')->updateOrInsert([
'emotion_id' => $emotion->id,
'event_type_id' => $typeIds[$slug],
], []);
}
}
}
}
}

View File

@@ -0,0 +1,244 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Str;
use App\Models\{Emotion, Task};
class EventTasksSeeder extends Seeder
{
public function run(): void
{
// Define 20 themed prompts per emotion (DE/EN)
$catalog = [
'Liebe' => [
['Herzrahmen', 'Formt mit euren Händen ein Herz um das Paar.', 'Heart Frame', 'Make a heart with your hands around the couple.'],
['Erster Blick', 'Fangt den Moment des ersten Blicks ein.', 'First Look', 'Capture the first look moment.'],
['Kuss im Konfetti', 'Kussmoment mit Konfetti oder Blütenregen.', 'Kiss in Confetti', 'A kiss with confetti or petals.'],
['Ringmoment', 'Nahaufnahme von Händen und Ringen.', 'Rings & Hands', 'Closeup of hands and rings.'],
['Schleierwind', 'Der Schleier weht fangt Bewegung ein.', 'Veil in the Wind', 'Catch the veil in motion.'],
['Liebesbrief', 'Lest euch eine kurze Botschaft vor.', 'Love Note', 'Read a short love note.'],
['Umarmung von hinten', 'Eine Person umarmt die andere von hinten.', 'Back Hug', 'Back hug pose.'],
['Sonnenuntergang', 'Silhouette im goldenen Licht.', 'Sunset Silhouette', 'Silhouette in golden light.'],
['Nasenstupser', 'Forehead oder NoseTouch.', 'Forehead Touch', 'Forehead or nose touch.'],
['Händchenhalten', 'Hände greifen sich Fokus auf Gefühl.', 'Holding Hands', 'Hands meeting focus on feeling.'],
['Tanz im Freien', 'Kurzer Tanzschritt unter freiem Himmel.', 'Dance Outside', 'A quick dance step outdoors.'],
['Lächeln mit Augen', '„Smize“ lächelt nur mit den Augen.', 'Smize', 'Smile with your eyes.'],
['Gegensätze ziehen an', 'Stellt eure Unterschiede liebvoll dar.', 'Opposites Attract', 'Show your differences playfully.'],
['Hinter dem Schleier', 'Kuss hinter dem Schleier.', 'Behind the Veil', 'Kiss behind the veil.'],
['Hand aufs Herz', 'Hände auf Herz echter Moment.', 'Hand on Heart', 'Hands on heart genuine moment.'],
['Namen schreiben', 'Schreibt Namen in die Luft (Lichtspur).', 'Name in Light', 'Write names in the air (light trail).'],
['Blick zurück', 'Geht weg und schaut zurück zur Kamera.', 'Look Back', 'Walk away, look back to camera.'],
['Gemeinsames Lachen', 'Bringt euch zum Lachen und klick.', 'Shared Laugh', 'Make each other laugh and snap.'],
['Spiegelmoment', 'Euer Spiegelbild kreativ einbauen.', 'Mirror Moment', 'Use your reflection creatively.'],
['Handkuss', 'Ein klassischer Handkuss.', 'Hand Kiss', 'A classic hand kiss.'],
],
'Freude' => [
['Lachwelle', 'Reihe bildet nacheinander eine Lachwelle.', 'Laugh Wave', 'Create a laugh wave one by one.'],
['HighFiveKette', 'Gebt euch reihum HighFives.', 'HighFive Chain', 'Highfives around the group.'],
['Freudensprung', 'Springt gleichzeitig in die Luft.', 'Jump of Joy', 'Jump together.'],
['Fotobomb', 'Huscht freundlich ins Bild.', 'Photobomb', 'Sneak into the shot (friendly!).'],
['Überraschtes Gesicht', 'Überraschte Mimik Hände hoch!', 'Surprised Face', 'Surprised faces hands up!'],
['SpiegelLacher', 'Spiegelt exakt die Mimik.', 'Mirror Laugh', 'Mirror each others laugh.'],
['Tanzender Eingang', 'Tanzend ins Bild laufen.', 'Dancing Entrance', 'Dance into the frame.'],
['KonfettiGrinsen', 'Konfetti werfen & lachen.', 'Confetti Grin', 'Throw confetti & grin.'],
['Witz erzählen', 'Schneller Witz Klick beim Lachen.', 'Tell a Joke', 'Tell a joke, snap at the laugh.'],
['FreundeHuddle', 'Köpfe zusammen, Grinsen groß.', 'Friends Huddle', 'Heads together, big grin.'],
['BacktoBack', 'Rücken an Rücken posieren.', 'Back to Back', 'Pose backtoback.'],
['VZeichen', 'PeaceZeichen kreativ einsetzen.', 'Peace Sign', 'Use peace sign creatively.'],
['Luftkuss', 'Kuss in die Kamera werfen.', 'Air Kiss', 'Blow a kiss to the camera.'],
['EmojiGesichter', 'Stellt Emojis nach.', 'Emoji Faces', 'Act out your favorite emojis.'],
['MiniChoreo', '3SchrittTanz, dann Foto.', 'Mini Choreo', '3step dance then photo.'],
['HutTausch', 'Accessoires tauschen & posen.', 'Hat Swap', 'Swap props and pose.'],
['Cheers!', 'Gläser/Tassen anstoßen.', 'Cheers!', 'Clink glasses/cups.'],
['Zungenakrobatik', 'Zunge raus Spaßpose.', 'Silly Tongue', 'Tongue out silly pose.'],
['OhyeahPose', 'BegeisterungsPose mit Fäusten.', 'OhYeah Pose', 'Fists up “ohyeah” pose.'],
['Händeschütteln', 'Übertriebenes Händeschütteln.', 'Epic Handshake', 'Overthetop handshake.'],
],
'Rührung' => [
['Berührender Blick', 'Schaut euch sanft in die Augen.', 'Tender Look', 'Gently look into each others eyes.'],
['Freudentränen', 'Ein Taschentuchmoment (authentisch).', 'Happy Tears', 'Capture a tissue moment.'],
['Hände nah', 'Nahaufnahme ineinanderliegender Hände.', 'Hands Close', 'Closeup of intertwined hands.'],
['Dankesumarmung', 'Umarmt jemanden, dem ihr danken wollt.', 'ThankYou Hug', 'Hug someone you want to thank.'],
['Erinnerungsstück', 'Haltet ein Erinnerungsstück in die Kamera.', 'Keepsake', 'Show a meaningful keepsake.'],
['Flüstern', 'Flüstert ein Kompliment ins Ohr.', 'Whisper Compliment', 'Whisper a compliment.'],
['Ruhemoment', 'Schließt kurz die Augen, atmet ein.', 'Quiet Moment', 'Close eyes, breathe in.'],
['Schulterblick', 'Kopf auf Schulter Geborgenheit.', 'Head on Shoulder', 'Head on shoulder warmth.'],
['Verlobungsstory', 'Zeigt „so wars“ mit Gesten.', 'Engagement Story', 'Act out “how it happened”.'],
['Geschenk öffnen', 'Kleines Geschenk öffnen Reaktion.', 'Open a Gift', 'Open a small gift reaction.'],
['Erste Erinnerung', 'Teilt eine kurze erste Erinnerung.', 'First Memory', 'Share a first memory.'],
['Wunsch ans Paar', 'Flüstert einen Wunsch fürs Paar.', 'Wish for Couple', 'Whisper a wish for the couple.'],
['Nahporträt', 'Sehr nahes Porträt sanftes Licht.', 'Close Portrait', 'Very close portrait, soft light.'],
['Schritt für Schritt', 'Langsam aufeinander zugehen.', 'Step by Step', 'Walk slowly towards each other.'],
['Gedankenpose', 'Denkt an etwas Schönes Klick.', 'Thoughtful Pose', 'Think of something lovely click.'],
['Ringkuss', 'Kuss auf die Hand mit Ring.', 'Ring Kiss', 'Kiss the hand with the ring.'],
['Stille Freude', 'Leises Lächeln, geschlossene Augen.', 'Quiet Joy', 'Soft smile, eyes closed.'],
['Vertrauter Halt', 'Arm einhaken, nah zusammen.', 'Linked Arms', 'Link arms, stand close.'],
['„Danke“ zeigen', 'Schreibt „Danke“ mit Händen/Karte.', 'Show “Thank You”', 'Show “Thank You” with hands/card.'],
['Ein Wort', 'Sagt gleichzeitig 1 Wort über den anderen.', 'One Word', 'Say one word about the other.'],
],
'Nostalgie' => [
['Altes Foto nachstellen', 'Stellt ein altes Familienfoto nach.', 'Recreate Old Photo', 'Recreate an old family photo.'],
['Schwarzweiß', 'Tut so, als wäre es 1960 s/w Look.', 'Black & White', 'Pretend its 1960 b/w mood.'],
['Tanz der Eltern', 'Imitiert den Tanz eurer Eltern.', 'Parents Dance', 'Imitate your parents dance.'],
['VintagePose', 'Hände gefaltet, altmodische Pose.', 'Vintage Pose', 'Folded hands, oldschool pose.'],
['Familienerbstück', 'Haltet ein Erbstück in die Kamera.', 'Heirloom', 'Show a family heirloom.'],
['Erstes Treffen', 'Stellt euer erstes Treffen nach.', 'First Meeting', 'Reenact your first meeting.'],
['Album aufschlagen', 'Album/HandyGalerie zeigen.', 'Open Album', 'Show an album/gallery.'],
['Zeitreise', 'Pose wie in eurer Lieblingsdekade.', 'Time Travel', 'Pose from your favorite decade.'],
['Brief an Zukunft', 'Haltet “Brief an uns” in Kamera.', 'Letter to Future', 'Hold “letter to us” to camera.'],
['Requisiten retro', 'RetroAccessoires improvisieren.', 'Retro Props', 'Improvise retro props.'],
['PolaroidLook', 'Haltet einen Rahmen wie Polaroid.', 'Polaroid Frame', 'Pose with a “polaroid” frame.'],
['Kinderfoto', 'Haltet ein Kinderfoto gleiche Pose.', 'Childhood Photo', 'Hold a childhood photo same pose.'],
['Alte Geste', 'Eine frühere Gewohnheit nachstellen.', 'Old Habit', 'Act out an old habit.'],
['Telefon von früher', 'Imitierte Telefongesten von früher.', 'Old Phone', 'Oldschool phone gesture.'],
['Hut & Handschuhe', 'Elegante 20erJahre Geste.', 'Hat & Gloves', 'Elegant 1920s gesture.'],
['NostalgieUmarmung', 'Langsame, lange Umarmung.', 'Nostalgic Hug', 'Slow, long hug.'],
['Erster Tanzschritt', 'Stellt den ersten Tanzschritt nach.', 'First Step', 'Reenact first dance step.'],
['Alte Kamera', 'Tut so, als würdet ihr analog knipsen.', 'Old Camera', 'Pretend to shoot on film.'],
['Kinoplakat', 'Stellt ein altes Filmplakat nach.', 'Movie Poster', 'Recreate a vintage movie poster.'],
['Handschrift', 'Schreibt Vornamen mit schöner Schrift.', 'Handwriting', 'Write names in neat script.'],
],
'Überraschung' => [
['KonfettiBoom', 'Unerwarteter Konfettischwall Klick!', 'Confetti Boom', 'Surprise confetti snap!'],
['Hinter dem Rücken', 'Zeigt etwas hinter dem Rücken vor.', 'Behind the Back', 'Reveal something from behind your back.'],
['Gäste tauchen auf', 'Neue Person springt ins Bild.', 'Popin Guest', 'A guest pops into frame.'],
['GeschenkReveal', 'Kleines Geschenk enthüllen.', 'Gift Reveal', 'Reveal a small gift.'],
['Plötzlicher Tanz', 'Unerwarteter Tanzmove!', 'Sudden Dance', 'Do a surprise dance move.'],
['Hand vor Mund', '„Oh!“Geste mit Augen groß.', 'Oh! Gesture', '“Oh!” gesture, big eyes.'],
['Wechsel der Plätze', 'Springt schnell die Plätze.', 'Switch Places', 'Quickly switch places.'],
['Falsche Richtung', 'Schaut alle woanders hin.', 'Look Away', 'Everyone looks elsewhere.'],
['FlipPose', 'Posenwechsel auf Kommando.', 'Flip Pose', 'Flip pose on cue.'],
['Plötzliches Lachen', 'Lachen aus dem Nichts.', 'Sudden Laugh', 'Burst into laughter.'],
['Versteckspiel', 'Versteckt euch hinter Deko.', 'Hide & Seek', 'Hide behind decor.'],
['Schattenspiel', 'Schatten an der Wand formen.', 'Shadow Play', 'Make shadows on the wall.'],
['SchnipsMoment', 'Schnippt und friert ein.', 'Snap & Freeze', 'Snap fingers and freeze.'],
['Unerwarteter Hut', 'Plötzlich Hut/Schal tauschen.', 'Surprise Hat', 'Swap hats/scarves.'],
['Zaubertrick', 'Kleiner „Magic“Trick.', 'Magic Trick', 'A tiny “magic” trick.'],
['Drehen & Stopp', 'Dreht euch Stopp Klick.', 'Spin & Stop', 'Spin and stop snap.'],
['Enger Zoom', 'Kamera ganz nah ran.', 'Tight Zoom', 'Get very close to the camera.'],
['Falscher Start', 'Tut so, als wärt ihr schon fertig.', 'False Start', 'Pretend you finished already.'],
['MiniSchreck', 'Erschreckt euch spielerisch.', 'Play Scare', 'Play a tiny scare.'],
['Jubelruf', 'Unerwarteter Jubel Arme hoch.', 'Cheer Burst', 'Sudden cheer hands up.'],
],
'Stolz' => [
['Siegerpose', 'Stolze Siegerpose mit Pokalgesten.', 'Victory Pose', 'Proud victory pose.'],
['Daumen hoch', 'Großer Daumen hoch zur Kamera.', 'Thumbs Up', 'Big thumbs up to camera.'],
['„Das haben wir geschafft“', 'Zeigt auf euch und lächelt stolz.', 'We Did It', 'Point at yourselves, proud smile.'],
['Orden anheften', 'Tut so, als würdet ihr Orden heften.', 'Pin a Medal', 'Pretend to pin a medal.'],
['Haltung zeigen', 'Aufrecht stehen, Brust raus.', 'Stand Tall', 'Stand upright, chest out.'],
['Meisterstück', 'Zeigt ein Ergebnis, auf das ihr stolz seid.', 'Masterpiece', 'Show a result youre proud of.'],
['TeamApplaus', 'Applaus füreinander, dann in Kamera.', 'Applaud Each Other', 'Applaud each other, then camera.'],
['Hand aufs Herz', 'Stolzer Blick, Hand aufs Herz.', 'Proud Heart', 'Hand on heart, proud look.'],
['Stehende Welle', 'Alle nacheinander aufstehen Klick.', 'Standing Wave', 'Stand up one by one snap.'],
['Schultern klopfen', 'Klopft euch freundlich auf die Schulter.', 'Pat on Back', 'Pat each other on the back.'],
['Banner hoch', 'Hebt ein Schild „Yeah!“ hoch.', 'Banner Up', 'Hold up a “Yeah!” sign.'],
['Heldenblick', 'Blick in die Ferne, Kinn hoch.', 'Hero Look', 'Look into distance, chin up.'],
['Trophäe improvisiert', 'ImproTrophäe in die Höhe.', 'Impro Trophy', 'Raise an improvised trophy.'],
['Da ist die Kamera', 'Selbstsicher direkt in die Linse.', 'Own the Lens', 'Confidently into the lens.'],
['Spitzenleistung', 'Zeigt die “Nummer Eins”Geste.', 'Number One', 'Show a “number one” gesture.'],
['Aufstellung', 'Stellt euch wie ein Teamfoto auf.', 'LineUp', 'Line up like a team photo.'],
['Partnerpose', 'Zwei nebeneinander stolz.', 'Partner Pose', 'Two side by side proud.'],
['Familienstolz', 'FamilienStolzpose mit Lächeln.', 'Family Pride', 'Family pride pose.'],
['Freundesstolz', 'FreundesStolzpose, Arm in Arm.', 'Friends Pride', 'Arms around pride.'],
['Applaus fürs Paar', 'Applaudiert dem Paar in Kamera.', 'Applause for Couple', 'Applaud the couple to camera.'],
],
'Teamgeist' => [
['Handkreis', 'Hände in der Mitte stapeln Go!', 'Hand Circle', 'Hands stacked in the middle.'],
['CongaLinie', 'CongaSchlange Foto von vorn.', 'Conga Line', 'Conga line shot from front.'],
['Telefonkette', 'Flüstern nacheinander letzter rufts.', 'Whisper Chain', 'Whisper chain last says it.'],
['Namensschrift', 'Formt den Namen des Paars.', 'Name Shape', 'Shape the couples name.'],
['Brückenbau', 'Bildet eine Menschenbrücke.', 'Human Bridge', 'Form a human bridge.'],
['Wimpelzug', 'Zieht imaginäre Wimpel hoch.', 'Pennant Pull', 'Pull up imaginary pennants.'],
['Schulter an Schulter', 'Dicht zusammen Teamblick.', 'Shoulder to Shoulder', 'Shoulder to shoulder.'],
['Wellenlauf', 'Eine Person läuft durch Spalier.', 'Guard of Honor', 'Run through a guard of honor.'],
['Spiegeln zu zweit', 'ZweierTeams spiegeln Posen.', 'Mirror in Pairs', 'Pairs mirror poses.'],
['Huckepack', 'Jemand trägt jemanden sicher!', 'Piggyback', 'Piggyback (safely!).'],
['DoppelCheers', 'Zweier“Cheers!” in Serie.', 'Double Cheers', 'Pairs do a quick cheers.'],
['TimingKlatscher', 'Alle klatschen gleichzeitig.', 'Sync Clap', 'Clap in sync.'],
['Zugseil', 'Tut so, als würdet ihr ein Seil ziehen.', 'Tug the Rope', 'Pretend tug of war.'],
['HandshakeKette', 'Kette aus Handschlägen.', 'Handshake Chain', 'Chain of handshakes.'],
['Formationsfoto', 'Stellt eine Form (Kreis/Herz).', 'Formation', 'Form a circle/heart.'],
['Staffelstab', 'Imitiere StaffelstabÜbergabe.', 'Relay Baton', 'Relay baton handoff.'],
['Teamruf', 'Alle rufen denselben Teamruf.', 'Team Chant', 'Shout a team chant.'],
['WirWelle', '“Wir!”Ruf, Hände hoch.', 'WeWave', 'Shout “We!” hands up.'],
['Gassenlauf', 'Gasse bilden jemand läuft durch.', 'Lane Run', 'Form a lane someone runs.'],
['Schulterreihe', 'Schultern fassen, Reihe bilden.', 'Linked Row', 'Link shoulders, form a row.'],
],
'Besinnlichkeit' => [
['Kerzenlicht', 'Portrait im Kerzenlicht (vorsichtig).', 'Candle Light', 'Portrait by candlelight (careful).'],
['Ruhepause', 'Augen schließen, tief atmen.', 'Quiet Pause', 'Close eyes, breathe deeply.'],
['Hand aufs Herz', 'Innere Ruhe Blick nach innen.', 'Hand on Heart', 'Look inward calm.'],
['Leises Lächeln', 'Ganz sanftes Lächeln.', 'Soft Smile', 'Very gentle smile.'],
['Fensterlicht', 'Seitliches Fensterlicht nutzen.', 'Window Light', 'Use side window light.'],
['Lesemoment', 'Jemand liest leise vor.', 'Reading Moment', 'Someone reads softly.'],
['Gebet/Wunsch', 'Ein stiller Wunsch oder Gebet.', 'Silent Wish', 'A quiet wish or prayer.'],
['DankeGeste', 'Dankbare Geste mit Blick zur Kamera.', 'Grateful Gesture', 'Grateful gesture to camera.'],
['Nahaufnahme Augen', 'Fokus auf die Augen.', 'Eyes CloseUp', 'Focus on the eyes.'],
['Hand in Hand', 'Langsame Bewegung der Hände.', 'Hands Moving', 'Slow hand movement.'],
['Anlehnen', 'Lehnt euch sanft aneinander.', 'Lean Gently', 'Lean gently together.'],
['Still stehen', '30 Sekunden ganz ruhig stehen.', 'Stand Still', 'Stand very still.'],
['Licht & Schatten', 'Sanftes Spiel aus Licht/Schatten.', 'Light & Shadow', 'Soft light/shadow play.'],
['Aufrichtigkeit', 'Direkter, ruhiger Blick.', 'Sincere Look', 'Direct, calm look.'],
['Hauch von Lächeln', 'Nur ein Hauch subtil.', 'Hint of Smile', 'Just a hint subtle.'],
['Hände im Schoß', 'Entspannte Hände im Schoß.', 'Hands in Lap', 'Hands resting in lap.'],
['Stiller Kreis', 'Kleiner Kreis, Köpfe zusammen.', 'Quiet Circle', 'Small circle, heads together.'],
['SchulterTouch', 'Kurzer Schulterkontakt, Ruhe.', 'Shoulder Touch', 'Brief shoulder touch.'],
['Atem zählen', 'Zählt 3 Atemzüge gemeinsam.', 'Count Breaths', 'Count 3 breaths together.'],
['Augen schließen', 'Alle Augen schließen Klick.', 'Eyes Closed', 'All close eyes click.'],
],
];
// Difficulty rotation
$difficulties = ['easy','easy','medium','easy','medium','hard'];
foreach (Emotion::all() as $emotion) {
$name = is_array($emotion->name) ? ($emotion->name['de'] ?? array_values($emotion->name)[0]) : (string) $emotion->name;
$list = $catalog[$name] ?? null;
if (!$list) continue; // skip unknown emotion labels
$created = 0; $order = 1;
foreach ($list as $i => $row) {
[$deTitle, $deDesc, $enTitle, $enDesc] = $row;
// Avoid duplicates: check same DE title within this emotion
$exists = Task::where('emotion_id', $emotion->id)
->where('title->de', $deTitle)
->exists();
if ($exists) { $order++; continue; }
Task::create([
'emotion_id' => $emotion->id,
'event_type_id' => null,
'title' => ['de' => $deTitle, 'en' => $enTitle],
'description' => ['de' => $deDesc, 'en' => $enDesc],
'example_text' => ['de' => null, 'en' => null],
'difficulty' => $difficulties[$i % count($difficulties)],
'sort_order' => $order++,
'is_active' => true,
]);
$created++;
}
// Ensure at least 20: if list shorter (shouldnt), cycle again with suffix
$i = 0;
while ($created < 20 && $i < count($list)) {
[$deTitle, $deDesc, $enTitle, $enDesc] = $list[$i];
$suffix = ' #' . ($created + 1);
Task::create([
'emotion_id' => $emotion->id,
'event_type_id' => null,
'title' => ['de' => $deTitle.$suffix, 'en' => $enTitle.$suffix],
'description' => ['de' => $deDesc, 'en' => $enDesc],
'example_text' => ['de' => null, 'en' => null],
'difficulty' => $difficulties[$created % count($difficulties)],
'sort_order' => $order++,
'is_active' => true,
]);
$created++; $i++;
}
}
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\EventType;
class EventTypesSeeder extends Seeder
{
public function run(): void
{
$types = [
['name' => ['de'=>'Hochzeit','en'=>'Wedding'], 'slug'=>'wedding', 'icon'=>'💍'],
['name' => ['de'=>'Weihnachten','en'=>'Christmas'], 'slug'=>'christmas', 'icon'=>'🎄'],
['name' => ['de'=>'Geburtstag','en'=>'Birthday'], 'slug'=>'birthday', 'icon'=>'🎂'],
['name' => ['de'=>'Firma','en'=>'Corporate'], 'slug'=>'corporate', 'icon'=>'🏢'],
];
foreach ($types as $t) {
EventType::updateOrCreate(['slug'=>$t['slug']], $t);
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
use App\Models\User;
class SuperAdminSeeder extends Seeder
{
public function run(): void
{
$email = env('ADMIN_EMAIL', 'admin@example.com');
$password = env('ADMIN_PASSWORD', 'ChangeMe123!');
User::updateOrCreate(['email'=>$email], [
'name' => 'Super Admin',
'password' => Hash::make($password),
'role' => 'super_admin',
]);
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\{Emotion, Task, EventType};
class TasksSeeder extends Seeder
{
public function run(): void
{
$seed = [
'Liebe' => [
['title'=>['de'=>'Kuss-Foto','en'=>'Kiss Photo'], 'description'=>['de'=>'Macht ein romantisches Kuss-Foto','en'=>'Take a romantic kiss photo'], 'difficulty'=>'easy'],
],
'Freude' => [
['title'=>['de'=>'Sprung-Foto','en'=>'Jump Photo'], 'description'=>['de'=>'Alle springen gleichzeitig!','en'=>'Everyone jump together!'], 'difficulty'=>'medium'],
],
'Teamgeist' => [
['title'=>['de'=>'High-Five-Runde','en'=>'High-Five Round'], 'description'=>['de'=>'Gebt euch High-Fives!','en'=>'Give each other high-fives!'], 'difficulty'=>'easy', 'event_type'=>'corporate'],
],
'Besinnlichkeit' => [
['title'=>['de'=>'Lichterglanz','en'=>'Glow of Lights'], 'description'=>['de'=>'Foto mit Lichterkette','en'=>'Photo with string lights'], 'difficulty'=>'easy', 'event_type'=>'christmas'],
],
];
$types = EventType::pluck('id','slug');
foreach ($seed as $emotionNameDe => $tasks) {
$emotion = Emotion::where('name->de', $emotionNameDe)->first();
if (!$emotion) continue;
foreach ($tasks as $t) {
Task::updateOrCreate([
'emotion_id' => $emotion->id,
'title->de' => $t['title']['de']
], [
'emotion_id' => $emotion->id,
'event_type_id' => isset($t['event_type']) && isset($types[$t['event_type']]) ? $types[$t['event_type']] : null,
'title' => $t['title'],
'description' => $t['description'],
'difficulty' => $t['difficulty'],
'is_active' => true,
]);
}
}
}
}

View File

@@ -0,0 +1,107 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\{Emotion, Task, EventType};
class WeddingTasksSeeder extends Seeder
{
public function run(): void
{
$weddingType = EventType::where('slug','wedding')->first();
if (!$weddingType) return;
// Helper to resolve emotion by English name (more stable given encoding issues)
$by = fn(string $en) => Emotion::where('name->en', $en)->first();
$emLove = $by('Love');
$emJoy = $by('Joy');
$emTouched = $by('Touched');
$emNostalgia = $by('Nostalgia');
$emSurprise = $by('Surprise');
$emPride = $by('Pride');
$tasks = [
// Liebe (10)
[$emLove, 'Kuss-Foto', 'Kiss Photo', 'Macht ein romantisches Kuss-Foto.', 'Take a romantic kiss photo.', 'easy'],
[$emLove, 'Herz mit Haenden', 'Hands Heart', 'Formt ein Herz mit euren Haenden.', 'Make a heart with your hands.', 'easy'],
[$emLove, 'Brautstrauss im Fokus', 'Bouquet Close-up', 'Brautstrauss nah an die Kamera halten.', 'Hold the bouquet close to the camera.', 'easy'],
[$emLove, 'Stirnkuss', 'Forehead Kiss', 'Sanfter Stirnkuss ganz verliebt.', 'A gentle forehead kiss.', 'easy'],
[$emLove, 'Herzensblick', 'Loving Gaze', 'Schaut euch nur in die Augen.', 'Look only into each others eyes.', 'easy'],
[$emLove, 'Schleiermoment', 'Veil Moment', 'Schleier ueber beide Koepfe legen.', 'Drape the veil over both of you.', 'medium'],
[$emLove, 'Ringnahaufnahme', 'Ring Macro', 'Zeigt eure Ringe nah an der Kamera.', 'Show your rings close to the camera.', 'easy'],
[$emLove, 'Hand in Hand', 'Holding Hands', 'Haende greifen, Kamera im Hintergrund.', 'Hold hands with the camera behind.', 'easy'],
[$emLove, 'Tanzschritt', 'First Dance Step', 'Ein kleiner Tanzschritt fuer das Foto.', 'A small dance step for the photo.', 'medium'],
[$emLove, 'Kuss hinter dem Strauss', 'Peek-a-boo Kiss', 'Kuss hinter dem Brautstrauss verstecken.', 'Hide a kiss behind the bouquet.', 'easy'],
// Freude (10)
[$emJoy, 'Sprung-Foto', 'Jump Photo', 'Alle springen gleichzeitig!', 'Everyone jump together!', 'medium'],
[$emJoy, 'Lachendes Gruppenfoto', 'Laughing Group', 'Erzaehlt einen Witz und klick!', 'Tell a joke and click!', 'easy'],
[$emJoy, 'Konfetti-Moment', 'Confetti Moment', 'Konfetti werfen (oder so tun).', 'Throw confetti (or pretend).', 'easy'],
[$emJoy, 'Cheers!', 'Cheers!', 'Glaser anstossen in die Kamera.', 'Clink glasses toward the camera.', 'easy'],
[$emJoy, 'Freudensprung zu zweit', 'Couple Jump', 'Brautpaar springt gemeinsam.', 'Couple jumps together.', 'medium'],
[$emJoy, 'Luftkuesse', 'Blowing Kisses', 'Luftkuesse in Richtung Kamera.', 'Blow kisses toward the camera.', 'easy'],
[$emJoy, 'Scherzbrillen', 'Silly Glasses', 'Accessoires aufsetzen und lachen.', 'Wear props and laugh.', 'easy'],
[$emJoy, 'Freudige Umarmung', 'Happy Hug', 'Grosse Umarmung in der Runde.', 'Big group hug.', 'easy'],
[$emJoy, 'Daumen hoch', 'Thumbs Up', 'Alle Daumen nach oben!', 'Thumbs up, everyone!', 'easy'],
[$emJoy, 'Victory-Zeichen', 'Peace Sign', 'Peace-Zeichen in die Kamera.', 'Peace sign to the camera.', 'easy'],
// Touched (8)
[$emTouched, 'Traenen des Gluecks', 'Tears of Joy', 'Sanftes Traenchen abtupfen.', 'Dab a happy tear.', 'easy'],
[$emTouched, 'Eltern-Umarmung', 'Parents Hug', 'Umarmung mit Eltern oder Trauzeugen.', 'Hug with parents or witnesses.', 'easy'],
[$emTouched, 'Hand aufs Herz', 'Hand on Heart', 'Hand aufs Herz ehrlicher Moment.', 'Hand on heart — a sincere moment.', 'easy'],
[$emTouched, 'Danke-Geste', 'Thank You Gesture', '„Danke“-Geste in die Kamera.', 'A “thank you” gesture to the camera.', 'easy'],
[$emTouched, 'Enger Nasenstups', 'Nose Boop', 'Stirn an Stirn, sanfter Nasenstups.', 'Forehead to forehead, a soft nose boop.', 'easy'],
[$emTouched, 'Geliebtes Andenken', 'Keepsake', 'Ein bedeutsames Andenken zeigen.', 'Show a meaningful keepsake.', 'easy'],
[$emTouched, 'Leise Worte', 'Whisper', 'Ein leises Kompliment ins Ohr.', 'Whisper a compliment.', 'easy'],
[$emTouched, 'Ruhe vor dem Sturm', 'Quiet Moment', 'Augen schliessen, tief durchatmen.', 'Close eyes and take a deep breath.', 'easy'],
// Nostalgia (8)
[$emNostalgia, 'Altes Foto nachstellen', 'Recreate Old Photo', 'Ein altes Familienfoto nachstellen.', 'Recreate an old family photo.', 'medium'],
[$emNostalgia, 'Kindheits-Pose', 'Childhood Pose', 'Lieblingspose aus der Kindheit.', 'Favorite childhood pose.', 'easy'],
[$emNostalgia, 'Erste Nachricht', 'First Message', 'Handys mit erster Nachricht zeigen.', 'Show your first message on phones.', 'medium'],
[$emNostalgia, 'Ringbox Vintage', 'Vintage Ring Box', 'Ringbox im Vintage-Stil inszenieren.', 'Stage the vintage ring box.', 'easy'],
[$emNostalgia, 'Familienerbstueck', 'Family Heirloom', 'Ein Familienerbstueck ins Bild.', 'Feature a family heirloom.', 'easy'],
[$emNostalgia, 'Schwarzweiss', 'Black & White', 'Schwarzweiss-Pose fuer klassisches Foto.', 'Pose for a black & white shot.', 'easy'],
[$emNostalgia, 'Erster Tanz (Mini)', 'Mini First Dance', 'Ein Schritt vom ersten Tanz.', 'One step of the first dance.', 'easy'],
[$emNostalgia, 'Gastebuch-Moment', 'Guestbook Moment', 'Eintrag ins Gaestebuch festhalten.', 'Capture a guestbook entry.', 'easy'],
// Surprise (7)
[$emSurprise, 'Photobomb!', 'Photobomb!', 'Ueberraschung im Hintergrund.', 'Surprise in the background.', 'easy'],
[$emSurprise, 'Erster Blick', 'First Look', 'Reaktion beim First Look nachstellen.', 'Recreate a first-look reaction.', 'medium'],
[$emSurprise, 'Ueberraschungs-Dip', 'Surprise Dip', 'Ueberraschender Tanz-Dip.', 'A surprise dance dip.', 'medium'],
[$emSurprise, 'Ballon-Pop', 'Balloon Pop', 'Ballon zerplatzen (oder so tun).', 'Pop a balloon (or pretend).', 'easy'],
[$emSurprise, 'Hutwechsel', 'Hat Swap', 'Huete/Accessoires spontan tauschen.', 'Swap hats/props on the fly.', 'easy'],
[$emSurprise, 'Versteckspiel', 'Peekaboo', 'Hinter Deko kurz verstecken.', 'Peek from behind decor.', 'easy'],
[$emSurprise, 'Gespiegelte Pose', 'Mirror Pose', 'Gegensaetzliche, gespiegelte Pose.', 'Opposite mirrored pose.', 'easy'],
// Pride (7)
[$emPride, 'Just Married', 'Just Married', '„Just Married“-Schild zeigen.', 'Show a “Just Married” sign.', 'easy'],
[$emPride, 'Ring zeigen', 'Show the Ring', 'Ring zur Kamera strecken.', 'Stretch ring toward the camera.', 'easy'],
[$emPride, 'Brautkleid-Detail', 'Dress Detail', 'Lieblingsdetail am Kleid zeigen.', 'Show a favorite dress detail.', 'easy'],
[$emPride, 'Anzug-Detail', 'Suit Detail', 'Manschette/Knopfloch zeigen.', 'Show cuff/ boutonniere.', 'easy'],
[$emPride, 'Team Braut', 'Team Bride', '„Team Braut“-Gruppenpose.', '“Team Bride” group pose.', 'easy'],
[$emPride, 'Team Braeutigam', 'Team Groom', '„Team Braeutigam“-Gruppenpose.', '“Team Groom” group pose.', 'easy'],
[$emPride, 'Siegesschrei', 'Victory Cheer', 'Arme hoch, Jubel in die Kamera.', 'Arms up, cheer to the camera.', 'easy'],
];
$sort = 1;
foreach ($tasks as [$emotion, $titleDe, $titleEn, $descDe, $descEn, $difficulty]) {
if (!$emotion) continue;
Task::updateOrCreate([
'emotion_id' => $emotion->id,
'title->de' => $titleDe,
], [
'emotion_id' => $emotion->id,
'event_type_id' => $weddingType->id,
'title' => ['de' => $titleDe, 'en' => $titleEn],
'description' => ['de' => $descDe, 'en' => $descEn],
'difficulty' => $difficulty,
'sort_order' => $sort++,
'is_active' => true,
]);
}
}
}