Expand branding controls and logo upload

This commit is contained in:
Codex Agent
2026-01-15 08:42:20 +01:00
parent a1f37bb491
commit b0ba29fcb6
11 changed files with 906 additions and 139 deletions

View File

@@ -38,6 +38,18 @@ const EVENT_ICON_COMPONENTS: Record<string, React.ComponentType<{ className?: st
camera: Camera,
};
type LogoSize = 's' | 'm' | 'l';
const LOGO_SIZE_CLASSES: Record<LogoSize, { container: string; image: string; emoji: string; icon: string }> = {
s: { container: 'h-8 w-8', image: 'h-7 w-7', emoji: 'text-lg', icon: 'h-4 w-4' },
m: { container: 'h-10 w-10', image: 'h-9 w-9', emoji: 'text-xl', icon: 'h-5 w-5' },
l: { container: 'h-12 w-12', image: 'h-11 w-11', emoji: 'text-2xl', icon: 'h-6 w-6' },
};
function getLogoClasses(size?: LogoSize) {
return LOGO_SIZE_CLASSES[size ?? 'm'];
}
const NOTIFICATION_ICON_MAP: Record<string, React.ComponentType<{ className?: string }>> = {
broadcast: MessageSquare,
feedback_request: MessageSquare,
@@ -69,18 +81,25 @@ function getInitials(name: string): string {
return name.substring(0, 2).toUpperCase();
}
function renderEventAvatar(name: string, icon: unknown, accentColor: string, textColor: string, logo?: { mode: 'emoticon' | 'upload'; value: string | null }) {
function renderEventAvatar(
name: string,
icon: unknown,
accentColor: string,
textColor: string,
logo?: { mode: 'emoticon' | 'upload'; value: string | null; size?: LogoSize }
) {
const sizes = getLogoClasses(logo?.size);
if (logo?.mode === 'upload' && logo.value) {
return (
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-white shadow-sm">
<img src={logo.value} alt={name} className="h-9 w-9 rounded-full object-contain" />
<div className={`flex items-center justify-center rounded-full bg-white shadow-sm ${sizes.container}`}>
<img src={logo.value} alt={name} className={`rounded-full object-contain ${sizes.image}`} />
</div>
);
}
if (logo?.mode === 'emoticon' && logo.value && isLikelyEmoji(logo.value)) {
return (
<div
className="flex h-10 w-10 items-center justify-center rounded-full text-xl shadow-sm"
className={`flex items-center justify-center rounded-full shadow-sm ${sizes.container} ${sizes.emoji}`}
style={{ backgroundColor: accentColor, color: textColor }}
>
<span aria-hidden>{logo.value}</span>
@@ -94,21 +113,21 @@ function renderEventAvatar(name: string, icon: unknown, accentColor: string, tex
if (trimmed) {
const normalized = trimmed.toLowerCase();
const IconComponent = EVENT_ICON_COMPONENTS[normalized];
if (IconComponent) {
return (
<div
className="flex h-10 w-10 items-center justify-center rounded-full shadow-sm"
style={{ backgroundColor: accentColor, color: textColor }}
>
<IconComponent className="h-5 w-5" aria-hidden />
</div>
);
}
if (IconComponent) {
return (
<div
className={`flex items-center justify-center rounded-full shadow-sm ${sizes.container}`}
style={{ backgroundColor: accentColor, color: textColor }}
>
<IconComponent className={sizes.icon} aria-hidden />
</div>
);
}
if (isLikelyEmoji(trimmed)) {
return (
<div
className="flex h-10 w-10 items-center justify-center rounded-full text-xl shadow-sm"
className={`flex items-center justify-center rounded-full shadow-sm ${sizes.container} ${sizes.emoji}`}
style={{ backgroundColor: accentColor, color: textColor }}
>
<span aria-hidden>{trimmed}</span>
@@ -188,6 +207,7 @@ export default function Header({ eventToken, title = '' }: { eventToken?: string
const headerFont = branding.typography?.heading ?? branding.fontFamily ?? undefined;
const bodyFont = branding.typography?.body ?? branding.fontFamily ?? undefined;
const logoPosition = branding.logo?.position ?? 'left';
const headerStyle: React.CSSProperties = {
background: `linear-gradient(135deg, ${branding.primaryColor}, ${branding.secondaryColor})`,
color: headerTextColor,
@@ -219,9 +239,20 @@ export default function Header({ eventToken, title = '' }: { eventToken?: string
className="guest-header z-20 flex items-center justify-between border-b border-white/10 px-4 py-3 text-white shadow-sm backdrop-blur"
style={headerStyle}
>
<div className="flex items-center gap-3">
<div
className={
logoPosition === 'center'
? 'flex flex-col items-center gap-2 text-center'
: logoPosition === 'right'
? 'flex flex-row-reverse items-center gap-3'
: 'flex items-center gap-3'
}
>
{renderEventAvatar(event.name, event.type?.icon, accentColor, headerTextColor, branding.logo)}
<div className="flex flex-col" style={headerFont ? { fontFamily: headerFont } : undefined}>
<div
className={`flex flex-col${logoPosition === 'center' ? ' items-center text-center' : ''}`}
style={headerFont ? { fontFamily: headerFont } : undefined}
>
<div className="font-semibold text-lg">{event.name}</div>
<div className="flex items-center gap-2 text-xs text-white/70" style={bodyFont ? { fontFamily: bodyFont } : undefined}>
{stats && tasksEnabled && (