Docfile commited on
Commit
9faa19a
·
verified ·
1 Parent(s): 1777d13

Create templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +572 -0
templates/index.html ADDED
@@ -0,0 +1,572 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Générateur de Comptes GabaoHub</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
8
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
9
+ <style>
10
+ body {
11
+ padding-top: 2rem;
12
+ background-color: #f8f9fa;
13
+ }
14
+ .card {
15
+ margin-bottom: 20px;
16
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
17
+ border-radius: 10px;
18
+ overflow: hidden;
19
+ }
20
+ .card-header {
21
+ background-color: #4c5daf;
22
+ color: white;
23
+ padding: 1rem;
24
+ }
25
+ .success {
26
+ background-color: #d4edda;
27
+ }
28
+ .failed {
29
+ background-color: #f8d7da;
30
+ }
31
+ .pending {
32
+ background-color: #fff3cd;
33
+ }
34
+ .account-details {
35
+ font-family: monospace;
36
+ font-size: 0.9rem;
37
+ }
38
+ .progress {
39
+ height: 25px;
40
+ border-radius: 15px;
41
+ }
42
+ #status-badge {
43
+ font-size: 1rem;
44
+ }
45
+ .settings-panel {
46
+ background-color: #f1f3f9;
47
+ border-radius: 8px;
48
+ padding: 15px;
49
+ margin-bottom: 20px;
50
+ }
51
+ .copy-btn {
52
+ cursor: pointer;
53
+ }
54
+ .export-options {
55
+ margin-bottom: 15px;
56
+ }
57
+ .stats-card {
58
+ background-color: #e9ecef;
59
+ border-radius: 8px;
60
+ padding: 15px;
61
+ margin-bottom: 15px;
62
+ transition: all 0.3s ease;
63
+ }
64
+ .stats-card:hover {
65
+ transform: translateY(-3px);
66
+ box-shadow: 0 6px 10px rgba(0, 0, 0, 0.1);
67
+ }
68
+ .stats-value {
69
+ font-size: 1.5rem;
70
+ font-weight: bold;
71
+ }
72
+ .table-responsive {
73
+ border-radius: 8px;
74
+ overflow: hidden;
75
+ }
76
+ /* Animation pour les nouveaux comptes créés */
77
+ @keyframes highlight {
78
+ 0% { background-color: #fff9c4; }
79
+ 100% { background-color: transparent; }
80
+ }
81
+ .highlight-new {
82
+ animation: highlight 2s ease-in-out;
83
+ }
84
+ </style>
85
+ </head>
86
+ <body>
87
+ <div class="container">
88
+ <div class="row justify-content-center">
89
+ <div class="col-md-10">
90
+ <div class="card">
91
+ <div class="card-header d-flex justify-content-between align-items-center">
92
+ <h2><i class="bi bi-person-plus-fill"></i> Générateur de Comptes GabaoHub</h2>
93
+ <span id="status-badge" class="badge {% if creation_in_progress %}bg-warning{% else %}bg-success{% endif %}">
94
+ {% if creation_in_progress %}
95
+ <i class="bi bi-hourglass-split"></i> En cours
96
+ {% else %}
97
+ <i class="bi bi-check-circle"></i> Prêt
98
+ {% endif %}
99
+ </span>
100
+ </div>
101
+ <div class="card-body">
102
+ <div class="settings-panel mb-4">
103
+ <h5><i class="bi bi-gear-fill"></i> Paramètres de création</h5>
104
+ <form action="/start" method="post" class="mb-3" id="creation-form">
105
+ <div class="row g-3">
106
+ <div class="col-md-6">
107
+ <label for="num_accounts" class="form-label">Nombre de comptes</label>
108
+ <div class="input-group">
109
+ <span class="input-group-text"><i class="bi bi-people-fill"></i></span>
110
+ <input type="number" class="form-control" id="num_accounts" name="num_accounts" value="100" min="1" max="100000" {% if creation_in_progress %}disabled{% endif %}>
111
+ </div>
112
+ </div>
113
+ <div class="col-md-6">
114
+ <label for="concurrency" class="form-label">Concurrence</label>
115
+ <div class="input-group">
116
+ <span class="input-group-text"><i class="bi bi-speedometer"></i></span>
117
+ <input type="number" class="form-control" id="concurrency" name="concurrency" value="5" min="1" max="10" {% if creation_in_progress %}disabled{% endif %}>
118
+ <button type="submit" class="btn btn-primary" {% if creation_in_progress %}disabled{% endif %}>
119
+ <i class="bi bi-play-fill"></i> Démarrer
120
+ </button>
121
+ </div>
122
+ <small class="text-muted">Nombre de tâches simultanées (1-10)</small>
123
+ </div>
124
+ </div>
125
+ </form>
126
+ </div>
127
+
128
+ {% if creation_in_progress or progress > 0 %}
129
+ <div class="mb-4">
130
+ <h5><i class="bi bi-graph-up"></i> Progression</h5>
131
+ <div class="progress mb-2">
132
+ <div id="progress-bar" class="progress-bar progress-bar-striped progress-bar-animated bg-primary"
133
+ role="progressbar" style="width: {{ (progress / total * 100) if total > 0 else 0 }}%;"
134
+ aria-valuenow="{{ progress }}" aria-valuemin="0" aria-valuemax="{{ total }}">
135
+ {{ progress }}/{{ total }}
136
+ </div>
137
+ </div>
138
+ <p id="progress-text" class="text-muted">
139
+ <i class="bi bi-info-circle"></i> {{ progress }} compte(s) sur {{ total }} créé(s)
140
+ {% if creation_in_progress %}
141
+ <span class="spinner-border spinner-border-sm text-primary ms-2" role="status"></span>
142
+ {% endif %}
143
+ </p>
144
+ </div>
145
+
146
+ <div class="row mb-4">
147
+ <div class="col-md-4">
148
+ <div class="stats-card text-center">
149
+ <i class="bi bi-check-circle-fill text-success mb-2" style="font-size: 1.5rem;"></i>
150
+ <h6>Réussis</h6>
151
+ <div id="success-count" class="stats-value text-success">{{ accounts|selectattr('status', 'equalto', 'success')|list|length }}</div>
152
+ </div>
153
+ </div>
154
+ <div class="col-md-4">
155
+ <div class="stats-card text-center">
156
+ <i class="bi bi-x-circle-fill text-danger mb-2" style="font-size: 1.5rem;"></i>
157
+ <h6>Échoués</h6>
158
+ <div id="failed-count" class="stats-value text-danger">{{ accounts|selectattr('status', 'equalto', 'failed')|list|length }}</div>
159
+ </div>
160
+ </div>
161
+ <div class="col-md-4">
162
+ <div class="stats-card text-center">
163
+ <i class="bi bi-hourglass-split text-warning mb-2" style="font-size: 1.5rem;"></i>
164
+ <h6>En attente</h6>
165
+ <div id="pending-count" class="stats-value text-warning">{{ accounts|selectattr('status', 'equalto', 'pending')|list|length }}</div>
166
+ </div>
167
+ </div>
168
+ </div>
169
+ {% endif %}
170
+
171
+ {% if accounts and accounts|length > 0 %}
172
+ <div>
173
+ <div class="d-flex justify-content-between align-items-center mb-3">
174
+ <h5><i class="bi bi-person-lines-fill"></i> Comptes créés</h5>
175
+ <div class="d-flex">
176
+ <div class="export-options me-2">
177
+ <div class="dropdown">
178
+ <button class="btn btn-outline-secondary dropdown-toggle" type="button" id="exportDropdown" data-bs-toggle="dropdown" aria-expanded="false">
179
+ <i class="bi bi-download"></i> Exporter
180
+ </button>
181
+ <ul class="dropdown-menu" aria-labelledby="exportDropdown">
182
+ <li><a class="dropdown-item" href="#" id="export-all"><i class="bi bi-file-earmark-text"></i> Tous les comptes</a></li>
183
+ <li><a class="dropdown-item" href="#" id="export-success"><i class="bi bi-file-earmark-check"></i> Comptes réussis</a></li>
184
+ <li><hr class="dropdown-divider"></li>
185
+ <li><a class="dropdown-item" href="#" id="copy-table"><i class="bi bi-clipboard"></i> Copier le tableau</a></li>
186
+ </ul>
187
+ </div>
188
+ </div>
189
+ <form action="/reset" method="post" onsubmit="return confirm('Êtes-vous sûr de vouloir supprimer toutes les données?');">
190
+ <button type="submit" class="btn btn-danger" {% if creation_in_progress %}disabled{% endif %}>
191
+ <i class="bi bi-trash"></i> Réinitialiser
192
+ </button>
193
+ </form>
194
+ </div>
195
+ </div>
196
+
197
+ <div class="table-responsive">
198
+ <table class="table table-striped table-hover" id="accounts-table">
199
+ <thead class="table-dark">
200
+ <tr>
201
+ <th>#</th>
202
+ <th>Nom d'utilisateur</th>
203
+ <th>Mot de passe</th>
204
+ <th>Province</th>
205
+ <th>Ville</th>
206
+ <th>Statut</th>
207
+ <th>Horodatage</th>
208
+ <th>Actions</th>
209
+ </tr>
210
+ </thead>
211
+ <tbody id="accounts-table-body">
212
+ {% for account in accounts %}
213
+ <tr class="{{ account.status }}">
214
+ <td>{{ loop.index }}</td>
215
+ <td>{{ account.username }}</td>
216
+ <td>
217
+ <span class="password-text">{{ account.password }}</span>
218
+ <i class="bi bi-clipboard copy-btn ms-2" data-clipboard-text="{{ account.password }}" title="Copier le mot de passe"></i>
219
+ </td>
220
+ <td>{{ account.province }}</td>
221
+ <td>{{ account.ville }}</td>
222
+ <td>
223
+ <span class="badge {% if account.status == 'success' %}bg-success{% elif account.status == 'failed' %}bg-danger{% else %}bg-warning{% endif %}">
224
+ {% if account.status == 'success' %}
225
+ <i class="bi bi-check-circle-fill"></i>
226
+ {% elif account.status == 'failed' %}
227
+ <i class="bi bi-x-circle-fill"></i>
228
+ {% else %}
229
+ <i class="bi bi-hourglass-split"></i>
230
+ {% endif %}
231
+ {{ account.status }}
232
+ </span>
233
+ </td>
234
+ <td>{{ account.timestamp if account.timestamp else 'N/A' }}</td>
235
+ <td>
236
+ <button class="btn btn-sm btn-outline-primary copy-account" data-username="{{ account.username }}" data-password="{{ account.password }}">
237
+ <i class="bi bi-clipboard-check"></i>
238
+ </button>
239
+ </td>
240
+ </tr>
241
+ {% endfor %}
242
+ </tbody>
243
+ </table>
244
+ </div>
245
+ </div>
246
+ {% else %}
247
+ <div class="alert alert-info">
248
+ <i class="bi bi-info-circle-fill"></i> Aucun compte n'a encore été créé. Cliquez sur "Démarrer" pour commencer.
249
+ </div>
250
+ {% endif %}
251
+ </div>
252
+ </div>
253
+ </div>
254
+ </div>
255
+ </div>
256
+
257
+ <!-- Toast pour les notifications -->
258
+ <div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11">
259
+ <div id="copyToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
260
+ <div class="toast-header bg-success text-white">
261
+ <i class="bi bi-check-circle-fill me-2"></i>
262
+ <strong class="me-auto">Notification</strong>
263
+ <button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Close"></button>
264
+ </div>
265
+ <div class="toast-body" id="toast-message">
266
+ Copié avec succès!
267
+ </div>
268
+ </div>
269
+ </div>
270
+
271
+ <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
272
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
273
+ <script>
274
+ // Toast pour afficher les notifications
275
+ function showToast(message, type = 'success') {
276
+ const toast = $('#copyToast');
277
+ const toastHeader = toast.find('.toast-header');
278
+
279
+ // Définir le type de toast
280
+ if (type === 'success') {
281
+ toastHeader.removeClass('bg-danger').addClass('bg-success');
282
+ toastHeader.find('i').removeClass('bi-x-circle-fill').addClass('bi-check-circle-fill');
283
+ } else {
284
+ toastHeader.removeClass('bg-success').addClass('bg-danger');
285
+ toastHeader.find('i').removeClass('bi-check-circle-fill').addClass('bi-x-circle-fill');
286
+ }
287
+
288
+ // Mettre à jour le message
289
+ $('#toast-message').text(message);
290
+
291
+ // Afficher le toast
292
+ const bsToast = new bootstrap.Toast(toast);
293
+ bsToast.show();
294
+ }
295
+
296
+ // Fonction pour copier du texte dans le presse-papiers
297
+ function copyToClipboard(text) {
298
+ navigator.clipboard.writeText(text).then(function() {
299
+ showToast('Copié avec succès!');
300
+ }, function() {
301
+ showToast('Échec de la copie', 'error');
302
+ });
303
+ }
304
+
305
+ // Fonction pour exporter les données en CSV
306
+ function exportToCSV(accounts, onlySuccess = false) {
307
+ if (!accounts || accounts.length === 0) return;
308
+
309
+ // Filtrer les comptes si nécessaire
310
+ const dataToExport = onlySuccess ? accounts.filter(a => a.status === 'success') : accounts;
311
+
312
+ // Créer les en-têtes CSV
313
+ let csv = 'Username,Password,Province,Ville,Status,Timestamp\n';
314
+
315
+ // Ajouter les données
316
+ dataToExport.forEach(account => {
317
+ const username = account.username || '';
318
+ const password = account.password || '';
319
+ const province = account.province || '';
320
+ const ville = account.ville || '';
321
+ const status = account.status || '';
322
+ const timestamp = account.timestamp || '';
323
+
324
+ csv += `"${username}","${password}","${province}","${ville}","${status}","${timestamp}"\n`;
325
+ });
326
+
327
+ // Créer un blob et télécharger
328
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
329
+ const url = URL.createObjectURL(blob);
330
+ const link = document.createElement('a');
331
+ link.setAttribute('href', url);
332
+ link.setAttribute('download', onlySuccess ? 'gabao_hub_success_accounts.csv' : 'gabao_hub_all_accounts.csv');
333
+ document.body.appendChild(link);
334
+ link.click();
335
+ document.body.removeChild(link);
336
+ }
337
+
338
+ // Fonction pour mettre à jour les informations sur la page
339
+ function updateProgress() {
340
+ if ({{ creation_in_progress|tojson }}) {
341
+ $.ajax({
342
+ url: '/progress',
343
+ type: 'GET',
344
+ dataType: 'json',
345
+ success: function(data) {
346
+ // Mettre à jour l'état
347
+ if (data.creation_in_progress) {
348
+ $('#status-badge').html('<i class="bi bi-hourglass-split"></i> En cours').removeClass('bg-success').addClass('bg-warning');
349
+ $('#creation-form button').prop('disabled', true);
350
+ $('#num_accounts').prop('disabled', true);
351
+ $('#concurrency').prop('disabled', true);
352
+ $('form[action="/reset"] button').prop('disabled', true);
353
+ } else {
354
+ $('#status-badge').html('<i class="bi bi-check-circle"></i> Prêt').removeClass('bg-warning').addClass('bg-success');
355
+ $('#creation-form button').prop('disabled', false);
356
+ $('#num_accounts').prop('disabled', false);
357
+ $('#concurrency').prop('disabled', false);
358
+ $('form[action="/reset"] button').prop('disabled', false);
359
+ }
360
+
361
+ // Mettre à jour la barre de progression
362
+ let percentage = data.total > 0 ? (data.progress / data.total * 100) : 0;
363
+ $('#progress-bar').css('width', percentage + '%').attr('aria-valuenow', data.progress);
364
+ $('#progress-bar').text(data.progress + '/' + data.total);
365
+
366
+ let progressText = data.progress + ' compte(s) sur ' + data.total + ' créé(s)';
367
+ if (data.creation_in_progress) {
368
+ progressText += ' <span class="spinner-border spinner-border-sm text-primary ms-2" role="status"></span>';
369
+ }
370
+ $('#progress-text').html('<i class="bi bi-info-circle"></i> ' + progressText);
371
+
372
+ // Mettre à jour les compteurs statistiques
373
+ updateStatCounters(data.accounts);
374
+
375
+ // Mettre à jour le tableau des comptes
376
+ updateAccountsTable(data.accounts);
377
+
378
+ // Continuer la mise à jour uniquement si la création est en cours
379
+ if (data.creation_in_progress) {
380
+ setTimeout(updateProgress, 2000);
381
+ } else {
382
+ // Rechargement de la page pour afficher les résultats finaux
383
+ location.reload();
384
+ }
385
+ }
386
+ });
387
+ }
388
+ }
389
+
390
+ // Fonction pour mettre à jour les compteurs statistiques
391
+ function updateStatCounters(accounts) {
392
+ if (!accounts || accounts.length === 0) return;
393
+
394
+ const successCount = accounts.filter(a => a.status === 'success').length;
395
+ const failedCount = accounts.filter(a => a.status === 'failed').length;
396
+ const pendingCount = accounts.filter(a => a.status === 'pending').length;
397
+
398
+ $('#success-count').text(successCount);
399
+ $('#failed-count').text(failedCount);
400
+ $('#pending-count').text(pendingCount);
401
+ }
402
+
403
+ // Fonction pour mettre à jour le tableau des comptes
404
+ function updateAccountsTable(accounts) {
405
+ if (!accounts || accounts.length === 0) return;
406
+
407
+ let tableBody = $('#accounts-table-body');
408
+ let existingRows = tableBody.find('tr').length;
409
+
410
+ // Parcourir les comptes et mettre à jour le tableau
411
+ accounts.forEach(function(account, index) {
412
+ let statusClass = '';
413
+ let statusIcon = '';
414
+ let statusBadge = '';
415
+
416
+ if (account.status === 'success') {
417
+ statusClass = 'success';
418
+ statusIcon = '<i class="bi bi-check-circle-fill"></i>';
419
+ statusBadge = '<span class="badge bg-success">' + statusIcon + ' success</span>';
420
+ } else if (account.status === 'failed') {
421
+ statusClass = 'failed';
422
+ statusIcon = '<i class="bi bi-x-circle-fill"></i>';
423
+ statusBadge = '<span class="badge bg-danger">' + statusIcon + ' failed</span>';
424
+ } else {
425
+ statusClass = 'pending';
426
+ statusIcon = '<i class="bi bi-hourglass-split"></i>';
427
+ statusBadge = '<span class="badge bg-warning">' + statusIcon + ' pending</span>';
428
+ }
429
+
430
+ const timestamp = account.timestamp ? account.timestamp : 'N/A';
431
+ const username = account.username || '';
432
+ const password = account.password || '';
433
+ const province = account.province || '';
434
+ const ville = account.ville || '';
435
+
436
+ // Vérifier si cette ligne existe déjà
437
+ let existingRow = tableBody.find('tr').eq(index);
438
+
439
+ if (existingRow.length) {
440
+ // Mettre à jour la ligne existante
441
+ existingRow.attr('class', statusClass);
442
+ existingRow.find('td').eq(1).text(username);
443
+
444
+ let passwordCell = existingRow.find('td').eq(2);
445
+ passwordCell.html('<span class="password-text">' + password + '</span><i class="bi bi-clipboard copy-btn ms-2" data-clipboard-text="' + password + '" title="Copier le mot de passe"></i>');
446
+
447
+ existingRow.find('td').eq(3).text(province);
448
+ existingRow.find('td').eq(4).text(ville);
449
+ existingRow.find('td').eq(5).html(statusBadge);
450
+ existingRow.find('td').eq(6).text(timestamp);
451
+
452
+ // Mettre à jour le bouton de copie
453
+ let actionsCell = existingRow.find('td').eq(7);
454
+ actionsCell.html('<button class="btn btn-sm btn-outline-primary copy-account" data-username="' + username + '" data-password="' + password + '"><i class="bi bi-clipboard-check"></i></button>');
455
+
456
+ // Ajouter animation si le statut a changé de "pending"
457
+ if (existingRow.data('status') === 'pending' && account.status !== 'pending') {
458
+ existingRow.addClass('highlight-new');
459
+ setTimeout(function() {
460
+ existingRow.removeClass('highlight-new');
461
+ }, 2000);
462
+ }
463
+
464
+ // Stocker le statut actuel
465
+ existingRow.data('status', account.status);
466
+
467
+ } else {
468
+ // Ajouter une nouvelle ligne
469
+ let newRow = $(`
470
+ <tr class="${statusClass}" data-status="${account.status}">
471
+ <td>${index + 1}</td>
472
+ <td>${username}</td>
473
+ <td>
474
+ <span class="password-text">${password}</span>
475
+ <i class="bi bi-clipboard copy-btn ms-2" data-clipboard-text="${password}" title="Copier le mot de passe"></i>
476
+ </td>
477
+ <td>${province}</td>
478
+ <td>${ville}</td>
479
+ <td>${statusBadge}</td>
480
+ <td>${timestamp}</td>
481
+ <td>
482
+ <button class="btn btn-sm btn-outline-primary copy-account" data-username="${username}" data-password="${password}">
483
+ <i class="bi bi-clipboard-check"></i>
484
+ </button>
485
+ </td>
486
+ </tr>
487
+ `);
488
+
489
+ tableBody.append(newRow);
490
+ newRow.addClass('highlight-new');
491
+ setTimeout(function() {
492
+ newRow.removeClass('highlight-new');
493
+ }, 2000);
494
+ }
495
+ });
496
+ }
497
+
498
+ // Lancer la mise à jour de la progression au chargement de la page
499
+ $(document).ready(function() {
500
+ // Initialiser les gestionnaires d'événements
501
+
502
+ // Copier le mot de passe
503
+ $(document).on('click', '.copy-btn', function() {
504
+ const text = $(this).data('clipboard-text');
505
+ copyToClipboard(text);
506
+ });
507
+
508
+ // Copier les informations complètes du compte
509
+ $(document).on('click', '.copy-account', function() {
510
+ const username = $(this).data('username');
511
+ const password = $(this).data('password');
512
+ const accountInfo = `Nom d'utilisateur: ${username}\nMot de passe: ${password}`;
513
+ copyToClipboard(accountInfo);
514
+ });
515
+
516
+ // Exporter tous les comptes
517
+ $('#export-all').click(function(e) {
518
+ e.preventDefault();
519
+ $.ajax({
520
+ url: '/progress',
521
+ type: 'GET',
522
+ dataType: 'json',
523
+ success: function(data) {
524
+ exportToCSV(data.accounts, false);
525
+ }
526
+ });
527
+ });
528
+
529
+ // Exporter uniquement les comptes réussis
530
+ $('#export-success').click(function(e) {
531
+ e.preventDefault();
532
+ $.ajax({
533
+ url: '/progress',
534
+ type: 'GET',
535
+ dataType: 'json',
536
+ success: function(data) {
537
+ exportToCSV(data.accounts, true);
538
+ }
539
+ });
540
+ });
541
+
542
+ // Copier le tableau entier
543
+ $('#copy-table').click(function(e) {
544
+ e.preventDefault();
545
+
546
+ // Construire un tableau formaté pour le presse-papiers
547
+ let tableText = "Index\tNom d'utilisateur\tMot de passe\tProvince\tVille\tStatut\tHorodatage\n";
548
+
549
+ $('#accounts-table tbody tr').each(function() {
550
+ const row = $(this);
551
+ const index = row.find('td').eq(0).text();
552
+ const username = row.find('td').eq(1).text();
553
+ const password = row.find('.password-text').text();
554
+ const province = row.find('td').eq(3).text();
555
+ const ville = row.find('td').eq(4).text();
556
+ const status = row.hasClass('success') ? 'success' : (row.hasClass('failed') ? 'failed' : 'pending');
557
+ const timestamp = row.find('td').eq(6).text();
558
+
559
+ tableText += `${index}\t${username}\t${password}\t${province}\t${ville}\t${status}\t${timestamp}\n`;
560
+ });
561
+
562
+ copyToClipboard(tableText);
563
+ });
564
+
565
+ // Lancer la mise à jour si création en cours
566
+ if ({{ creation_in_progress|tojson }}) {
567
+ setTimeout(updateProgress, 1000);
568
+ }
569
+ });
570
+ </script>
571
+ </body>
572
+ </html>