Docfile commited on
Commit
3f851f5
·
verified ·
1 Parent(s): c5bddce

Update templates/philosophie.html

Browse files
Files changed (1) hide show
  1. templates/philosophie.html +204 -725
templates/philosophie.html CHANGED
@@ -1,741 +1,220 @@
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
- <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
9
- <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js"></script>
10
- <script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert2/11.7.3/sweetalert2.all.min.js"></script>
11
- <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.3.0/marked.min.js"></script>
12
- <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
13
- <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/locale/fr.js"></script>
14
- <link href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert2/11.7.3/sweetalert2.min.css" rel="stylesheet">
15
- <script src="https://cdn.tailwindcss.com"></script>
16
- <style>
17
- /* Styles existants */
18
- .collapsible {
19
- cursor: pointer;
20
- padding: 18px;
21
- width: 100%;
22
- border: none;
23
- text-align: left;
24
- outline: none;
25
- font-size: 15px;
26
- background-color: #f1f1f1;
27
- display: flex;
28
- justify-content: space-between;
29
- align-items: center;
30
- }
31
-
32
- /* Nouveaux styles pour le Markdown responsive */
33
- .prose {
34
- max-width: 100% !important;
35
- }
36
-
37
- .prose p {
38
- margin-top: 1.25em;
39
- margin-bottom: 1.25em;
40
- line-height: 1.75;
41
- }
42
-
43
- .prose ul {
44
- margin-top: 1.25em;
45
- margin-bottom: 1.25em;
46
- padding-left: 1.625em;
47
- }
48
-
49
- .prose li {
50
- margin-top: 0.5em;
51
- margin-bottom: 0.5em;
52
- padding-left: 0.375em;
53
- }
54
-
55
- .prose h1, .prose h2, .prose h3 {
56
- margin-top: 2em;
57
- margin-bottom: 1em;
58
- line-height: 1.3;
59
- }
60
-
61
- /* Styles spécifiques pour mobile */
62
- @media (max-width: 640px) {
63
- .prose {
64
- font-size: 0.95rem;
65
- }
66
-
67
- .prose p {
68
- margin-top: 1em;
69
- margin-bottom: 1em;
70
- }
71
-
72
- .prose ul {
73
- padding-left: 1.25em;
74
- }
75
-
76
- .prose li {
77
- margin-top: 0.375em;
78
- margin-bottom: 0.375em;
79
- }
80
-
81
- .prose h1, .prose h2, .prose h3 {
82
- margin-top: 1.5em;
83
- margin-bottom: 0.75em;
84
- }
85
- }
86
-
87
- /* Styles pour améliorer la lisibilité du texte */
88
- #response .prose {
89
- color: #374151;
90
- word-wrap: break-word;
91
- overflow-wrap: break-word;
92
- hyphens: auto;
93
- }
94
-
95
- #response .prose > * + * {
96
- margin-top: 1em;
97
- }
98
-
99
- #response .prose blockquote {
100
- margin: 1.5em 0;
101
- padding-left: 1em;
102
- border-left: 4px solid #e5e7eb;
103
- font-style: italic;
104
- }
105
-
106
- #response .prose code {
107
- background-color: #f3f4f6;
108
- padding: 0.2em 0.4em;
109
- border-radius: 0.25em;
110
- font-size: 0.875em;
111
- }
112
-
113
- .active, .collapsible:hover {
114
- background-color: #ddd;
115
- }
116
-
117
- .content {
118
- padding: 0 18px;
119
- display: none;
120
- overflow: hidden;
121
- background-color: white;
122
- }
123
- .animate-fadeIn {
124
- animation: fadeIn 0.5s ease-out forwards;
125
- }
126
- .animate-slideUp {
127
- animation: slideUp 0.5s ease-out forwards;
128
- }
129
- .animate-shake {
130
- animation: shake 0.5s ease-in-out;
131
- }
132
- @keyframes fadeIn {
133
- from { opacity: 0; transform: translateY(10px); }
134
- to { opacity: 1; transform: translateY(0); }
135
- }
136
- @keyframes slideUp {
137
- from { opacity: 0; transform: translateY(20px); }
138
- to { opacity: 1; transform: translateY(0); }
139
- }
140
- @keyframes shake {
141
- 0%, 100% { transform: translateX(0); }
142
- 25% { transform: translateX(-5px); }
143
- 75% { transform: translateX(5px); }
144
- }
145
-
146
- /* Ajout du style pour Select2 */
147
- .select2-container--default .select2-selection--single {
148
- border: 1px solid #e5e7eb;
149
- border-radius: 0.75rem;
150
- height: auto;
151
- padding: 0.625rem 1rem;
152
- background-color: white;
153
- box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
154
- display: flex;
155
- align-items: center;
156
- }
157
- .select2-container--default .select2-selection--single .select2-selection__rendered {
158
- color: #374151;
159
- line-height: inherit;
160
- padding-right: 1.5rem;
161
- }
162
- .select2-container--default .select2-selection--single .select2-selection__arrow {
163
- top: 50%;
164
- transform: translateY(-50%);
165
- right: 0.75rem;
166
- }
167
- .select2-dropdown {
168
- border: 1px solid #e5e7eb;
169
- border-radius: 0.75rem;
170
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
171
- padding: 0.25rem;
172
- }
173
- .select2-search--dropdown .select2-search__field {
174
- border: 1px solid #e5e7eb;
175
- border-radius: 0.375rem;
176
- padding: 0.5rem;
177
- }
178
- .select2-results__options {
179
- padding: 0.25rem;
180
- }
181
- .select2-results__option {
182
- padding: 0.5rem 0.75rem;
183
- }
184
- .select2-container--default .select2-results__option--highlighted.select2-results__option--selectable{
185
- background-color: #ede9fe;
186
- color:#374151;
187
- }
188
- .select2-container--default .select2-results__option--selected {
189
- background-color: #f3f4f6;
190
- color:#374151;
191
- }
192
- .select2-results__option .course-author {
193
- font-size: 0.875rem;
194
- color: #6b7280;
195
- display: block;
196
- margin-top: 0.25rem;
197
- }
198
- .select2-container--open .select2-selection--single .select2-selection__arrow {
199
- border-color: transparent transparent #a8a29e;
200
- }
201
- </style>
202
- </head>
203
- <body class="bg-gradient-to-br from-violet-50 to-indigo-50 min-h-screen">
204
- <!-- Navbar -->
205
- <nav class="bg-white/80 backdrop-blur-md border-b border-gray-200 fixed w-full z-50">
206
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
207
- <div class="flex justify-between items-center h-16">
208
- <div class="flex items-center">
209
- <div class="text-2xl font-bold bg-gradient-to-r from-violet-600 to-indigo-600 text-transparent bg-clip-text">
210
- Mariam AI
211
- </div>
212
- </div>
213
- <div class="flex items-center space-x-4">
214
- <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-violet-100 text-violet-800">
215
- <span class="w-2 h-2 bg-violet-400 rounded-full animate-pulse mr-2"></span>
216
- Assistant Philosophique
217
- </span>
218
- </div>
219
- </div>
220
- </div>
221
- </nav>
222
-
223
- <!-- Main Content -->
224
- <div class="pt-24 pb-12 px-4 sm:px-6 lg:px-8">
225
- <div class="max-w-4xl mx-auto">
226
- <!-- Main Card -->
227
- <div class="bg-white/80 backdrop-blur-md rounded-2xl shadow-xl border border-gray-100 overflow-hidden">
228
- <!-- Header Section -->
229
- <div class="bg-gradient-to-r from-violet-600 to-indigo-600 p-6 text-white">
230
- <h2 class="text-2xl font-bold">Gen'Dissertation</h2>
231
- <p class="mt-2 opacity-90">Créez des dissertations philosophiques pertinentes et structurées</p>
232
- </div>
233
- <!-- Content Section -->
234
- <div class="p-8 space-y-8">
235
- <!-- Type Selection -->
236
- <div class="space-y-3">
237
- <label class="block text-sm font-medium text-gray-700">Type de dissertation</label>
238
- <div class="relative">
239
- <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">
240
- <option value="1">Type 1</option>
241
- <option value="2">Type 2</option>
242
- <option value="3">Synthèse</option>
243
- </select>
244
- <div class="absolute inset-y-0 right-0 flex items-center px-4 pointer-events-none">
245
- <svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
246
- <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" />
247
- </svg>
248
- </div>
249
- </div>
250
- <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">
251
- Sujet de type 1
252
- </div>
253
- </div>
254
- <!-- Course Selection -->
255
- <div class="space-y-3">
256
- <label class="block text-sm font-medium text-gray-700">Sélection du cours</label>
257
- <select id="course-select" class="w-full">
258
- <option value="">Choisir un cours...</option>
259
- </select>
260
- <div class="course-meta hidden">
261
- <div class="bg-gradient-to-r from-gray-50 to-white rounded-xl p-4 border border-gray-100">
262
- <div class="flex justify-between items-center">
263
- <span id="course-author" class="flex items-center space-x-2">
264
- <svg class="h-5 w-5 text-violet-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
265
- <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" />
266
- </svg>
267
- <span class="text-gray-600"></span>
268
- </span>
269
- <span id="course-date" class="flex items-center space-x-2">
270
- <svg class="h-5 w-5 text-violet-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
271
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
272
- </svg>
273
- <span class="text-gray-600"></span>
274
- </span>
275
- </div>
276
- </div>
277
- </div>
278
- </div>
279
- <!-- Question Input -->
280
- <div class="space-y-3">
281
- <label class="block text-sm font-medium text-gray-700">Sujet de dissertation</label>
282
- <div class="relative">
283
- <textarea id="question" rows="4" 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" placeholder="Saisissez votre sujet de dissertation..."></textarea>
284
- <div class="absolute bottom-3 right-3 flex items-center space-x-2 text-gray-400">
285
- <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
286
- <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" />
287
- </svg>
288
- </div>
289
- </div>
290
- </div>
291
- <!-- Submit Button -->
292
- <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">
293
- <span class="flex items-center justify-center space-x-2">
294
- <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
295
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
296
- </svg>
297
- <span>Générer la dissertation</span>
298
- </span>
299
- </button>
300
- <!-- Response Section -->
301
- <div id="response" class="hidden mt-8 prose prose-violet max-w-none">
302
- <div class="bg-gradient-to-r from-gray-50 to-white rounded-xl p-6 border border-gray-100">
303
- <!-- La réponse sera insérée ici -->
304
- </div>
305
- </div>
306
- <!-- Copy Button -->
307
- <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">
308
- <span class="flex items-center justify-center space-x-2">
309
- <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
310
- <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" />
311
- </svg>
312
- <span>Copier la dissertation</span>
313
- </span>
314
- </button>
315
- <!-- Saved Dissertations Section -->
316
- <div id="saved-dissertations" class="mt-8">
317
- <h3 class="text-lg font-medium text-gray-700 mb-4">Dissertations Sauvegardées</h3>
318
- <div id="dissertations-list" class="space-y-4">
319
- <!-- Les dissertations sauvegardées seront insérées ici -->
320
- </div>
321
- </div>
322
- </div>
323
- </div>
324
- </div>
325
- </div>
326
-
327
- <!-- Bouton flottant DeepThink -->
328
- <button id="deepthink-btn" class="fixed bottom-6 right-6 z-50 bg-indigo-600 text-white px-4 py-2 rounded-full shadow-lg hover:bg-indigo-700 focus:outline-none">
329
- DeepThink
330
- </button>
331
-
332
- <script>
333
- $(document).ready(function() {
334
- // Initialisation de Select2
335
- $('#course-select').select2({
336
- placeholder: 'Choisir un cours...',
337
- templateResult: function (course) {
338
- if (!course.id) {
339
- return course.text;
340
- }
341
- const author = $(course.element).data('author');
342
- return $(`
343
- <span>${course.text}</span>
344
- <span class="course-author">Pr. ${author}</span>
345
- `);
346
- },
347
- templateSelection: function (course) {
348
- return course.text;
349
- },
350
- });
351
-
352
- // Configuration de marked
353
- marked.setOptions({
354
- breaks: true,
355
- gfm: true,
356
- headerIds: true,
357
- langPrefix: 'language-',
358
- smartLists: true,
359
- smartypants: true
360
- });
361
-
362
- moment.locale('fr');
363
-
364
- // Configuration des notifications SweetAlert2
365
- const Toast = Swal.mixin({
366
- toast: true,
367
- position: 'top-end',
368
- showConfirmButton: false,
369
- timer: 3000,
370
- timerProgressBar: true,
371
- customClass: {
372
- popup: 'rounded-lg shadow-xl border border-gray-100'
373
  }
374
- });
375
-
376
- // Animation des boutons
377
- function addButtonAnimation(buttonId) {
378
- $(`#${buttonId}`).on('mousedown', function() {
379
- $(this).addClass('scale-95');
380
- }).on('mouseup mouseleave', function() {
381
- $(this).removeClass('scale-95');
382
- });
383
- }
384
- addButtonAnimation('submit-btn');
385
- addButtonAnimation('copy-btn');
386
-
387
- // Gestion du changement de type
388
- $('#type-select').change(function() {
389
- const selectedOption = $(this).find('option:selected');
390
- const typeLabel = selectedOption.text();
391
- const type = $(this).val();
392
- let labelText;
393
- if (type === '3') {
394
- labelText = 'S - Synthèse';
395
- } else {
396
- labelText = `Type ${type} - ${typeLabel}`;
397
  }
398
- $('#current-type-label').text(labelText);
399
- });
400
-
401
- // Chargement des cours
402
- function loadCourses() {
403
- return $.ajax({
404
- url: '/api/philosophy/courses',
405
- method: 'GET',
406
- beforeSend: function() {
407
- $('#course-select').prop('disabled', true).addClass('animate-pulse');
408
- },
409
- complete: function() {
410
- $('#course-select').prop('disabled', false).removeClass('animate-pulse');
411
- }
412
- });
413
- }
414
- loadCourses()
415
- .done(function(courses) {
416
- const select = $('#course-select');
417
- courses.forEach(course => {
418
- const newOption = new Option(course.title, course.id, false, false);
419
- $(newOption).data('author', course.author);
420
- select.append(newOption);
421
- });
422
- })
423
- .fail(function() {
424
- Toast.fire({
425
- icon: 'error',
426
- title: 'Erreur de chargement des cours',
427
- text: 'Veuillez réessayer ultérieurement'
428
- });
429
- });
430
-
431
- // Gestion du changement de cours
432
- $('#course-select').on('change', function() {
433
- const courseId = $(this).val();
434
- if (courseId) {
435
- $.ajax({
436
- url: `/api/philosophy/courses/${courseId}`,
437
- method: 'GET',
438
- beforeSend: function() {
439
- $('.course-meta').addClass('animate-pulse');
440
- },
441
- success: function(course) {
442
- $('.course-meta').removeClass('hidden animate-pulse')
443
- .addClass('animate-fadeIn');
444
- $('#course-author span').text(`Pr. ${course.author}`);
445
- $('#course-date span').text(new Date(course.updated_at).toLocaleDateString('fr-FR', {
446
- day: 'numeric',
447
- month: 'long',
448
- year: 'numeric'
449
- }));
450
- Toast.fire({
451
- icon: 'success',
452
- title: 'Cours chargé avec succès'
453
- });
454
- },
455
- error: function() {
456
- Toast.fire({
457
- icon: 'error',
458
- title: 'Erreur',
459
- text: 'Impossible de charger les détails du cours'
460
- });
461
- }
462
- });
463
- } else {
464
- $('.course-meta').addClass('animate-fadeOut').on('animationend', function() {
465
- $(this).addClass('hidden').removeClass('animate-fadeOut');
466
- });
467
  }
468
- });
469
-
470
- // Gestion de la soumission standard
471
- $('#submit-btn').click(function() {
472
- const question = $('#question').val().trim();
473
- if (!question) {
474
- return;
 
475
  }
476
- Swal.fire({
477
- title: 'Génération en cours',
478
- html: `
479
- <div class="space-y-4">
480
- <div class="flex justify-center">
481
- <div class="w-16 h-16 relative">
482
- <div class="absolute inset-0 rounded-full border-4 border-violet-200 animate-ping"></div>
483
- <div class="absolute inset-0 rounded-full border-4 border-violet-500 animate-pulse"></div>
484
- </div>
485
- </div>
486
- <div class="text-gray-600">
487
- <p class="animate-pulse">Analyse philosophique en cours...</p>
488
- <p class="text-sm mt-2 text-gray-500">Veuillez patienter quelques instants</p>
489
- </div>
490
- </div>
491
- `,
492
- allowOutsideClick: false,
493
- showConfirmButton: false,
494
- customClass: {
495
- popup: 'rounded-2xl'
496
- }
497
- });
498
- const data = {
499
- question: question,
500
- type: $('#type-select').val(),
501
- courseId: $('#course-select').val() || null
502
- };
503
- $.ajax({
504
- url: '/submit_philo',
505
- method: 'POST',
506
- contentType: 'application/json',
507
- data: JSON.stringify(data),
508
- success: function(data) {
509
- Swal.close();
510
- const htmlContent = marked.parse(data.response);
511
- $('#response > div').html(htmlContent);
512
- $('#response').removeClass('hidden').addClass('animate-fadeIn');
513
- $('#copy-btn').removeClass('hidden').addClass('animate-slideUp');
514
- saveDissertation(question, data.response);
515
- Toast.fire({
516
- icon: 'success',
517
- title: 'Dissertation générée et sauvegardée avec succès',
518
- timer: 2000
519
- });
520
- },
521
- error: function() {
522
- Swal.fire({
523
- icon: 'error',
524
- title: 'Erreur de génération',
525
- text: 'Une erreur est survenue lors de la génération de votre dissertation.',
526
- customClass: {
527
- popup: 'rounded-2xl',
528
- confirmButton: 'bg-violet-600 hover:bg-violet-700 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200'
529
- }
530
- });
531
- }
532
- });
533
- });
534
-
535
- function saveDissertation(title, content) {
536
- let savedDissertations = JSON.parse(localStorage.getItem('dissertations')) || [];
537
- savedDissertations.push({ title, content, timestamp: Date.now() });
538
- localStorage.setItem('dissertations', JSON.stringify(savedDissertations));
539
- updateSavedDissertationsList();
540
- }
541
 
542
- function deleteDissertation(index) {
543
- let savedDissertations = JSON.parse(localStorage.getItem('dissertations')) || [];
544
- savedDissertations.splice(index, 1);
545
- localStorage.setItem('dissertations', JSON.stringify(savedDissertations));
546
- updateSavedDissertationsList();
547
- Toast.fire({
548
- icon: 'success',
549
- title: 'Dissertation supprimée avec succès'
550
- });
551
- }
552
 
553
- function updateSavedDissertationsList() {
554
- const dissertationsList = $('#dissertations-list');
555
- dissertationsList.empty();
556
- let savedDissertations = JSON.parse(localStorage.getItem('dissertations')) || [];
557
- if (savedDissertations.length === 0) {
558
- dissertationsList.append('<p class="text-gray-500">Aucune dissertation sauvegardée.</p>');
559
- return;
560
- }
561
- savedDissertations.forEach((diss, index) => {
562
- const date = moment(diss.timestamp).format('LLL');
563
- 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>`);
564
- const deleteButton = $('<button class="text-red-500 hover:text-red-700 ml-2">Supprimer</button>');
565
- const content = $('<div class="content prose prose-violet max-w-none p-4"></div>').html(marked.parse(diss.content));
566
- collapsible.append(deleteButton);
567
- dissertationsList.append(collapsible, content);
568
- collapsible.click(function(event) {
569
- if (event.target === deleteButton[0]) {
570
- return;
571
- }
572
- content.slideToggle("fast");
573
- collapsible.toggleClass("active");
574
- });
575
- deleteButton.click(function() {
576
- deleteDissertation(index);
577
- });
578
- });
579
- }
580
- updateSavedDissertationsList();
581
 
582
- $('#copy-btn').click(function() {
583
- const responseDiv = document.querySelector('#response > div');
584
- let textToCopy = '';
585
- const temp = document.createElement('div');
586
- temp.innerHTML = responseDiv.innerHTML;
587
- function extractText(node) {
588
- let text = '';
589
- node.childNodes.forEach(child => {
590
- if (child.nodeType === 3) {
591
- text += child.textContent;
592
- } else if (child.nodeType === 1) {
593
- if (window.getComputedStyle(child).display === 'block') {
594
- text += '\n';
595
- }
596
- text += extractText(child);
597
- if (window.getComputedStyle(child).display === 'block') {
598
- text += '\n';
599
- }
600
- }
601
- });
602
- return text;
603
- }
604
- textToCopy = extractText(temp).trim();
605
- $(this).addClass('scale-95 bg-violet-100');
606
- navigator.clipboard.writeText(textToCopy)
607
- .then(() => {
608
- $(this).removeClass('scale-95 bg-violet-100')
609
- .addClass('bg-green-50 text-green-700');
610
- setTimeout(() => {
611
- $(this).removeClass('bg-green-50 text-green-700');
612
- }, 1000);
613
- Toast.fire({
614
- icon: 'success',
615
- title: 'Copié avec succès',
616
- text: 'Le contenu a été copié dans votre presse-papiers',
617
- timer: 2000
 
 
 
 
 
 
 
 
 
618
  });
619
- })
620
- .catch((err) => {
621
- try {
622
- const textarea = document.createElement('textarea');
623
- textarea.value = textToCopy;
624
- textarea.style.position = 'fixed';
625
- textarea.style.opacity = '0';
626
- document.body.appendChild(textarea);
627
- textarea.select();
628
- document.execCommand('copy');
629
- document.body.removeChild(textarea);
630
- $(this).removeClass('scale-95 bg-violet-100')
631
- .addClass('bg-green-50 text-green-700');
632
- setTimeout(() => {
633
- $(this).removeClass('bg-green-50 text-green-700');
634
- }, 1000);
635
- Toast.fire({
636
- icon: 'success',
637
- title: 'Copié avec succès',
638
- timer: 2000
639
- });
640
- } catch (fallbackErr) {
641
- $(this).removeClass('scale-95 bg-violet-100')
642
- .addClass('bg-red-50 text-red-700');
643
- setTimeout(() => {
644
- $(this).removeClass('bg-red-50 text-red-700');
645
- }, 1000);
646
- Toast.fire({
647
- icon: 'error',
648
- title: 'Erreur de copie',
649
- text: 'Impossible de copier le contenu',
650
- timer: 3000
651
- });
 
 
 
652
  }
653
- });
654
- });
655
 
656
- // Bouton flottant DeepThink
657
- // Vérification de la limite d'une utilisation par jour via localStorage
658
- function isToday(dateStr) {
659
- const today = new Date();
660
- const date = new Date(dateStr);
661
- return date.getFullYear() === today.getFullYear() &&
662
- date.getMonth() === today.getMonth() &&
663
- date.getDate() === today.getDate();
664
- }
 
 
 
 
 
 
 
 
665
 
666
- $('#deepthink-btn').click(function() {
667
- const lastUsed = localStorage.getItem('deepThinkLastUsed');
668
- if (lastUsed && isToday(lastUsed)) {
669
- Swal.fire({
670
- icon: 'warning',
671
- title: "Limite quotidienne",
672
- text: "Vous ne pouvez utiliser DeepThink qu'une seule fois par jour."
673
- });
674
- return;
675
- }
676
- const question = $('#question').val().trim();
677
- const type = $('#type-select').val();
678
- const courseId = $('#course-select').val() || null;
679
- if (!question) {
680
- Swal.fire({
681
- icon: 'error',
682
- title: "Sujet manquant",
683
- text: "Veuillez saisir un sujet dans le champ principal."
684
- });
685
- return;
686
- }
687
- Swal.fire({
688
- title: 'DeepThink en cours',
689
- html: `
690
- <div class="flex justify-center">
691
- <div class="w-16 h-16 relative">
692
- <div class="absolute inset-0 rounded-full border-4 border-indigo-200 animate-ping"></div>
693
- <div class="absolute inset-0 rounded-full border-4 border-indigo-500 animate-pulse"></div>
694
- </div>
695
- </div>
696
- <p class="mt-4 text-gray-600">Veuillez patienter quelques instants...</p>
697
- `,
698
- allowOutsideClick: false,
699
- showConfirmButton: false,
700
- customClass: {
701
- popup: 'rounded-2xl'
702
- }
703
- });
704
- const data = { question, type, courseId };
705
- $.ajax({
706
- url: '/submit_philo_deepthink',
707
- method: 'POST',
708
- contentType: 'application/json',
709
- data: JSON.stringify(data),
710
- success: function(response) {
711
- Swal.close();
712
- const htmlContent = marked.parse(response.response);
713
- $('#response > div').html(htmlContent);
714
- $('#response').removeClass('hidden').addClass('animate-fadeIn');
715
- $('#copy-btn').removeClass('hidden').addClass('animate-slideUp');
716
- saveDissertation(question, response.response);
717
- localStorage.setItem('deepThinkLastUsed', new Date().toISOString());
718
- Swal.fire({
719
- icon: 'success',
720
- title: 'DeepThink généré avec succès',
721
- timer: 2000,
722
- showConfirmButton: false
723
- });
724
- },
725
- error: function() {
726
- Swal.fire({
727
- icon: 'error',
728
- title: 'Erreur DeepThink',
729
- text: 'Une erreur est survenue lors de la génération DeepThink.',
730
- customClass: {
731
- popup: 'rounded-2xl'
732
- }
733
- });
734
- }
735
  });
736
- });
737
- });
738
- </script>
739
- </body>
740
- </html>
741
 
 
 
 
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>Assistant de Philosophie</title>
7
+
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link href="https://fonts.googleapis.com/css2?family=Kalam&family=Lato:wght@400;700&display=swap" rel="stylesheet">
12
+
13
+ <style>
14
+ /* --- Styles Généraux et Interface --- */
15
+ body {
16
+ font-family: 'Lato', sans-serif;
17
+ background-color: #f4f4f9;
18
+ margin: 0;
19
+ padding: 20px;
20
+ color: #333;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  }
22
+ .container {
23
+ max-width: 900px;
24
+ margin: 0 auto;
25
+ background: white;
26
+ padding: 30px;
27
+ border-radius: 8px;
28
+ box-shadow: 0 4px 10px rgba(0,0,0,0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  }
30
+ h1 {
31
+ text-align: center;
32
+ color: #2c3e50;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  }
34
+ textarea {
35
+ width: 100%;
36
+ padding: 10px;
37
+ border-radius: 4px;
38
+ border: 1px solid #ddd;
39
+ min-height: 80px;
40
+ margin-bottom: 10px;
41
+ font-size: 16px;
42
  }
43
+ button {
44
+ display: block;
45
+ width: 100%;
46
+ padding: 15px;
47
+ background-color: #2980b9;
48
+ color: white;
49
+ border: none;
50
+ border-radius: 5px;
51
+ font-size: 18px;
52
+ cursor: pointer;
53
+ transition: background-color 0.3s;
54
+ }
55
+ button:hover { background-color: #3498db; }
56
+ button:disabled { background-color: #95a5a6; cursor: not-allowed; }
57
+
58
+ .loader {
59
+ display: none;
60
+ border: 8px solid #f3f3f3;
61
+ border-top: 8px solid #3498db;
62
+ border-radius: 50%;
63
+ width: 50px;
64
+ height: 50px;
65
+ animation: spin 1s linear infinite;
66
+ margin: 20px auto;
67
+ }
68
+ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
69
+ .error { color: #c0392b; text-align: center; margin-top: 20px; }
70
+
71
+ /* --- Styles du Rendu "Cahier" --- */
72
+ .dissertation-paper {
73
+ font-family: 'Kalam', cursive;
74
+ font-size: 20px;
75
+ color: #1a2a4c;
76
+ background-color: #fdfaf4;
77
+ line-height: 1.8;
78
+ background-image: linear-gradient(transparent 97%, #d8e2ee 98%);
79
+ background-size: 100% 36px;
80
+ border-left: 2px solid #ffaaab;
81
+ padding-left: 4em;
82
+ margin: 40px -30px -30px -30px; /* S'étend aux bords du container */
83
+ padding-top: 30px;
84
+ padding-bottom: 30px;
85
+ padding-right: 30px;
86
+ }
87
+ .dissertation-paper h2 { font-size: 1.5em; text-align: center; margin-bottom: 1.8em; }
88
+ .dissertation-paper h3 { font-size: 1.2em; margin-top: 3.6em; margin-bottom: 1.8em; text-transform: uppercase; text-decoration: underline; }
89
+ .dissertation-paper .development-block { margin-top: 3.6em; }
90
+ .dissertation-paper p { text-align: justify; margin: 0; padding: 0; }
91
+ .dissertation-paper .prof { text-align: center; font-style: italic; margin-bottom: 1.8em; }
92
+ .dissertation-paper .indented { text-indent: 3em; }
93
+ .dissertation-paper .transition { margin-top: 1.8em; margin-bottom: 1.8em; font-style: italic; color: #4a6a9c; }
94
+ .dissertation-paper .pdf-button-container { text-align: center; margin-top: 3.6em; }
95
+ .dissertation-paper .pdf-button { font-family: 'Lato', sans-serif; font-size: 16px; padding: 10px 20px; width: auto; }
96
+ </style>
97
+ </head>
98
+ <body>
 
 
 
 
 
 
 
 
 
99
 
100
+ <div class="container">
101
+ <h1>Assistant de Dissertation Philosophique</h1>
102
+ <form id="philo-form">
103
+ <textarea id="question" name="question" placeholder="Entrez votre sujet de dissertation ici... Par exemple : 'Sommes-nous toujours conscients ?'"></textarea>
104
+ <button type="submit" id="submit-btn">Générer la dissertation</button>
105
+ </form>
 
 
 
 
106
 
107
+ <div id="loader" class="loader"></div>
108
+ <div id="result-container"></div>
109
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
+ <script>
112
+ document.addEventListener('DOMContentLoaded', () => {
113
+ const form = document.getElementById('philo-form');
114
+ const submitBtn = document.getElementById('submit-btn');
115
+ const loader = document.getElementById('loader');
116
+ const resultContainer = document.getElementById('result-container');
117
+
118
+ form.addEventListener('submit', async (event) => {
119
+ event.preventDefault();
120
+ const question = document.getElementById('question').value.trim();
121
+
122
+ if (!question) {
123
+ showError("Veuillez entrer un sujet de dissertation.");
124
+ return;
125
+ }
126
+
127
+ // UI management
128
+ submitBtn.disabled = true;
129
+ submitBtn.textContent = 'Génération en cours...';
130
+ loader.style.display = 'block';
131
+ resultContainer.innerHTML = '';
132
+
133
+ try {
134
+ const response = await fetch('/api/generate_dissertation', {
135
+ method: 'POST',
136
+ headers: { 'Content-Type': 'application/json' },
137
+ body: JSON.stringify({ question: question })
138
+ });
139
+
140
+ if (!response.ok) {
141
+ const errorData = await response.json();
142
+ throw new Error(errorData.error || "Une erreur inconnue est survenue.");
143
+ }
144
+
145
+ const data = await response.json();
146
+ renderDissertation(data);
147
+
148
+ } catch (error) {
149
+ showError(error.message);
150
+ } finally {
151
+ // Reset UI
152
+ submitBtn.disabled = false;
153
+ submitBtn.textContent = 'Générer la dissertation';
154
+ loader.style.display = 'none';
155
+ }
156
  });
157
+
158
+ function renderDissertation(data) {
159
+ let html = `
160
+ <div id="dissertation-content" class="dissertation-paper">
161
+ <h2>Sujet : ${data.sujet}</h2>
162
+ <p class="prof">Prof : ${data.prof}</p>
163
+ <h3>Introduction</h3>
164
+ <p class="indented">${data.introduction.replace(/\n/g, '<br>')}</p>
165
+ `;
166
+
167
+ data.parties.forEach((partie, index) => {
168
+ html += `<div class="development-block">`;
169
+ html += `<p class="indented">${partie.chapeau.replace(/\n/g, '<br>')}</p>`;
170
+ partie.arguments.forEach(arg => {
171
+ html += `<p class="indented">${arg.paragraphe_argumentatif.replace(/\n/g, '<br>')}</p>`;
172
+ });
173
+ html += `</div>`;
174
+
175
+ if (partie.transition) {
176
+ html += `<p class="indented transition">${partie.transition.replace(/\n/g, '<br>')}</p>`;
177
+ }
178
+ });
179
+
180
+ html += `
181
+ <h3>Conclusion</h3>
182
+ <p class="indented">${data.conclusion.replace(/\n/g, '<br>')}</p>
183
+ <div class="pdf-button-container">
184
+ <button class="pdf-button" id="download-pdf">Télécharger en PDF</button>
185
+ </div>
186
+ </div>
187
+ `;
188
+
189
+ resultContainer.innerHTML = html;
190
+
191
+ // Attach event listener to the newly created button
192
+ document.getElementById('download-pdf').addEventListener('click', generatePDF);
193
  }
 
 
194
 
195
+ function generatePDF() {
196
+ const element = document.getElementById('dissertation-content');
197
+ // Temporairement enlever le bouton du PDF pour qu'il n'apparaisse pas dans le fichier
198
+ const pdfButtonContainer = element.querySelector('.pdf-button-container');
199
+ pdfButtonContainer.style.display = 'none';
200
+
201
+ const options = {
202
+ margin: [0.5, 0.5, 0.5, 0.5], filename: 'dissertation-philosophie.pdf',
203
+ image: { type: 'jpeg', quality: 0.98 }, html2canvas: { scale: 2, useCORS: true },
204
+ jsPDF: { unit: 'in', format: 'a4', orientation: 'portrait' }
205
+ };
206
+
207
+ html2pdf().set(options).from(element).save().then(() => {
208
+ // Remettre le bouton visible après la génération
209
+ pdfButtonContainer.style.display = 'block';
210
+ });
211
+ }
212
 
213
+ function showError(message) {
214
+ resultContainer.innerHTML = `<p class="error">${message}</p>`;
215
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  });
217
+ </script>
 
 
 
 
218
 
219
+ </body>
220
+ </html>