feat: extend event toolkit and polish guest pwa

This commit is contained in:
Codex Agent
2025-10-28 18:28:22 +01:00
parent f29067f570
commit a7bbf230fd
45 changed files with 3809 additions and 351 deletions

View File

@@ -25,6 +25,7 @@ class DatabaseSeeder extends Seeder
TasksSeeder::class,
EventTasksSeeder::class,
TaskCollectionsSeeder::class,
InviteLayoutSeeder::class,
]);
// Seed demo and admin data

View File

@@ -0,0 +1,57 @@
<?php
namespace Database\Seeders;
use App\Models\InviteLayout;
use App\Support\JoinTokenLayoutRegistry;
use Illuminate\Database\Seeder;
use ReflectionClass;
class InviteLayoutSeeder extends Seeder
{
public function run(): void
{
$reflection = new ReflectionClass(JoinTokenLayoutRegistry::class);
$layoutsConst = $reflection->getReflectionConstant('LAYOUTS');
$fallbackLayouts = $layoutsConst ? $layoutsConst->getValue() : [];
foreach ($fallbackLayouts as $layout) {
$preview = [
'background' => $layout['background'] ?? null,
'background_gradient' => $layout['background_gradient'] ?? null,
'accent' => $layout['accent'] ?? null,
'secondary' => $layout['secondary'] ?? null,
'text' => $layout['text'] ?? null,
'badge' => $layout['badge'] ?? null,
'qr' => $layout['qr'] ?? ['size_px' => 320],
'svg' => $layout['svg'] ?? ['width' => 1080, 'height' => 1520],
];
$options = [
'badge_label' => $layout['badge_label'] ?? 'Digitale Gästebox',
'instructions_heading' => $layout['instructions_heading'] ?? "So funktioniert's",
'link_heading' => $layout['link_heading'] ?? 'Alternative zum Einscannen',
'cta_label' => $layout['cta_label'] ?? 'Scan mich & starte direkt',
'cta_caption' => $layout['cta_caption'] ?? 'Scan mich & starte direkt',
'link_label' => $layout['link_label'] ?? null,
'logo_url' => $layout['logo_url'] ?? null,
'formats' => $layout['formats'] ?? ['pdf', 'svg'],
];
InviteLayout::updateOrCreate(
['slug' => $layout['id']],
[
'name' => $layout['name'],
'subtitle' => $layout['subtitle'] ?? null,
'description' => $layout['description'] ?? null,
'paper' => $layout['paper'] ?? 'a4',
'orientation' => $layout['orientation'] ?? 'portrait',
'preview' => $preview,
'layout_options' => $options,
'instructions' => $layout['instructions'] ?? [],
'is_active' => true,
]
);
}
}
}

View File

@@ -2,9 +2,12 @@
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\Str;
use App\Models\{Emotion, Task, EventType};
class TasksSeeder extends Seeder
{
@@ -25,40 +28,83 @@ class TasksSeeder extends Seeder
$seed = [
'Liebe' => [
['title'=>['de'=>'Kuss-Foto','en'=>'Kiss Photo'], 'description'=>['de'=>'Macht ein romantisches Kuss-Foto','en'=>'Take a romantic kiss photo'], 'difficulty'=>'easy'],
['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'],
['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'],
['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'],
['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');
$types = EventType::pluck('id', 'slug');
$position = 10;
foreach ($seed as $emotionNameDe => $tasks) {
$emotion = Emotion::where('name->de', $emotionNameDe)->first();
if (!$emotion) continue;
foreach ($tasks as $t) {
$slugBase = Str::slug($t['title']['en'] ?? $t['title']['de']);
$slug = $slugBase ? $slugBase . '-' . $emotion->id : Str::uuid()->toString();
Task::updateOrCreate([
'slug' => $slug,
], [
if (! $emotion) {
continue;
}
$emotionTranslations = is_array($emotion->name) ? $emotion->name : [];
$emotionNameEn = $emotionTranslations['en'] ?? $emotionNameDe;
$collection = TaskCollection::updateOrCreate(
[
'tenant_id' => $demoTenant->id,
'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,
'sort_order' => $t['sort_order'] ?? 0,
]);
'slug' => 'demo-'.Str::slug($emotionTranslations['en'] ?? $emotionNameDe),
],
[
'name_translations' => [
'de' => $emotionNameDe,
'en' => $emotionNameEn,
],
'description_translations' => [
'de' => 'Aufgaben rund um '.$emotionNameDe.'.',
'en' => 'Prompts inspired by '.$emotionNameEn.'.',
],
'event_type_id' => null,
'is_default' => false,
'position' => $position,
]
);
$position += 10;
$syncPayload = [];
foreach ($tasks as $index => $t) {
$slugBase = Str::slug($t['title']['en'] ?? $t['title']['de']);
$slug = $slugBase ? $slugBase.'-'.$emotion->id : Str::uuid()->toString();
$sortOrder = $t['sort_order'] ?? (($index + 1) * 10);
$task = Task::updateOrCreate(
[
'slug' => $slug,
],
[
'tenant_id' => $demoTenant->id,
'emotion_id' => $emotion->id,
'event_type_id' => isset($t['event_type'], $types[$t['event_type']]) ? $types[$t['event_type']] : null,
'title' => $t['title'],
'description' => $t['description'],
'difficulty' => $t['difficulty'],
'is_active' => true,
'sort_order' => $sortOrder,
'collection_id' => $collection->id,
]
);
$syncPayload[$task->id] = ['sort_order' => $sortOrder];
}
if (! empty($syncPayload)) {
$collection->tasks()->syncWithoutDetaching($syncPayload);
}
}
}