Docfile commited on
Commit
13a0d6d
·
verified ·
1 Parent(s): be37199

Create philosophie.html

Browse files
Files changed (1) hide show
  1. templates/philosophie.html +734 -0
templates/philosophie.html ADDED
@@ -0,0 +1,734 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 - Assistant Philosophique</title>
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert2/11.7.3/sweetalert2.all.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.3.0/marked.min.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/locale/fr.js"></script>
12
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert2/11.7.3/sweetalert2.min.css" rel="stylesheet">
13
+ <script src="https://cdn.tailwindcss.com"></script>
14
+ <style>
15
+ /* Styles existants */
16
+ .collapsible {
17
+ cursor: pointer;
18
+ padding: 18px;
19
+ width: 100%;
20
+ border: none;
21
+ text-align: left;
22
+ outline: none;
23
+ font-size: 15px;
24
+ background-color: #f1f1f1;
25
+ display: flex;
26
+ justify-content: space-between;
27
+ align-items: center;
28
+ }
29
+
30
+ /* Nouveaux styles pour le Markdown responsive */
31
+ .prose {
32
+ max-width: 100% !important;
33
+ }
34
+
35
+ .prose p {
36
+ margin-top: 1.25em;
37
+ margin-bottom: 1.25em;
38
+ line-height: 1.75;
39
+ }
40
+
41
+ .prose ul {
42
+ margin-top: 1.25em;
43
+ margin-bottom: 1.25em;
44
+ padding-left: 1.625em;
45
+ }
46
+
47
+ .prose li {
48
+ margin-top: 0.5em;
49
+ margin-bottom: 0.5em;
50
+ padding-left: 0.375em;
51
+ }
52
+
53
+ .prose h1, .prose h2, .prose h3 {
54
+ margin-top: 2em;
55
+ margin-bottom: 1em;
56
+ line-height: 1.3;
57
+ }
58
+
59
+ /* Styles spécifiques pour mobile */
60
+ @media (max-width: 640px) {
61
+ .prose {
62
+ font-size: 0.95rem;
63
+ }
64
+
65
+ .prose p {
66
+ margin-top: 1em;
67
+ margin-bottom: 1em;
68
+ }
69
+
70
+ .prose ul {
71
+ padding-left: 1.25em;
72
+ }
73
+
74
+ .prose li {
75
+ margin-top: 0.375em;
76
+ margin-bottom: 0.375em;
77
+ }
78
+
79
+ .prose h1, .prose h2, .prose h3 {
80
+ margin-top: 1.5em;
81
+ margin-bottom: 0.75em;
82
+ }
83
+ }
84
+
85
+ /* Styles pour améliorer la lisibilité du texte */
86
+ #response .prose {
87
+ color: #374151;
88
+ word-wrap: break-word;
89
+ overflow-wrap: break-word;
90
+ hyphens: auto;
91
+ }
92
+
93
+ #response .prose > * + * {
94
+ margin-top: 1em;
95
+ }
96
+
97
+ #response .prose blockquote {
98
+ margin: 1.5em 0;
99
+ padding-left: 1em;
100
+ border-left: 4px solid #e5e7eb;
101
+ font-style: italic;
102
+ }
103
+
104
+ #response .prose code {
105
+ background-color: #f3f4f6;
106
+ padding: 0.2em 0.4em;
107
+ border-radius: 0.25em;
108
+ font-size: 0.875em;
109
+ }
110
+
111
+ .active, .collapsible:hover {
112
+ background-color: #ddd;
113
+ }
114
+
115
+ .content {
116
+ padding: 0 18px;
117
+ display: none;
118
+ overflow: hidden;
119
+ background-color: white;
120
+ }
121
+
122
+ .animate-fadeIn {
123
+ animation: fadeIn 0.5s ease-out forwards;
124
+ }
125
+
126
+ .animate-slideUp {
127
+ animation: slideUp 0.5s ease-out forwards;
128
+ }
129
+
130
+ .animate-shake {
131
+ animation: shake 0.5s ease-in-out;
132
+ }
133
+
134
+ @keyframes fadeIn {
135
+ from { opacity: 0; transform: translateY(10px); }
136
+ to { opacity: 1; transform: translateY(0); }
137
+ }
138
+
139
+ @keyframes slideUp {
140
+ from { opacity: 0; transform: translateY(20px); }
141
+ to { opacity: 1; transform: translateY(0); }
142
+ }
143
+
144
+ @keyframes shake {
145
+ 0%, 100% { transform: translateX(0); }
146
+ 25% { transform: translateX(-5px); }
147
+ 75% { transform: translateX(5px); }
148
+ }
149
+
150
+ /* Styles pour la zone de sélection des cours */
151
+ .course-selection-container {
152
+ background: linear-gradient(135deg, #EEF2FF 0%, #F9FAFB 100%); /* Dégradé subtil */
153
+ border-radius: 1.25rem; /* Coins arrondis */
154
+ padding: 2rem; /* Espace intérieur */
155
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); /* Ombre portée */
156
+ }
157
+
158
+ .course-select-label {
159
+ font-size: 1.125rem; /* Taille de police légèrement plus grande */
160
+ font-weight: 600; /* Police en gras */
161
+ color: #4F46E5; /* Couleur violette */
162
+ margin-bottom: 1rem; /* Espace en dessous du label */
163
+ }
164
+
165
+ .course-select {
166
+ background-color: white; /* Fond blanc pour le select */
167
+ border: 2px solid #4F46E5; /* Bordure violette */
168
+ padding: 0.75rem 1rem; /* Espace intérieur */
169
+ font-size: 1rem; /* Taille de police */
170
+ color: #374151; /* Couleur du texte */
171
+ appearance: none; /* Supprime l'apparence par défaut */
172
+ -webkit-appearance: none;
173
+ -moz-appearance: none;
174
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%234F46E5' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E"); /* Icône de flèche */
175
+ background-repeat: no-repeat;
176
+ background-position: right 0.75rem center;
177
+ background-size: 1.25em;
178
+ }
179
+
180
+ .course-select:focus {
181
+ outline: none;
182
+ box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.5); /* Ombre au focus */
183
+ }
184
+
185
+ .course-info {
186
+ margin-top: 1.5rem; /* Espace au-dessus des informations du cours */
187
+ padding: 1rem; /* Espace intérieur */
188
+ background-color: white;
189
+ border-radius: 0.75rem;
190
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
191
+ }
192
+
193
+ .course-title {
194
+ font-size: 1.25rem;
195
+ font-weight: 600;
196
+ color: #374151;
197
+ margin-bottom: 0.5rem;
198
+ }
199
+
200
+ .course-author {
201
+ font-size: 0.875rem;
202
+ color: #6B7280;
203
+ }
204
+
205
+ .course-author-icon {
206
+ color: #4F46E5;
207
+ margin-right: 0.375rem;
208
+ }
209
+ </style>
210
+ </head>
211
+
212
+ <body class="bg-gradient-to-br from-violet-50 to-indigo-50 min-h-screen">
213
+ <!-- Navbar -->
214
+ <nav class="bg-white/80 backdrop-blur-md border-b border-gray-200 fixed w-full z-50">
215
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
216
+ <div class="flex justify-between items-center h-16">
217
+ <div class="flex items-center">
218
+ <div class="text-2xl font-bold bg-gradient-to-r from-violet-600 to-indigo-600 text-transparent bg-clip-text">
219
+ Mariam AI
220
+ </div>
221
+ </div>
222
+ <div class="flex items-center space-x-4">
223
+ <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-violet-100 text-violet-800">
224
+ <span class="w-2 h-2 bg-violet-400 rounded-full animate-pulse mr-2"></span>
225
+ Assistant Philosophique
226
+ </span>
227
+ </div>
228
+ </div>
229
+ </div>
230
+ </nav>
231
+
232
+ <!-- Main Content -->
233
+ <div class="pt-24 pb-12 px-4 sm:px-6 lg:px-8">
234
+ <div class="max-w-4xl mx-auto">
235
+ <!-- Main Card -->
236
+ <div class="bg-white/80 backdrop-blur-md rounded-2xl shadow-xl border border-gray-100 overflow-hidden">
237
+ <!-- Header Section -->
238
+ <div class="bg-gradient-to-r from-violet-600 to-indigo-600 p-6 text-white">
239
+ <h2 class="text-2xl font-bold">Gen'Dissertation</h2>
240
+ <p class="mt-2 opacity-90">Créez des dissertations philosophiques pertinentes et structurées</p>
241
+ </div>
242
+
243
+ <!-- Content Section -->
244
+ <div class="p-8 space-y-8">
245
+ <!-- Type Selection -->
246
+ <div class="space-y-3">
247
+ <label class="block text-sm font-medium text-gray-700">Type de dissertation</label>
248
+ <div class="relative">
249
+ <select id="type-select" class="w-full rounded-xl border-gray-200 shadow-sm focus:border-violet-500 focus:ring-violet-500 appearance-none bg-white py-3 px-4 pr-10">
250
+ <option value="1">Type 1 -</option>
251
+ <option value="2">Type 2 -</option>
252
+ <option value="3">Synthèse -</option>
253
+ </select>
254
+ <div class="absolute inset-y-0 right-0 flex items-center px-4 pointer-events-none">
255
+ <svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
256
+ <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
257
+ </svg>
258
+ </div>
259
+ </div>
260
+ <div id="current-type-label" class="inline-flex px-4 py-2 rounded-xl text-sm font-medium bg-gradient-to-r from-violet-50 to-indigo-50 text-violet-700 border border-violet-200">
261
+ Sujet de type 1
262
+ </div>
263
+ </div>
264
+
265
+ <!-- Course Selection -->
266
+ <div class="course-selection-container space-y-3">
267
+ <label for="course-select" class="course-select-label">
268
+ Sélection du cours
269
+ </label>
270
+ <select id="course-select" class="course-select w-full">
271
+ <option value="">Choisir un cours...</option>
272
+ </select>
273
+ <div class="course-info hidden">
274
+ <h3 id="course-title" class="course-title"></h3>
275
+ <p id="course-author" class="course-author">
276
+ <svg class="course-author-icon h-4 w-4 inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
277
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
278
+ </svg>
279
+ <span id="course-author-name"></span>
280
+ </p>
281
+ </div>
282
+ </div>
283
+
284
+ <!-- Question Input -->
285
+ <div class="space-y-3">
286
+ <label class="block text-sm font-medium text-gray-700">Sujet de dissertation</label>
287
+ <div class="relative">
288
+ <textarea id="question" rows="4"
289
+ class="w-full rounded-xl border-gray-200 shadow-sm focus:border-violet-500 focus:ring-violet-500 resize-none bg-white py-3 px-4"
290
+ placeholder="Saisissez votre sujet de dissertation..."></textarea>
291
+ <div class="absolute bottom-3 right-3 flex items-center space-x-2 text-gray-400">
292
+ <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
293
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
294
+ </svg>
295
+ </div>
296
+ </div>
297
+ </div>
298
+
299
+ <!-- Submit Button -->
300
+ <button id="submit-btn" class="w-full py-4 px-6 rounded-xl bg-gradient-to-r from-violet-600 to-indigo-600 text-white font-medium shadow-lg shadow-violet-200 hover:shadow-xl hover:shadow-violet-300 transform hover:-translate-y-0.5 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-violet-500 focus:ring-offset-2">
301
+ <span class="flex items-center justify-center space-x-2">
302
+ <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
303
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
304
+ </svg>
305
+ <span>Générer la dissertation</span>
306
+ </span>
307
+ </button>
308
+
309
+ <!-- Response Section -->
310
+ <div id="response" class="hidden mt-8 prose prose-violet max-w-none">
311
+ <div class="bg-gradient-to-r from-gray-50 to-white rounded-xl p-6 border border-gray-100">
312
+ <!-- La réponse sera insérée ici -->
313
+ </div>
314
+ </div>
315
+
316
+ <!-- Copy Button -->
317
+ <button id="copy-btn" class="hidden w-full py-3 px-6 rounded-xl bg-gray-50 text-gray-700 font-medium border border-gray-200 hover:bg-gray-100 transform hover:-translate-y-0.5 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2">
318
+ <span class="flex items-center justify-center space-x-2">
319
+ <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
320
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
321
+ </svg>
322
+ <span>Copier la dissertation</span>
323
+ </span>
324
+ </button>
325
+
326
+ <!-- Saved Dissertations Section -->
327
+ <div id="saved-dissertations" class="mt-8">
328
+ <h3 class="text-lg font-medium text-gray-700 mb-4">Dissertations Sauvegardées</h3>
329
+ <div id="dissertations-list" class="space-y-4">
330
+ <!-- Les dissertations sauvegardées seront insérées ici -->
331
+ </div>
332
+ </div>
333
+ </div>
334
+ </div>
335
+ </div>
336
+ </div>
337
+
338
+ <script>
339
+ $(document).ready(function() {
340
+ // Configuration de marked
341
+ marked.setOptions({
342
+ breaks: true,
343
+ gfm: true,
344
+ headerIds: true,
345
+ langPrefix: 'language-',
346
+ smartLists: true,
347
+ smartypants: true
348
+ });
349
+
350
+ moment.locale('fr'); // Configuration de moment.js en français
351
+
352
+ // Configuration des notifications
353
+ const Toast = Swal.mixin({
354
+ toast: true,
355
+ position: 'top-end',
356
+ showConfirmButton: false,
357
+ timer: 3000,
358
+ timerProgressBar: true,
359
+ customClass: {
360
+ popup: 'rounded-lg shadow-xl border border-gray-100'
361
+ }
362
+ });
363
+
364
+ // Animation des boutons
365
+ function addButtonAnimation(buttonId) {
366
+ $(`#${buttonId}`).on('mousedown', function() {
367
+ $(this).addClass('scale-95');
368
+ }).on('mouseup mouseleave', function() {
369
+ $(this).removeClass('scale-95');
370
+ });
371
+ }
372
+ addButtonAnimation('submit-btn');
373
+ addButtonAnimation('copy-btn');
374
+
375
+ // Gestion du changement de type
376
+ $('#type-select').change(function() {
377
+ const type = $(this).val();
378
+ const labels = {
379
+ '1': 'Type1',
380
+ '2': 'Type2',
381
+ '3': 'Synthèse'
382
+ };
383
+ $('#current-type-label').text(`Type ${type} - ${labels[type]}`);
384
+ });
385
+
386
+ // Chargement des cours avec animation
387
+ function loadCourses() {
388
+ return $.ajax({
389
+ url: '/api/philosophy/courses',
390
+ method: 'GET',
391
+ beforeSend: function() {
392
+ $('#course-select').addClass('animate-pulse');
393
+ },
394
+ complete: function() {
395
+ $('#course-select').removeClass('animate-pulse');
396
+ }
397
+ });
398
+ }
399
+
400
+ loadCourses()
401
+ .done(function(courses) {
402
+ const select = $('#course-select');
403
+ courses.forEach(course => {
404
+ select.append(`<option value="${course.id}">${course.title}</option>`);
405
+ });
406
+ })
407
+ .fail(function() {
408
+ Toast.fire({
409
+ icon: 'error',
410
+ title: 'Erreur de chargement des cours',
411
+ text: 'Veuillez réessayer ultérieurement'
412
+ });
413
+ });
414
+
415
+ // Gestion du changement de cours avec animations
416
+ $('#course-select').change(function() {
417
+ const courseId = $(this).val();
418
+ if (courseId) {
419
+ $.ajax({
420
+ url: `/api/philosophy/courses/${courseId}`,
421
+ method: 'GET',
422
+ beforeSend: function() {
423
+ $('.course-info').addClass('animate-pulse');
424
+ },
425
+ success: function(course) {
426
+ $('.course-info').removeClass('hidden animate-pulse')
427
+ .addClass('animate-fadeIn');
428
+ $('#course-title').text(course.title);
429
+ $('#course-author-name').text(`Pr. ${course.author}`);
430
+ $('#course-date span').text(new Date(course.updated_at).toLocaleDateString('fr-FR', {
431
+ day: 'numeric',
432
+ month: 'long',
433
+ year: 'numeric'
434
+ }));
435
+
436
+ // Afficher une notification de succès
437
+ Toast.fire({
438
+ icon: 'success',
439
+ title: 'Cours chargé avec succès'
440
+ });
441
+ },
442
+ error: function() {
443
+ Toast.fire({
444
+ icon: 'error',
445
+ title: 'Erreur',
446
+ text: 'Impossible de charger les détails du cours'
447
+ });
448
+ }
449
+ });
450
+ } else {
451
+ $('.course-info').addClass('animate-fadeOut').on('animationend', function() {
452
+ $(this).addClass('hidden').removeClass('animate-fadeOut');
453
+ });
454
+ }
455
+ });
456
+
457
+ // Gestion de la soumission avec conversion en Markdown et sauvegarde
458
+ $('#submit-btn').click(function() {
459
+ const question = $('#question').val().trim();
460
+
461
+ if (!question) {
462
+ // Gestion de l'erreur si la question est vide (inchangée)
463
+ return;
464
+ }
465
+
466
+ // Animation de chargement sophistiquée
467
+ Swal.fire({
468
+ title: 'Génération en cours',
469
+ html: `
470
+ <div class="space-y-4">
471
+ <div class="flex justify-center">
472
+ <div class="w-16 h-16 relative">
473
+ <div class="absolute inset-0 rounded-full border-4 border-violet-200 animate-ping"></div>
474
+ <div class="absolute inset-0 rounded-full border-4 border-violet-500 animate-pulse"></div>
475
+ </div>
476
+ </div>
477
+ <div class="text-gray-600">
478
+ <p class="animate-pulse">Analyse philosophique en cours...</p>
479
+ <p class="text-sm mt-2 text-gray-500">Veuillez patienter quelques instants</p>
480
+ </div>
481
+ </div>
482
+ `,
483
+ allowOutsideClick: false,
484
+ showConfirmButton: false,
485
+ customClass: {
486
+ popup: 'rounded-2xl'
487
+ }
488
+ });
489
+
490
+ const data = {
491
+ question: question,
492
+ type: $('#type-select').val(),
493
+ courseId: $('#course-select').val() || null
494
+ };
495
+
496
+ $.ajax({
497
+ url: '/submit_philo',
498
+ method: 'POST',
499
+ contentType: 'application/json',
500
+ data: JSON.stringify(data),
501
+ success: function(data) {
502
+ Swal.close();
503
+
504
+ const htmlContent = marked.parse(data.response);
505
+ // Afficher la dissertation
506
+ $('#response > div').html(htmlContent);
507
+ $('#response').removeClass('hidden').addClass('animate-fadeIn');
508
+ $('#copy-btn').removeClass('hidden').addClass('animate-slideUp');
509
+
510
+ // Sauvegarder la dissertation
511
+ saveDissertation(question, data.response);
512
+
513
+ Toast.fire({
514
+ icon: 'success',
515
+ title: 'Dissertation générée et sauvegardée avec succès',
516
+ timer: 2000
517
+ });
518
+ },
519
+ error: function() {
520
+ Swal.fire({
521
+ icon: 'error',
522
+ title: 'Erreur de génération',
523
+ text: 'Une erreur est survenue lors de la génération de votre dissertation.',
524
+ customClass: {
525
+ popup: 'rounded-2xl',
526
+ confirmButton: 'bg-violet-600 hover:bg-violet-700 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200'
527
+ }
528
+ });
529
+ }
530
+ });
531
+ });
532
+
533
+ // Fonction pour sauvegarder la dissertation (localStorage ou autre)
534
+ function saveDissertation(title, content) {
535
+ let savedDissertations = JSON.parse(localStorage.getItem('dissertations')) || [];
536
+ savedDissertations.push({
537
+ title,
538
+ content,
539
+ timestamp: Date.now()
540
+ });
541
+ localStorage.setItem('dissertations', JSON.stringify(savedDissertations));
542
+ updateSavedDissertationsList();
543
+
544
+ }
545
+
546
+ // Fonction pour supprimer une dissertation sauvegardée
547
+ function deleteDissertation(index) {
548
+ let savedDissertations = JSON.parse(localStorage.getItem('dissertations')) || [];
549
+ savedDissertations.splice(index, 1);
550
+ localStorage.setItem('dissertations', JSON.stringify(savedDissertations));
551
+ updateSavedDissertationsList();
552
+ Toast.fire({
553
+ icon: 'success',
554
+ title: 'Dissertation supprimée avec succès'
555
+ });
556
+
557
+ }
558
+
559
+ // Fonction pour afficher les dissertations sauvegardées (avec sections repliables et suppression)
560
+ function updateSavedDissertationsList() {
561
+ const dissertationsList = $('#dissertations-list');
562
+ dissertationsList.empty(); // Vider la liste actuelle
563
+
564
+ let savedDissertations = JSON.parse(localStorage.getItem('dissertations')) || [];
565
+
566
+ if (savedDissertations.length === 0) {
567
+ dissertationsList.append('<p class="text-gray-500">Aucune dissertation sauvegardée.</p>');
568
+ return;
569
+ }
570
+
571
+ savedDissertations.forEach((diss, index) => {
572
+ const date = moment(diss.timestamp).format('LLL');
573
+
574
+ const collapsible = $(`<button class="collapsible rounded-xl border border-gray-100 flex justify-between w-full"><span>${diss.title}</span><span class="text-gray-500 text-sm">${date}</span></button>`);
575
+ const deleteButton = $('<button class="text-red-500 hover:text-red-700 ml-2">Supprimer</button>');
576
+ const content = $('<div class="content prose prose-violet max-w-none p-4"></div>').html(marked.parse(diss.content)); // Convertir en HTML
577
+
578
+ collapsible.append(deleteButton); // Ajouter le bouton supprimer
579
+ dissertationsList.append(collapsible, content);
580
+
581
+ // Gestionnaire d'événement pour chaque section repliable
582
+ collapsible.click(function(event) {
583
+ // Empêcher la propagation de l'événement click sur le bouton "Supprimer"
584
+ if (event.target === deleteButton[0]) {
585
+ return;
586
+ }
587
+
588
+ content.slideToggle("fast");
589
+ collapsible.toggleClass("active");
590
+ });
591
+
592
+ deleteButton.click(function() {
593
+ // Supprimer la dissertation correspondante
594
+ deleteDissertation(index);
595
+
596
+ });
597
+ });
598
+ }
599
+
600
+ // Appeler la fonction pour afficher les dissertations au chargement de la page
601
+ updateSavedDissertationsList();
602
+
603
+ // Nouvelle gestion de la copie
604
+ $('#copy-btn').click(function() {
605
+ // Sélectionner le contenu de la réponse en utilisant innerHTML pour obtenir le HTML formaté
606
+ const responseDiv = document.querySelector('#response > div');
607
+ let textToCopy = '';
608
+
609
+ // Créer un élément temporaire pour convertir le HTML en texte brut tout en préservant le formatage
610
+ const temp = document.createElement('div');
611
+ temp.innerHTML = responseDiv.innerHTML;
612
+
613
+ // Fonction récursive pour extraire le texte en préservant les sauts de ligne
614
+ function extractText(node) {
615
+ let text = '';
616
+ node.childNodes.forEach(child => {
617
+ if (child.nodeType === 3) { // Nœud texte
618
+ text += child.textContent;
619
+ } else if (child.nodeType === 1) { // Élément
620
+ // Ajouter des sauts de ligne pour les éléments de bloc
621
+ if (window.getComputedStyle(child).display === 'block') {
622
+ text += '\n';
623
+ }
624
+ text += extractText(child);
625
+ if (window.getComputedStyle(child).display === 'block') {
626
+ text += '\n';
627
+ }
628
+ }
629
+ });
630
+ return text;
631
+ }
632
+
633
+ textToCopy = extractText(temp).trim();
634
+
635
+ // Animation et copie
636
+ $(this).addClass('scale-95 bg-violet-100');
637
+
638
+ // Utiliser l'API Clipboard avec gestion des erreurs
639
+ navigator.clipboard.writeText(textToCopy)
640
+ .then(() => {
641
+ $(this).removeClass('scale-95 bg-violet-100')
642
+ .addClass('bg-green-50 text-green-700');
643
+
644
+ setTimeout(() => {
645
+ $(this).removeClass('bg-green-50 text-green-700');
646
+ }, 1000);
647
+
648
+ Toast.fire({
649
+ icon: 'success',
650
+ title: 'Copié avec succès',
651
+ text: 'Le contenu a été copié dans votre presse-papiers',
652
+ timer: 2000
653
+ });
654
+ })
655
+ .catch((err) => {
656
+ // Fallback pour les appareils mobiles qui ne supportent pas l'API Clipboard
657
+ try {
658
+ // Créer un élément textarea temporaire
659
+ const textarea = document.createElement('textarea');
660
+ textarea.value = textToCopy;
661
+ textarea.style.position = 'fixed'; // Évite le défilement
662
+ textarea.style.opacity = '0';
663
+ document.body.appendChild(textarea);
664
+
665
+ // Sélectionner et copier le texte
666
+ textarea.select();
667
+ document.execCommand('copy');
668
+
669
+ // Nettoyer
670
+ document.body.removeChild(textarea);
671
+
672
+ // Feedback positif
673
+ $(this).removeClass('scale-95 bg-violet-100')
674
+ .addClass('bg-green-50 text-green-700');
675
+
676
+ setTimeout(() => {
677
+ $(this).removeClass('bg-green-50 text-green-700');
678
+ }, 1000);
679
+
680
+ Toast.fire({
681
+ icon: 'success',
682
+ title: 'Copié avec succès',
683
+ timer: 2000
684
+ });
685
+ } catch (fallbackErr) {
686
+ // Si même le fallback échoue
687
+ $(this).removeClass('scale-95 bg-violet-100')
688
+ .addClass('bg-red-50 text-red-700');
689
+
690
+ setTimeout(() => {
691
+ $(this).removeClass('bg-red-50 text-red-700');
692
+ }, 1000);
693
+
694
+ Toast.fire({
695
+ icon: 'error',
696
+ title: 'Erreur de copie',
697
+ text: 'Impossible de copier le contenu',
698
+ timer: 3000
699
+ });
700
+ }
701
+ });
702
+ });
703
+
704
+ // Ajout des styles d'animation personnalisés
705
+ const style = document.createElement('style');
706
+ style.textContent = `
707
+ @keyframes fadeIn {
708
+ from { opacity: 0; transform: translateY(10px); }
709
+ to { opacity: 1; transform: translateY(0); }
710
+ }
711
+ @keyframes slideUp {
712
+ from { opacity: 0; transform: translateY(20px); }
713
+ to { opacity: 1; transform: translateY(0); }
714
+ }
715
+ @keyframes shake {
716
+ 0%, 100% { transform: translateX(0); }
717
+ 25% { transform: translateX(-5px); }
718
+ 75% { transform: translateX(5px); }
719
+ }
720
+ .animate-fadeIn {
721
+ animation: fadeIn 0.5s ease-out forwards;
722
+ }
723
+ .animate-slideUp {
724
+ animation: slideUp 0.5s ease-out forwards;
725
+ }
726
+ .animate-shake {
727
+ animation: shake 0.5s ease-in-out;
728
+ }
729
+ `;
730
+ document.head.appendChild(style);
731
+ });
732
+ </script>
733
+ </body>
734
+ </html>