Docfile commited on
Commit
460bd1d
·
verified ·
1 Parent(s): 41ab6cb

Upload index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +277 -495
templates/index.html CHANGED
@@ -1,520 +1,302 @@
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>Mariam AI - Analyse d'Image</title>
 
 
 
 
 
7
 
8
- <!-- Tailwind CSS -->
9
- <script src="https://cdn.tailwindcss.com"></script>
10
- <script>
11
- tailwind.config = {
12
- theme: {
13
- extend: {
14
- typography: {
15
- DEFAULT: {
16
- css: {
17
- maxWidth: 'none',
18
- table: {
19
- width: '100%'
20
- }
21
- }
22
- }
23
- }
24
- }
25
- }
26
- }
27
- </script>
28
-
29
- <!-- Autres CDN -->
30
- <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
31
- <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
32
- <script src="https://cdn.jsdelivr.net/npm/[email protected]/moment.min.js"></script>
33
-
34
- <style>
35
- /* Base styles */
36
- .markdown-content {
37
- width: 100%;
38
- max-width: 100%;
39
- overflow-x: auto;
40
- }
41
-
42
- .markdown-content table {
43
- width: 100%;
44
- border-collapse: collapse;
45
- margin: 1rem 0;
46
- }
47
-
48
- .markdown-content th,
49
- .markdown-content td {
50
- border: 1px solid #e5e7eb;
51
- padding: 0.75rem;
52
- text-align: left;
53
- }
54
-
55
- .markdown-content th {
56
- background-color: #f9fafb;
57
- }
58
-
59
- /* Loader */
60
- .loader {
61
- width: 48px;
62
- height: 48px;
63
- border: 5px solid #FFF;
64
- border-bottom-color: #6366F1;
65
- border-radius: 50%;
66
- display: inline-block;
67
- box-sizing: border-box;
68
- animation: rotation 1s linear infinite;
69
- }
70
-
71
- @keyframes rotation {
72
- 0% { transform: rotate(0deg); }
73
- 100% { transform: rotate(360deg); }
74
- }
75
-
76
- /* Animations */
77
- .fade-in {
78
- animation: fadeIn 0.3s ease-in;
79
- }
80
-
81
- @keyframes fadeIn {
82
- 0% { opacity: 0; }
83
- 100% { opacity: 1; }
84
- }
85
-
86
- /* Upload zone styles */
87
- .upload-zone {
88
- border: 2px dashed #6366F1;
89
- border-radius: 0.5rem;
90
- padding: 2rem;
91
- transition: all 0.3s ease;
92
- }
93
-
94
- .upload-zone:hover {
95
- border-color: #4F46E5;
96
- background-color: #EEF2FF;
97
- }
98
-
99
- /* SweetAlert customization */
100
- .swal2-popup {
101
- font-size: 0.875rem;
102
- }
103
- </style>
104
  </head>
105
-
106
- <body class="min-h-screen bg-gray-50">
107
- <header class="bg-white shadow-sm">
108
- <div class="max-w-7xl mx-auto px-4 py-4 sm:px-6 lg:px-8">
109
- <h1 class="text-2xl md:text-3xl font-bold text-indigo-600">Mariam AI</h1>
110
- <p class="mt-1 text-sm text-gray-500">Assistant pour commentaire composé</p>
111
- </div>
 
112
  </header>
113
 
114
- <main class="max-w-7xl mx-auto px-4 py-8 sm:px-6 lg:px-8">
115
- <div class="bg-white rounded-lg shadow p-6 mb-8">
116
- <form id="uploadForm" class="space-y-6">
117
- <div class="upload-zone" id="dropZone">
118
- <input type="file" id="imageInput" accept="image/*" class="hidden">
119
- <label for="imageInput" class="block text-center cursor-pointer">
120
- <svg class="mx-auto h-12 w-12 text-indigo-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
121
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
122
- d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
123
- </svg>
124
- <p id="fileName" class="mt-2 text-sm text-gray-500">
125
- Cliquez ou glissez une image ici
126
- </p>
127
- </label>
128
- </div>
129
-
130
- <div id="imagePreview" class="hidden">
131
- <div class="relative max-w-lg mx-auto">
132
- <img id="preview" src="#" alt="Prévisualisation"
133
- class="rounded-lg shadow-md max-h-[400px] w-full object-contain">
134
- <button type="button" onclick="removeImage()"
135
- class="absolute top-2 right-2 p-2 rounded-full bg-white/90 hover:bg-red-500 hover:text-white
136
- transition-colors duration-200">
137
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
138
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
139
- d="M6 18L18 6M6 6l12 12" />
140
- </svg>
141
- </button>
142
- </div>
143
- </div>
144
-
145
- <div class="flex justify-center">
146
- <button type="submit"
147
- class="px-6 py-3 bg-indigo-600 text-white font-medium rounded-lg
148
- hover:bg-indigo-700 focus:outline-none focus:ring-2
149
- focus:ring-offset-2 focus:ring-indigo-500 transition-colors duration-200">
150
- Analyser l'image
151
- </button>
152
- </div>
153
- </form>
154
- </div>
155
-
156
- <div id="loading" class="hidden">
157
- <div class="flex flex-col items-center justify-center space-y-4">
158
- <span class="loader"></span>
159
- <p class="text-gray-500">Analyse en cours, veuillez patienter...</p>
160
  </div>
 
 
 
 
161
  </div>
162
 
163
- <div id="results" class="space-y-8 hidden">
164
- <div class="flex justify-center space-x-4">
165
- <button id="showDissertation"
166
- class="px-4 py-2 rounded-lg bg-indigo-600 text-white hover:bg-indigo-700
167
- transition-colors duration-200">
168
- Dissertation
169
- </button>
170
- <button id="showTableau"
171
- class="px-4 py-2 rounded-lg bg-white text-indigo-600 hover:bg-indigo-50
172
- transition-colors duration-200">
173
- Tableau
174
- </button>
175
- <button id="showBackups"
176
- class="px-4 py-2 rounded-lg bg-white text-indigo-600 hover:bg-indigo-50
177
- transition-colors duration-200">
178
- Sauvegardes
179
- </button>
180
- </div>
181
-
182
- <div id="dissertationTab" class="tab-content bg-white rounded-lg shadow p-6">
183
- <h2 class="text-xl font-bold text-gray-900 mb-4">Dissertation</h2>
184
- <div id="dissertationResult" class="markdown-content"></div>
185
- <button id="saveDissertation"
186
- class="mt-4 px-6 py-2 bg-green-600 text-white rounded-lg
187
- hover:bg-green-700 transition-colors duration-200">
188
- Sauvegarder la dissertation
189
- </button>
190
- </div>
191
-
192
- <div id="tableauTab" class="tab-content bg-white rounded-lg shadow p-6 hidden">
193
- <h2 class="text-xl font-bold text-gray-900 mb-4">Tableau d'analyse</h2>
194
- <div id="tableauResult" class="markdown-content"></div>
195
- <button id="saveTableau"
196
- class="mt-4 px-6 py-2 bg-green-600 text-white rounded-lg
197
- hover:bg-green-700 transition-colors duration-200">
198
- Sauvegarder le tableau
199
- </button>
200
- </div>
201
-
202
- <div id="backupsTab" class="tab-content bg-white rounded-lg shadow p-6 hidden">
203
- <h2 class="text-xl font-bold text-gray-900 mb-4">Sauvegardes locales</h2>
204
- <div id="backupList" class="space-y-3"></div>
205
- </div>
206
  </div>
207
- </main>
208
-
209
-
210
- <script>
211
- let tableauContent = '';
212
- let dissertationContent = '';
213
- let backups = loadBackups();
214
-
215
- // Gestion du drag & drop
216
- const dropZone = document.getElementById('dropZone');
217
- ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
218
- dropZone.addEventListener(eventName, preventDefaults, false);
219
- });
220
-
221
- function preventDefaults(e) {
222
- e.preventDefault();
223
- e.stopPropagation();
224
- }
225
 
226
- ['dragenter', 'dragover'].forEach(eventName => {
227
- dropZone.addEventListener(eventName, highlight, false);
228
- });
 
229
 
230
- ['dragleave', 'drop'].forEach(eventName => {
231
- dropZone.addEventListener(eventName, unhighlight, false);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  });
233
 
234
- function highlight(e) {
235
- dropZone.classList.add('drag-over');
236
- }
237
-
238
- function unhighlight(e) {
239
- dropZone.classList.remove('drag-over');
240
- }
241
-
242
- dropZone.addEventListener('drop', handleDrop, false);
243
-
244
- function handleDrop(e) {
245
- const dt = e.dataTransfer;
246
- const files = dt.files;
247
-
248
- if (files && files[0]) {
249
- handleFiles(files);
250
- }
251
- }
252
 
253
- function handleFiles(files) {
254
- const imageInput = document.getElementById('imageInput');
255
- imageInput.files = files;
256
- handleImageSelect();
257
  }
258
 
259
- function handleImageSelect() {
260
- const input = document.getElementById('imageInput');
261
- const preview = document.getElementById('preview');
262
- const previewContainer = document.getElementById('imagePreview');
263
- const fileName = document.getElementById('fileName');
264
-
265
- if (input.files && input.files[0]) {
266
- const reader = new FileReader();
267
-
268
- reader.onload = function(e) {
269
- preview.src = e.target.result;
270
- previewContainer.classList.remove('hidden');
271
- previewContainer.classList.add('fade-in');
272
- fileName.textContent = input.files[0].name;
273
- };
274
-
275
- reader.readAsDataURL(input.files[0]);
276
- }
277
- }
278
-
279
- function removeImage() {
280
- const input = document.getElementById('imageInput');
281
- const previewContainer = document.getElementById('imagePreview');
282
- const fileName = document.getElementById('fileName');
283
-
284
- input.value = '';
285
- previewContainer.classList.add('hidden');
286
- fileName.textContent = 'Cliquez ou glissez une image ici';
287
- }
288
-
289
- // Gestion des onglets
290
- document.querySelectorAll('.tab-button').forEach(button => {
291
- button.addEventListener('click', () => {
292
- const tabName = button.id.replace('show', '').toLowerCase();
293
- showTab(tabName);
294
- });
295
  });
296
 
297
- function showTab(tabName) {
298
- document.querySelectorAll('.tab-content').forEach(tab => tab.classList.add('hidden'));
299
- document.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active'));
300
-
301
- const targetTab = document.getElementById(`${tabName}Tab`);
302
- const targetButton = document.getElementById(`show${tabName.charAt(0).toUpperCase() + tabName.slice(1)}`);
303
-
304
- targetTab.classList.remove('hidden');
305
- targetButton.classList.add('active');
306
-
307
- if (tabName === 'tableau') {
308
- renderTableauContent();
309
- } else if (tabName === 'backups') {
310
- updateBackupList();
311
- }
312
- }
313
-
314
- // Gestion des sauvegardes
315
- function loadBackups() {
316
- try {
317
- return JSON.parse(localStorage.getItem('mariamAIBackups')) || [];
318
- } catch {
319
- return [];
320
- }
321
- }
322
-
323
- function saveBackup(type, content) {
324
- const timestamp = moment().format('YYYY-MM-DD HH:mm:ss');
325
- backups.unshift({ timestamp, type, content });
326
- localStorage.setItem('mariamAIBackups', JSON.stringify(backups));
327
- updateBackupList();
328
- Swal.fire({
329
- icon: 'success',
330
- title: 'Sauvegarde réussie!',
331
- toast: true,
332
- position: 'top-end',
333
- showConfirmButton: false,
334
- timer: 3000
335
- });
336
- }
337
-
338
- function updateBackupList() {
339
- const backupList = document.getElementById('backupList');
340
- backupList.innerHTML = backups.length ? '' : '<p class="text-gray-500 text-center py-4">Aucune sauvegarde disponible</p>';
341
-
342
- backups.forEach((backup, index) => {
343
- const div = document.createElement('div');
344
- div.className = 'backup-item';
345
- div.innerHTML = `
346
- <div class="flex-1">
347
- <h3 class="font-medium">${backup.type === 'dissertation' ? 'Dissertation' : 'Tableau'}</h3>
348
- <p class="text-sm text-gray-500">${backup.timestamp}</p>
349
- </div>
350
- <div class="backup-actions">
351
- <button data-index="${index}" class="view-backup px-3 py-1 rounded-lg bg-blue-500 text-white text-sm hover:bg-blue-600">
352
- Voir
353
- </button>
354
- <button data-index="${index}" class="delete-backup px-3 py-1 rounded-lg bg-red-500 text-white text-sm hover:bg-red-600">
355
- Supprimer
356
- </button>
357
- </div>
358
- `;
359
- backupList.appendChild(div);
360
- });
361
-
362
- document.querySelectorAll('.view-backup').forEach(btn =>
363
- btn.addEventListener('click', viewBackup));
364
- document.querySelectorAll('.delete-backup').forEach(btn =>
365
- btn.addEventListener('click', deleteBackup));
366
- }
367
-
368
- function viewBackup(event) {
369
- const backup = backups[event.target.dataset.index];
370
- Swal.fire({
371
- title: backup.type === 'dissertation' ? 'Dissertation sauvegardée' : 'Tableau sauvegardé',
372
- html: marked.parse(backup.content),
373
- width: '90%',
374
- customClass: {
375
- popup: 'swal2-popup-large',
376
- htmlContainer: 'markdown-content px-4'
377
- },
378
- showCloseButton: true,
379
- showConfirmButton: false
380
- });
381
- }
382
-
383
- function deleteBackup(event) {
384
- const index = event.target.dataset.index;
385
- const backup = backups[index];
386
-
387
- Swal.fire({
388
- title: 'Confirmer la suppression',
389
- text: `Voulez-vous vraiment supprimer cette sauvegarde du ${backup.timestamp} ?`,
390
- icon: 'warning',
391
- showCancelButton: true,
392
- confirmButtonColor: '#EF4444',
393
- cancelButtonColor: '#6B7280',
394
- confirmButtonText: 'Supprimer',
395
- cancelButtonText: 'Annuler'
396
- }).then((result) => {
397
- if (result.isConfirmed) {
398
- backups.splice(index, 1);
399
- localStorage.setItem('mariamAIBackups', JSON.stringify(backups));
400
- updateBackupList();
401
-
402
- Swal.fire({
403
- title: 'Supprimé!',
404
- text: 'La sauvegarde a été supprimée.',
405
- icon: 'success',
406
- toast: true,
407
- position: 'top-end',
408
- showConfirmButton: false,
409
- timer: 3000
410
- });
411
- }
412
- });
413
- }
414
-
415
- // Gestion des boutons de sauvegarde
416
- document.getElementById('saveDissertation').addEventListener('click', () => {
417
- if (!dissertationContent.trim()) {
418
- Swal.fire({
419
- icon: 'error',
420
- title: 'Erreur',
421
- text: 'Aucun contenu à sauvegarder'
422
- });
423
- return;
424
- }
425
- saveBackup('dissertation', dissertationContent);
426
  });
427
-
428
- document.getElementById('saveTableau').addEventListener('click', () => {
429
- if (!tableauContent.trim()) {
430
- Swal.fire({
431
- icon: 'error',
432
- title: 'Erreur',
433
- text: 'Aucun contenu à sauvegarder'
434
- });
435
- return;
436
- }
437
- saveBackup('tableau', tableauContent);
438
  });
439
-
440
- // Gestion du formulaire d'upload
441
- document.getElementById('uploadForm').addEventListener('submit', async (e) => {
442
- e.preventDefault();
443
-
444
- const imageInput = document.getElementById('imageInput');
445
- if (!imageInput.files || !imageInput.files[0]) {
446
- Swal.fire({
447
- icon: 'error',
448
- title: 'Aucune image sélectionnée',
449
- text: 'Veuillez sélectionner une image à analyser.'
450
- });
451
- return;
452
- }
453
-
454
- const loading = document.getElementById('loading');
455
- const results = document.getElementById('results');
456
- const dissertationResult = document.getElementById('dissertationResult');
457
-
458
- const formData = new FormData();
459
- formData.append('image', imageInput.files[0]);
460
-
461
- loading.classList.remove('hidden');
462
- results.classList.add('hidden');
463
-
464
- try {
465
- const response = await fetch('/analyze', {
466
- method: 'POST',
467
- body: formData
468
- });
469
-
470
- if (!response.ok) {
471
- throw new Error(`Erreur serveur: ${response.status}`);
472
- }
473
-
474
- const data = await response.json();
475
-
476
- dissertationContent = data.dissertation;
477
- tableauContent = data.tableau;
478
-
479
- // Configuration de marked pour le rendu Markdown
480
- marked.setOptions({
481
- breaks: true,
482
- gfm: true,
483
- tables: true,
484
- smartLists: true,
485
- smartypants: true
486
- });
487
-
488
- dissertationResult.innerHTML = marked.parse(data.dissertation);
489
-
490
- results.classList.remove('hidden');
491
- results.classList.add('fade-in');
492
-
493
- // Scroll vers les résultats
494
- results.scrollIntoView({ behavior: 'smooth', block: 'start' });
495
-
496
- } catch (error) {
497
- console.error("Erreur lors de l'analyse:", error);
498
- Swal.fire({
499
- icon: 'error',
500
- title: 'Erreur',
501
- text: "Une erreur est survenue lors de l'analyse de l'image. Veuillez réessayer."
502
- });
503
- } finally {
504
- loading.classList.add('hidden');
505
- }
506
  });
507
-
508
- function renderTableauContent() {
509
- const tableauResult = document.getElementById('tableauResult');
510
- if (tableauContent) {
511
- tableauResult.innerHTML = marked.parse(tableauContent);
512
- }
513
- }
514
-
515
- // Initialisation
516
- document.getElementById('imageInput').addEventListener('change', handleImageSelect);
517
- updateBackupList();
518
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
519
  </body>
520
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="fr">
3
+ <!-- Le head reste identique jusqu'au script -->
4
  <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Mariam AI - Section Commentaire Composé</title>
8
+ <script src="https://cdn.tailwindcss.com?plugins=typography"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.0/gsap.min.js"></script>
11
+ <style>
12
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
13
 
14
+ body {
15
+ font-family: 'Poppins', sans-serif;
16
+ background: linear-gradient(135deg, #f6f9fc 0%, #edf2f7 100%);
17
+ min-height: 100vh;
18
+ }
19
+
20
+ .spinner {
21
+ border: 3px solid rgba(79, 70, 229, 0.1);
22
+ width: 2.5rem;
23
+ height: 2.5rem;
24
+ border-radius: 50%;
25
+ border-left-color: #4f46e5;
26
+ animation: spin 1s cubic-bezier(0.4, 0, 0.2, 1) infinite;
27
+ }
28
+
29
+ @keyframes spin {
30
+ to { transform: rotate(360deg); }
31
+ }
32
+
33
+ .card-shadow {
34
+ box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.1);
35
+ }
36
+
37
+ .prose table {
38
+ width: 100%;
39
+ border-collapse: separate;
40
+ border-spacing: 0;
41
+ border-radius: 8px;
42
+ overflow: hidden;
43
+ }
44
+
45
+ .prose th,
46
+ .prose td {
47
+ border: 1px solid #e5e7eb;
48
+ padding: 1rem;
49
+ background: white;
50
+ }
51
+
52
+ .prose th {
53
+ background: #f8fafc;
54
+ font-weight: 600;
55
+ text-transform: uppercase;
56
+ font-size: 0.875rem;
57
+ letter-spacing: 0.05em;
58
+ }
59
+
60
+ .file-upload-area {
61
+ border: 2px dashed #e2e8f0;
62
+ border-radius: 1rem;
63
+ transition: all 0.3s ease;
64
+ }
65
+
66
+ .file-upload-area:hover {
67
+ border-color: #4f46e5;
68
+ background: rgba(79, 70, 229, 0.05);
69
+ }
70
+
71
+ .animated-button {
72
+ transition: transform 0.2s;
73
+ }
74
+
75
+ .animated-button:hover {
76
+ transform: translateY(-2px);
77
+ }
78
+ </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  </head>
80
+ <body class="py-8 px-4">
81
+ <!-- Le HTML reste identique -->
82
+ <div class="max-w-4xl mx-auto">
83
+ <header class="text-center mb-12">
84
+ <h1 class="text-5xl font-bold bg-gradient-to-r from-indigo-600 to-blue-500 bg-clip-text text-transparent">
85
+ Mariam AI
86
+ </h1>
87
+ <p class="text-xl text-gray-600 mt-2">Section Commentaire Composé</p>
88
  </header>
89
 
90
+ <div class="bg-white rounded-2xl card-shadow p-8">
91
+ <form id="uploadForm" class="mb-8">
92
+ <div class="file-upload-area p-8 text-center">
93
+ <label for="imageInput" class="block cursor-pointer">
94
+ <div class="mb-4">
95
+ <svg class="w-12 h-12 mx-auto text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
96
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
97
+ </svg>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  </div>
99
+ <span class="text-lg font-medium text-gray-700">Sélectionnez une image</span>
100
+ <p class="text-sm text-gray-500 mt-1">ou glissez-déposez votre fichier ici</p>
101
+ </label>
102
+ <input type="file" id="imageInput" name="image" accept="image/*" required class="hidden">
103
  </div>
104
 
105
+ <div id="previewContainer" class="mt-6 hidden">
106
+ <p class="text-gray-700 font-medium mb-3">Aperçu :</p>
107
+ <div class="relative rounded-xl overflow-hidden shadow-lg">
108
+ <img id="previewImage" src="#" alt="Prévisualisation" class="w-full max-h-80 object-contain">
109
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
+ <button type="submit" class="animated-button w-full mt-6 bg-gradient-to-r from-indigo-600 to-blue-500 text-white font-semibold py-3 px-6 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300">
113
+ Lancer l'analyse
114
+ </button>
115
+ </form>
116
 
117
+ <div id="loading" class="hidden">
118
+ <div class="flex flex-col items-center space-y-4 py-8">
119
+ <div class="spinner"></div>
120
+ <span class="text-gray-700 font-medium">Analyse en cours...</span>
121
+ </div>
122
+ </div>
123
+
124
+ <button id="toggleButton" class="animated-button hidden w-full mb-6 bg-gradient-to-r from-green-500 to-emerald-500 text-white font-semibold py-3 px-6 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300">
125
+ Afficher le Tableau
126
+ </button>
127
+
128
+ <div id="result" class="hidden space-y-8">
129
+ <section>
130
+ <h2 class="text-2xl font-semibold text-gray-800 mb-4">Dissertation</h2>
131
+ <div id="dissertationContent" class="prose prose-lg max-w-none bg-gray-50 rounded-xl p-8"></div>
132
+ </section>
133
+
134
+ <section id="tableauSection" class="hidden">
135
+ <h2 class="text-2xl font-semibold text-gray-800 mb-4">Tableau d'analyse</h2>
136
+ <div id="tableauContent" class="prose prose-lg max-w-none bg-gray-50 rounded-xl p-8"></div>
137
+ </section>
138
+ </div>
139
+ </div>
140
+ </div>
141
+
142
+ <script>
143
+ // Configuration des variables
144
+ const config = {
145
+ uploadForm: document.getElementById('uploadForm'),
146
+ imageInput: document.getElementById('imageInput'),
147
+ previewContainer: document.getElementById('previewContainer'),
148
+ previewImage: document.getElementById('previewImage'),
149
+ loadingIndicator: document.getElementById('loading'),
150
+ resultDiv: document.getElementById('result'),
151
+ toggleButton: document.getElementById('toggleButton'),
152
+ tableauSection: document.getElementById('tableauSection'),
153
+ tableauContent: document.getElementById('tableauContent'),
154
+ dissertationContent: document.getElementById('dissertationContent')
155
+ };
156
+
157
+ // Gestion de l'aperçu des fichiers
158
+ function handleFilePreview(file) {
159
+ if (file) {
160
+ const reader = new FileReader();
161
+ reader.onload = function(e) {
162
+ config.previewImage.src = e.target.result;
163
+ config.previewContainer.classList.remove('hidden');
164
+ gsap.from(config.previewContainer, {
165
+ opacity: 0,
166
+ y: 20,
167
+ duration: 0.3
168
+ });
169
+ };
170
+ reader.readAsDataURL(file);
171
+ }
172
+ }
173
+
174
+ // Gestionnaire d'événements pour l'input file
175
+ config.imageInput.addEventListener('change', (e) => {
176
+ handleFilePreview(e.target.files[0]);
177
+ });
178
+
179
+ // Fonction pour afficher/masquer le loading
180
+ function toggleLoading(show) {
181
+ if (show) {
182
+ config.loadingIndicator.classList.remove('hidden');
183
+ config.resultDiv.classList.add('hidden');
184
+ config.toggleButton.classList.add('hidden');
185
+ config.tableauSection.classList.add('hidden');
186
+ } else {
187
+ config.loadingIndicator.classList.add('hidden');
188
+ }
189
+ }
190
+
191
+ // Fonction pour gérer la soumission du formulaire
192
+ async function handleFormSubmit(e) {
193
+ e.preventDefault();
194
+
195
+ toggleLoading(true);
196
+
197
+ try {
198
+ const formData = new FormData(config.uploadForm);
199
+ const response = await fetch('/analyze', {
200
+ method: 'POST',
201
+ body: formData
202
  });
203
 
204
+ const data = await response.json();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
 
206
+ if (!response.ok) {
207
+ throw new Error(data.error || 'Une erreur est survenue');
 
 
208
  }
209
 
210
+ // Mise à jour du contenu
211
+ config.dissertationContent.innerHTML = marked.parse(data.dissertation);
212
+ config.tableauContent.innerHTML = marked.parse(data.tableau);
213
+
214
+ // Affichage des résultats
215
+ config.resultDiv.classList.remove('hidden');
216
+ config.toggleButton.classList.remove('hidden');
217
+ config.tableauSection.classList.add('hidden');
218
+ config.toggleButton.textContent = 'Afficher le Tableau';
219
+
220
+ // Animation des résultats
221
+ gsap.from([config.resultDiv, config.toggleButton], {
222
+ opacity: 0,
223
+ y: 20,
224
+ duration: 0.3,
225
+ stagger: 0.1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  });
227
 
228
+ } catch (error) {
229
+ console.error('Erreur:', error);
230
+ alert(error.message || "Une erreur est survenue lors de l'analyse.");
231
+ } finally {
232
+ toggleLoading(false);
233
+ }
234
+ }
235
+
236
+ // Gestionnaire de soumission du formulaire
237
+ config.uploadForm.addEventListener('submit', handleFormSubmit);
238
+
239
+ // Gestion du toggle du tableau
240
+ config.toggleButton.addEventListener('click', function() {
241
+ const isHidden = config.tableauSection.classList.contains('hidden');
242
+
243
+ if (isHidden) {
244
+ config.tableauSection.classList.remove('hidden');
245
+ this.textContent = 'Masquer le Tableau';
246
+ gsap.from(config.tableauSection, {
247
+ opacity: 0,
248
+ y: 20,
249
+ duration: 0.3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  });
251
+ } else {
252
+ gsap.to(config.tableauSection, {
253
+ opacity: 0,
254
+ y: 20,
255
+ duration: 0.2,
256
+ onComplete: () => {
257
+ config.tableauSection.classList.add('hidden');
258
+ this.textContent = 'Afficher le Tableau';
259
+ }
 
 
260
  });
261
+ }
262
+ });
263
+
264
+ // Gestion du drag & drop
265
+ function initDragAndDrop() {
266
+ const dropArea = document.querySelector('.file-upload-area');
267
+
268
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
269
+ dropArea.addEventListener(eventName, (e) => {
270
+ e.preventDefault();
271
+ e.stopPropagation();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  });
273
+ });
274
+
275
+ function highlight() {
276
+ dropArea.classList.add('border-indigo-600', 'bg-indigo-50');
277
+ }
278
+
279
+ function unhighlight() {
280
+ dropArea.classList.remove('border-indigo-600', 'bg-indigo-50');
281
+ }
282
+
283
+ ['dragenter', 'dragover'].forEach(eventName => {
284
+ dropArea.addEventListener(eventName, highlight);
285
+ });
286
+
287
+ ['dragleave', 'drop'].forEach(eventName => {
288
+ dropArea.addEventListener(eventName, unhighlight);
289
+ });
290
+
291
+ dropArea.addEventListener('drop', (e) => {
292
+ const dt = e.dataTransfer;
293
+ config.imageInput.files = dt.files;
294
+ handleFilePreview(dt.files[0]);
295
+ });
296
+ }
297
+
298
+ // Initialisation du drag & drop
299
+ initDragAndDrop();
300
+ </script>
301
  </body>
302
  </html>