Update guest v2 home and tasks experience
This commit is contained in:
@@ -2,14 +2,14 @@ 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 { Image as ImageIcon, RefreshCcw, UploadCloud, Trash2, ChevronDown, Save, Droplets, Lock, X } from 'lucide-react';
|
||||
import { YStack, XStack } from '@tamagui/stacks';
|
||||
import { SizableText as Text } from '@tamagui/text';
|
||||
import { Pressable } from '@tamagui/react-native-web-lite';
|
||||
import { Slider } from 'tamagui';
|
||||
import { MobileShell, HeaderActionButton } from './components/MobileShell';
|
||||
import { MobileCard, CTAButton, SkeletonCard } from './components/Primitives';
|
||||
import { MobileColorInput, MobileField, MobileFileInput, MobileInput, MobileSelect } from './components/FormControls';
|
||||
import { MobileColorInput, MobileField, MobileFileInput, MobileInput, MobileSelect, MobileTextArea } from './components/FormControls';
|
||||
import { TenantEvent, getEvent, updateEvent, getTenantFonts, getTenantSettings, TenantFont, WatermarkSettings, trackOnboarding } from '../api';
|
||||
import { isAuthError } from '../auth/tokens';
|
||||
import { ApiError, getApiErrorMessage } from '../lib/apiError';
|
||||
@@ -37,6 +37,7 @@ const BRANDING_FORM_DEFAULTS = {
|
||||
buttonPrimary: DEFAULT_EVENT_BRANDING.buttons?.primary ?? DEFAULT_EVENT_BRANDING.primaryColor,
|
||||
buttonSecondary: DEFAULT_EVENT_BRANDING.buttons?.secondary ?? DEFAULT_EVENT_BRANDING.secondaryColor,
|
||||
linkColor: DEFAULT_EVENT_BRANDING.buttons?.linkColor ?? DEFAULT_EVENT_BRANDING.secondaryColor,
|
||||
welcomeMessage: '',
|
||||
fontSize: DEFAULT_EVENT_BRANDING.typography?.sizePreset ?? 'm',
|
||||
logoMode: DEFAULT_EVENT_BRANDING.logo?.mode ?? 'emoticon',
|
||||
logoPosition: DEFAULT_EVENT_BRANDING.logo?.position ?? 'left',
|
||||
@@ -76,6 +77,9 @@ const resolveBrandingDefaults = (tenantBranding: BrandingFormValues | null) => {
|
||||
const bodyFont = tenantBranding.bodyFont.trim()
|
||||
? tenantBranding.bodyFont
|
||||
: BRANDING_FORM_DEFAULTS.bodyFont;
|
||||
const welcomeMessage = tenantBranding.welcomeMessage?.trim()
|
||||
? tenantBranding.welcomeMessage
|
||||
: BRANDING_FORM_DEFAULTS.welcomeMessage;
|
||||
|
||||
return {
|
||||
...BRANDING_FORM_DEFAULTS,
|
||||
@@ -90,6 +94,7 @@ const resolveBrandingDefaults = (tenantBranding: BrandingFormValues | null) => {
|
||||
buttonPrimary: primary,
|
||||
buttonSecondary: accent,
|
||||
linkColor: accent,
|
||||
welcomeMessage,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -241,6 +246,12 @@ export default function MobileBrandingPage() {
|
||||
const previewLogoUrl = previewForm.logoMode === 'upload' ? previewForm.logoDataUrl : '';
|
||||
const previewLogoValue = previewForm.logoMode === 'emoticon' ? previewForm.logoValue : '';
|
||||
const previewInitials = getInitials(previewTitle);
|
||||
const previewWelcomeTemplate = previewForm.welcomeMessage.trim()
|
||||
? previewForm.welcomeMessage.trim()
|
||||
: t('events.branding.welcomeMessageFallback', 'Willkommen bei {eventName}.');
|
||||
const previewWelcomeMessage = previewWelcomeTemplate
|
||||
.replace('{eventName}', previewTitle)
|
||||
.replace('{name}', t('events.branding.welcomeGuestFallback', 'Gast'));
|
||||
const previewThemeName = previewForm.mode === 'dark' ? 'guestNight' : 'guestLight';
|
||||
const previewIsDark = previewThemeName === 'guestNight';
|
||||
const previewVariables = {
|
||||
@@ -346,6 +357,7 @@ export default function MobileBrandingPage() {
|
||||
secondary: form.buttonSecondary,
|
||||
link_color: form.linkColor,
|
||||
},
|
||||
welcome_message: form.welcomeMessage.trim() ? form.welcomeMessage.trim() : null,
|
||||
logo_data_url: form.logoMode === 'upload' && logoIsDataUrl ? logoUploadValue : null,
|
||||
logo_url: form.logoMode === 'upload' ? (normalizedLogoPath || null) : null,
|
||||
logo_mode: form.logoMode,
|
||||
@@ -705,6 +717,48 @@ export default function MobileBrandingPage() {
|
||||
</XStack>
|
||||
|
||||
<YStack gap="$2">
|
||||
<YStack
|
||||
position="relative"
|
||||
padding="$3"
|
||||
borderRadius={14}
|
||||
backgroundColor={previewSurface}
|
||||
borderWidth={1}
|
||||
borderColor={previewBorder}
|
||||
style={{ boxShadow: previewSurfaceShadow }}
|
||||
>
|
||||
<YStack position="absolute" top={-10} right={-10}>
|
||||
<YStack
|
||||
width={28}
|
||||
height={28}
|
||||
borderRadius={999}
|
||||
backgroundColor={previewSurface}
|
||||
borderWidth={1}
|
||||
borderColor={previewBorder}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
style={{
|
||||
boxShadow: previewIsDark
|
||||
? '0 6px 0 rgba(0, 0, 0, 0.45)'
|
||||
: '0 6px 0 rgba(15, 23, 42, 0.2)',
|
||||
}}
|
||||
>
|
||||
<X size={12} color={previewSurfaceText} />
|
||||
</YStack>
|
||||
</YStack>
|
||||
<Text
|
||||
fontWeight="700"
|
||||
color={previewSurfaceText}
|
||||
style={{ fontFamily: previewHeadingFont, fontSize: 14 * previewScale }}
|
||||
>
|
||||
{t('events.branding.previewWelcome', 'Willkommen!')}
|
||||
</Text>
|
||||
<Text
|
||||
color={previewMutedForeground}
|
||||
style={{ fontFamily: previewBodyFont, fontSize: 12 * previewScale }}
|
||||
>
|
||||
{previewWelcomeMessage}
|
||||
</Text>
|
||||
</YStack>
|
||||
<YStack
|
||||
padding="$3"
|
||||
borderRadius={14}
|
||||
@@ -744,25 +798,37 @@ export default function MobileBrandingPage() {
|
||||
flex={1}
|
||||
>
|
||||
<Text fontSize="$2" color={previewMutedForeground}>
|
||||
{t('events.branding.previewStat', 'Online Guests')}
|
||||
{t('events.branding.previewGuests', '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,
|
||||
}}
|
||||
<YStack
|
||||
padding="$2"
|
||||
borderRadius="$pill"
|
||||
backgroundColor={previewSurface}
|
||||
borderWidth={1}
|
||||
borderColor={previewBorder}
|
||||
flex={1}
|
||||
>
|
||||
{t('events.branding.previewCta', 'Fotos hochladen')}
|
||||
</div>
|
||||
<Text fontSize="$2" color={previewMutedForeground}>
|
||||
{t('events.branding.previewLikes', 'Likes')}
|
||||
</Text>
|
||||
<Text fontWeight="800" color={previewSurfaceText} style={{ fontSize: 14 * previewScale }}>
|
||||
392
|
||||
</Text>
|
||||
</YStack>
|
||||
</XStack>
|
||||
<XStack gap="$2" alignItems="center">
|
||||
<YStack width={64} height={48} borderRadius={12} backgroundColor={previewIconSurface} />
|
||||
<YStack width={64} height={48} borderRadius={12} backgroundColor={previewIconSurface} />
|
||||
<YStack width={64} height={48} borderRadius={12} backgroundColor={previewIconSurface} />
|
||||
</XStack>
|
||||
<XStack gap="$2">
|
||||
<YStack flex={1} height={44} borderRadius={12} backgroundColor={previewIconSurface} />
|
||||
<YStack flex={1} height={44} borderRadius={12} backgroundColor={previewIconSurface} />
|
||||
<YStack flex={1} height={44} borderRadius={12} backgroundColor={previewIconSurface} />
|
||||
</XStack>
|
||||
</YStack>
|
||||
|
||||
@@ -909,6 +975,23 @@ export default function MobileBrandingPage() {
|
||||
</XStack>
|
||||
</MobileCard>
|
||||
|
||||
<MobileCard gap="$3">
|
||||
<Text fontSize="$md" fontWeight="800" color={textStrong}>
|
||||
{t('events.branding.welcomeMessage.title', 'Welcome message')}
|
||||
</Text>
|
||||
<MobileField label={t('events.branding.welcomeMessage.label', 'Message')}>
|
||||
<MobileTextArea
|
||||
value={form.welcomeMessage}
|
||||
placeholder={t('events.branding.welcomeMessage.placeholder', 'Welcome to {eventName}!')}
|
||||
onChange={(event) => setForm((prev) => ({ ...prev, welcomeMessage: event.target.value }))}
|
||||
disabled={brandingDisabled}
|
||||
/>
|
||||
</MobileField>
|
||||
<Text fontSize="$xs" color={muted}>
|
||||
{t('events.branding.welcomeMessage.hint', 'Use {eventName} or {name} as placeholders.')}
|
||||
</Text>
|
||||
</MobileCard>
|
||||
|
||||
<MobileCard gap="$3">
|
||||
<Text fontSize="$md" fontWeight="800" color={textStrong}>
|
||||
{t('events.branding.logo', 'Logo')}
|
||||
|
||||
@@ -54,6 +54,7 @@ const TENANT_BRANDING_DEFAULTS = {
|
||||
buttonPrimary: DEFAULT_EVENT_BRANDING.buttons?.primary ?? DEFAULT_EVENT_BRANDING.primaryColor,
|
||||
buttonSecondary: DEFAULT_EVENT_BRANDING.buttons?.secondary ?? DEFAULT_EVENT_BRANDING.secondaryColor,
|
||||
linkColor: DEFAULT_EVENT_BRANDING.buttons?.linkColor ?? DEFAULT_EVENT_BRANDING.secondaryColor,
|
||||
welcomeMessage: '',
|
||||
fontSize: DEFAULT_EVENT_BRANDING.typography?.sizePreset ?? 'm',
|
||||
logoMode: DEFAULT_EVENT_BRANDING.logo?.mode ?? 'emoticon',
|
||||
logoPosition: DEFAULT_EVENT_BRANDING.logo?.position ?? 'left',
|
||||
@@ -263,6 +264,7 @@ export default function MobileProfileAccountPage() {
|
||||
body_font: brandingForm.bodyFont,
|
||||
font_size: brandingForm.fontSize,
|
||||
mode: brandingForm.mode,
|
||||
welcome_message: brandingForm.welcomeMessage.trim() ? brandingForm.welcomeMessage.trim() : null,
|
||||
typography: {
|
||||
...(typeof existingBranding.typography === 'object' ? (existingBranding.typography as Record<string, unknown>) : {}),
|
||||
heading: brandingForm.headingFont,
|
||||
|
||||
Reference in New Issue
Block a user