mukoshi commited on
Commit
6fc4f85
·
verified ·
1 Parent(s): 0bda8c2

There should be a setting in the code for categories such as cables and sensors, and users should be able to easily add new categories in the code. - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +624 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Katalog 2 Better
3
- emoji: 🐠
4
- colorFrom: blue
5
- colorTo: red
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: katalog-2-better
3
+ emoji: 🐳
4
+ colorFrom: yellow
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,624 @@
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 Components 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 styles that can't be done with Tailwind */
11
+ .product-card:hover .product-actions {
12
+ opacity: 1;
13
+ }
14
+
15
+ .gradient-bg {
16
+ background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
17
+ }
18
+
19
+ /* Animation for modal */
20
+ @keyframes fadeIn {
21
+ from { opacity: 0; transform: translateY(20px); }
22
+ to { opacity: 1; transform: translateY(0); }
23
+ }
24
+
25
+ .modal-animation {
26
+ animation: fadeIn 0.3s ease-out forwards;
27
+ }
28
+ </style>
29
+ </head>
30
+ <body class="bg-gray-100 font-sans">
31
+ <!-- Header -->
32
+ <header class="bg-blue-600 text-white shadow-lg">
33
+ <div class="container mx-auto px-4 py-4 flex justify-between items-center">
34
+ <div class="flex items-center space-x-2">
35
+ <i class="fas fa-bolt text-2xl"></i>
36
+ <h1 class="text-2xl font-bold">ElectroCatalog</h1>
37
+ </div>
38
+ <button id="add-product-btn" class="bg-white text-blue-600 px-4 py-2 rounded-lg font-medium hover:bg-blue-50 transition">
39
+ <i class="fas fa-plus mr-2"></i>Add Product
40
+ </button>
41
+ </div>
42
+ </header>
43
+
44
+ <!-- Main Content -->
45
+ <main class="container mx-auto px-4 py-8">
46
+ <!-- Search and Filter Section -->
47
+ <div class="bg-white rounded-lg shadow-md p-6 mb-8">
48
+ <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
49
+ <div class="relative flex-grow">
50
+ <input type="text" id="search-input" placeholder="Search products..."
51
+ class="w-full pl-10 pr-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
52
+ <i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
53
+ </div>
54
+ <div class="flex flex-col sm:flex-row gap-3">
55
+ <select id="category-filter" class="px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
56
+ <option value="">All Categories</option>
57
+ <option value="cables">Cables</option>
58
+ <option value="components">Components</option>
59
+ <option value="sensors">Sensors</option>
60
+ </select>
61
+ <select id="box-filter" class="px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
62
+ <option value="">All Boxes</option>
63
+ <!-- Box options will be populated by JavaScript -->
64
+ </select>
65
+ <button id="reset-filters" class="px-4 py-2 bg-gray-200 rounded-lg hover:bg-gray-300 transition">
66
+ <i class="fas fa-filter-circle-xmark mr-2"></i>Reset
67
+ </button>
68
+ </div>
69
+ </div>
70
+ </div>
71
+
72
+ <!-- Categories Navigation -->
73
+ <div class="flex overflow-x-auto mb-8 pb-2 scrollbar-hide">
74
+ <button class="category-btn px-6 py-2 mx-1 rounded-full bg-white shadow hover:bg-blue-50 transition whitespace-nowrap" data-category="all">
75
+ All Products
76
+ </button>
77
+ <button class="category-btn px-6 py-2 mx-1 rounded-full bg-white shadow hover:bg-blue-50 transition whitespace-nowrap" data-category="cables">
78
+ <i class="fas fa-ethernet mr-2"></i>Cables
79
+ </button>
80
+ <button class="category-btn px-6 py-2 mx-1 rounded-full bg-white shadow hover:bg-blue-50 transition whitespace-nowrap" data-category="components">
81
+ <i class="fas fa-microchip mr-2"></i>Components
82
+ </button>
83
+ <button class="category-btn px-6 py-2 mx-1 rounded-full bg-white shadow hover:bg-blue-50 transition whitespace-nowrap" data-category="sensors">
84
+ <i class="fas fa-temperature-three-quarters mr-2"></i>Sensors
85
+ </button>
86
+ </div>
87
+
88
+ <!-- Products Grid -->
89
+ <div id="products-container" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
90
+ <!-- Products will be loaded here by JavaScript -->
91
+ </div>
92
+
93
+ <!-- Empty State -->
94
+ <div id="empty-state" class="hidden text-center py-12">
95
+ <i class="fas fa-box-open text-5xl text-gray-300 mb-4"></i>
96
+ <h3 class="text-xl font-medium text-gray-500">No products found</h3>
97
+ <p class="text-gray-400 mt-2">Try adjusting your search or filters</p>
98
+ <button id="add-first-product" class="mt-4 px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition">
99
+ <i class="fas fa-plus mr-2"></i>Add Your First Product
100
+ </button>
101
+ </div>
102
+ </main>
103
+
104
+ <!-- Add/Edit Product Modal -->
105
+ <div id="product-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
106
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4 modal-animation">
107
+ <div class="flex justify-between items-center border-b p-4">
108
+ <h3 class="text-lg font-semibold" id="modal-title">Add New Product</h3>
109
+ <button id="close-modal" class="text-gray-500 hover:text-gray-700">
110
+ <i class="fas fa-times"></i>
111
+ </button>
112
+ </div>
113
+
114
+ <form id="product-form" class="p-6">
115
+ <input type="hidden" id="product-id">
116
+
117
+ <!-- Preview Image -->
118
+ <div class="mb-6">
119
+ <div id="image-preview" class="w-full h-48 rounded-lg bg-gray-100 flex items-center justify-center overflow-hidden mb-2">
120
+ <span class="text-gray-400">Product Image</span>
121
+ </div>
122
+ <input type="file" id="product-image" accept="image/*" class="hidden">
123
+ <button type="button" id="upload-btn" class="w-full py-2 bg-gray-100 rounded-lg hover:bg-gray-200 transition">
124
+ <i class="fas fa-upload mr-2"></i>Upload Image
125
+ </button>
126
+ </div>
127
+
128
+ <!-- Form Fields -->
129
+ <div class="space-y-4">
130
+ <div>
131
+ <label for="product-name" class="block text-sm font-medium text-gray-700 mb-1">Product Name</label>
132
+ <input type="text" id="product-name" required
133
+ class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
134
+ </div>
135
+
136
+ <div>
137
+ <label for="product-category" class="block text-sm font-medium text-gray-700 mb-1">Category</label>
138
+ <select id="product-category" required
139
+ class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
140
+ <option value="">Select a category</option>
141
+ <option value="cables">Cables</option>
142
+ <option value="components">Components</option>
143
+ <option value="sensors">Sensors</option>
144
+ </select>
145
+ </div>
146
+
147
+ <div>
148
+ <label for="product-description" class="block text-sm font-medium text-gray-700 mb-1">Description</label>
149
+ <textarea id="product-description" rows="3"
150
+ class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea>
151
+ </div>
152
+
153
+ <div class="grid grid-cols-2 gap-4">
154
+ <div>
155
+ <label for="product-quantity" class="block text-sm font-medium text-gray-700 mb-1">Quantity</label>
156
+ <input type="number" id="product-quantity" min="0" required
157
+ class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
158
+ </div>
159
+
160
+ <div>
161
+ <label for="product-box" class="block text-sm font-medium text-gray-700 mb-1">Box Number</label>
162
+ <input type="text" id="product-box"
163
+ class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
164
+ </div>
165
+ </div>
166
+ </div>
167
+
168
+ <!-- Form Actions -->
169
+ <div class="flex justify-end space-x-3 mt-6 pt-4 border-t">
170
+ <button type="button" id="cancel-btn" class="px-4 py-2 bg-gray-200 rounded-lg hover:bg-gray-300 transition">
171
+ Cancel
172
+ </button>
173
+ <button type="submit" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition">
174
+ Save Product
175
+ </button>
176
+ </div>
177
+ </form>
178
+ </div>
179
+ </div>
180
+
181
+ <!-- Quantity Adjustment Modal -->
182
+ <div id="quantity-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
183
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-sm mx-4 modal-animation">
184
+ <div class="flex justify-between items-center border-b p-4">
185
+ <h3 class="text-lg font-semibold">Adjust Quantity</h3>
186
+ <button id="close-qty-modal" class="text-gray-500 hover:text-gray-700">
187
+ <i class="fas fa-times"></i>
188
+ </button>
189
+ </div>
190
+
191
+ <div class="p-6">
192
+ <div class="flex items-center justify-between mb-6">
193
+ <button id="decrease-qty" class="w-12 h-12 rounded-full bg-red-100 text-red-600 hover:bg-red-200 transition flex items-center justify-center">
194
+ <i class="fas fa-minus"></i>
195
+ </button>
196
+
197
+ <div class="text-center">
198
+ <div class="text-3xl font-bold" id="current-qty">0</div>
199
+ <div class="text-sm text-gray-500">Current Quantity</div>
200
+ </div>
201
+
202
+ <button id="increase-qty" class="w-12 h-12 rounded-full bg-green-100 text-green-600 hover:bg-green-200 transition flex items-center justify-center">
203
+ <i class="fas fa-plus"></i>
204
+ </button>
205
+ </div>
206
+
207
+ <div class="flex justify-end space-x-3 mt-6 pt-4 border-t">
208
+ <button type="button" id="cancel-qty-btn" class="px-4 py-2 bg-gray-200 rounded-lg hover:bg-gray-300 transition">
209
+ Cancel
210
+ </button>
211
+ <button type="button" id="save-qty-btn" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition">
212
+ Save
213
+ </button>
214
+ </div>
215
+ </div>
216
+ </div>
217
+ </div>
218
+
219
+ <script>
220
+ // Database simulation using localStorage
221
+ const DB_KEY = 'electroCatalogProducts';
222
+
223
+ // Generate a random gradient for product cards
224
+ function generateGradient() {
225
+ const colors = [
226
+ ['#FF9A9E', '#FAD0C4'],
227
+ ['#A18CD1', '#FBC2EB'],
228
+ ['#FFC3A0', '#FFAFBD'],
229
+ ['#6A11CB', '#2575FC'],
230
+ ['#11998E', '#38EF7D'],
231
+ ['#FF512F', '#DD2476'],
232
+ ['#1A2980', '#26D0CE'],
233
+ ['#4776E6', '#8E54E9']
234
+ ];
235
+
236
+ const randomColors = colors[Math.floor(Math.random() * colors.length)];
237
+ return {
238
+ start: randomColors[0],
239
+ end: randomColors[1]
240
+ };
241
+ }
242
+
243
+ // Initialize the app
244
+ document.addEventListener('DOMContentLoaded', function() {
245
+ // Load products from localStorage
246
+ loadProducts();
247
+
248
+ // Set up event listeners
249
+ setupEventListeners();
250
+
251
+ // Update box filter options
252
+ updateBoxFilter();
253
+ });
254
+
255
+ // Load products from localStorage
256
+ function loadProducts() {
257
+ const products = getProducts();
258
+ renderProducts(products);
259
+ }
260
+
261
+ // Get all products from localStorage
262
+ function getProducts() {
263
+ const productsJSON = localStorage.getItem(DB_KEY);
264
+ return productsJSON ? JSON.parse(productsJSON) : [];
265
+ }
266
+
267
+ // Save products to localStorage
268
+ function saveProducts(products) {
269
+ localStorage.setItem(DB_KEY, JSON.stringify(products));
270
+ }
271
+
272
+ // Render products to the page
273
+ function renderProducts(products) {
274
+ const container = document.getElementById('products-container');
275
+ const emptyState = document.getElementById('empty-state');
276
+
277
+ container.innerHTML = '';
278
+
279
+ if (products.length === 0) {
280
+ container.classList.add('hidden');
281
+ emptyState.classList.remove('hidden');
282
+ return;
283
+ }
284
+
285
+ container.classList.remove('hidden');
286
+ emptyState.classList.add('hidden');
287
+
288
+ products.forEach(product => {
289
+ const productCard = createProductCard(product);
290
+ container.appendChild(productCard);
291
+ });
292
+ }
293
+
294
+ // Create a product card element
295
+ function createProductCard(product) {
296
+ const card = document.createElement('div');
297
+ card.className = 'bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition relative';
298
+ card.dataset.id = product.id;
299
+ card.dataset.category = product.category;
300
+ card.dataset.box = product.box || '';
301
+
302
+ // Set gradient background
303
+ card.style.setProperty('--gradient-start', product.gradient.start);
304
+ card.style.setProperty('--gradient-end', product.gradient.end);
305
+
306
+ card.innerHTML = `
307
+ <div class="gradient-bg h-32 flex items-center justify-center">
308
+ ${product.image ?
309
+ `<img src="${product.image}" alt="${product.name}" class="h-full w-full object-contain p-4">` :
310
+ `<i class="fas fa-box-open text-5xl text-white opacity-70"></i>`}
311
+ </div>
312
+ <div class="p-4">
313
+ <div class="flex justify-between items-start">
314
+ <h3 class="font-semibold text-lg truncate">${product.name}</h3>
315
+ <span class="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">${product.category}</span>
316
+ </div>
317
+ <p class="text-gray-600 text-sm mt-2 line-clamp-2">${product.description || 'No description'}</p>
318
+ <div class="mt-4 flex justify-between items-center">
319
+ <div>
320
+ <span class="text-gray-500 text-sm">Qty:</span>
321
+ <span class="font-medium ml-1">${product.quantity}</span>
322
+ </div>
323
+ ${product.box ?
324
+ `<div class="flex items-center">
325
+ <i class="fas fa-box text-gray-500 mr-1"></i>
326
+ <span class="text-gray-700">${product.box}</span>
327
+ </div>` : ''}
328
+ </div>
329
+ </div>
330
+ <div class="product-actions absolute top-2 right-2 opacity-0 transition-opacity duration-200">
331
+ <button class="edit-btn bg-white p-2 rounded-full shadow hover:bg-blue-50 text-blue-600 mr-1" title="Edit">
332
+ <i class="fas fa-pen"></i>
333
+ </button>
334
+ <button class="qty-btn bg-white p-2 rounded-full shadow hover:bg-blue-50 text-green-600" title="Adjust Quantity">
335
+ <i class="fas fa-calculator"></i>
336
+ </button>
337
+ </div>
338
+ `;
339
+
340
+ // Add event listeners to action buttons
341
+ card.querySelector('.edit-btn').addEventListener('click', () => openEditModal(product.id));
342
+ card.querySelector('.qty-btn').addEventListener('click', () => openQuantityModal(product.id));
343
+
344
+ return card;
345
+ }
346
+
347
+ // Set up all event listeners
348
+ function setupEventListeners() {
349
+ // Add product button
350
+ document.getElementById('add-product-btn').addEventListener('click', openAddModal);
351
+ document.getElementById('add-first-product').addEventListener('click', openAddModal);
352
+
353
+ // Modal close buttons
354
+ document.getElementById('close-modal').addEventListener('click', closeModal);
355
+ document.getElementById('cancel-btn').addEventListener('click', closeModal);
356
+ document.getElementById('close-qty-modal').addEventListener('click', closeQuantityModal);
357
+ document.getElementById('cancel-qty-btn').addEventListener('click', closeQuantityModal);
358
+
359
+ // Image upload
360
+ document.getElementById('upload-btn').addEventListener('click', () => {
361
+ document.getElementById('product-image').click();
362
+ });
363
+
364
+ document.getElementById('product-image').addEventListener('change', function(e) {
365
+ const file = e.target.files[0];
366
+ if (file) {
367
+ const reader = new FileReader();
368
+ reader.onload = function(event) {
369
+ document.getElementById('image-preview').innerHTML =
370
+ `<img src="${event.target.result}" class="w-full h-full object-contain" alt="Preview">`;
371
+ };
372
+ reader.readAsDataURL(file);
373
+ }
374
+ });
375
+
376
+ // Product form submission
377
+ document.getElementById('product-form').addEventListener('submit', handleFormSubmit);
378
+
379
+ // Quantity adjustment
380
+ document.getElementById('increase-qty').addEventListener('click', () => adjustQuantity(1));
381
+ document.getElementById('decrease-qty').addEventListener('click', () => adjustQuantity(-1));
382
+ document.getElementById('save-qty-btn').addEventListener('click', saveQuantity);
383
+
384
+ // Search and filter
385
+ document.getElementById('search-input').addEventListener('input', filterProducts);
386
+ document.getElementById('category-filter').addEventListener('change', filterProducts);
387
+ document.getElementById('box-filter').addEventListener('change', filterProducts);
388
+ document.getElementById('reset-filters').addEventListener('click', resetFilters);
389
+
390
+ // Category buttons
391
+ document.querySelectorAll('.category-btn').forEach(btn => {
392
+ btn.addEventListener('click', function() {
393
+ const category = this.dataset.category;
394
+ filterByCategory(category);
395
+ });
396
+ });
397
+ }
398
+
399
+ // Open modal to add new product
400
+ function openAddModal() {
401
+ document.getElementById('modal-title').textContent = 'Add New Product';
402
+ document.getElementById('product-form').reset();
403
+ document.getElementById('product-id').value = '';
404
+ document.getElementById('image-preview').innerHTML = '<span class="text-gray-400">Product Image</span>';
405
+ document.getElementById('product-modal').classList.remove('hidden');
406
+ }
407
+
408
+ // Open modal to edit product
409
+ function openEditModal(productId) {
410
+ const products = getProducts();
411
+ const product = products.find(p => p.id === productId);
412
+
413
+ if (product) {
414
+ document.getElementById('modal-title').textContent = 'Edit Product';
415
+ document.getElementById('product-id').value = product.id;
416
+ document.getElementById('product-name').value = product.name;
417
+ document.getElementById('product-category').value = product.category;
418
+ document.getElementById('product-description').value = product.description || '';
419
+ document.getElementById('product-quantity').value = product.quantity;
420
+ document.getElementById('product-box').value = product.box || '';
421
+
422
+ // Set image preview if exists
423
+ const imagePreview = document.getElementById('image-preview');
424
+ if (product.image) {
425
+ imagePreview.innerHTML = `<img src="${product.image}" class="w-full h-full object-contain" alt="Preview">`;
426
+ } else {
427
+ imagePreview.innerHTML = '<span class="text-gray-400">Product Image</span>';
428
+ }
429
+
430
+ document.getElementById('product-modal').classList.remove('hidden');
431
+ }
432
+ }
433
+
434
+ // Close the product modal
435
+ function closeModal() {
436
+ document.getElementById('product-modal').classList.add('hidden');
437
+ }
438
+
439
+ // Handle form submission (add/edit product)
440
+ function handleFormSubmit(e) {
441
+ e.preventDefault();
442
+
443
+ const productId = document.getElementById('product-id').value;
444
+ const name = document.getElementById('product-name').value.trim();
445
+ const category = document.getElementById('product-category').value;
446
+ const description = document.getElementById('product-description').value.trim();
447
+ const quantity = parseInt(document.getElementById('product-quantity').value);
448
+ const box = document.getElementById('product-box').value.trim();
449
+
450
+ // Get image data if uploaded
451
+ const imageInput = document.getElementById('product-image');
452
+ let image = '';
453
+
454
+ if (imageInput.files.length > 0) {
455
+ const file = imageInput.files[0];
456
+ const reader = new FileReader();
457
+ reader.onload = function(event) {
458
+ image = event.target.result;
459
+ saveProductData(productId, name, category, description, quantity, box, image);
460
+ };
461
+ reader.readAsDataURL(file);
462
+ } else {
463
+ // If editing and no new image selected, keep existing image
464
+ if (productId) {
465
+ const products = getProducts();
466
+ const existingProduct = products.find(p => p.id === productId);
467
+ image = existingProduct ? existingProduct.image : '';
468
+ }
469
+ saveProductData(productId, name, category, description, quantity, box, image);
470
+ }
471
+ }
472
+
473
+ // Save product data to database
474
+ function saveProductData(id, name, category, description, quantity, box, image) {
475
+ const products = getProducts();
476
+
477
+ if (id) {
478
+ // Update existing product
479
+ const index = products.findIndex(p => p.id === id);
480
+ if (index !== -1) {
481
+ products[index] = {
482
+ ...products[index],
483
+ name,
484
+ category,
485
+ description,
486
+ quantity,
487
+ box,
488
+ image: image || products[index].image
489
+ };
490
+ }
491
+ } else {
492
+ // Add new product
493
+ const newProduct = {
494
+ id: Date.now().toString(),
495
+ name,
496
+ category,
497
+ description,
498
+ quantity,
499
+ box,
500
+ image,
501
+ gradient: generateGradient()
502
+ };
503
+ products.push(newProduct);
504
+ }
505
+
506
+ saveProducts(products);
507
+ renderProducts(products);
508
+ updateBoxFilter();
509
+ closeModal();
510
+ }
511
+
512
+ // Open quantity adjustment modal
513
+ function openQuantityModal(productId) {
514
+ const products = getProducts();
515
+ const product = products.find(p => p.id === productId);
516
+
517
+ if (product) {
518
+ document.getElementById('quantity-modal').dataset.productId = productId;
519
+ document.getElementById('current-qty').textContent = product.quantity;
520
+ document.getElementById('quantity-modal').classList.remove('hidden');
521
+ }
522
+ }
523
+
524
+ // Close quantity modal
525
+ function closeQuantityModal() {
526
+ document.getElementById('quantity-modal').classList.add('hidden');
527
+ }
528
+
529
+ // Adjust quantity in the modal
530
+ function adjustQuantity(change) {
531
+ const qtyElement = document.getElementById('current-qty');
532
+ let currentQty = parseInt(qtyElement.textContent);
533
+ currentQty += change;
534
+
535
+ // Don't allow negative quantities
536
+ if (currentQty < 0) currentQty = 0;
537
+
538
+ qtyElement.textContent = currentQty;
539
+ }
540
+
541
+ // Save the adjusted quantity
542
+ function saveQuantity() {
543
+ const productId = document.getElementById('quantity-modal').dataset.productId;
544
+ const newQuantity = parseInt(document.getElementById('current-qty').textContent);
545
+
546
+ const products = getProducts();
547
+ const product = products.find(p => p.id === productId);
548
+
549
+ if (product) {
550
+ product.quantity = newQuantity;
551
+ saveProducts(products);
552
+ renderProducts(products);
553
+ }
554
+
555
+ closeQuantityModal();
556
+ }
557
+
558
+ // Filter products by search and filters
559
+ function filterProducts() {
560
+ const searchTerm = document.getElementById('search-input').value.toLowerCase();
561
+ const category = document.getElementById('category-filter').value;
562
+ const box = document.getElementById('box-filter').value;
563
+
564
+ const products = getProducts();
565
+ let filtered = [...products];
566
+
567
+ // Apply search filter
568
+ if (searchTerm) {
569
+ filtered = filtered.filter(product =>
570
+ product.name.toLowerCase().includes(searchTerm) ||
571
+ (product.description && product.description.toLowerCase().includes(searchTerm))
572
+ );
573
+ }
574
+
575
+ // Apply category filter
576
+ if (category) {
577
+ filtered = filtered.filter(product => product.category === category);
578
+ }
579
+
580
+ // Apply box filter
581
+ if (box) {
582
+ filtered = filtered.filter(product => product.box === box);
583
+ }
584
+
585
+ renderProducts(filtered);
586
+ }
587
+
588
+ // Filter by category from buttons
589
+ function filterByCategory(category) {
590
+ document.getElementById('category-filter').value = category === 'all' ? '' : category;
591
+ filterProducts();
592
+ }
593
+
594
+ // Reset all filters
595
+ function resetFilters() {
596
+ document.getElementById('search-input').value = '';
597
+ document.getElementById('category-filter').value = '';
598
+ document.getElementById('box-filter').value = '';
599
+ filterProducts();
600
+ }
601
+
602
+ // Update box filter options based on existing products
603
+ function updateBoxFilter() {
604
+ const products = getProducts();
605
+ const boxes = [...new Set(products.map(p => p.box).filter(box => box))].sort();
606
+
607
+ const boxFilter = document.getElementById('box-filter');
608
+
609
+ // Keep the first option (All Boxes)
610
+ while (boxFilter.options.length > 1) {
611
+ boxFilter.remove(1);
612
+ }
613
+
614
+ // Add box options
615
+ boxes.forEach(box => {
616
+ const option = document.createElement('option');
617
+ option.value = box;
618
+ option.textContent = box;
619
+ boxFilter.appendChild(option);
620
+ });
621
+ }
622
+ </script>
623
+ <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/katalog-2-better" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
624
+ </html>