Merge pull request #757 from PicPeak/fix/hero-logo-global-inherit-756
fix(branding): make 'Show logo in hero' a true global toggle with per-event override (#756)
This commit is contained in:
@@ -25,6 +25,15 @@ interface GalleryLayoutProps {
|
||||
promo_mode?: 'inherit' | 'custom' | 'off';
|
||||
promo_markdown?: string | null;
|
||||
};
|
||||
// Effective hero-logo visibility for THIS gallery, already resolved by the
|
||||
// backend (per-event override, else the global branding toggle) (#756).
|
||||
// When provided it wins over brandingSettings.logo_display_hero.
|
||||
heroLogoVisible?: boolean;
|
||||
// Effective hero-logo SIZE, resolved the same way (per-event override, else
|
||||
// the global branding_logo_size) (#756). When provided it wins over
|
||||
// brandingSettings.logo_size for the hero logo — so both render paths
|
||||
// (this layout and the hero-header) size the logo identically.
|
||||
heroLogoSize?: 'small' | 'medium' | 'large' | 'xlarge' | 'custom';
|
||||
brandingSettings?: {
|
||||
company_name?: string;
|
||||
company_tagline?: string;
|
||||
@@ -108,6 +117,8 @@ const HeaderDownloadButton: React.FC<{
|
||||
export const GalleryLayout: React.FC<GalleryLayoutProps> = ({
|
||||
event,
|
||||
brandingSettings,
|
||||
heroLogoVisible,
|
||||
heroLogoSize,
|
||||
showLogout = false,
|
||||
onLogout,
|
||||
showDownloadAll = false,
|
||||
@@ -157,7 +168,10 @@ export const GalleryLayout: React.FC<GalleryLayoutProps> = ({
|
||||
|
||||
// Calculate logo size classes based on settings
|
||||
const getLogoDimensions = (context: 'header' | 'hero'): { className: string; style?: React.CSSProperties } => {
|
||||
const size = brandingSettings?.logo_size || 'medium';
|
||||
// #756: the hero logo uses the backend-resolved per-event size (override,
|
||||
// else global) so it matches the hero-header layout; the header logo keeps
|
||||
// the global size.
|
||||
const size = (context === 'hero' && heroLogoSize) ? heroLogoSize : (brandingSettings?.logo_size || 'medium');
|
||||
const maxHeight = brandingSettings?.logo_max_height || 48;
|
||||
|
||||
if (size === 'custom') {
|
||||
@@ -202,6 +216,9 @@ export const GalleryLayout: React.FC<GalleryLayoutProps> = ({
|
||||
if (context === 'header') {
|
||||
return brandingSettings?.logo_display_header !== false;
|
||||
} else {
|
||||
// #756: prefer the backend-resolved per-event value (override, else
|
||||
// global); fall back to the global branding toggle if not provided.
|
||||
if (heroLogoVisible !== undefined) return heroLogoVisible;
|
||||
return brandingSettings?.logo_display_hero !== false;
|
||||
}
|
||||
};
|
||||
@@ -213,7 +230,7 @@ export const GalleryLayout: React.FC<GalleryLayoutProps> = ({
|
||||
};
|
||||
|
||||
const headerLogoSize = getLogoDimensions('header');
|
||||
const heroLogoSize = getLogoDimensions('hero');
|
||||
const heroLogoDimensions = getLogoDimensions('hero');
|
||||
|
||||
// Footer overhaul (#441 + #440). All five socials are independent;
|
||||
// empty string = hide just that icon. Per-event promo override:
|
||||
@@ -599,9 +616,9 @@ export const GalleryLayout: React.FC<GalleryLayoutProps> = ({
|
||||
'/picpeak-logo-transparent.png'
|
||||
}
|
||||
alt={brandingSettings?.company_name || 'PicPeak'}
|
||||
className={`${heroLogoSize.className} w-auto object-contain mx-auto`}
|
||||
className={`${heroLogoDimensions.className} w-auto object-contain mx-auto`}
|
||||
style={{
|
||||
...(heroLogoSize.style || {}),
|
||||
...(heroLogoDimensions.style || {}),
|
||||
// Only apply brightness/invert filter to default logo; custom logos display as-is
|
||||
filter: brandLogoUrl
|
||||
? 'drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3))'
|
||||
|
||||
@@ -820,6 +820,8 @@ export const GalleryView: React.FC<GalleryViewProps> = ({ slug, event }) => {
|
||||
promo_markdown: (data?.event as { promo_markdown?: string | null })?.promo_markdown,
|
||||
}}
|
||||
brandingSettings={brandingSettings}
|
||||
heroLogoVisible={data?.event?.hero_logo_visible !== false}
|
||||
heroLogoSize={data?.event?.hero_logo_size || undefined}
|
||||
headerStyle={data?.event?.header_style || theme.headerStyle}
|
||||
showLogout={true}
|
||||
onLogout={logout}
|
||||
|
||||
@@ -1110,6 +1110,9 @@
|
||||
"protectionLevelMaximum": "Maximum - DevTools-Erkennung & Canvas-Rendering",
|
||||
"heroLogoSettings": "Hero-Logo-Einstellungen",
|
||||
"heroLogoVisible": "Logo im Hero-Bereich anzeigen",
|
||||
"heroLogoInherit": "Branding-Standard verwenden",
|
||||
"heroLogoShow": "Immer anzeigen",
|
||||
"heroLogoHide": "Immer ausblenden",
|
||||
"heroLogoSize": "Logo-Größe",
|
||||
"heroLogoSizeSmall": "Klein",
|
||||
"heroLogoSizeMedium": "Mittel",
|
||||
|
||||
@@ -655,6 +655,9 @@
|
||||
"protectionLevelMaximum": "Maximum - DevTools detection & canvas rendering",
|
||||
"heroLogoSettings": "Hero Logo Settings",
|
||||
"heroLogoVisible": "Display logo in hero section",
|
||||
"heroLogoInherit": "Use branding default",
|
||||
"heroLogoShow": "Always show",
|
||||
"heroLogoHide": "Always hide",
|
||||
"heroLogoSize": "Logo Size",
|
||||
"heroLogoSizeSmall": "Small",
|
||||
"heroLogoSizeMedium": "Medium",
|
||||
|
||||
@@ -306,9 +306,11 @@ export const EventDetailsPage: React.FC = () => {
|
||||
allow_presigned_download: (event as { allow_presigned_download?: boolean }).allow_presigned_download ?? false,
|
||||
enable_devtools_protection: event.enable_devtools_protection ?? true,
|
||||
use_canvas_rendering: event.use_canvas_rendering ?? false,
|
||||
// Load hero logo settings from event
|
||||
hero_logo_visible: event.hero_logo_visible ?? true,
|
||||
hero_logo_size: event.hero_logo_size || 'medium',
|
||||
// Load hero logo settings from event. Preserve null = "inherit global"
|
||||
// (#756) — don't collapse it to true, or saving would snapshot an override.
|
||||
hero_logo_visible: event.hero_logo_visible ?? null,
|
||||
// Preserve null = "inherit global size" (#756) — don't collapse to medium.
|
||||
hero_logo_size: event.hero_logo_size ?? null,
|
||||
hero_logo_position: event.hero_logo_position || 'top',
|
||||
// Hero image anchor position (#162)
|
||||
hero_image_anchor: event.hero_image_anchor || 'center',
|
||||
|
||||
@@ -589,28 +589,43 @@ export const EventInformationCard: React.FC<EventInformationCardProps> = ({
|
||||
</h3>
|
||||
|
||||
<div className="space-y-3">
|
||||
<label className="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={editForm.hero_logo_visible}
|
||||
onChange={(e) => setEditForm(prev => ({ ...prev, hero_logo_visible: e.target.checked }))}
|
||||
className="w-4 h-4 text-accent border-neutral-300 dark:border-neutral-600 rounded focus:ring-primary-500"
|
||||
/>
|
||||
<Image className="w-4 h-4 ml-2 mr-1 text-neutral-500 dark:text-neutral-400" />
|
||||
<span className="text-sm text-neutral-700 dark:text-neutral-300">{t('events.heroLogoVisible', 'Display logo in hero section')}</span>
|
||||
</label>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-neutral-700 dark:text-neutral-300 mb-1 flex items-center gap-1">
|
||||
<Image className="w-4 h-4 text-neutral-500 dark:text-neutral-400" />
|
||||
{t('events.heroLogoVisible', 'Display logo in hero section')}
|
||||
</label>
|
||||
<select
|
||||
// Tri-state (#756): "inherit" = null = follow the global
|
||||
// Branding → "Show logo in hero" toggle; show/hide override it
|
||||
// for just this gallery.
|
||||
value={editForm.hero_logo_visible === null || editForm.hero_logo_visible === undefined
|
||||
? 'inherit'
|
||||
: editForm.hero_logo_visible ? 'show' : 'hide'}
|
||||
onChange={(e) => setEditForm(prev => ({
|
||||
...prev,
|
||||
hero_logo_visible: e.target.value === 'inherit' ? null : e.target.value === 'show'
|
||||
}))}
|
||||
className="w-full sm:w-64 px-3 py-2 border border-neutral-300 dark:border-neutral-600 bg-white dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100 rounded-md shadow-sm focus:ring-primary-500 focus:border-accent-dark text-sm"
|
||||
>
|
||||
<option value="inherit">{t('events.heroLogoInherit', 'Use branding default')}</option>
|
||||
<option value="show">{t('events.heroLogoShow', 'Always show')}</option>
|
||||
<option value="hide">{t('events.heroLogoHide', 'Always hide')}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{editForm.hero_logo_visible && (
|
||||
{editForm.hero_logo_visible !== false && (
|
||||
<>
|
||||
<div className="ml-6">
|
||||
<label className="block text-sm font-medium text-neutral-700 dark:text-neutral-300 mb-1">
|
||||
{t('events.heroLogoSize', 'Logo Size')}
|
||||
</label>
|
||||
<select
|
||||
value={editForm.hero_logo_size}
|
||||
onChange={(e) => setEditForm(prev => ({ ...prev, hero_logo_size: e.target.value as 'small' | 'medium' | 'large' | 'xlarge' }))}
|
||||
// '' = inherit the global branding logo size (#756).
|
||||
value={editForm.hero_logo_size ?? ''}
|
||||
onChange={(e) => setEditForm(prev => ({ ...prev, hero_logo_size: e.target.value === '' ? null : e.target.value as 'small' | 'medium' | 'large' | 'xlarge' }))}
|
||||
className="w-full sm:w-48 px-3 py-2 border border-neutral-300 dark:border-neutral-600 bg-white dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100 rounded-md shadow-sm focus:ring-primary-500 focus:border-accent-dark text-sm"
|
||||
>
|
||||
<option value="">{t('events.heroLogoInherit', 'Use branding default')}</option>
|
||||
<option value="small">{t('events.heroLogoSizeSmall', 'Small')}</option>
|
||||
<option value="medium">{t('events.heroLogoSizeMedium', 'Medium')}</option>
|
||||
<option value="large">{t('events.heroLogoSizeLarge', 'Large')}</option>
|
||||
|
||||
@@ -24,9 +24,9 @@ export type EditFormState = {
|
||||
allow_presigned_download: boolean;
|
||||
enable_devtools_protection: boolean;
|
||||
use_canvas_rendering: boolean;
|
||||
// Hero logo settings
|
||||
hero_logo_visible: boolean;
|
||||
hero_logo_size: 'small' | 'medium' | 'large' | 'xlarge';
|
||||
// Hero logo settings. null = inherit the global branding toggle (#756).
|
||||
hero_logo_visible: boolean | null;
|
||||
hero_logo_size: 'small' | 'medium' | 'large' | 'xlarge' | null;
|
||||
hero_logo_position: 'top' | 'center' | 'bottom';
|
||||
// Hero image anchor position (#162) – keyword or "X% Y%" focal point
|
||||
hero_image_anchor: string;
|
||||
@@ -72,9 +72,9 @@ export const INITIAL_EDIT_FORM: EditFormState = {
|
||||
allow_presigned_download: false,
|
||||
enable_devtools_protection: true,
|
||||
use_canvas_rendering: false,
|
||||
// Hero logo settings
|
||||
hero_logo_visible: true,
|
||||
hero_logo_size: 'medium',
|
||||
// Hero logo settings — null = inherit global branding toggle (#756)
|
||||
hero_logo_visible: null,
|
||||
hero_logo_size: null,
|
||||
hero_logo_position: 'top',
|
||||
// Hero image anchor position (#162)
|
||||
hero_image_anchor: 'center',
|
||||
|
||||
@@ -43,8 +43,8 @@ export interface Event {
|
||||
enable_devtools_protection?: boolean;
|
||||
use_canvas_rendering?: boolean;
|
||||
// Hero logo customization fields
|
||||
hero_logo_visible?: boolean;
|
||||
hero_logo_size?: 'small' | 'medium' | 'large' | 'xlarge';
|
||||
hero_logo_visible?: boolean | null;
|
||||
hero_logo_size?: 'small' | 'medium' | 'large' | 'xlarge' | null;
|
||||
hero_logo_position?: 'top' | 'center' | 'bottom';
|
||||
hero_logo_url?: string | null;
|
||||
// Per-event opt-in for using the hero photo as the social-share
|
||||
@@ -188,8 +188,8 @@ export interface GalleryData {
|
||||
fragmentation_level?: number;
|
||||
overlay_protection?: boolean;
|
||||
// Hero logo customization fields
|
||||
hero_logo_visible?: boolean;
|
||||
hero_logo_size?: 'small' | 'medium' | 'large' | 'xlarge';
|
||||
hero_logo_visible?: boolean | null;
|
||||
hero_logo_size?: 'small' | 'medium' | 'large' | 'xlarge' | null;
|
||||
hero_logo_position?: 'top' | 'center' | 'bottom';
|
||||
hero_logo_url?: string | null;
|
||||
// Header style settings (decoupled from layout)
|
||||
|
||||
Reference in New Issue
Block a user