fix(branding): theme-aware logo across all login / auth entry pages
Extends the dark-logo fix to the customer login, customer accept-invite, customer reset-password, and gallery client-access pages — they all rendered only the light logo on the themed (possibly dark) surface. Also makes the login-page pick frame-aware: a framed login logo sits on a fixed cream plate, so the light (dark-ink) logo always reads there; only the frameless logo sits on the themed page background and uses the dark variant. This corrects the admin login too (was unconditionally swapping when dark). Customer/gallery pages read isDark from usePublicDarkMode (branding_force_ color_mode + OS fallback), matching CustomerLayout.
This commit is contained in:
@@ -7,6 +7,7 @@ import { Card, CardContent, Input, Button, Loading } from '../components/common'
|
||||
import { useGalleryAuth } from '../contexts';
|
||||
import { useGalleryInfo } from '../hooks/useGallery';
|
||||
import { usePublicSettings } from '../hooks/usePublicSettings';
|
||||
import { usePublicDarkMode } from '../hooks/usePublicDarkMode';
|
||||
import { buildResourceUrl } from '../utils/url';
|
||||
|
||||
export const ClientAccessPage: React.FC = () => {
|
||||
@@ -22,6 +23,13 @@ export const ClientAccessPage: React.FC = () => {
|
||||
const { data: galleryInfo, isLoading: isLoadingInfo, error: infoError } = useGalleryInfo(slug);
|
||||
|
||||
const { data: settingsData } = usePublicSettings();
|
||||
// Theme-aware logo: the page background follows the themed
|
||||
// --color-background (dark when branding_force_color_mode / OS is dark),
|
||||
// so pick the dark logo variant accordingly.
|
||||
const { isDark } = usePublicDarkMode();
|
||||
const lightLogo = settingsData?.branding_logo_url?.trim();
|
||||
const darkLogo = settingsData?.branding_logo_url_dark?.trim();
|
||||
const brandLogo = isDark ? (darkLogo || lightLogo) : (lightLogo || darkLogo);
|
||||
|
||||
// If already authenticated as client, redirect to gallery
|
||||
React.useEffect(() => {
|
||||
@@ -76,10 +84,10 @@ export const ClientAccessPage: React.FC = () => {
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: 'var(--color-background, #fafafa)' }}>
|
||||
<div className="min-h-screen flex flex-col">
|
||||
{settingsData?.branding_logo_url && (
|
||||
{brandLogo && (
|
||||
<div className="p-8 text-center">
|
||||
<img
|
||||
src={buildResourceUrl(settingsData.branding_logo_url)}
|
||||
src={buildResourceUrl(brandLogo)}
|
||||
alt={settingsData.branding_company_name || 'Company Logo'}
|
||||
className="h-16 w-auto object-contain mx-auto"
|
||||
/>
|
||||
@@ -103,10 +111,10 @@ export const ClientAccessPage: React.FC = () => {
|
||||
<div className="min-h-screen" style={{ backgroundColor: 'var(--color-background, #fafafa)' }}>
|
||||
<div className="min-h-screen flex flex-col">
|
||||
{/* Logo */}
|
||||
{settingsData?.branding_logo_url && (
|
||||
{brandLogo && (
|
||||
<div className="p-8 text-center">
|
||||
<img
|
||||
src={buildResourceUrl(settingsData.branding_logo_url)}
|
||||
src={buildResourceUrl(brandLogo)}
|
||||
alt={settingsData.branding_company_name || 'Company Logo'}
|
||||
className="h-16 w-auto object-contain mx-auto"
|
||||
/>
|
||||
|
||||
@@ -32,11 +32,15 @@ export const AdminLoginPage: React.FC = () => {
|
||||
|
||||
const companyName = settingsData?.branding_company_name?.trim() || 'PicPeak';
|
||||
// Theme-aware logo: the login page honours the admin dark-mode preference
|
||||
// (and any branding_force_color_mode), so a dark-text logo isn't shown on
|
||||
// the dark background. Falls back to whichever variant exists.
|
||||
// (and any branding_force_color_mode). NOTE the frame nuance — a framed
|
||||
// logo sits on a fixed cream plate (see render), so the light (dark-ink)
|
||||
// logo always reads there; only the frameless logo sits on the themed
|
||||
// (possibly dark) page background and needs the dark variant.
|
||||
const lightLogo = settingsData?.branding_logo_url?.trim();
|
||||
const darkLogo = settingsData?.branding_logo_url_dark?.trim();
|
||||
const logoUrl = isDark ? (darkLogo || lightLogo) : (lightLogo || darkLogo);
|
||||
const loginFrameEnabled = settingsData?.branding_login_logo_frame_enabled !== false;
|
||||
const themedLogo = isDark ? (darkLogo || lightLogo) : (lightLogo || darkLogo);
|
||||
const logoUrl = loginFrameEnabled ? (lightLogo || darkLogo) : themedLogo;
|
||||
const resolvedLogoUrl = logoUrl || '/picpeak-logo-transparent.png';
|
||||
|
||||
// Check for session expired message
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
type CustomerProfilePrefill,
|
||||
} from '../../services/customer.service';
|
||||
import { usePublicSettings } from '../../hooks/usePublicSettings';
|
||||
import { usePublicDarkMode } from '../../hooks/usePublicDarkMode';
|
||||
|
||||
interface FormState {
|
||||
display_name: string;
|
||||
@@ -76,8 +77,14 @@ export const CustomerAcceptInvitePage: React.FC = () => {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const { data: settingsData } = usePublicSettings();
|
||||
// Theme-aware logo: the page renders on the themed customer surface
|
||||
// (dark when branding_force_color_mode is dark / OS dark), so pick the
|
||||
// dark logo variant accordingly. No frame here — logo sits on the page bg.
|
||||
const { isDark } = usePublicDarkMode();
|
||||
const companyName = settingsData?.branding_company_name?.trim() || 'PicPeak';
|
||||
const logoUrl = settingsData?.branding_logo_url?.trim();
|
||||
const lightLogo = settingsData?.branding_logo_url?.trim();
|
||||
const darkLogo = settingsData?.branding_logo_url_dark?.trim();
|
||||
const logoUrl = isDark ? (darkLogo || lightLogo) : (lightLogo || darkLogo);
|
||||
const resolvedLogoUrl = logoUrl || '/picpeak-logo-transparent.png';
|
||||
|
||||
// Pre-flight invitation lookup. The response carries any prefill data
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Button, Input, Card, ReCaptcha } from '../../components/common';
|
||||
import { useCustomerAuth } from '../../contexts/CustomerAuthContext';
|
||||
import { customerService } from '../../services/customer.service';
|
||||
import { usePublicSettings } from '../../hooks/usePublicSettings';
|
||||
import { usePublicDarkMode } from '../../hooks/usePublicDarkMode';
|
||||
import { resolveLoginLogoClasses } from '../../utils/loginLogoSize';
|
||||
|
||||
export const CustomerLoginPage: React.FC = () => {
|
||||
@@ -28,8 +29,17 @@ export const CustomerLoginPage: React.FC = () => {
|
||||
const [recaptchaToken, setRecaptchaToken] = useState<string | null>(null);
|
||||
|
||||
const { data: settingsData } = usePublicSettings();
|
||||
// Customer surface follows branding_force_color_mode (+ OS fallback); isDark
|
||||
// drives the theme-aware logo pick. A framed logo sits on a fixed cream
|
||||
// plate (see render), so the light (dark-ink) logo always reads there;
|
||||
// only the frameless logo sits on the themed (possibly dark) page bg.
|
||||
const { isDark } = usePublicDarkMode();
|
||||
const companyName = settingsData?.branding_company_name?.trim() || 'PicPeak';
|
||||
const logoUrl = settingsData?.branding_logo_url?.trim();
|
||||
const lightLogo = settingsData?.branding_logo_url?.trim();
|
||||
const darkLogo = settingsData?.branding_logo_url_dark?.trim();
|
||||
const loginFrameEnabled = settingsData?.branding_login_logo_frame_enabled !== false;
|
||||
const themedLogo = isDark ? (darkLogo || lightLogo) : (lightLogo || darkLogo);
|
||||
const logoUrl = loginFrameEnabled ? (lightLogo || darkLogo) : themedLogo;
|
||||
const resolvedLogoUrl = logoUrl || '/picpeak-logo-transparent.png';
|
||||
|
||||
// After /accept-invite the user is redirected here with ?accepted=1
|
||||
|
||||
@@ -18,6 +18,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Button, Input, Card, Loading } from '../../components/common';
|
||||
import { customerService } from '../../services/customer.service';
|
||||
import { usePublicSettings } from '../../hooks/usePublicSettings';
|
||||
import { usePublicDarkMode } from '../../hooks/usePublicDarkMode';
|
||||
|
||||
export const CustomerResetPasswordPage: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -33,8 +34,14 @@ export const CustomerResetPasswordPage: React.FC = () => {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const { data: settingsData } = usePublicSettings();
|
||||
// Theme-aware logo: page renders on the themed customer surface (dark when
|
||||
// branding_force_color_mode is dark / OS dark). No frame — logo sits on the
|
||||
// page bg, so pick the dark variant when dark.
|
||||
const { isDark } = usePublicDarkMode();
|
||||
const companyName = settingsData?.branding_company_name?.trim() || 'PicPeak';
|
||||
const logoUrl = settingsData?.branding_logo_url?.trim();
|
||||
const lightLogo = settingsData?.branding_logo_url?.trim();
|
||||
const darkLogo = settingsData?.branding_logo_url_dark?.trim();
|
||||
const logoUrl = isDark ? (darkLogo || lightLogo) : (lightLogo || darkLogo);
|
||||
const resolvedLogoUrl = logoUrl || '/picpeak-logo-transparent.png';
|
||||
|
||||
// Pre-flight token validation. Same pattern as the invite page — if the
|
||||
|
||||
Reference in New Issue
Block a user