fix(branding): theme-aware logo on customer-facing public pages

The public quote, contract-signing, and payment-check pages baked a single
light logo (the contract page showed none), so the dark page rendered a
dark-text logo on a dark background.

- usePublicDarkMode now returns { isDark } (reactive) alongside applying
  the .dark class, so pages can pick a theme-aware asset.
- The three public routes now surface both branding logo URLs (logoUrl +
  logoUrlDark) in the issuer block; the contract issuer gains a logo too.
- QuoteResponsePage, ContractResponsePage, and the payment-check
  BrandingHeader pick the dark variant when isDark, falling back to
  whichever exists. Covers the accept/accepted states of each page.
This commit is contained in:
Luca
2026-06-03 16:48:54 +02:00
parent a5011b1ea2
commit 05c1e8d18b
10 changed files with 109 additions and 35 deletions
@@ -81,8 +81,9 @@ export const ContractResponsePage: React.FC = () => {
// Honour branding dark/light mode the same way QuoteResponsePage
// does — without this the page renders in light regardless of admin
// settings. The wrapper styling below still has `dark:` variants
// so the page reads cleanly in either mode.
usePublicDarkMode();
// so the page reads cleanly in either mode. `isDark` drives the
// theme-aware logo pick in the header.
const { isDark } = usePublicDarkMode();
const canvasRef = useRef<HTMLCanvasElement>(null);
const padRef = useRef<SignaturePad | null>(null);
@@ -233,6 +234,18 @@ export const ContractResponsePage: React.FC = () => {
<div className="max-w-3xl mx-auto py-8 px-4">
{/* Issuer header — same shape as QuoteResponsePage. */}
<div className="text-center mb-6">
{(() => {
const logo = isDark
? (c.issuer?.logoUrlDark || c.issuer?.logoUrl)
: (c.issuer?.logoUrl || c.issuer?.logoUrlDark);
return logo ? (
<img
src={logo}
alt={c.issuer?.companyName || 'Logo'}
className="mx-auto mb-3 h-16 object-contain"
/>
) : null;
})()}
{c.issuer?.companyName && (
<h2 className="text-xl font-bold">{c.issuer.companyName}</h2>
)}
@@ -260,12 +260,16 @@ export const PaymentCheckPage: React.FC = () => {
};
const BrandingHeader: React.FC<{ issuer: PaymentCheckIssuer | null }> = ({ issuer }) => {
if (!issuer || (!issuer.logoUrl && !issuer.companyName)) return null;
const { isDark } = usePublicDarkMode();
if (!issuer || (!issuer.logoUrl && !issuer.logoUrlDark && !issuer.companyName)) return null;
const logo = isDark
? (issuer.logoUrlDark || issuer.logoUrl)
: (issuer.logoUrl || issuer.logoUrlDark);
return (
<header className="text-center mb-8">
{issuer.logoUrl && (
{logo && (
<img
src={issuer.logoUrl}
src={logo}
alt={issuer.companyName || 'Logo'}
className="mx-auto h-16 w-auto object-contain mb-3"
/>
@@ -39,7 +39,8 @@ export const QuoteResponsePage: React.FC = () => {
// Apply dark mode per the branding settings (forced dark/light)
// or fall back to the OS preference. Without this the public
// quote page renders in light mode regardless of admin settings.
usePublicDarkMode();
// `isDark` drives the theme-aware logo pick below.
const { isDark } = usePublicDarkMode();
const { data, isLoading, isError, refetch } = useQuery({
queryKey: ['public-quote', token],
@@ -153,13 +154,18 @@ export const QuoteResponsePage: React.FC = () => {
the backend storage directory directly. */}
{quote.issuer && (
<div className="text-center mb-6">
{quote.issuer.logoUrl && (
<img
src={quote.issuer.logoUrl}
alt={quote.issuer.companyName || 'Logo'}
className="mx-auto mb-3 h-16 object-contain"
/>
)}
{(() => {
const logo = isDark
? (quote.issuer.logoUrlDark || quote.issuer.logoUrl)
: (quote.issuer.logoUrl || quote.issuer.logoUrlDark);
return logo ? (
<img
src={logo}
alt={quote.issuer.companyName || 'Logo'}
className="mx-auto mb-3 h-16 object-contain"
/>
) : null;
})()}
<h2 className="text-xl font-bold">{quote.issuer.companyName}</h2>
{quote.issuer.website && (
<p className="text-sm text-neutral-500 dark:text-neutral-400">{quote.issuer.website}</p>