| <!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> |
| |
| .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%); |
| } |
| |
| ::-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 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> |
| |
| |
| <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 class="container mx-auto px-4 py-8"> |
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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"> |
| |
| </tbody> |
| </table> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <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> |
| |
| </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"> |
| |
| </div> |
| </div> |
|
|
| |
| <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> |
|
|
| |
| <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"> |
| |
| </div> |
| </div> |
|
|
| |
| <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> |
| |
| |
| <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 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>© 2023 ElectroCatalog. All rights reserved.</p> |
| </div> |
| </div> |
| </footer> |
|
|
| <script> |
| |
| const dbName = 'electroCatalogDB'; |
| let currentProductId = null; |
| |
| |
| 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)); |
| } |
| } |
| |
| |
| function getAllProducts() { |
| const db = JSON.parse(localStorage.getItem(dbName)); |
| return db.products; |
| } |
| |
| |
| function getProductById(id) { |
| const db = JSON.parse(localStorage.getItem(dbName)); |
| return db.products.find(product => product.id === id); |
| } |
| |
| |
| 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; |
| } |
| |
| |
| 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; |
| } |
| |
| |
| 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; |
| } |
| |
| |
| 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)]; |
| } |
| |
| |
| function getUniqueBoxes() { |
| const products = getAllProducts(); |
| const boxes = [...new Set(products.map(product => product.box))]; |
| return boxes.sort(); |
| } |
| |
| |
| function filterByCategory(category) { |
| showView('all-products'); |
| document.getElementById('productFilter').value = category; |
| renderAllProducts(); |
| } |
| |
| |
| function showView(viewId) { |
| document.querySelectorAll('.view').forEach(view => { |
| view.classList.add('hidden'); |
| }); |
| document.getElementById(viewId).classList.remove('hidden'); |
| hideMobileMenu(); |
| |
| |
| document.querySelectorAll('nav a').forEach(link => { |
| link.classList.remove('font-semibold', 'border-b-2', 'border-white'); |
| }); |
| |
| |
| 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; |
| } |
| } |
| |
| |
| function toggleMobileMenu() { |
| const menu = document.getElementById('mobileMenu'); |
| menu.classList.toggle('hidden'); |
| } |
| |
| |
| function hideMobileMenu() { |
| document.getElementById('mobileMenu').classList.add('hidden'); |
| } |
| |
| |
| 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; |
| } |
| |
| |
| 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); |
| }); |
| } |
| |
| |
| 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); |
| }); |
| } |
| |
| |
| 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); |
| }); |
| } |
| |
| |
| function showBoxContents(boxNumber) { |
| const products = getAllProducts().filter(product => product.box === boxNumber); |
| showView('all-products'); |
| document.getElementById('boxFilter').value = boxNumber; |
| renderAllProducts(); |
| } |
| |
| |
| 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); |
| }); |
| |
| |
| if (boxes.includes(currentValue)) { |
| boxFilter.value = currentValue; |
| } |
| } |
| |
| |
| function getCategoryColor(category) { |
| switch(category) { |
| case 'cables': return 'blue'; |
| case 'components': return 'green'; |
| case 'sensors': return 'purple'; |
| default: return 'gray'; |
| } |
| } |
| |
| |
| 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; |
| |
| |
| document.getElementById('editForm').classList.add('hidden'); |
| |
| |
| document.getElementById('productModal').classList.remove('hidden'); |
| } |
| |
| |
| function closeModal() { |
| document.getElementById('productModal').classList.add('hidden'); |
| currentProductId = null; |
| } |
| |
| |
| 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'); |
| } |
| |
| |
| function hideEditForm() { |
| document.getElementById('editForm').classList.add('hidden'); |
| } |
| |
| |
| function incrementQuantity() { |
| const product = getProductById(currentProductId); |
| if (!product) return; |
| |
| const newQuantity = product.quantity + 1; |
| updateProduct(currentProductId, { quantity: newQuantity }); |
| document.getElementById('modalProductQuantity').textContent = newQuantity; |
| |
| |
| renderRecentProducts(); |
| renderAllProducts(); |
| updateDashboardStats(); |
| } |
| |
| |
| 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; |
| |
| |
| renderRecentProducts(); |
| renderAllProducts(); |
| updateDashboardStats(); |
| } |
| |
| |
| function deleteProduct() { |
| if (!confirm('Are you sure you want to delete this product?')) return; |
| |
| if (deleteProductById(currentProductId)) { |
| closeModal(); |
| |
| |
| renderRecentProducts(); |
| renderAllProducts(); |
| updateDashboardStats(); |
| |
| |
| alert('Product deleted successfully!'); |
| } |
| } |
| |
| |
| function initApp() { |
| initDB(); |
| showView('dashboard'); |
| |
| |
| 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(); |
| |
| |
| renderRecentProducts(); |
| renderAllProducts(); |
| updateDashboardStats(); |
| |
| |
| 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)) { |
| |
| openProductModal(currentProductId); |
| |
| |
| renderRecentProducts(); |
| renderAllProducts(); |
| updateDashboardStats(); |
| |
| |
| alert('Product updated successfully!'); |
| } |
| }); |
| |
| |
| document.getElementById('productSearch').addEventListener('input', renderAllProducts); |
| document.getElementById('productFilter').addEventListener('change', renderAllProducts); |
| document.getElementById('boxFilter').addEventListener('change', renderAllProducts); |
| |
| |
| document.getElementById('quickSearch').addEventListener('keyup', function(e) { |
| if (e.key === 'Enter') { |
| showView('all-products'); |
| document.getElementById('productSearch').value = this.value; |
| renderAllProducts(); |
| this.value = ''; |
| } |
| }); |
| } |
| |
| |
| 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> |