Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Modern E-Commerce Dashboard</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .product-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); | |
| } | |
| .pagination-btn.active { | |
| background-color: #3b82f6; | |
| color: white; | |
| } | |
| .admin-panel { | |
| transition: all 0.3s ease; | |
| } | |
| .fade-in { | |
| animation: fadeIn 0.3s ease-in; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; } | |
| to { opacity: 1; } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <!-- Header --> | |
| <header class="flex justify-between items-center mb-8"> | |
| <h1 class="text-3xl font-bold text-indigo-700">ShopSphere</h1> | |
| <div class="flex items-center space-x-4"> | |
| <div class="relative"> | |
| <input type="text" id="searchInput" placeholder="Search products..." | |
| class="pl-10 pr-4 py-2 rounded-full border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"> | |
| <i class="fas fa-search absolute left-3 top-3 text-gray-400"></i> | |
| </div> | |
| <button id="adminButton" class="bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition"> | |
| <i class="fas fa-cog mr-2"></i>Admin Panel | |
| </button> | |
| </div> | |
| </header> | |
| <!-- Admin Panel (Hidden by default) --> | |
| <div id="adminPanel" class="admin-panel hidden mb-8 bg-white rounded-lg shadow-lg p-6"> | |
| <h2 class="text-2xl font-bold mb-4 text-indigo-700">Product Management</h2> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-8"> | |
| <!-- Product Form --> | |
| <div class="bg-gray-50 p-6 rounded-lg"> | |
| <h3 class="text-lg font-semibold mb-4">Add/Edit Product</h3> | |
| <form id="productForm" class="space-y-4"> | |
| <input type="hidden" id="productId"> | |
| <div> | |
| <label for="productName" class="block text-sm font-medium text-gray-700">Product Name</label> | |
| <input type="text" id="productName" required | |
| class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"> | |
| </div> | |
| <div> | |
| <label for="productDescription" class="block text-sm font-medium text-gray-700">Description</label> | |
| <textarea id="productDescription" rows="3" required | |
| class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"></textarea> | |
| </div> | |
| <div> | |
| <label for="productPrice" class="block text-sm font-medium text-gray-700">Price ($)</label> | |
| <input type="number" id="productPrice" step="0.01" min="0" required | |
| class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"> | |
| </div> | |
| <div> | |
| <label for="productImage" class="block text-sm font-medium text-gray-700">Image URL</label> | |
| <input type="url" id="productImage" required | |
| class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"> | |
| </div> | |
| <div class="flex space-x-4"> | |
| <button type="submit" class="bg-indigo-600 text-white px-4 py-2 rounded-md hover:bg-indigo-700"> | |
| Save Product | |
| </button> | |
| <button type="reset" class="bg-gray-200 text-gray-800 px-4 py-2 rounded-md hover:bg-gray-300"> | |
| Clear Form | |
| </button> | |
| </div> | |
| </form> | |
| <div class="mt-6"> | |
| <h3 class="text-lg font-semibold mb-2">Bulk Import</h3> | |
| <button id="bulkImportButton" class="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700"> | |
| <i class="fas fa-file-import mr-2"></i>Import CSV | |
| </button> | |
| <input type="file" id="csvFile" accept=".csv" class="hidden"> | |
| <p class="text-xs text-gray-500 mt-2">CSV format: Name,Description,Price,Image,Category</p> | |
| </div> | |
| </div> | |
| <!-- Product List for Admin --> | |
| <div> | |
| <h3 class="text-lg font-semibold mb-4">Current Products</h3> | |
| <div id="adminProductList" class="space-y-4 max-h-96 overflow-y-auto p-2"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Main Content --> | |
| <main class="fade-in"> | |
| <!-- Product Grid --> | |
| <div id="productList" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 mb-8"></div> | |
| <!-- Pagination --> | |
| <div id="pagination" class="flex justify-center space-x-2 mt-8"></div> | |
| </main> | |
| </div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.0/papaparse.min.js"></script> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Sample products data (would normally come from products.json) | |
| let products = [ | |
| { | |
| id: 1, | |
| name: "Wireless Headphones", | |
| description: "Premium noise-cancelling wireless headphones with 30-hour battery life.", | |
| price: 199.99, | |
| image: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80", | |
| category: "Electronics" | |
| }, | |
| { | |
| id: 2, | |
| name: "Smart Watch", | |
| description: "Fitness tracker with heart rate monitor and smartphone notifications.", | |
| price: 159.99, | |
| image: "https://images.unsplash.com/photo-1523275335684-37898b6baf30?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80", | |
| category: "Electronics" | |
| }, | |
| { | |
| id: 3, | |
| name: "Leather Wallet", | |
| description: "Genuine leather wallet with multiple card slots and RFID protection.", | |
| price: 49.99, | |
| image: "https://images.unsplash.com/photo-1611591437281-460914d15503?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80", | |
| category: "Accessories" | |
| }, | |
| { | |
| id: 4, | |
| name: "Bluetooth Speaker", | |
| description: "Portable waterproof speaker with 20W output and 15-hour playtime.", | |
| price: 89.99, | |
| image: "https://images.unsplash.com/photo-1572569511254-d8f925fe2cbb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80", | |
| category: "Electronics" | |
| }, | |
| { | |
| id: 5, | |
| name: "Running Shoes", | |
| description: "Lightweight running shoes with responsive cushioning and breathable mesh.", | |
| price: 129.99, | |
| image: "https://images.unsplash.com/photo-1460353581641-37baddab0fa2?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80", | |
| category: "Footwear" | |
| }, | |
| { | |
| id: 6, | |
| name: "Coffee Maker", | |
| description: "Programmable coffee maker with thermal carafe and built-in grinder.", | |
| price: 149.99, | |
| image: "https://images.unsplash.com/photo-1556911463-12d4dfcb1f68?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80", | |
| category: "Home" | |
| }, | |
| { | |
| id: 7, | |
| name: "Backpack", | |
| description: "Durable backpack with laptop compartment and USB charging port.", | |
| price: 79.99, | |
| image: "https://images.unsplash.com/photo-1553062407-98eeb64c6a62?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80", | |
| category: "Accessories" | |
| }, | |
| { | |
| id: 8, | |
| name: "Wireless Charger", | |
| description: "Fast wireless charging pad compatible with Qi-enabled devices.", | |
| price: 29.99, | |
| image: "https://images.unsplash.com/photo-1583394838336-acd977736f90?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80", | |
| category: "Electronics" | |
| } | |
| ]; | |
| let currentPage = 1; | |
| const productsPerPage = 8; | |
| // Initialize the page | |
| renderProductList(); | |
| renderPagination(); | |
| // Render product list | |
| function renderProductList() { | |
| const productList = document.getElementById('productList'); | |
| productList.innerHTML = ''; | |
| const startIndex = (currentPage - 1) * productsPerPage; | |
| const endIndex = startIndex + productsPerPage; | |
| const paginatedProducts = products.slice(startIndex, endIndex); | |
| paginatedProducts.forEach(product => { | |
| const productItem = document.createElement('div'); | |
| productItem.className = 'product-card bg-white rounded-lg shadow-md overflow-hidden transition duration-300'; | |
| productItem.innerHTML = ` | |
| <div class="h-48 overflow-hidden"> | |
| <img src="${product.image}" alt="${product.name}" class="w-full h-full object-cover"> | |
| </div> | |
| <div class="p-4"> | |
| <div class="flex justify-between items-start mb-2"> | |
| <h3 class="text-lg font-semibold text-gray-800">${product.name}</h3> | |
| <span class="bg-indigo-100 text-indigo-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="text-lg font-bold text-indigo-700">$${product.price.toFixed(2)}</span> | |
| <button class="bg-indigo-600 text-white px-3 py-1 rounded-md hover:bg-indigo-700 transition"> | |
| <i class="fas fa-shopping-cart mr-1"></i> Add to Cart | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| productList.appendChild(productItem); | |
| }); | |
| } | |
| // Render pagination | |
| function renderPagination() { | |
| const pagination = document.getElementById('pagination'); | |
| pagination.innerHTML = ''; | |
| const totalPages = Math.ceil(products.length / productsPerPage); | |
| // Previous button | |
| if (currentPage > 1) { | |
| const prevButton = document.createElement('button'); | |
| prevButton.innerHTML = '<i class="fas fa-chevron-left"></i>'; | |
| prevButton.className = 'px-3 py-1 rounded-md border border-gray-300 hover:bg-gray-100'; | |
| prevButton.addEventListener('click', () => { | |
| currentPage--; | |
| renderProductList(); | |
| renderPagination(); | |
| }); | |
| pagination.appendChild(prevButton); | |
| } | |
| // Page buttons | |
| for (let i = 1; i <= totalPages; i++) { | |
| const pageButton = document.createElement('button'); | |
| pageButton.textContent = i; | |
| pageButton.className = `px-3 py-1 rounded-md border border-gray-300 hover:bg-gray-100 pagination-btn ${i === currentPage ? 'active' : ''}`; | |
| pageButton.addEventListener('click', () => { | |
| currentPage = i; | |
| renderProductList(); | |
| renderPagination(); | |
| }); | |
| pagination.appendChild(pageButton); | |
| } | |
| // Next button | |
| if (currentPage < totalPages) { | |
| const nextButton = document.createElement('button'); | |
| nextButton.innerHTML = '<i class="fas fa-chevron-right"></i>'; | |
| nextButton.className = 'px-3 py-1 rounded-md border border-gray-300 hover:bg-gray-100'; | |
| nextButton.addEventListener('click', () => { | |
| currentPage++; | |
| renderProductList(); | |
| renderPagination(); | |
| }); | |
| pagination.appendChild(nextButton); | |
| } | |
| } | |
| // Search functionality | |
| document.getElementById('searchInput').addEventListener('input', (event) => { | |
| const query = event.target.value.toLowerCase(); | |
| const filteredProducts = products.filter(product => | |
| product.name.toLowerCase().includes(query) || | |
| product.category.toLowerCase().includes(query) || | |
| product.description.toLowerCase().includes(query) | |
| ); | |
| products = filteredProducts; | |
| currentPage = 1; | |
| renderProductList(); | |
| renderPagination(); | |
| // Reset to all products if search is empty | |
| if (query === '') { | |
| // In a real app, we would reload from the original JSON | |
| // For this demo, we'll just use our sample data | |
| products = [ | |
| { | |
| id: 1, | |
| name: "Wireless Headphones", | |
| description: "Premium noise-cancelling wireless headphones with 30-hour battery life.", | |
| price: 199.99, | |
| image: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80", | |
| category: "Electronics" | |
| }, | |
| // ... (rest of the sample products) | |
| ]; | |
| renderProductList(); | |
| renderPagination(); | |
| } | |
| }); | |
| // Admin panel toggle | |
| document.getElementById('adminButton').addEventListener('click', () => { | |
| const adminPanel = document.getElementById('adminPanel'); | |
| adminPanel.style.display = adminPanel.style.display === 'none' ? 'block' : 'none'; | |
| renderAdminProductList(); | |
| }); | |
| // Save product form | |
| document.getElementById('productForm').addEventListener('submit', (event) => { | |
| event.preventDefault(); | |
| const productId = document.getElementById('productId').value; | |
| const productName = document.getElementById('productName').value; | |
| const productDescription = document.getElementById('productDescription').value; | |
| const productPrice = parseFloat(document.getElementById('productPrice').value); | |
| const productImage = document.getElementById('productImage').value; | |
| if (!productName || !productDescription || isNaN(productPrice) || !productImage) { | |
| alert('Please fill in all fields correctly.'); | |
| return; | |
| } | |
| if (productId) { | |
| // Edit existing product | |
| const productIndex = products.findIndex(p => p.id === parseInt(productId)); | |
| if (productIndex !== -1) { | |
| products[productIndex] = { | |
| id: parseInt(productId), | |
| name: productName, | |
| description: productDescription, | |
| price: productPrice, | |
| image: productImage, | |
| category: "General" | |
| }; | |
| } | |
| } else { | |
| // Add new product | |
| const newProduct = { | |
| id: products.length ? products[products.length - 1].id + 1 : 1, | |
| name: productName, | |
| description: productDescription, | |
| price: productPrice, | |
| image: productImage, | |
| category: "General" | |
| }; | |
| products.push(newProduct); | |
| } | |
| saveProductsToFile(); | |
| renderProductList(); | |
| renderPagination(); | |
| renderAdminProductList(); | |
| document.getElementById('productForm').reset(); | |
| document.getElementById('productId').value = ''; | |
| }); | |
| // Render admin product list | |
| function renderAdminProductList() { | |
| const adminProductList = document.getElementById('adminProductList'); | |
| adminProductList.innerHTML = ''; | |
| products.forEach(product => { | |
| const productItem = document.createElement('div'); | |
| productItem.className = 'bg-gray-50 p-4 rounded-lg flex items-start space-x-4'; | |
| productItem.innerHTML = ` | |
| <img src="${product.image}" alt="${product.name}" class="w-16 h-16 object-cover rounded"> | |
| <div class="flex-1"> | |
| <h3 class="font-semibold">${product.name}</h3> | |
| <p class="text-sm text-gray-600 mb-1">${product.description.substring(0, 50)}...</p> | |
| <div class="flex justify-between items-center"> | |
| <span class="font-bold text-indigo-700">$${product.price.toFixed(2)}</span> | |
| <div class="space-x-2"> | |
| <button onclick="editProduct(${product.id})" class="text-blue-600 hover:text-blue-800"> | |
| <i class="fas fa-edit"></i> Edit | |
| </button> | |
| <button onclick="deleteProduct(${product.id})" class="text-red-600 hover:text-red-800"> | |
| <i class="fas fa-trash-alt"></i> Delete | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| adminProductList.appendChild(productItem); | |
| }); | |
| } | |
| // Edit product | |
| window.editProduct = function(id) { | |
| const product = products.find(p => p.id === id); | |
| if (product) { | |
| document.getElementById('productId').value = product.id; | |
| document.getElementById('productName').value = product.name; | |
| document.getElementById('productDescription').value = product.description; | |
| document.getElementById('productPrice').value = product.price; | |
| document.getElementById('productImage').value = product.image; | |
| // Scroll to form | |
| document.getElementById('productForm').scrollIntoView({ behavior: 'smooth' }); | |
| } | |
| }; | |
| // Delete product | |
| window.deleteProduct = function(id) { | |
| if (confirm('Are you sure you want to delete this product?')) { | |
| products = products.filter(p => p.id !== id); | |
| saveProductsToFile(); | |
| renderProductList(); | |
| renderPagination(); | |
| renderAdminProductList(); | |
| } | |
| }; | |
| // Bulk import CSV | |
| document.getElementById('bulkImportButton').addEventListener('click', () => { | |
| document.getElementById('csvFile').click(); | |
| }); | |
| document.getElementById('csvFile').addEventListener('change', (event) => { | |
| const file = event.target.files[0]; | |
| if (file) { | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| const text = e.target.result; | |
| const csvData = Papa.parse(text, { header: true }).data; | |
| csvData.forEach(row => { | |
| if (row.Name && row.Description && row.Price && row.Image) { | |
| const newProduct = { | |
| id: products.length ? products[products.length - 1].id + 1 : 1, | |
| name: row.Name, | |
| description: row.Description, | |
| price: parseFloat(row.Price), | |
| image: row.Image, | |
| category: row.Category || 'General' | |
| }; | |
| products.push(newProduct); | |
| } | |
| }); | |
| saveProductsToFile(); | |
| renderProductList(); | |
| renderPagination(); | |
| renderAdminProductList(); | |
| // Show success message | |
| alert(`Successfully imported ${csvData.length} products!`); | |
| }; | |
| reader.readAsText(file); | |
| } | |
| }); | |
| // Save products to JSON file (simulated for this demo) | |
| function saveProductsToFile() { | |
| // In a real application, this would save to a server or local file | |
| console.log('Products saved:', products); | |
| } | |
| }); | |
| </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=supportATP/catalog" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |