Tamime commited on
Commit
b1a8767
·
verified ·
1 Parent(s): 33c90de

faire un tableau Excel - Follow Up Deployment

Browse files
Files changed (1) hide show
  1. index.html +415 -600
index.html CHANGED
@@ -1,663 +1,478 @@
1
  <!DOCTYPE html>
2
- <html lang="fr">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Gestion des Cartes Carburant - Entreprise Algérienne</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
- <script src="https://cdn.sheetjs.com/xlsx-0.19.3/package/dist/xlsx.full.min.js"></script>
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  <style>
11
- .sidebar {
12
- transition: all 0.3s ease;
 
 
13
  }
14
- @media (max-width: 768px) {
15
- .sidebar {
16
- transform: translateX(-100%);
17
- }
18
- .sidebar.active {
19
- transform: translateX(0);
20
- }
 
 
 
 
21
  }
22
- .card-item:hover {
23
- transform: translateY(-5px);
24
- box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
 
 
 
 
 
25
  }
26
- .progress-bar {
27
- transition: width 1s ease-in-out;
 
 
 
 
 
 
 
 
 
28
  }
29
  </style>
30
  </head>
31
- <body class="bg-gray-100 font-sans">
32
- <!-- Sidebar Toggle for Mobile -->
33
- <div class="md:hidden fixed top-4 left-4 z-50">
34
- <button id="sidebarToggle" class="p-2 rounded-lg bg-blue-600 text-white">
35
- <i class="fas fa-bars"></i>
36
- </button>
37
- </div>
38
-
39
- <!-- Sidebar -->
40
- <div id="sidebar" class="sidebar fixed inset-y-0 left-0 w-64 bg-blue-800 text-white p-4 shadow-lg">
41
- <div class="flex items-center justify-between mb-8">
42
- <div class="flex items-center space-x-2">
43
- <i class="fas fa-gas-pump text-2xl"></i>
44
- <h1 class="text-xl font-bold">Gestion Carburant</h1>
 
 
45
  </div>
46
- </div>
47
-
48
- <div class="mb-6 p-4 bg-blue-700 rounded-lg">
49
  <div class="flex items-center space-x-3">
50
- <div class="w-10 h-10 rounded-full bg-blue-500 flex items-center justify-center">
51
- <i class="fas fa-user"></i>
52
- </div>
53
- <div>
54
- <p class="font-medium" id="usernameDisplay">Admin</p>
55
- <p class="text-xs text-blue-200">Administrateur</p>
56
- </div>
57
  </div>
58
  </div>
59
-
60
- <nav>
61
- <ul class="space-y-2">
62
- <li>
63
- <a href="#" class="flex items-center space-x-3 p-3 rounded-lg bg-blue-700">
64
- <i class="fas fa-tachometer-alt"></i>
65
- <span>Tableau de bord</span>
66
- </a>
67
- </li>
68
- <li>
69
- <a href="#" class="flex items-center space-x-3 p-3 rounded-lg hover:bg-blue-700 transition">
70
- <i class="fas fa-id-card"></i>
71
- <span>Cartes carburant</span>
72
- </a>
73
- </li>
74
- <li>
75
- <a href="#" class="flex items-center space-x-3 p-3 rounded-lg hover:bg-blue-700 transition">
76
- <i class="fas fa-users"></i>
77
- <span>Utilisateurs</span>
78
- </a>
79
- </li>
80
- <li>
81
- <a href="#" class="flex items-center space-x-3 p-3 rounded-lg hover:bg-blue-700 transition">
82
- <i class="fas fa-chart-line"></i>
83
- <span>Statistiques</span>
84
- </a>
85
- </li>
86
- <li>
87
- <a href="#" class="flex items-center space-x-3 p-3 rounded-lg hover:bg-blue-700 transition">
88
- <i class="fas fa-cog"></i>
89
- <span>Paramètres</span>
90
- </a>
91
- </li>
92
- </ul>
93
- </nav>
94
-
95
- <div class="absolute bottom-4 left-4 right-4">
96
- <button class="w-full flex items-center space-x-3 p-3 rounded-lg hover:bg-blue-700 transition">
97
- <i class="fas fa-sign-out-alt"></i>
98
- <span>Déconnexion</span>
99
- </button>
100
- </div>
101
- </div>
102
-
103
- <!-- Main Content -->
104
- <div class="md:ml-64 min-h-screen">
105
- <!-- Header -->
106
- <header class="bg-white shadow-sm p-4">
107
- <div class="flex justify-between items-center">
108
- <h2 class="text-xl font-semibold text-gray-800">Gestion des Cartes Carburant</h2>
109
- <div class="flex items-center space-x-4">
110
- <div class="relative">
111
- <i class="fas fa-bell text-gray-500"></i>
112
- <span class="absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full"></span>
113
- </div>
114
- <div class="flex items-center space-x-2">
115
- <div class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center">
116
- <i class="fas fa-user text-blue-600"></i>
117
- </div>
118
- <span class="text-sm font-medium">Admin</span>
119
- </div>
120
- </div>
121
  </div>
122
- </header>
123
-
124
- <!-- Content -->
125
- <main class="p-4">
126
- <!-- Stats Cards -->
127
- <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
128
- <div class="bg-white p-4 rounded-lg shadow">
129
- <div class="flex justify-between">
130
- <div>
131
- <p class="text-gray-500">Cartes actives</p>
132
- <h3 class="text-2xl font-bold" id="activeCardsCount">0</h3>
133
- </div>
134
- <div class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center">
135
- <i class="fas fa-check-circle text-green-600"></i>
136
- </div>
137
- </div>
138
- </div>
139
- <div class="bg-white p-4 rounded-lg shadow">
140
- <div class="flex justify-between">
141
- <div>
142
- <p class="text-gray-500">Crédit total</p>
143
- <h3 class="text-2xl font-bold" id="totalCredit">0 DA</h3>
144
- </div>
145
- <div class="w-12 h-12 rounded-full bg-blue-100 flex items-center justify-center">
146
- <i class="fas fa-coins text-blue-600"></i>
147
- </div>
148
- </div>
149
- </div>
150
- <div class="bg-white p-4 rounded-lg shadow">
151
- <div class="flex justify-between">
152
- <div>
153
- <p class="text-gray-500">Cartes expirées</p>
154
- <h3 class="text-2xl font-bold" id="expiredCardsCount">0</h3>
155
- </div>
156
- <div class="w-12 h-12 rounded-full bg-red-100 flex items-center justify-center">
157
- <i class="fas fa-exclamation-circle text-red-600"></i>
158
- </div>
159
- </div>
160
- </div>
161
  </div>
162
-
163
- <!-- Actions -->
164
- <div class="bg-white p-4 rounded-lg shadow mb-6">
165
- <div class="flex flex-wrap justify-between items-center gap-4">
166
- <h3 class="text-lg font-semibold">Actions rapides</h3>
167
- <div class="flex flex-wrap gap-2">
168
- <button id="addCardBtn" class="px-4 py-2 bg-blue-600 text-white rounded-lg flex items-center space-x-2 hover:bg-blue-700 transition">
169
- <i class="fas fa-plus"></i>
170
- <span>Ajouter une carte</span>
171
- </button>
172
- <button id="exportBtn" class="px-4 py-2 bg-green-600 text-white rounded-lg flex items-center space-x-2 hover:bg-green-700 transition">
173
- <i class="fas fa-file-export"></i>
174
- <span>Exporter Excel</span>
175
- </button>
176
- <label for="importFile" class="px-4 py-2 bg-purple-600 text-white rounded-lg flex items-center space-x-2 hover:bg-purple-700 transition cursor-pointer">
177
- <i class="fas fa-file-import"></i>
178
- <span>Importer Excel</span>
179
- <input type="file" id="importFile" accept=".xlsx,.xls" class="hidden">
180
- </label>
181
- </div>
182
- </div>
 
 
 
 
 
 
183
  </div>
 
 
184
 
185
- <!-- Cards List -->
186
- <div class="bg-white p-4 rounded-lg shadow">
187
- <div class="flex justify-between items-center mb-4">
188
- <h3 class="text-lg font-semibold">Liste des cartes carburant</h3>
189
- <div class="relative">
190
- <input type="text" id="searchInput" placeholder="Rechercher..." class="pl-10 pr-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
191
- <i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
192
- </div>
193
- </div>
194
 
195
- <div class="overflow-x-auto">
196
- <table class="min-w-full divide-y divide-gray-200">
197
- <thead class="bg-gray-50">
 
 
 
 
 
198
  <tr>
199
- <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">N° Carte</th>
200
- <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Utilisateur</th>
201
- <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Matricule</th>
202
- <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Crédit (DA)</th>
203
- <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date expiration</th>
204
- <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Statut</th>
205
- <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
206
  </tr>
207
  </thead>
208
- <tbody id="cardsTableBody" class="bg-white divide-y divide-gray-200">
209
- <!-- Cards will be added here dynamically -->
210
  </tbody>
211
  </table>
212
  </div>
213
-
214
- <div class="mt-4 flex justify-between items-center">
215
- <div class="text-sm text-gray-500">
216
- Affichage de <span id="startItem">1</span> à <span id="endItem">10</span> sur <span id="totalItems">0</span> cartes
 
 
 
 
217
  </div>
218
  <div class="flex space-x-2">
219
- <button id="prevPage" class="px-3 py-1 border rounded-lg disabled:opacity-50" disabled>
220
- <i class="fas fa-chevron-left"></i>
221
- </button>
222
- <button id="nextPage" class="px-3 py-1 border rounded-lg disabled:opacity-50" disabled>
223
- <i class="fas fa-chevron-right"></i>
224
- </button>
225
  </div>
226
  </div>
227
  </div>
228
- </main>
229
- </div>
230
-
231
- <!-- Add/Edit Card Modal -->
232
- <div id="cardModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
233
- <div class="bg-white rounded-lg shadow-xl w-full max-w-md">
234
- <div class="flex justify-between items-center border-b p-4">
235
- <h3 class="text-lg font-semibold" id="modalTitle">Ajouter une carte</h3>
236
- <button id="closeModalBtn" class="text-gray-500 hover:text-gray-700">
237
- <i class="fas fa-times"></i>
238
- </button>
239
- </div>
240
- <div class="p-4">
241
- <form id="cardForm">
242
- <input type="hidden" id="cardId">
243
- <div class="mb-4">
244
- <label for="cardNumber" class="block text-sm font-medium text-gray-700 mb-1">Numéro de carte</label>
245
- <input type="text" id="cardNumber" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" required>
246
- </div>
247
- <div class="mb-4">
248
- <label for="userName" class="block text-sm font-medium text-gray-700 mb-1">Nom de l'utilisateur</label>
249
- <input type="text" id="userName" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" required>
250
- </div>
251
- <div class="mb-4">
252
- <label for="userMatricule" class="block text-sm font-medium text-gray-700 mb-1">Matricule</label>
253
- <input type="text" id="userMatricule" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" required>
254
- </div>
255
- <div class="mb-4">
256
- <label for="cardCredit" class="block text-sm font-medium text-gray-700 mb-1">Crédit (DA)</label>
257
- <input type="number" id="cardCredit" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" required>
258
- </div>
259
- <div class="mb-4">
260
- <label for="expiryDate" class="block text-sm font-medium text-gray-700 mb-1">Date d'expiration</label>
261
- <input type="date" id="expiryDate" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" required>
262
- </div>
263
- <div class="mb-4">
264
- <label for="cardStatus" class="block text-sm font-medium text-gray-700 mb-1">Statut</label>
265
- <select id="cardStatus" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
266
- <option value="active">Active</option>
267
- <option value="inactive">Inactive</option>
268
- <option value="expired">Expirée</option>
269
- </select>
270
- </div>
271
- </form>
272
- </div>
273
- <div class="flex justify-end space-x-3 p-4 border-t">
274
- <button id="cancelModalBtn" class="px-4 py-2 border rounded-lg hover:bg-gray-100 transition">Annuler</button>
275
- <button id="saveCardBtn" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition">Enregistrer</button>
276
- </div>
277
- </div>
278
- </div>
279
-
280
- <!-- Confirmation Modal -->
281
- <div id="confirmModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
282
- <div class="bg-white rounded-lg shadow-xl w-full max-w-md">
283
- <div class="flex justify-between items-center border-b p-4">
284
- <h3 class="text-lg font-semibold">Confirmation</h3>
285
- <button id="closeConfirmModalBtn" class="text-gray-500 hover:text-gray-700">
286
- <i class="fas fa-times"></i>
287
- </button>
288
- </div>
289
- <div class="p-4">
290
- <p id="confirmMessage">Êtes-vous sûr de vouloir supprimer cette carte ?</p>
291
- </div>
292
- <div class="flex justify-end space-x-3 p-4 border-t">
293
- <button id="cancelConfirmBtn" class="px-4 py-2 border rounded-lg hover:bg-gray-100 transition">Non</button>
294
- <button id="confirmActionBtn" class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition">Oui</button>
295
  </div>
296
  </div>
297
  </div>
298
 
299
  <script>
300
- // Sample data
301
- let cards = [
302
- { id: 1, number: 'CARD-001', userName: 'Mohamed Benali', matricule: 'EMP-001', credit: 15000, expiryDate: '2023-12-31', status: 'active' },
303
- { id: 2, number: 'CARD-002', userName: 'Karim Boukherouba', matricule: 'EMP-002', credit: 20000, expiryDate: '2023-11-30', status: 'active' },
304
- { id: 3, number: 'CARD-003', userName: 'Nadia Cherif', matricule: 'EMP-003', credit: 12000, expiryDate: '2023-10-15', status: 'expired' },
305
- { id: 4, number: 'CARD-004', userName: 'Samir Zitouni', matricule: 'EMP-004', credit: 18000, expiryDate: '2024-01-20', status: 'active' },
306
- { id: 5, number: 'CARD-005', userName: 'Leila Hammadi', matricule: 'EMP-005', credit: 9000, expiryDate: '2023-09-01', status: 'inactive' },
307
- { id: 6, number: 'CARD-006', userName: 'Youssef Kaci', matricule: 'EMP-006', credit: 25000, expiryDate: '2024-02-28', status: 'active' },
308
- { id: 7, number: 'CARD-007', userName: 'Amine Saadi', matricule: 'EMP-007', credit: 17000, expiryDate: '2023-08-15', status: 'expired' },
309
- { id: 8, number: 'CARD-008', userName: 'Houda Belkacem', matricule: 'EMP-008', credit: 14000, expiryDate: '2024-03-10', status: 'active' },
310
- { id: 9, number: 'CARD-009', userName: 'Farid Bensaid', matricule: 'EMP-009', credit: 11000, expiryDate: '2023-07-30', status: 'inactive' },
311
- { id: 10, number: 'CARD-010', userName: 'Sonia Touati', matricule: 'EMP-010', credit: 22000, expiryDate: '2024-04-05', status: 'active' }
312
- ];
313
-
314
- // DOM Elements
315
- const sidebar = document.getElementById('sidebar');
316
- const sidebarToggle = document.getElementById('sidebarToggle');
317
- const cardModal = document.getElementById('cardModal');
318
- const confirmModal = document.getElementById('confirmModal');
319
- const addCardBtn = document.getElementById('addCardBtn');
320
- const closeModalBtn = document.getElementById('closeModalBtn');
321
- const cancelModalBtn = document.getElementById('cancelModalBtn');
322
- const saveCardBtn = document.getElementById('saveCardBtn');
323
- const closeConfirmModalBtn = document.getElementById('closeConfirmModalBtn');
324
- const cancelConfirmBtn = document.getElementById('cancelConfirmBtn');
325
- const confirmActionBtn = document.getElementById('confirmActionBtn');
326
- const exportBtn = document.getElementById('exportBtn');
327
- const importFile = document.getElementById('importFile');
328
- const searchInput = document.getElementById('searchInput');
329
- const cardsTableBody = document.getElementById('cardsTableBody');
330
- const activeCardsCount = document.getElementById('activeCardsCount');
331
- const totalCredit = document.getElementById('totalCredit');
332
- const expiredCardsCount = document.getElementById('expiredCardsCount');
333
- const prevPageBtn = document.getElementById('prevPage');
334
- const nextPageBtn = document.getElementById('nextPage');
335
- const startItem = document.getElementById('startItem');
336
- const endItem = document.getElementById('endItem');
337
- const totalItems = document.getElementById('totalItems');
338
-
339
- // Variables
340
- let currentPage = 1;
341
- const itemsPerPage = 5;
342
- let currentAction = '';
343
- let cardToDelete = null;
344
- let filteredCards = [...cards];
345
-
346
- // Initialize
347
- document.addEventListener('DOMContentLoaded', () => {
348
- updateStats();
349
- renderCards();
350
- setupEventListeners();
351
- });
352
-
353
- // Setup event listeners
354
- function setupEventListeners() {
355
- // Sidebar toggle for mobile
356
- sidebarToggle.addEventListener('click', () => {
357
- sidebar.classList.toggle('active');
358
- });
359
-
360
- // Add card button
361
- addCardBtn.addEventListener('click', () => {
362
- document.getElementById('modalTitle').textContent = 'Ajouter une carte';
363
- document.getElementById('cardForm').reset();
364
- document.getElementById('cardId').value = '';
365
- currentAction = 'add';
366
- cardModal.classList.remove('hidden');
367
- });
368
-
369
- // Close modal buttons
370
- closeModalBtn.addEventListener('click', () => cardModal.classList.add('hidden'));
371
- cancelModalBtn.addEventListener('click', () => cardModal.classList.add('hidden'));
372
- closeConfirmModalBtn.addEventListener('click', () => confirmModal.classList.add('hidden'));
373
- cancelConfirmBtn.addEventListener('click', () => confirmModal.classList.add('hidden'));
374
-
375
- // Save card
376
- saveCardBtn.addEventListener('click', () => {
377
- const form = document.getElementById('cardForm');
378
- if (!form.checkValidity()) {
379
- form.reportValidity();
380
- return;
381
  }
382
-
383
- const cardData = {
384
- id: document.getElementById('cardId').value ? parseInt(document.getElementById('cardId').value) : cards.length + 1,
385
- number: document.getElementById('cardNumber').value,
386
- userName: document.getElementById('userName').value,
387
- matricule: document.getElementById('userMatricule').value,
388
- credit: parseInt(document.getElementById('cardCredit').value),
389
- expiryDate: document.getElementById('expiryDate').value,
390
- status: document.getElementById('cardStatus').value
391
- };
392
-
393
- if (currentAction === 'add') {
394
- cards.push(cardData);
395
- } else {
396
- const index = cards.findIndex(c => c.id === cardData.id);
397
- if (index !== -1) {
398
- cards[index] = cardData;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  }
 
 
400
  }
401
-
402
- updateStats();
403
- renderCards();
404
- cardModal.classList.add('hidden');
405
- });
406
-
407
- // Export to Excel
408
- exportBtn.addEventListener('click', exportToExcel);
409
-
410
- // Import from Excel
411
- importFile.addEventListener('change', importFromExcel);
412
-
413
- // Search
414
- searchInput.addEventListener('input', () => {
415
- const searchTerm = searchInput.value.toLowerCase();
416
- if (searchTerm === '') {
417
- filteredCards = [...cards];
418
- } else {
419
- filteredCards = cards.filter(card =>
420
- card.number.toLowerCase().includes(searchTerm) ||
421
- card.userName.toLowerCase().includes(searchTerm) ||
422
- card.matricule.toLowerCase().includes(searchTerm)
423
- );
424
  }
425
- currentPage = 1;
426
- renderCards();
427
- });
428
-
429
- // Pagination
430
- prevPageBtn.addEventListener('click', () => {
431
- if (currentPage > 1) {
432
- currentPage--;
433
- renderCards();
434
  }
435
- });
436
-
437
- nextPageBtn.addEventListener('click', () => {
438
- const totalPages = Math.ceil(filteredCards.length / itemsPerPage);
439
- if (currentPage < totalPages) {
440
- currentPage++;
441
- renderCards();
 
 
 
 
442
  }
443
- });
444
- }
445
-
446
- // Render cards table
447
- function renderCards() {
448
- const startIndex = (currentPage - 1) * itemsPerPage;
449
- const endIndex = startIndex + itemsPerPage;
450
- const paginatedCards = filteredCards.slice(startIndex, endIndex);
451
-
452
- cardsTableBody.innerHTML = '';
453
-
454
- if (paginatedCards.length === 0) {
455
- const row = document.createElement('tr');
456
- row.innerHTML = `
457
- <td colspan="7" class="px-6 py-4 text-center text-gray-500">Aucune carte trouvée</td>
458
- `;
459
- cardsTableBody.appendChild(row);
460
- } else {
461
- paginatedCards.forEach(card => {
462
- const row = document.createElement('tr');
463
- row.className = 'hover:bg-gray-50';
464
 
465
- // Status badge
466
- let statusClass = '';
467
- let statusText = '';
468
- switch (card.status) {
469
- case 'active':
470
- statusClass = 'bg-green-100 text-green-800';
471
- statusText = 'Active';
472
- break;
473
- case 'inactive':
474
- statusClass = 'bg-gray-100 text-gray-800';
475
- statusText = 'Inactive';
476
- break;
477
- case 'expired':
478
- statusClass = 'bg-red-100 text-red-800';
479
- statusText = 'Expirée';
480
- break;
 
 
 
 
 
 
 
 
481
  }
482
-
483
- row.innerHTML = `
484
- <td class="px-6 py-4 whitespace-nowrap font-medium">${card.number}</td>
485
- <td class="px-6 py-4 whitespace-nowrap">${card.userName}</td>
486
- <td class="px-6 py-4 whitespace-nowrap">${card.matricule}</td>
487
- <td class="px-6 py-4 whitespace-nowrap font-semibold">${card.credit.toLocaleString()} DA</td>
488
- <td class="px-6 py-4 whitespace-nowrap">${formatDate(card.expiryDate)}</td>
489
- <td class="px-6 py-4 whitespace-nowrap">
490
- <span class="px-2 py-1 text-xs rounded-full ${statusClass}">${statusText}</span>
491
- </td>
492
- <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
493
- <button onclick="editCard(${card.id})" class="text-blue-600 hover:text-blue-900 mr-3">
494
- <i class="fas fa-edit"></i>
495
- </button>
496
- <button onclick="deleteCard(${card.id})" class="text-red-600 hover:text-red-900">
497
- <i class="fas fa-trash"></i>
498
- </button>
499
- </td>
500
- `;
501
- cardsTableBody.appendChild(row);
502
  });
503
- }
504
-
505
- // Update pagination info
506
- const totalItemsCount = filteredCards.length;
507
- startItem.textContent = startIndex + 1;
508
- endItem.textContent = Math.min(endIndex, totalItemsCount);
509
- totalItems.textContent = totalItemsCount;
510
-
511
- // Update pagination buttons
512
- prevPageBtn.disabled = currentPage === 1;
513
- nextPageBtn.disabled = currentPage === Math.ceil(totalItemsCount / itemsPerPage);
514
- }
515
-
516
- // Update statistics
517
- function updateStats() {
518
- const activeCount = cards.filter(card => card.status === 'active').length;
519
- const expiredCount = cards.filter(card => card.status === 'expired').length;
520
- const total = cards.reduce((sum, card) => sum + card.credit, 0);
521
-
522
- activeCardsCount.textContent = activeCount;
523
- expiredCardsCount.textContent = expiredCount;
524
- totalCredit.textContent = total.toLocaleString();
525
- }
526
-
527
- // Format date
528
- function formatDate(dateString) {
529
- const options = { year: 'numeric', month: 'short', day: 'numeric' };
530
- return new Date(dateString).toLocaleDateString('fr-FR', options);
531
- }
532
-
533
- // Edit card
534
- window.editCard = function(id) {
535
- const card = cards.find(c => c.id === id);
536
- if (card) {
537
- document.getElementById('modalTitle').textContent = 'Modifier la carte';
538
- document.getElementById('cardId').value = card.id;
539
- document.getElementById('cardNumber').value = card.number;
540
- document.getElementById('userName').value = card.userName;
541
- document.getElementById('userMatricule').value = card.matricule;
542
- document.getElementById('cardCredit').value = card.credit;
543
- document.getElementById('expiryDate').value = card.expiryDate;
544
- document.getElementById('cardStatus').value = card.status;
545
 
546
- currentAction = 'edit';
547
- cardModal.classList.remove('hidden');
548
- }
549
- };
550
-
551
- // Delete card
552
- window.deleteCard = function(id) {
553
- cardToDelete = id;
554
- document.getElementById('confirmMessage').textContent = 'Êtes-vous sûr de vouloir supprimer cette carte ?';
555
- currentAction = 'delete';
556
- confirmModal.classList.remove('hidden');
557
- };
558
-
559
- // Confirm action
560
- confirmActionBtn.addEventListener('click', () => {
561
- if (currentAction === 'delete' && cardToDelete) {
562
- cards = cards.filter(card => card.id !== cardToDelete);
563
- filteredCards = filteredCards.filter(card => card.id !== cardToDelete);
564
- updateStats();
565
- renderCards();
 
 
 
566
  }
567
- confirmModal.classList.add('hidden');
568
- });
569
-
570
- // Export to Excel
571
- function exportToExcel() {
572
- const data = [
573
- ['N° Carte', 'Utilisateur', 'Matricule', 'Crédit (DA)', 'Date expiration', 'Statut'],
574
- ...cards.map(card => [
575
- card.number,
576
- card.userName,
577
- card.matricule,
578
- card.credit,
579
- formatDate(card.expiryDate),
580
- card.status === 'active' ? 'Active' : card.status === 'inactive' ? 'Inactive' : 'Expirée'
581
- ])
582
- ];
583
-
584
- const ws = XLSX.utils.aoa_to_sheet(data);
585
- const wb = XLSX.utils.book_new();
586
- XLSX.utils.book_append_sheet(wb, ws, 'CartesCarburant');
587
 
588
- // Generate current date string for filename
589
- const today = new Date();
590
- const dateStr = `${today.getFullYear()}-${(today.getMonth()+1).toString().padStart(2, '0')}-${today.getDate().toString().padStart(2, '0')}`;
 
 
 
 
 
 
 
 
 
 
591
 
592
- XLSX.writeFile(wb, `Cartes_Carburant_${dateStr}.xlsx`);
593
- }
594
-
595
- // Import from Excel
596
- function importFromExcel(event) {
597
- const file = event.target.files[0];
598
- if (!file) return;
599
-
600
- const reader = new FileReader();
601
- reader.onload = function(e) {
602
- const data = new Uint8Array(e.target.result);
603
- const workbook = XLSX.read(data, { type: 'array' });
604
 
605
- // Get first worksheet
606
- const worksheetName = workbook.SheetNames[0];
607
- const worksheet = workbook.Sheets[worksheetName];
 
608
 
609
- // Convert to JSON
610
- const jsonData = XLSX.utils.sheet_to_json(worksheet);
 
611
 
612
- // Map Excel data to our card structure
613
- const importedCards = jsonData.map((row, index) => {
614
- // Try to find the correct columns (Excel files might have different headers)
615
- const number = row['N° Carte'] || row['Numéro'] || row['Carte'] || `CARD-IMPORT-${index+1}`;
616
- const userName = row['Utilisateur'] || row['Nom'] || row['Employé'] || 'Inconnu';
617
- const matricule = row['Matricule'] || row['ID'] || `EMP-IMPORT-${index+1}`;
618
- const credit = parseInt(row['Crédit (DA)'] || row['Crédit'] || row['Montant'] || 0);
619
-
620
- // Try to parse date (Excel dates might be stored as numbers)
621
- let expiryDate = row['Date expiration'] || row['Expiration'] || '2023-12-31';
622
- if (typeof expiryDate === 'number') {
623
- // Convert Excel date number to JS date
624
- const date = new Date((expiryDate - (25567 + 2)) * 86400 * 1000);
625
- expiryDate = date.toISOString().split('T')[0];
626
  }
627
-
628
- const status = row['Statut'] || 'active';
629
-
630
- return {
631
- id: cards.length + index + 1,
632
- number,
633
- userName,
634
- matricule,
635
- credit,
636
- expiryDate,
637
- status: status.toLowerCase()
638
- };
639
  });
640
-
641
- // Add imported cards to our data
642
- cards = [...cards, ...importedCards];
643
- filteredCards = [...cards];
 
 
644
 
645
- // Reset search and pagination
646
- searchInput.value = '';
647
- currentPage = 1;
 
648
 
649
- // Update UI
650
- updateStats();
651
- renderCards();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
652
 
653
- // Show success message
654
- alert(`${importedCards.length} cartes ont été importées avec succès !`);
655
- };
656
- reader.readAsArrayBuffer(file);
657
 
658
- // Reset file input
659
- event.target.value = '';
660
- }
661
  </script>
662
  <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=Tamime/mycards" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
663
  </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>Excel-like Spreadsheet</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
+ <script>
10
+ tailwind.config = {
11
+ theme: {
12
+ extend: {
13
+ colors: {
14
+ 'excel-green': '#217346',
15
+ 'excel-light': '#f8f9fa',
16
+ 'excel-border': '#d9d9d9',
17
+ 'excel-header': '#e7e9eb',
18
+ 'excel-selected': '#d5e8d4',
19
+ 'excel-hover': '#f0f7ff'
20
+ }
21
+ }
22
+ }
23
+ }
24
+ </script>
25
  <style>
26
+ .cell:focus {
27
+ outline: 2px solid #217346;
28
+ outline-offset: -1px;
29
+ z-index: 10;
30
  }
31
+ .selected {
32
+ background-color: #d5e8d4 !important;
33
+ }
34
+ .resize-handle {
35
+ position: absolute;
36
+ right: -2px;
37
+ bottom: -2px;
38
+ width: 8px;
39
+ height: 8px;
40
+ cursor: nwse-resize;
41
+ z-index: 20;
42
  }
43
+ .col-resize {
44
+ position: absolute;
45
+ top: 0;
46
+ right: -2px;
47
+ width: 4px;
48
+ height: 100%;
49
+ cursor: col-resize;
50
+ z-index: 20;
51
  }
52
+ .row-resize {
53
+ position: absolute;
54
+ bottom: -2px;
55
+ left: 0;
56
+ height: 4px;
57
+ width: 100%;
58
+ cursor: row-resize;
59
+ z-index: 20;
60
+ }
61
+ .formula-bar:focus {
62
+ outline: 2px solid #217346;
63
  }
64
  </style>
65
  </head>
66
+ <body class="bg-gray-100 min-h-screen flex flex-col">
67
+ <!-- Header -->
68
+ <header class="bg-excel-green text-white p-3 shadow-md">
69
+ <div class="container mx-auto flex justify-between items-center">
70
+ <div class="flex items-center space-x-4">
71
+ <h1 class="text-xl font-bold flex items-center">
72
+ <i class="fas fa-file-excel mr-2"></i>Excel Clone
73
+ </h1>
74
+ <div class="hidden md:flex space-x-2">
75
+ <button class="px-3 py-1 hover:bg-green-600 rounded">File</button>
76
+ <button class="px-3 py-1 hover:bg-green-600 rounded">Home</button>
77
+ <button class="px-3 py-1 hover:bg-green-600 rounded">Insert</button>
78
+ <button class="px-3 py-1 hover:bg-green-600 rounded">Formulas</button>
79
+ <button class="px-3 py-1 hover:bg-green-600 rounded">Data</button>
80
+ <button class="px-3 py-1 hover:bg-green-600 rounded">View</button>
81
+ </div>
82
  </div>
 
 
 
83
  <div class="flex items-center space-x-3">
84
+ <button class="bg-green-600 hover:bg-green-500 px-3 py-1 rounded">
85
+ <i class="fas fa-user-plus mr-1"></i> Share
86
+ </button>
87
+ <button class="bg-green-600 hover:bg-green-500 px-3 py-1 rounded">
88
+ <i class="fas fa-user mr-1"></i> Account
89
+ </button>
 
90
  </div>
91
  </div>
92
+ </header>
93
+
94
+ <!-- Toolbar -->
95
+ <div class="bg-excel-header border-b border-excel-border">
96
+ <div class="container mx-auto py-2 px-3 flex flex-wrap gap-2">
97
+ <div class="flex items-center space-x-1">
98
+ <button class="p-1.5 hover:bg-gray-200 rounded">
99
+ <i class="fas fa-font"></i>
100
+ </button>
101
+ <button class="p-1.5 hover:bg-gray-200 rounded">
102
+ <i class="fas fa-bold"></i>
103
+ </button>
104
+ <button class="p-1.5 hover:bg-gray-200 rounded">
105
+ <i class="fas fa-italic"></i>
106
+ </button>
107
+ <button class="p-1.5 hover:bg-gray-200 rounded">
108
+ <i class="fas fa-underline"></i>
109
+ </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  </div>
111
+
112
+ <div class="border-l border-gray-300 mx-2 h-6"></div>
113
+
114
+ <div class="flex items-center space-x-1">
115
+ <button class="p-1.5 hover:bg-gray-200 rounded">
116
+ <i class="fas fa-align-left"></i>
117
+ </button>
118
+ <button class="p-1.5 hover:bg-gray-200 rounded">
119
+ <i class="fas fa-align-center"></i>
120
+ </button>
121
+ <button class="p-1.5 hover:bg-gray-200 rounded">
122
+ <i class="fas fa-align-right"></i>
123
+ </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  </div>
125
+
126
+ <div class="border-l border-gray-300 mx-2 h-6"></div>
127
+
128
+ <div class="flex items-center space-x-1">
129
+ <button class="p-1.5 hover:bg-gray-200 rounded">
130
+ <i class="fas fa-border-all"></i>
131
+ </button>
132
+ <button class="p-1.5 hover:bg-gray-200 rounded">
133
+ <i class="fas fa-paint-brush"></i>
134
+ </button>
135
+ <button class="p-1.5 hover:bg-gray-200 rounded">
136
+ <i class="fas fa-calculator"></i>
137
+ </button>
138
+ </div>
139
+
140
+ <div class="border-l border-gray-300 mx-2 h-6"></div>
141
+
142
+ <div class="flex items-center space-x-1">
143
+ <button class="p-1.5 hover:bg-gray-200 rounded">
144
+ <i class="fas fa-plus"></i> Row
145
+ </button>
146
+ <button class="p-1.5 hover:bg-gray-200 rounded">
147
+ <i class="fas fa-plus"></i> Column
148
+ </button>
149
+ <button class="p-1.5 hover:bg-gray-200 rounded">
150
+ <i class="fas fa-trash"></i>
151
+ </button>
152
  </div>
153
+ </div>
154
+ </div>
155
 
156
+ <!-- Formula Bar -->
157
+ <div class="bg-excel-header border-b border-excel-border py-1 px-3">
158
+ <div class="container mx-auto flex items-center">
159
+ <div class="font-mono text-sm w-10 text-center">fx</div>
160
+ <input type="text" class="formula-bar w-full border border-gray-300 px-2 py-1 font-mono text-sm"
161
+ placeholder="Enter formula or value">
162
+ </div>
163
+ </div>
 
164
 
165
+ <!-- Spreadsheet Container -->
166
+ <div class="flex-grow overflow-auto">
167
+ <div class="container mx-auto p-3">
168
+ <div class="bg-white shadow-lg rounded-lg overflow-hidden">
169
+ <!-- Spreadsheet -->
170
+ <div class="overflow-auto max-h-[70vh] relative" id="spreadsheet-container">
171
+ <table class="border-collapse" id="spreadsheet">
172
+ <thead>
173
  <tr>
174
+ <th class="w-12 h-8 bg-excel-header border border-excel-border sticky top-0 left-0 z-20"></th>
175
+ <!-- Column headers (A, B, C, ...) will be generated by JS -->
 
 
 
 
 
176
  </tr>
177
  </thead>
178
+ <tbody>
179
+ <!-- Rows will be generated by JS -->
180
  </tbody>
181
  </table>
182
  </div>
183
+
184
+ <!-- Status Bar -->
185
+ <div class="bg-excel-header border-t border-excel-border py-1 px-3 flex justify-between text-sm">
186
+ <div class="flex space-x-4">
187
+ <span>Ready</span>
188
+ <span>Average: 0</span>
189
+ <span>Count: 0</span>
190
+ <span>Sum: 0</span>
191
  </div>
192
  <div class="flex space-x-2">
193
+ <button class="px-2 hover:bg-gray-200 rounded">100%</button>
194
+ <button class="px-2 hover:bg-gray-200 rounded">Page Layout</button>
195
+ <button class="px-2 hover:bg-gray-200 rounded">Normal</button>
 
 
 
196
  </div>
197
  </div>
198
  </div>
199
+
200
+ <!-- Sample Data Notice -->
201
+ <div class="mt-4 p-4 bg-blue-50 rounded-lg border border-blue-200">
202
+ <h3 class="font-bold text-blue-800 mb-2">How to use:</h3>
203
+ <ul class="list-disc pl-5 text-blue-700 space-y-1">
204
+ <li>Click on any cell to edit its content</li>
205
+ <li>Double-click to edit directly in the cell</li>
206
+ <li>Use the formula bar for complex inputs</li>
207
+ <li>Drag the bottom-right corner of a cell to copy its content</li>
208
+ <li>Try basic formulas: =SUM(A1:B1), =AVERAGE(A1:A5)</li>
209
+ </ul>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  </div>
211
  </div>
212
  </div>
213
 
214
  <script>
215
+ document.addEventListener('DOMContentLoaded', function() {
216
+ // Configuration
217
+ const ROWS = 50;
218
+ const COLS = 15;
219
+ const COL_WIDTH = 100;
220
+ const ROW_HEIGHT = 30;
221
+
222
+ const spreadsheet = document.getElementById('spreadsheet');
223
+ const spreadsheetContainer = document.getElementById('spreadsheet-container');
224
+ const formulaBar = document.querySelector('.formula-bar');
225
+
226
+ // Data storage
227
+ let sheetData = {};
228
+ let selectedCell = null;
229
+
230
+ // Generate column headers (A, B, C, ...)
231
+ function generateColumnHeaders() {
232
+ const headerRow = spreadsheet.querySelector('thead tr');
233
+
234
+ // Add empty corner cell
235
+ headerRow.innerHTML = '<th class="w-12 h-8 bg-excel-header border border-excel-border sticky top-0 left-0 z-20"></th>';
236
+
237
+ for (let col = 0; col < COLS; col++) {
238
+ const colName = numberToLetters(col);
239
+ const th = document.createElement('th');
240
+ th.className = 'bg-excel-header border border-excel-border text-center font-medium sticky top-0 z-10';
241
+ th.textContent = colName;
242
+ th.style.minWidth = `${COL_WIDTH}px`;
243
+ th.style.width = `${COL_WIDTH}px`;
244
+
245
+ // Add resize handle
246
+ const resizeHandle = document.createElement('div');
247
+ resizeHandle.className = 'col-resize bg-excel-green hover:bg-green-600';
248
+ resizeHandle.dataset.col = col;
249
+ th.appendChild(resizeHandle);
250
+
251
+ headerRow.appendChild(th);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  }
253
+ }
254
+
255
+ // Generate rows
256
+ function generateRows() {
257
+ const tbody = spreadsheet.querySelector('tbody');
258
+ tbody.innerHTML = '';
259
+
260
+ for (let row = 0; row < ROWS; row++) {
261
+ const tr = document.createElement('tr');
262
+
263
+ // Add row header
264
+ const rowHeader = document.createElement('th');
265
+ rowHeader.className = 'bg-excel-header border border-excel-border text-center font-medium sticky left-0 z-10';
266
+ rowHeader.textContent = row + 1;
267
+ rowHeader.style.height = `${ROW_HEIGHT}px`;
268
+
269
+ // Add resize handle
270
+ const resizeHandle = document.createElement('div');
271
+ resizeHandle.className = 'row-resize bg-excel-green hover:bg-green-600';
272
+ resizeHandle.dataset.row = row;
273
+ rowHeader.appendChild(resizeHandle);
274
+
275
+ tr.appendChild(rowHeader);
276
+
277
+ // Add cells
278
+ for (let col = 0; col < COLS; col++) {
279
+ const cellId = `${numberToLetters(col)}${row + 1}`;
280
+ const td = document.createElement('td');
281
+ td.className = 'cell border border-excel-border bg-white relative p-0';
282
+ td.dataset.id = cellId;
283
+ td.dataset.row = row;
284
+ td.dataset.col = col;
285
+ td.style.height = `${ROW_HEIGHT}px`;
286
+
287
+ const contentDiv = document.createElement('div');
288
+ contentDiv.className = 'w-full h-full px-2 py-1 overflow-hidden';
289
+ contentDiv.textContent = getCellValue(cellId);
290
+ td.appendChild(contentDiv);
291
+
292
+ // Add resize handle
293
+ const resizeHandle = document.createElement('div');
294
+ resizeHandle.className = 'resize-handle bg-excel-green hover:bg-green-600';
295
+ td.appendChild(resizeHandle);
296
+
297
+ tr.appendChild(td);
298
  }
299
+
300
+ tbody.appendChild(tr);
301
  }
302
+ }
303
+
304
+ // Convert number to Excel column letters (0->A, 1->B, 25->Z, 26->AA, etc.)
305
+ function numberToLetters(num) {
306
+ let letters = '';
307
+ while (num >= 0) {
308
+ letters = String.fromCharCode(65 + (num % 26)) + letters;
309
+ num = Math.floor(num / 26) - 1;
310
+ if (num < 0) break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  }
312
+ return letters;
313
+ }
314
+
315
+ // Get cell value
316
+ function getCellValue(cellId) {
317
+ if (sheetData[cellId]) {
318
+ return sheetData[cellId].value;
 
 
319
  }
320
+
321
+ // Generate sample data
322
+ const col = cellId.match(/[A-Z]+/)[0];
323
+ const row = parseInt(cellId.match(/\d+/)[0]);
324
+
325
+ if (row === 1) {
326
+ if (col === 'A') return 'Product';
327
+ if (col === 'B') return 'Category';
328
+ if (col === 'C') return 'Price';
329
+ if (col === 'D') return 'Quantity';
330
+ if (col === 'E') return 'Total';
331
  }
332
+
333
+ if (row > 1 && row < 6) {
334
+ const products = ['Laptop', 'Smartphone', 'Tablet', 'Monitor', 'Keyboard'];
335
+ const categories = ['Electronics', 'Electronics', 'Electronics', 'Electronics', 'Accessories'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
 
337
+ if (col === 'A') return products[row - 2] || '';
338
+ if (col === 'B') return categories[row - 2] || '';
339
+ if (col === 'C') return (500 + (row * 100)).toFixed(2);
340
+ if (col === 'D') return (row * 2).toString();
341
+ if (col === 'E') return `=C${row}*D${row}`;
342
+ }
343
+
344
+ return '';
345
+ }
346
+
347
+ // Initialize spreadsheet
348
+ function initSpreadsheet() {
349
+ generateColumnHeaders();
350
+ generateRows();
351
+ addEventListeners();
352
+ }
353
+
354
+ // Add event listeners
355
+ function addEventListeners() {
356
+ // Cell selection
357
+ spreadsheet.addEventListener('click', function(e) {
358
+ const cell = e.target.closest('.cell');
359
+ if (cell) {
360
+ selectCell(cell);
361
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
 
364
+ // Cell editing
365
+ spreadsheet.addEventListener('dblclick', function(e) {
366
+ const cell = e.target.closest('.cell');
367
+ if (cell) {
368
+ editCell(cell);
369
+ }
370
+ });
371
+
372
+ // Formula bar editing
373
+ formulaBar.addEventListener('keydown', function(e) {
374
+ if (e.key === 'Enter' && selectedCell) {
375
+ const cellId = selectedCell.dataset.id;
376
+ sheetData[cellId] = {
377
+ value: formulaBar.value,
378
+ displayValue: calculateDisplayValue(formulaBar.value)
379
+ };
380
+
381
+ selectedCell.querySelector('div').textContent = sheetData[cellId].displayValue;
382
+ selectedCell.classList.remove('selected');
383
+ selectedCell = null;
384
+ formulaBar.value = '';
385
+ }
386
+ });
387
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
 
389
+ // Select a cell
390
+ function selectCell(cell) {
391
+ if (selectedCell) {
392
+ selectedCell.classList.remove('selected');
393
+ }
394
+
395
+ cell.classList.add('selected');
396
+ selectedCell = cell;
397
+
398
+ const cellId = cell.dataset.id;
399
+ formulaBar.value = sheetData[cellId] ? sheetData[cellId].value : getCellValue(cellId);
400
+ formulaBar.focus();
401
+ }
402
 
403
+ // Edit cell content
404
+ function editCell(cell) {
405
+ const cellId = cell.dataset.id;
406
+ const contentDiv = cell.querySelector('div');
407
+ const currentValue = sheetData[cellId] ? sheetData[cellId].value : getCellValue(cellId);
 
 
 
 
 
 
 
408
 
409
+ const input = document.createElement('input');
410
+ input.type = 'text';
411
+ input.value = currentValue;
412
+ input.className = 'w-full h-full px-2 py-1 border-0 focus:outline-none';
413
 
414
+ contentDiv.textContent = '';
415
+ contentDiv.appendChild(input);
416
+ input.focus();
417
 
418
+ input.addEventListener('blur', function() {
419
+ finishEditing(cell, input.value);
420
+ });
421
+
422
+ input.addEventListener('keydown', function(e) {
423
+ if (e.key === 'Enter') {
424
+ finishEditing(cell, input.value);
 
 
 
 
 
 
 
425
  }
 
 
 
 
 
 
 
 
 
 
 
 
426
  });
427
+ }
428
+
429
+ // Finish editing cell
430
+ function finishEditing(cell, value) {
431
+ const cellId = cell.dataset.id;
432
+ const contentDiv = cell.querySelector('div');
433
 
434
+ sheetData[cellId] = {
435
+ value: value,
436
+ displayValue: calculateDisplayValue(value)
437
+ };
438
 
439
+ contentDiv.textContent = sheetData[cellId].displayValue;
440
+ }
441
+
442
+ // Calculate display value (simple implementation)
443
+ function calculateDisplayValue(value) {
444
+ if (value.startsWith('=')) {
445
+ // Simple formula calculation
446
+ const formula = value.substring(1);
447
+
448
+ // SUM formula
449
+ if (formula.startsWith('SUM(')) {
450
+ const range = formula.match(/SUM\(([A-Z]+\d+:[A-Z]+\d+)\)/);
451
+ if (range) {
452
+ const [start, end] = range[1].split(':');
453
+ return "=SUM(...)"; // Simplified for demo
454
+ }
455
+ }
456
+
457
+ // Basic arithmetic
458
+ if (formula.includes('+') || formula.includes('-') || formula.includes('*') || formula.includes('/')) {
459
+ try {
460
+ // Very basic evaluation - not safe for production!
461
+ return eval(formula);
462
+ } catch {
463
+ return value;
464
+ }
465
+ }
466
+
467
+ return value;
468
+ }
469
 
470
+ return value;
471
+ }
 
 
472
 
473
+ // Initialize the spreadsheet
474
+ initSpreadsheet();
475
+ });
476
  </script>
477
  <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=Tamime/mycards" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
478
  </html>