| <!DOCTYPE html> |
| <html lang="fr"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Boutique en ligne</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| .fade-enter-active, .fade-leave-active { |
| transition: opacity 0.3s ease; |
| } |
| .fade-enter-from, .fade-leave-to { |
| opacity: 0; |
| } |
| .card-hover { |
| transition: transform 0.2s ease, box-shadow 0.2s ease; |
| } |
| .card-hover:hover { |
| transform: translateY(-2px); |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); |
| } |
| .badge-hover { |
| transition: background-color 0.2s ease; |
| } |
| .badge-hover:hover { |
| background-color: #3b82f6; |
| color: white; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50 min-h-screen"> |
| <div id="app" class="container mx-auto px-4 py-8"> |
| |
| <header class="flex justify-between items-center mb-8"> |
| <div> |
| <h1 class="text-3xl font-bold text-gray-800">Boutique en ligne</h1> |
| <p class="text-gray-600">Découvrez nos produits exceptionnels</p> |
| </div> |
| <div class="relative"> |
| <button @click="toggleCart" class="flex items-center space-x-2 bg-white px-4 py-2 rounded-lg shadow-sm border border-gray-200 hover:bg-gray-50 transition"> |
| <i class="fas fa-shopping-cart text-gray-700"></i> |
| <span class="font-medium">Panier</span> |
| <span v-if="cart.length > 0" class="absolute -top-2 -right-2 bg-blue-500 text-white text-xs font-bold rounded-full h-5 w-5 flex items-center justify-center"> |
| {{ cart.reduce((acc, item) => acc + item.quantity, 0) }} |
| </span> |
| </button> |
| </div> |
| </header> |
|
|
| |
| <transition name="fade"> |
| <div v-if="showCart" class="absolute right-4 mt-2 w-80 bg-white rounded-lg shadow-lg z-10 border border-gray-200 overflow-hidden"> |
| <div class="p-4 border-b border-gray-200"> |
| <div class="flex justify-between items-center"> |
| <h3 class="font-bold text-lg">Votre panier</h3> |
| <button @click="toggleCart" class="text-gray-500 hover:text-gray-700"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| </div> |
| <div class="max-h-96 overflow-y-auto"> |
| <div v-if="cart.length === 0" class="p-4 text-center text-gray-500"> |
| Votre panier est vide |
| </div> |
| <div v-for="item in cart" :key="item.id" class="p-4 border-b border-gray-200 last:border-b-0 flex justify-between items-center"> |
| <div class="flex items-center space-x-3"> |
| <div class="w-12 h-12 bg-gray-100 rounded-md flex items-center justify-center"> |
| <i :class="item.icon" class="text-gray-500"></i> |
| </div> |
| <div> |
| <h4 class="font-medium">{{ item.name }}</h4> |
| <p class="text-sm text-gray-500">{{ item.price }} € x {{ item.quantity }}</p> |
| </div> |
| </div> |
| <div class="flex items-center space-x-2"> |
| <button @click="decreaseQuantity(item.id)" class="w-6 h-6 flex items-center justify-center bg-gray-100 rounded hover:bg-gray-200"> |
| <i class="fas fa-minus text-xs"></i> |
| </button> |
| <button @click="increaseQuantity(item.id)" class="w-6 h-6 flex items-center justify-center bg-gray-100 rounded hover:bg-gray-200"> |
| <i class="fas fa-plus text-xs"></i> |
| </button> |
| <button @click="removeFromCart(item.id)" class="w-6 h-6 flex items-center justify-center bg-red-100 rounded hover:bg-red-200 text-red-500"> |
| <i class="fas fa-trash text-xs"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| <div v-if="cart.length > 0" class="p-4 bg-gray-50"> |
| <div class="flex justify-between mb-2"> |
| <span class="font-medium">Total</span> |
| <span class="font-bold">{{ totalPrice }} €</span> |
| </div> |
| <button class="w-full bg-blue-500 hover:bg-blue-600 text-white py-2 rounded-lg font-medium transition"> |
| Passer la commande |
| </button> |
| </div> |
| </div> |
| </transition> |
|
|
| |
| <div class="mb-8"> |
| <h2 class="text-lg font-medium text-gray-700 mb-3">Filtrer par catégorie</h2> |
| <div class="flex flex-wrap gap-2"> |
| <button |
| v-for="category in categories" |
| :key="category.id" |
| @click="setActiveCategory(category.id)" |
| :class="[ |
| 'px-4 py-2 rounded-full text-sm font-medium border border-gray-200 badge-hover', |
| activeCategory === category.id ? 'bg-blue-500 text-white border-blue-500' : 'bg-white text-gray-700' |
| ]" |
| > |
| {{ category.name }} |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> |
| <div |
| v-for="product in filteredProducts" |
| :key="product.id" |
| class="bg-white rounded-xl overflow-hidden shadow-sm border border-gray-200 card-hover" |
| > |
| <div class="h-48 bg-gray-100 flex items-center justify-center"> |
| <i :class="product.icon" class="text-5xl text-gray-400"></i> |
| </div> |
| <div class="p-4"> |
| <div class="flex justify-between items-start mb-2"> |
| <h3 class="font-bold text-gray-800">{{ product.name }}</h3> |
| <span class="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">{{ product.category }}</span> |
| </div> |
| <p class="text-gray-600 text-sm mb-3">{{ product.description }}</p> |
| <div class="flex justify-between items-center"> |
| <span class="font-bold text-gray-900">{{ product.price }} €</span> |
| <button |
| @click="addToCart(product)" |
| class="px-3 py-1 bg-blue-500 hover:bg-blue-600 text-white rounded-lg text-sm font-medium transition flex items-center space-x-1" |
| > |
| <i class="fas fa-cart-plus text-xs"></i> |
| <span>Ajouter</span> |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div v-if="filteredProducts.length === 0" class="text-center py-12"> |
| <i class="fas fa-box-open text-5xl text-gray-300 mb-4"></i> |
| <h3 class="text-xl font-medium text-gray-700 mb-2">Aucun produit trouvé</h3> |
| <p class="text-gray-500">Essayez de changer vos filtres de recherche</p> |
| </div> |
| </div> |
|
|
| <script> |
| const { createApp, ref, computed } = Vue; |
| |
| createApp({ |
| setup() { |
| const showCart = ref(false); |
| const activeCategory = ref('all'); |
| |
| const categories = ref([ |
| { id: 'all', name: 'Tous les produits' }, |
| { id: 'electronics', name: 'Électronique' }, |
| { id: 'clothing', name: 'Vêtements' }, |
| { id: 'home', name: 'Maison' }, |
| { id: 'sports', name: 'Sports' }, |
| { id: 'books', name: 'Livres' } |
| ]); |
| |
| const products = ref([ |
| { |
| id: 1, |
| name: 'Smartphone Premium', |
| description: 'Écran 6.7", 128Go, triple caméra', |
| price: 799, |
| category: 'electronics', |
| icon: 'fas fa-mobile-alt' |
| }, |
| { |
| id: 2, |
| name: 'Casque sans fil', |
| description: 'Réduction de bruit active, 30h autonomie', |
| price: 199, |
| category: 'electronics', |
| icon: 'fas fa-headphones' |
| }, |
| { |
| id: 3, |
| name: 'T-shirt coton', |
| description: '100% coton bio, plusieurs coloris', |
| price: 29, |
| category: 'clothing', |
| icon: 'fas fa-tshirt' |
| }, |
| { |
| id: 4, |
| name: 'Lampadaire design', |
| description: 'Hauteur 1m80, métal et verre', |
| price: 149, |
| category: 'home', |
| icon: 'fas fa-lightbulb' |
| }, |
| { |
| id: 5, |
| name: 'Vélo de course', |
| description: 'Cadre carbone, 21 vitesses', |
| price: 1299, |
| category: 'sports', |
| icon: 'fas fa-bicycle' |
| }, |
| { |
| id: 6, |
| name: 'Roman best-seller', |
| description: 'Édition limitée, 400 pages', |
| price: 19, |
| category: 'books', |
| icon: 'fas fa-book' |
| }, |
| { |
| id: 7, |
| name: 'Montre connectée', |
| description: 'Suivi santé, écran AMOLED', |
| price: 249, |
| category: 'electronics', |
| icon: 'fas fa-clock' |
| }, |
| { |
| id: 8, |
| name: 'Coussin décoratif', |
| description: '45x45cm, plusieurs motifs', |
| price: 39, |
| category: 'home', |
| icon: 'fas fa-couch' |
| } |
| ]); |
| |
| const cart = ref([]); |
| |
| const filteredProducts = computed(() => { |
| if (activeCategory.value === 'all') { |
| return products.value; |
| } |
| return products.value.filter(product => product.category === activeCategory.value); |
| }); |
| |
| const totalPrice = computed(() => { |
| return cart.value.reduce((total, item) => total + (item.price * item.quantity), 0); |
| }); |
| |
| const toggleCart = () => { |
| showCart.value = !showCart.value; |
| }; |
| |
| const setActiveCategory = (categoryId) => { |
| activeCategory.value = categoryId; |
| }; |
| |
| const addToCart = (product) => { |
| const existingItem = cart.value.find(item => item.id === product.id); |
| |
| if (existingItem) { |
| existingItem.quantity += 1; |
| } else { |
| cart.value.push({ |
| ...product, |
| quantity: 1 |
| }); |
| } |
| |
| |
| const button = event.target.closest('button'); |
| if (button) { |
| button.classList.add('bg-green-500'); |
| setTimeout(() => { |
| button.classList.remove('bg-green-500'); |
| }, 300); |
| } |
| }; |
| |
| const removeFromCart = (productId) => { |
| cart.value = cart.value.filter(item => item.id !== productId); |
| }; |
| |
| const increaseQuantity = (productId) => { |
| const item = cart.value.find(item => item.id === productId); |
| if (item) item.quantity += 1; |
| }; |
| |
| const decreaseQuantity = (productId) => { |
| const item = cart.value.find(item => item.id === productId); |
| if (item) { |
| if (item.quantity > 1) { |
| item.quantity -= 1; |
| } else { |
| removeFromCart(productId); |
| } |
| } |
| }; |
| |
| return { |
| showCart, |
| activeCategory, |
| categories, |
| products, |
| cart, |
| filteredProducts, |
| totalPrice, |
| toggleCart, |
| setActiveCategory, |
| addToCart, |
| removeFromCart, |
| increaseQuantity, |
| decreaseQuantity |
| }; |
| } |
| }).mount('#app'); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=RadMann/boutique" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |