diff --git a/DATABASE template/Design/Details/Style.txt b/DATABASE template/Design/Details/Style.txt index 8e73aa4..25e28b3 100644 --- a/DATABASE template/Design/Details/Style.txt +++ b/DATABASE template/Design/Details/Style.txt @@ -1,2 +1,3 @@ cornerRadius: 12 style: rounded +logoSize: 1 diff --git a/DATABASE/Design/Details/Style.txt b/DATABASE/Design/Details/Style.txt index b226a3e..f88f711 100644 --- a/DATABASE/Design/Details/Style.txt +++ b/DATABASE/Design/Details/Style.txt @@ -1,2 +1,3 @@ cornerRadius: 0 -style: sharp \ No newline at end of file +style: sharp +logoSize: 1 diff --git a/app/about/page.tsx b/app/about/page.tsx index b2b2da7..85e070c 100644 --- a/app/about/page.tsx +++ b/app/about/page.tsx @@ -51,7 +51,7 @@ export default function AboutPage() { const response = await fetch('/api/design'); const designData = await response.json(); setDesign(designData); - document.title = `${designData.companyName} - About`; + document.title = `${designData.companyName || 'Shuttle'} - About`; const versionResponse = await fetch('/api/version'); const versionData = await versionResponse.json(); diff --git a/app/cart/page.tsx b/app/cart/page.tsx index acf84c7..40a9078 100644 --- a/app/cart/page.tsx +++ b/app/cart/page.tsx @@ -39,6 +39,7 @@ export default function CartPage() { fetch('/api/inventory').then(r => r.ok ? r.json() : []).catch(() => []), ]).then(([designData, inventoryData]) => { setDesign(designData); + document.title = `${designData.companyName || 'Shuttle'} - Cart`; const map: Record = {}; inventoryData.forEach((item: any) => { map[item.productId] = item.stock; }); setStockMap(map); diff --git a/app/checkout/page.tsx b/app/checkout/page.tsx index 13a4c88..1d57ad4 100644 --- a/app/checkout/page.tsx +++ b/app/checkout/page.tsx @@ -83,7 +83,10 @@ export default function CheckoutPage() { fetch('/api/design') .then(r => r.json()) - .then(setDesign) + .then((designData) => { + setDesign(designData); + document.title = `${designData.companyName || 'Shuttle'} - Checkout`; + }) .catch(console.error); fetch('/api/presets') diff --git a/app/collections/[collectionId]/page.tsx b/app/collections/[collectionId]/page.tsx index adbe2ee..bacb767 100644 --- a/app/collections/[collectionId]/page.tsx +++ b/app/collections/[collectionId]/page.tsx @@ -45,7 +45,7 @@ export default function CollectionPage({ params }: { params: Promise<{ collectio const coll = await collectionResponse.json(); setCollection(coll); - document.title = `${designData.companyName} - ${coll.name}`; + document.title = `${designData.companyName || 'Shuttle'} - ${coll.name}`; // Use showcase photo if available, otherwise fall back to product images const showcaseImage = designData.collectionShowcaseImages?.[resolvedParams.collectionId]; diff --git a/app/collections/page.tsx b/app/collections/page.tsx index b6189a8..08d46aa 100644 --- a/app/collections/page.tsx +++ b/app/collections/page.tsx @@ -28,7 +28,7 @@ export default function CollectionsPage() { setDesign(designData); setCollections(collectionsData); - document.title = `${designData.companyName} - Collections`; + document.title = `${designData.companyName || 'Shuttle'} - Collections`; } loadData(); }, [router]); diff --git a/app/layout.tsx b/app/layout.tsx index 492d6aa..8150060 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -5,10 +5,14 @@ import Header from "@/components/Header"; import Footer from "@/components/Footer"; import PasswordProtection from "@/components/PasswordProtection"; +// Always render per-request so the title/branding reflect the current +// DATABASE contents instead of whatever was present at build time. +export const dynamic = 'force-dynamic'; + export async function generateMetadata(): Promise { const design = getDesignData(); return { - title: design.companyName, + title: design.companyName || 'Shuttle', description: design.descriptions.tagline, icons: { icon: '/favicon.ico', @@ -83,6 +87,7 @@ export default function RootLayout({ titleFont={design.fonts.titleFont} bodyFont={design.fonts.bodyFont} cornerRadius={design.style.cornerRadius} + logoSize={design.style.logoSize} />
{children} diff --git a/app/order-success/page.tsx b/app/order-success/page.tsx index 92a7d32..84d2f54 100644 --- a/app/order-success/page.tsx +++ b/app/order-success/page.tsx @@ -170,7 +170,10 @@ function OrderSuccessContent() { useEffect(() => { fetch('/api/design') .then(r => r.json()) - .then(setDesign) + .then((designData) => { + setDesign(designData); + document.title = `${designData.companyName || 'Shuttle'} - Order Confirmation`; + }) .catch(console.error); try { diff --git a/app/page.tsx b/app/page.tsx index 97de2d8..41662e7 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -23,7 +23,7 @@ export default function Home() { setDesign(designData); setCollections(collectionsData); - document.title = designData.companyName; + document.title = designData.companyName || 'Shuttle'; // If single collection, flatten products and fetch inventory for stock badges if (collectionsData.length >= 1 && collectionsData.length <= 1) { @@ -95,7 +95,7 @@ export default function Home() { src={design.logoWhitePath} alt={design.companyName} className="w-auto mx-auto mb-4" - style={{ height: '4.5rem', objectFit: 'contain' }} + style={{ height: `${4.5 * (design.style?.logoSize || 1)}rem`, objectFit: 'contain' }} /> ) : (

{ diff --git a/app/secret/page.tsx b/app/secret/page.tsx index 8d51ff0..992a7e7 100644 --- a/app/secret/page.tsx +++ b/app/secret/page.tsx @@ -30,7 +30,10 @@ export default function SecretPage() { useEffect(() => { fetch('/api/design') .then(r => r.json()) - .then(setDesign) + .then((designData) => { + setDesign(designData); + document.title = designData.companyName || 'Shuttle'; + }) .catch(console.error); }, []); diff --git a/app/shop-all/page.tsx b/app/shop-all/page.tsx index ec94636..9eded61 100644 --- a/app/shop-all/page.tsx +++ b/app/shop-all/page.tsx @@ -24,7 +24,7 @@ export default function ShopAllPage() { const collectionsData = await collectionsResponse.json(); setDesign(designData); - document.title = `${designData.companyName} - Shop All`; + document.title = `${designData.companyName || 'Shuttle'} - Shop All`; // Build stock map from inventory if (inventoryResponse?.ok) { diff --git a/components/Footer.tsx b/components/Footer.tsx index 6b98732..c44b4e0 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -8,8 +8,31 @@ interface FooterProps { bodyFont?: string; } +/** + * Returns true when a hex color is light enough that black foreground + * elements should be used on top of it. Falls back to "dark" (white + * foreground) when the color can't be parsed. + */ +function isLightColor(hex: string): boolean { + if (!hex) return false; + let h = hex.trim().replace('#', ''); + if (h.length === 3) h = h.split('').map((c) => c + c).join(''); + if (h.length !== 6) return false; + const r = parseInt(h.slice(0, 2), 16); + const g = parseInt(h.slice(2, 4), 16); + const b = parseInt(h.slice(4, 6), 16); + if ([r, g, b].some(isNaN)) return false; + // Perceived luminance (ITU-R BT.601) + return (0.299 * r + 0.587 * g + 0.114 * b) / 255 > 0.6; +} + export default function Footer({ footerText, primaryColor, bodyFont = 'Inter' }: FooterProps) { const version = getVersion(); + const lightBg = isLightColor(primaryColor); + const textColor = lightBg ? '#1a1a1a' : '#ffffff'; + // LR Paris logo is black artwork on a transparent background: + // keep it black on light footers, invert to white on dark footers. + const logoFilter = lightBg ? 'brightness(0)' : 'brightness(0) invert(1)'; return (