Adjust branding defaults and tenant presets
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-01-30 18:15:52 +01:00
parent b1f9f7cee0
commit e39ddd2143
9 changed files with 592 additions and 220 deletions

View File

@@ -28,6 +28,8 @@ const BRANDING_FORM_DEFAULTS = {
accent: DEFAULT_EVENT_BRANDING.secondaryColor,
background: DEFAULT_EVENT_BRANDING.backgroundColor,
surface: DEFAULT_EVENT_BRANDING.palette?.surface ?? DEFAULT_EVENT_BRANDING.backgroundColor,
headingFont: DEFAULT_EVENT_BRANDING.typography?.heading ?? DEFAULT_EVENT_BRANDING.fontFamily ?? '',
bodyFont: DEFAULT_EVENT_BRANDING.typography?.body ?? DEFAULT_EVENT_BRANDING.fontFamily ?? '',
mode: DEFAULT_EVENT_BRANDING.mode ?? 'auto',
buttonStyle: DEFAULT_EVENT_BRANDING.buttons?.style ?? 'filled',
buttonRadius: DEFAULT_EVENT_BRANDING.buttons?.radius ?? 12,
@@ -40,14 +42,11 @@ const BRANDING_FORM_DEFAULTS = {
logoSize: DEFAULT_EVENT_BRANDING.logo?.size ?? 'm',
};
const BRANDING_FORM_BASE: BrandingFormValues = {
...BRANDING_FORM_DEFAULTS,
headingFont: '',
bodyFont: '',
const buildBrandingFormBase = (defaults: typeof BRANDING_FORM_DEFAULTS): BrandingFormValues => ({
...defaults,
logoDataUrl: '',
logoValue: '',
useDefaultBranding: false,
};
});
const FONT_SIZE_SCALE: Record<BrandingFormValues['fontSize'], number> = {
s: 0.94,
@@ -61,6 +60,38 @@ const LOGO_SIZE_PREVIEW: Record<BrandingFormValues['logoSize'], number> = {
l: 44,
};
const resolveBrandingDefaults = (tenantBranding: BrandingFormValues | null) => {
if (!tenantBranding) {
return BRANDING_FORM_DEFAULTS;
}
const primary = tenantBranding.primary.trim() ? tenantBranding.primary : BRANDING_FORM_DEFAULTS.primary;
const accent = tenantBranding.accent.trim() ? tenantBranding.accent : BRANDING_FORM_DEFAULTS.accent;
const background = tenantBranding.background.trim() ? tenantBranding.background : BRANDING_FORM_DEFAULTS.background;
const surface = tenantBranding.surface.trim() ? tenantBranding.surface : BRANDING_FORM_DEFAULTS.surface;
const headingFont = tenantBranding.headingFont.trim()
? tenantBranding.headingFont
: BRANDING_FORM_DEFAULTS.headingFont;
const bodyFont = tenantBranding.bodyFont.trim()
? tenantBranding.bodyFont
: BRANDING_FORM_DEFAULTS.bodyFont;
return {
...BRANDING_FORM_DEFAULTS,
primary,
accent,
background,
surface,
headingFont,
bodyFont,
fontSize: tenantBranding.fontSize ?? BRANDING_FORM_DEFAULTS.fontSize,
mode: tenantBranding.mode ?? BRANDING_FORM_DEFAULTS.mode,
buttonPrimary: primary,
buttonSecondary: accent,
linkColor: accent,
};
};
type WatermarkPosition =
| 'top-left'
| 'top-center'
@@ -95,7 +126,7 @@ export default function MobileBrandingPage() {
const { textStrong, muted, subtle, border, primary, accentSoft, danger, surfaceMuted, surface } = useAdminTheme();
const [event, setEvent] = React.useState<TenantEvent | null>(null);
const [form, setForm] = React.useState<BrandingFormValues>(BRANDING_FORM_BASE);
const [form, setForm] = React.useState<BrandingFormValues>(() => buildBrandingFormBase(BRANDING_FORM_DEFAULTS));
const [watermarkForm, setWatermarkForm] = React.useState<WatermarkForm>({
mode: 'base',
assetPath: '',
@@ -120,10 +151,13 @@ export default function MobileBrandingPage() {
const [fontsLoaded, setFontsLoaded] = React.useState(false);
const [tenantBranding, setTenantBranding] = React.useState<BrandingFormValues | null>(null);
const [tenantBrandingLoaded, setTenantBrandingLoaded] = React.useState(false);
const [formInitialized, setFormInitialized] = React.useState(false);
const resolvedDefaults = React.useMemo(() => resolveBrandingDefaults(tenantBranding), [tenantBranding]);
React.useEffect(() => {
if (!slug) return;
(async () => {
setFormInitialized(false);
setLoading(true);
try {
const data = await getEvent(slug);
@@ -132,7 +166,6 @@ export default function MobileBrandingPage() {
return;
}
setEvent(data);
setForm(extractBrandingForm(data.settings ?? {}, BRANDING_FORM_DEFAULTS));
setWatermarkForm(extractWatermark(data));
setError(null);
} catch (err) {
@@ -145,6 +178,12 @@ export default function MobileBrandingPage() {
})();
}, [slug, t]);
React.useEffect(() => {
if (!event || !tenantBrandingLoaded || formInitialized) return;
setForm(extractBrandingForm(event.settings ?? {}, resolvedDefaults));
setFormInitialized(true);
}, [event, tenantBrandingLoaded, formInitialized, resolvedDefaults]);
React.useEffect(() => {
if (!showFontsSheet || fontsLoaded) return;
setFontsLoading(true);
@@ -177,7 +216,7 @@ export default function MobileBrandingPage() {
};
}, [tenantBrandingLoaded]);
const previewForm = form.useDefaultBranding && tenantBranding ? tenantBranding : form;
const previewForm = form;
const previewBackground = previewForm.background;
const previewSurfaceCandidate = previewForm.surface || previewBackground;
const backgroundLuminance = relativeLuminance(previewBackground);
@@ -206,7 +245,7 @@ export default function MobileBrandingPage() {
const brandingAllowed = isBrandingAllowed(event ?? null);
const customWatermarkAllowed = watermarkAllowed && brandingAllowed;
const watermarkLocked = watermarkAllowed && !brandingAllowed;
const brandingDisabled = !brandingAllowed || form.useDefaultBranding;
const brandingDisabled = !brandingAllowed;
React.useEffect(() => {
setWatermarkForm((prev) => {
@@ -243,7 +282,6 @@ export default function MobileBrandingPage() {
settings.branding = {
...(typeof settings.branding === 'object' ? (settings.branding as Record<string, unknown>) : {}),
use_default_branding: form.useDefaultBranding,
primary_color: form.primary,
secondary_color: form.accent,
accent_color: form.accent,
@@ -334,7 +372,7 @@ export default function MobileBrandingPage() {
function handleReset() {
if (event) {
setForm(extractBrandingForm(event.settings ?? {}, BRANDING_FORM_DEFAULTS));
setForm(extractBrandingForm(event.settings ?? {}, resolvedDefaults));
setWatermarkForm(extractWatermark(event));
}
}
@@ -662,36 +700,7 @@ export default function MobileBrandingPage() {
/>
) : null}
<MobileCard space="$3">
<Text fontSize="$md" fontWeight="800" color={textStrong}>
{t('events.branding.source', 'Branding Source')}
</Text>
<Text fontSize="$sm" color={muted}>
{t('events.branding.sourceHint', 'Use the default branding or customize this event only.')}
</Text>
<XStack space="$2">
<ModeButton
label={t('events.branding.useDefault', 'Default')}
active={form.useDefaultBranding}
onPress={() => setForm((prev) => ({ ...prev, useDefaultBranding: true }))}
disabled={!brandingAllowed}
/>
<ModeButton
label={t('events.branding.useCustom', 'This event')}
active={!form.useDefaultBranding}
onPress={() => setForm((prev) => ({ ...prev, useDefaultBranding: false }))}
disabled={!brandingAllowed}
/>
</XStack>
<Text fontSize="$xs" color={muted}>
{form.useDefaultBranding
? t('events.branding.usingDefault', 'Account-Branding aktiv')
: t('events.branding.usingCustom', 'Event-Branding aktiv')}
</Text>
</MobileCard>
{form.useDefaultBranding ? null : (
<>
<>
<MobileCard space="$3">
<Text fontSize="$md" fontWeight="800" color={textStrong}>
{t('events.branding.mode', 'Theme')}
@@ -1024,8 +1033,7 @@ export default function MobileBrandingPage() {
disabled={brandingDisabled}
/>
</MobileCard>
</>
)}
</>
</>
) : (
renderWatermarkTab()