catalog / index.html
supportATP's picture
Add 2 files
ff9089d verified
Raw
History Blame Contribute Delete
24.7 kB
<!DOCTYPE html>
<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>