Docfile commited on
Commit
7f981b5
·
verified ·
1 Parent(s): 7bfe8df

Update templates/histoire.html

Browse files
Files changed (1) hide show
  1. templates/histoire.html +247 -104
templates/histoire.html CHANGED
@@ -4,27 +4,27 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Histoire et Géographie - Assistant Académique</title>
7
-
8
  <!-- Feuilles de style et polices -->
9
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/aos.css" rel="stylesheet">
10
  <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
11
-
12
  <!-- Scripts et Tailwind -->
13
  <script src="https://cdn.tailwindcss.com"></script>
14
  <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.0.2/marked.min.js"></script>
15
  <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js"></script>
16
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/aos.js"></script>
17
-
18
  <style>
19
  /* Police et réglages de base */
20
  body {
21
  font-family: 'Poppins', sans-serif;
22
- background-color: #f1f5f9;
23
  scroll-behavior: smooth;
24
  }
25
  /* Couleur et dégradé de la navigation et du footer */
26
  .gradient-bg {
27
- background: linear-gradient(120deg, #1a365d 0%, #2563eb 100%);
28
  }
29
  /* Cartes et transitions */
30
  .card {
@@ -48,16 +48,24 @@
48
  }
49
  .form-input:focus {
50
  transform: scale(1.01);
51
- border-color: #2563eb;
52
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.3);
53
  }
 
 
 
 
 
 
 
 
54
  /* Prévisualisation d'images */
55
  .image-preview {
56
  max-width: 200px;
57
  max-height: 200px;
58
  margin: 10px;
59
  border: 1px solid #ccc;
60
- border-radius: 0.375rem;
61
  }
62
  /* Style Markdown optimisé */
63
  .markdown-output {
@@ -71,7 +79,7 @@
71
  line-height: 1.6;
72
  font-size: 16px;
73
  }
74
- .markdown-output h1, .markdown-output h2,
75
  .markdown-output h3, .markdown-output h4 {
76
  margin: 1.5em 0 0.5em;
77
  line-height: 1.3;
@@ -84,6 +92,7 @@
84
  margin: 0.5em 0;
85
  line-height: 1.4;
86
  }
 
87
  @media (max-width: 768px) {
88
  .markdown-output { font-size: 15px; }
89
  .markdown-output p { margin: 0.8em 0; }
@@ -92,28 +101,57 @@
92
  .markdown-output h2 { font-size: 1.5em; }
93
  .markdown-output h3 { font-size: 1.3em; }
94
  .markdown-output h4 { font-size: 1.1em; }
95
- .markdown-output pre {
96
  padding: 1em;
97
  overflow-x: auto;
98
- white-space: pre-wrap;
99
- word-wrap: break-word;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  }
101
- .markdown-output code { word-break: break-word; }
102
  }
103
  /* Bouton Retour en haut */
104
  #back-to-top {
105
  position: fixed;
106
  bottom: 30px;
107
  right: 30px;
108
- background-color: #2563eb;
109
  color: #fff;
110
  border-radius: 50%;
111
- padding: 0.75rem;
112
  cursor: pointer;
113
  opacity: 0;
114
  visibility: hidden;
115
  transition: opacity 0.3s ease, visibility 0.3s ease;
116
  z-index: 50;
 
 
 
 
117
  }
118
  #back-to-top.show {
119
  opacity: 1;
@@ -135,7 +173,7 @@
135
  </div>
136
  </nav>
137
  </header>
138
-
139
  <!-- Contenu principal -->
140
  <main role="main" class="container mx-auto p-6">
141
  <!-- Section d'introduction -->
@@ -145,7 +183,7 @@
145
  Générez des dissertations structurées et détaillées pour vos devoirs d'histoire et de géographie.
146
  </p>
147
  </section>
148
-
149
  <!-- Bloc Dissertation d'Histoire Type 1 & 3 -->
150
  <section class="mb-8" data-aos="fade-up">
151
  <div class="bg-white rounded-xl shadow-xl p-8 card">
@@ -153,34 +191,35 @@
153
  <i class="fas fa-landmark text-blue-600 text-2xl mr-3"></i>
154
  <h2 class="text-2xl font-bold text-gray-800">Dissertation d'Histoire - Type 1 & 3</h2>
155
  </div>
156
- <form id="histoire-form" class="space-y-6" aria-label="Formulaire dissertation histoire">
157
  <div class="space-y-2">
158
  <label for="sujet-histoire" class="text-sm font-medium text-gray-700">
159
- <i class="fas fa-pen-fancy mr-2"></i>Sujet de la dissertation
160
  </label>
161
- <textarea name="sujet-histoire" id="sujet-histoire" rows="3" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm" placeholder="Entrez le sujet de votre dissertation..."></textarea>
 
162
  </div>
163
  <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
164
  <div class="space-y-2">
165
  <label for="pointt1-histoire" class="text-sm font-medium text-gray-700">
166
  <i class="fas fa-list-ol mr-2"></i>Premier point
167
  </label>
168
- <input type="text" name="pointt1-histoire" id="pointt1-histoire" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm" placeholder="Premier point...">
169
  </div>
170
  <div class="space-y-2">
171
  <label for="point2-histoire" class="text-sm font-medium text-gray-700">
172
  <i class="fas fa-list-ol mr-2"></i>Deuxième point
173
  </label>
174
- <input type="text" name="point2-histoire" id="point2-histoire" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm" placeholder="Deuxième point...">
175
  </div>
176
  <div class="space-y-2">
177
  <label for="point3-histoire" class="text-sm font-medium text-gray-700">
178
  <i class="fas fa-list-ol mr-2"></i>Troisième point
179
  </label>
180
- <input type="text" name="point3-histoire" id="point3-histoire" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm" placeholder="Troisième point...">
181
  </div>
182
  </div>
183
- <button type="submit" class="w-full bg-blue-600 text-white px-6 py-3 rounded-lg font-medium flex items-center justify-center hover:bg-blue-700 transition-colors duration-200">
184
  <i class="fas fa-magic mr-2"></i>
185
  <span>Générer la dissertation</span>
186
  </button>
@@ -188,7 +227,7 @@
188
  <div id="histoire-output" class="mt-6 p-6 bg-gray-50 rounded-lg hidden" aria-live="polite"></div>
189
  </div>
190
  </section>
191
-
192
  <!-- Bloc Histoire Type 2 (Images) -->
193
  <section class="mb-8" data-aos="fade-up">
194
  <div class="bg-white rounded-xl shadow-xl p-8 card">
@@ -196,21 +235,22 @@
196
  <i class="fas fa-scroll text-green-600 text-2xl mr-3"></i>
197
  <h2 class="text-2xl font-bold text-gray-800">Histoire - Type 2 (Images)</h2>
198
  </div>
199
- <form id="histoire-type2-form" class="space-y-6" enctype="multipart/form-data" aria-label="Formulaire histoire images">
200
  <div class="space-y-2">
201
  <label for="sujet-histoire-type2" class="text-sm font-medium text-gray-700">
202
- <i class="fas fa-pen-fancy mr-2"></i>Sujet
203
  </label>
204
- <textarea name="sujet-histoire-type2" id="sujet-histoire-type2" rows="3" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm" placeholder="Entrez le sujet..."></textarea>
 
205
  </div>
206
  <div class="space-y-2">
207
  <label for="images-histoire-type2" class="text-sm font-medium text-gray-700">
208
  <i class="fas fa-images mr-2"></i>Images (Multiple)
209
  </label>
210
- <input type="file" name="images-histoire-type2" id="images-histoire-type2" multiple class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm">
211
- <div id="image-preview-histoire-type2" class="flex flex-wrap"></div>
212
  </div>
213
- <button type="submit" class="w-full bg-green-600 text-white px-6 py-3 rounded-lg font-medium flex items-center justify-center hover:bg-green-700 transition-colors duration-200">
214
  <i class="fas fa-magic mr-2"></i>
215
  <span>Générer l'analyse</span>
216
  </button>
@@ -218,7 +258,7 @@
218
  <div id="histoire-type2-output" class="mt-6 p-6 bg-gray-50 rounded-lg hidden" aria-live="polite"></div>
219
  </div>
220
  </section>
221
-
222
  <!-- Bloc Dissertation de Géographie Type 1 & 3 -->
223
  <section class="mb-8" data-aos="fade-up">
224
  <div class="bg-white rounded-xl shadow-xl p-8 card">
@@ -226,34 +266,35 @@
226
  <i class="fas fa-globe-europe text-purple-600 text-2xl mr-3"></i>
227
  <h2 class="text-2xl font-bold text-gray-800">Dissertation de Géographie - Type 1 & 3</h2>
228
  </div>
229
- <form id="geographie-form" class="space-y-6" aria-label="Formulaire dissertation géographie">
230
  <div class="space-y-2">
231
  <label for="sujet-geographie" class="text-sm font-medium text-gray-700">
232
- <i class="fas fa-pen-fancy mr-2"></i>Sujet de la dissertation
233
  </label>
234
- <textarea name="sujet-geographie" id="sujet-geographie" rows="3" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm" placeholder="Entrez le sujet de votre dissertation..."></textarea>
 
235
  </div>
236
  <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
237
  <div class="space-y-2">
238
  <label for="point1-geographie" class="text-sm font-medium text-gray-700">
239
  <i class="fas fa-list-ol mr-2"></i>Premier point
240
  </label>
241
- <input type="text" name="point1-geographie" id="point1-geographie" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm" placeholder="Premier argument...">
242
  </div>
243
  <div class="space-y-2">
244
  <label for="point2-geographie" class="text-sm font-medium text-gray-700">
245
  <i class="fas fa-list-ol mr-2"></i>Deuxième point
246
  </label>
247
- <input type="text" name="point2-geographie" id="point2-geographie" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm" placeholder="Deuxième argument...">
248
  </div>
249
  <div class="space-y-2">
250
  <label for="point3-geographie" class="text-sm font-medium text-gray-700">
251
  <i class="fas fa-list-ol mr-2"></i>Troisième point
252
  </label>
253
- <input type="text" name="point3-geographie" id="point3-geographie" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm" placeholder="Troisième argument...">
254
  </div>
255
  </div>
256
- <button type="submit" class="w-full bg-purple-600 text-white px-6 py-3 rounded-lg font-medium flex items-center justify-center hover:bg-purple-700 transition-colors duration-200">
257
  <i class="fas fa-magic mr-2"></i>
258
  <span>Générer la dissertation</span>
259
  </button>
@@ -261,7 +302,7 @@
261
  <div id="geographie-output" class="mt-6 p-6 bg-gray-50 rounded-lg hidden" aria-live="polite"></div>
262
  </div>
263
  </section>
264
-
265
  <!-- Bloc Géographie Type 2 (Images) -->
266
  <section class="mb-8" data-aos="fade-up">
267
  <div class="bg-white rounded-xl shadow-xl p-8 card">
@@ -269,21 +310,22 @@
269
  <i class="fas fa-map-marked-alt text-yellow-600 text-2xl mr-3"></i>
270
  <h2 class="text-2xl font-bold text-gray-800">Géographie - Type 2 (Images)</h2>
271
  </div>
272
- <form id="geographie-type2-form" class="space-y-6" enctype="multipart/form-data" aria-label="Formulaire géographie images">
273
  <div class="space-y-2">
274
  <label for="sujet-geographie-type2" class="text-sm font-medium text-gray-700">
275
- <i class="fas fa-pen-fancy mr-2"></i>Sujet
276
  </label>
277
- <textarea name="sujet-geographie-type2" id="sujet-geographie-type2" rows="3" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm" placeholder="Entrez le sujet..."></textarea>
 
278
  </div>
279
  <div class="space-y-2">
280
  <label for="images-geographie-type2" class="text-sm font-medium text-gray-700">
281
  <i class="fas fa-images mr-2"></i>Images (Multiple)
282
  </label>
283
- <input type="file" name="images-geographie-type2" id="images-geographie-type2" multiple class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm">
284
- <div id="image-preview-geographie-type2" class="flex flex-wrap"></div>
285
  </div>
286
- <button type="submit" class="w-full bg-yellow-600 text-white px-6 py-3 rounded-lg font-medium flex items-center justify-center hover:bg-yellow-700 transition-colors duration-200">
287
  <i class="fas fa-magic mr-2"></i>
288
  <span>Générer l'analyse</span>
289
  </button>
@@ -292,36 +334,39 @@
292
  </div>
293
  </section>
294
  </main>
295
-
296
  <!-- Bouton Retour en haut -->
297
  <button id="back-to-top" aria-label="Retour en haut">
298
  <i class="fas fa-arrow-up"></i>
299
  </button>
300
-
301
  <!-- Pied de page -->
302
  <footer class="gradient-bg mt-12 py-6" role="contentinfo">
303
  <div class="container mx-auto px-6 text-center text-white">
304
  <p class="text-sm">© 2024 Mariam - Tous droits réservés</p>
305
  </div>
306
  </footer>
307
-
308
  <!-- Scripts personnalisés -->
309
  <script>
310
  window.addEventListener('load', function() {
 
311
  AOS.init({
312
  duration: 800,
313
  once: true,
314
  offset: 100
315
  });
316
-
 
317
  marked.setOptions({
318
- breaks: true,
319
- gfm: true,
320
- headerIds: false,
321
- renderer: new marked.Renderer()
322
  });
323
-
324
- function showLoading(outputId) {
 
325
  const output = document.getElementById(outputId);
326
  output.classList.remove('hidden');
327
  output.innerHTML = `
@@ -330,96 +375,194 @@
330
  <span class="text-gray-600">Génération en cours...</span>
331
  </div>
332
  `;
 
 
 
 
 
 
 
 
 
 
 
333
  }
334
-
 
335
  async function submitForm(formId, outputId, endpoint) {
336
  const form = document.getElementById(formId);
337
  const output = document.getElementById(outputId);
338
-
 
 
 
 
 
 
 
 
 
339
  form.addEventListener('submit', async (e) => {
340
- e.preventDefault();
341
- showLoading(outputId);
342
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
  try {
344
  const formData = new FormData(form);
345
  const response = await fetch(endpoint, {
346
  method: 'POST',
347
  body: formData
 
348
  });
349
-
350
- if (!response.ok) throw new Error(`Erreur HTTP: ${response.status}`);
351
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  const result = await response.json();
353
- output.classList.remove('hidden');
 
 
 
 
 
 
 
354
  output.innerHTML = `
355
  <div class="markdown-output prose max-w-none">
356
  ${marked.parse(result.output)}
357
  </div>
358
  `;
359
-
360
- // Adaptation des tableaux pour mobile
361
  Array.from(output.getElementsByTagName('table')).forEach(table => {
362
- table.parentNode.style.overflow = 'auto';
363
- table.style.minWidth = '100%';
 
 
364
  });
 
365
  } catch (error) {
366
- output.classList.remove('hidden');
 
367
  output.innerHTML = `
368
- <div class="bg-red-50 border-l-4 border-red-500 p-4">
369
  <div class="flex items-center">
370
- <i class="fas fa-exclamation-circle text-red-500"></i>
371
- <p class="ml-3 text-red-700">Une erreur est survenue : ${error.message}</p>
 
 
 
372
  </div>
373
  </div>
374
  `;
 
 
 
375
  }
376
  });
377
  }
378
-
379
- // Initialisation des formulaires
380
  submitForm('histoire-form', 'histoire-output', '/api/histoire');
381
  submitForm('histoire-type2-form', 'histoire-type2-output', '/api/histoire-type2');
382
  submitForm('geographie-form', 'geographie-output', '/api/geographie');
383
  submitForm('geographie-type2-form', 'geographie-type2-output', '/api/geographie-type2');
384
-
385
- // Prévisualisation des images
386
  function handleImagePreviews(inputId, previewContainerId) {
387
  const input = document.getElementById(inputId);
388
  const previewContainer = document.getElementById(previewContainerId);
389
-
 
 
390
  input.addEventListener('change', function() {
391
- previewContainer.innerHTML = '';
392
- Array.from(this.files).forEach(file => {
393
- if (file.type.startsWith('image/')) {
394
- const reader = new FileReader();
395
- reader.onload = (e) => {
396
- const img = document.createElement('img');
397
- img.src = e.target.result;
398
- img.classList.add('image-preview');
399
- previewContainer.appendChild(img);
400
- };
401
- reader.readAsDataURL(file);
402
- }
403
- });
 
 
 
 
 
 
 
 
 
404
  });
405
  }
406
-
 
407
  handleImagePreviews('images-histoire-type2', 'image-preview-histoire-type2');
408
  handleImagePreviews('images-geographie-type2', 'image-preview-geographie-type2');
409
-
410
- // Bouton Retour en haut
411
  const backToTopBtn = document.getElementById('back-to-top');
412
- window.addEventListener('scroll', () => {
413
- if (window.scrollY > 300) {
414
- backToTopBtn.classList.add('show');
415
- } else {
416
- backToTopBtn.classList.remove('show');
417
- }
418
- });
419
- backToTopBtn.addEventListener('click', () => {
420
- window.scrollTo({ top: 0, behavior: 'smooth' });
421
- });
 
 
422
  });
423
  </script>
424
  </body>
425
- </html>
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Histoire et Géographie - Assistant Académique</title>
7
+
8
  <!-- Feuilles de style et polices -->
9
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/aos.css" rel="stylesheet">
10
  <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
11
+
12
  <!-- Scripts et Tailwind -->
13
  <script src="https://cdn.tailwindcss.com"></script>
14
  <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.0.2/marked.min.js"></script>
15
  <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js"></script>
16
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/aos.js"></script>
17
+
18
  <style>
19
  /* Police et réglages de base */
20
  body {
21
  font-family: 'Poppins', sans-serif;
22
+ background-color: #f1f5f9; /* slate-100 */
23
  scroll-behavior: smooth;
24
  }
25
  /* Couleur et dégradé de la navigation et du footer */
26
  .gradient-bg {
27
+ background: linear-gradient(120deg, #1a365d 0%, #2563eb 100%); /* Adjusted colors */
28
  }
29
  /* Cartes et transitions */
30
  .card {
 
48
  }
49
  .form-input:focus {
50
  transform: scale(1.01);
51
+ border-color: #2563eb; /* blue-600 */
52
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.3);
53
  }
54
+ /* Style pour le champ invalide */
55
+ .form-input.invalid {
56
+ border-color: #ef4444; /* red-500 */
57
+ }
58
+ .form-input.invalid:focus {
59
+ border-color: #ef4444; /* red-500 */
60
+ box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.3); /* red-500 with opacity */
61
+ }
62
  /* Prévisualisation d'images */
63
  .image-preview {
64
  max-width: 200px;
65
  max-height: 200px;
66
  margin: 10px;
67
  border: 1px solid #ccc;
68
+ border-radius: 0.375rem; /* rounded-md */
69
  }
70
  /* Style Markdown optimisé */
71
  .markdown-output {
 
79
  line-height: 1.6;
80
  font-size: 16px;
81
  }
82
+ .markdown-output h1, .markdown-output h2,
83
  .markdown-output h3, .markdown-output h4 {
84
  margin: 1.5em 0 0.5em;
85
  line-height: 1.3;
 
92
  margin: 0.5em 0;
93
  line-height: 1.4;
94
  }
95
+ /* Responsive Markdown */
96
  @media (max-width: 768px) {
97
  .markdown-output { font-size: 15px; }
98
  .markdown-output p { margin: 0.8em 0; }
 
101
  .markdown-output h2 { font-size: 1.5em; }
102
  .markdown-output h3 { font-size: 1.3em; }
103
  .markdown-output h4 { font-size: 1.1em; }
104
+ .markdown-output pre { /* Styling for code blocks */
105
  padding: 1em;
106
  overflow-x: auto;
107
+ white-space: pre-wrap; /* Allow wrapping */
108
+ word-wrap: break-word; /* Break long words */
109
+ background-color: #f3f4f6; /* gray-100 */
110
+ border-radius: 0.375rem; /* rounded-md */
111
+ }
112
+ .markdown-output code { /* Styling for inline code */
113
+ word-break: break-word; /* Break long words */
114
+ background-color: #e5e7eb; /* gray-200 */
115
+ padding: 0.2em 0.4em;
116
+ border-radius: 0.25rem; /* rounded-sm */
117
+ }
118
+ /* Responsive table wrapper */
119
+ .table-wrapper {
120
+ overflow-x: auto;
121
+ width: 100%;
122
+ }
123
+ .markdown-output table {
124
+ width: 100%;
125
+ border-collapse: collapse;
126
+ margin: 1em 0;
127
+ }
128
+ .markdown-output th, .markdown-output td {
129
+ border: 1px solid #d1d5db; /* gray-300 */
130
+ padding: 0.5em;
131
+ text-align: left;
132
+ }
133
+ .markdown-output th {
134
+ background-color: #f3f4f6; /* gray-100 */
135
  }
 
136
  }
137
  /* Bouton Retour en haut */
138
  #back-to-top {
139
  position: fixed;
140
  bottom: 30px;
141
  right: 30px;
142
+ background-color: #2563eb; /* blue-600 */
143
  color: #fff;
144
  border-radius: 50%;
145
+ padding: 0.75rem; /* Adjusted padding */
146
  cursor: pointer;
147
  opacity: 0;
148
  visibility: hidden;
149
  transition: opacity 0.3s ease, visibility 0.3s ease;
150
  z-index: 50;
151
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
152
+ }
153
+ #back-to-top:hover {
154
+ background-color: #1d4ed8; /* blue-700 */
155
  }
156
  #back-to-top.show {
157
  opacity: 1;
 
173
  </div>
174
  </nav>
175
  </header>
176
+
177
  <!-- Contenu principal -->
178
  <main role="main" class="container mx-auto p-6">
179
  <!-- Section d'introduction -->
 
183
  Générez des dissertations structurées et détaillées pour vos devoirs d'histoire et de géographie.
184
  </p>
185
  </section>
186
+
187
  <!-- Bloc Dissertation d'Histoire Type 1 & 3 -->
188
  <section class="mb-8" data-aos="fade-up">
189
  <div class="bg-white rounded-xl shadow-xl p-8 card">
 
191
  <i class="fas fa-landmark text-blue-600 text-2xl mr-3"></i>
192
  <h2 class="text-2xl font-bold text-gray-800">Dissertation d'Histoire - Type 1 & 3</h2>
193
  </div>
194
+ <form id="histoire-form" class="space-y-6" aria-label="Formulaire dissertation histoire" novalidate> <!-- novalidate désactive la validation navigateur par défaut pour laisser JS gérer -->
195
  <div class="space-y-2">
196
  <label for="sujet-histoire" class="text-sm font-medium text-gray-700">
197
+ <i class="fas fa-pen-fancy mr-2"></i>Sujet de la dissertation <span class="text-red-500">*</span>
198
  </label>
199
+ <textarea name="sujet-histoire" id="sujet-histoire" rows="3" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50" placeholder="Entrez le sujet de votre dissertation..." required aria-describedby="sujet-histoire-error"></textarea>
200
+ <div id="sujet-histoire-error" class="text-red-600 text-sm mt-1 hidden" role="alert">Le champ sujet est obligatoire.</div>
201
  </div>
202
  <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
203
  <div class="space-y-2">
204
  <label for="pointt1-histoire" class="text-sm font-medium text-gray-700">
205
  <i class="fas fa-list-ol mr-2"></i>Premier point
206
  </label>
207
+ <input type="text" name="pointt1-histoire" id="pointt1-histoire" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50" placeholder="Premier point...">
208
  </div>
209
  <div class="space-y-2">
210
  <label for="point2-histoire" class="text-sm font-medium text-gray-700">
211
  <i class="fas fa-list-ol mr-2"></i>Deuxième point
212
  </label>
213
+ <input type="text" name="point2-histoire" id="point2-histoire" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50" placeholder="Deuxième point...">
214
  </div>
215
  <div class="space-y-2">
216
  <label for="point3-histoire" class="text-sm font-medium text-gray-700">
217
  <i class="fas fa-list-ol mr-2"></i>Troisième point
218
  </label>
219
+ <input type="text" name="point3-histoire" id="point3-histoire" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50" placeholder="Troisième point...">
220
  </div>
221
  </div>
222
+ <button type="submit" class="w-full bg-blue-600 text-white px-6 py-3 rounded-lg font-medium flex items-center justify-center hover:bg-blue-700 transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed">
223
  <i class="fas fa-magic mr-2"></i>
224
  <span>Générer la dissertation</span>
225
  </button>
 
227
  <div id="histoire-output" class="mt-6 p-6 bg-gray-50 rounded-lg hidden" aria-live="polite"></div>
228
  </div>
229
  </section>
230
+
231
  <!-- Bloc Histoire Type 2 (Images) -->
232
  <section class="mb-8" data-aos="fade-up">
233
  <div class="bg-white rounded-xl shadow-xl p-8 card">
 
235
  <i class="fas fa-scroll text-green-600 text-2xl mr-3"></i>
236
  <h2 class="text-2xl font-bold text-gray-800">Histoire - Type 2 (Images)</h2>
237
  </div>
238
+ <form id="histoire-type2-form" class="space-y-6" enctype="multipart/form-data" aria-label="Formulaire histoire images" novalidate>
239
  <div class="space-y-2">
240
  <label for="sujet-histoire-type2" class="text-sm font-medium text-gray-700">
241
+ <i class="fas fa-pen-fancy mr-2"></i>Sujet <span class="text-red-500">*</span>
242
  </label>
243
+ <textarea name="sujet-histoire-type2" id="sujet-histoire-type2" rows="3" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-green-500 focus:ring focus:ring-green-200 focus:ring-opacity-50" placeholder="Entrez le sujet..." required aria-describedby="sujet-histoire-type2-error"></textarea>
244
+ <div id="sujet-histoire-type2-error" class="text-red-600 text-sm mt-1 hidden" role="alert">Le champ sujet est obligatoire.</div>
245
  </div>
246
  <div class="space-y-2">
247
  <label for="images-histoire-type2" class="text-sm font-medium text-gray-700">
248
  <i class="fas fa-images mr-2"></i>Images (Multiple)
249
  </label>
250
+ <input type="file" name="images-histoire-type2" id="images-histoire-type2" multiple accept="image/*" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-green-50 file:text-green-700 hover:file:bg-green-100 focus:border-green-500 focus:ring focus:ring-green-200 focus:ring-opacity-50">
251
+ <div id="image-preview-histoire-type2" class="flex flex-wrap mt-2"></div>
252
  </div>
253
+ <button type="submit" class="w-full bg-green-600 text-white px-6 py-3 rounded-lg font-medium flex items-center justify-center hover:bg-green-700 transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed">
254
  <i class="fas fa-magic mr-2"></i>
255
  <span>Générer l'analyse</span>
256
  </button>
 
258
  <div id="histoire-type2-output" class="mt-6 p-6 bg-gray-50 rounded-lg hidden" aria-live="polite"></div>
259
  </div>
260
  </section>
261
+
262
  <!-- Bloc Dissertation de Géographie Type 1 & 3 -->
263
  <section class="mb-8" data-aos="fade-up">
264
  <div class="bg-white rounded-xl shadow-xl p-8 card">
 
266
  <i class="fas fa-globe-europe text-purple-600 text-2xl mr-3"></i>
267
  <h2 class="text-2xl font-bold text-gray-800">Dissertation de Géographie - Type 1 & 3</h2>
268
  </div>
269
+ <form id="geographie-form" class="space-y-6" aria-label="Formulaire dissertation géographie" novalidate>
270
  <div class="space-y-2">
271
  <label for="sujet-geographie" class="text-sm font-medium text-gray-700">
272
+ <i class="fas fa-pen-fancy mr-2"></i>Sujet de la dissertation <span class="text-red-500">*</span>
273
  </label>
274
+ <textarea name="sujet-geographie" id="sujet-geographie" rows="3" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-purple-500 focus:ring focus:ring-purple-200 focus:ring-opacity-50" placeholder="Entrez le sujet de votre dissertation..." required aria-describedby="sujet-geographie-error"></textarea>
275
+ <div id="sujet-geographie-error" class="text-red-600 text-sm mt-1 hidden" role="alert">Le champ sujet est obligatoire.</div>
276
  </div>
277
  <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
278
  <div class="space-y-2">
279
  <label for="point1-geographie" class="text-sm font-medium text-gray-700">
280
  <i class="fas fa-list-ol mr-2"></i>Premier point
281
  </label>
282
+ <input type="text" name="point1-geographie" id="point1-geographie" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-purple-500 focus:ring focus:ring-purple-200 focus:ring-opacity-50" placeholder="Premier argument...">
283
  </div>
284
  <div class="space-y-2">
285
  <label for="point2-geographie" class="text-sm font-medium text-gray-700">
286
  <i class="fas fa-list-ol mr-2"></i>Deuxième point
287
  </label>
288
+ <input type="text" name="point2-geographie" id="point2-geographie" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-purple-500 focus:ring focus:ring-purple-200 focus:ring-opacity-50" placeholder="Deuxième argument...">
289
  </div>
290
  <div class="space-y-2">
291
  <label for="point3-geographie" class="text-sm font-medium text-gray-700">
292
  <i class="fas fa-list-ol mr-2"></i>Troisième point
293
  </label>
294
+ <input type="text" name="point3-geographie" id="point3-geographie" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-purple-500 focus:ring focus:ring-purple-200 focus:ring-opacity-50" placeholder="Troisième argument...">
295
  </div>
296
  </div>
297
+ <button type="submit" class="w-full bg-purple-600 text-white px-6 py-3 rounded-lg font-medium flex items-center justify-center hover:bg-purple-700 transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed">
298
  <i class="fas fa-magic mr-2"></i>
299
  <span>Générer la dissertation</span>
300
  </button>
 
302
  <div id="geographie-output" class="mt-6 p-6 bg-gray-50 rounded-lg hidden" aria-live="polite"></div>
303
  </div>
304
  </section>
305
+
306
  <!-- Bloc Géographie Type 2 (Images) -->
307
  <section class="mb-8" data-aos="fade-up">
308
  <div class="bg-white rounded-xl shadow-xl p-8 card">
 
310
  <i class="fas fa-map-marked-alt text-yellow-600 text-2xl mr-3"></i>
311
  <h2 class="text-2xl font-bold text-gray-800">Géographie - Type 2 (Images)</h2>
312
  </div>
313
+ <form id="geographie-type2-form" class="space-y-6" enctype="multipart/form-data" aria-label="Formulaire géographie images" novalidate>
314
  <div class="space-y-2">
315
  <label for="sujet-geographie-type2" class="text-sm font-medium text-gray-700">
316
+ <i class="fas fa-pen-fancy mr-2"></i>Sujet <span class="text-red-500">*</span>
317
  </label>
318
+ <textarea name="sujet-geographie-type2" id="sujet-geographie-type2" rows="3" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-yellow-500 focus:ring focus:ring-yellow-200 focus:ring-opacity-50" placeholder="Entrez le sujet..." required aria-describedby="sujet-geographie-type2-error"></textarea>
319
+ <div id="sujet-geographie-type2-error" class="text-red-600 text-sm mt-1 hidden" role="alert">Le champ sujet est obligatoire.</div>
320
  </div>
321
  <div class="space-y-2">
322
  <label for="images-geographie-type2" class="text-sm font-medium text-gray-700">
323
  <i class="fas fa-images mr-2"></i>Images (Multiple)
324
  </label>
325
+ <input type="file" name="images-geographie-type2" id="images-geographie-type2" multiple accept="image/*" class="form-input mt-1 block w-full rounded-lg border-gray-300 shadow-sm file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-yellow-50 file:text-yellow-700 hover:file:bg-yellow-100 focus:border-yellow-500 focus:ring focus:ring-yellow-200 focus:ring-opacity-50">
326
+ <div id="image-preview-geographie-type2" class="flex flex-wrap mt-2"></div>
327
  </div>
328
+ <button type="submit" class="w-full bg-yellow-600 text-white px-6 py-3 rounded-lg font-medium flex items-center justify-center hover:bg-yellow-700 transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed">
329
  <i class="fas fa-magic mr-2"></i>
330
  <span>Générer l'analyse</span>
331
  </button>
 
334
  </div>
335
  </section>
336
  </main>
337
+
338
  <!-- Bouton Retour en haut -->
339
  <button id="back-to-top" aria-label="Retour en haut">
340
  <i class="fas fa-arrow-up"></i>
341
  </button>
342
+
343
  <!-- Pied de page -->
344
  <footer class="gradient-bg mt-12 py-6" role="contentinfo">
345
  <div class="container mx-auto px-6 text-center text-white">
346
  <p class="text-sm">© 2024 Mariam - Tous droits réservés</p>
347
  </div>
348
  </footer>
349
+
350
  <!-- Scripts personnalisés -->
351
  <script>
352
  window.addEventListener('load', function() {
353
+ // Initialisation AOS (Animations au scroll)
354
  AOS.init({
355
  duration: 800,
356
  once: true,
357
  offset: 100
358
  });
359
+
360
+ // Configuration de Marked (convertisseur Markdown vers HTML)
361
  marked.setOptions({
362
+ breaks: true, // Convertit les sauts de ligne simples en <br>
363
+ gfm: true, // Active les spécificités GitHub Flavored Markdown (tableaux, etc.)
364
+ headerIds: false, // Ne génère pas d'ID pour les titres
365
+ renderer: new marked.Renderer() // Utilise le rendu par défaut
366
  });
367
+
368
+ // Fonction pour afficher l'indicateur de chargement
369
+ function showLoading(outputId, formButton) {
370
  const output = document.getElementById(outputId);
371
  output.classList.remove('hidden');
372
  output.innerHTML = `
 
375
  <span class="text-gray-600">Génération en cours...</span>
376
  </div>
377
  `;
378
+ // Désactiver le bouton pendant le chargement
379
+ if (formButton) formButton.disabled = true;
380
+ }
381
+
382
+ // Fonction pour cacher l'indicateur de chargement et réactiver le bouton
383
+ function hideLoading(outputId, formButton) {
384
+ const output = document.getElementById(outputId);
385
+ // Ne cache pas l'output si on va afficher un résultat ou une erreur juste après
386
+ // output.classList.add('hidden');
387
+ // output.innerHTML = ''; // Optionnel : vider le contenu
388
+ if (formButton) formButton.disabled = false;
389
  }
390
+
391
+ // Fonction principale pour gérer la soumission des formulaires
392
  async function submitForm(formId, outputId, endpoint) {
393
  const form = document.getElementById(formId);
394
  const output = document.getElementById(outputId);
395
+ const submitButton = form.querySelector('button[type="submit"]');
396
+
397
+ // Trouver le champ sujet spécifique à ce formulaire
398
+ const subjectField = form.querySelector('textarea[id^="sujet-"]'); // Trouve le textarea dont l'ID commence par "sujet-"
399
+ if (!subjectField) {
400
+ console.error(`Champ sujet non trouvé pour le formulaire ${formId}`);
401
+ return; // Arrêter si le champ sujet n'existe pas
402
+ }
403
+ const errorMsgElement = form.querySelector('#' + subjectField.id + '-error'); // Trouve le div d'erreur correspondant
404
+
405
  form.addEventListener('submit', async (e) => {
406
+ e.preventDefault(); // Toujours prévenir le comportement par défaut
407
+
408
+ // --- Validation du sujet ---
409
+ const subjectValue = subjectField.value.trim();
410
+ let isValid = true;
411
+
412
+ // Réinitialiser l'état d'erreur visuel avant la validation
413
+ subjectField.classList.remove('invalid');
414
+ if (errorMsgElement) errorMsgElement.classList.add('hidden');
415
+ // Ne pas cacher l'output ici, seulement le vider si on est sûr de le remplacer
416
+ // output.classList.add('hidden');
417
+
418
+ if (subjectValue === '') {
419
+ isValid = false;
420
+ subjectField.classList.add('invalid'); // Ajouter style d'erreur
421
+ if (errorMsgElement) errorMsgElement.classList.remove('hidden'); // Afficher message d'erreur
422
+ subjectField.focus(); // Mettre le focus sur le champ
423
+ }
424
+ // --- Fin Validation ---
425
+
426
+ // Si le formulaire n'est pas valide, arrêter ici
427
+ if (!isValid) {
428
+ return;
429
+ }
430
+
431
+ // Si valide, continuer : afficher le chargement et envoyer la requête
432
+ showLoading(outputId, submitButton);
433
+
434
  try {
435
  const formData = new FormData(form);
436
  const response = await fetch(endpoint, {
437
  method: 'POST',
438
  body: formData
439
+ // Ajouter des headers si nécessaire (ex: 'Accept': 'application/json')
440
  });
441
+
442
+ // Cacher le loading seulement après avoir reçu la réponse
443
+ // (que ce soit un succès ou une erreur HTTP)
444
+ // hideLoading(outputId, submitButton); // Fait avant l'affichage du résultat/erreur
445
+
446
+ if (!response.ok) {
447
+ // Essayer de lire le corps de l'erreur si possible
448
+ let errorBody = `Erreur HTTP: ${response.status}`;
449
+ try {
450
+ const errorData = await response.json();
451
+ errorBody += ` - ${errorData.message || JSON.stringify(errorData)}`;
452
+ } catch(jsonError) {
453
+ // Si le corps de l'erreur n'est pas du JSON, utiliser le texte brut
454
+ try {
455
+ const errorText = await response.text();
456
+ errorBody += ` - ${errorText}`;
457
+ } catch (textError) {
458
+ // Ignorer si même le texte ne peut être lu
459
+ }
460
+ }
461
+ throw new Error(errorBody);
462
+ }
463
+
464
  const result = await response.json();
465
+ // Vérifier si la réponse contient bien la propriété 'output'
466
+ if (typeof result.output !== 'string') {
467
+ throw new Error("La réponse du serveur n'a pas le format attendu.");
468
+ }
469
+
470
+ output.classList.remove('hidden'); // S'assurer que la zone est visible
471
+ // Utilisation de DOMPurify si inclus, sinon directement marked.parse
472
+ // const sanitizedHtml = typeof DOMPurify !== 'undefined' ? DOMPurify.sanitize(marked.parse(result.output)) : marked.parse(result.output);
473
  output.innerHTML = `
474
  <div class="markdown-output prose max-w-none">
475
  ${marked.parse(result.output)}
476
  </div>
477
  `;
478
+
479
+ // Adaptation des tableaux générés pour mobile
480
  Array.from(output.getElementsByTagName('table')).forEach(table => {
481
+ const wrapper = document.createElement('div');
482
+ wrapper.classList.add('table-wrapper');
483
+ table.parentNode.insertBefore(wrapper, table);
484
+ wrapper.appendChild(table);
485
  });
486
+
487
  } catch (error) {
488
+ console.error(`Erreur lors de la soumission du formulaire ${formId}:`, error);
489
+ output.classList.remove('hidden'); // S'assurer que la zone d'erreur est visible
490
  output.innerHTML = `
491
+ <div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4" role="alert">
492
  <div class="flex items-center">
493
+ <i class="fas fa-exclamation-triangle mr-3"></i>
494
+ <div>
495
+ <p class="font-bold">Une erreur est survenue</p>
496
+ <p>${error.message || "Impossible de contacter le serveur ou erreur inattendue."}</p>
497
+ </div>
498
  </div>
499
  </div>
500
  `;
501
+ } finally {
502
+ // Réactiver le bouton dans tous les cas (succès, erreur fetch, erreur traitement)
503
+ hideLoading(outputId, submitButton); // Déplacé ici pour s'assurer qu'il est toujours réactivé
504
  }
505
  });
506
  }
507
+
508
+ // Initialisation des gestionnaires pour chaque formulaire
509
  submitForm('histoire-form', 'histoire-output', '/api/histoire');
510
  submitForm('histoire-type2-form', 'histoire-type2-output', '/api/histoire-type2');
511
  submitForm('geographie-form', 'geographie-output', '/api/geographie');
512
  submitForm('geographie-type2-form', 'geographie-type2-output', '/api/geographie-type2');
513
+
514
+ // Fonction pour gérer la prévisualisation des images
515
  function handleImagePreviews(inputId, previewContainerId) {
516
  const input = document.getElementById(inputId);
517
  const previewContainer = document.getElementById(previewContainerId);
518
+
519
+ if (!input || !previewContainer) return; // Ne rien faire si les éléments n'existent pas
520
+
521
  input.addEventListener('change', function() {
522
+ previewContainer.innerHTML = ''; // Vider les prévisualisations précédentes
523
+ if (this.files) {
524
+ Array.from(this.files).forEach(file => {
525
+ if (file.type.startsWith('image/')) {
526
+ const reader = new FileReader();
527
+ reader.onload = (e) => {
528
+ const img = document.createElement('img');
529
+ img.src = e.target.result;
530
+ img.alt = `Prévisualisation de ${file.name}`; // Texte alternatif
531
+ img.classList.add('image-preview', 'object-cover'); // object-cover pour éviter la déformation
532
+ previewContainer.appendChild(img);
533
+ };
534
+ reader.readAsDataURL(file);
535
+ } else {
536
+ // Optionnel : Afficher un message si le fichier n'est pas une image
537
+ const errorMsg = document.createElement('p');
538
+ errorMsg.textContent = `${file.name} n'est pas une image valide.`;
539
+ errorMsg.classList.add('text-red-600', 'text-sm', 'm-2');
540
+ previewContainer.appendChild(errorMsg);
541
+ }
542
+ });
543
+ }
544
  });
545
  }
546
+
547
+ // Lier la prévisualisation aux champs d'upload d'images
548
  handleImagePreviews('images-histoire-type2', 'image-preview-histoire-type2');
549
  handleImagePreviews('images-geographie-type2', 'image-preview-geographie-type2');
550
+
551
+ // Gestion du bouton "Retour en haut"
552
  const backToTopBtn = document.getElementById('back-to-top');
553
+ if (backToTopBtn) {
554
+ window.addEventListener('scroll', () => {
555
+ if (window.scrollY > 300) {
556
+ backToTopBtn.classList.add('show');
557
+ } else {
558
+ backToTopBtn.classList.remove('show');
559
+ }
560
+ });
561
+ backToTopBtn.addEventListener('click', () => {
562
+ window.scrollTo({ top: 0, behavior: 'smooth' });
563
+ });
564
+ }
565
  });
566
  </script>
567
  </body>
568
+ </html>