catalog / index.html
mukoshi's picture
undefined - Initial Deployment
e98c977 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ElectroCatalog - Electrical & Electronics Inventory</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>
/* Custom CSS for gradients and animations */
.product-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.gradient-text {
background-clip: text;
-webkit-background-clip: text;
color: transparent;
background-image: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: #888;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #555;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<!-- Header/Navigation -->
<header class="gradient-bg text-white shadow-lg">
<div class="container mx-auto px-4 py-6">
<div class="flex justify-between items-center">
<div class="flex items-center space-x-2">
<i class="fas fa-bolt text-2xl"></i>
<h1 class="text-2xl font-bold">ElectroCatalog</h1>
</div>
<nav class="hidden md:flex space-x-6">
<a href="#" class="hover:text-gray-200 transition" onclick="showView('dashboard')">Dashboard</a>
<a href="#" class="hover:text-gray-200 transition" onclick="showView('all-products')">All Products</a>
<a href="#" class="hover:text-gray-200 transition" onclick="showView('add-product')">Add Product</a>
<a href="#" class="hover:text-gray-200 transition" onclick="showView('boxes')">Boxes</a>
</nav>
<button class="md:hidden text-xl" onclick="toggleMobileMenu()">
<i class="fas fa-bars"></i>
</button>
</div>
</div>
<!-- Mobile Menu -->
<div id="mobileMenu" class="hidden md:hidden bg-indigo-800 px-4 py-2">
<div class="flex flex-col space-y-3">
<a href="#" class="text-white hover:text-gray-200 transition" onclick="showView('dashboard')">Dashboard</a>
<a href="#" class="text-white hover:text-gray-200 transition" onclick="showView('all-products')">All Products</a>
<a href="#" class="text-white hover:text-gray-200 transition" onclick="showView('add-product')">Add Product</a>
<a href="#" class="text-white hover:text-gray-200 transition" onclick="showView('boxes')">Boxes</a>
</div>
</div>
</header>
<!-- Main Content -->
<main class="container mx-auto px-4 py-8">
<!-- Dashboard View (Default) -->
<div id="dashboard" class="view">
<div class="flex justify-between items-center mb-8">
<h2 class="text-2xl font-bold gradient-text">Dashboard</h2>
<div class="relative">
<input type="text" placeholder="Quick search..." class="px-4 py-2 rounded-full border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent" id="quickSearch">
<button class="absolute right-3 top-2 text-gray-500 hover:text-indigo-600">
<i class="fas fa-search"></i>
</button>
</div>
</div>
<!-- Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500">Total Products</p>
<h3 class="text-3xl font-bold" id="totalProducts">0</h3>
</div>
<div class="p-3 rounded-full bg-indigo-100 text-indigo-600">
<i class="fas fa-boxes text-xl"></i>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500">Categories</p>
<h3 class="text-3xl font-bold" id="totalCategories">0</h3>
</div>
<div class="p-3 rounded-full bg-green-100 text-green-600">
<i class="fas fa-tags text-xl"></i>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500">Boxes Used</p>
<h3 class="text-3xl font-bold" id="totalBoxes">0</h3>
</div>
<div class="p-3 rounded-full bg-purple-100 text-purple-600">
<i class="fas fa-archive text-xl"></i>
</div>
</div>
</div>
</div>
<!-- Categories -->
<h3 class="text-xl font-semibold mb-4">Browse Categories</h3>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mb-8">
<div class="category-card bg-white rounded-lg shadow overflow-hidden cursor-pointer transition hover:shadow-lg" onclick="filterByCategory('cables')">
<div class="h-2 bg-blue-500"></div>
<div class="p-4">
<div class="flex items-center space-x-3">
<div class="p-2 rounded-full bg-blue-100 text-blue-600">
<i class="fas fa-plug"></i>
</div>
<h4 class="font-semibold">Cables</h4>
</div>
<p class="text-gray-500 text-sm mt-2">Power cables, data cables, connectors</p>
</div>
</div>
<div class="category-card bg-white rounded-lg shadow overflow-hidden cursor-pointer transition hover:shadow-lg" onclick="filterByCategory('components')">
<div class="h-2 bg-green-500"></div>
<div class="p-4">
<div class="flex items-center space-x-3">
<div class="p-2 rounded-full bg-green-100 text-green-600">
<i class="fas fa-microchip"></i>
</div>
<h4 class="font-semibold">Components</h4>
</div>
<p class="text-gray-500 text-sm mt-2">Resistors, capacitors, ICs, transistors</p>
</div>
</div>
<div class="category-card bg-white rounded-lg shadow overflow-hidden cursor-pointer transition hover:shadow-lg" onclick="filterByCategory('sensors')">
<div class="h-2 bg-purple-500"></div>
<div class="p-4">
<div class="flex items-center space-x-3">
<div class="p-2 rounded-full bg-purple-100 text-purple-600">
<i class="fas fa-sensor"></i>
</div>
<h4 class="font-semibold">Sensors</h4>
</div>
<p class="text-gray-500 text-sm mt-2">Temperature, motion, light, pressure sensors</p>
</div>
</div>
</div>
<!-- Recent Products -->
<h3 class="text-xl font-semibold mb-4">Recently Added Products</h3>
<div class="bg-white rounded-lg shadow overflow-hidden">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Product</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Category</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Quantity</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Box #</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200" id="recentProductsTable">
<!-- Recent products will be inserted here by JavaScript -->
</tbody>
</table>
</div>
</div>
</div>
<!-- All Products View -->
<div id="all-products" class="view hidden">
<div class="flex justify-between items-center mb-8">
<h2 class="text-2xl font-bold gradient-text">All Products</h2>
<div class="flex space-x-2">
<div class="relative">
<input type="text" placeholder="Search products..." class="px-4 py-2 rounded-full border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent" id="productSearch">
<button class="absolute right-3 top-2 text-gray-500 hover:text-indigo-600">
<i class="fas fa-search"></i>
</button>
</div>
<select id="productFilter" class="px-4 py-2 rounded-full border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
<option value="all">All Categories</option>
<option value="cables">Cables</option>
<option value="components">Components</option>
<option value="sensors">Sensors</option>
</select>
<select id="boxFilter" class="px-4 py-2 rounded-full border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
<option value="all">All Boxes</option>
<!-- Box options will be added by JavaScript -->
</select>
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6" id="allProductsGrid">
<!-- All products will be inserted here by JavaScript -->
</div>
</div>
<!-- Add Product View -->
<div id="add-product" class="view hidden">
<h2 class="text-2xl font-bold gradient-text mb-8">Add New Product</h2>
<div class="bg-white rounded-lg shadow p-6 max-w-3xl mx-auto">
<form id="addProductForm">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label for="productName" class="block text-sm font-medium text-gray-700 mb-1">Product Name*</label>
<input type="text" id="productName" class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent" required>
</div>
<div>
<label for="productCategory" class="block text-sm font-medium text-gray-700 mb-1">Category*</label>
<select id="productCategory" class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent" required>
<option value="">Select a category</option>
<option value="cables">Cables</option>
<option value="components">Components</option>
<option value="sensors">Sensors</option>
</select>
</div>
<div>
<label for="productQuantity" class="block text-sm font-medium text-gray-700 mb-1">Quantity*</label>
<input type="number" id="productQuantity" min="1" class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent" required>
</div>
<div>
<label for="productBox" class="block text-sm font-medium text-gray-700 mb-1">Box Number*</label>
<input type="text" id="productBox" class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent" required>
</div>
<div class="md:col-span-2">
<label for="productDescription" class="block text-sm font-medium text-gray-700 mb-1">Description</label>
<textarea id="productDescription" rows="3" class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"></textarea>
</div>
<div>
<label for="productSupplier" class="block text-sm font-medium text-gray-700 mb-1">Supplier</label>
<input type="text" id="productSupplier" class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
</div>
<div>
<label for="productPrice" class="block text-sm font-medium text-gray-700 mb-1">Unit Price ($)</label>
<input type="number" step="0.01" id="productPrice" class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
</div>
</div>
<div class="mt-8 flex justify-end space-x-4">
<button type="reset" class="px-6 py-2 rounded-lg border border-gray-300 text-gray-700 hover:bg-gray-100 transition">Reset</button>
<button type="submit" class="px-6 py-2 rounded-lg bg-indigo-600 text-white hover:bg-indigo-700 transition">Add Product</button>
</div>
</form>
</div>
</div>
<!-- Boxes View -->
<div id="boxes" class="view hidden">
<h2 class="text-2xl font-bold gradient-text mb-8">Box Inventory</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6" id="boxesGrid">
<!-- Boxes will be inserted here by JavaScript -->
</div>
</div>
<!-- Product Detail Modal -->
<div id="productModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
<div class="p-6">
<div class="flex justify-between items-start">
<h3 class="text-xl font-bold" id="modalProductName">Product Name</h3>
<button onclick="closeModal()" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<div class="h-48 rounded-lg mb-4" id="modalProductGradient"></div>
<div class="flex space-x-4">
<div>
<p class="text-sm text-gray-500">Category</p>
<p class="font-medium" id="modalProductCategory">Cables</p>
</div>
<div>
<p class="text-sm text-gray-500">Box #</p>
<p class="font-medium" id="modalProductBox">B-12</p>
</div>
<div>
<p class="text-sm text-gray-500">Quantity</p>
<p class="font-medium" id="modalProductQuantity">25</p>
</div>
</div>
</div>
<div>
<h4 class="font-semibold mb-2">Description</h4>
<p class="text-gray-700 mb-4" id="modalProductDescription">No description provided.</p>
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<p class="text-sm text-gray-500">Supplier</p>
<p class="font-medium" id="modalProductSupplier">-</p>
</div>
<div>
<p class="text-sm text-gray-500">Unit Price</p>
<p class="font-medium" id="modalProductPrice">-</p>
</div>
<div>
<p class="text-sm text-gray-500">Added On</p>
<p class="font-medium" id="modalProductDate">-</p>
</div>
</div>
</div>
</div>
<div class="mt-6 pt-6 border-t border-gray-200 flex justify-between">
<div class="flex space-x-2">
<button onclick="decrementQuantity()" class="px-4 py-2 bg-red-100 text-red-600 rounded-lg hover:bg-red-200 transition">
<i class="fas fa-minus"></i> Decrease
</button>
<button onclick="incrementQuantity()" class="px-4 py-2 bg-green-100 text-green-600 rounded-lg hover:bg-green-200 transition">
<i class="fas fa-plus"></i> Increase
</button>
</div>
<div class="flex space-x-2">
<button onclick="showEditForm()" class="px-4 py-2 bg-blue-100 text-blue-600 rounded-lg hover:bg-blue-200 transition">
<i class="fas fa-edit"></i> Edit
</button>
<button onclick="deleteProduct()" class="px-4 py-2 bg-red-100 text-red-600 rounded-lg hover:bg-red-200 transition">
<i class="fas fa-trash"></i> Delete
</button>
</div>
</div>
<!-- Edit Form (hidden by default) -->
<div id="editForm" class="mt-6 pt-6 border-t border-gray-200 hidden">
<h4 class="font-semibold mb-4">Edit Product</h4>
<form id="editProductForm">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm text-gray-500 mb-1">Product Name</label>
<input type="text" id="editProductName" class="w-full px-3 py-2 rounded-lg border border-gray-300">
</div>
<div>
<label class="block text-sm text-gray-500 mb-1">Category</label>
<select id="editProductCategory" class="w-full px-3 py-2 rounded-lg border border-gray-300">
<option value="cables">Cables</option>
<option value="components">Components</option>
<option value="sensors">Sensors</option>
</select>
</div>
<div>
<label class="block text-sm text-gray-500 mb-1">Quantity</label>
<input type="number" id="editProductQuantity" min="1" class="w-full px-3 py-2 rounded-lg border border-gray-300">
</div>
<div>
<label class="block text-sm text-gray-500 mb-1">Box Number</label>
<input type="text" id="editProductBox" class="w-full px-3 py-2 rounded-lg border border-gray-300">
</div>
<div class="md:col-span-2">
<label class="block text-sm text-gray-500 mb-1">Description</label>
<textarea id="editProductDescription" rows="2" class="w-full px-3 py-2 rounded-lg border border-gray-300"></textarea>
</div>
<div>
<label class="block text-sm text-gray-500 mb-1">Supplier</label>
<input type="text" id="editProductSupplier" class="w-full px-3 py-2 rounded-lg border border-gray-300">
</div>
<div>
<label class="block text-sm text-gray-500 mb-1">Unit Price ($)</label>
<input type="number" step="0.01" id="editProductPrice" class="w-full px-3 py-2 rounded-lg border border-gray-300">
</div>
</div>
<div class="mt-4 flex justify-end space-x-2">
<button type="button" onclick="hideEditForm()" class="px-4 py-2 rounded-lg border border-gray-300 text-gray-700 hover:bg-gray-100 transition">Cancel</button>
<button type="submit" class="px-4 py-2 rounded-lg bg-indigo-600 text-white hover:bg-indigo-700 transition">Save Changes</button>
</div>
</form>
</div>
</div>
</div>
</div>
</main>
<!-- Footer -->
<footer class="bg-gray-800 text-white py-8">
<div class="container mx-auto px-4">
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="mb-4 md:mb-0">
<div class="flex items-center space-x-2">
<i class="fas fa-bolt text-xl"></i>
<h2 class="text-xl font-bold">ElectroCatalog</h2>
</div>
<p class="text-gray-400 mt-2">Your electrical and electronics inventory solution</p>
</div>
<div class="flex space-x-6">
<a href="#" class="hover:text-indigo-400 transition">About</a>
<a href="#" class="hover:text-indigo-400 transition">Contact</a>
<a href="#" class="hover:text-indigo-400 transition">Privacy</a>
<a href="#" class="hover:text-indigo-400 transition">Terms</a>
</div>
</div>
<div class="mt-8 pt-6 border-t border-gray-700 text-center text-gray-400 text-sm">
<p>&copy; 2023 ElectroCatalog. All rights reserved.</p>
</div>
</div>
</footer>
<script>
// Database simulation using localStorage
const dbName = 'electroCatalogDB';
let currentProductId = null;
// Initialize database if not exists
function initDB() {
if (!localStorage.getItem(dbName)) {
const initialData = {
products: [
{
id: 1,
name: "HDMI Cable 2.0",
category: "cables",
quantity: 15,
box: "B-12",
description: "High speed HDMI cable with Ethernet, 4K@60Hz support",
supplier: "CableTech Inc.",
price: 8.99,
date: "2023-05-15",
gradient: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
},
{
id: 2,
name: "Arduino Uno R3",
category: "components",
quantity: 8,
box: "B-05",
description: "Microcontroller board based on the ATmega328P",
supplier: "Arduino LLC",
price: 22.50,
date: "2023-06-02",
gradient: "linear-gradient(135deg, #f093fb 0%, #f5576c 100%)"
},
{
id: 3,
name: "DHT22 Sensor",
category: "sensors",
quantity: 12,
box: "B-08",
description: "Digital temperature and humidity sensor",
supplier: "SensorWorld",
price: 9.95,
date: "2023-06-10",
gradient: "linear-gradient(135deg, #5ee7df 0%, #b490ca 100%)"
}
],
nextId: 4
};
localStorage.setItem(dbName, JSON.stringify(initialData));
}
}
// Get all products
function getAllProducts() {
const db = JSON.parse(localStorage.getItem(dbName));
return db.products;
}
// Get product by ID
function getProductById(id) {
const db = JSON.parse(localStorage.getItem(dbName));
return db.products.find(product => product.id === id);
}
// Add new product
function addProduct(product) {
const db = JSON.parse(localStorage.getItem(dbName));
const newProduct = {
id: db.nextId++,
...product,
date: new Date().toISOString().split('T')[0],
gradient: generateGradient()
};
db.products.push(newProduct);
localStorage.setItem(dbName, JSON.stringify(db));
return newProduct;
}
// Update product
function updateProduct(id, updatedData) {
const db = JSON.parse(localStorage.getItem(dbName));
const index = db.products.findIndex(product => product.id === id);
if (index !== -1) {
db.products[index] = { ...db.products[index], ...updatedData };
localStorage.setItem(dbName, JSON.stringify(db));
return true;
}
return false;
}
// Delete product
function deleteProductById(id) {
const db = JSON.parse(localStorage.getItem(dbName));
const index = db.products.findIndex(product => product.id === id);
if (index !== -1) {
db.products.splice(index, 1);
localStorage.setItem(dbName, JSON.stringify(db));
return true;
}
return false;
}
// Generate random gradient for product card
function generateGradient() {
const gradients = [
"linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
"linear-gradient(135deg, #f093fb 0%, #f5576c 100%)",
"linear-gradient(135deg, #5ee7df 0%, #b490ca 100%)",
"linear-gradient(135deg, #f6d365 0%, #fda085 100%)",
"linear-gradient(135deg, #c471f5 0%, #fa71cd 100%)",
"linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)"
];
return gradients[Math.floor(Math.random() * gradients.length)];
}
// Get unique box numbers
function getUniqueBoxes() {
const products = getAllProducts();
const boxes = [...new Set(products.map(product => product.box))];
return boxes.sort();
}
// Filter products by category
function filterByCategory(category) {
showView('all-products');
document.getElementById('productFilter').value = category;
renderAllProducts();
}
// Show view and hide others
function showView(viewId) {
document.querySelectorAll('.view').forEach(view => {
view.classList.add('hidden');
});
document.getElementById(viewId).classList.remove('hidden');
hideMobileMenu();
// Update active link in navigation
document.querySelectorAll('nav a').forEach(link => {
link.classList.remove('font-semibold', 'border-b-2', 'border-white');
});
// Update UI based on view
switch(viewId) {
case 'dashboard':
document.querySelector('nav a:nth-child(1)').classList.add('font-semibold', 'border-b-2', 'border-white');
updateDashboardStats();
renderRecentProducts();
break;
case 'all-products':
document.querySelector('nav a:nth-child(2)').classList.add('font-semibold', 'border-b-2', 'border-white');
renderAllProducts();
updateBoxFilterOptions();
break;
case 'add-product':
document.querySelector('nav a:nth-child(3)').classList.add('font-semibold', 'border-b-2', 'border-white');
break;
case 'boxes':
document.querySelector('nav a:nth-child(4)').classList.add('font-semibold', 'border-b-2', 'border-white');
renderBoxesView();
break;
}
}
// Toggle mobile menu
function toggleMobileMenu() {
const menu = document.getElementById('mobileMenu');
menu.classList.toggle('hidden');
}
// Hide mobile menu
function hideMobileMenu() {
document.getElementById('mobileMenu').classList.add('hidden');
}
// Update dashboard statistics
function updateDashboardStats() {
const products = getAllProducts();
const categories = new Set(products.map(product => product.category));
const boxes = new Set(products.map(product => product.box));
document.getElementById('totalProducts').textContent = products.length;
document.getElementById('totalCategories').textContent = categories.size;
document.getElementById('totalBoxes').textContent = boxes.size;
}
// Render recent products table
function renderRecentProducts() {
const products = getAllProducts().slice(-5).reverse();
const tableBody = document.getElementById('recentProductsTable');
tableBody.innerHTML = '';
if (products.length === 0) {
tableBody.innerHTML = '<tr><td colspan="5" class="px-6 py-4 text-center text-gray-500">No products found</td></tr>';
return;
}
products.forEach(product => {
const row = document.createElement('tr');
row.className = 'hover:bg-gray-50';
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="flex-shrink-0 h-10 w-10 rounded" style="background: ${product.gradient}"></div>
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">${product.name}</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-${getCategoryColor(product.category)}-100 text-${getCategoryColor(product.category)}-800 capitalize">
${product.category}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${product.quantity}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${product.box}</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button onclick="openProductModal(${product.id})" class="text-indigo-600 hover:text-indigo-900 mr-3">View</button>
<button onclick="editProduct(${product.id})" class="text-blue-600 hover:text-blue-900">Edit</button>
</td>
`;
tableBody.appendChild(row);
});
}
// Render all products grid
function renderAllProducts() {
const searchTerm = document.getElementById('productSearch').value.toLowerCase();
const categoryFilter = document.getElementById('productFilter').value;
const boxFilter = document.getElementById('boxFilter').value;
const products = getAllProducts().filter(product => {
const matchesSearch = product.name.toLowerCase().includes(searchTerm) ||
(product.description && product.description.toLowerCase().includes(searchTerm));
const matchesCategory = categoryFilter === 'all' || product.category === categoryFilter;
const matchesBox = boxFilter === 'all' || product.box === boxFilter;
return matchesSearch && matchesCategory && matchesBox;
});
const productsGrid = document.getElementById('allProductsGrid');
productsGrid.innerHTML = '';
if (products.length === 0) {
productsGrid.innerHTML = '<div class="col-span-full text-center py-10 text-gray-500">No products match your criteria</div>';
return;
}
products.forEach(product => {
const card = document.createElement('div');
card.className = 'product-card bg-white rounded-lg shadow overflow-hidden';
card.innerHTML = `
<div class="h-32" style="background: ${product.gradient}"></div>
<div class="p-4">
<h3 class="font-semibold text-lg mb-1 truncate">${product.name}</h3>
<div class="flex justify-between items-center mb-2">
<span class="text-xs px-2 py-1 rounded-full bg-${getCategoryColor(product.category)}-100 text-${getCategoryColor(product.category)}-800 capitalize">${product.category}</span>
<span class="text-sm font-medium">Qty: ${product.quantity}</span>
</div>
<div class="flex justify-between items-center text-sm text-gray-600">
<span>Box: ${product.box}</span>
<button onclick="openProductModal(${product.id})" class="text-indigo-600 hover:text-indigo-800 font-medium">Details</button>
</div>
</div>
`;
productsGrid.appendChild(card);
});
}
// Render boxes view
function renderBoxesView() {
const boxes = getUniqueBoxes();
const products = getAllProducts();
const boxesGrid = document.getElementById('boxesGrid');
boxesGrid.innerHTML = '';
if (boxes.length === 0) {
boxesGrid.innerHTML = '<div class="col-span-full text-center py-10 text-gray-500">No boxes found</div>';
return;
}
boxes.forEach(box => {
const boxProducts = products.filter(product => product.box === box);
const boxTotalItems = boxProducts.reduce((sum, product) => sum + product.quantity, 0);
const boxCategories = [...new Set(boxProducts.map(product => product.category))];
const boxCard = document.createElement('div');
boxCard.className = 'product-card bg-white rounded-lg shadow overflow-hidden';
boxCard.innerHTML = `
<div class="h-32" style="background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%)"></div>
<div class="p-4">
<h3 class="font-semibold text-lg mb-1">Box ${box}</h3>
<div class="flex justify-between items-center mb-2">
<span class="text-sm text-gray-600">${boxTotalItems} items</span>
<span class="text-sm text-gray-600">${boxProducts.length} products</span>
</div>
<div class="mb-3">
<p class="text-sm text-gray-600 mb-1">Categories:</p>
<div class="flex flex-wrap gap-1">
${boxCategories.map(cat => `<span class="text-xs px-2 py-1 rounded-full bg-${getCategoryColor(cat)}-100 text-${getCategoryColor(cat)}-800 capitalize">${cat}</span>`).join('')}
</div>
</div>
<button onclick="showBoxContents('${box}')" class="w-full py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition">View Contents</button>
</div>
`;
boxesGrid.appendChild(boxCard);
});
}
// Show box contents
function showBoxContents(boxNumber) {
const products = getAllProducts().filter(product => product.box === boxNumber);
showView('all-products');
document.getElementById('boxFilter').value = boxNumber;
renderAllProducts();
}
// Update box filter options
function updateBoxFilterOptions() {
const boxFilter = document.getElementById('boxFilter');
const currentValue = boxFilter.value;
boxFilter.innerHTML = '<option value="all">All Boxes</option>';
const boxes = getUniqueBoxes();
boxes.forEach(box => {
const option = document.createElement('option');
option.value = box;
option.textContent = box;
boxFilter.appendChild(option);
});
// Restore previous selection if still valid
if (boxes.includes(currentValue)) {
boxFilter.value = currentValue;
}
}
// Get category color for styling
function getCategoryColor(category) {
switch(category) {
case 'cables': return 'blue';
case 'components': return 'green';
case 'sensors': return 'purple';
default: return 'gray';
}
}
// Open product modal
function openProductModal(id) {
const product = getProductById(id);
if (!product) return;
currentProductId = id;
document.getElementById('modalProductName').textContent = product.name;
document.getElementById('modalProductCategory').textContent = product.category;
document.getElementById('modalProductBox').textContent = product.box;
document.getElementById('modalProductQuantity').textContent = product.quantity;
document.getElementById('modalProductDescription').textContent = product.description || 'No description provided.';
document.getElementById('modalProductSupplier').textContent = product.supplier || '-';
document.getElementById('modalProductPrice').textContent = product.price ? `$${product.price.toFixed(2)}` : '-';
document.getElementById('modalProductDate').textContent = product.date;
const gradientElement = document.getElementById('modalProductGradient');
gradientElement.style.background = product.gradient;
// Hide edit form if visible
document.getElementById('editForm').classList.add('hidden');
// Show modal
document.getElementById('productModal').classList.remove('hidden');
}
// Close modal
function closeModal() {
document.getElementById('productModal').classList.add('hidden');
currentProductId = null;
}
// Show edit form in modal
function showEditForm() {
const product = getProductById(currentProductId);
if (!product) return;
document.getElementById('editProductName').value = product.name;
document.getElementById('editProductCategory').value = product.category;
document.getElementById('editProductQuantity').value = product.quantity;
document.getElementById('editProductBox').value = product.box;
document.getElementById('editProductDescription').value = product.description || '';
document.getElementById('editProductSupplier').value = product.supplier || '';
document.getElementById('editProductPrice').value = product.price || '';
document.getElementById('editForm').classList.remove('hidden');
}
// Hide edit form in modal
function hideEditForm() {
document.getElementById('editForm').classList.add('hidden');
}
// Increment product quantity
function incrementQuantity() {
const product = getProductById(currentProductId);
if (!product) return;
const newQuantity = product.quantity + 1;
updateProduct(currentProductId, { quantity: newQuantity });
document.getElementById('modalProductQuantity').textContent = newQuantity;
// Update UI
renderRecentProducts();
renderAllProducts();
updateDashboardStats();
}
// Decrement product quantity
function decrementQuantity() {
const product = getProductById(currentProductId);
if (!product) return;
const newQuantity = Math.max(0, product.quantity - 1);
updateProduct(currentProductId, { quantity: newQuantity });
document.getElementById('modalProductQuantity').textContent = newQuantity;
// Update UI
renderRecentProducts();
renderAllProducts();
updateDashboardStats();
}
// Delete current product
function deleteProduct() {
if (!confirm('Are you sure you want to delete this product?')) return;
if (deleteProductById(currentProductId)) {
closeModal();
// Update UI
renderRecentProducts();
renderAllProducts();
updateDashboardStats();
// Show success message
alert('Product deleted successfully!');
}
}
// Initialize the app
function initApp() {
initDB();
showView('dashboard');
// Event listeners
document.getElementById('addProductForm').addEventListener('submit', function(e) {
e.preventDefault();
const product = {
name: document.getElementById('productName').value,
category: document.getElementById('productCategory').value,
quantity: parseInt(document.getElementById('productQuantity').value),
box: document.getElementById('productBox').value,
description: document.getElementById('productDescription').value,
supplier: document.getElementById('productSupplier').value,
price: parseFloat(document.getElementById('productPrice').value) || null
};
addProduct(product);
this.reset();
// Update UI
renderRecentProducts();
renderAllProducts();
updateDashboardStats();
// Show success message
alert('Product added successfully!');
});
document.getElementById('editProductForm').addEventListener('submit', function(e) {
e.preventDefault();
const updatedData = {
name: document.getElementById('editProductName').value,
category: document.getElementById('editProductCategory').value,
quantity: parseInt(document.getElementById('editProductQuantity').value),
box: document.getElementById('editProductBox').value,
description: document.getElementById('editProductDescription').value,
supplier: document.getElementById('editProductSupplier').value,
price: parseFloat(document.getElementById('editProductPrice').value) || null
};
if (updateProduct(currentProductId, updatedData)) {
// Update modal with new data
openProductModal(currentProductId);
// Update UI
renderRecentProducts();
renderAllProducts();
updateDashboardStats();
// Show success message
alert('Product updated successfully!');
}
});
// Search and filter event listeners
document.getElementById('productSearch').addEventListener('input', renderAllProducts);
document.getElementById('productFilter').addEventListener('change', renderAllProducts);
document.getElementById('boxFilter').addEventListener('change', renderAllProducts);
// Quick search from dashboard
document.getElementById('quickSearch').addEventListener('keyup', function(e) {
if (e.key === 'Enter') {
showView('all-products');
document.getElementById('productSearch').value = this.value;
renderAllProducts();
this.value = '';
}
});
}
// Initialize the app when DOM is loaded
document.addEventListener('DOMContentLoaded', initApp);
</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=mukoshi/catalog" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>