Darkmind64 commited on
Commit
43b0f4f
·
verified ·
1 Parent(s): 0f2a2bc

Ajoute la possibilité de faire un inventaire de matériel de sonorisation et éclairage de spectacles. - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1157 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Inventaire
3
- emoji: 📈
4
- colorFrom: green
5
- colorTo: gray
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: inventaire
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: red
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,1157 @@
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>Système d'Inventaire Informatique</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 scrollbar */
11
+ ::-webkit-scrollbar {
12
+ width: 8px;
13
+ }
14
+ ::-webkit-scrollbar-track {
15
+ background: #f1f1f1;
16
+ }
17
+ ::-webkit-scrollbar-thumb {
18
+ background: #888;
19
+ border-radius: 4px;
20
+ }
21
+ ::-webkit-scrollbar-thumb:hover {
22
+ background: #555;
23
+ }
24
+
25
+ /* Animation for alerts */
26
+ @keyframes slideIn {
27
+ from { transform: translateX(100%); }
28
+ to { transform: translateX(0); }
29
+ }
30
+
31
+ @keyframes fadeOut {
32
+ from { opacity: 1; }
33
+ to { opacity: 0; }
34
+ }
35
+
36
+ .alert-slide-in {
37
+ animation: slideIn 0.3s forwards;
38
+ }
39
+
40
+ .alert-fade-out {
41
+ animation: fadeOut 1s forwards;
42
+ }
43
+ </style>
44
+ </head>
45
+ <body class="bg-gray-100 font-sans">
46
+ <div class="container mx-auto px-4 py-8">
47
+ <!-- Header -->
48
+ <header class="mb-8">
49
+ <div class="flex justify-between items-center">
50
+ <div class="flex items-center space-x-4">
51
+ <h1 class="text-3xl font-bold text-blue-800">
52
+ <i class="fas fa-laptop mr-2"></i>Inventaire
53
+ </h1>
54
+ <select id="inventoryType" class="px-3 py-1 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
55
+ <option value="computer">Matériel Informatique</option>
56
+ <option value="audio">Sonorisation</option>
57
+ <option value="lighting">Éclairage</option>
58
+ </select>
59
+ </div>
60
+ <div class="relative">
61
+ <button id="notificationBtn" class="p-2 rounded-full hover:bg-gray-200 relative">
62
+ <i class="fas fa-bell text-gray-600"></i>
63
+ <span id="notificationCount" class="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center hidden">0</span>
64
+ </button>
65
+ <div id="notificationDropdown" class="hidden absolute right-0 mt-2 w-64 bg-white rounded-md shadow-lg z-10">
66
+ <div class="p-4 border-b">
67
+ <h3 class="font-semibold">Notifications</h3>
68
+ </div>
69
+ <div id="notificationList" class="max-h-60 overflow-y-auto">
70
+ <div class="p-3 text-sm text-gray-600">Aucune nouvelle notification</div>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ <p class="text-gray-600 mt-2">Gérez tous les ordinateurs de l'entreprise en un seul endroit</p>
76
+ </header>
77
+
78
+ <!-- Main Content -->
79
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
80
+ <!-- Form Section -->
81
+ <div class="lg:col-span-1 bg-white rounded-lg shadow-md p-6 h-fit">
82
+ <h2 class="text-xl font-semibold mb-4 text-blue-700 border-b pb-2">
83
+ <i class="fas fa-plus-circle mr-2"></i>Ajouter un Équipement
84
+ </h2>
85
+ <form id="equipmentForm" class="space-y-4">
86
+ <div>
87
+ <label for="serialNumber" class="block text-sm font-medium text-gray-700 mb-1">Numéro de série *</label>
88
+ <input type="text" id="serialNumber" name="serialNumber" required
89
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
90
+ </div>
91
+
92
+ <div>
93
+ <label for="model" class="block text-sm font-medium text-gray-700 mb-1">Modèle *</label>
94
+ <input type="text" id="model" name="model" required
95
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
96
+ </div>
97
+
98
+ <div>
99
+ <label for="brand" class="block text-sm font-medium text-gray-700 mb-1">Marque *</label>
100
+ <select id="brand" name="brand" required
101
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
102
+ <option value="">Sélectionnez une marque</option>
103
+ <option value="Dell">Dell</option>
104
+ <option value="HP">HP</option>
105
+ <option value="Lenovo">Lenovo</option>
106
+ <option value="Apple">Apple</option>
107
+ <option value="Asus">Asus</option>
108
+ <option value="Acer">Acer</option>
109
+ <option value="Microsoft">Microsoft</option>
110
+ <option value="Other">Other</option>
111
+ </select>
112
+ </div>
113
+
114
+ <div id="computerFields">
115
+ <div>
116
+ <label for="type" class="block text-sm font-medium text-gray-700 mb-1">Type *</label>
117
+ <select id="type" name="type" required
118
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
119
+ <option value="">Sélectionnez un type</option>
120
+ <option value="Laptop">Portable</option>
121
+ <option value="Desktop">Bureau</option>
122
+ <option value="Workstation">Station de travail</option>
123
+ <option value="Server">Serveur</option>
124
+ </select>
125
+ </div>
126
+ </div>
127
+
128
+ <div id="audioFields" class="hidden">
129
+ <div>
130
+ <label for="audioType" class="block text-sm font-medium text-gray-700 mb-1">Type *</label>
131
+ <select id="audioType" name="audioType"
132
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
133
+ <option value="">Sélectionnez un type</option>
134
+ <option value="Mixer">Table de mixage</option>
135
+ <option value="Speaker">Enceinte</option>
136
+ <option value="Amplifier">Amplificateur</option>
137
+ <option value="Microphone">Microphone</option>
138
+ <option value="Processor">Processeur</option>
139
+ <option value="Cable">Câble</option>
140
+ </select>
141
+ </div>
142
+ <div>
143
+ <label for="power" class="block text-sm font-medium text-gray-700 mb-1">Puissance (W)</label>
144
+ <input type="number" id="power" name="power"
145
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
146
+ </div>
147
+ </div>
148
+
149
+ <div id="lightingFields" class="hidden">
150
+ <div>
151
+ <label for="lightingType" class="block text-sm font-medium text-gray-700 mb-1">Type *</label>
152
+ <select id="lightingType" name="lightingType"
153
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
154
+ <option value="">Sélectionnez un type</option>
155
+ <option value="Spot">Projecteur</option>
156
+ <option value="Laser">Laser</option>
157
+ <option value="Strobe">Stroboscope</option>
158
+ <option value="MovingHead">Moving Head</option>
159
+ <option value="PAR">PAR</option>
160
+ <option value="Controller">Contrôleur</option>
161
+ </select>
162
+ </div>
163
+ <div>
164
+ <label for="wattage" class="block text-sm font-medium text-gray-700 mb-1">Puissance (W)</label>
165
+ <input type="number" id="wattage" name="wattage"
166
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
167
+ </div>
168
+ <div>
169
+ <label for="dmxChannels" class="block text-sm font-medium text-gray-700 mb-1">Canaux DMX</label>
170
+ <input type="number" id="dmxChannels" name="dmxChannels"
171
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
172
+ </div>
173
+ </div>
174
+
175
+ <div>
176
+ <label for="processor" class="block text-sm font-medium text-gray-700 mb-1">Processor</label>
177
+ <input type="text" id="processor" name="processor"
178
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
179
+ </div>
180
+
181
+ <div>
182
+ <label for="ram" class="block text-sm font-medium text-gray-700 mb-1">RAM (GB)</label>
183
+ <input type="number" id="ram" name="ram" min="1"
184
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
185
+ </div>
186
+
187
+ <div>
188
+ <label for="storage" class="block text-sm font-medium text-gray-700 mb-1">Storage (GB)</label>
189
+ <input type="number" id="storage" name="storage" min="1"
190
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
191
+ </div>
192
+
193
+ <div>
194
+ <label for="buyDate" class="block text-sm font-medium text-gray-700 mb-1">Purchase Date *</label>
195
+ <input type="date" id="buyDate" name="buyDate" required
196
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
197
+ </div>
198
+
199
+ <div>
200
+ <label for="warrantyDuration" class="block text-sm font-medium text-gray-700 mb-1">Warranty Duration (months) *</label>
201
+ <input type="number" id="warrantyDuration" name="warrantyDuration" min="1" required
202
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
203
+ </div>
204
+
205
+ <div>
206
+ <label for="assignedTo" class="block text-sm font-medium text-gray-700 mb-1">Assigned To</label>
207
+ <input type="text" id="assignedTo" name="assignedTo"
208
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
209
+ </div>
210
+
211
+ <div>
212
+ <label for="status" class="block text-sm font-medium text-gray-700 mb-1">Status *</label>
213
+ <select id="status" name="status" required
214
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
215
+ <option value="Active">Active</option>
216
+ <option value="In Maintenance">In Maintenance</option>
217
+ <option value="Retired">Retired</option>
218
+ <option value="Lost/Stolen">Lost/Stolen</option>
219
+ </select>
220
+ </div>
221
+
222
+ <div>
223
+ <label for="notes" class="block text-sm font-medium text-gray-700 mb-1">Notes</label>
224
+ <textarea id="notes" name="notes" rows="3"
225
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea>
226
+ </div>
227
+
228
+ <div class="flex space-x-3 pt-2">
229
+ <button type="submit" class="flex-1 bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-md transition duration-200">
230
+ <i class="fas fa-save mr-2"></i>Enregistrer
231
+ </button>
232
+ <button type="reset" class="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-md transition duration-200">
233
+ <i class="fas fa-undo mr-2"></i>Réinitialiser
234
+ </button>
235
+ </div>
236
+ </form>
237
+ </div>
238
+
239
+ <!-- Inventory List Section -->
240
+ <div class="lg:col-span-2 bg-white rounded-lg shadow-md p-6">
241
+ <div class="flex justify-between items-center mb-4">
242
+ <h2 class="text-xl font-semibold text-blue-700">
243
+ <i class="fas fa-list mr-2"></i>Inventory List
244
+ </h2>
245
+ <div class="relative">
246
+ <input type="text" id="searchInput" placeholder="Rechercher..."
247
+ class="pl-10 pr-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
248
+ <i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
249
+ </div>
250
+ </div>
251
+
252
+ <!-- Filters -->
253
+ <div class="flex flex-wrap gap-3 mb-4">
254
+ <select id="filterBrand" class="px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
255
+ <option value="">Toutes les marques</option>
256
+ <option value="Dell">Dell</option>
257
+ <option value="HP">HP</option>
258
+ <option value="Lenovo">Lenovo</option>
259
+ <option value="Apple">Apple</option>
260
+ </select>
261
+
262
+ <select id="filterStatus" class="px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
263
+ <option value="">Tous les statuts</option>
264
+ <option value="Active">Active</option>
265
+ <option value="In Maintenance">In Maintenance</option>
266
+ <option value="Retired">Retired</option>
267
+ </select>
268
+
269
+ <select id="filterWarranty" class="px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
270
+ <option value="">Toutes les garanties</option>
271
+ <option value="expired">Garantie expirée</option>
272
+ <option value="active">Garantie active</option>
273
+ <option value="30">Expire dans 30 jours</option>
274
+ <option value="90">Expire dans 90 jours</option>
275
+ </select>
276
+ </div>
277
+
278
+ <!-- Stats Cards -->
279
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
280
+ <div class="bg-blue-50 p-4 rounded-lg border border-blue-100">
281
+ <div class="flex justify-between items-start">
282
+ <div>
283
+ <p class="text-sm text-blue-600 font-medium">Ordinateurs totaux</p>
284
+ <h3 class="text-2xl font-bold text-blue-800 mt-1" id="totalComputers">0</h3>
285
+ </div>
286
+ <i class="fas fa-laptop text-blue-400 text-xl"></i>
287
+ </div>
288
+ </div>
289
+
290
+ <div class="bg-green-50 p-4 rounded-lg border border-green-100">
291
+ <div class="flex justify-between items-start">
292
+ <div>
293
+ <p class="text-sm text-green-600 font-medium">Actifs</p>
294
+ <h3 class="text-2xl font-bold text-green-800 mt-1" id="activeComputers">0</h3>
295
+ </div>
296
+ <i class="fas fa-check-circle text-green-400 text-xl"></i>
297
+ </div>
298
+ </div>
299
+
300
+ <div class="bg-yellow-50 p-4 rounded-lg border border-yellow-100">
301
+ <div class="flex justify-between items-start">
302
+ <div>
303
+ <p class="text-sm text-yellow-600 font-medium">En maintenance</p>
304
+ <h3 class="text-2xl font-bold text-yellow-800 mt-1" id="maintenanceComputers">0</h3>
305
+ </div>
306
+ <i class="fas fa-tools text-yellow-400 text-xl"></i>
307
+ </div>
308
+ </div>
309
+
310
+ <div class="bg-red-50 p-4 rounded-lg border border-red-100">
311
+ <div class="flex justify-between items-start">
312
+ <div>
313
+ <p class="text-sm text-red-600 font-medium">Garantie expirée</p>
314
+ <h3 class="text-2xl font-bold text-red-800 mt-1" id="expiredWarranty">0</h3>
315
+ </div>
316
+ <i class="fas fa-exclamation-triangle text-red-400 text-xl"></i>
317
+ </div>
318
+ </div>
319
+ </div>
320
+
321
+ <!-- Inventory Table -->
322
+ <div class="overflow-x-auto">
323
+ <table class="min-w-full divide-y divide-gray-200">
324
+ <thead class="bg-gray-50">
325
+ <tr>
326
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer sortable" data-sort="serialNumber">
327
+ Série <i class="fas fa-sort ml-1"></i>
328
+ </th>
329
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer sortable" data-sort="model">
330
+ Modèle <i class="fas fa-sort ml-1"></i>
331
+ </th>
332
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer sortable" data-sort="brand">
333
+ Marque <i class="fas fa-sort ml-1"></i>
334
+ </th>
335
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
336
+ Type Spécifique
337
+ </th>
338
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
339
+ Garantie
340
+ </th>
341
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer sortable" data-sort="status">
342
+ Statut <i class="fas fa-sort ml-1"></i>
343
+ </th>
344
+ <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
345
+ Actions
346
+ </th>
347
+ </tr>
348
+ </thead>
349
+ <tbody id="inventoryTableBody" class="bg-white divide-y divide-gray-200">
350
+ <!-- Data will be inserted here by JavaScript -->
351
+ <tr>
352
+ <td colspan="6" class="px-6 py-4 text-center text-gray-500">No computers found. Add one to get started.</td>
353
+ </tr>
354
+ </tbody>
355
+ </table>
356
+ </div>
357
+
358
+ <!-- Pagination -->
359
+ <div class="flex items-center justify-between mt-4">
360
+ <div class="text-sm text-gray-500">
361
+ Showing <span id="startItem">0</span> to <span id="endItem">0</span> of <span id="totalItems">0</span> computers
362
+ </div>
363
+ <div class="flex space-x-1">
364
+ <button id="prevPage" class="px-3 py-1 border rounded-md text-gray-700 bg-white disabled:opacity-50" disabled>
365
+ <i class="fas fa-chevron-left"></i>
366
+ </button>
367
+ <button id="nextPage" class="px-3 py-1 border rounded-md text-gray-700 bg-white disabled:opacity-50" disabled>
368
+ <i class="fas fa-chevron-right"></i>
369
+ </button>
370
+ </div>
371
+ </div>
372
+ </div>
373
+ </div>
374
+ </div>
375
+
376
+ <!-- Computer Detail Modal -->
377
+ <div id="computerModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
378
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-2xl max-h-[90vh] overflow-y-auto">
379
+ <div class="flex justify-between items-center border-b p-4">
380
+ <h3 class="text-xl font-semibold text-blue-700">Computer Details</h3>
381
+ <button id="closeModal" class="text-gray-500 hover:text-gray-700">
382
+ <i class="fas fa-times"></i>
383
+ </button>
384
+ </div>
385
+ <div class="p-6" id="modalContent">
386
+ <!-- Content will be loaded here -->
387
+ </div>
388
+ <div class="border-t p-4 flex justify-end space-x-3">
389
+ <button id="editComputer" class="bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-md">
390
+ <i class="fas fa-edit mr-2"></i>Edit
391
+ </button>
392
+ <button id="deleteComputer" class="bg-red-600 hover:bg-red-700 text-white py-2 px-4 rounded-md">
393
+ <i class="fas fa-trash mr-2"></i>Delete
394
+ </button>
395
+ <button id="closeModalBtn" class="bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-md">
396
+ Close
397
+ </button>
398
+ </div>
399
+ </div>
400
+ </div>
401
+
402
+ <!-- Alert Container -->
403
+ <div id="alertContainer" class="fixed bottom-4 right-4 space-y-2 z-50"></div>
404
+
405
+ <script>
406
+ // Sample data
407
+ let equipment = [
408
+ {
409
+ id: 1,
410
+ type: 'computer',
411
+ serialNumber: "SN12345678",
412
+ model: "Latitude 5420",
413
+ brand: "Dell",
414
+ computerType: "Laptop",
415
+ processor: "Intel Core i7-1185G7",
416
+ ram: 16,
417
+ storage: 512,
418
+ buyDate: "2022-03-15",
419
+ warrantyDuration: 36,
420
+ assignedTo: "John Doe",
421
+ status: "Active",
422
+ notes: "Primary work laptop"
423
+ },
424
+ {
425
+ id: 2,
426
+ serialNumber: "SN87654321",
427
+ model: "EliteBook 840 G8",
428
+ brand: "HP",
429
+ type: "Laptop",
430
+ processor: "Intel Core i5-1135G7",
431
+ ram: 8,
432
+ storage: 256,
433
+ buyDate: "2021-11-20",
434
+ warrantyDuration: 24,
435
+ assignedTo: "Jane Smith",
436
+ status: "Active",
437
+ notes: "For marketing team"
438
+ },
439
+ {
440
+ id: 3,
441
+ serialNumber: "SN19283746",
442
+ model: "ThinkPad X1 Carbon",
443
+ brand: "Lenovo",
444
+ type: "Laptop",
445
+ processor: "Intel Core i7-1165G7",
446
+ ram: 16,
447
+ storage: 1024,
448
+ buyDate: "2020-08-10",
449
+ warrantyDuration: 36,
450
+ assignedTo: "Mike Johnson",
451
+ status: "In Maintenance",
452
+ notes: "Screen replacement needed"
453
+ },
454
+ {
455
+ id: 4,
456
+ serialNumber: "SN56473829",
457
+ model: "MacBook Pro",
458
+ brand: "Apple",
459
+ type: "Laptop",
460
+ processor: "M1 Pro",
461
+ ram: 16,
462
+ storage: 512,
463
+ buyDate: "2021-10-25",
464
+ warrantyDuration: 12,
465
+ assignedTo: "Sarah Williams",
466
+ status: "Active",
467
+ notes: "Design team"
468
+ },
469
+ {
470
+ id: 5,
471
+ serialNumber: "SN98765432",
472
+ model: "OptiPlex 7080",
473
+ brand: "Dell",
474
+ type: "Desktop",
475
+ processor: "Intel Core i5-10500",
476
+ ram: 8,
477
+ storage: 256,
478
+ buyDate: "2020-05-15",
479
+ warrantyDuration: 36,
480
+ assignedTo: "Reception",
481
+ status: "Active",
482
+ notes: "Front desk computer"
483
+ },
484
+ {
485
+ id: 6,
486
+ type: 'audio',
487
+ serialNumber: "AUD12345",
488
+ model: "XR18",
489
+ brand: "Behringer",
490
+ audioType: "Mixer",
491
+ power: 500,
492
+ buyDate: "2021-05-10",
493
+ warrantyDuration: 24,
494
+ status: "Active",
495
+ notes: "Mixeur numérique 18 canaux"
496
+ },
497
+ {
498
+ id: 7,
499
+ type: 'lighting',
500
+ serialNumber: "LGT78901",
501
+ model: "Quantum Wash",
502
+ brand: "Chauvet",
503
+ lightingType: "MovingHead",
504
+ wattage: 250,
505
+ dmxChannels: 16,
506
+ buyDate: "2022-02-15",
507
+ warrantyDuration: 12,
508
+ status: "Active",
509
+ notes: "Moving Head LED"
510
+ }
511
+ ];
512
+
513
+ // DOM Elements
514
+ const computerForm = document.getElementById('computerForm');
515
+ const inventoryTableBody = document.getElementById('inventoryTableBody');
516
+ const searchInput = document.getElementById('searchInput');
517
+ const filterBrand = document.getElementById('filterBrand');
518
+ const filterStatus = document.getElementById('filterStatus');
519
+ const filterWarranty = document.getElementById('filterWarranty');
520
+ const computerModal = document.getElementById('computerModal');
521
+ const modalContent = document.getElementById('modalContent');
522
+ const closeModal = document.getElementById('closeModal');
523
+ const closeModalBtn = document.getElementById('closeModalBtn');
524
+ const editComputer = document.getElementById('editComputer');
525
+ const deleteComputer = document.getElementById('deleteComputer');
526
+ const prevPage = document.getElementById('prevPage');
527
+ const nextPage = document.getElementById('nextPage');
528
+ const startItem = document.getElementById('startItem');
529
+ const endItem = document.getElementById('endItem');
530
+ const totalItems = document.getElementById('totalItems');
531
+ const notificationBtn = document.getElementById('notificationBtn');
532
+ const notificationDropdown = document.getElementById('notificationDropdown');
533
+ const notificationList = document.getElementById('notificationList');
534
+ const notificationCount = document.getElementById('notificationCount');
535
+ const alertContainer = document.getElementById('alertContainer');
536
+ const totalComputers = document.getElementById('totalComputers');
537
+ const activeComputers = document.getElementById('activeComputers');
538
+ const maintenanceComputers = document.getElementById('maintenanceComputers');
539
+ const expiredWarranty = document.getElementById('expiredWarranty');
540
+
541
+ // State variables
542
+ let currentPage = 1;
543
+ const itemsPerPage = 5;
544
+ let sortColumn = 'serialNumber';
545
+ let sortDirection = 'asc';
546
+ let currentComputerId = null;
547
+ let notifications = [];
548
+
549
+ // Initialize the app
550
+ function init() {
551
+ // Handle inventory type change
552
+ document.getElementById('inventoryType').addEventListener('change', function() {
553
+ const type = this.value;
554
+
555
+ // Show/hide form fields
556
+ document.getElementById('computerFields').classList.toggle('hidden', type !== 'computer');
557
+ document.getElementById('audioFields').classList.toggle('hidden', type !== 'audio');
558
+ document.getElementById('lightingFields').classList.toggle('hidden', type !== 'lighting');
559
+
560
+ // Update table
561
+ renderInventoryTable();
562
+ });
563
+
564
+ renderInventoryTable();
565
+ updateStats();
566
+ checkWarrantyNotifications();
567
+
568
+ // Event listeners
569
+ computerForm.addEventListener('submit', handleFormSubmit);
570
+ searchInput.addEventListener('input', handleSearch);
571
+ filterBrand.addEventListener('change', handleFilter);
572
+ filterStatus.addEventListener('change', handleFilter);
573
+ filterWarranty.addEventListener('change', handleFilter);
574
+ closeModal.addEventListener('click', () => computerModal.classList.add('hidden'));
575
+ closeModalBtn.addEventListener('click', () => computerModal.classList.add('hidden'));
576
+ editComputer.addEventListener('click', handleEditComputer);
577
+ deleteComputer.addEventListener('click', handleDeleteComputer);
578
+ prevPage.addEventListener('click', () => changePage(-1));
579
+ nextPage.addEventListener('click', () => changePage(1));
580
+ notificationBtn.addEventListener('click', toggleNotificationDropdown);
581
+
582
+ // Sortable columns
583
+ document.querySelectorAll('.sortable').forEach(header => {
584
+ header.addEventListener('click', () => {
585
+ const column = header.dataset.sort;
586
+ if (sortColumn === column) {
587
+ sortDirection = sortDirection === 'asc' ? 'desc' : 'asc';
588
+ } else {
589
+ sortColumn = column;
590
+ sortDirection = 'asc';
591
+ }
592
+ renderInventoryTable();
593
+ updateSortIcons();
594
+ });
595
+ });
596
+
597
+ // Close dropdown when clicking outside
598
+ document.addEventListener('click', (e) => {
599
+ if (!notificationBtn.contains(e.target) && !notificationDropdown.contains(e.target)) {
600
+ notificationDropdown.classList.add('hidden');
601
+ }
602
+ });
603
+ }
604
+
605
+ // Render the inventory table
606
+ function renderInventoryTable() {
607
+ const filteredComputers = filterComputers();
608
+ const sortedComputers = sortComputers(filteredComputers);
609
+ const paginatedComputers = paginateComputers(sortedComputers);
610
+
611
+ if (paginatedComputers.length === 0) {
612
+ inventoryTableBody.innerHTML = `
613
+ <tr>
614
+ <td colspan="6" class="px-6 py-4 text-center text-gray-500">No computers found matching your criteria.</td>
615
+ </tr>
616
+ `;
617
+ return;
618
+ }
619
+
620
+ let tableHTML = '';
621
+
622
+ paginatedComputers.forEach(computer => {
623
+ const warrantyStatus = getWarrantyStatus(computer);
624
+ const warrantyClass = warrantyStatus === 'Expired' ? 'bg-red-100 text-red-800' :
625
+ warrantyStatus.includes('days') ? 'bg-yellow-100 text-yellow-800' : 'bg-green-100 text-green-800';
626
+
627
+ tableHTML += `
628
+ <tr class="hover:bg-gray-50 cursor-pointer" data-id="${item.id}">
629
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${item.serialNumber}</td>
630
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${item.model}</td>
631
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${item.brand}</td>
632
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
633
+ ${item.type === 'computer' ? item.computerType :
634
+ item.type === 'audio' ? item.audioType :
635
+ item.lightingType}
636
+ </td>
637
+ <td class="px-6 py-4 whitespace-nowrap">
638
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${warrantyClass}">
639
+ ${warrantyStatus}
640
+ </span>
641
+ </td>
642
+ <td class="px-6 py-4 whitespace-nowrap">
643
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${
644
+ computer.status === 'Active' ? 'bg-green-100 text-green-800' :
645
+ computer.status === 'In Maintenance' ? 'bg-yellow-100 text-yellow-800' :
646
+ 'bg-red-100 text-red-800'
647
+ }">
648
+ ${computer.status}
649
+ </span>
650
+ </td>
651
+ <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
652
+ <button class="text-blue-600 hover:text-blue-900 mr-3 view-btn">
653
+ <i class="fas fa-eye"></i>
654
+ </button>
655
+ <button class="text-indigo-600 hover:text-indigo-900 edit-btn">
656
+ <i class="fas fa-edit"></i>
657
+ </button>
658
+ </td>
659
+ </tr>
660
+ `;
661
+ });
662
+
663
+ inventoryTableBody.innerHTML = tableHTML;
664
+
665
+ // Update pagination info
666
+ const totalFiltered = filteredComputers.length;
667
+ startItem.textContent = ((currentPage - 1) * itemsPerPage) + 1;
668
+ endItem.textContent = Math.min(currentPage * itemsPerPage, totalFiltered);
669
+ totalItems.textContent = totalFiltered;
670
+
671
+ // Enable/disable pagination buttons
672
+ prevPage.disabled = currentPage === 1;
673
+ nextPage.disabled = currentPage * itemsPerPage >= totalFiltered;
674
+
675
+ // Add event listeners to view and edit buttons
676
+ document.querySelectorAll('.view-btn').forEach(btn => {
677
+ btn.addEventListener('click', (e) => {
678
+ e.stopPropagation();
679
+ const computerId = parseInt(btn.closest('tr').dataset.id);
680
+ showComputerDetails(computerId);
681
+ });
682
+ });
683
+
684
+ document.querySelectorAll('.edit-btn').forEach(btn => {
685
+ btn.addEventListener('click', (e) => {
686
+ e.stopPropagation();
687
+ const computerId = parseInt(btn.closest('tr').dataset.id);
688
+ editComputerDetails(computerId);
689
+ });
690
+ });
691
+
692
+ // Add click event to table rows
693
+ document.querySelectorAll('tbody tr[data-id]').forEach(row => {
694
+ row.addEventListener('click', () => {
695
+ const computerId = parseInt(row.dataset.id);
696
+ showComputerDetails(computerId);
697
+ });
698
+ });
699
+ }
700
+
701
+ // Filter computers based on search and filters
702
+ function filterComputers() {
703
+ const searchTerm = searchInput.value.toLowerCase();
704
+ const brandFilter = filterBrand.value;
705
+ const statusFilter = filterStatus.value;
706
+ const warrantyFilter = filterWarranty.value;
707
+
708
+ return computers.filter(computer => {
709
+ // Search term
710
+ const matchesSearch =
711
+ computer.serialNumber.toLowerCase().includes(searchTerm) ||
712
+ computer.model.toLowerCase().includes(searchTerm) ||
713
+ computer.brand.toLowerCase().includes(searchTerm) ||
714
+ (computer.assignedTo && computer.assignedTo.toLowerCase().includes(searchTerm));
715
+
716
+ // Brand filter
717
+ const matchesBrand = brandFilter === '' || computer.brand === brandFilter;
718
+
719
+ // Status filter
720
+ const matchesStatus = statusFilter === '' || computer.status === statusFilter;
721
+
722
+ // Warranty filter
723
+ let matchesWarranty = true;
724
+ if (warrantyFilter !== '') {
725
+ const today = new Date();
726
+ const buyDate = new Date(computer.buyDate);
727
+ const warrantyEnd = new Date(buyDate);
728
+ warrantyEnd.setMonth(warrantyEnd.getMonth() + computer.warrantyDuration);
729
+
730
+ if (warrantyFilter === 'expired') {
731
+ matchesWarranty = warrantyEnd < today;
732
+ } else if (warrantyFilter === 'active') {
733
+ matchesWarranty = warrantyEnd >= today;
734
+ } else {
735
+ // Check if warranty expires in X days
736
+ const daysLeft = Math.floor((warrantyEnd - today) / (1000 * 60 * 60 * 24));
737
+ matchesWarranty = daysLeft <= parseInt(warrantyFilter) && daysLeft >= 0;
738
+ }
739
+ }
740
+
741
+ return matchesSearch && matchesBrand && matchesStatus && matchesWarranty;
742
+ });
743
+ }
744
+
745
+ // Sort computers
746
+ function sortComputers(computersToSort) {
747
+ return [...computersToSort].sort((a, b) => {
748
+ // Handle warranty status differently
749
+ if (sortColumn === 'warranty') {
750
+ const aStatus = getWarrantyStatus(a);
751
+ const bStatus = getWarrantyStatus(b);
752
+
753
+ if (aStatus === bStatus) return 0;
754
+ if (sortDirection === 'asc') {
755
+ return aStatus.localeCompare(bStatus);
756
+ } else {
757
+ return bStatus.localeCompare(aStatus);
758
+ }
759
+ }
760
+
761
+ // Standard sorting for other columns
762
+ if (a[sortColumn] < b[sortColumn]) {
763
+ return sortDirection === 'asc' ? -1 : 1;
764
+ }
765
+ if (a[sortColumn] > b[sortColumn]) {
766
+ return sortDirection === 'asc' ? 1 : -1;
767
+ }
768
+ return 0;
769
+ });
770
+ }
771
+
772
+ // Paginate computers
773
+ function paginateComputers(computersToPaginate) {
774
+ const startIndex = (currentPage - 1) * itemsPerPage;
775
+ return computersToPaginate.slice(startIndex, startIndex + itemsPerPage);
776
+ }
777
+
778
+ // Change page
779
+ function changePage(direction) {
780
+ const filteredComputers = filterComputers();
781
+ const totalPages = Math.ceil(filteredComputers.length / itemsPerPage);
782
+
783
+ currentPage += direction;
784
+
785
+ if (currentPage < 1) currentPage = 1;
786
+ if (currentPage > totalPages) currentPage = totalPages;
787
+
788
+ renderInventoryTable();
789
+ }
790
+
791
+ // Update sort icons
792
+ function updateSortIcons() {
793
+ document.querySelectorAll('.sortable i').forEach(icon => {
794
+ icon.className = 'fas fa-sort ml-1';
795
+ });
796
+
797
+ const activeHeader = document.querySelector(`.sortable[data-sort="${sortColumn}"] i`);
798
+ if (activeHeader) {
799
+ activeHeader.className = sortDirection === 'asc' ?
800
+ 'fas fa-sort-up ml-1' : 'fas fa-sort-down ml-1';
801
+ }
802
+ }
803
+
804
+ // Handle form submission
805
+ function handleFormSubmit(e) {
806
+ e.preventDefault();
807
+
808
+ const formData = new FormData(computerForm);
809
+ const computerData = Object.fromEntries(formData.entries());
810
+
811
+ // Convert number fields
812
+ computerData.ram = parseInt(computerData.ram) || 0;
813
+ computerData.storage = parseInt(computerData.storage) || 0;
814
+ computerData.warrantyDuration = parseInt(computerData.warrantyDuration) || 0;
815
+
816
+ if (currentComputerId) {
817
+ // Update existing computer
818
+ const index = computers.findIndex(c => c.id === currentComputerId);
819
+ if (index !== -1) {
820
+ computers[index] = { ...computers[index], ...computerData };
821
+ showAlert('Computer updated successfully!', 'success');
822
+ }
823
+ } else {
824
+ // Add new computer
825
+ const newId = computers.length > 0 ? Math.max(...computers.map(c => c.id)) + 1 : 1;
826
+ computerData.id = newId;
827
+ computers.push(computerData);
828
+ showAlert('Computer added successfully!', 'success');
829
+ }
830
+
831
+ // Reset form and update UI
832
+ computerForm.reset();
833
+ currentComputerId = null;
834
+ renderInventoryTable();
835
+ updateStats();
836
+ checkWarrantyNotifications();
837
+ }
838
+
839
+ // Show computer details in modal
840
+ function showComputerDetails(id) {
841
+ const computer = computers.find(c => c.id === id);
842
+ if (!computer) return;
843
+
844
+ currentComputerId = id;
845
+
846
+ const warrantyStatus = getWarrantyStatus(computer);
847
+ const warrantyClass = warrantyStatus === 'Expired' ? 'bg-red-100 text-red-800' :
848
+ warrantyStatus.includes('days') ? 'bg-yellow-100 text-yellow-800' : 'bg-green-100 text-green-800';
849
+
850
+ const buyDate = new Date(computer.buyDate);
851
+ const warrantyEnd = new Date(buyDate);
852
+ warrantyEnd.setMonth(warrantyEnd.getMonth() + computer.warrantyDuration);
853
+
854
+ modalContent.innerHTML = `
855
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
856
+ <div>
857
+ <h4 class="text-lg font-medium text-gray-900 mb-2">Basic Information</h4>
858
+ <div class="space-y-3">
859
+ <div>
860
+ <p class="text-sm text-gray-500">Serial Number</p>
861
+ <p class="font-medium">${computer.serialNumber}</p>
862
+ </div>
863
+ <div>
864
+ <p class="text-sm text-gray-500">Model</p>
865
+ <p class="font-medium">${computer.model}</p>
866
+ </div>
867
+ <div>
868
+ <p class="text-sm text-gray-500">Brand</p>
869
+ <p class="font-medium">${computer.brand}</p>
870
+ </div>
871
+ <div>
872
+ <p class="text-sm text-gray-500">Type</p>
873
+ <p class="font-medium">${computer.type}</p>
874
+ </div>
875
+ <div>
876
+ <p class="text-sm text-gray-500">Assigned To</p>
877
+ <p class="font-medium">${computer.assignedTo || 'Not assigned'}</p>
878
+ </div>
879
+ </div>
880
+ </div>
881
+
882
+ <div>
883
+ <h4 class="text-lg font-medium text-gray-900 mb-2">Technical Specifications</h4>
884
+ <div class="space-y-3">
885
+ <div>
886
+ <p class="text-sm text-gray-500">Processor</p>
887
+ <p class="font-medium">${computer.processor || 'Not specified'}</p>
888
+ </div>
889
+ <div>
890
+ <p class="text-sm text-gray-500">RAM</p>
891
+ <p class="font-medium">${computer.ram ? computer.ram + ' GB' : 'Not specified'}</p>
892
+ </div>
893
+ <div>
894
+ <p class="text-sm text-gray-500">Storage</p>
895
+ <p class="font-medium">${computer.storage ? computer.storage + ' GB' : 'Not specified'}</p>
896
+ </div>
897
+ </div>
898
+ </div>
899
+
900
+ <div>
901
+ <h4 class="text-lg font-medium text-gray-900 mb-2">Purchase & Warranty</h4>
902
+ <div class="space-y-3">
903
+ <div>
904
+ <p class="text-sm text-gray-500">Purchase Date</p>
905
+ <p class="font-medium">${formatDate(computer.buyDate)}</p>
906
+ </div>
907
+ <div>
908
+ <p class="text-sm text-gray-500">Warranty Duration</p>
909
+ <p class="font-medium">${computer.warrantyDuration} months</p>
910
+ </div>
911
+ <div>
912
+ <p class="text-sm text-gray-500">Warranty End Date</p>
913
+ <p class="font-medium">${formatDate(warrantyEnd)}</p>
914
+ </div>
915
+ <div>
916
+ <p class="text-sm text-gray-500">Warranty Status</p>
917
+ <p class="font-medium">
918
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${warrantyClass}">
919
+ ${warrantyStatus}
920
+ </span>
921
+ </p>
922
+ </div>
923
+ </div>
924
+ </div>
925
+
926
+ <div>
927
+ <h4 class="text-lg font-medium text-gray-900 mb-2">Status & Notes</h4>
928
+ <div class="space-y-3">
929
+ <div>
930
+ <p class="text-sm text-gray-500">Status</p>
931
+ <p class="font-medium">
932
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${
933
+ computer.status === 'Active' ? 'bg-green-100 text-green-800' :
934
+ computer.status === 'In Maintenance' ? 'bg-yellow-100 text-yellow-800' :
935
+ 'bg-red-100 text-red-800'
936
+ }">
937
+ ${computer.status}
938
+ </span>
939
+ </p>
940
+ </div>
941
+ <div>
942
+ <p class="text-sm text-gray-500">Notes</p>
943
+ <p class="font-medium">${computer.notes || 'No notes'}</p>
944
+ </div>
945
+ </div>
946
+ </div>
947
+ </div>
948
+ `;
949
+
950
+ computerModal.classList.remove('hidden');
951
+ }
952
+
953
+ // Edit computer details
954
+ function editComputerDetails(id) {
955
+ const computer = computers.find(c => c.id === id);
956
+ if (!computer) return;
957
+
958
+ currentComputerId = id;
959
+
960
+ // Fill the form with computer data
961
+ Object.keys(computer).forEach(key => {
962
+ if (key !== 'id' && computerForm.elements[key]) {
963
+ computerForm.elements[key].value = computer[key];
964
+ }
965
+ });
966
+
967
+ // Scroll to form
968
+ document.querySelector('.lg\\:col-span-1').scrollIntoView({ behavior: 'smooth' });
969
+ }
970
+
971
+ // Handle edit button in modal
972
+ function handleEditComputer() {
973
+ if (!currentComputerId) return;
974
+
975
+ editComputerDetails(currentComputerId);
976
+ computerModal.classList.add('hidden');
977
+ }
978
+
979
+ // Handle delete button in modal
980
+ function handleDeleteComputer() {
981
+ if (!currentComputerId || !confirm('Are you sure you want to delete this computer?')) return;
982
+
983
+ computers = computers.filter(c => c.id !== currentComputerId);
984
+ showAlert('Computer deleted successfully!', 'success');
985
+ computerModal.classList.add('hidden');
986
+ renderInventoryTable();
987
+ updateStats();
988
+ checkWarrantyNotifications();
989
+ }
990
+
991
+ // Handle search
992
+ function handleSearch() {
993
+ currentPage = 1;
994
+ renderInventoryTable();
995
+ }
996
+
997
+ // Handle filter changes
998
+ function handleFilter() {
999
+ currentPage = 1;
1000
+ renderInventoryTable();
1001
+ }
1002
+
1003
+ // Get warranty status
1004
+ function getWarrantyStatus(computer) {
1005
+ const today = new Date();
1006
+ const buyDate = new Date(computer.buyDate);
1007
+ const warrantyEnd = new Date(buyDate);
1008
+ warrantyEnd.setMonth(warrantyEnd.getMonth() + computer.warrantyDuration);
1009
+
1010
+ if (warrantyEnd < today) {
1011
+ return 'Expired';
1012
+ }
1013
+
1014
+ const daysLeft = Math.floor((warrantyEnd - today) / (1000 * 60 * 60 * 24));
1015
+
1016
+ if (daysLeft <= 30) {
1017
+ return `Expires in ${daysLeft} day${daysLeft !== 1 ? 's' : ''}`;
1018
+ }
1019
+
1020
+ return 'Active';
1021
+ }
1022
+
1023
+ // Format date
1024
+ function formatDate(dateString) {
1025
+ const options = { year: 'numeric', month: 'long', day: 'numeric' };
1026
+ return new Date(dateString).toLocaleDateString(undefined, options);
1027
+ }
1028
+
1029
+ // Update statistics
1030
+ function updateStats() {
1031
+ totalComputers.textContent = computers.length;
1032
+ activeComputers.textContent = computers.filter(c => c.status === 'Active').length;
1033
+ maintenanceComputers.textContent = computers.filter(c => c.status === 'In Maintenance').length;
1034
+
1035
+ // Count expired warranties
1036
+ const today = new Date();
1037
+ const expiredCount = computers.filter(computer => {
1038
+ const buyDate = new Date(computer.buyDate);
1039
+ const warrantyEnd = new Date(buyDate);
1040
+ warrantyEnd.setMonth(warrantyEnd.getMonth() + computer.warrantyDuration);
1041
+ return warrantyEnd < today;
1042
+ }).length;
1043
+
1044
+ expiredWarranty.textContent = expiredCount;
1045
+ }
1046
+
1047
+ // Check for warranty notifications
1048
+ function checkWarrantyNotifications() {
1049
+ notifications = [];
1050
+ const today = new Date();
1051
+
1052
+ computers.forEach(computer => {
1053
+ const buyDate = new Date(computer.buyDate);
1054
+ const warrantyEnd = new Date(buyDate);
1055
+ warrantyEnd.setMonth(warrantyEnd.getMonth() + computer.warrantyDuration);
1056
+
1057
+ const daysLeft = Math.floor((warrantyEnd - today) / (1000 * 60 * 60 * 24));
1058
+
1059
+ if (daysLeft <= 30 && daysLeft >= 0) {
1060
+ notifications.push({
1061
+ type: 'warning',
1062
+ message: `Warranty for ${computer.brand} ${computer.model} (${computer.serialNumber}) expires in ${daysLeft} day${daysLeft !== 1 ? 's' : ''}`,
1063
+ computerId: computer.id
1064
+ });
1065
+ } else if (warrantyEnd < today) {
1066
+ notifications.push({
1067
+ type: 'error',
1068
+ message: `Warranty for ${computer.brand} ${computer.model} (${computer.serialNumber}) has expired`,
1069
+ computerId: computer.id
1070
+ });
1071
+ }
1072
+ });
1073
+
1074
+ updateNotificationUI();
1075
+ }
1076
+
1077
+ // Update notification UI
1078
+ function updateNotificationUI() {
1079
+ if (notifications.length > 0) {
1080
+ notificationCount.textContent = notifications.length;
1081
+ notificationCount.classList.remove('hidden');
1082
+
1083
+ // Update notification list
1084
+ notificationList.innerHTML = '';
1085
+ notifications.slice(0, 5).forEach(notification => {
1086
+ const notificationItem = document.createElement('div');
1087
+ notificationItem.className = `p-3 border-b hover:bg-gray-50 cursor-pointer ${
1088
+ notification.type === 'error' ? 'text-red-600' : 'text-yellow-600'
1089
+ }`;
1090
+ notificationItem.innerHTML = `
1091
+ <div class="flex items-start">
1092
+ <i class="fas fa-${
1093
+ notification.type === 'error' ? 'exclamation-triangle' : 'exclamation-circle'
1094
+ } mt-1 mr-2"></i>
1095
+ <div>
1096
+ <p class="text-sm">${notification.message}</p>
1097
+ <p class="text-xs text-gray-500 mt-1">Click to view</p>
1098
+ </div>
1099
+ </div>
1100
+ `;
1101
+ notificationItem.addEventListener('click', () => {
1102
+ showComputerDetails(notification.computerId);
1103
+ notificationDropdown.classList.add('hidden');
1104
+ });
1105
+ notificationList.appendChild(notificationItem);
1106
+ });
1107
+
1108
+ if (notifications.length > 5) {
1109
+ const moreItem = document.createElement('div');
1110
+ moreItem.className = 'p-3 text-sm text-center text-gray-500';
1111
+ moreItem.textContent = `+${notifications.length - 5} more notifications`;
1112
+ notificationList.appendChild(moreItem);
1113
+ }
1114
+ } else {
1115
+ notificationCount.classList.add('hidden');
1116
+ notificationList.innerHTML = '<div class="p-3 text-sm text-gray-600">No new notifications</div>';
1117
+ }
1118
+ }
1119
+
1120
+ // Toggle notification dropdown
1121
+ function toggleNotificationDropdown() {
1122
+ notificationDropdown.classList.toggle('hidden');
1123
+ }
1124
+
1125
+ // Show alert
1126
+ function showAlert(message, type) {
1127
+ const alert = document.createElement('div');
1128
+ alert.className = `alert-slide-in px-4 py-3 rounded-md shadow-md flex items-start ${
1129
+ type === 'success' ? 'bg-green-100 text-green-800' :
1130
+ type === 'error' ? 'bg-red-100 text-red-800' :
1131
+ 'bg-blue-100 text-blue-800'
1132
+ }`;
1133
+ alert.innerHTML = `
1134
+ <i class="fas ${
1135
+ type === 'success' ? 'fa-check-circle' :
1136
+ type === 'error' ? 'fa-exclamation-circle' :
1137
+ 'fa-info-circle'
1138
+ } mr-2 mt-0.5"></i>
1139
+ <span>${message}</span>
1140
+ `;
1141
+
1142
+ alertContainer.appendChild(alert);
1143
+
1144
+ // Remove alert after 5 seconds
1145
+ setTimeout(() => {
1146
+ alert.classList.add('alert-fade-out');
1147
+ setTimeout(() => {
1148
+ alert.remove();
1149
+ }, 1000);
1150
+ }, 5000);
1151
+ }
1152
+
1153
+ // Initialize the app
1154
+ document.addEventListener('DOMContentLoaded', init);
1155
+ </script>
1156
+ <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=Darkmind64/inventaire" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1157
+ </html>