mukoshi commited on
Commit
4a0de11
·
verified ·
1 Parent(s): e058077

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
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +782 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Index4 Best
3
- emoji: 🏃
4
- colorFrom: indigo
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
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>