zu fabricjs gewechselt, noch nicht funktionsfähig

This commit is contained in:
Codex Agent
2025-10-31 20:19:09 +01:00
parent 06df61f706
commit eb0c31c90b
33 changed files with 7718 additions and 2062 deletions

View File

@@ -12,108 +12,149 @@ class JoinTokenLayoutRegistry
* @var array<string, array>
*/
private const LAYOUTS = [
'modern-poster' => [
'id' => 'modern-poster',
'name' => 'Modern Poster',
'subtitle' => 'Große, auffällige Fläche perfekt für den Eingangsbereich.',
'description' => 'Helle Posteroptik mit diagonalem Farbband und deutlicher Call-to-Action.',
'paper' => 'a4',
'orientation' => 'portrait',
'background' => '#F8FAFC',
'text' => '#0F172A',
'accent' => '#6366F1',
'secondary' => '#CBD5F5',
'badge' => '#0EA5E9',
'qr' => ['size_px' => 500],
'svg' => ['width' => 1080, 'height' => 1520],
'instructions' => [
'Scanne den Code und tritt dem Event direkt bei.',
'Speichere deine Lieblingsmomente mit Foto-Uploads.',
'Merke dir dein Gäste-Pseudonym für Likes und Badges.',
],
],
'elegant-frame' => [
'id' => 'elegant-frame',
'name' => 'Elegant Frame',
'subtitle' => 'Ein ruhiges Layout mit Fokus auf Eleganz.',
'description' => 'Serifen-Schrift, pastellige Flächen und dezente Rahmen für elegante Anlässe.',
'evergreen-vows' => [
'id' => 'evergreen-vows',
'name' => 'Evergreen Vows',
'subtitle' => 'Romantische Einladung für Trauung & Empfang.',
'description' => 'Weiche Pastelltöne, florale Akzente und viel Raum für eine herzliche Begrüßung.',
'paper' => 'a4',
'orientation' => 'portrait',
'background' => '#FBF7F2',
'text' => '#2B1B13',
'accent' => '#C08457',
'secondary' => '#E6D5C3',
'badge' => '#8B5CF6',
'qr' => ['size_px' => 460],
'svg' => ['width' => 1080, 'height' => 1520],
'instructions' => [
'QR-Code scannen oder Link im Browser eingeben.',
'Name eingeben, Lieblingssprache auswählen und loslegen.',
'Zeige diesen Druck am Empfang als Orientierung für Gäste.',
],
],
'bold-gradient' => [
'id' => 'bold-gradient',
'name' => 'Bold Gradient',
'subtitle' => 'Farbverlauf mit starkem Kontrast.',
'description' => 'Ein kraftvolles Farbstatement mit großem QR-Code ideal für Partys.',
'paper' => 'a4',
'orientation' => 'portrait',
'background' => '#F97316',
'background_gradient' => [
'angle' => 190,
'stops' => ['#F97316', '#EC4899', '#8B5CF6'],
'angle' => 165,
'stops' => ['#FBF7F2', '#FDECEF', '#F4F0FF'],
],
'text' => '#FFFFFF',
'accent' => '#FFFFFF',
'secondary' => 'rgba(255,255,255,0.72)',
'badge' => '#1E293B',
'qr' => ['size_px' => 540],
'svg' => ['width' => 1080, 'height' => 1520],
'text' => '#2C1A27',
'accent' => '#B85C76',
'secondary' => '#E7D6DC',
'badge' => '#7A9375',
'badge_label' => 'Unsere Gästegalerie',
'instructions_heading' => 'So seid ihr dabei',
'link_heading' => 'Falls der Scan nicht klappt',
'cta_label' => 'Gästegalerie öffnen',
'cta_caption' => 'Jetzt Erinnerungen sammeln',
'qr' => ['size_px' => 520],
'svg' => ['width' => 1240, 'height' => 1754],
'instructions' => [
'Sofort scannen der QR-Code führt direkt zum Event.',
'Fotos knipsen, Challenges lösen und Likes sammeln.',
'Teile den Link mit Freund:innen, falls kein Scan möglich ist.',
'QR-Code scannen und mit eurem Lieblingsnamen anmelden.',
'Ein paar Schnappschüsse teilen gern auch Behind-the-Scenes!',
'Likes vergeben und Grüße für das Brautpaar schreiben.',
],
],
'photo-strip' => [
'id' => 'photo-strip',
'name' => 'Photo Strip',
'subtitle' => 'Layout mit Fotostreifen-Anmutung und Checkliste.',
'description' => 'Horizontale Teilung, Platz für Hinweise und Storytelling.',
'midnight-gala' => [
'id' => 'midnight-gala',
'name' => 'Midnight Gala',
'subtitle' => 'Eleganter Auftritt für Corporate Events & Galas.',
'description' => 'Dunkle Bühne mit goldenen Akzenten und kräftiger Typografie.',
'paper' => 'a4',
'orientation' => 'portrait',
'background' => '#FFFFFF',
'text' => '#111827',
'accent' => '#0EA5E9',
'secondary' => '#94A3B8',
'badge' => '#334155',
'qr' => ['size_px' => 500],
'svg' => ['width' => 1080, 'height' => 1520],
'background' => '#0B132B',
'background_gradient' => [
'angle' => 200,
'stops' => ['#0B132B', '#1C2541', '#274690'],
],
'text' => '#F8FAFC',
'accent' => '#F9C74F',
'secondary' => '#4E5D8F',
'badge' => '#F94144',
'badge_label' => 'Team Lounge Access',
'instructions_heading' => 'In drei Schritten bereit',
'link_heading' => 'Link teilen statt scannen',
'cta_label' => 'Jetzt Event-Hub öffnen',
'cta_caption' => 'Programm, Uploads & Highlights',
'qr' => ['size_px' => 560],
'svg' => ['width' => 1240, 'height' => 1754],
'instructions' => [
'Schritt 1: QR-Code scannen oder Kurzlink nutzen.',
'Schritt 2: Profilname eingeben kreativ sein!',
'Schritt 3: Fotos hochladen und Teamaufgaben lösen.',
'QR-Code scannen oder Kurzlink eingeben.',
'Mit Firmen-E-Mail anmelden und Zugang bestätigen.',
'Agenda verfolgen, Fotos teilen und Highlights voten.',
],
],
'minimal-card' => [
'id' => 'minimal-card',
'name' => 'Minimal Card',
'subtitle' => 'Kleine Karte mehrfach druckbar als Tischaufsteller.',
'description' => 'Schlichtes Kartenformat mit klarer Typografie und viel Weißraum.',
'garden-brunch' => [
'id' => 'garden-brunch',
'name' => 'Garden Brunch',
'subtitle' => 'Luftiges Layout für Tages-Events & Familienfeiern.',
'description' => 'Sanfte Grüntöne, natürliche Formen und Platz für Hinweise.',
'paper' => 'a4',
'orientation' => 'portrait',
'background' => '#F9FAFB',
'text' => '#111827',
'accent' => '#9333EA',
'secondary' => '#E0E7FF',
'badge' => '#64748B',
'qr' => ['size_px' => 440],
'svg' => ['width' => 1080, 'height' => 1520],
'background' => '#F6F9F4',
'background_gradient' => [
'angle' => 120,
'stops' => ['#F6F9F4', '#EEF5E7', '#F8FAF0'],
],
'text' => '#2F4030',
'accent' => '#6BAA75',
'secondary' => '#DDE9D8',
'badge' => '#F1C376',
'badge_label' => 'Brunch Fotostation',
'instructions_heading' => 'So funktionierts',
'link_heading' => 'Alternativ zum Scannen',
'cta_label' => 'Gästebuch öffnen',
'cta_caption' => 'Eure Grüße festhalten',
'qr' => ['size_px' => 520],
'svg' => ['width' => 1240, 'height' => 1754],
'instructions' => [
'Code scannen, Profil erstellen, Erinnerungen festhalten.',
'Halte diese Karte an mehreren Stellen bereit.',
'Für Ausdrucke auf 200g/m² Kartenpapier empfohlen.',
'QR-Code scannen und Namen eintragen.',
'Lieblingsfoto hochladen oder neue Momente festhalten.',
'Aufgaben ausprobieren und anderen ein Herz dalassen.',
],
],
'sparkler-soiree' => [
'id' => 'sparkler-soiree',
'name' => 'Sparkler Soirée',
'subtitle' => 'Abendliches Layout mit funkelndem Verlauf.',
'description' => 'Dynamische Typografie mit zentralem Fokus auf dem QR-Code.',
'paper' => 'a4',
'orientation' => 'portrait',
'background' => '#1B1A44',
'background_gradient' => [
'angle' => 205,
'stops' => ['#1B1A44', '#42275A', '#734B8F'],
],
'text' => '#FDF7FF',
'accent' => '#F9A826',
'secondary' => '#DDB7FF',
'badge' => '#FF6F61',
'badge_label' => 'Night Shots',
'instructions_heading' => 'Step-by-Step',
'link_heading' => 'QR funktioniert nicht?',
'cta_label' => 'Partyfeed starten',
'cta_caption' => 'Momente live teilen',
'qr' => ['size_px' => 560],
'svg' => ['width' => 1240, 'height' => 1754],
'instructions' => [
'Code scannen und kurz registrieren.',
'Spotlights & Challenges entdecken.',
'Fotos hochladen und die besten Shots voten.',
],
],
'confetti-bash' => [
'id' => 'confetti-bash',
'name' => 'Confetti Bash',
'subtitle' => 'Verspielter Look für Geburtstage & Jubiläen.',
'description' => 'Konfetti-Sprenkel, fröhliche Farben und viel Platz für Hinweise.',
'paper' => 'a4',
'orientation' => 'portrait',
'background' => '#FFF9F0',
'background_gradient' => [
'angle' => 145,
'stops' => ['#FFF9F0', '#FFEFEF', '#FFF5D6'],
],
'text' => '#31291F',
'accent' => '#FF6F61',
'secondary' => '#F9D6A5',
'badge' => '#4E88FF',
'badge_label' => 'Party-Schnappschüsse',
'instructions_heading' => 'Leg direkt los',
'link_heading' => 'Kurzlink für Gäste',
'cta_label' => 'Zur Geburtstagswand',
'cta_caption' => 'Fotos & Grüße posten',
'qr' => ['size_px' => 520],
'svg' => ['width' => 1240, 'height' => 1754],
'instructions' => [
'QR-Code scannen und Wunschname auswählen.',
'Dein erstes Foto oder Video hochladen.',
'Freunde einladen, Likes vergeben und gemeinsam feiern!',
],
],
];
@@ -182,18 +223,39 @@ class JoinTokenLayoutRegistry
'link_label' => null,
'logo_url' => null,
'qr' => [
'size_px' => 320,
'size_px' => 360,
],
'svg' => [
'width' => 1080,
'height' => 1520,
'width' => 1240,
'height' => 1754,
],
'background_gradient' => null,
'instructions' => [],
'formats' => ['pdf', 'svg'],
'formats' => ['pdf', 'png'],
];
return array_replace_recursive($defaults, $layout);
$normalized = array_replace_recursive($defaults, $layout);
$formats = $normalized['formats'] ?? ['pdf', 'png'];
if (! is_array($formats)) {
$formats = [$formats];
}
$normalizedFormats = [];
foreach ($formats as $format) {
$value = strtolower((string) $format);
if ($value === 'svg') {
$value = 'png';
}
if (in_array($value, ['pdf', 'png'], true) && ! in_array($value, $normalizedFormats, true)) {
$normalizedFormats[] = $value;
}
}
$normalized['formats'] = $normalizedFormats ?: ['pdf', 'png'];
return $normalized;
}
private static function fromModel(InviteLayout $layout): array
@@ -229,7 +291,7 @@ class JoinTokenLayoutRegistry
'width' => $preview['svg']['width'] ?? $options['svg']['width'] ?? $preview['svg_width'] ?? $options['svg_width'] ?? null,
'height' => $preview['svg']['height'] ?? $options['svg']['height'] ?? $preview['svg_height'] ?? $options['svg_height'] ?? null,
]),
'formats' => $options['formats'] ?? ['pdf', 'svg'],
'formats' => $options['formats'] ?? ['pdf', 'png'],
'instructions' => $instructions,
], fn ($value) => $value !== null && $value !== []);
}
@@ -243,13 +305,19 @@ class JoinTokenLayoutRegistry
public static function toResponse(callable $urlResolver): array
{
return array_map(function (array $layout) use ($urlResolver) {
$formats = $layout['formats'] ?? ['pdf', 'svg'];
$formats = $layout['formats'] ?? ['pdf', 'png'];
return [
'id' => $layout['id'],
'name' => $layout['name'],
'description' => $layout['description'],
'subtitle' => $layout['subtitle'],
'badge_label' => $layout['badge_label'] ?? null,
'instructions_heading' => $layout['instructions_heading'] ?? null,
'link_heading' => $layout['link_heading'] ?? null,
'cta_label' => $layout['cta_label'] ?? null,
'cta_caption' => $layout['cta_caption'] ?? null,
'instructions' => $layout['instructions'] ?? [],
'preview' => [
'background' => $layout['background'],
'background_gradient' => $layout['background_gradient'],