added more translations and added the new layout wizard
This commit is contained in:
@@ -132,6 +132,7 @@ class EventJoinTokenController extends Controller
|
||||
'metadata.layout_customization.instructions.*' => ['nullable', 'string', 'max:160'],
|
||||
'metadata.layout_customization.logo_url' => ['nullable', 'string', 'max:2048'],
|
||||
'metadata.layout_customization.logo_data_url' => ['nullable', 'string'],
|
||||
'metadata.layout_customization.background_preset' => ['nullable', 'string', 'max:120'],
|
||||
'metadata.layout_customization.accent_color' => ['nullable', 'string', 'regex:/^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/'],
|
||||
'metadata.layout_customization.text_color' => ['nullable', 'string', 'regex:/^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/'],
|
||||
'metadata.layout_customization.background_color' => ['nullable', 'string', 'regex:/^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/'],
|
||||
|
||||
@@ -14,6 +14,17 @@ use SimpleSoftwareIO\QrCode\Facades\QrCode;
|
||||
|
||||
class EventJoinTokenLayoutController extends Controller
|
||||
{
|
||||
/**
|
||||
* Mapping of preset keys to portrait background assets.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private const BACKGROUND_PRESETS = [
|
||||
'bg-blue-floral' => 'storage/layouts/backgrounds-portrait/bg-blue-floral.png',
|
||||
'bg-goldframe' => 'storage/layouts/backgrounds-portrait/bg-goldframe.png',
|
||||
'gr-green-floral' => 'storage/layouts/backgrounds-portrait/gr-green-floral.png',
|
||||
];
|
||||
|
||||
public function index(Request $request, Event $event, EventJoinToken $joinToken)
|
||||
{
|
||||
$this->ensureBelongsToEvent($event, $joinToken);
|
||||
@@ -59,6 +70,7 @@ class EventJoinTokenLayoutController extends Controller
|
||||
|
||||
$backgroundStyle = $this->buildBackgroundStyle($layoutConfig);
|
||||
$eventName = $this->resolveEventName($event);
|
||||
$backgroundImage = $layoutConfig['background_image'] ?? null;
|
||||
|
||||
$viewData = [
|
||||
'layout' => $layoutConfig,
|
||||
@@ -68,6 +80,7 @@ class EventJoinTokenLayoutController extends Controller
|
||||
'tokenUrl' => $tokenUrl,
|
||||
'qrPngDataUri' => $qrPngDataUri,
|
||||
'backgroundStyle' => $backgroundStyle,
|
||||
'backgroundImage' => $backgroundImage,
|
||||
'customization' => $joinToken->metadata['layout_customization'] ?? null,
|
||||
'advancedLayout' => $this->buildAdvancedLayout(
|
||||
$layoutConfig,
|
||||
@@ -200,11 +213,23 @@ class EventJoinTokenLayoutController extends Controller
|
||||
$layout['logo_url'] = $customization['logo_url'];
|
||||
}
|
||||
|
||||
if (! empty($customization['background_preset']) && is_string($customization['background_preset'])) {
|
||||
$presetImage = $this->resolveBackgroundPreset($customization['background_preset']);
|
||||
if ($presetImage) {
|
||||
$layout['background_image'] = $presetImage;
|
||||
$layout['background_preset'] = $customization['background_preset'];
|
||||
}
|
||||
}
|
||||
|
||||
return $layout;
|
||||
}
|
||||
|
||||
private function buildBackgroundStyle(array $layout): string
|
||||
{
|
||||
if (! empty($layout['background_image']) && is_string($layout['background_image'])) {
|
||||
return sprintf('url(%s) center center / cover no-repeat', $layout['background_image']);
|
||||
}
|
||||
|
||||
$gradient = $layout['background_gradient'] ?? null;
|
||||
|
||||
if (is_array($gradient) && ! empty($gradient['stops'])) {
|
||||
@@ -239,6 +264,11 @@ class EventJoinTokenLayoutController extends Controller
|
||||
$text = $layout['text'] ?? '#0F172A';
|
||||
$secondary = $layout['secondary'] ?? '#1F2937';
|
||||
$badge = $layout['badge'] ?? $accent;
|
||||
$backgroundImage = $layout['background_image'] ?? null;
|
||||
|
||||
if (! $backgroundImage && ! empty($customization['background_preset']) && is_string($customization['background_preset'])) {
|
||||
$backgroundImage = $this->resolveBackgroundPreset($customization['background_preset']);
|
||||
}
|
||||
|
||||
$resolved = [];
|
||||
|
||||
@@ -306,6 +336,7 @@ class EventJoinTokenLayoutController extends Controller
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'background' => $layout['background'] ?? '#FFFFFF',
|
||||
'background_image' => $backgroundImage,
|
||||
'background_gradient' => $layout['background_gradient'] ?? null,
|
||||
'accent' => $accent,
|
||||
'text' => $text,
|
||||
@@ -317,6 +348,30 @@ class EventJoinTokenLayoutController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
private function resolveBackgroundPreset(string $preset): ?string
|
||||
{
|
||||
$path = self::BACKGROUND_PRESETS[$preset] ?? null;
|
||||
|
||||
if (! $path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$fullPath = public_path($path);
|
||||
|
||||
if (! file_exists($fullPath) || ! is_readable($fullPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$mime = mime_content_type($fullPath) ?: 'image/png';
|
||||
$data = @file_get_contents($fullPath);
|
||||
|
||||
if ($data === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return 'data:'.$mime.';base64,'.base64_encode($data);
|
||||
}
|
||||
|
||||
private function resolveElementContent(string $type, array $customization, array $layout, string $eventName, string $tokenUrl, $fallback = null): ?string
|
||||
{
|
||||
return match ($type) {
|
||||
|
||||
@@ -12,6 +12,39 @@ class JoinTokenLayoutRegistry
|
||||
* @var array<string, array>
|
||||
*/
|
||||
private const LAYOUTS = [
|
||||
'foldable-table-a5' => [
|
||||
'id' => 'foldable-table-a5',
|
||||
'name' => 'Foldable Table Card (A5)',
|
||||
'subtitle' => 'Doppelseitige Tischkarte zum Falten – QR vorn & hinten.',
|
||||
'description' => 'Zwei identische Hälften auf A4 quer, rechte Seite gespiegelt für sauberes Falten.',
|
||||
'paper' => 'a4',
|
||||
'orientation' => 'landscape',
|
||||
'panel_mode' => 'double-mirror',
|
||||
'container_padding_px' => 28,
|
||||
'background' => '#F8FAFC',
|
||||
'background_gradient' => [
|
||||
'angle' => 180,
|
||||
'stops' => ['#F8FAFC', '#EEF2FF', '#F8FAFC'],
|
||||
],
|
||||
'text' => '#0F172A',
|
||||
'accent' => '#2563EB',
|
||||
'secondary' => '#E0E7FF',
|
||||
'badge' => '#1D4ED8',
|
||||
'badge_label' => 'Digitale Gästebox',
|
||||
'instructions_heading' => "So funktioniert's",
|
||||
'link_heading' => 'Alternative zum Einscannen',
|
||||
'cta_label' => 'Scan & loslegen',
|
||||
'cta_caption' => 'Kein Login nötig',
|
||||
'link_label' => 'fotospiel.app/DEINCODE',
|
||||
'qr' => ['size_px' => 520],
|
||||
'svg' => ['width' => 1754, 'height' => 1240],
|
||||
'instructions' => [
|
||||
'QR-Code scannen oder Kurzlink öffnen.',
|
||||
'Anzeigenamen wählen – kein Account nötig.',
|
||||
'Fotos hochladen, liken & kommentieren.',
|
||||
'Challenges spielen und Punkte sammeln.',
|
||||
],
|
||||
],
|
||||
'evergreen-vows' => [
|
||||
'id' => 'evergreen-vows',
|
||||
'name' => 'Evergreen Vows',
|
||||
@@ -225,6 +258,8 @@ class JoinTokenLayoutRegistry
|
||||
'description' => '',
|
||||
'paper' => 'a4',
|
||||
'orientation' => 'portrait',
|
||||
'panel_mode' => null,
|
||||
'container_padding_px' => 48,
|
||||
'background' => '#F9FAFB',
|
||||
'text' => '#0F172A',
|
||||
'accent' => '#6366F1',
|
||||
@@ -327,6 +362,9 @@ class JoinTokenLayoutRegistry
|
||||
'name' => $layout['name'],
|
||||
'description' => $layout['description'],
|
||||
'subtitle' => $layout['subtitle'],
|
||||
'paper' => $layout['paper'] ?? 'a4',
|
||||
'orientation' => $layout['orientation'] ?? 'portrait',
|
||||
'panel_mode' => $layout['panel_mode'] ?? null,
|
||||
'badge_label' => $layout['badge_label'] ?? null,
|
||||
'instructions_heading' => $layout['instructions_heading'] ?? null,
|
||||
'link_heading' => $layout['link_heading'] ?? null,
|
||||
|
||||
Reference in New Issue
Block a user