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 (
+
+
+
+
+
+ {[1, 2, 3].map((item) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ {Array.from({ length: 8 }).map((_, index) => (
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/src/components/storefront/Navbar.jsx b/src/components/storefront/Navbar.jsx
index a24cea1..d109c96 100644
--- a/src/components/storefront/Navbar.jsx
+++ b/src/components/storefront/Navbar.jsx
@@ -5,22 +5,66 @@ 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 hasInitialCollections = initialCollections.length > 0
+ const hasInitialProducts = initialProducts.length > 0
+ const hasInitialSettings = Boolean(initialStoreSettings)
+
+ useEffect(() => {
+ setCollections(initialCollections)
+ }, [initialCollections])
+
+ useEffect(() => {
+ setProducts(initialProducts)
+ }, [initialProducts])
useEffect(() => {
- // Fetch collections, products, and store settings for navigation
- fetchCollectionsAndProducts()
- fetchStoreSettings()
- }, [])
+ 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 +78,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 +90,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)
diff --git a/src/pages/storefront/Storefront.jsx b/src/pages/storefront/Storefront.jsx
index 8a4b313..7d07776 100644
--- a/src/pages/storefront/Storefront.jsx
+++ b/src/pages/storefront/Storefront.jsx
@@ -4,12 +4,22 @@ import { Hero } from '../../components/storefront/Hero'
import { Carousel } from '../../components/storefront/Carousel'
import { ProductCard } from '../../components/storefront/ProductCard'
import { Footer } from '../../components/storefront/Footer'
+import { HydrationOverlay } from '../../components/storefront/HydrationOverlay'
import { Button } from '../../components/ui/button'
export function Storefront() {
const [products, setProducts] = useState([])
const [collections, setCollections] = useState([])
const [selectedCollection, setSelectedCollection] = useState(null)
+ const [storeSettings, setStoreSettings] = useState({
+ logoType: 'text',
+ logoText: 'OpenShop',
+ logoImageUrl: '',
+ heroImageUrl: '',
+ heroTitle: 'Welcome to OpenShop',
+ heroSubtitle: 'Discover amazing products at unbeatable prices. Built on Cloudflare for lightning-fast performance.'
+ })
+ const [contactEmail, setContactEmail] = useState('contact@example.com')
const [loading, setLoading] = useState(true)
useEffect(() => {
@@ -21,20 +31,40 @@ export function Storefront() {
setLoading(true)
// Fetch products and collections
- const [productsResponse, collectionsResponse] = await Promise.all([
- fetch('/api/products'),
- fetch('/api/collections')
+ const [
+ productsResponse,
+ collectionsResponse,
+ settingsResponse,
+ contactResponse
+ ] = await Promise.all([
+ fetch('/api/products').catch(() => null),
+ fetch('/api/collections').catch(() => null),
+ fetch('/api/store-settings').catch(() => null),
+ fetch('/api/contact-email').catch(() => null)
])
- if (productsResponse.ok) {
+ if (productsResponse?.ok) {
const productsData = await productsResponse.json()
setProducts(productsData)
}
- if (collectionsResponse.ok) {
+ if (collectionsResponse?.ok) {
const collectionsData = await collectionsResponse.json()
setCollections(collectionsData)
}
+
+ if (settingsResponse?.ok) {
+ const settingsData = await settingsResponse.json()
+ setStoreSettings(prev => ({
+ ...prev,
+ ...settingsData
+ }))
+ }
+
+ if (contactResponse?.ok) {
+ const contactData = await contactResponse.json()
+ setContactEmail(contactData.email || 'contact@example.com')
+ }
} catch (error) {
console.error('Error fetching data:', error)
} finally {
@@ -49,25 +79,19 @@ export function Storefront() {
const featuredProducts = products.slice(0, 3) // Show first 3 products in carousel
if (loading) {
- return (
-
-
-
-
-
-
Loading products...
-
-
-
- )
+ return
}
return (
-
-
+
+
{/* Hero Section */}
-
+
{/* Featured Products Carousel */}
{featuredProducts.length > 0 && (
@@ -132,7 +156,7 @@ export function Storefront() {
{/* Footer */}
-
+
)
}
From d27e7d63b2dfabfbf91ca95a03e8db5a0d68319c Mon Sep 17 00:00:00 2001
From: AJ Frio
Date: Wed, 1 Oct 2025 14:02:59 -0600
Subject: [PATCH 2/2] Improve storefront hydration caching and navigation
---
src/components/storefront/Navbar.jsx | 33 ++++++++--
src/lib/storefrontCache.js | 26 ++++++++
src/pages/storefront/Storefront.jsx | 92 ++++++++++++++++++++++------
3 files changed, 128 insertions(+), 23 deletions(-)
create mode 100644 src/lib/storefrontCache.js
diff --git a/src/components/storefront/Navbar.jsx b/src/components/storefront/Navbar.jsx
index d109c96..0a9d2c0 100644
--- a/src/components/storefront/Navbar.jsx
+++ b/src/components/storefront/Navbar.jsx
@@ -1,6 +1,6 @@
-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'
@@ -25,10 +25,32 @@ export function Navbar({
})
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(() => {
setCollections(initialCollections)
}, [initialCollections])
@@ -105,7 +127,7 @@ export function Navbar({
{/* Logo */}
-
+
{storeSettings.logoType === 'image' && storeSettings.logoImageUrl ? (
Home
@@ -243,7 +266,7 @@ export function Navbar({
{/* Mobile menu button */}
-