2026-01-31 16:42:16 +00:00

275 lines
12 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import type { ReactElement } from 'react';
import Head from 'next/head';
import Link from 'next/link';
import axios from 'axios';
import BaseButton from '../components/BaseButton';
import { getPageTitle } from '../config';
import LayoutGuest from '../layouts/Guest';
import { mdiCart, mdiArrowRight, mdiStar } from '@mdi/js';
import BaseIcon from '../components/BaseIcon';
import { useAppDispatch, useAppSelector } from '../stores/hooks';
import { addToCart } from '../stores/shoppingCartSlice';
export default function Home() {
const [categories, setCategories] = useState([]);
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
const dispatch = useAppDispatch();
const cartItems = useAppSelector((state) => state.shoppingCart.items);
useEffect(() => {
const fetchData = async () => {
try {
const [catRes, prodRes] = await Promise.all([
axios.get('/categories'),
axios.get('/products?limit=8'),
]);
setCategories(catRes.data.rows || []);
setProducts(prodRes.data.rows || []);
} catch (error) {
console.error('Error fetching storefront data:', error);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
const handleQuickAdd = (product: any) => {
dispatch(addToCart({
id: Math.random().toString(36).substr(2, 9),
productId: product.id,
title: product.title,
price: product.price,
quantity: 1,
image: product.images?.[0]?.url
}));
};
const cartCount = cartItems.reduce((acc, item) => acc + item.quantity, 0);
const getAvgRating = (reviews: any[]) => {
if (!reviews || reviews.length === 0) return 0;
return reviews.reduce((acc, r) => acc + r.rating, 0) / reviews.length;
};
return (
<div className="bg-white min-h-screen">
<Head>
<title>{getPageTitle('Home')}</title>
</Head>
{/* Navigation */}
<nav className="border-b border-gray-100 py-4 px-6 flex justify-between items-center sticky top-0 bg-white z-50">
<div className="flex items-center space-x-8">
<Link href="/" className="text-2xl font-bold text-blue-600">
Storefront
</Link>
<div className="hidden md:flex space-x-6">
<Link href="/products" className="text-gray-600 hover:text-blue-600 font-medium">
Products
</Link>
<Link href="/categories" className="text-gray-600 hover:text-blue-600 font-medium">
Categories
</Link>
</div>
</div>
<div className="flex items-center space-x-4">
<Link href="/cart" className="relative p-2 text-gray-600 hover:text-blue-600 mr-2">
<BaseIcon path={mdiCart} size={24} />
{cartCount > 0 && (
<span className="absolute top-0 right-0 bg-blue-600 text-white text-xs font-bold rounded-full h-5 w-5 flex items-center justify-center">
{cartCount}
</span>
)}
</Link>
<Link href="/login" className="text-gray-600 hover:text-blue-600 font-medium">
Login
</Link>
<Link
href="/register"
className="bg-blue-600 text-white px-5 py-2 rounded-full font-medium hover:bg-blue-700 transition"
>
Sign Up
</Link>
</div>
</nav>
{/* Hero Section */}
<section className="relative bg-gray-50 py-20 px-6">
<div className="max-w-7xl mx-auto flex flex-col md:flex-row items-center">
<div className="md:w-1/2 space-y-6">
<h1 className="text-5xl md:text-6xl font-extrabold text-gray-900 leading-tight">
Upgrade Your Lifestyle with <span className="text-blue-600">Premium Goods</span>
</h1>
<p className="text-xl text-gray-600 max-w-lg">
Discover our curated collection of high-quality products designed for the modern world.
</p>
<div className="flex space-x-4">
<BaseButton
href="/products"
label="Shop Now"
color="info"
className="px-8 py-3 rounded-xl text-lg shadow-lg hover:shadow-xl transition-all"
/>
<BaseButton
href="/register"
label="Get Started"
outline
color="info"
className="px-8 py-3 rounded-xl text-lg"
/>
</div>
</div>
<div className="md:w-1/2 mt-12 md:mt-0 relative">
<div className="w-full h-96 bg-blue-100 rounded-3xl overflow-hidden shadow-2xl relative">
<img
src="https://images.pexels.com/photos/1350789/pexels-photo-1350789.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
alt="Hero"
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-t from-blue-900/40 to-transparent"></div>
</div>
</div>
</div>
</section>
{/* Categories Grid */}
<section className="py-20 px-6 max-w-7xl mx-auto">
<div className="flex justify-between items-end mb-10">
<div>
<h2 className="text-3xl font-bold text-gray-900">Shop by Category</h2>
<p className="text-gray-500 mt-2">Explore our wide range of products across different categories.</p>
</div>
<Link href="/categories" className="text-blue-600 font-semibold flex items-center hover:underline">
View All <BaseIcon path={mdiArrowRight} size={20} className="ml-1" />
</Link>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
{loading
? Array(4)
.fill(0)
.map((_, i) => (
<div key={i} className="h-48 bg-gray-100 rounded-2xl animate-pulse"></div>
))
: categories.slice(0, 4).map((cat) => (
<Link
key={cat.id}
href={`/products?category=${cat.id}`}
className="group relative h-48 rounded-2xl overflow-hidden shadow-md hover:shadow-xl transition-all"
>
<div className="absolute inset-0 bg-blue-600 opacity-10 group-hover:opacity-20 transition-opacity"></div>
<div className="absolute inset-0 flex flex-col items-center justify-center p-4">
<span className="text-xl font-bold text-gray-900 group-hover:text-blue-600 transition-colors">
{cat.name}
</span>
<span className="text-sm text-gray-500 mt-1 uppercase tracking-wider">Explore</span>
</div>
</Link>
))}
</div>
</section>
{/* Featured Products */}
<section className="py-20 px-6 bg-gray-50">
<div className="max-w-7xl mx-auto">
<div className="flex justify-between items-end mb-10">
<div>
<h2 className="text-3xl font-bold text-gray-900">Featured Products</h2>
<p className="text-gray-500 mt-2">Our handpicked selection for you this season.</p>
</div>
<Link href="/products" className="text-blue-600 font-semibold flex items-center hover:underline">
View All <BaseIcon path={mdiArrowRight} size={20} className="ml-1" />
</Link>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8">
{loading
? Array(4)
.fill(0)
.map((_, i) => (
<div key={i} className="bg-white rounded-2xl p-4 shadow-sm h-80 animate-pulse"></div>
))
: products.map((product) => {
const avg = getAvgRating(product.reviews);
return (
<div
key={product.id}
className="bg-white rounded-2xl overflow-hidden shadow-sm hover:shadow-xl transition-all group flex flex-col"
>
<div className="h-48 bg-gray-200 relative overflow-hidden">
<img
src={`https://images.pexels.com/photos/1350789/pexels-photo-1350789.jpeg?auto=compress&cs=tinysrgb&w=300`}
alt={product.title}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
/>
<button
onClick={() => handleQuickAdd(product)}
className="absolute top-4 right-4 bg-white/90 backdrop-blur-sm p-2 rounded-full shadow-md hover:bg-white hover:text-blue-600 transition-colors"
>
<BaseIcon path={mdiCart} size={20} />
</button>
</div>
<div className="p-5 flex-grow flex flex-col">
<div className="flex items-center justify-between mb-2">
<h3 className="text-lg font-bold text-gray-900 line-clamp-1">{product.title}</h3>
{avg > 0 && (
<div className="flex items-center text-yellow-400 bg-yellow-50 px-2 py-1 rounded-lg">
<BaseIcon path={mdiStar} size={14} className="mr-1" />
<span className="text-xs font-bold">{avg.toFixed(1)}</span>
</div>
)}
</div>
<p className="text-gray-500 text-sm mt-1 line-clamp-2 flex-grow">
{product.description || 'No description available.'}
</p>
<div className="mt-4 flex justify-between items-center">
<span className="text-xl font-extrabold text-blue-600">${product.price}</span>
<Link
href={`/products/${product.id}`}
className="text-sm font-semibold text-gray-700 hover:text-blue-600"
>
View Details
</Link>
</div>
</div>
</div>
);
})}
</div>
</div>
</section>
{/* Footer */}
<footer className="bg-white border-t border-gray-100 py-12 px-6">
<div className="max-w-7xl mx-auto flex flex-col md:flex-row justify-between items-center space-y-6 md:space-y-0">
<div className="flex flex-col items-center md:items-start">
<Link href="/" className="text-2xl font-bold text-blue-600">
Storefront
</Link>
<p className="text-gray-500 mt-2 text-sm text-center md:text-left">
© 2026 Storefront platform. All rights reserved.
</p>
</div>
<div className="flex space-x-8">
<Link href="/privacy-policy" className="text-gray-500 hover:text-blue-600 text-sm">
Privacy Policy
</Link>
<Link href="/terms-of-use" className="text-gray-500 hover:text-blue-600 text-sm">
Terms of Use
</Link>
<Link href="/dashboard" className="text-blue-600 hover:text-blue-700 text-sm font-bold">
Admin Interface
</Link>
</div>
</div>
</footer>
</div>
);
}
Home.getLayout = function getLayout(page: ReactElement) {
return <LayoutGuest>{page}</LayoutGuest>;
};