Docfile commited on
Commit
352039a
·
verified ·
1 Parent(s): 90ab9e6

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +221 -276
templates/index.html CHANGED
@@ -1,172 +1,194 @@
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 Données Synthétiques</title>
7
- <script src="https://cdn.tailwindcss.com"></script>
8
- <script>
9
- tailwind.config = {
10
- theme: {
11
- extend: {
12
- animation: {
13
- 'shimmer': 'shimmer 2s infinite',
14
- 'bounce-in': 'bounceIn 0.5s ease-out',
15
- 'slide-in': 'slideIn 0.3s ease-out'
16
- },
17
- keyframes: {
18
- shimmer: {
19
- '0%': { transform: 'translateX(-100%)' },
20
- '100%': { transform: 'translateX(100%)' }
21
- },
22
- bounceIn: {
23
- '0%': { transform: 'scale(0.3)', opacity: '0' },
24
- '50%': { transform: 'scale(1.05)' },
25
- '100%': { transform: 'scale(1)', opacity: '1' }
26
- },
27
- slideIn: {
28
- '0%': { transform: 'translateX(100%)', opacity: '0' },
29
- '100%': { transform: 'translateX(0)', opacity: '1' }
30
- }
31
- }
32
- }
33
- }
34
  }
35
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  </head>
37
- <body class="min-h-screen bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500 flex items-center justify-center p-5">
38
- <div class="bg-white rounded-3xl shadow-2xl p-8 max-w-4xl w-full backdrop-blur-sm">
39
- <!-- Header -->
40
- <div class="text-center mb-10">
41
- <h1 class="text-4xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent mb-3">
42
- 🤖 Générateur de Données Synthétiques
43
- </h1>
44
- <p class="text-gray-600 text-lg">Uploadez votre fichier pour générer 470 jeux de données synthétiques</p>
45
  </div>
46
-
47
- <!-- Upload Section -->
48
- <div id="uploadSection" class="border-3 border-dashed border-gray-300 rounded-2xl p-10 text-center mb-8 transition-all duration-300 hover:border-indigo-500 hover:bg-indigo-50/30">
49
- <div class="space-y-4">
50
- <h3 class="text-xl font-semibold text-gray-700">📁 Sélectionnez votre fichier</h3>
51
- <p class="text-gray-500">Glissez-déposez votre fichier ici ou cliquez pour sélectionner</p>
52
- <input type="file" id="fileInput" accept=".txt" class="hidden">
53
- <div class="space-x-4">
54
- <button onclick="document.getElementById('fileInput').click()"
55
- class="bg-gradient-to-r from-indigo-500 to-purple-600 text-white px-8 py-3 rounded-full font-semibold transition-all duration-300 hover:scale-105 hover:shadow-lg">
56
- Choisir le fichier
57
- </button>
58
- <button id="startBtn" onclick="startGeneration()"
59
- class="hidden bg-gradient-to-r from-green-500 to-emerald-600 text-white px-8 py-3 rounded-full font-semibold transition-all duration-300 hover:scale-105 hover:shadow-lg">
60
- 🚀 Démarrer la génération
61
- </button>
62
- </div>
63
  </div>
64
  </div>
65
-
66
- <!-- Progress Section -->
67
- <div id="progressSection" class="hidden space-y-6">
68
- <div class="text-center">
69
- <h3 class="text-2xl font-bold text-gray-800 mb-4">⏳ Génération en cours...</h3>
70
- <div class="w-full bg-gray-200 rounded-full h-6 overflow-hidden">
71
- <div id="progressFill" class="h-full bg-gradient-to-r from-indigo-500 to-purple-600 transition-all duration-500 relative">
72
- <div class="absolute inset-0 bg-gradient-to-r from-transparent via-white/30 to-transparent animate-shimmer"></div>
73
- </div>
74
- </div>
75
- </div>
76
-
77
- <!-- Status Cards -->
78
- <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
79
- <div class="bg-gradient-to-br from-blue-50 to-indigo-100 p-6 rounded-xl border-l-4 border-indigo-500">
80
- <h4 class="font-semibold text-gray-700 mb-2">Progrès</h4>
81
- <p id="progressText" class="text-2xl font-bold text-indigo-600">0 / 470</p>
82
- </div>
83
- <div class="bg-gradient-to-br from-green-50 to-emerald-100 p-6 rounded-xl border-l-4 border-emerald-500">
84
- <h4 class="font-semibold text-gray-700 mb-2">Pourcentage</h4>
85
- <p id="percentageText" class="text-2xl font-bold text-emerald-600">0%</p>
86
- </div>
87
- <div class="bg-gradient-to-br from-purple-50 to-violet-100 p-6 rounded-xl border-l-4 border-violet-500">
88
- <h4 class="font-semibold text-gray-700 mb-2">Statut</h4>
89
- <p id="statusText" class="text-lg font-bold text-violet-600">En attente</p>
90
- </div>
91
- <div class="bg-gradient-to-br from-orange-50 to-amber-100 p-6 rounded-xl border-l-4 border-amber-500">
92
- <h4 class="font-semibold text-gray-700 mb-2">Heure de début</h4>
93
- <p id="startTimeText" class="text-lg font-bold text-amber-600">-</p>
94
- </div>
95
  </div>
96
-
97
- <!-- Download Button -->
98
- <div class="text-center">
99
- <button id="downloadBtn" onclick="downloadResults()"
100
- class="hidden bg-gradient-to-r from-green-500 to-emerald-600 text-white px-8 py-3 rounded-full font-semibold transition-all duration-300 hover:scale-105 hover:shadow-lg">
101
- 📥 Télécharger les résultats
102
- </button>
103
- <!-- Bouton de téléchargement en cours -->
104
- <button id="downloadProgressBtn" onclick="downloadCurrentResults()"
105
- class="hidden bg-gradient-to-r from-yellow-500 to-orange-500 text-white px-8 py-3 rounded-full font-semibold transition-all duration-300 hover:scale-105 hover:shadow-lg">
106
- 📥 Télécharger les données actuelles
107
- </button>
108
  </div>
109
  </div>
110
-
111
- <!-- Tasks Section -->
112
- <div class="mt-10 pt-8 border-t-2 border-gray-200">
113
- <h3 class="text-xl font-bold text-gray-800 mb-6">📋 Tâches précédentes</h3>
114
- <div id="tasksList" class="space-y-4"></div>
115
  </div>
116
  </div>
117
-
118
- <!-- Notification -->
119
- <div id="notification" class="fixed top-5 right-5 px-6 py-4 rounded-xl text-white font-semibold transform translate-x-full transition-transform duration-300 z-50"></div>
120
-
121
  <script>
 
122
  let currentTaskId = null;
123
- let statusInterval = null;
124
-
125
- // Gestion du drag & drop
126
- const uploadSection = document.getElementById('uploadSection');
127
  const fileInput = document.getElementById('fileInput');
128
-
129
- uploadSection.addEventListener('dragover', (e) => {
 
130
  e.preventDefault();
131
- uploadSection.classList.add('border-indigo-500', 'bg-indigo-50/50');
132
  });
133
-
134
- uploadSection.addEventListener('dragleave', () => {
135
- uploadSection.classList.remove('border-indigo-500', 'bg-indigo-50/50');
136
  });
137
-
138
- uploadSection.addEventListener('drop', (e) => {
139
  e.preventDefault();
140
- uploadSection.classList.remove('border-indigo-500', 'bg-indigo-50/50');
141
-
142
  const files = e.dataTransfer.files;
143
  if (files.length > 0) {
144
- fileInput.files = files;
145
- handleFileSelect();
146
  }
147
  });
148
-
149
- fileInput.addEventListener('change', handleFileSelect);
150
-
151
- function handleFileSelect() {
152
- const file = fileInput.files[0];
153
- if (file) {
154
- document.getElementById('startBtn').classList.remove('hidden');
155
- document.getElementById('startBtn').classList.add('animate-bounce-in');
156
- showNotification(`Fichier sélectionné: ${file.name}`, 'success');
157
- }
158
- }
159
-
160
- function startGeneration() {
161
- const file = fileInput.files[0];
162
- if (!file) {
163
- showNotification('Veuillez sélectionner un fichier', 'error');
164
- return;
165
  }
166
-
 
 
167
  const formData = new FormData();
168
  formData.append('file', file);
169
-
 
 
170
  fetch('/upload', {
171
  method: 'POST',
172
  body: formData
@@ -174,155 +196,78 @@
174
  .then(response => response.json())
175
  .then(data => {
176
  if (data.error) {
177
- showNotification(data.error, 'error');
178
  } else {
179
  currentTaskId = data.task_id;
180
- document.getElementById('progressSection').classList.remove('hidden');
181
- document.getElementById('downloadProgressBtn').classList.remove('hidden');
182
- startStatusPolling();
183
- showNotification('Génération démarrée!', 'success');
184
  }
185
  })
186
  .catch(error => {
187
- showNotification('Erreur lors du démarrage', 'error');
188
  console.error('Error:', error);
189
  });
190
  }
191
-
192
- function startStatusPolling() {
193
- if (statusInterval) clearInterval(statusInterval);
 
194
 
195
- statusInterval = setInterval(() => {
196
- if (currentTaskId) {
197
- fetch(`/status/${currentTaskId}`)
198
- .then(response => response.json())
199
- .then(data => {
200
- updateProgress(data);
201
- if (data.status === 'completed') {
202
- clearInterval(statusInterval);
203
- document.getElementById('downloadBtn').classList.remove('hidden');
204
- document.getElementById('downloadProgressBtn').classList.add('hidden');
205
- showNotification('Génération terminée!', 'success');
206
- }
207
- })
208
- .catch(console.error);
209
- }
210
- }, 2000);
211
  }
212
-
213
- function updateProgress(data) {
214
- const progressFill = document.getElementById('progressFill');
215
- const progressText = document.getElementById('progressText');
216
- const percentageText = document.getElementById('percentageText');
217
- const statusText = document.getElementById('statusText');
218
- const startTimeText = document.getElementById('startTimeText');
219
-
220
- progressFill.style.width = `${data.percentage}%`;
221
- progressText.textContent = `${data.progress} / ${data.total}`;
222
- percentageText.textContent = `${data.percentage}%`;
223
- statusText.textContent = data.status === 'running' ? 'En cours' :
224
- data.status === 'completed' ? 'Terminé' : data.status;
225
- startTimeText.textContent = data.start_time;
226
  }
227
-
228
- function downloadResults() {
229
- if (currentTaskId) {
230
- window.location.href = `/download/${currentTaskId}`;
 
231
  }
232
- }
233
-
234
- function downloadCurrentResults() {
235
- if (currentTaskId) {
236
- window.location.href = `/download/${currentTaskId}?partial=true`;
237
- showNotification('Téléchargement des données actuelles...', 'success');
 
 
 
 
 
238
  }
239
- }
240
-
241
- function showNotification(message, type) {
242
- const notification = document.getElementById('notification');
243
- notification.textContent = message;
244
-
245
- // Reset classes
246
- notification.className = 'fixed top-5 right-5 px-6 py-4 rounded-xl text-white font-semibold transform translate-x-full transition-transform duration-300 z-50';
247
-
248
- // Add type-specific classes
249
- if (type === 'success') {
250
- notification.classList.add('bg-gradient-to-r', 'from-green-500', 'to-emerald-600');
251
- } else if (type === 'error') {
252
- notification.classList.add('bg-gradient-to-r', 'from-red-500', 'to-rose-600');
253
  }
254
-
255
- // Show notification
256
- setTimeout(() => {
257
- notification.classList.remove('translate-x-full');
258
- notification.classList.add('animate-slide-in');
259
- }, 100);
260
-
261
- // Hide notification after 3 seconds
262
- setTimeout(() => {
263
- notification.classList.add('translate-x-full');
264
- }, 3000);
265
- }
266
-
267
- function loadTasks() {
268
- fetch('/tasks')
269
- .then(response => response.json())
270
- .then(tasks => {
271
- const tasksList = document.getElementById('tasksList');
272
- tasksList.innerHTML = '';
273
-
274
- tasks.forEach(task => {
275
- const taskItem = document.createElement('div');
276
- taskItem.className = `bg-gradient-to-r from-gray-50 to-gray-100 p-6 rounded-xl border-l-4 ${
277
- task.status === 'completed' ? 'border-green-500' :
278
- task.status === 'running' ? 'border-blue-500' : 'border-gray-400'
279
- } transition-all duration-300 hover:shadow-md`;
280
-
281
- taskItem.innerHTML = `
282
- <div class="flex justify-between items-center">
283
- <div class="space-y-2">
284
- <div class="flex items-center space-x-3">
285
- <span class="font-bold text-gray-800">Tâche ${task.id.substring(0, 8)}...</span>
286
- <span class="px-3 py-1 rounded-full text-sm font-medium ${
287
- task.status === 'completed' ? 'bg-green-100 text-green-800' :
288
- task.status === 'running' ? 'bg-blue-100 text-blue-800' : 'bg-gray-100 text-gray-800'
289
- }">
290
- ${task.status === 'completed' ? 'Terminé' :
291
- task.status === 'running' ? 'En cours' : task.status}
292
- </span>
293
- </div>
294
- <div class="text-sm text-gray-600">
295
- <span>Démarré: ${task.start_time}</span> |
296
- <span>Progrès: ${task.progress}/${task.total} (${task.percentage}%)</span>
297
- </div>
298
- </div>
299
- <div class="space-x-2">
300
- ${task.status === 'completed' ?
301
- `<button onclick="window.location.href='/download/${task.id}'"
302
- class="bg-gradient-to-r from-green-500 to-emerald-600 text-white px-4 py-2 rounded-lg font-medium transition-all duration-300 hover:scale-105">
303
- Télécharger
304
- </button>` :
305
- task.status === 'running' ?
306
- `<button onclick="window.location.href='/download/${task.id}?partial=true'"
307
- class="bg-gradient-to-r from-yellow-500 to-orange-500 text-white px-4 py-2 rounded-lg font-medium transition-all duration-300 hover:scale-105">
308
- Télécharger actuel
309
- </button>` :
310
- `<span class="text-gray-500 font-medium">${task.status}</span>`
311
- }
312
- </div>
313
- </div>
314
- `;
315
- tasksList.appendChild(taskItem);
316
- });
317
- })
318
- .catch(console.error);
319
- }
320
-
321
- // Charger les tâches au démarrage
322
- loadTasks();
323
-
324
- // Recharger les tâches toutes les 30 secondes
325
- setInterval(loadTasks, 30000);
326
  </script>
327
  </body>
328
  </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>Résolveur Mathématique IMO</title>
6
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.4/socket.io.js"></script>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body {
10
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
11
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
12
+ min-height: 100vh;
13
+ padding: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  }
15
+ .container {
16
+ max-width: 1200px;
17
+ margin: 0 auto;
18
+ background: white;
19
+ border-radius: 15px;
20
+ box-shadow: 0 20px 40px rgba(0,0,0,0.1);
21
+ overflow: hidden;
22
+ }
23
+ .header {
24
+ background: linear-gradient(135deg, #2196F3, #21CBF3);
25
+ padding: 30px;
26
+ text-align: center;
27
+ color: white;
28
+ }
29
+ .header h1 { font-size: 2.5em; margin-bottom: 10px; }
30
+ .header p { font-size: 1.1em; opacity: 0.9; }
31
+
32
+ .upload-section {
33
+ padding: 40px;
34
+ text-align: center;
35
+ border-bottom: 1px solid #eee;
36
+ }
37
+ .upload-box {
38
+ border: 3px dashed #ddd;
39
+ border-radius: 10px;
40
+ padding: 40px;
41
+ transition: all 0.3s ease;
42
+ cursor: pointer;
43
+ }
44
+ .upload-box:hover { border-color: #2196F3; background: #f8f9ff; }
45
+ .upload-box.dragover { border-color: #2196F3; background: #e3f2fd; }
46
+
47
+ .btn {
48
+ background: linear-gradient(135deg, #2196F3, #21CBF3);
49
+ color: white;
50
+ padding: 12px 30px;
51
+ border: none;
52
+ border-radius: 25px;
53
+ font-size: 16px;
54
+ cursor: pointer;
55
+ transition: all 0.3s ease;
56
+ }
57
+ .btn:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(33,150,243,0.3); }
58
+
59
+ .logs-section {
60
+ display: flex;
61
+ height: 600px;
62
+ }
63
+ .extracted-text {
64
+ flex: 1;
65
+ padding: 20px;
66
+ border-right: 1px solid #eee;
67
+ }
68
+ .logs-panel {
69
+ flex: 1;
70
+ padding: 20px;
71
+ }
72
+ .log-container {
73
+ height: 500px;
74
+ overflow-y: auto;
75
+ background: #1e1e1e;
76
+ color: #fff;
77
+ padding: 15px;
78
+ border-radius: 8px;
79
+ font-family: 'Courier New', monospace;
80
+ font-size: 13px;
81
+ }
82
+ .log-entry {
83
+ margin-bottom: 5px;
84
+ padding: 5px;
85
+ border-radius: 3px;
86
+ }
87
+ .log-info { color: #4CAF50; }
88
+ .log-success { color: #8BC34A; background: rgba(139,195,74,0.1); }
89
+ .log-warning { color: #FF9800; }
90
+ .log-error { color: #F44336; background: rgba(244,67,54,0.1); }
91
+
92
+ .status-bar {
93
+ padding: 20px;
94
+ background: #f5f5f5;
95
+ text-align: center;
96
+ }
97
+ .status-badge {
98
+ display: inline-block;
99
+ padding: 8px 16px;
100
+ border-radius: 20px;
101
+ font-weight: bold;
102
+ text-transform: uppercase;
103
+ }
104
+ .status-processing { background: #FFC107; color: #333; }
105
+ .status-completed { background: #4CAF50; color: white; }
106
+ .status-failed { background: #F44336; color: white; }
107
+
108
+ .download-section {
109
+ padding: 20px;
110
+ text-align: center;
111
+ display: none;
112
+ }
113
+ .hidden { display: none; }
114
+ </style>
115
  </head>
116
+ <body>
117
+ <div class="container">
118
+ <div class="header">
119
+ <h1>🧮 Résolveur Mathématique IMO</h1>
120
+ <p>Uploadez une image de votre problème mathématique et obtenez une solution rigoureuse</p>
 
 
 
121
  </div>
122
+
123
+ <div class="upload-section">
124
+ <div class="upload-box" id="uploadBox">
125
+ <h3>📁 Glisser-déposer votre image ici</h3>
126
+ <p>ou cliquez pour sélectionner un fichier</p>
127
+ <input type="file" id="fileInput" accept="image/*" style="display: none;">
128
+ <button class="btn" onclick="document.getElementById('fileInput').click()">
129
+ Choisir un fichier
130
+ </button>
 
 
 
 
 
 
 
 
131
  </div>
132
  </div>
133
+
134
+ <div class="status-bar">
135
+ <div id="statusBadge" class="status-badge" style="display: none;">En attente</div>
136
+ <div id="taskInfo" style="margin-top: 10px; display: none;"></div>
137
+ </div>
138
+
139
+ <div class="logs-section hidden" id="logsSection">
140
+ <div class="extracted-text">
141
+ <h3>📝 Texte extrait de l'image</h3>
142
+ <div id="extractedText" style="background: #f9f9f9; padding: 15px; border-radius: 8px; margin-top: 10px; white-space: pre-wrap; max-height: 450px; overflow-y: auto;"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  </div>
144
+ <div class="logs-panel">
145
+ <h3>📊 Logs de traitement en temps réel</h3>
146
+ <div id="logContainer" class="log-container"></div>
 
 
 
 
 
 
 
 
 
147
  </div>
148
  </div>
149
+
150
+ <div class="download-section" id="downloadSection">
151
+ <h3>✅ Solution prête !</h3>
152
+ <button class="btn" id="downloadBtn">📥 Télécharger la solution</button>
 
153
  </div>
154
  </div>
 
 
 
 
155
  <script>
156
+ const socket = io();
157
  let currentTaskId = null;
158
+
159
+ // Gestion de l'upload
160
+ const uploadBox = document.getElementById('uploadBox');
 
161
  const fileInput = document.getElementById('fileInput');
162
+
163
+ uploadBox.addEventListener('click', () => fileInput.click());
164
+ uploadBox.addEventListener('dragover', (e) => {
165
  e.preventDefault();
166
+ uploadBox.classList.add('dragover');
167
  });
168
+ uploadBox.addEventListener('dragleave', () => {
169
+ uploadBox.classList.remove('dragover');
 
170
  });
171
+ uploadBox.addEventListener('drop', (e) => {
 
172
  e.preventDefault();
173
+ uploadBox.classList.remove('dragover');
 
174
  const files = e.dataTransfer.files;
175
  if (files.length > 0) {
176
+ uploadFile(files[0]);
 
177
  }
178
  });
179
+
180
+ fileInput.addEventListener('change', (e) => {
181
+ if (e.target.files.length > 0) {
182
+ uploadFile(e.target.files[0]);
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  }
184
+ });
185
+
186
+ function uploadFile(file) {
187
  const formData = new FormData();
188
  formData.append('file', file);
189
+
190
+ updateStatus('processing', 'Upload en cours...');
191
+
192
  fetch('/upload', {
193
  method: 'POST',
194
  body: formData
 
196
  .then(response => response.json())
197
  .then(data => {
198
  if (data.error) {
199
+ updateStatus('failed', data.error);
200
  } else {
201
  currentTaskId = data.task_id;
202
+ document.getElementById('extractedText').textContent = data.extracted_text;
203
+ document.getElementById('logsSection').classList.remove('hidden');
204
+ updateStatus('processing', 'Résolution en cours...');
 
205
  }
206
  })
207
  .catch(error => {
208
+ updateStatus('failed', 'Erreur lors de l\'upload');
209
  console.error('Error:', error);
210
  });
211
  }
212
+
213
+ function updateStatus(status, message) {
214
+ const badge = document.getElementById('statusBadge');
215
+ const info = document.getElementById('taskInfo');
216
 
217
+ badge.style.display = 'inline-block';
218
+ badge.className = `status-badge status-${status}`;
219
+ badge.textContent = status === 'processing' ? 'En cours' :
220
+ status === 'completed' ? 'Terminé' : 'Échec';
221
+
222
+ info.style.display = 'block';
223
+ info.textContent = message;
 
 
 
 
 
 
 
 
 
224
  }
225
+
226
+ function addLog(timestamp, level, message) {
227
+ const container = document.getElementById('logContainer');
228
+ const logEntry = document.createElement('div');
229
+ logEntry.className = `log-entry log-${level}`;
230
+ logEntry.innerHTML = `<span style="color: #666;">[${timestamp}]</span> ${message}`;
231
+ container.appendChild(logEntry);
232
+ container.scrollTop = container.scrollHeight;
 
 
 
 
 
 
233
  }
234
+
235
+ // WebSocket events
236
+ socket.on('log_update', (data) => {
237
+ if (data.task_id === currentTaskId) {
238
+ addLog(data.log.timestamp, data.log.level, data.log.message);
239
  }
240
+ });
241
+
242
+ socket.on('task_completed', (data) => {
243
+ if (data.task_id === currentTaskId) {
244
+ updateStatus('completed', 'Solution générée avec succès !');
245
+ const downloadSection = document.getElementById('downloadSection');
246
+ downloadSection.style.display = 'block';
247
+
248
+ document.getElementById('downloadBtn').onclick = () => {
249
+ window.location.href = `/download/${currentTaskId}`;
250
+ };
251
  }
252
+ });
253
+
254
+ socket.on('task_failed', (data) => {
255
+ if (data.task_id === currentTaskId) {
256
+ updateStatus('failed', 'Échec de la résolution (solution partielle disponible)');
257
+ const downloadSection = document.getElementById('downloadSection');
258
+ downloadSection.style.display = 'block';
259
+
260
+ document.getElementById('downloadBtn').onclick = () => {
261
+ window.location.href = `/download/${currentTaskId}`;
262
+ };
 
 
 
263
  }
264
+ });
265
+
266
+ socket.on('task_error', (data) => {
267
+ if (data.task_id === currentTaskId) {
268
+ updateStatus('failed', `Erreur: ${data.error}`);
269
+ }
270
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  </script>
272
  </body>
273
  </html>