ernestmindres commited on
Commit
4f79710
·
verified ·
1 Parent(s): b56de37

Update templates/static_deploy.html

Browse files
Files changed (1) hide show
  1. templates/static_deploy.html +377 -272
templates/static_deploy.html CHANGED
@@ -1,273 +1,378 @@
1
- <html lang="fr">
2
- <head>
3
- <meta charset="UTF-8">
4
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
5
- <title>Mailix | Déploiement Statique</title>
6
- <link rel="icon" type="image/png" href="https://i.imgur.com/7Gn3toV.png">
7
- <link rel="stylesheet"
8
- href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@24,400,0,0" />
9
- <script src="https://cdn.tailwindcss.com"></script>
10
- <style>
11
- :root {
12
- --main-bg: #2d2d30;
13
- --bar-color: #3e3e42;
14
- --text-color: #f0f0f0;
15
- --accent-color: #007acc;
16
- --border-color: #444444;
17
- }
18
-
19
- body {
20
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
21
- background-color: var(--main-bg);
22
- color: var(--text-color);
23
- padding: 20px;
24
- }
25
-
26
- .container {
27
- max-width: 900px;
28
- margin: 0 auto;
29
- background-color: var(--bar-color);
30
- padding: 20px;
31
- border-radius: 8px;
32
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
33
- }
34
-
35
- .input-group {
36
- margin-bottom: 20px;
37
- border: 2px dashed var(--border-color);
38
- padding: 20px;
39
- border-radius: 6px;
40
- text-align: center;
41
- cursor: pointer;
42
- transition: border-color 0.3s;
43
- }
44
-
45
- .input-group:hover {
46
- border-color: var(--accent-color);
47
- }
48
-
49
- #file-input {
50
- display: none;
51
- }
52
-
53
- /* Boutons */
54
- .btn {
55
- padding: 10px 20px;
56
- border-radius: 4px;
57
- font-weight: bold;
58
- cursor: pointer;
59
- transition: background-color 0.2s;
60
- }
61
-
62
- #commit-btn {
63
- background-color: var(--accent-color);
64
- color: white;
65
- }
66
-
67
- #commit-btn:hover:not(:disabled) {
68
- background-color: #005f99;
69
- }
70
-
71
- #commit-btn:disabled {
72
- background-color: #444;
73
- cursor: not-allowed;
74
- opacity: 0.7;
75
- }
76
-
77
- .status-badge {
78
- padding: 4px 8px;
79
- border-radius: 4px;
80
- font-size: 0.8em;
81
- font-weight: bold;
82
- display: inline-block;
83
- }
84
-
85
- .status-badge.running {
86
- background-color: #10b981; /* Vert */
87
- color: white;
88
- }
89
-
90
- .status-badge.error {
91
- background-color: #ef4444; /* Rouge */
92
- color: white;
93
- }
94
-
95
- table {
96
- width: 100%;
97
- border-collapse: collapse;
98
- margin-top: 20px;
99
- }
100
-
101
- th, td {
102
- border: 1px solid var(--border-color);
103
- padding: 10px;
104
- text-align: left;
105
- }
106
-
107
- th {
108
- background-color: #444444;
109
- }
110
- </style>
111
- </head>
112
- <body>
113
-
114
- <div class="container">
115
- <h2 class="text-2xl font-bold mb-4">🌐 Déploiement de Site Statique Temporaire</h2>
116
-
117
- <div class="input-group" id="drop-area">
118
- <p class="text-xl">Glissez-déposez vos fichiers ici (HTML, CSS, JS, Images)</p>
119
- <p class="text-sm text-gray-400">ou cliquez pour sélectionner un ou plusieurs fichiers.</p>
120
- <input type="file" id="file-input" multiple>
121
- </div>
122
-
123
- <div id="upload-status" class="mb-4 text-center">
124
- <span id="status-text" class="text-yellow-400">Importez des fichiers et assurez-vous d'avoir un "index.html".</span>
125
- </div>
126
-
127
- <div class="flex justify-between items-center mb-4">
128
- <button id="commit-btn" class="btn" disabled>
129
- <span id="commit-text">Pas d'application fichier</span>
130
- </button>
131
- <div id="running-status" class="status-badge error">INACTIF</div>
132
- </div>
133
-
134
- <h3 class="text-xl font-semibold mb-2 mt-6">Fichiers Actuels (<span id="file-count">0</span>)</h3>
135
- <div id="file-list-container" class="max-h-60 overflow-y-auto border border-gray-600 rounded">
136
- <table>
137
- <thead>
138
- <tr>
139
- <th class="w-1/2">Nom du Fichier</th>
140
- <th class="w-1/2">Type</th>
141
- </tr>
142
- </thead>
143
- <tbody id="files-table-body">
144
- {% for file in files %}
145
- <tr>
146
- <td>{{ file }}</td>
147
- <td>{{ file.rsplit('.', 1)[1] if '.' in file else 'Inconnu' }}</td>
148
- </tr>
149
- {% endfor %}
150
- </tbody>
151
- </table>
152
- {% if not files %}
153
- <p id="no-files-message" class="p-4 text-center text-gray-400">Aucun fichier importé.</p>
154
- {% endif %}
155
- </div>
156
-
157
- </div>
158
-
159
- <script>
160
- const fileInput = document.getElementById('file-input');
161
- const dropArea = document.getElementById('drop-area');
162
- const commitBtn = document.getElementById('commit-btn');
163
- const commitText = document.getElementById('commit-text');
164
- const runningStatus = document.getElementById('running-status');
165
- const statusText = document.getElementById('status-text');
166
- const filesTableBody = document.getElementById('files-table-body');
167
- const fileCountSpan = document.getElementById('file-count');
168
- const noFilesMessage = document.getElementById('no-files-message');
169
-
170
- let deployId = "{{ deploy_id }}";
171
- let isIndexPresent = {{ 'true' if index_file_present else 'false' }};
172
- let launchUrl = null;
173
-
174
- // Fonction pour mettre à jour l'interface après un upload
175
- function updateUI(files, indexPresent, newLaunchUrl, message) {
176
- isIndexPresent = indexPresent;
177
- launchUrl = newLaunchUrl;
178
- statusText.textContent = message;
179
- fileCountSpan.textContent = files.length;
180
-
181
- // Mise à jour du tableau
182
- filesTableBody.innerHTML = '';
183
- if (files.length > 0) {
184
- noFilesMessage.style.display = 'none';
185
- files.forEach(file => {
186
- const row = filesTableBody.insertRow();
187
- row.innerHTML = `<td>${file}</td><td>${file.split('.').pop() || 'Inconnu'}</td>`;
188
- });
189
- } else {
190
- noFilesMessage.style.display = 'block';
191
- }
192
-
193
- // Mise à jour du bouton Commit et du badge de statut
194
- if (isIndexPresent) {
195
- commitBtn.disabled = false;
196
- commitText.textContent = "Lancer le Site Statique";
197
- runningStatus.textContent = "PRÊT";
198
- runningStatus.className = 'status-badge running';
199
- } else {
200
- commitBtn.disabled = true;
201
- commitText.textContent = "Pas d'application fichier (index.html manquant)";
202
- runningStatus.textContent = "ERREUR";
203
- runningStatus.className = 'status-badge error';
204
- }
205
- }
206
-
207
- // Initialiser l'UI avec les données du template (si la page est rechargée)
208
- const initialFiles = [{% for file in files %}"{{ file }}"{% if not loop.last %}, {% endif %}{% endfor %}];
209
- updateUI(initialFiles, isIndexPresent, null, 'Bienvenue sur le Déploiement Statique.');
210
-
211
-
212
- // 1. Déclencheur du dialogue de fichier
213
- dropArea.addEventListener('click', () => {
214
- fileInput.click();
215
- });
216
-
217
- // 2. Gestion de la sélection et de l'upload
218
- fileInput.addEventListener('change', async (event) => {
219
- const files = event.target.files;
220
- if (files.length === 0) return;
221
-
222
- statusText.textContent = `Téléversement de ${files.length} fichiers...`;
223
- commitBtn.disabled = true;
224
-
225
- const formData = new FormData();
226
- // Ajouter tous les fichiers sélectionnés à FormData
227
- for (let i = 0; i < files.length; i++) {
228
- // Utiliser une clé générique pour que le backend puisse itérer sur request.files
229
- formData.append('files[]', files[i]);
230
- }
231
-
232
- try {
233
- const response = await fetch('/upload-static', {
234
- method: 'POST',
235
- body: formData
236
- });
237
-
238
- const data = await response.json();
239
-
240
- if (response.ok && data.status === 'success') {
241
- deployId = data.deploy_id;
242
-
243
- // Mettre à jour la liste des fichiers et l'état
244
- const fileNames = Array.from(files).map(f => f.name);
245
- updateUI(fileNames, data.index_present, data.launch_url, "Upload réussi. Le site est prêt à être lancé.");
246
-
247
- } else {
248
- throw new Error(data.message || "Erreur de téléversement.");
249
- }
250
-
251
- } catch (error) {
252
- console.error("Erreur d'upload:", error);
253
- statusText.textContent = `Erreur: ${error.message}`;
254
- commitBtn.disabled = true;
255
- }
256
-
257
- // Réinitialiser l'input file
258
- fileInput.value = "";
259
- });
260
-
261
- // 3. Gestion du bouton "Lancer le Site Statique"
262
- commitBtn.addEventListener('click', () => {
263
- if (isIndexPresent && launchUrl) {
264
- // Rediriger vers l'interface de lancement
265
- window.location.href = `/html-run?url=${encodeURIComponent(launchUrl)}`;
266
- } else {
267
- alert("Veuillez d'abord importer les fichiers et vous assurer qu'un index.html est présent.");
268
- }
269
- });
270
- </script>
271
-
272
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  </html>
 
1
+ <html lang="fr">
2
+ <head>
3
+ <meta charset="UTF-8">
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
5
+ <title>Mailix | Déploiement Statique</title>
6
+ <link rel="icon" type="image/png" href="https://i.imgur.com/7Gn3toV.png">
7
+ <link rel="stylesheet"
8
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@24,400,0,0" />
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+
11
+ <style>
12
+ /* -------------------------------------- */
13
+ /* CSS pour la zone de Glisser-Déposer */
14
+ /* -------------------------------------- */
15
+ .dropzone {
16
+ border: 3px dashed #007bff; /* Bordure plus visible et couleur primaire */
17
+ background-color: #f8f9fa; /* Fond légèrement gris */
18
+ padding: 40px; /* Augmenter le padding pour le rendre plus grand */
19
+ text-align: center;
20
+ cursor: pointer;
21
+ transition: background-color 0.2s, border-color 0.2s;
22
+ border-radius: 12px; /* Coins arrondis pour un look moderne */
23
+ margin-bottom: 20px;
24
+ user-select: none; /* Empêcher la sélection de texte */
25
+ }
26
+
27
+ .dropzone.dragover {
28
+ background-color: #e2f0ff; /* Couleur au survol/glissement */
29
+ border-color: #0056b3;
30
+ }
31
+
32
+ .dropzone-icon {
33
+ font-size: 3rem;
34
+ color: #007bff;
35
+ margin-bottom: 10px;
36
+ }
37
+
38
+ .dropzone-text {
39
+ font-size: 1.2rem;
40
+ font-weight: 600;
41
+ color: #343a40;
42
+ }
43
+
44
+ .dropzone-hint {
45
+ font-size: 0.9rem;
46
+ color: #6c757d;
47
+ margin-top: 5px;
48
+ }
49
+
50
+ /* -------------------------------------- */
51
+ /* Style pour le Responsive (Mobile) */
52
+ /* -------------------------------------- */
53
+ @media (max-width: 576px) {
54
+ .dropzone {
55
+ padding: 25px;
56
+ }
57
+ .dropzone-icon {
58
+ font-size: 2.5rem;
59
+ }
60
+ .dropzone-text {
61
+ font-size: 1rem;
62
+ }
63
+ .file-list {
64
+ font-size: 0.9rem;
65
+ }
66
+ }
67
+
68
+ /* Style pour la liste de fichiers */
69
+ .file-list {
70
+ list-style-type: none;
71
+ padding: 0;
72
+ margin-top: 15px;
73
+ text-align: left;
74
+ }
75
+ .file-list li {
76
+ padding: 8px 10px;
77
+ border-bottom: 1px solid #eee;
78
+ display: flex;
79
+ justify-content: space-between;
80
+ align-items: center;
81
+ }
82
+ .file-list li:last-child {
83
+ border-bottom: none;
84
+ }
85
+ .file-icon {
86
+ margin-right: 8px;
87
+ color: #007bff;
88
+ }
89
+
90
+ /* Style pour le bouton de copie */
91
+ #copy-url-button {
92
+ width: 50px; /* Fixer la largeur du bouton de copie */
93
+ }
94
+ </style>
95
+
96
+ <input type="file" id="file-input" multiple style="display: none;">
97
+
98
+
99
+ <div class="container my-5">
100
+ <div class="row">
101
+ <div class="col-12 col-lg-8 mx-auto">
102
+ <h2 class="text-center mb-4"> Déploiement Statique Instantané</h2>
103
+
104
+ <div id="drop-zone" class="dropzone">
105
+ <div class="dropzone-icon"></div>
106
+ <div class="dropzone-text">Glissez & Déposez vos fichiers ici</div>
107
+ <div class="dropzone-hint">ou cliquez pour sélectionner des fichiers (HTML, CSS, JS, Images...)</div>
108
+ <div id="upload-status" class="mt-3 text-muted">Prêt pour l'upload.</div>
109
+ </div>
110
+
111
+ <div id="launch-url-container" class="mt-4 p-3 bg-light border rounded"
112
+ style="display: none; background-color: #fff3cd !important; border-color: #ffc107 !important;">
113
+ <label class="form-label fw-bold d-block text-warning" style="color: #664d03 !important;">
114
+ <span class="material-symbols-rounded align-middle me-1">share</span> Lien de Déploiement Temporaire (Partageable)
115
+ </label>
116
+ <div class="input-group mb-2">
117
+ <input type="text" id="launch-url-display" class="form-control" readonly
118
+ value="{{ launch_url if launch_url else '' }}">
119
+ <button class="btn btn-outline-secondary" type="button" id="copy-url-button" title="Copier le lien">
120
+ <span class="material-symbols-rounded">content_copy</span>
121
+ </button>
122
+ </div>
123
+ <small class="text-danger mt-1 d-block">
124
+ ⚠️ **Attention:** Ce lien est temporaire et sera supprimé lors du prochain redémarrage du service.
125
+ </small>
126
+ </div>
127
+ <div class="d-grid gap-2 d-md-flex justify-content-md-center mb-4 mt-4">
128
+ <button class="btn btn-primary btn-lg" id="launch-button" disabled>
129
+ <span class="material-symbols-rounded align-middle me-1">open_in_new</span> Lancer l'Aperçu
130
+ </button>
131
+ <button class="btn btn-danger btn-lg" id="clear-button" disabled>
132
+ <span class="material-symbols-rounded align-middle me-1">delete</span> Vider
133
+ </button>
134
+ </div>
135
+
136
+ <div class="card shadow-sm">
137
+ <div class="card-header bg-light">
138
+ <h5 class="mb-0">Fichiers Actuellement Déployés (<span id="file-count">{{ files|length }}</span>)</h5>
139
+ </div>
140
+ <div class="card-body">
141
+ {% if files %}
142
+ <ul class="file-list" id="uploaded-file-list">
143
+ {% for file in files %}
144
+ <li>
145
+ <span class="file-icon"></span> {{ file }}
146
+ {% if file == 'index.html' %}
147
+ <span class="badge bg-success" style="background-color: #198754 !important;">PAGE D'ACCUEIL</span>
148
+ {% endif %}
149
+ </li>
150
+ {% endfor %}
151
+ </ul>
152
+ {% else %}
153
+ <p class="text-center text-muted" id="no-files-message">Aucun fichier n'a été téléversé pour le moment.</p>
154
+ {% endif %}
155
+ </div>
156
+ </div>
157
+
158
+ {% if deploy_id %}
159
+ <small class="text-muted mt-3 d-block text-center">ID de Déploiement Temporaire: <code>{{ deploy_id }}</code></small>
160
+ {% endif %}
161
+
162
+ </div>
163
+ </div>
164
+ </div>
165
+
166
+ <script>
167
+ // Récupération des éléments du DOM
168
+ const dropZone = document.getElementById('drop-zone');
169
+ const fileInput = document.getElementById('file-input');
170
+ const uploadStatus = document.getElementById('upload-status');
171
+ const launchButton = document.getElementById('launch-button');
172
+ const clearButton = document.getElementById('clear-button');
173
+ const urlContainer = document.getElementById('launch-url-container');
174
+ const urlInput = document.getElementById('launch-url-display');
175
+ const copyButton = document.getElementById('copy-url-button');
176
+ const fileCountSpan = document.getElementById('file-count');
177
+
178
+ // Récupération de l'URL injectée par Flask (sera null si aucun déploiement actif)
179
+ const currentLaunchUrl = urlInput.value;
180
+
181
+
182
+ /* -------------------------------------- */
183
+ /* 0. Initialisation des Boutons/Affichage*/
184
+ /* -------------------------------------- */
185
+
186
+ // 0a. Afficher l'URL si elle est présente dans le champ (injectée par Jinja2)
187
+ if (currentLaunchUrl) {
188
+ urlContainer.style.display = 'block';
189
+ }
190
+
191
+ // 0b. État initial du bouton Lancer (géré par Jinja2 au chargement initial)
192
+ const indexFilePresent = {{ index_file_present | tojson }};
193
+ const fileCount = {{ files|length }};
194
+
195
+ if (!indexFilePresent || fileCount === 0) {
196
+ launchButton.disabled = true;
197
+ } else {
198
+ launchButton.disabled = false;
199
+ }
200
+ if (fileCount > 0) {
201
+ clearButton.disabled = false;
202
+ }
203
+
204
+ // 0c. Gère le lancement de l'aperçu (utilise l'URL complète si disponible)
205
+ launchButton.addEventListener('click', () => {
206
+ if (currentLaunchUrl) {
207
+ window.open(currentLaunchUrl, '_blank');
208
+ } else {
209
+ // Logique de secours au cas où l'URL n'a pas été injectée mais le deploy_id est là
210
+ const deployId = '{{ deploy_id }}';
211
+ if (deployId) {
212
+ window.open(`/serve-static/${deployId}/index.html`, '_blank');
213
+ }
214
+ }
215
+ });
216
+
217
+
218
+ /* -------------------------------------- */
219
+ /* 1. Gestion du Clic sur la Dropzone */
220
+ /* -------------------------------------- */
221
+ dropZone.addEventListener('click', () => {
222
+ fileInput.click();
223
+ });
224
+
225
+ /* -------------------------------------- */
226
+ /* 2. Gestion du Glisser-Déposer */
227
+ /* -------------------------------------- */
228
+
229
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
230
+ dropZone.addEventListener(eventName, preventDefaults, false);
231
+ });
232
+
233
+ function preventDefaults(e) {
234
+ e.preventDefault();
235
+ e.stopPropagation();
236
+ }
237
+
238
+ ['dragenter', 'dragover'].forEach(eventName => {
239
+ dropZone.addEventListener(eventName, highlight, false);
240
+ });
241
+
242
+ ['dragleave', 'drop'].forEach(eventName => {
243
+ dropZone.addEventListener(eventName, unhighlight, false);
244
+ });
245
+
246
+ function highlight() {
247
+ dropZone.classList.add('dragover');
248
+ uploadStatus.textContent = "Relâchez les fichiers pour téléverser...";
249
+ }
250
+
251
+ function unhighlight() {
252
+ dropZone.classList.remove('dragover');
253
+ }
254
+
255
+ dropZone.addEventListener('drop', handleDrop, false);
256
+
257
+ function handleDrop(e) {
258
+ const dt = e.dataTransfer;
259
+ const files = dt.files;
260
+
261
+ fileInput.files = files;
262
+ handleFileSelection();
263
+ }
264
+
265
+
266
+ /* -------------------------------------- */
267
+ /* 3. Gestion de la Sélection de Fichiers */
268
+ /* -------------------------------------- */
269
+ fileInput.addEventListener('change', handleFileSelection);
270
+
271
+ function handleFileSelection() {
272
+ if (fileInput.files.length > 0) {
273
+ uploadFiles(fileInput.files);
274
+ }
275
+ }
276
+
277
+
278
+ /* -------------------------------------- */
279
+ /* 4. Logique de l'Upload (AJAX) */
280
+ /* -------------------------------------- */
281
+ function uploadFiles(files) {
282
+ const url = "{{ url_for('web_bp.upload_static_files') }}";
283
+ const formData = new FormData();
284
+
285
+ // 1. Ajouter tous les fichiers au FormData
286
+ for (let i = 0; i < files.length; i++) {
287
+ formData.append('file-' + i, files[i]);
288
+ }
289
+
290
+ // 2. Mettre à jour l'état et désactiver les boutons
291
+ uploadStatus.className = 'mt-3 text-info';
292
+ uploadStatus.textContent = `Téléversement de ${files.length} fichiers...`;
293
+ launchButton.disabled = true;
294
+ clearButton.disabled = true;
295
+
296
+ // 3. Envoyer la requête AJAX
297
+ fetch(url, {
298
+ method: 'POST',
299
+ body: formData,
300
+ })
301
+ .then(response => {
302
+ if (!response.ok) {
303
+ // Tente de lire l'erreur JSON même en cas de statut HTTP 4xx/5xx
304
+ return response.json().then(err => { throw new Error(err.message || 'Erreur serveur inconnu'); });
305
+ }
306
+ return response.json();
307
+ })
308
+ .then(data => {
309
+ // 4. Mettre à jour l'interface avec les résultats
310
+ if (data.status === 'success') {
311
+ uploadStatus.className = 'mt-3 text-success';
312
+ uploadStatus.textContent = data.message + " Redirection en cours...";
313
+
314
+ // L'URL de lancement est maintenant dans data.launch_url
315
+ // On affiche le lien (temporairement) et on recharge la page pour la robustesse.
316
+ urlInput.value = data.launch_url;
317
+ urlContainer.style.display = 'block';
318
+
319
+ // Recharger pour que le backend (Jinja2) mette à jour l'interface
320
+ setTimeout(() => {
321
+ window.location.href = "{{ url_for('web_bp.static_deploy_page') }}";
322
+ }, 500); // Petite pause pour que l'utilisateur voie le message de succès
323
+
324
+ } else {
325
+ uploadStatus.className = 'mt-3 text-danger';
326
+ uploadStatus.textContent = `Échec du téléversement: ${data.message}`;
327
+ }
328
+ })
329
+ .catch(error => {
330
+ console.error('Erreur AJAX:', error);
331
+ uploadStatus.className = 'mt-3 text-danger';
332
+ uploadStatus.textContent = `Erreur critique: ${error.message}`;
333
+
334
+ // Réactiver les boutons si l'upload a échoué mais des fichiers étaient présents
335
+ if (fileCount > 0) {
336
+ launchButton.disabled = !indexFilePresent;
337
+ clearButton.disabled = false;
338
+ }
339
+ });
340
+ }
341
+
342
+
343
+ /* -------------------------------------- */
344
+ /* 5. Gestion du Bouton de Copie */
345
+ /* -------------------------------------- */
346
+ copyButton.addEventListener('click', () => {
347
+ if (urlInput.value) {
348
+ urlInput.select();
349
+ urlInput.setSelectionRange(0, 99999); // Pour mobile
350
+ navigator.clipboard.writeText(urlInput.value)
351
+ .then(() => {
352
+ // Rétroaction visuelle
353
+ copyButton.innerHTML = '<span class="material-symbols-rounded">done</span>';
354
+ copyButton.classList.remove('btn-outline-secondary');
355
+ copyButton.classList.add('btn-success');
356
+ setTimeout(() => {
357
+ copyButton.innerHTML = '<span class="material-symbols-rounded">content_copy</span>';
358
+ copyButton.classList.remove('btn-success');
359
+ copyButton.classList.add('btn-outline-secondary');
360
+ }, 2000);
361
+ })
362
+ .catch(err => {
363
+ console.error('Erreur lors de la copie: ', err);
364
+ alert('Erreur lors de la copie du lien.');
365
+ });
366
+ }
367
+ });
368
+
369
+
370
+ // Gère le bouton Vider (doit être implémenté au backend pour la suppression du dossier)
371
+ clearButton.addEventListener('click', () => {
372
+ alert("Fonction Vider: Une route de suppression doit être implémentée au backend pour supprimer le dossier temporaire et vider la session.");
373
+ });
374
+
375
+ </script>
376
+
377
+ </body>
378
  </html>