diff --git a/resources/js/admin/i18n/locales/de/management.json b/resources/js/admin/i18n/locales/de/management.json index c460474..00d5681 100644 --- a/resources/js/admin/i18n/locales/de/management.json +++ b/resources/js/admin/i18n/locales/de/management.json @@ -1925,10 +1925,10 @@ "surface": "Fläche", "lockedBranding": "Branding ist in diesem Paket gesperrt.", "source": "Branding-Quelle", - "sourceHint": "Nutze das Tenant-Branding oder überschreibe es für dieses Event.", - "useDefault": "Tenant", - "useCustom": "Event", - "usingDefault": "Tenant-Branding aktiv", + "sourceHint": "Nutze das Standard-Branding oder passe nur dieses Event an.", + "useDefault": "Standard", + "useCustom": "Dieses Event", + "usingDefault": "Standard-Branding aktiv", "usingCustom": "Event-Branding aktiv", "mode": "Theme", "modeLight": "Hell", diff --git a/resources/js/admin/i18n/locales/en/management.json b/resources/js/admin/i18n/locales/en/management.json index dd88a5a..d26c6de 100644 --- a/resources/js/admin/i18n/locales/en/management.json +++ b/resources/js/admin/i18n/locales/en/management.json @@ -1929,10 +1929,10 @@ "surface": "Surface", "lockedBranding": "Branding is locked for this package.", "source": "Branding source", - "sourceHint": "Use tenant branding or override for this event.", - "useDefault": "Tenant", - "useCustom": "Event", - "usingDefault": "Tenant branding active", + "sourceHint": "Use the default branding or customize this event only.", + "useDefault": "Default", + "useCustom": "This event", + "usingDefault": "Default branding active", "usingCustom": "Event branding active", "mode": "Theme", "modeLight": "Light", diff --git a/resources/js/admin/lib/__tests__/events.test.ts b/resources/js/admin/lib/__tests__/events.test.ts new file mode 100644 index 0000000..9dc61d0 --- /dev/null +++ b/resources/js/admin/lib/__tests__/events.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from 'vitest'; +import { isBrandingAllowed, isWatermarkAllowed } from '../events'; + +describe('event branding access helpers', () => { + it('respects package-level disallow', () => { + const event = { + settings: { branding_allowed: true, watermark_allowed: true }, + package: { branding_allowed: false, watermark_allowed: false }, + }; + + expect(isBrandingAllowed(event as any)).toBe(false); + expect(isWatermarkAllowed(event as any)).toBe(false); + }); + + it('uses settings when package allows', () => { + const event = { + settings: { branding_allowed: false, watermark_allowed: true }, + package: { branding_allowed: true, watermark_allowed: true }, + }; + + expect(isBrandingAllowed(event as any)).toBe(false); + expect(isWatermarkAllowed(event as any)).toBe(true); + }); + + it('defaults to allow when nothing is set', () => { + expect(isBrandingAllowed({} as any)).toBe(true); + expect(isWatermarkAllowed({} as any)).toBe(true); + }); +}); diff --git a/resources/js/admin/lib/events.ts b/resources/js/admin/lib/events.ts index 02cabab..6cd3e39 100644 --- a/resources/js/admin/lib/events.ts +++ b/resources/js/admin/lib/events.ts @@ -92,7 +92,28 @@ export function resolveEngagementMode(event?: TenantEvent | null): 'tasks' | 'ph export function isBrandingAllowed(event?: TenantEvent | null): boolean { if (!event) return true; - return Boolean((event.package as any)?.branding_allowed ?? true); + const settings = (event.settings ?? {}) as Record; + const packageAllowed = (event.package as any)?.branding_allowed; + if (packageAllowed === false) { + return false; + } + if (typeof settings.branding_allowed === 'boolean') { + return settings.branding_allowed; + } + return true; +} + +export function isWatermarkAllowed(event?: TenantEvent | null): boolean { + if (!event) return true; + const settings = (event.settings ?? {}) as Record; + const packageAllowed = (event.package as any)?.watermark_allowed; + if (packageAllowed === false) { + return false; + } + if (typeof settings.watermark_allowed === 'boolean') { + return settings.watermark_allowed; + } + return true; } export function formatEventStatusLabel( diff --git a/resources/js/admin/mobile/BrandingPage.tsx b/resources/js/admin/mobile/BrandingPage.tsx index 5400f0c..6c4c3c0 100644 --- a/resources/js/admin/mobile/BrandingPage.tsx +++ b/resources/js/admin/mobile/BrandingPage.tsx @@ -10,7 +10,7 @@ import { MobileCard, CTAButton, SkeletonCard } from './components/Primitives'; import { TenantEvent, getEvent, updateEvent, getTenantFonts, getTenantSettings, TenantFont, WatermarkSettings, trackOnboarding } from '../api'; import { isAuthError } from '../auth/tokens'; import { ApiError, getApiErrorMessage } from '../lib/apiError'; -import { isBrandingAllowed } from '../lib/events'; +import { isBrandingAllowed, isWatermarkAllowed } from '../lib/events'; import { MobileSheet } from './components/Sheet'; import toast from 'react-hot-toast'; import { adminPath } from '../constants'; @@ -179,7 +179,7 @@ export default function MobileBrandingPage() { const previewLogoUrl = previewForm.logoMode === 'upload' ? previewForm.logoDataUrl : ''; const previewLogoValue = previewForm.logoMode === 'emoticon' ? previewForm.logoValue : ''; const previewInitials = getInitials(previewTitle); - const watermarkAllowed = event?.package?.watermark_allowed !== false; + const watermarkAllowed = isWatermarkAllowed(event ?? null); const brandingAllowed = isBrandingAllowed(event ?? null); const watermarkLocked = watermarkAllowed && !brandingAllowed; const brandingDisabled = !brandingAllowed || form.useDefaultBranding; @@ -616,17 +616,17 @@ export default function MobileBrandingPage() { {t('events.branding.source', 'Branding Source')} - {t('events.branding.sourceHint', 'Nutze das Tenant-Branding oder überschreibe es für dieses Event.')} + {t('events.branding.sourceHint', 'Use the default branding or customize this event only.')} setForm((prev) => ({ ...prev, useDefaultBranding: true }))} disabled={!brandingAllowed} /> setForm((prev) => ({ ...prev, useDefaultBranding: false }))} disabled={!brandingAllowed} @@ -1275,7 +1275,7 @@ function LabeledSlider({ disabled?: boolean; suffix?: string; }) { - const { textStrong, muted } = useAdminTheme(); + const { textStrong, muted, primary } = useAdminTheme(); return ( @@ -1295,7 +1295,7 @@ function LabeledSlider({ value={value} disabled={disabled} onChange={(event) => onChange(Number(event.target.value))} - style={{ width: '100%' }} + style={{ width: '100%', height: 28, accentColor: primary }} /> );