feat: implement server-side product search, category filtering, and infinite scroll pagination on the homepage
This commit is contained in:
parent
470fbd4b40
commit
b010e1a126
@ -9,10 +9,28 @@ class HomepageController extends Controller
|
|||||||
{
|
{
|
||||||
public function __invoke()
|
public function __invoke()
|
||||||
{
|
{
|
||||||
|
$search = request('search');
|
||||||
|
$categorySlug = request('category');
|
||||||
|
|
||||||
return inertia('homepage', [
|
return inertia('homepage', [
|
||||||
'categories' => Category::all(),
|
'categories' => Category::active()->get(),
|
||||||
'bestSellers' => Product::with(['categories', 'prices'])->active()->latest()->take(4)->get(),
|
'bestSellers' => Product::with(['categories' => fn ($q) => $q->active(), 'prices'])
|
||||||
'topSellingProducts' => Product::with(['categories', 'prices'])->withCount('orderItems')->active()->orderBy('order_items_count', 'desc')->take(3)->get(),
|
->when($search, function ($query, $search) {
|
||||||
|
$query->where(function ($q) use ($search) {
|
||||||
|
$q->where('name', 'like', "%{$search}%")
|
||||||
|
->orWhere('description', 'like', "%{$search}%");
|
||||||
|
});
|
||||||
|
})
|
||||||
|
->when($categorySlug, function ($query, $categorySlug) {
|
||||||
|
$query->whereHas('categories', function ($q) use ($categorySlug) {
|
||||||
|
$q->where('categories.slug', $categorySlug);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
->active()
|
||||||
|
->latest()
|
||||||
|
->paginate(12)
|
||||||
|
->withQueryString(),
|
||||||
|
'topSellingProducts' => Product::with(['categories' => fn ($q) => $q->active(), 'prices'])->withCount('orderItems')->active()->orderBy('order_items_count', 'desc')->take(3)->get(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,62 @@
|
|||||||
import { Link } from '@inertiajs/react';
|
|
||||||
import { ArrowRight, Facebook, Instagram, Twitter, X, Youtube } from 'lucide-react';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { formatCurrency } from '@/lib/formatters';
|
import { formatCurrency } from '@/lib/formatters';
|
||||||
|
import { Link, router } from '@inertiajs/react';
|
||||||
|
import { ArrowRight, Facebook, Instagram, Search, X } from 'lucide-react';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
export default function Homepage({ categories = [], bestSellers = [], topSellingProducts = [] }: { categories: any[], bestSellers: any[], topSellingProducts: any[] }) {
|
export default function Homepage({ categories = [], bestSellers, topSellingProducts = [] }: { categories: any[], bestSellers: any, topSellingProducts: any[] }) {
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
const [selectedProduct, setSelectedProduct] = useState<any>(null);
|
const [selectedProduct, setSelectedProduct] = useState<any>(null);
|
||||||
|
const [allProducts, setAllProducts] = useState<any[]>(bestSellers.data);
|
||||||
|
const [search, setSearch] = useState('');
|
||||||
|
const [selectedCategorySlug, setSelectedCategorySlug] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
router.get(window.location.pathname, { search, category: selectedCategorySlug }, {
|
||||||
|
preserveState: true,
|
||||||
|
preserveScroll: true,
|
||||||
|
only: ['bestSellers'],
|
||||||
|
onSuccess: (page: any) => {
|
||||||
|
setAllProducts(page.props.bestSellers.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [search]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
router.get(window.location.pathname, { search, category: selectedCategorySlug }, {
|
||||||
|
preserveState: true,
|
||||||
|
preserveScroll: true,
|
||||||
|
only: ['bestSellers'],
|
||||||
|
onSuccess: (page: any) => {
|
||||||
|
setAllProducts(page.props.bestSellers.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [selectedCategorySlug]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (bestSellers.current_page > 1) {
|
||||||
|
setAllProducts(prev => {
|
||||||
|
// Prevent duplicates if data already exists
|
||||||
|
const existingIds = new Set(prev.map(p => p.id));
|
||||||
|
const newProducts = bestSellers.data.filter((p: any) => !existingIds.has(p.id));
|
||||||
|
return [...prev, ...newProducts];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [bestSellers]);
|
||||||
|
|
||||||
|
const loadMore = () => {
|
||||||
|
if (bestSellers.next_page_url) {
|
||||||
|
router.get(bestSellers.next_page_url, {}, {
|
||||||
|
preserveState: true,
|
||||||
|
preserveScroll: true,
|
||||||
|
only: ['bestSellers'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
@ -39,7 +89,7 @@ export default function Homepage({ categories = [], bestSellers = [], topSelling
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const scrollToSection = (e: React.MouseEvent<HTMLAnchorElement>, id: string) => {
|
const scrollToSection = (e: React.MouseEvent<HTMLElement>, id: string) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const element = document.querySelector(id);
|
const element = document.querySelector(id);
|
||||||
if (element) {
|
if (element) {
|
||||||
@ -103,10 +153,21 @@ export default function Homepage({ categories = [], bestSellers = [], topSelling
|
|||||||
<div className="order-2 flex flex-col justify-center lg:order-1 lg:col-span-3">
|
<div className="order-2 flex flex-col justify-center lg:order-1 lg:col-span-3">
|
||||||
<div className="relative mb-8">
|
<div className="relative mb-8">
|
||||||
<div className="custom-scrollbar flex max-h-[300px] flex-col overflow-y-auto border-y border-[#1e4a6d]/20">
|
<div className="custom-scrollbar flex max-h-[300px] flex-col overflow-y-auto border-y border-[#1e4a6d]/20">
|
||||||
{categories.map((cat) => (
|
{categories.map((cat: any) => (
|
||||||
<button key={cat.id} className="group flex items-center justify-between border-b border-[#1e4a6d]/10 px-4 py-4 text-left text-xs font-bold uppercase tracking-wider transition-all hover:bg-[#1e4a6d] hover:text-white last:border-0">
|
<button
|
||||||
|
key={cat.id}
|
||||||
|
onClick={(e) => {
|
||||||
|
setSelectedCategorySlug(cat.slug);
|
||||||
|
scrollToSection(e, '#product');
|
||||||
|
}}
|
||||||
|
className={`group flex items-center justify-between border-b border-[#1e4a6d]/10 px-4 py-4 text-left text-xs font-bold uppercase tracking-wider transition-all hover:bg-[#1e4a6d] hover:text-white last:border-0 ${
|
||||||
|
selectedCategorySlug === cat.slug ? 'bg-[#1e4a6d] text-white' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{cat.name}
|
{cat.name}
|
||||||
<ArrowRight size={14} className="opacity-0 transition-all group-hover:translate-x-1 group-hover:opacity-100" />
|
<ArrowRight size={14} className={`transition-all group-hover:translate-x-1 group-hover:opacity-100 ${
|
||||||
|
selectedCategorySlug === cat.slug ? 'opacity-100 translate-x-1' : 'opacity-0'
|
||||||
|
}`} />
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
{categories.length === 0 && (
|
{categories.length === 0 && (
|
||||||
@ -124,9 +185,8 @@ export default function Homepage({ categories = [], bestSellers = [], topSelling
|
|||||||
<p className="mb-4 text-[10px] font-bold uppercase tracking-widest opacity-60">Ikuti Kami</p>
|
<p className="mb-4 text-[10px] font-bold uppercase tracking-widest opacity-60">Ikuti Kami</p>
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
<Facebook size={16} className="cursor-pointer hover:opacity-50" />
|
<Facebook size={16} className="cursor-pointer hover:opacity-50" />
|
||||||
<Twitter size={16} className="cursor-pointer hover:opacity-50" />
|
<X size={16} className="cursor-pointer hover:opacity-50" />
|
||||||
<Instagram size={16} className="cursor-pointer hover:opacity-50" />
|
<Instagram size={16} className="cursor-pointer hover:opacity-50" />
|
||||||
<Youtube size={16} className="cursor-pointer hover:opacity-50" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -134,7 +194,7 @@ export default function Homepage({ categories = [], bestSellers = [], topSelling
|
|||||||
{/* Hero Image */}
|
{/* Hero Image */}
|
||||||
<div className="order-1 lg:order-2 lg:col-span-4">
|
<div className="order-1 lg:order-2 lg:col-span-4">
|
||||||
<div className="relative aspect-[3/4] overflow-hidden bg-white shadow-xl">
|
<div className="relative aspect-[3/4] overflow-hidden bg-white shadow-xl">
|
||||||
<img src="https://images.unsplash.com/photo-1605763240000-7e93b172d754?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTV8fGRyZXNzfGVufDB8fDB8fHww" alt="Model" className="h-full w-full object-cover" />
|
<img src="https://images.unsplash.com/photo-1719947348984-b7af56dcdbfd?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDF8fHxlbnwwfHx8fHw%3D" alt="Model" className="h-full w-full object-cover" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -147,9 +207,9 @@ export default function Homepage({ categories = [], bestSellers = [], topSelling
|
|||||||
<p className="mb-8 text-sm leading-relaxed opacity-70">
|
<p className="mb-8 text-sm leading-relaxed opacity-70">
|
||||||
Tingkatkan gaya harian Anda dengan koleksi esensial yang dibuat dengan ketelitian dan kualitas tinggi. Mulai dari bahan berkelanjutan hingga desain yang tak lekang oleh waktu.
|
Tingkatkan gaya harian Anda dengan koleksi esensial yang dibuat dengan ketelitian dan kualitas tinggi. Mulai dari bahan berkelanjutan hingga desain yang tak lekang oleh waktu.
|
||||||
</p>
|
</p>
|
||||||
<button className="bg-[#1e4a6d] px-8 py-3 text-[10px] font-bold uppercase tracking-[0.2em] text-white transition-all hover:bg-[#15344e]">
|
<a href="#product" onClick={(e) => scrollToSection(e, '#product')} className="inline-block bg-[#1e4a6d] px-8 py-3 text-[10px] font-bold uppercase tracking-[0.2em] text-white transition-all hover:bg-[#15344e]">
|
||||||
Jelajahi Sekarang
|
Jelajahi Sekarang
|
||||||
</button>
|
</a>
|
||||||
|
|
||||||
<div className="mt-16 flex gap-4">
|
<div className="mt-16 flex gap-4">
|
||||||
{topSellingProducts.map((product, i) => (
|
{topSellingProducts.map((product, i) => (
|
||||||
@ -159,7 +219,7 @@ export default function Homepage({ categories = [], bestSellers = [], topSelling
|
|||||||
onClick={() => setSelectedProduct(product)}
|
onClick={() => setSelectedProduct(product)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={product.thumbnail_url || 'https://images.unsplash.com/photo-1594179056536-dc8714fc8fdf?w=200&auto=format&fit=crop&q=60'}
|
src={product.thumbnail_url || 'https://images.unsplash.com/photo-1719947348940-7aa339028cbc?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDgzfHx8ZW58MHx8fHx8'}
|
||||||
alt={product.name}
|
alt={product.name}
|
||||||
className="h-full w-full object-cover"
|
className="h-full w-full object-cover"
|
||||||
/>
|
/>
|
||||||
@ -167,7 +227,7 @@ export default function Homepage({ categories = [], bestSellers = [], topSelling
|
|||||||
))}
|
))}
|
||||||
{topSellingProducts.length === 0 && (
|
{topSellingProducts.length === 0 && (
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
{[1,2,3].map(i => (
|
{[1, 2, 3].map(i => (
|
||||||
<div key={i} className="h-24 w-20 rounded-t-full border border-dashed border-[#1e4a6d]/20 bg-transparent"></div>
|
<div key={i} className="h-24 w-20 rounded-t-full border border-dashed border-[#1e4a6d]/20 bg-transparent"></div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -182,11 +242,38 @@ export default function Homepage({ categories = [], bestSellers = [], topSelling
|
|||||||
{/* Best Sellers Section */}
|
{/* Best Sellers Section */}
|
||||||
<section id="product" className="border-b border-[#1e4a6d]/20">
|
<section id="product" className="border-b border-[#1e4a6d]/20">
|
||||||
<div className="mx-auto max-w-7xl">
|
<div className="mx-auto max-w-7xl">
|
||||||
<div className="flex items-center justify-between border-x border-[#1e4a6d]/20 px-6 py-6 lg:px-12">
|
<div className="flex flex-col gap-4 border-x border-[#1e4a6d]/20 px-6 py-6 sm:flex-row sm:items-center sm:justify-between lg:px-12">
|
||||||
<h2 className="text-xl font-medium">Produk Terlaris</h2>
|
<h2 className="text-xl font-medium">Produk Terlaris</h2>
|
||||||
<a href="#product" onClick={(e) => scrollToSection(e, '#product')} className="flex items-center gap-2 text-[10px] font-bold uppercase tracking-widest hover:opacity-70 transition-all">
|
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
|
||||||
Semua Produk <ArrowRight size={14} />
|
{/* Category Filter */}
|
||||||
</a>
|
<div className="relative">
|
||||||
|
<select
|
||||||
|
value={selectedCategorySlug}
|
||||||
|
onChange={(e) => setSelectedCategorySlug(e.target.value)}
|
||||||
|
className="appearance-none border border-[#1e4a6d]/20 bg-transparent py-2 pl-4 pr-10 text-[10px] font-bold uppercase tracking-widest outline-none transition-all focus:border-[#1e4a6d] cursor-pointer"
|
||||||
|
>
|
||||||
|
<option value="">Semua Kategori</option>
|
||||||
|
{categories.map((cat: any) => (
|
||||||
|
<option key={cat.id} value={cat.slug}>{cat.name}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<div className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2">
|
||||||
|
<div className="h-1 w-1 rotate-45 border-b border-r border-[#1e4a6d]"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Search Input */}
|
||||||
|
<div className="relative flex items-center">
|
||||||
|
<Search size={14} className="absolute left-3 opacity-40" />
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Cari produk..."
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
className="border border-[#1e4a6d]/20 bg-transparent py-2 pl-10 pr-4 text-[10px] font-bold uppercase tracking-widest outline-none transition-all focus:border-[#1e4a6d] w-full sm:w-[200px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -194,14 +281,14 @@ export default function Homepage({ categories = [], bestSellers = [], topSelling
|
|||||||
<div className="border-y border-[#1e4a6d]/20">
|
<div className="border-y border-[#1e4a6d]/20">
|
||||||
<div className="mx-auto max-w-7xl">
|
<div className="mx-auto max-w-7xl">
|
||||||
<div className="grid grid-cols-1 border-x border-[#1e4a6d]/20 sm:grid-cols-2 lg:grid-cols-4">
|
<div className="grid grid-cols-1 border-x border-[#1e4a6d]/20 sm:grid-cols-2 lg:grid-cols-4">
|
||||||
{bestSellers.map((item, idx) => (
|
{allProducts.map((item, idx) => (
|
||||||
<div key={item.id} className="group border-b border-[#1e4a6d]/20 p-8 last:border-b-0 sm:border-r sm:even:border-r-0 lg:border-b-0 lg:border-r lg:last:border-r-0">
|
<div key={item.id} className="group border-b border-[#1e4a6d]/20 p-8 last:border-b-0 sm:border-r sm:even:border-r-0 lg:border-b-0 lg:border-r lg:last:border-r-0">
|
||||||
<div
|
<div
|
||||||
className="mb-8 aspect-square overflow-hidden bg-white cursor-pointer"
|
className="mb-8 aspect-square overflow-hidden bg-white cursor-pointer"
|
||||||
onClick={() => setSelectedProduct(item)}
|
onClick={() => setSelectedProduct(item)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={item.thumbnail_url || 'https://images.unsplash.com/photo-1594179056536-dc8714fc8fdf?w=500&auto=format&fit=crop&q=60'}
|
src={item.thumbnail_url || 'https://images.unsplash.com/photo-1719947348940-7aa339028cbc?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDgzfHx8ZW58MHx8fHx8'}
|
||||||
alt={item.name}
|
alt={item.name}
|
||||||
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
|
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
|
||||||
/>
|
/>
|
||||||
@ -221,12 +308,37 @@ export default function Homepage({ categories = [], bestSellers = [], topSelling
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{bestSellers.length === 0 && (
|
{allProducts.length === 0 && (
|
||||||
<div className="col-span-full py-20 text-center text-xs uppercase tracking-widest opacity-40">
|
<div className="col-span-full border-x border-[#1e4a6d]/20 py-32 text-center">
|
||||||
Produk belum tersedia
|
<div className="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-[#1e4a6d]/5">
|
||||||
|
<Search size={24} className="opacity-20" />
|
||||||
|
</div>
|
||||||
|
<h3 className="mb-2 text-xl font-medium">Produk tidak ditemukan</h3>
|
||||||
|
<p className="mb-8 text-sm opacity-60">Coba gunakan kata kunci lain atau periksa ejaan Anda.</p>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setSearch('');
|
||||||
|
setSelectedCategorySlug('');
|
||||||
|
}}
|
||||||
|
className="border border-[#1e4a6d] px-8 py-3 text-[10px] font-bold uppercase tracking-widest transition-all hover:bg-[#1e4a6d] hover:text-white"
|
||||||
|
>
|
||||||
|
Bersihkan Filter
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{bestSellers.next_page_url && (
|
||||||
|
<div className="border-t border-[#1e4a6d]/20 py-12 text-center border-x">
|
||||||
|
<button
|
||||||
|
onClick={loadMore}
|
||||||
|
className="group relative inline-flex items-center gap-4 border border-[#1e4a6d] px-12 py-4 text-[10px] font-bold uppercase tracking-[0.2em] transition-all hover:bg-[#1e4a6d] hover:text-white"
|
||||||
|
>
|
||||||
|
Muat Lebih Banyak
|
||||||
|
<ArrowRight size={14} className="transition-transform group-hover:translate-x-1" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@ -297,7 +409,7 @@ export default function Homepage({ categories = [], bestSellers = [], topSelling
|
|||||||
</p>
|
</p>
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
<Facebook size={18} className="cursor-pointer hover:opacity-50" />
|
<Facebook size={18} className="cursor-pointer hover:opacity-50" />
|
||||||
<Twitter size={18} className="cursor-pointer hover:opacity-50" />
|
<X size={18} className="cursor-pointer hover:opacity-50" />
|
||||||
<Instagram size={18} className="cursor-pointer hover:opacity-50" />
|
<Instagram size={18} className="cursor-pointer hover:opacity-50" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -362,19 +474,17 @@ export default function Homepage({ categories = [], bestSellers = [], topSelling
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-20 flex gap-6">
|
<div className="mt-20 flex gap-6">
|
||||||
<Facebook size={24} className="hover:opacity-50 cursor-pointer" />
|
<Facebook size={24} className="hover:opacity-50 cursor-pointer" />
|
||||||
<Twitter size={24} className="hover:opacity-50 cursor-pointer" />
|
<X size={24} className="hover:opacity-50 cursor-pointer" />
|
||||||
<Instagram size={24} className="hover:opacity-50 cursor-pointer" />
|
<Instagram size={24} className="hover:opacity-50 cursor-pointer" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Product Detail Modal */}
|
{/* Product Detail Modal */}
|
||||||
<div className={`fixed inset-0 z-[70] flex items-center justify-center p-4 transition-all duration-500 ${
|
<div className={`fixed inset-0 z-[70] flex items-center justify-center p-4 transition-all duration-500 ${selectedProduct ? 'visible opacity-100' : 'invisible opacity-0'
|
||||||
selectedProduct ? 'visible opacity-100' : 'invisible opacity-0'
|
|
||||||
}`}>
|
}`}>
|
||||||
<div className="absolute inset-0 bg-[#1e4a6d]/40 backdrop-blur-sm" onClick={() => setSelectedProduct(null)}></div>
|
<div className="absolute inset-0 bg-[#1e4a6d]/40 backdrop-blur-sm" onClick={() => setSelectedProduct(null)}></div>
|
||||||
<div className={`relative w-full max-w-4xl overflow-hidden bg-[#f7f3eb] shadow-2xl transition-all duration-500 ${
|
<div className={`relative w-full max-w-4xl overflow-hidden bg-[#f7f3eb] shadow-2xl transition-all duration-500 ${selectedProduct ? 'translate-y-0 scale-100' : 'translate-y-10 scale-95'
|
||||||
selectedProduct ? 'translate-y-0 scale-100' : 'translate-y-10 scale-95'
|
|
||||||
}`}>
|
}`}>
|
||||||
<button className="absolute right-4 top-4 z-10 p-2 hover:rotate-90 transition-transform" onClick={() => setSelectedProduct(null)}>
|
<button className="absolute right-4 top-4 z-10 p-2 hover:rotate-90 transition-transform" onClick={() => setSelectedProduct(null)}>
|
||||||
<X size={24} />
|
<X size={24} />
|
||||||
@ -385,7 +495,7 @@ export default function Homepage({ categories = [], bestSellers = [], topSelling
|
|||||||
<div className="border-b border-[#1e4a6d]/10 md:border-b-0 md:border-r overflow-hidden bg-white">
|
<div className="border-b border-[#1e4a6d]/10 md:border-b-0 md:border-r overflow-hidden bg-white">
|
||||||
<div className="aspect-[4/5] overflow-hidden">
|
<div className="aspect-[4/5] overflow-hidden">
|
||||||
<img
|
<img
|
||||||
src={selectedProduct?.thumbnail_url || 'https://images.unsplash.com/photo-1594179056536-dc8714fc8fdf?w=800&auto=format&fit=crop&q=60'}
|
src={selectedProduct?.thumbnail_url || 'https://images.unsplash.com/photo-1719947348940-7aa339028cbc?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDgzfHx8ZW58MHx8fHx8'}
|
||||||
alt={selectedProduct?.name}
|
alt={selectedProduct?.name}
|
||||||
className="h-full w-full object-cover"
|
className="h-full w-full object-cover"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user