Spaces:
Running
Running
I want a catalog site. It needs to display products in the electrical and electronics field in a simple and understandable way. There should be categories such as “cables,” “components,” and “sensors.” These categories should be stored in an array in the code, and new categories should be able to be added to that array at any time. The newly added categories should also automatically exist in the system and be functional. There should be a menu where I can add products in detail, and I should be able to see which product is stored in which box using the “box number” feature. Products should have photos, and these photos should be displayed in the main menu. I should also be able to edit them. I should be able to set this when adding a product. Additionally, there should be an editing menu for each product where I can increase or decrease the quantity of a previously added product and modify its information. There should also be a menu that displays all products across all categories, with filtering and search options. The search option should allow text searches in the product description/title, and the filtering options should include information such as box number. You should store information about these products in a database, and this database should be viewable and editable independently. When products are added, a unique gradient(?) should be automatically generated for them. The code should be easy to read and edit and should include comments. It should be simple, clean, and user-friendly. - Initial Deployment
Browse files- README.md +6 -4
- index.html +782 -19
README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: yellow
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
---
|
| 2 |
+
title: index4-best
|
| 3 |
+
emoji: 🐳
|
| 4 |
+
colorFrom: blue
|
| 5 |
colorTo: yellow
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
tags:
|
| 9 |
+
- deepsite
|
| 10 |
---
|
| 11 |
|
| 12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
index.html
CHANGED
|
@@ -1,19 +1,782 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>ElectroCatalog - Electrical & Electronics Inventory</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 9 |
+
<style>
|
| 10 |
+
/* Custom CSS for gradients and animations */
|
| 11 |
+
.gradient-bg {
|
| 12 |
+
background: linear-gradient(135deg, #6e8efb, #a777e3);
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
.product-card:hover {
|
| 16 |
+
transform: translateY(-5px);
|
| 17 |
+
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
.fade-in {
|
| 21 |
+
animation: fadeIn 0.3s ease-in-out;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
@keyframes fadeIn {
|
| 25 |
+
from { opacity: 0; transform: translateY(10px); }
|
| 26 |
+
to { opacity: 1; transform: translateY(0); }
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
/* Custom scrollbar */
|
| 30 |
+
::-webkit-scrollbar {
|
| 31 |
+
width: 8px;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
::-webkit-scrollbar-track {
|
| 35 |
+
background: #f1f1f1;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
::-webkit-scrollbar-thumb {
|
| 39 |
+
background: #888;
|
| 40 |
+
border-radius: 4px;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
::-webkit-scrollbar-thumb:hover {
|
| 44 |
+
background: #555;
|
| 45 |
+
}
|
| 46 |
+
</style>
|
| 47 |
+
</head>
|
| 48 |
+
<body class="bg-gray-100 font-sans">
|
| 49 |
+
<!-- Header -->
|
| 50 |
+
<header class="gradient-bg text-white shadow-lg">
|
| 51 |
+
<div class="container mx-auto px-4 py-6">
|
| 52 |
+
<div class="flex justify-between items-center">
|
| 53 |
+
<div class="flex items-center space-x-2">
|
| 54 |
+
<i class="fas fa-bolt text-3xl"></i>
|
| 55 |
+
<h1 class="text-2xl font-bold">ElectroCatalog</h1>
|
| 56 |
+
</div>
|
| 57 |
+
<button id="viewDbBtn" class="bg-white text-purple-700 px-4 py-2 rounded-lg font-medium hover:bg-gray-100 transition">
|
| 58 |
+
<i class="fas fa-database mr-2"></i>View Database
|
| 59 |
+
</button>
|
| 60 |
+
</div>
|
| 61 |
+
</div>
|
| 62 |
+
</header>
|
| 63 |
+
|
| 64 |
+
<!-- Main Content -->
|
| 65 |
+
<main class="container mx-auto px-4 py-8">
|
| 66 |
+
<!-- Category Navigation -->
|
| 67 |
+
<div class="mb-8">
|
| 68 |
+
<div class="flex justify-between items-center mb-4">
|
| 69 |
+
<h2 class="text-xl font-semibold text-gray-800">Categories</h2>
|
| 70 |
+
<button id="addCategoryBtn" class="bg-blue-500 text-white px-3 py-1 rounded-lg text-sm hover:bg-blue-600 transition">
|
| 71 |
+
<i class="fas fa-plus mr-1"></i>Add Category
|
| 72 |
+
</button>
|
| 73 |
+
</div>
|
| 74 |
+
|
| 75 |
+
<div id="categoryTabs" class="flex flex-wrap gap-2 mb-4">
|
| 76 |
+
<!-- Categories will be loaded here -->
|
| 77 |
+
</div>
|
| 78 |
+
|
| 79 |
+
<div id="addCategoryForm" class="hidden bg-white p-4 rounded-lg shadow-md mb-4">
|
| 80 |
+
<div class="flex items-center space-x-2">
|
| 81 |
+
<input type="text" id="newCategoryName" placeholder="Category name" class="flex-1 px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
| 82 |
+
<button id="saveCategoryBtn" class="bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-600 transition">
|
| 83 |
+
Save
|
| 84 |
+
</button>
|
| 85 |
+
<button id="cancelCategoryBtn" class="bg-gray-500 text-white px-4 py-2 rounded-lg hover:bg-gray-600 transition">
|
| 86 |
+
Cancel
|
| 87 |
+
</button>
|
| 88 |
+
</div>
|
| 89 |
+
</div>
|
| 90 |
+
</div>
|
| 91 |
+
|
| 92 |
+
<!-- Product Management -->
|
| 93 |
+
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
| 94 |
+
<!-- Add Product Form -->
|
| 95 |
+
<div class="bg-white p-6 rounded-lg shadow-md lg:col-span-1">
|
| 96 |
+
<h2 class="text-xl font-semibold text-gray-800 mb-4">Add New Product</h2>
|
| 97 |
+
|
| 98 |
+
<form id="productForm" class="space-y-4">
|
| 99 |
+
<div>
|
| 100 |
+
<label for="productCategory" class="block text-sm font-medium text-gray-700 mb-1">Category</label>
|
| 101 |
+
<select id="productCategory" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
| 102 |
+
<!-- Categories will be loaded here -->
|
| 103 |
+
</select>
|
| 104 |
+
</div>
|
| 105 |
+
|
| 106 |
+
<div>
|
| 107 |
+
<label for="productName" class="block text-sm font-medium text-gray-700 mb-1">Product Name</label>
|
| 108 |
+
<input type="text" id="productName" required class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
| 109 |
+
</div>
|
| 110 |
+
|
| 111 |
+
<div>
|
| 112 |
+
<label for="productDescription" class="block text-sm font-medium text-gray-700 mb-1">Description</label>
|
| 113 |
+
<textarea id="productDescription" rows="3" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea>
|
| 114 |
+
</div>
|
| 115 |
+
|
| 116 |
+
<div>
|
| 117 |
+
<label for="productQuantity" class="block text-sm font-medium text-gray-700 mb-1">Quantity</label>
|
| 118 |
+
<input type="number" id="productQuantity" min="0" value="1" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
| 119 |
+
</div>
|
| 120 |
+
|
| 121 |
+
<div>
|
| 122 |
+
<label for="productBoxNumber" class="block text-sm font-medium text-gray-700 mb-1">Box Number</label>
|
| 123 |
+
<input type="text" id="productBoxNumber" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
| 124 |
+
</div>
|
| 125 |
+
|
| 126 |
+
<div>
|
| 127 |
+
<label for="productImage" class="block text-sm font-medium text-gray-700 mb-1">Product Image</label>
|
| 128 |
+
<input type="file" id="productImage" accept="image/*" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
| 129 |
+
<div id="imagePreview" class="mt-2 hidden">
|
| 130 |
+
<img id="previewImage" src="#" alt="Preview" class="max-w-full h-32 object-contain rounded-lg border">
|
| 131 |
+
</div>
|
| 132 |
+
</div>
|
| 133 |
+
|
| 134 |
+
<button type="submit" class="w-full bg-blue-500 text-white py-2 rounded-lg hover:bg-blue-600 transition">
|
| 135 |
+
<i class="fas fa-save mr-2"></i>Save Product
|
| 136 |
+
</button>
|
| 137 |
+
</form>
|
| 138 |
+
</div>
|
| 139 |
+
|
| 140 |
+
<!-- Product Display -->
|
| 141 |
+
<div class="bg-white p-6 rounded-lg shadow-md lg:col-span-2">
|
| 142 |
+
<div class="flex justify-between items-center mb-6">
|
| 143 |
+
<h2 id="currentCategoryTitle" class="text-xl font-semibold text-gray-800">All Products</h2>
|
| 144 |
+
|
| 145 |
+
<div class="flex items-center space-x-2">
|
| 146 |
+
<div class="relative">
|
| 147 |
+
<input type="text" id="productSearch" placeholder="Search products..." class="pl-10 pr-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
| 148 |
+
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
|
| 149 |
+
</div>
|
| 150 |
+
<select id="boxFilter" class="px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
| 151 |
+
<option value="">All Boxes</option>
|
| 152 |
+
<!-- Box numbers will be loaded here -->
|
| 153 |
+
</select>
|
| 154 |
+
</div>
|
| 155 |
+
</div>
|
| 156 |
+
|
| 157 |
+
<div id="productsContainer" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
| 158 |
+
<!-- Products will be loaded here -->
|
| 159 |
+
</div>
|
| 160 |
+
|
| 161 |
+
<div id="noProductsMessage" class="text-center py-8 text-gray-500 hidden">
|
| 162 |
+
<i class="fas fa-box-open text-4xl mb-2"></i>
|
| 163 |
+
<p>No products found in this category.</p>
|
| 164 |
+
</div>
|
| 165 |
+
</div>
|
| 166 |
+
</div>
|
| 167 |
+
</main>
|
| 168 |
+
|
| 169 |
+
<!-- Database Modal -->
|
| 170 |
+
<div id="databaseModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
|
| 171 |
+
<div class="bg-white rounded-lg shadow-xl w-full max-w-6xl max-h-[90vh] flex flex-col">
|
| 172 |
+
<div class="flex justify-between items-center border-b p-4">
|
| 173 |
+
<h3 class="text-lg font-semibold">Product Database</h3>
|
| 174 |
+
<button id="closeDbModal" class="text-gray-500 hover:text-gray-700">
|
| 175 |
+
<i class="fas fa-times"></i>
|
| 176 |
+
</button>
|
| 177 |
+
</div>
|
| 178 |
+
|
| 179 |
+
<div class="overflow-auto p-4 flex-1">
|
| 180 |
+
<pre id="databaseContent" class="bg-gray-100 p-4 rounded-lg overflow-x-auto"></pre>
|
| 181 |
+
</div>
|
| 182 |
+
|
| 183 |
+
<div class="border-t p-4 flex justify-end space-x-2">
|
| 184 |
+
<button id="exportDbBtn" class="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition">
|
| 185 |
+
<i class="fas fa-download mr-2"></i>Export JSON
|
| 186 |
+
</button>
|
| 187 |
+
<button id="importDbBtn" class="bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-600 transition">
|
| 188 |
+
<i class="fas fa-upload mr-2"></i>Import JSON
|
| 189 |
+
</button>
|
| 190 |
+
<input type="file" id="importDbFile" accept=".json" class="hidden">
|
| 191 |
+
</div>
|
| 192 |
+
</div>
|
| 193 |
+
</div>
|
| 194 |
+
|
| 195 |
+
<!-- Edit Product Modal -->
|
| 196 |
+
<div id="editProductModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
|
| 197 |
+
<div class="bg-white rounded-lg shadow-xl w-full max-w-md max-h-[90vh] overflow-y-auto">
|
| 198 |
+
<div class="flex justify-between items-center border-b p-4">
|
| 199 |
+
<h3 class="text-lg font-semibold">Edit Product</h3>
|
| 200 |
+
<button id="closeEditModal" class="text-gray-500 hover:text-gray-700">
|
| 201 |
+
<i class="fas fa-times"></i>
|
| 202 |
+
</button>
|
| 203 |
+
</div>
|
| 204 |
+
|
| 205 |
+
<form id="editProductForm" class="p-4 space-y-4">
|
| 206 |
+
<input type="hidden" id="editProductId">
|
| 207 |
+
|
| 208 |
+
<div>
|
| 209 |
+
<label for="editProductCategory" class="block text-sm font-medium text-gray-700 mb-1">Category</label>
|
| 210 |
+
<select id="editProductCategory" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
| 211 |
+
<!-- Categories will be loaded here -->
|
| 212 |
+
</select>
|
| 213 |
+
</div>
|
| 214 |
+
|
| 215 |
+
<div>
|
| 216 |
+
<label for="editProductName" class="block text-sm font-medium text-gray-700 mb-1">Product Name</label>
|
| 217 |
+
<input type="text" id="editProductName" required class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
| 218 |
+
</div>
|
| 219 |
+
|
| 220 |
+
<div>
|
| 221 |
+
<label for="editProductDescription" class="block text-sm font-medium text-gray-700 mb-1">Description</label>
|
| 222 |
+
<textarea id="editProductDescription" rows="3" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea>
|
| 223 |
+
</div>
|
| 224 |
+
|
| 225 |
+
<div class="flex space-x-4">
|
| 226 |
+
<div class="flex-1">
|
| 227 |
+
<label for="editProductQuantity" class="block text-sm font-medium text-gray-700 mb-1">Quantity</label>
|
| 228 |
+
<div class="flex items-center">
|
| 229 |
+
<button type="button" id="decreaseQuantity" class="bg-gray-200 px-3 py-1 rounded-l-lg hover:bg-gray-300">
|
| 230 |
+
<i class="fas fa-minus"></i>
|
| 231 |
+
</button>
|
| 232 |
+
<input type="number" id="editProductQuantity" min="0" class="w-full px-3 py-1 border-t border-b text-center">
|
| 233 |
+
<button type="button" id="increaseQuantity" class="bg-gray-200 px-3 py-1 rounded-r-lg hover:bg-gray-300">
|
| 234 |
+
<i class="fas fa-plus"></i>
|
| 235 |
+
</button>
|
| 236 |
+
</div>
|
| 237 |
+
</div>
|
| 238 |
+
|
| 239 |
+
<div class="flex-1">
|
| 240 |
+
<label for="editProductBoxNumber" class="block text-sm font-medium text-gray-700 mb-1">Box Number</label>
|
| 241 |
+
<input type="text" id="editProductBoxNumber" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
| 242 |
+
</div>
|
| 243 |
+
</div>
|
| 244 |
+
|
| 245 |
+
<div>
|
| 246 |
+
<label class="block text-sm font-medium text-gray-700 mb-1">Current Image</label>
|
| 247 |
+
<div id="currentImageContainer" class="flex justify-center">
|
| 248 |
+
<!-- Current image will be shown here -->
|
| 249 |
+
</div>
|
| 250 |
+
</div>
|
| 251 |
+
|
| 252 |
+
<div>
|
| 253 |
+
<label for="editProductImage" class="block text-sm font-medium text-gray-700 mb-1">Change Image</label>
|
| 254 |
+
<input type="file" id="editProductImage" accept="image/*" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
| 255 |
+
<div id="editImagePreview" class="mt-2 hidden">
|
| 256 |
+
<img id="editPreviewImage" src="#" alt="Preview" class="max-w-full h-32 object-contain rounded-lg border">
|
| 257 |
+
</div>
|
| 258 |
+
</div>
|
| 259 |
+
|
| 260 |
+
<div class="flex justify-between pt-4">
|
| 261 |
+
<button type="button" id="deleteProductBtn" class="bg-red-500 text-white px-4 py-2 rounded-lg hover:bg-red-600 transition">
|
| 262 |
+
<i class="fas fa-trash mr-2"></i>Delete
|
| 263 |
+
</button>
|
| 264 |
+
<button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition">
|
| 265 |
+
<i class="fas fa-save mr-2"></i>Save Changes
|
| 266 |
+
</button>
|
| 267 |
+
</div>
|
| 268 |
+
</form>
|
| 269 |
+
</div>
|
| 270 |
+
</div>
|
| 271 |
+
|
| 272 |
+
<script>
|
| 273 |
+
// Database structure
|
| 274 |
+
let database = {
|
| 275 |
+
categories: ['Cables', 'Components', 'Sensors'], // Initial categories
|
| 276 |
+
products: [
|
| 277 |
+
{
|
| 278 |
+
id: '1',
|
| 279 |
+
category: 'Cables',
|
| 280 |
+
name: 'HDMI Cable',
|
| 281 |
+
description: 'High-speed HDMI cable with Ethernet, 2m length',
|
| 282 |
+
quantity: 15,
|
| 283 |
+
boxNumber: 'B12',
|
| 284 |
+
image: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWNhYmxlIj48cGF0aCBkPSJNMTAgMThoLjAxIi8+PHBhdGggZD0iTTE0IDE3aC4wMSIvPjxwYXRoIGQ9Ik0xNS41IDE0aC4wMSIvPjxwYXRoIGQ9Ik0xNyAxMWguMDEiLz48cGF0aCBkPSJNNyAxM2guMDEiLz48cGF0aCBkPSJNNC41IDE2aC4wMSIvPjxwYXRoIGQ9Ik0yIDguNWE0LjUgNiAwIDAgMSA0LjUtNi41YzMuMTYgMCA0LjUgMy4xNCA0LjUgNC41QTIgMiAwIDAgMSAxMC44IDE2SDkuNWEyIDIgMCAwIDEtMS45NS0xLjVMNyAxMCIvPjxwYXRoIGQ9Ik0yMiAxNS41YTQuNSA2IDAgMCAxLTQuNSA2LjVjLTMuMTYgMC00LjUtMy4xNC00LjUtNC41QTIgMiAwIDAgMSAxMy4yIDhoMS4zYTIgMiAwIDAgMSAxLjk1IDEuNUwxNyAxNCIvPjwvc3ZnPg=='
|
| 285 |
+
},
|
| 286 |
+
{
|
| 287 |
+
id: '2',
|
| 288 |
+
category: 'Components',
|
| 289 |
+
name: 'Arduino Uno',
|
| 290 |
+
description: 'Arduino Uno R3 microcontroller board',
|
| 291 |
+
quantity: 8,
|
| 292 |
+
boxNumber: 'B05',
|
| 293 |
+
image: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWNpcmN1aXQtYm9hcmQiPjxwYXRoIGQ9Ik0yIDEyaDQiLz48cGF0aCBkPSJNMTYgNGgtMTJhMiAyIDAgMCAwLTIgMnYxMmEyIDIgMCAwIDAgMiAyaDEyYTIgMiAwIDAgMCAyLTJ2LTEyYTIgMiAwIDAgMC0yLTJ6Ii8+PHBhdGggZD0iTTYgMTJoNCIvPjxwYXRoIGQ9Ik0xNCAxMmg0Ii8+PHBhdGggZD0iTTEwIDEydjQiLz48L3N2Zz4='
|
| 294 |
+
},
|
| 295 |
+
{
|
| 296 |
+
id: '3',
|
| 297 |
+
category: 'Sensors',
|
| 298 |
+
name: 'Temperature Sensor',
|
| 299 |
+
description: 'DS18B20 waterproof temperature sensor',
|
| 300 |
+
quantity: 20,
|
| 301 |
+
boxNumber: 'B08',
|
| 302 |
+
image: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXRoZXJtb21ldGVyIj48cGF0aCBkPSJNMTQgMTRhMiAyIDAgMSAwLTQgMCIvPjxwYXRoIGQ9Ik0xMi4zIDEydi0uMSIvPjxwYXRoIGQ9Ik0xMCA5LjZMMTIuMiA3LjRhNCA0IDAgMCAxIDUuMyAwbDEuOCAxLjgiLz48cGF0aCBkPSJNMTguNCAxMS44YTIgMiAwIDAgMSAwIDIuOGwtMy4xIDMuMWExLjcgMS43IDAgMCAxLTIuMyAwbC0xLjYtMS42YTIgMiAwIDAgMSAwLTIuOGwyLjktMi45Ii8+PHBhdGggZD0iTTggMTcuOGMtLjEuMS0uMi4zLS4zLjQtLjcuOC0xLjYgMS4xLTIuNCAxLjMtMS42LjQtMi44LjQtNC4xLjktLjkuMy0xLjQuMS0xLjcuNi0uMy4xLS40LjItLjMuNS4xLjYuNCAxIC44IDEuMyAxLjIgMS4yIDIuNiAyLjQgMy44IDMuOC4zLjMuNS4xLjgtLjEuNi0uNiAxLjItMS4yIDEuOC0xLjguMy0uMy4xLS41LS4xLS44LS4zLS40LS42LS43LS45LTEuMS0uMy0uNC0uMS0uNi4xLS45LjEtLjEuMy0uMi40LS4zLjgtLjQgMS4zLS44IDEuOS0xLjMuMS0uMS4zLS4yLjQtLjMuMS0uMS4xLS4zIDAtLjQtLjEtLjEtLjMtLjItLjQtLjMtLjQtLjQtLjctLjktMS0xLjQtLjMtLjUtLjEtLjcuMi0uOS4zLS4yLjYtLjQuOS0uNi44LS40IDEuNi0uOCAyLjQtMS4yLjEtLjEuMy0uMS40IDB6Ii8+PC9zdmc+'
|
| 303 |
+
}
|
| 304 |
+
]
|
| 305 |
+
};
|
| 306 |
+
|
| 307 |
+
// Generate a unique ID for new products
|
| 308 |
+
function generateId() {
|
| 309 |
+
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
// Generate a gradient color based on product name
|
| 313 |
+
function generateGradient(name) {
|
| 314 |
+
const hash = Array.from(name).reduce((hash, char) => {
|
| 315 |
+
return char.charCodeAt(0) + ((hash << 5) - hash);
|
| 316 |
+
}, 0);
|
| 317 |
+
|
| 318 |
+
const h = Math.abs(hash % 360);
|
| 319 |
+
return `linear-gradient(135deg, hsl(${h}, 80%, 70%), hsl(${(h + 30) % 360}, 80%, 60%))`;
|
| 320 |
+
}
|
| 321 |
+
|
| 322 |
+
// Initialize the application
|
| 323 |
+
document.addEventListener('DOMContentLoaded', function() {
|
| 324 |
+
// Load categories
|
| 325 |
+
loadCategories();
|
| 326 |
+
|
| 327 |
+
// Load products (show all by default)
|
| 328 |
+
loadProducts();
|
| 329 |
+
|
| 330 |
+
// Set up event listeners
|
| 331 |
+
setupEventListeners();
|
| 332 |
+
});
|
| 333 |
+
|
| 334 |
+
// Load categories into the UI
|
| 335 |
+
function loadCategories() {
|
| 336 |
+
const categoryTabs = document.getElementById('categoryTabs');
|
| 337 |
+
const productCategorySelect = document.getElementById('productCategory');
|
| 338 |
+
const editProductCategorySelect = document.getElementById('editProductCategory');
|
| 339 |
+
|
| 340 |
+
// Clear existing options
|
| 341 |
+
categoryTabs.innerHTML = '';
|
| 342 |
+
productCategorySelect.innerHTML = '';
|
| 343 |
+
editProductCategorySelect.innerHTML = '';
|
| 344 |
+
|
| 345 |
+
// Add "All" option
|
| 346 |
+
const allTab = document.createElement('button');
|
| 347 |
+
allTab.className = 'px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition';
|
| 348 |
+
allTab.textContent = 'All';
|
| 349 |
+
allTab.onclick = () => {
|
| 350 |
+
document.querySelectorAll('#categoryTabs button').forEach(btn => {
|
| 351 |
+
btn.classList.remove('bg-blue-500', 'text-white');
|
| 352 |
+
btn.classList.add('bg-gray-200', 'text-gray-800');
|
| 353 |
+
});
|
| 354 |
+
allTab.classList.remove('bg-gray-200', 'text-gray-800');
|
| 355 |
+
allTab.classList.add('bg-blue-500', 'text-white');
|
| 356 |
+
document.getElementById('currentCategoryTitle').textContent = 'All Products';
|
| 357 |
+
loadProducts();
|
| 358 |
+
};
|
| 359 |
+
categoryTabs.appendChild(allTab);
|
| 360 |
+
|
| 361 |
+
// Add category tabs and select options
|
| 362 |
+
database.categories.forEach(category => {
|
| 363 |
+
// Category tab
|
| 364 |
+
const tab = document.createElement('button');
|
| 365 |
+
tab.className = 'px-4 py-2 bg-gray-200 text-gray-800 rounded-lg hover:bg-gray-300 transition';
|
| 366 |
+
tab.textContent = category;
|
| 367 |
+
tab.onclick = () => {
|
| 368 |
+
document.querySelectorAll('#categoryTabs button').forEach(btn => {
|
| 369 |
+
btn.classList.remove('bg-blue-500', 'text-white');
|
| 370 |
+
btn.classList.add('bg-gray-200', 'text-gray-800');
|
| 371 |
+
});
|
| 372 |
+
tab.classList.remove('bg-gray-200', 'text-gray-800');
|
| 373 |
+
tab.classList.add('bg-blue-500', 'text-white');
|
| 374 |
+
document.getElementById('currentCategoryTitle').textContent = `${category} Products`;
|
| 375 |
+
loadProducts(category);
|
| 376 |
+
};
|
| 377 |
+
categoryTabs.appendChild(tab);
|
| 378 |
+
|
| 379 |
+
// Category select options
|
| 380 |
+
const option = document.createElement('option');
|
| 381 |
+
option.value = category;
|
| 382 |
+
option.textContent = category;
|
| 383 |
+
productCategorySelect.appendChild(option.cloneNode(true));
|
| 384 |
+
editProductCategorySelect.appendChild(option.cloneNode(true));
|
| 385 |
+
});
|
| 386 |
+
|
| 387 |
+
// Load box numbers for filter
|
| 388 |
+
loadBoxNumbers();
|
| 389 |
+
}
|
| 390 |
+
|
| 391 |
+
// Load products into the UI
|
| 392 |
+
function loadProducts(category = null) {
|
| 393 |
+
const productsContainer = document.getElementById('productsContainer');
|
| 394 |
+
const noProductsMessage = document.getElementById('noProductsMessage');
|
| 395 |
+
|
| 396 |
+
// Filter products by category if specified
|
| 397 |
+
let filteredProducts = [...database.products];
|
| 398 |
+
if (category) {
|
| 399 |
+
filteredProducts = filteredProducts.filter(product => product.category === category);
|
| 400 |
+
}
|
| 401 |
+
|
| 402 |
+
// Apply search filter if any
|
| 403 |
+
const searchTerm = document.getElementById('productSearch').value.toLowerCase();
|
| 404 |
+
if (searchTerm) {
|
| 405 |
+
filteredProducts = filteredProducts.filter(product =>
|
| 406 |
+
product.name.toLowerCase().includes(searchTerm) ||
|
| 407 |
+
product.description.toLowerCase().includes(searchTerm)
|
| 408 |
+
);
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
// Apply box filter if any
|
| 412 |
+
const boxFilter = document.getElementById('boxFilter').value;
|
| 413 |
+
if (boxFilter) {
|
| 414 |
+
filteredProducts = filteredProducts.filter(product => product.boxNumber === boxFilter);
|
| 415 |
+
}
|
| 416 |
+
|
| 417 |
+
// Clear existing products
|
| 418 |
+
productsContainer.innerHTML = '';
|
| 419 |
+
|
| 420 |
+
// Display products or no products message
|
| 421 |
+
if (filteredProducts.length === 0) {
|
| 422 |
+
noProductsMessage.classList.remove('hidden');
|
| 423 |
+
} else {
|
| 424 |
+
noProductsMessage.classList.add('hidden');
|
| 425 |
+
filteredProducts.forEach(product => {
|
| 426 |
+
const productCard = createProductCard(product);
|
| 427 |
+
productsContainer.appendChild(productCard);
|
| 428 |
+
});
|
| 429 |
+
}
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
// Create a product card element
|
| 433 |
+
function createProductCard(product) {
|
| 434 |
+
const card = document.createElement('div');
|
| 435 |
+
card.className = 'product-card bg-white rounded-lg shadow-md overflow-hidden transition duration-300 fade-in';
|
| 436 |
+
|
| 437 |
+
// Generate gradient based on product name
|
| 438 |
+
const gradient = generateGradient(product.name);
|
| 439 |
+
|
| 440 |
+
// Product image or placeholder
|
| 441 |
+
const imageHtml = product.image
|
| 442 |
+
? `<img src="${product.image}" alt="${product.name}" class="w-full h-32 object-contain p-4">`
|
| 443 |
+
: `<div class="w-full h-32 flex items-center justify-center" style="background: ${gradient}">
|
| 444 |
+
<i class="fas fa-box-open text-white text-4xl"></i>
|
| 445 |
+
</div>`;
|
| 446 |
+
|
| 447 |
+
// Product details
|
| 448 |
+
card.innerHTML = `
|
| 449 |
+
${imageHtml}
|
| 450 |
+
<div class="p-4">
|
| 451 |
+
<div class="flex justify-between items-start mb-2">
|
| 452 |
+
<h3 class="font-semibold text-gray-800 truncate">${product.name}</h3>
|
| 453 |
+
<span class="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">${product.quantity}</span>
|
| 454 |
+
</div>
|
| 455 |
+
<p class="text-gray-600 text-sm mb-3 line-clamp-2">${product.description}</p>
|
| 456 |
+
<div class="flex justify-between items-center text-sm">
|
| 457 |
+
<span class="text-gray-500"><i class="fas fa-cube mr-1"></i>${product.boxNumber || 'N/A'}</span>
|
| 458 |
+
<button class="text-blue-500 hover:text-blue-700 transition edit-btn" data-id="${product.id}">
|
| 459 |
+
<i class="fas fa-edit"></i>
|
| 460 |
+
</button>
|
| 461 |
+
</div>
|
| 462 |
+
</div>
|
| 463 |
+
`;
|
| 464 |
+
|
| 465 |
+
return card;
|
| 466 |
+
}
|
| 467 |
+
|
| 468 |
+
// Load box numbers into the filter dropdown
|
| 469 |
+
function loadBoxNumbers() {
|
| 470 |
+
const boxFilter = document.getElementById('boxFilter');
|
| 471 |
+
|
| 472 |
+
// Get unique box numbers
|
| 473 |
+
const boxNumbers = [...new Set(database.products.map(product => product.boxNumber).filter(Boolean))];
|
| 474 |
+
|
| 475 |
+
// Clear existing options (keep "All Boxes")
|
| 476 |
+
while (boxFilter.options.length > 1) {
|
| 477 |
+
boxFilter.remove(1);
|
| 478 |
+
}
|
| 479 |
+
|
| 480 |
+
// Add box number options
|
| 481 |
+
boxNumbers.sort().forEach(box => {
|
| 482 |
+
const option = document.createElement('option');
|
| 483 |
+
option.value = box;
|
| 484 |
+
option.textContent = box;
|
| 485 |
+
boxFilter.appendChild(option);
|
| 486 |
+
});
|
| 487 |
+
}
|
| 488 |
+
|
| 489 |
+
// Set up all event listeners
|
| 490 |
+
function setupEventListeners() {
|
| 491 |
+
// Add category button
|
| 492 |
+
document.getElementById('addCategoryBtn').addEventListener('click', () => {
|
| 493 |
+
document.getElementById('addCategoryForm').classList.remove('hidden');
|
| 494 |
+
});
|
| 495 |
+
|
| 496 |
+
// Cancel adding category
|
| 497 |
+
document.getElementById('cancelCategoryBtn').addEventListener('click', () => {
|
| 498 |
+
document.getElementById('addCategoryForm').classList.add('hidden');
|
| 499 |
+
document.getElementById('newCategoryName').value = '';
|
| 500 |
+
});
|
| 501 |
+
|
| 502 |
+
// Save new category
|
| 503 |
+
document.getElementById('saveCategoryBtn').addEventListener('click', () => {
|
| 504 |
+
const newCategory = document.getElementById('newCategoryName').value.trim();
|
| 505 |
+
if (newCategory && !database.categories.includes(newCategory)) {
|
| 506 |
+
database.categories.push(newCategory);
|
| 507 |
+
document.getElementById('addCategoryForm').classList.add('hidden');
|
| 508 |
+
document.getElementById('newCategoryName').value = '';
|
| 509 |
+
loadCategories();
|
| 510 |
+
}
|
| 511 |
+
});
|
| 512 |
+
|
| 513 |
+
// Add product form submission
|
| 514 |
+
document.getElementById('productForm').addEventListener('submit', function(e) {
|
| 515 |
+
e.preventDefault();
|
| 516 |
+
|
| 517 |
+
const category = document.getElementById('productCategory').value;
|
| 518 |
+
const name = document.getElementById('productName').value.trim();
|
| 519 |
+
const description = document.getElementById('productDescription').value.trim();
|
| 520 |
+
const quantity = parseInt(document.getElementById('productQuantity').value);
|
| 521 |
+
const boxNumber = document.getElementById('productBoxNumber').value.trim();
|
| 522 |
+
const imageInput = document.getElementById('productImage');
|
| 523 |
+
|
| 524 |
+
if (name && category) {
|
| 525 |
+
// Create new product
|
| 526 |
+
const newProduct = {
|
| 527 |
+
id: generateId(),
|
| 528 |
+
category,
|
| 529 |
+
name,
|
| 530 |
+
description,
|
| 531 |
+
quantity: isNaN(quantity) ? 0 : quantity,
|
| 532 |
+
boxNumber: boxNumber || null,
|
| 533 |
+
image: null
|
| 534 |
+
};
|
| 535 |
+
|
| 536 |
+
// Handle image upload if any
|
| 537 |
+
if (imageInput.files && imageInput.files[0]) {
|
| 538 |
+
const reader = new FileReader();
|
| 539 |
+
reader.onload = function(e) {
|
| 540 |
+
newProduct.image = e.target.result;
|
| 541 |
+
database.products.push(newProduct);
|
| 542 |
+
loadProducts();
|
| 543 |
+
this.reset();
|
| 544 |
+
document.getElementById('imagePreview').classList.add('hidden');
|
| 545 |
+
};
|
| 546 |
+
reader.readAsDataURL(imageInput.files[0]);
|
| 547 |
+
} else {
|
| 548 |
+
database.products.push(newProduct);
|
| 549 |
+
loadProducts();
|
| 550 |
+
this.reset();
|
| 551 |
+
}
|
| 552 |
+
|
| 553 |
+
// Update box filter if needed
|
| 554 |
+
if (boxNumber && !document.getElementById('boxFilter').querySelector(`option[value="${boxNumber}"]`)) {
|
| 555 |
+
loadBoxNumbers();
|
| 556 |
+
}
|
| 557 |
+
}
|
| 558 |
+
});
|
| 559 |
+
|
| 560 |
+
// Product image preview
|
| 561 |
+
document.getElementById('productImage').addEventListener('change', function() {
|
| 562 |
+
if (this.files && this.files[0]) {
|
| 563 |
+
const reader = new FileReader();
|
| 564 |
+
reader.onload = function(e) {
|
| 565 |
+
document.getElementById('previewImage').src = e.target.result;
|
| 566 |
+
document.getElementById('imagePreview').classList.remove('hidden');
|
| 567 |
+
};
|
| 568 |
+
reader.readAsDataURL(this.files[0]);
|
| 569 |
+
}
|
| 570 |
+
});
|
| 571 |
+
|
| 572 |
+
// Search functionality
|
| 573 |
+
document.getElementById('productSearch').addEventListener('input', () => {
|
| 574 |
+
loadProducts(document.querySelector('#categoryTabs button.bg-blue-500').textContent === 'All' ? null : document.querySelector('#categoryTabs button.bg-blue-500').textContent);
|
| 575 |
+
});
|
| 576 |
+
|
| 577 |
+
// Box filter change
|
| 578 |
+
document.getElementById('boxFilter').addEventListener('change', () => {
|
| 579 |
+
loadProducts(document.querySelector('#categoryTabs button.bg-blue-500').textContent === 'All' ? null : document.querySelector('#categoryTabs button.bg-blue-500').textContent);
|
| 580 |
+
});
|
| 581 |
+
|
| 582 |
+
// View database modal
|
| 583 |
+
document.getElementById('viewDbBtn').addEventListener('click', () => {
|
| 584 |
+
document.getElementById('databaseContent').textContent = JSON.stringify(database, null, 2);
|
| 585 |
+
document.getElementById('databaseModal').classList.remove('hidden');
|
| 586 |
+
});
|
| 587 |
+
|
| 588 |
+
// Close database modal
|
| 589 |
+
document.getElementById('closeDbModal').addEventListener('click', () => {
|
| 590 |
+
document.getElementById('databaseModal').classList.add('hidden');
|
| 591 |
+
});
|
| 592 |
+
|
| 593 |
+
// Export database
|
| 594 |
+
document.getElementById('exportDbBtn').addEventListener('click', () => {
|
| 595 |
+
const dataStr = JSON.stringify(database, null, 2);
|
| 596 |
+
const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
|
| 597 |
+
|
| 598 |
+
const exportFileDefaultName = 'electrocatalog-database.json';
|
| 599 |
+
|
| 600 |
+
const linkElement = document.createElement('a');
|
| 601 |
+
linkElement.setAttribute('href', dataUri);
|
| 602 |
+
linkElement.setAttribute('download', exportFileDefaultName);
|
| 603 |
+
linkElement.click();
|
| 604 |
+
});
|
| 605 |
+
|
| 606 |
+
// Import database
|
| 607 |
+
document.getElementById('importDbBtn').addEventListener('click', () => {
|
| 608 |
+
document.getElementById('importDbFile').click();
|
| 609 |
+
});
|
| 610 |
+
|
| 611 |
+
document.getElementById('importDbFile').addEventListener('change', function() {
|
| 612 |
+
if (this.files && this.files[0]) {
|
| 613 |
+
const reader = new FileReader();
|
| 614 |
+
reader.onload = function(e) {
|
| 615 |
+
try {
|
| 616 |
+
const importedData = JSON.parse(e.target.result);
|
| 617 |
+
if (importedData.categories && importedData.products) {
|
| 618 |
+
if (confirm('Are you sure you want to import this database? All current data will be replaced.')) {
|
| 619 |
+
database = importedData;
|
| 620 |
+
loadCategories();
|
| 621 |
+
loadProducts();
|
| 622 |
+
document.getElementById('databaseModal').classList.add('hidden');
|
| 623 |
+
}
|
| 624 |
+
} else {
|
| 625 |
+
alert('Invalid database format');
|
| 626 |
+
}
|
| 627 |
+
} catch (error) {
|
| 628 |
+
alert('Error parsing JSON file');
|
| 629 |
+
}
|
| 630 |
+
};
|
| 631 |
+
reader.readAsText(this.files[0]);
|
| 632 |
+
this.value = ''; // Reset file input
|
| 633 |
+
}
|
| 634 |
+
});
|
| 635 |
+
|
| 636 |
+
// Edit product (delegated event listener)
|
| 637 |
+
document.addEventListener('click', function(e) {
|
| 638 |
+
if (e.target.closest('.edit-btn')) {
|
| 639 |
+
const productId = e.target.closest('.edit-btn').getAttribute('data-id');
|
| 640 |
+
openEditModal(productId);
|
| 641 |
+
}
|
| 642 |
+
});
|
| 643 |
+
|
| 644 |
+
// Close edit modal
|
| 645 |
+
document.getElementById('closeEditModal').addEventListener('click', () => {
|
| 646 |
+
document.getElementById('editProductModal').classList.add('hidden');
|
| 647 |
+
});
|
| 648 |
+
|
| 649 |
+
// Quantity controls in edit modal
|
| 650 |
+
document.getElementById('increaseQuantity').addEventListener('click', () => {
|
| 651 |
+
const quantityInput = document.getElementById('editProductQuantity');
|
| 652 |
+
quantityInput.value = parseInt(quantityInput.value) + 1;
|
| 653 |
+
});
|
| 654 |
+
|
| 655 |
+
document.getElementById('decreaseQuantity').addEventListener('click', () => {
|
| 656 |
+
const quantityInput = document.getElementById('editProductQuantity');
|
| 657 |
+
const currentValue = parseInt(quantityInput.value);
|
| 658 |
+
if (currentValue > 0) {
|
| 659 |
+
quantityInput.value = currentValue - 1;
|
| 660 |
+
}
|
| 661 |
+
});
|
| 662 |
+
|
| 663 |
+
// Edit product image preview
|
| 664 |
+
document.getElementById('editProductImage').addEventListener('change', function() {
|
| 665 |
+
if (this.files && this.files[0]) {
|
| 666 |
+
const reader = new FileReader();
|
| 667 |
+
reader.onload = function(e) {
|
| 668 |
+
document.getElementById('editPreviewImage').src = e.target.result;
|
| 669 |
+
document.getElementById('editImagePreview').classList.remove('hidden');
|
| 670 |
+
};
|
| 671 |
+
reader.readAsDataURL(this.files[0]);
|
| 672 |
+
}
|
| 673 |
+
});
|
| 674 |
+
|
| 675 |
+
// Delete product
|
| 676 |
+
document.getElementById('deleteProductBtn').addEventListener('click', function() {
|
| 677 |
+
const productId = document.getElementById('editProductId').value;
|
| 678 |
+
if (confirm('Are you sure you want to delete this product?')) {
|
| 679 |
+
database.products = database.products.filter(product => product.id !== productId);
|
| 680 |
+
loadProducts();
|
| 681 |
+
document.getElementById('editProductModal').classList.add('hidden');
|
| 682 |
+
loadBoxNumbers(); // Update box filter in case this was the last product in a box
|
| 683 |
+
}
|
| 684 |
+
});
|
| 685 |
+
|
| 686 |
+
// Edit product form submission
|
| 687 |
+
document.getElementById('editProductForm').addEventListener('submit', function(e) {
|
| 688 |
+
e.preventDefault();
|
| 689 |
+
|
| 690 |
+
const productId = document.getElementById('editProductId').value;
|
| 691 |
+
const category = document.getElementById('editProductCategory').value;
|
| 692 |
+
const name = document.getElementById('editProductName').value.trim();
|
| 693 |
+
const description = document.getElementById('editProductDescription').value.trim();
|
| 694 |
+
const quantity = parseInt(document.getElementById('editProductQuantity').value);
|
| 695 |
+
const boxNumber = document.getElementById('editProductBoxNumber').value.trim();
|
| 696 |
+
const imageInput = document.getElementById('editProductImage');
|
| 697 |
+
|
| 698 |
+
if (name && category) {
|
| 699 |
+
// Find the product to update
|
| 700 |
+
const productIndex = database.products.findIndex(p => p.id === productId);
|
| 701 |
+
if (productIndex !== -1) {
|
| 702 |
+
const oldBoxNumber = database.products[productIndex].boxNumber;
|
| 703 |
+
|
| 704 |
+
// Update product
|
| 705 |
+
database.products[productIndex] = {
|
| 706 |
+
...database.products[productIndex],
|
| 707 |
+
category,
|
| 708 |
+
name,
|
| 709 |
+
description,
|
| 710 |
+
quantity: isNaN(quantity) ? 0 : quantity,
|
| 711 |
+
boxNumber: boxNumber || null
|
| 712 |
+
};
|
| 713 |
+
|
| 714 |
+
// Handle image upload if any
|
| 715 |
+
if (imageInput.files && imageInput.files[0]) {
|
| 716 |
+
const reader = new FileReader();
|
| 717 |
+
reader.onload = function(e) {
|
| 718 |
+
database.products[productIndex].image = e.target.result;
|
| 719 |
+
loadProducts();
|
| 720 |
+
document.getElementById('editProductModal').classList.add('hidden');
|
| 721 |
+
};
|
| 722 |
+
reader.readAsDataURL(imageInput.files[0]);
|
| 723 |
+
} else {
|
| 724 |
+
loadProducts();
|
| 725 |
+
document.getElementById('editProductModal').classList.add('hidden');
|
| 726 |
+
}
|
| 727 |
+
|
| 728 |
+
// Update box filter if box number changed
|
| 729 |
+
if (oldBoxNumber !== boxNumber) {
|
| 730 |
+
loadBoxNumbers();
|
| 731 |
+
}
|
| 732 |
+
}
|
| 733 |
+
}
|
| 734 |
+
});
|
| 735 |
+
}
|
| 736 |
+
|
| 737 |
+
// Open the edit product modal
|
| 738 |
+
function openEditModal(productId) {
|
| 739 |
+
const product = database.products.find(p => p.id === productId);
|
| 740 |
+
if (product) {
|
| 741 |
+
// Fill the form with product data
|
| 742 |
+
document.getElementById('editProductId').value = product.id;
|
| 743 |
+
document.getElementById('editProductCategory').value = product.category;
|
| 744 |
+
document.getElementById('editProductName').value = product.name;
|
| 745 |
+
document.getElementById('editProductDescription').value = product.description || '';
|
| 746 |
+
document.getElementById('editProductQuantity').value = product.quantity;
|
| 747 |
+
document.getElementById('editProductBoxNumber').value = product.boxNumber || '';
|
| 748 |
+
|
| 749 |
+
// Display current image
|
| 750 |
+
const currentImageContainer = document.getElementById('currentImageContainer');
|
| 751 |
+
currentImageContainer.innerHTML = '';
|
| 752 |
+
|
| 753 |
+
if (product.image) {
|
| 754 |
+
const img = document.createElement('img');
|
| 755 |
+
img.src = product.image;
|
| 756 |
+
img.alt = product.name;
|
| 757 |
+
img.className = 'max-w-full h-32 object-contain rounded-lg border';
|
| 758 |
+
currentImageContainer.appendChild(img);
|
| 759 |
+
} else {
|
| 760 |
+
const gradient = generateGradient(product.name);
|
| 761 |
+
const placeholder = document.createElement('div');
|
| 762 |
+
placeholder.className = 'w-full h-32 flex items-center justify-center rounded-lg';
|
| 763 |
+
placeholder.style.background = gradient;
|
| 764 |
+
|
| 765 |
+
const icon = document.createElement('i');
|
| 766 |
+
icon.className = 'fas fa-box-open text-white text-4xl';
|
| 767 |
+
placeholder.appendChild(icon);
|
| 768 |
+
|
| 769 |
+
currentImageContainer.appendChild(placeholder);
|
| 770 |
+
}
|
| 771 |
+
|
| 772 |
+
// Reset image preview and file input
|
| 773 |
+
document.getElementById('editImagePreview').classList.add('hidden');
|
| 774 |
+
document.getElementById('editProductImage').value = '';
|
| 775 |
+
|
| 776 |
+
// Show the modal
|
| 777 |
+
document.getElementById('editProductModal').classList.remove('hidden');
|
| 778 |
+
}
|
| 779 |
+
}
|
| 780 |
+
</script>
|
| 781 |
+
<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/index4-best" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
|
| 782 |
+
</html>
|