Update guest v2 branding and theming
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-02-03 15:18:44 +01:00
parent 10c99de1e2
commit 298a8375b6
57 changed files with 1416 additions and 277 deletions

View File

@@ -1,6 +1,7 @@
import React from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Theme } from '@tamagui/core';
import { Image as ImageIcon, RefreshCcw, UploadCloud, Trash2, ChevronDown, Save, Droplets, Lock } from 'lucide-react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
@@ -240,6 +241,27 @@ export default function MobileBrandingPage() {
const previewLogoUrl = previewForm.logoMode === 'upload' ? previewForm.logoDataUrl : '';
const previewLogoValue = previewForm.logoMode === 'emoticon' ? previewForm.logoValue : '';
const previewInitials = getInitials(previewTitle);
const previewThemeName = previewForm.mode === 'dark' ? 'guestNight' : 'guestLight';
const previewIsDark = previewThemeName === 'guestNight';
const previewVariables = {
'--guest-primary': previewForm.primary,
'--guest-secondary': previewForm.accent,
'--guest-background': previewBackground,
'--guest-surface': previewSurface,
'--guest-font-scale': String(previewScale),
'--guest-body-font': previewBodyFont,
'--guest-heading-font': previewHeadingFont,
'--guest-button-radius': `${previewForm.buttonRadius}px`,
'--guest-radius': `${previewForm.buttonRadius}px`,
'--guest-link': previewForm.linkColor,
} as React.CSSProperties;
const previewAmbient = previewIsDark
? 'radial-gradient(circle at 18% 12%, rgba(255, 110, 110, 0.22), transparent 46%), radial-gradient(circle at 82% 18%, rgba(78, 205, 196, 0.18), transparent 44%), linear-gradient(180deg, rgba(6, 9, 20, 0.96), rgba(10, 14, 28, 1))'
: 'radial-gradient(circle at 18% 12%, color-mix(in oklab, var(--guest-primary, #FF6B6B) 24%, white), transparent 50%), radial-gradient(circle at 82% 18%, color-mix(in oklab, var(--guest-secondary, #4ECDC4) 20%, white), transparent 48%), linear-gradient(180deg, color-mix(in oklab, var(--guest-background, #FFF5F5) 96%, white), color-mix(in oklab, var(--guest-background, #FFF5F5) 72%, white))';
const previewSurfaceShadow = previewIsDark
? '0 18px 32px rgba(2, 6, 23, 0.55)'
: '0 16px 28px rgba(15, 23, 42, 0.15)';
const previewIconSurface = previewIsDark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(15, 23, 42, 0.08)';
const watermarkAllowed = isWatermarkAllowed(event ?? null);
const watermarkRemovalAllowed = isWatermarkRemovalAllowed(event ?? null);
const brandingAllowed = isBrandingAllowed(event ?? null);
@@ -616,79 +638,158 @@ export default function MobileBrandingPage() {
<Text fontSize="$sm" fontWeight="700" color={textStrong}>
{t('events.branding.previewTitle', 'Guest App Preview')}
</Text>
<YStack borderRadius={16} borderWidth={1} borderColor={previewBorder} backgroundColor={previewBackground} padding="$3" gap="$2" alignItems="center">
<YStack width="100%" borderRadius={12} backgroundColor={previewSurface} borderWidth={1} borderColor={previewBorder} overflow="hidden">
<Theme name={previewThemeName}>
<YStack
borderRadius={18}
borderWidth={1}
borderColor={previewBorder}
overflow="hidden"
style={previewVariables}
>
<YStack
height={64}
style={{ background: `linear-gradient(135deg, ${previewForm.primary}, ${previewForm.accent})` }}
/>
<YStack padding="$3" gap="$2">
<XStack
alignItems="center"
gap="$2"
flexDirection={previewForm.logoPosition === 'center' ? 'column' : previewForm.logoPosition === 'right' ? 'row-reverse' : 'row'}
justifyContent={previewForm.logoPosition === 'center' ? 'center' : 'flex-start'}
>
<YStack
width={previewLogoSize}
height={previewLogoSize}
borderRadius={previewLogoSize}
padding="$3"
gap="$3"
style={{
backgroundImage: previewAmbient,
backgroundSize: '140% 140%, 140% 140%, 100% 100%',
}}
>
<XStack alignItems="center" justifyContent="space-between" gap="$3">
<XStack
alignItems="center"
justifyContent="center"
backgroundColor={previewForm.accent}
gap="$2"
flexDirection={previewForm.logoPosition === 'center' ? 'column' : previewForm.logoPosition === 'right' ? 'row-reverse' : 'row'}
justifyContent={previewForm.logoPosition === 'center' ? 'center' : 'flex-start'}
flex={1}
>
{previewLogoUrl ? (
<img
src={previewLogoUrl}
alt={t('events.branding.logoAlt', 'Logo')}
style={{ width: previewLogoSize - 6, height: previewLogoSize - 6, borderRadius: previewLogoSize, objectFit: 'cover' }}
/>
) : (
<Text fontSize="$sm" color={previewSurfaceText} fontWeight="700">
{previewLogoValue || previewInitials}
</Text>
)}
</YStack>
<YStack>
<Text
fontWeight="800"
color={previewSurfaceText}
style={{ fontFamily: previewHeadingFont, fontSize: 18 * previewScale }}
<YStack
width={previewLogoSize}
height={previewLogoSize}
borderRadius={previewLogoSize}
alignItems="center"
justifyContent="center"
backgroundColor={previewForm.accent}
>
{previewTitle}
{previewLogoUrl ? (
<img
src={previewLogoUrl}
alt={t('events.branding.logoAlt', 'Logo')}
style={{ width: previewLogoSize - 6, height: previewLogoSize - 6, borderRadius: previewLogoSize, objectFit: 'cover' }}
/>
) : (
<Text fontSize="$sm" color={previewSurfaceText} fontWeight="700">
{previewLogoValue || previewInitials}
</Text>
)}
</YStack>
<YStack gap="$1">
<Text
fontWeight="800"
color={previewSurfaceText}
style={{ fontFamily: previewHeadingFont, fontSize: 18 * previewScale }}
>
{previewTitle}
</Text>
<Text
color={previewMutedForeground}
style={{ fontFamily: previewBodyFont, fontSize: 13 * previewScale }}
>
{t('events.branding.previewSubtitle', 'Aktuelle Farben & Schriften')}
</Text>
</YStack>
</XStack>
<XStack gap="$2">
<YStack width={28} height={28} borderRadius={999} backgroundColor={previewIconSurface} />
<YStack width={28} height={28} borderRadius={999} backgroundColor={previewIconSurface} />
</XStack>
</XStack>
<YStack gap="$2">
<YStack
padding="$3"
borderRadius={14}
backgroundColor={previewSurface}
borderWidth={1}
borderColor={previewBorder}
style={{ boxShadow: previewSurfaceShadow }}
>
<Text
fontWeight="700"
color={previewSurfaceText}
style={{ fontFamily: previewHeadingFont, fontSize: 14 * previewScale }}
>
{t('events.branding.previewTitleShort', 'Dein Event-Hub')}
</Text>
<Text
color={previewMutedForeground}
style={{ fontFamily: previewBodyFont, fontSize: 13 * previewScale }}
style={{ fontFamily: previewBodyFont, fontSize: 12 * previewScale }}
>
{t('events.branding.previewSubtitle', 'Aktuelle Farben & Schriften')}
{t('events.branding.previewSubtitleShort', 'Gäste, Fotos, Highlights')}
</Text>
<XStack gap="$2" marginTop="$2">
<ColorSwatch color={previewForm.primary} label={t('events.branding.primary', 'Primary')} borderColor={previewBorder} />
<ColorSwatch color={previewForm.accent} label={t('events.branding.accent', 'Accent')} borderColor={previewBorder} />
<ColorSwatch color={previewBackground} label={t('events.branding.background', 'Background')} borderColor={previewBorder} />
<ColorSwatch color={previewSurface} label={t('events.branding.surface', 'Surface')} borderColor={previewBorder} />
</XStack>
</YStack>
</XStack>
<XStack gap="$2" marginTop="$1">
<ColorSwatch color={previewForm.primary} label={t('events.branding.primary', 'Primary')} borderColor={previewBorder} />
<ColorSwatch color={previewForm.accent} label={t('events.branding.accent', 'Accent')} borderColor={previewBorder} />
<ColorSwatch color={previewBackground} label={t('events.branding.background', 'Background')} borderColor={previewBorder} />
<ColorSwatch color={previewSurface} label={t('events.branding.surface', 'Surface')} borderColor={previewBorder} />
</XStack>
<XStack marginTop="$2">
<div
style={{
padding: '8px 14px',
borderRadius: previewForm.buttonRadius,
background: previewForm.buttonStyle === 'outline' ? 'transparent' : previewButtonColor,
color: previewForm.buttonStyle === 'outline' ? previewForm.linkColor : previewButtonText,
border: previewForm.buttonStyle === 'outline' ? `1px solid ${previewForm.linkColor}` : 'none',
fontWeight: 700,
fontSize: 13 * previewScale,
}}
>
{t('events.branding.previewCta', 'Fotos hochladen')}
</div>
<XStack alignItems="center" justifyContent="space-between" gap="$2">
<YStack
padding="$2"
borderRadius="$pill"
backgroundColor={previewSurface}
borderWidth={1}
borderColor={previewBorder}
flex={1}
>
<Text fontSize="$2" color={previewMutedForeground}>
{t('events.branding.previewStat', 'Online Guests')}
</Text>
<Text fontWeight="800" color={previewSurfaceText} style={{ fontSize: 14 * previewScale }}>
148
</Text>
</YStack>
<div
style={{
padding: '8px 14px',
borderRadius: previewForm.buttonRadius,
background: previewForm.buttonStyle === 'outline' ? 'transparent' : previewButtonColor,
color: previewForm.buttonStyle === 'outline' ? previewForm.linkColor : previewButtonText,
border: previewForm.buttonStyle === 'outline' ? `1px solid ${previewForm.linkColor}` : 'none',
fontWeight: 700,
fontSize: 13 * previewScale,
}}
>
{t('events.branding.previewCta', 'Fotos hochladen')}
</div>
</XStack>
</YStack>
<XStack
alignItems="center"
justifyContent="space-between"
gap="$2"
padding="$2"
borderRadius="$pill"
backgroundColor={previewSurface}
borderWidth={1}
borderColor={previewBorder}
>
<XStack gap="$2">
<YStack width={28} height={28} borderRadius={999} backgroundColor={previewIconSurface} />
<YStack width={28} height={28} borderRadius={999} backgroundColor={previewIconSurface} />
<YStack width={28} height={28} borderRadius={999} backgroundColor={previewIconSurface} />
</XStack>
<YStack width={36} height={36} borderRadius={999} backgroundColor={previewButtonColor} />
<XStack gap="$2">
<YStack width={28} height={28} borderRadius={999} backgroundColor={previewIconSurface} />
<YStack width={28} height={28} borderRadius={999} backgroundColor={previewIconSurface} />
</XStack>
</XStack>
</YStack>
</YStack>
</YStack>
</Theme>
</MobileCard>
{!brandingAllowed ? (

View File

@@ -99,6 +99,10 @@ vi.mock('@tamagui/text', () => ({
SizableText: ({ children }: { children: React.ReactNode }) => <span>{children}</span>,
}));
vi.mock('@tamagui/core', () => ({
Theme: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));
vi.mock('@tamagui/react-native-web-lite', () => ({
Pressable: ({ children, onPress }: { children: React.ReactNode; onPress?: () => void }) => (
<button type="button" onClick={onPress}>