feat: Add product images support with WP Media Library integration
- Add WP Media Library integration for product and variation images - Support images array (URLs) conversion to attachment IDs - Add images array to API responses (Admin & Customer SPA) - Implement drag-and-drop sortable images in Admin product form - Add image gallery thumbnails in Customer SPA product page - Initialize WooCommerce session for guest cart operations - Fix product variations and attributes display in Customer SPA - Add variation image field in Admin SPA Changes: - includes/Api/ProductsController.php: Handle images array, add to responses - includes/Frontend/ShopController.php: Add images array for customer SPA - includes/Frontend/CartController.php: Initialize WC session for guests - admin-spa/src/lib/wp-media.ts: Add openWPMediaGallery function - admin-spa/src/routes/Products/partials/tabs/GeneralTab.tsx: WP Media + sortable images - admin-spa/src/routes/Products/partials/tabs/VariationsTab.tsx: Add variation image field - customer-spa/src/pages/Product/index.tsx: Add gallery thumbnails display
This commit is contained in:
261
customer-spa/src/layouts/BaseLayout.tsx
Normal file
261
customer-spa/src/layouts/BaseLayout.tsx
Normal file
@@ -0,0 +1,261 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useLayout } from '../contexts/ThemeContext';
|
||||
|
||||
interface BaseLayoutProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base Layout Component
|
||||
*
|
||||
* Renders the appropriate layout based on theme configuration
|
||||
*/
|
||||
export function BaseLayout({ children }: BaseLayoutProps) {
|
||||
const { layout } = useLayout();
|
||||
|
||||
// Dynamically import and render the appropriate layout
|
||||
switch (layout) {
|
||||
case 'classic':
|
||||
return <ClassicLayout>{children}</ClassicLayout>;
|
||||
case 'modern':
|
||||
return <ModernLayout>{children}</ModernLayout>;
|
||||
case 'boutique':
|
||||
return <BoutiqueLayout>{children}</BoutiqueLayout>;
|
||||
case 'launch':
|
||||
return <LaunchLayout>{children}</LaunchLayout>;
|
||||
default:
|
||||
return <ModernLayout>{children}</ModernLayout>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classic Layout - Traditional ecommerce
|
||||
*/
|
||||
function ClassicLayout({ children }: BaseLayoutProps) {
|
||||
return (
|
||||
<div className="classic-layout min-h-screen flex flex-col">
|
||||
<header className="classic-header bg-white border-b sticky top-0 z-50">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="flex items-center justify-between h-20">
|
||||
{/* Logo */}
|
||||
<div className="flex-shrink-0">
|
||||
<Link to="/shop" className="text-2xl font-bold" style={{ color: 'var(--color-primary)' }}>
|
||||
{(window as any).woonoowCustomer?.siteTitle || 'Store Title'}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="hidden md:flex items-center space-x-8">
|
||||
<Link to="/shop" className="hover:text-primary transition-colors">Shop</Link>
|
||||
<a href="/about" className="hover:text-primary transition-colors">About</a>
|
||||
<a href="/contact" className="hover:text-primary transition-colors">Contact</a>
|
||||
</nav>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex items-center space-x-4">
|
||||
<Link to="/my-account" className="hover:text-primary transition-colors">Account</Link>
|
||||
<Link to="/cart" className="hover:text-primary transition-colors">Cart (0)</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="classic-main flex-1">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
<footer className="classic-footer bg-gray-100 border-t mt-auto">
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
|
||||
<div>
|
||||
<h3 className="font-semibold mb-4">About</h3>
|
||||
<p className="text-sm text-gray-600">Your store description here.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold mb-4">Quick Links</h3>
|
||||
<ul className="space-y-2 text-sm">
|
||||
<li><a href="/shop" className="text-gray-600 hover:text-primary">Shop</a></li>
|
||||
<li><a href="/about" className="text-gray-600 hover:text-primary">About</a></li>
|
||||
<li><a href="/contact" className="text-gray-600 hover:text-primary">Contact</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold mb-4">Customer Service</h3>
|
||||
<ul className="space-y-2 text-sm">
|
||||
<li><a href="/shipping" className="text-gray-600 hover:text-primary">Shipping</a></li>
|
||||
<li><a href="/returns" className="text-gray-600 hover:text-primary">Returns</a></li>
|
||||
<li><a href="/faq" className="text-gray-600 hover:text-primary">FAQ</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold mb-4">Newsletter</h3>
|
||||
<p className="text-sm text-gray-600 mb-4">Subscribe to get updates</p>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Your email"
|
||||
className="w-full px-4 py-2 border rounded-md text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t mt-8 pt-8 text-center text-sm text-gray-600">
|
||||
© 2024 Your Store. All rights reserved.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modern Layout - Minimalist, clean
|
||||
*/
|
||||
function ModernLayout({ children }: BaseLayoutProps) {
|
||||
return (
|
||||
<div className="modern-layout min-h-screen flex flex-col">
|
||||
<header className="modern-header bg-white border-b sticky top-0 z-50">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="flex flex-col items-center py-6">
|
||||
{/* Logo - Centered */}
|
||||
<Link to="/shop" className="text-3xl font-bold mb-4" style={{ color: 'var(--color-primary)' }}>
|
||||
{(window as any).woonoowCustomer?.siteTitle || 'Store Title'}
|
||||
</Link>
|
||||
|
||||
{/* Navigation - Centered */}
|
||||
<nav className="flex items-center space-x-8">
|
||||
<Link to="/shop" className="hover:text-primary transition-colors">Shop</Link>
|
||||
<a href="/about" className="hover:text-primary transition-colors">About</a>
|
||||
<a href="/contact" className="hover:text-primary transition-colors">Contact</a>
|
||||
<Link to="/my-account" className="hover:text-primary transition-colors">Account</Link>
|
||||
<Link to="/cart" className="hover:text-primary transition-colors">Cart</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="modern-main flex-1">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
<footer className="modern-footer bg-white border-t mt-auto">
|
||||
<div className="container mx-auto px-4 py-12 text-center">
|
||||
<div className="mb-6">
|
||||
<a href="/" className="text-2xl font-bold" style={{ color: 'var(--color-primary)' }}>
|
||||
Store Logo
|
||||
</a>
|
||||
</div>
|
||||
<nav className="flex justify-center space-x-6 mb-6">
|
||||
<a href="/shop" className="text-sm text-gray-600 hover:text-primary">Shop</a>
|
||||
<a href="/about" className="text-sm text-gray-600 hover:text-primary">About</a>
|
||||
<a href="/contact" className="text-sm text-gray-600 hover:text-primary">Contact</a>
|
||||
</nav>
|
||||
<p className="text-sm text-gray-600">
|
||||
© 2024 Your Store. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Boutique Layout - Luxury, elegant
|
||||
*/
|
||||
function BoutiqueLayout({ children }: BaseLayoutProps) {
|
||||
return (
|
||||
<div className="boutique-layout min-h-screen flex flex-col font-serif">
|
||||
<header className="boutique-header bg-white border-b sticky top-0 z-50">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="flex items-center justify-between h-24">
|
||||
{/* Logo */}
|
||||
<div className="flex-1"></div>
|
||||
|
||||
<div className="flex-shrink-0">
|
||||
<Link to="/shop" className="text-3xl font-bold tracking-wide" style={{ color: 'var(--color-primary)' }}>
|
||||
{(window as any).woonoowCustomer?.siteTitle || 'BOUTIQUE'}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 flex justify-end">
|
||||
<nav className="hidden md:flex items-center space-x-8">
|
||||
<Link to="/shop" className="text-sm uppercase tracking-wider hover:text-primary transition-colors">Shop</Link>
|
||||
<Link to="/my-account" className="text-sm uppercase tracking-wider hover:text-primary transition-colors">Account</Link>
|
||||
<Link to="/cart" className="text-sm uppercase tracking-wider hover:text-primary transition-colors">Cart</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="boutique-main flex-1">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
<footer className="boutique-footer bg-gray-50 border-t mt-auto">
|
||||
<div className="container mx-auto px-4 py-16 text-center">
|
||||
<div className="mb-8">
|
||||
<a href="/" className="text-3xl font-bold tracking-wide" style={{ color: 'var(--color-primary)' }}>
|
||||
BOUTIQUE
|
||||
</a>
|
||||
</div>
|
||||
<nav className="flex justify-center space-x-8 mb-8">
|
||||
<a href="/shop" className="text-sm uppercase tracking-wider text-gray-600 hover:text-primary">Shop</a>
|
||||
<a href="/about" className="text-sm uppercase tracking-wider text-gray-600 hover:text-primary">About</a>
|
||||
<a href="/contact" className="text-sm uppercase tracking-wider text-gray-600 hover:text-primary">Contact</a>
|
||||
</nav>
|
||||
<p className="text-sm text-gray-600 tracking-wide">
|
||||
© 2024 BOUTIQUE. ALL RIGHTS RESERVED.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch Layout - Single product funnel
|
||||
* Note: Landing page is custom (user's page builder)
|
||||
* WooNooW only takes over from checkout onwards
|
||||
*/
|
||||
function LaunchLayout({ children }: BaseLayoutProps) {
|
||||
const isCheckoutFlow = window.location.pathname.includes('/checkout') ||
|
||||
window.location.pathname.includes('/my-account') ||
|
||||
window.location.pathname.includes('/order-received');
|
||||
|
||||
if (!isCheckoutFlow) {
|
||||
// For non-checkout pages, use minimal layout
|
||||
return (
|
||||
<div className="launch-layout min-h-screen flex flex-col">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// For checkout flow: minimal header, no footer
|
||||
return (
|
||||
<div className="launch-layout min-h-screen flex flex-col bg-gray-50">
|
||||
<header className="launch-header bg-white border-b">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="flex items-center justify-center h-16">
|
||||
<Link to="/shop" className="text-xl font-bold" style={{ color: 'var(--color-primary)' }}>
|
||||
{(window as any).woonoowCustomer?.siteTitle || 'Store Title'}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="launch-main flex-1 py-8">
|
||||
<div className="container mx-auto px-4 max-w-2xl">
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* Minimal footer for checkout */}
|
||||
<footer className="launch-footer bg-white border-t py-4">
|
||||
<div className="container mx-auto px-4 text-center text-sm text-gray-600">
|
||||
© 2024 Your Store. Secure Checkout.
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user