diff --git a/src/components/storefront/Footer.jsx b/src/components/storefront/Footer.jsx index c51986f..c4cf106 100644 --- a/src/components/storefront/Footer.jsx +++ b/src/components/storefront/Footer.jsx @@ -1,26 +1,36 @@ import { useState, useEffect } from 'react' -import { Button } from '../ui/button' -export function Footer() { - const [contactEmail, setContactEmail] = useState('contact@example.com') - const [loading, setLoading] = useState(true) +const fallbackEmail = 'contact@example.com' + +export function Footer({ contactEmail: initialContactEmail = null }) { + const [contactEmail, setContactEmail] = useState(initialContactEmail || fallbackEmail) + const [loading, setLoading] = useState(!initialContactEmail) + + useEffect(() => { + if (!initialContactEmail) { + fetchContactEmail() + } + }, [initialContactEmail]) useEffect(() => { - fetchContactEmail() - }, []) + if (initialContactEmail) { + setContactEmail(initialContactEmail) + setLoading(false) + } + }, [initialContactEmail]) const fetchContactEmail = async () => { try { const response = await fetch('/api/contact-email') if (response.ok) { const data = await response.json() - setContactEmail(data.email || 'contact@example.com') + setContactEmail(data.email || fallbackEmail) } else { - setContactEmail('contact@example.com') + setContactEmail(fallbackEmail) } } catch (error) { console.error('Error fetching contact email:', error) - setContactEmail('contact@example.com') + setContactEmail(fallbackEmail) } finally { setLoading(false) } @@ -37,7 +47,7 @@ export function Footer() { Have questions about our products or need support? We'd love to hear from you.

{loading ? 'Loading...' : 'Contact Us'} diff --git a/src/components/storefront/Hero.jsx b/src/components/storefront/Hero.jsx index a6ae950..10c37f5 100644 --- a/src/components/storefront/Hero.jsx +++ b/src/components/storefront/Hero.jsx @@ -2,11 +2,16 @@ import { useEffect, useState } from 'react' import { normalizeImageUrl } from '../../lib/utils' import { Button } from '../ui/button' -export function Hero() { +const defaultHeroSettings = { + heroImageUrl: '', + heroTitle: 'Welcome to OpenShop', + heroSubtitle: 'Discover amazing products at unbeatable prices. Built on Cloudflare for lightning-fast performance.' +} + +export function Hero({ initialSettings = null }) { const [settings, setSettings] = useState({ - heroImageUrl: '', - heroTitle: 'Welcome to OpenShop', - heroSubtitle: 'Discover amazing products at unbeatable prices. Built on Cloudflare for lightning-fast performance.' + ...defaultHeroSettings, + ...(initialSettings || {}) }) useEffect(() => { @@ -27,9 +32,20 @@ export function Hero() { console.error('Failed to load store settings', e) } } - fetchSettings() + if (!initialSettings) { + fetchSettings() + } return () => { isMounted = false } - }, []) + }, [initialSettings]) + + useEffect(() => { + if (initialSettings) { + setSettings(prev => ({ + ...prev, + ...initialSettings + })) + } + }, [initialSettings]) return (
diff --git a/src/components/storefront/HydrationOverlay.jsx b/src/components/storefront/HydrationOverlay.jsx new file mode 100644 index 0000000..048bf68 --- /dev/null +++ b/src/components/storefront/HydrationOverlay.jsx @@ -0,0 +1,67 @@ +const shimmer = 'animate-pulse bg-gradient-to-r from-gray-200 via-gray-100 to-gray-200' + +export function HydrationOverlay() { + return ( +
+
+
+ + ) +} diff --git a/src/components/storefront/Navbar.jsx b/src/components/storefront/Navbar.jsx index a24cea1..0a9d2c0 100644 --- a/src/components/storefront/Navbar.jsx +++ b/src/components/storefront/Navbar.jsx @@ -1,26 +1,92 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, useCallback } from 'react' import { normalizeImageUrl } from '../../lib/utils' -import { Link } from 'react-router-dom' +import { Link, useNavigate } from 'react-router-dom' import { Button } from '../ui/button' import { useCart } from '../../contexts/CartContext' import { ShoppingCart } from 'lucide-react' -export function Navbar() { - const [collections, setCollections] = useState([]) +const defaultStoreSettings = { + logoType: 'text', + logoText: 'OpenShop', + logoImageUrl: '' +} + +export function Navbar({ + initialCollections = [], + initialProducts = [], + initialStoreSettings = null +}) { + const [collections, setCollections] = useState(initialCollections) + const [products, setProducts] = useState(initialProducts) const [collectionsWithProducts, setCollectionsWithProducts] = useState([]) const [storeSettings, setStoreSettings] = useState({ - logoType: 'text', - logoText: 'OpenShop', - logoImageUrl: '' + ...defaultStoreSettings, + ...(initialStoreSettings || {}) }) const [mobileMenuOpen, setMobileMenuOpen] = useState(false) const { itemCount, toggleCart } = useCart() + const navigate = useNavigate() + const hasInitialCollections = initialCollections.length > 0 + const hasInitialProducts = initialProducts.length > 0 + const hasInitialSettings = Boolean(initialStoreSettings) + + const handleLogoClick = useCallback((event) => { + if ( + event.defaultPrevented || + event.button !== 0 || + event.metaKey || + event.altKey || + event.ctrlKey || + event.shiftKey + ) { + return + } + + event.preventDefault() + setMobileMenuOpen(false) + navigate('/') + + if (typeof window !== 'undefined') { + window.scrollTo({ top: 0, behavior: 'auto' }) + } + }, [navigate]) useEffect(() => { - // Fetch collections, products, and store settings for navigation - fetchCollectionsAndProducts() - fetchStoreSettings() - }, []) + setCollections(initialCollections) + }, [initialCollections]) + + useEffect(() => { + setProducts(initialProducts) + }, [initialProducts]) + + useEffect(() => { + if (initialStoreSettings) { + setStoreSettings(prev => ({ + ...prev, + ...initialStoreSettings + })) + } + }, [initialStoreSettings]) + + useEffect(() => { + if (!hasInitialCollections || !hasInitialProducts) { + fetchCollectionsAndProducts() + } + }, [hasInitialCollections, hasInitialProducts]) + + useEffect(() => { + if (!hasInitialSettings) { + fetchStoreSettings() + } + }, [hasInitialSettings]) + + useEffect(() => { + const grouped = collections.map(collection => ({ + ...collection, + products: products.filter(product => product.collectionId === collection.id) + })) + setCollectionsWithProducts(grouped) + }, [collections, products]) const fetchCollectionsAndProducts = async () => { try { @@ -34,14 +100,7 @@ export function Navbar() { const productsData = await productsResponse.json() setCollections(collectionsData) - - // Group products by collection - const collectionsWithProducts = collectionsData.map(collection => ({ - ...collection, - products: productsData.filter(product => product.collectionId === collection.id) - })) - - setCollectionsWithProducts(collectionsWithProducts) + setProducts(productsData) } } catch (error) { console.error('Error fetching collections and products:', error) @@ -53,7 +112,10 @@ export function Navbar() { const response = await fetch('/api/store-settings') if (response.ok) { const data = await response.json() - setStoreSettings(data) + setStoreSettings(prev => ({ + ...prev, + ...data + })) } } catch (error) { console.error('Error fetching store settings:', error) @@ -65,7 +127,7 @@ export function Navbar() {
{/* Logo */} - + {storeSettings.logoType === 'image' && storeSettings.logoImageUrl ? ( Home @@ -203,7 +266,7 @@ export function Navbar() { {/* Mobile menu button */}
-