feat: implement header/footer visibility controls for checkout and thankyou pages

- Created LayoutWrapper component to conditionally render header/footer based on route
- Created MinimalHeader component (logo only)
- Created MinimalFooter component (trust badges + policy links)
- Created usePageVisibility hook to get visibility settings per page
- Wrapped ClassicLayout with LayoutWrapper for conditional rendering
- Header/footer visibility now controlled directly in React SPA
- Settings: show/minimal/hide for both header and footer
- Background color support for checkout and thankyou pages
This commit is contained in:
Dwindi Ramadhana
2025-12-25 22:20:48 +07:00
parent c37ecb8e96
commit 9ac09582d2
104 changed files with 14801 additions and 1213 deletions

View File

@@ -1,4 +1,4 @@
import React, { createContext, useContext, useEffect, ReactNode } from 'react';
import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
interface ThemeColors {
primary: string;
@@ -14,6 +14,7 @@ interface ThemeTypography {
heading: string;
body: string;
};
scale?: number;
}
interface ThemeConfig {
@@ -28,32 +29,41 @@ interface ThemeContextValue {
isFullSPA: boolean;
isCheckoutOnly: boolean;
isLaunchLayout: boolean;
loading: boolean;
}
const ThemeContext = createContext<ThemeContextValue | null>(null);
// Map our predefined font pairs to presets
const FONT_PAIR_MAP: Record<string, string> = {
modern: 'modern',
editorial: 'elegant',
friendly: 'professional',
elegant: 'elegant',
};
const TYPOGRAPHY_PRESETS = {
professional: {
modern: {
heading: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
body: "'Lora', Georgia, serif",
body: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
headingWeight: 700,
bodyWeight: 400,
},
modern: {
professional: {
heading: "'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
body: "'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
body: "'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
headingWeight: 600,
bodyWeight: 400,
},
elegant: {
heading: "'Playfair Display', Georgia, serif",
body: "'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
body: "'Source Sans 3', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
headingWeight: 700,
bodyWeight: 400,
},
tech: {
heading: "'Space Grotesk', monospace",
body: "'IBM Plex Mono', monospace",
heading: "'Cormorant Garamond', Georgia, serif",
body: "'Lato', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
headingWeight: 700,
bodyWeight: 400,
},
@@ -112,12 +122,62 @@ function generateColorShades(baseColor: string): Record<number, string> {
}
export function ThemeProvider({
config,
config: initialConfig,
children
}: {
config: ThemeConfig;
children: ReactNode;
}) {
const [config, setConfig] = useState<ThemeConfig>(initialConfig);
const [loading, setLoading] = useState(true);
// Fetch settings from API
useEffect(() => {
const fetchSettings = async () => {
try {
const apiRoot = (window as any).woonoowCustomer?.apiRoot || '/wp-json/woonoow/v1';
const response = await fetch(`${apiRoot}/appearance/settings`, {
credentials: 'include',
});
if (response.ok) {
const data = await response.json();
const settings = data.data;
if (settings?.general) {
const general = settings.general;
// Map API settings to theme config
const mappedPreset = FONT_PAIR_MAP[general.typography?.predefined_pair] || 'modern';
const newConfig: ThemeConfig = {
mode: general.spa_mode || 'full',
layout: 'modern', // Keep existing layout for now
colors: {
primary: general.colors?.primary || '#3B82F6',
secondary: general.colors?.secondary || '#8B5CF6',
accent: general.colors?.accent || '#10B981',
background: general.colors?.background || '#ffffff',
text: general.colors?.text || '#111827',
},
typography: {
preset: mappedPreset as 'professional' | 'modern' | 'elegant' | 'tech' | 'custom',
scale: general.typography?.scale || 1.0,
},
};
setConfig(newConfig);
}
}
} catch (error) {
console.error('Failed to fetch appearance settings:', error);
} finally {
setLoading(false);
}
};
fetchSettings();
}, []);
useEffect(() => {
const root = document.documentElement;
@@ -142,8 +202,13 @@ export function ThemeProvider({
root.style.setProperty('--font-weight-body', typoPreset.bodyWeight.toString());
}
// Load Google Fonts
loadTypography(config.typography.preset, config.typography.customFonts);
// Apply font scale
if (config.typography.scale) {
root.style.setProperty('--font-scale', config.typography.scale.toString());
}
// We're using self-hosted fonts now, no need to load from Google
// loadTypography(config.typography.preset, config.typography.customFonts);
// Add layout class to body
document.body.classList.remove('layout-classic', 'layout-modern', 'layout-boutique', 'layout-launch');
@@ -159,6 +224,7 @@ export function ThemeProvider({
isFullSPA: config.mode === 'full',
isCheckoutOnly: config.mode === 'checkout_only',
isLaunchLayout: config.layout === 'launch',
loading,
};
return (