pvanand's picture
Update static/ui/index.html
2f09e0e verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Electronics Store</title>
<script src="https://unpkg.com/[email protected]/dist/vue.global.prod.js"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
}
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
.product-card {
transform: translateY(0);
transition: all 0.3s ease;
}
.product-card:hover {
transform: translateY(-5px);
}
.stock-badge {
transition: all 0.3s ease;
}
</style>
</head>
<body class="bg-gray-50">
<div id="app" class="min-h-screen">
<!-- Header -->
<header class="bg-white shadow-sm">
<div class="container mx-auto px-4 py-6">
<h1 class="text-4xl font-bold text-gray-900">Electronics Store</h1>
</div>
</header>
<main class="container mx-auto px-4 py-8">
<!-- Filters Section -->
<div class="bg-white rounded-xl shadow-sm p-6 mb-8">
<div class="flex flex-col md:flex-row gap-4 items-center justify-between">
<div class="flex gap-4 w-full md:w-auto">
<select v-model="selectedCategory"
class="w-full md:w-48 p-2.5 border border-gray-200 rounded-lg bg-gray-50 focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="">All Categories</option>
<option v-for="category in categories" :key="category" :value="category">
{{ category }}
</option>
</select>
<select v-model="sortBy"
class="w-full md:w-48 p-2.5 border border-gray-200 rounded-lg bg-gray-50 focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="price-asc">Price: Low to High</option>
<option value="price-desc">Price: High to Low</option>
<option value="rating">Top Rated</option>
</select>
</div>
<div class="text-sm text-gray-500">
{{ filteredProducts.length }} products found
</div>
</div>
</div>
<!-- Products Grid -->
<transition-group name="fade" tag="div"
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
<div v-for="product in filteredProducts"
:key="product.id"
class="product-card bg-white rounded-xl shadow-sm overflow-hidden">
<div class="p-6">
<!-- Product Category Badge -->
<div class="inline-block px-3 py-1 rounded-full text-xs font-medium bg-blue-50 text-blue-600 mb-4">
{{ product.category }}
</div>
<!-- Product Name -->
<h2 class="text-xl font-semibold text-gray-900 mb-4">{{ product.name }}</h2>
<!-- Specifications -->
<div class="space-y-3 mb-6">
<div v-for="(value, key) in product.specs"
:key="key"
class="flex justify-between text-sm">
<span class="text-gray-500">{{ key }}</span>
<span class="text-gray-900 font-medium">{{ value }}</span>
</div>
</div>
<!-- Price and Rating -->
<div class="flex items-center justify-between pt-4 border-t border-gray-100">
<div class="text-2xl font-bold text-gray-900">
${{ product.price.toLocaleString() }}
</div>
<div class="flex items-center gap-1">
<span class="text-yellow-400 text-lg"></span>
<span class="font-medium">{{ product.rating }}</span>
</div>
</div>
<!-- Stock Status -->
<div class="mt-4">
<span class="stock-badge px-3 py-1 rounded-full text-sm font-medium"
:class="{
'bg-red-50 text-red-600': product.stock < 10,
'bg-green-50 text-green-600': product.stock >= 10
}">
{{ product.stock }} in stock
</span>
</div>
</div>
</div>
</transition-group>
<!-- Empty State -->
<div v-if="filteredProducts.length === 0"
class="text-center py-12">
<p class="text-gray-500">No products found matching your criteria</p>
</div>
</main>
</div>
<script>
const { createApp, ref, computed } = Vue
createApp({
setup() {
const products = ref([])
const selectedCategory = ref('')
const sortBy = ref('price-asc')
// Fetch products from API
fetch('/api/electronics/products')
.then(response => response.json())
.then(data => {
products.value = data.products || []
})
.catch(error => {
console.error('Error fetching products:', error)
})
const categories = computed(() => {
if (!products.value?.length) return []
return [...new Set(products.value.map(p => p.category))]
})
const filteredProducts = computed(() => {
if (!products.value?.length) return []
let filtered = [...products.value]
if (selectedCategory.value) {
filtered = filtered.filter(p => p.category === selectedCategory.value)
}
switch (sortBy.value) {
case 'price-asc':
filtered.sort((a, b) => a.price - b.price)
break
case 'price-desc':
filtered.sort((a, b) => b.price - a.price)
break
case 'rating':
filtered.sort((a, b) => b.rating - a.rating)
break
}
return filtered
})
return {
products,
categories,
selectedCategory,
sortBy,
filteredProducts
}
}
}).mount('#app')
</script>
</body>
</html>