feat: add vip and public shop
This commit is contained in:
parent
ce2cfec9f3
commit
35f20dbb4e
@ -4,7 +4,7 @@ import { useState, useEffect } from 'react'
|
||||
import { StarIcon } from '@heroicons/react/20/solid'
|
||||
import { HeartIcon, ShoppingCartIcon } from '@heroicons/react/24/outline'
|
||||
import { HeartIcon as HeartIconSolid } from '@heroicons/react/24/solid'
|
||||
import PageLayout from '../components/PageLayout'
|
||||
import PageLayout from '../../components/PageLayout'
|
||||
|
||||
// Mock-Produktdaten im Tailwind UI Plus Format
|
||||
const products = [
|
||||
415
src/app/shop/vip/page.tsx
Normal file
415
src/app/shop/vip/page.tsx
Normal file
@ -0,0 +1,415 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { StarIcon } from '@heroicons/react/20/solid'
|
||||
import { HeartIcon, ShoppingCartIcon } from '@heroicons/react/24/outline'
|
||||
import { HeartIcon as HeartIconSolid } from '@heroicons/react/24/solid'
|
||||
import PageLayout from '../../components/PageLayout'
|
||||
|
||||
// Featured Products für die Shop-Startseite
|
||||
const featuredProducts = [
|
||||
{
|
||||
id: 101,
|
||||
name: 'Leather Long Wallet',
|
||||
color: 'Natural',
|
||||
price: '$75',
|
||||
href: '#',
|
||||
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/home-page-04-trending-product-02.jpg',
|
||||
imageAlt: 'Hand stitched, orange leather long wallet.',
|
||||
},
|
||||
{
|
||||
id: 102,
|
||||
name: 'Machined Pencil and Pen Set',
|
||||
color: 'Black',
|
||||
price: '$70',
|
||||
href: '#',
|
||||
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/home-page-04-trending-product-03.jpg',
|
||||
imageAlt: '12-sided, machined black pencil and pen.',
|
||||
},
|
||||
{
|
||||
id: 103,
|
||||
name: 'Mini-Sketchbooks',
|
||||
color: 'Light Brown',
|
||||
price: '$27',
|
||||
href: '#',
|
||||
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/home-page-04-trending-product-04.jpg',
|
||||
imageAlt: 'Set of three light and dark brown mini sketch books.',
|
||||
},
|
||||
{
|
||||
id: 104,
|
||||
name: 'Organizer Set',
|
||||
color: 'Walnut',
|
||||
price: '$149',
|
||||
href: '#',
|
||||
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/home-page-04-trending-product-01.jpg',
|
||||
imageAlt: 'Beautiful walnut organizer set with multiple white compartments',
|
||||
},
|
||||
]
|
||||
|
||||
// Mock-Produktdaten im Tailwind UI Plus Format
|
||||
const products = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Premium Bio-Kaffee Starter Set',
|
||||
price: '€24.99',
|
||||
rating: 5,
|
||||
reviewCount: 142,
|
||||
imageSrc: 'https://images.unsplash.com/photo-1559056199-641a0ac8b55e?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
|
||||
imageAlt: 'Premium Bio-Kaffee Set mit Bohnen und Filter',
|
||||
href: '#',
|
||||
category: 'Getränke',
|
||||
inStock: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Nachhaltiger Laptop-Ständer',
|
||||
price: '€89.99',
|
||||
rating: 5,
|
||||
reviewCount: 87,
|
||||
imageSrc: 'https://images.unsplash.com/photo-1527864550417-7fd91fc51a46?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
|
||||
imageAlt: 'Ergonomischer Laptop-Ständer aus Bambus',
|
||||
href: '#',
|
||||
category: 'Technik',
|
||||
inStock: true,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Öko-Sportbekleidung Set',
|
||||
price: '€149.99',
|
||||
rating: 5,
|
||||
reviewCount: 203,
|
||||
imageSrc: 'https://images.unsplash.com/photo-1506629905607-b5f9a71351e8?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
|
||||
imageAlt: 'Nachhaltige Sportkleidung aus recycelten Materialien',
|
||||
href: '#',
|
||||
category: 'Kleidung',
|
||||
inStock: false,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Smart Home Energie-Monitor',
|
||||
price: '€199.99',
|
||||
rating: 4,
|
||||
reviewCount: 156,
|
||||
imageSrc: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
|
||||
imageAlt: 'Smart Home Gerät zur Energieüberwachung',
|
||||
href: '#',
|
||||
category: 'Technik',
|
||||
inStock: true,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Bio-Hautpflege Starter-Set',
|
||||
price: '€79.99',
|
||||
rating: 4,
|
||||
reviewCount: 92,
|
||||
imageSrc: 'https://images.unsplash.com/photo-1556228578-8c89e6adf883?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
|
||||
imageAlt: 'Natürliche Hautpflege Produkte ohne Chemikalien',
|
||||
href: '#',
|
||||
category: 'Beauty',
|
||||
inStock: true,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Solarbetriebene Powerbank',
|
||||
price: '€129.99',
|
||||
rating: 5,
|
||||
reviewCount: 78,
|
||||
imageSrc: 'https://images.unsplash.com/photo-1609091839311-d5365f9ff1c5?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
|
||||
imageAlt: 'Portable Solarenergie Powerbank',
|
||||
href: '#',
|
||||
category: 'Technik',
|
||||
inStock: true,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'Nachhaltige Trinkflasche',
|
||||
price: '€25.99',
|
||||
rating: 4,
|
||||
reviewCount: 64,
|
||||
imageSrc: 'https://images.unsplash.com/photo-1523362628745-0c100150b504?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
|
||||
imageAlt: 'Wiederverwendbare Edelstahl Trinkflasche',
|
||||
href: '#',
|
||||
category: 'Lifestyle',
|
||||
inStock: true,
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'Öko-Notizbuch Set',
|
||||
price: '€19.99',
|
||||
rating: 5,
|
||||
reviewCount: 41,
|
||||
imageSrc: 'https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
|
||||
imageAlt: 'Recyceltes Papier Notizbuch Set',
|
||||
href: '#',
|
||||
category: 'Büro',
|
||||
inStock: true,
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: 'Bambus Handy-Halterung',
|
||||
price: '€32.99',
|
||||
rating: 4,
|
||||
reviewCount: 24,
|
||||
imageSrc: 'https://images.unsplash.com/photo-1572635196237-14b3f281503f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
|
||||
imageAlt: 'Nachhaltige Bambus Handy-Halterung',
|
||||
href: '#',
|
||||
category: 'Technik',
|
||||
inStock: true,
|
||||
},
|
||||
]
|
||||
|
||||
const stats = [
|
||||
{ name: 'Total Users', stat: '71,897' },
|
||||
{ name: 'Gold Members', stat: '68,161' },
|
||||
{ name: 'Saved through Gold Membership', stat: '25k €' },
|
||||
]
|
||||
|
||||
function classNames(...classes: (string | undefined | null | boolean)[]): string {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default function ShopPage() {
|
||||
const [favorites, setFavorites] = useState<number[]>([])
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
|
||||
// Load favorites from localStorage on component mount
|
||||
useEffect(() => {
|
||||
const savedFavorites = localStorage.getItem('shop-favorites')
|
||||
if (savedFavorites) {
|
||||
try {
|
||||
setFavorites(JSON.parse(savedFavorites))
|
||||
} catch (error) {
|
||||
console.error('Error parsing favorites from localStorage:', error)
|
||||
}
|
||||
}
|
||||
setIsLoading(false)
|
||||
}, [])
|
||||
|
||||
// Save favorites to localStorage whenever favorites change
|
||||
useEffect(() => {
|
||||
if (!isLoading) {
|
||||
localStorage.setItem('shop-favorites', JSON.stringify(favorites))
|
||||
}
|
||||
}, [favorites, isLoading])
|
||||
|
||||
const toggleFavorite = (productId: number) => {
|
||||
setFavorites(prev => {
|
||||
const newFavorites = prev.includes(productId)
|
||||
? prev.filter(id => id !== productId)
|
||||
: [...prev, productId]
|
||||
|
||||
// Show feedback to user
|
||||
const product = products.find(p => p.id === productId)
|
||||
if (product) {
|
||||
if (newFavorites.includes(productId)) {
|
||||
console.log(`❤️ ${product.name} zu Favoriten hinzugefügt`)
|
||||
} else {
|
||||
console.log(`💔 ${product.name} aus Favoriten entfernt`)
|
||||
}
|
||||
}
|
||||
|
||||
return newFavorites
|
||||
})
|
||||
}
|
||||
|
||||
const addToCart = (productId: number) => {
|
||||
const product = products.find(p => p.id === productId)
|
||||
if (product) {
|
||||
console.log(`🛒 ${product.name} zum Warenkorb hinzugefügt`)
|
||||
// Hier würde die echte Add-to-Cart Logik implementiert werden
|
||||
// z.B. API-Call oder Zustand-Update
|
||||
}
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<PageLayout>
|
||||
<div className="min-h-screen flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[#8D6B1D] mx-auto mb-4"></div>
|
||||
<p className="text-[#4A4A4A]">Shop wird geladen...</p>
|
||||
</div>
|
||||
</div>
|
||||
</PageLayout>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<PageLayout>
|
||||
<div className="bg-white">
|
||||
{/* Header Section */}
|
||||
<div
|
||||
className="text-white py-16 relative"
|
||||
style={{
|
||||
backgroundImage: 'url(/images/misc/grey_BG.jpg)',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundRepeat: 'no-repeat'
|
||||
}}
|
||||
>
|
||||
{/* <div className="absolute inset-0 bg-black/40"></div> */}
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
||||
<div className="text-center">
|
||||
<div>
|
||||
<dl className="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
|
||||
{stats.map((item) => (
|
||||
<div
|
||||
key={item.name}
|
||||
className="overflow-hidden rounded-lg bg-[#0F172A]/90 px-4 py-5 shadow-sm inset-ring inset-ring-white/10 sm:p-6"
|
||||
>
|
||||
<dt className="truncate text-sm font-medium text-gray-400">{item.name}</dt>
|
||||
<dd className="mt-1 text-3xl font-semibold tracking-tight text-white">{item.stat}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Featured Products Section */}
|
||||
<div className="bg-white">
|
||||
<div className="mx-auto max-w-2xl px-4 py-16 sm:px-6 sm:py-24 lg:max-w-7xl lg:px-8">
|
||||
<div className="md:flex md:items-center md:justify-between">
|
||||
<h2 className="text-2xl font-bold tracking-tight text-gray-900">Trending products</h2>
|
||||
<a href="#" className="hidden text-sm font-medium text-indigo-600 hover:text-indigo-500 md:block">
|
||||
Shop the collection
|
||||
<span aria-hidden="true"> →</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-2 gap-x-4 gap-y-10 sm:gap-x-6 md:grid-cols-4 md:gap-y-0 lg:gap-x-8">
|
||||
{featuredProducts.map((product) => (
|
||||
<div key={product.id} className="group relative">
|
||||
<div className="h-56 w-full overflow-hidden rounded-md bg-gray-200 group-hover:opacity-75 lg:h-72 xl:h-80">
|
||||
<img alt={product.imageAlt} src={product.imageSrc} className="size-full object-cover" />
|
||||
</div>
|
||||
<h3 className="mt-4 text-sm text-gray-700">
|
||||
<a href={product.href}>
|
||||
<span className="absolute inset-0" />
|
||||
{product.name}
|
||||
</a>
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-gray-500">{product.color}</p>
|
||||
<p className="mt-1 text-sm font-medium text-gray-900">{product.price}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-8 text-sm md:hidden">
|
||||
<a href="#" className="font-medium text-indigo-600 hover:text-indigo-500">
|
||||
Shop the collection
|
||||
<span aria-hidden="true"> →</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Products Section - Tailwind UI Plus "Product Grid" */}
|
||||
<div className="mx-auto max-w-2xl px-4 py-16 sm:px-6 sm:py-24 lg:max-w-7xl lg:px-8">
|
||||
<h2 className="sr-only">Products</h2>
|
||||
|
||||
<div className="grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8">
|
||||
{products.map((product) => (
|
||||
<a key={product.id} href={product.href} className="group">
|
||||
<div className="relative">
|
||||
{/* Product Image */}
|
||||
<div className="aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-lg bg-gray-200 xl:aspect-h-8 xl:aspect-w-7">
|
||||
<img
|
||||
alt={product.imageAlt}
|
||||
src={product.imageSrc}
|
||||
className="h-full w-full object-cover object-center group-hover:opacity-75"
|
||||
/>
|
||||
|
||||
{/* Favorite Button - Now with better positioning */}
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
toggleFavorite(product.id)
|
||||
}}
|
||||
className={classNames(
|
||||
'absolute top-3 right-3 rounded-full bg-white p-2 text-gray-400 shadow-sm hover:bg-gray-50 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-[#8D6B1D]',
|
||||
'opacity-0 transition-opacity group-hover:opacity-100',
|
||||
favorites.includes(product.id) ? 'opacity-100 text-red-500 hover:text-red-600' : ''
|
||||
)}
|
||||
title={favorites.includes(product.id) ? 'Aus Favoriten entfernen' : 'Zu Favoriten hinzufügen'}
|
||||
>
|
||||
{favorites.includes(product.id) ? (
|
||||
<HeartIconSolid className="h-5 w-5" />
|
||||
) : (
|
||||
<HeartIcon className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Category Badge */}
|
||||
<div className="absolute top-3 left-3">
|
||||
<span className="inline-flex items-center rounded-full bg-[#8D6B1D] px-2 py-1 text-xs font-medium text-white">
|
||||
{product.category}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Out of Stock Overlay */}
|
||||
{!product.inStock && (
|
||||
<div className="absolute inset-0 flex items-center justify-center rounded-lg bg-black bg-opacity-50">
|
||||
<span className="rounded-md bg-white px-3 py-1 text-sm font-semibold text-gray-900">
|
||||
Ausverkauft
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Product Info */}
|
||||
<div className="mt-4 flex justify-between">
|
||||
<div>
|
||||
<h3 className="text-sm text-gray-700">
|
||||
{product.name}
|
||||
</h3>
|
||||
|
||||
{/* Rating */}
|
||||
<div className="mt-1 flex items-center">
|
||||
<div className="flex items-center">
|
||||
{[0, 1, 2, 3, 4].map((rating) => (
|
||||
<StarIcon
|
||||
key={rating}
|
||||
aria-hidden="true"
|
||||
className={classNames(
|
||||
product.rating > rating ? 'text-yellow-400' : 'text-gray-300',
|
||||
'h-4 w-4 flex-shrink-0'
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<p className="ml-1 text-sm text-gray-500">({product.reviewCount})</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-lg font-medium text-gray-900">{product.price}</p>
|
||||
</div>
|
||||
|
||||
{/* Add to Cart Button */}
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
addToCart(product.id)
|
||||
}}
|
||||
disabled={!product.inStock}
|
||||
className={classNames(
|
||||
'mt-4 flex w-full items-center justify-center rounded-md border border-transparent px-8 py-2 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:ring-offset-2',
|
||||
product.inStock
|
||||
? 'bg-[#8D6B1D] text-white hover:bg-[#7A5E1A]'
|
||||
: 'bg-gray-100 text-gray-400 cursor-not-allowed'
|
||||
)}
|
||||
>
|
||||
<ShoppingCartIcon className="mr-2 h-4 w-4" />
|
||||
{product.inStock ? 'In den Warenkorb' : 'Ausverkauft'}
|
||||
</button>
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageLayout>
|
||||
)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user