Docfile commited on
Commit
9ad06ab
·
verified ·
1 Parent(s): e3ed368

Upload philosophie.html

Browse files
Files changed (1) hide show
  1. templates/philosophie.html +750 -686
templates/philosophie.html CHANGED
@@ -1,718 +1,782 @@
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
- <!-- jQuery first, then Select2 JS -->
8
- <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
9
- <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/select2.min.css" rel="stylesheet" />
10
- <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
11
- <!-- Other JS libraries -->
12
- <script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert2/11.7.3/sweetalert2.all.min.js"></script>
13
- <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.3.0/marked.min.js"></script>
14
- <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
15
- <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/locale/fr.js"></script>
16
- <!-- CSS -->
17
- <link href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert2/11.7.3/sweetalert2.min.css" rel="stylesheet">
18
- <script src="https://cdn.tailwindcss.com"></script>
19
- <style>
20
- /* Styles exactly as provided before (collapsible, prose, original animations, Select2 base styles) */
21
- .collapsible {
22
- cursor: pointer;
23
- padding: 18px;
24
- width: 100%;
25
- border: none;
26
- text-align: left;
27
- outline: none;
28
- font-size: 15px;
29
- background-color: #f1f1f1;
30
- display: flex;
31
- justify-content: space-between;
32
- align-items: center;
33
- }
34
-
35
- .prose { max-width: 100% !important; }
36
- .prose p { margin-top: 1.25em; margin-bottom: 1.25em; line-height: 1.75; }
37
- .prose ul { margin-top: 1.25em; margin-bottom: 1.25em; padding-left: 1.625em; }
38
- .prose li { margin-top: 0.5em; margin-bottom: 0.5em; padding-left: 0.375em; }
39
- .prose h1, .prose h2, .prose h3 { margin-top: 2em; margin-bottom: 1em; line-height: 1.3; }
40
-
41
- @media (max-width: 640px) {
42
- .prose { font-size: 0.95rem; }
43
- .prose p { margin-top: 1em; margin-bottom: 1em; }
44
- .prose ul { padding-left: 1.25em; }
45
- .prose li { margin-top: 0.375em; margin-bottom: 0.375em; }
46
- .prose h1, .prose h2, .prose h3 { margin-top: 1.5em; margin-bottom: 0.75em; }
47
- }
48
-
49
- #response .prose { color: #374151; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; }
50
- #response .prose > * + * { margin-top: 1em; }
51
- #response .prose blockquote { margin: 1.5em 0; padding-left: 1em; border-left: 4px solid #e5e7eb; font-style: italic; }
52
- #response .prose code { background-color: #f3f4f6; padding: 0.2em 0.4em; border-radius: 0.25em; font-size: 0.875em; }
53
-
54
- /* Original hover/active for collapsible button */
55
- .active, .collapsible:hover {
56
- background-color: #ddd;
57
- }
58
-
59
- .content {
60
- padding: 0 18px;
61
- display: none;
62
- overflow: hidden;
63
- background-color: white;
64
- }
65
-
66
- /* Original Animations */
67
- @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
68
- @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } /* Added fadeOut */
69
- @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
70
- @keyframes shake { 0%, 100% { transform: translateX(0); } 10%, 30%, 50%, 70%, 90% { transform: translateX(-3px); } 20%, 40%, 60%, 80% { transform: translateX(3px); } }
71
- .animate-fadeIn { animation: fadeIn 0.5s ease-out forwards; }
72
- .animate-fadeOut { animation: fadeOut 0.3s ease-out forwards; } /* Added fadeOut */
73
- .animate-slideUp { animation: slideUp 0.5s ease-out forwards; }
74
- .animate-shake { animation: shake 0.5s ease-in-out; }
75
- .rotate-180 { transform: rotate(180deg); } /* For chevron */
76
- .chevron { transition: transform 0.2s ease-in-out; } /* For chevron */
77
-
78
-
79
- /* Select2 Styles */
80
- .select2-container--default .select2-selection--single { border: 1px solid #e5e7eb !important; border-radius: 0.75rem !important; height: auto !important; padding: 0.625rem 1rem !important; background-color: white !important; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) !important; display: flex !important; align-items: center !important; }
81
- .select2-container--default .select2-selection--single .select2-selection__rendered { color: #374151 !important; line-height: inherit !important; padding-right: 1.5rem !important; }
82
- .select2-container--default .select2-selection--single .select2-selection__placeholder { color: #9ca3af !important; }
83
- .select2-container--default .select2-selection--single .select2-selection__arrow { top: 50% !important; transform: translateY(-50%) !important; right: 0.75rem !important; height: 1.25rem !important; width: 1.25rem !important; }
84
- .select2-container--default .select2-selection--single .select2-selection__clear { font-size: 1.5rem; font-weight: bold; color: #9ca3af; margin-right: 0.5rem; }
85
-
86
- .select2-dropdown { border: 1px solid #e5e7eb !important; border-radius: 0.75rem !important; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) !important; padding: 0.25rem !important; background-color: white !important; }
87
- .select2-search--dropdown .select2-search__field { border: 1px solid #e5e7eb !important; border-radius: 0.375rem !important; padding: 0.5rem !important; }
88
- .select2-results__options { padding: 0.25rem !important; max-height: 250px !important; }
89
- .select2-results__option { padding: 0.5rem 0.75rem !important; }
90
- .select2-container--default .select2-results__option--highlighted.select2-results__option--selectable { background-color: #ede9fe !important; color:#374151 !important; }
91
- .select2-container--default .select2-results__option--selected { background-color: #f3f4f6 !important; color:#374151 !important; }
92
- .select2-results__option .course-author { font-size: 0.875rem; color: #6b7280; display: block; margin-top: 0.25rem; }
93
- .select2-container--default .select2-results__option[aria-disabled=true] { color: #9ca3af; }
94
-
95
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  </head>
97
-
98
  <body class="bg-gradient-to-br from-violet-50 to-indigo-50 min-h-screen">
99
- <!-- Navbar -->
100
- <nav class="bg-white/80 backdrop-blur-md border-b border-gray-200 fixed w-full z-50">
101
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
102
- <div class="flex justify-between items-center h-16">
103
- <div class="flex items-center">
104
- <div class="text-2xl font-bold bg-gradient-to-r from-violet-600 to-indigo-600 text-transparent bg-clip-text">
105
- Mariam AI
106
- </div>
107
- </div>
108
- <div class="flex items-center space-x-4">
109
- <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-violet-100 text-violet-800">
110
- <span class="w-2 h-2 bg-violet-400 rounded-full animate-pulse mr-2"></span>
111
- Assistant Philosophique
112
- </span>
113
- </div>
114
- </div>
115
  </div>
116
- </nav>
117
-
118
- <!-- Main Content -->
119
- <div class="pt-24 pb-12 px-4 sm:px-6 lg:px-8">
120
- <div class="max-w-4xl mx-auto">
121
- <!-- Main Card -->
122
- <div class="bg-white/80 backdrop-blur-md rounded-2xl shadow-xl border border-gray-100 overflow-hidden">
123
- <!-- Header Section -->
124
- <div class="bg-gradient-to-r from-violet-600 to-indigo-600 p-6 text-white">
125
- <h2 class="text-2xl font-bold">Gen'Dissertation</h2>
126
- <p class="mt-2 opacity-90">Créez des dissertations philosophiques pertinentes et structurées</p>
127
- </div>
128
-
129
- <!-- Content Section -->
130
- <div class="p-8 space-y-8">
131
- <!-- Type Selection -->
132
- <div class="space-y-3">
133
- <label class="block text-sm font-medium text-gray-700">Type de dissertation</label>
134
- <div class="relative">
135
- <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">
136
- <option value="1">Type 1</option>
137
- <option value="2">Type 2</option>
138
- <option value="3">Synthèse</option>
139
- </select>
140
- <div class="absolute inset-y-0 right-0 flex items-center px-4 pointer-events-none">
141
- <svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
142
- <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" />
143
- </svg>
144
- </div>
145
- </div>
146
- <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">
147
- <!-- Label set by JS -->
148
- </div>
149
- </div>
150
-
151
- <!-- Course Selection -->
152
- <div class="space-y-3">
153
- <label for="course-select" class="block text-sm font-medium text-gray-700">Sélection du cours (Optionnel)</label>
154
- <select id="course-select" class="w-full">
155
- <!-- Placeholder Option - Select2 will use this -->
156
- <option value="">Choisir un cours...</option>
157
- </select>
158
- <div class="course-meta hidden mt-2"> <!-- Meta info appears below -->
159
- <div class="bg-gradient-to-r from-gray-50 to-white rounded-xl p-4 border border-gray-100">
160
- <div class="flex justify-between items-center text-sm">
161
- <span id="course-author" class="flex items-center space-x-2">
162
- <svg class="h-5 w-5 text-violet-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
163
- <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" />
164
- </svg>
165
- <span class="text-gray-600"></span>
166
- </span>
167
- <span id="course-date" class="flex items-center space-x-2">
168
- <svg class="h-5 w-5 text-violet-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
169
- <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" />
170
- </svg>
171
- <span class="text-gray-600"></span>
172
- </span>
173
- </div>
174
- </div>
175
- </div>
176
- </div>
177
-
178
-
179
- <!-- Question Input -->
180
- <div class="space-y-3">
181
- <label for="question" class="block text-sm font-medium text-gray-700">Sujet de dissertation</label>
182
- <div class="relative">
183
- <textarea id="question" rows="4"
184
- 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"
185
- placeholder="Saisissez votre sujet de dissertation..."></textarea>
186
- <div class="absolute bottom-3 right-3 flex items-center space-x-2 text-gray-400">
187
- <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
188
- <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" />
189
- </svg>
190
- </div>
191
- </div>
192
- </div>
193
-
194
- <!-- Submit Button -->
195
- <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">
196
- <span class="flex items-center justify-center space-x-2">
197
- <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
198
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
199
- </svg>
200
- <span>Générer la dissertation</span>
201
- </span>
202
- </button>
203
-
204
- <!-- Response Section -->
205
- <div id="response" class="hidden mt-8 prose prose-violet max-w-none">
206
- <div class="bg-gradient-to-r from-gray-50 to-white rounded-xl p-6 border border-gray-100">
207
- <!-- La réponse sera insérée ici -->
208
- </div>
209
- </div>
210
-
211
- <!-- Copy Button -->
212
- <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">
213
- <span class="flex items-center justify-center space-x-2">
214
- <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
215
- <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" />
216
- </svg>
217
- <span>Copier la dissertation</span>
218
- </span>
219
- </button>
220
-
221
- <!-- Saved Dissertations Section -->
222
- <div id="saved-dissertations" class="mt-8">
223
- <h3 class="text-lg font-medium text-gray-700 mb-4">Dissertations Sauvegardées</h3>
224
- <div id="dissertations-list" class="space-y-4">
225
- <!-- Les dissertations sauvegardées seront insérées ici -->
226
- </div>
227
- </div>
228
-
229
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  </div>
 
231
  </div>
 
232
  </div>
233
-
234
- <!-- DeepThink Floating Action Button -->
235
- <button id="deepthink-btn"
236
- title="Utiliser DeepThink (1 fois/jour)"
237
- class="fixed bottom-6 right-6 z-50 bg-gradient-to-br from-purple-600 to-blue-500 hover:from-purple-700 hover:to-blue-600 text-white p-4 rounded-full shadow-lg hover:shadow-xl transition-all duration-300 transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 disabled:hover:shadow-lg">
238
- <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
239
- <path stroke-linecap="round" stroke-linejoin="round" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z" />
240
- </svg>
241
- <span class="sr-only">DeepThink</span> <!-- Screen reader text -->
242
- </button>
243
-
244
- <script>
 
 
 
 
 
 
 
 
 
245
  $(document).ready(function() {
246
-
247
- const $courseSelect = $('#course-select'); // Cache selector
248
-
249
- // 1. Initialize Select2 for Course Selection
250
- $courseSelect.select2({
251
- placeholder: 'Choisir un cours...',
252
- allowClear: true, // Allows removing the selection
253
- // Keep the initial placeholder option from HTML
254
- templateResult: function (course) {
255
- if (!course.id) { // For placeholder
256
- return course.text;
257
- }
258
- // Get author data attached to the option element
259
- const author = $(course.element).data('author');
260
- if (!author) {
261
- return course.text; // Fallback if author is missing
262
- }
263
- return $(`
264
- <span>${course.text}</span>
265
- <span class="course-author">Pr. ${author}</span>
266
- `);
267
- },
268
- templateSelection: function (course) {
269
- // Handle placeholder or selected item text
270
- return course.text || 'Choisir un cours...';
271
- },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  });
273
-
274
- // 2. Configuration for marked (Markdown parser)
275
- marked.setOptions({
276
- breaks: true, gfm: true, headerIds: false, // headerIds: false is usually safer
277
- langPrefix: 'language-', smartLists: true, smartypants: true
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  });
279
-
280
- // 3. Configure moment.js locale
281
- moment.locale('fr');
282
-
283
- // 4. Configure SweetAlert2 Toast notifications
284
- const Toast = Swal.mixin({
285
- toast: true, position: 'top-end', showConfirmButton: false,
286
- timer: 3000, timerProgressBar: true,
287
- customClass: { popup: 'rounded-lg shadow-xl border border-gray-100' }
 
 
 
 
 
 
 
288
  });
289
 
290
- // 5. Button Animation Helper
291
- function addButtonAnimation(buttonId) {
292
- $(`#${buttonId}`).on('mousedown', function() {
293
- $(this).addClass('scale-95');
294
- }).on('mouseup mouseleave', function() {
295
- $(this).removeClass('scale-95');
296
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  }
298
- addButtonAnimation('submit-btn');
299
- addButtonAnimation('copy-btn');
300
- addButtonAnimation('deepthink-btn');
301
 
302
- // 6. Type Selection Change Handler
303
- $('#type-select').change(function() {
304
- const selectedOption = $(this).find('option:selected');
305
- const typeLabel = selectedOption.text();
306
- const type = $(this).val();
307
- let labelText = (type === '3') ? 'S - Synthèse' : `Type ${type} - ${typeLabel}`;
308
- $('#current-type-label').text(labelText);
309
- }).trigger('change'); // Trigger initially to set the label
310
-
311
- // 7. Load Courses Function (with original animation indication)
312
- function loadCourses() {
313
- console.log("Attempting to load courses..."); // Debug log
314
- return $.ajax({
315
- url: '/api/philosophy/courses',
316
- method: 'GET',
317
- dataType: 'json',
318
- beforeSend: function() {
319
- // Original animation: Disable and add pulse class to the Select2 container
320
- $courseSelect.prop('disabled', true).next('.select2-container').addClass('animate-pulse');
321
- console.log("Set loading state (pulse) for course select."); // Debug log
322
- },
323
- // Remove pulse on complete, regardless of success/fail
324
- complete: function() {
325
- $courseSelect.prop('disabled', false).next('.select2-container').removeClass('animate-pulse');
326
- console.log("Removed loading state (pulse)."); // Debug log
327
- }
328
- });
329
  }
330
-
331
- // 8. Execute Course Loading and Populate Select2 (using append and .data())
332
- loadCourses()
333
- .done(function(courses) {
334
- console.log("Courses received:", courses); // Debug log
335
- if (!Array.isArray(courses)) {
336
- console.error("Received data is not an array:", courses);
337
- Toast.fire({ icon: 'error', title: 'Erreur Format Données', text: 'Les données des cours reçues sont invalides.' });
338
- return; // Stop processing if data is invalid
339
- }
340
-
341
- // Clear existing course options (keep the placeholder)
342
- $courseSelect.find('option:not([value=""])').remove();
343
-
344
- // Add new options from fetched data
345
- courses.forEach(course => {
346
- // Create a new <option> element using the Option constructor
347
- const newOption = new Option(course.title, course.id); // (text, value)
348
-
349
- // Attach the author and update date as data attributes TO THE OPTION
350
- $(newOption).data('author', course.author);
351
- $(newOption).data('updated_at', course.updated_at); // Store update date if needed
352
-
353
- // Append the new option to the original select element
354
- $courseSelect.append(newOption);
355
- });
356
-
357
- // IMPORTANT: Trigger change for Select2 to recognize new options
358
- // Use 'change.select2' for better compatibility if needed, but 'change' usually works
359
- $courseSelect.trigger('change');
360
-
361
- console.log("Appended new options and triggered change."); // Debug log
362
-
363
- })
364
- .fail(function(jqXHR, textStatus, errorThrown) {
365
- console.error("Failed to load courses:", textStatus, errorThrown, jqXHR.responseText); // Debug log
366
- Toast.fire({
367
- icon: 'error', title: 'Erreur Chargement Cours',
368
- text: 'Impossible de récupérer la liste des cours.'
369
- });
370
- // Optionally clear or show an error message in the select itself
371
- $courseSelect.find('option:not([value=""])').remove(); // Clear any potentially added options on fail
372
- });
373
-
374
- // 9. Course Selection Change Handler (Original animation for meta info)
375
- $courseSelect.on('change', function() {
376
- const courseId = $(this).val();
377
- const $courseMeta = $('.course-meta'); // Cache the element
378
- const selectedOption = $(this).find('option:selected'); // Get the selected <option>
379
-
380
- console.log("Course selection changed. ID:", courseId); // Debug log
381
-
382
- if (courseId) {
383
- // Get data directly from the selected <option> element's data attributes
384
- const author = selectedOption.data('author');
385
- const updatedAt = selectedOption.data('updated_at'); // Get the date stored earlier
386
-
387
- if (author && updatedAt) {
388
- console.log("Displaying meta:", author, updatedAt); // Debug log
389
- // Display Meta Info using data attributes (no second AJAX needed here)
390
- $('#course-author span').text(`Pr. ${author}`);
391
- $('#course-date span').text(moment(updatedAt).format('LL')); // Format date nicely
392
-
393
- // Original animation: Remove hidden, add fadeIn
394
- $courseMeta.removeClass('hidden animate-fadeOut').addClass('animate-fadeIn');
395
-
396
- // Original subtle success Toast (uncomment if desired)
397
- /* Toast.fire({
398
- icon: 'success',
399
- title: 'Cours chargé avec succès',
400
- timer: 1500
401
- });*/
402
- } else {
403
- // Data attributes might be missing, potentially fallback to AJAX if really needed
404
- console.warn("Author or updated_at data missing from selected option.", selectedOption);
405
- // Hide meta if data is incomplete
406
- if (!$courseMeta.hasClass('hidden')) {
407
- $courseMeta.addClass('animate-fadeOut').on('animationend', function() {
408
- $(this).addClass('hidden').removeClass('animate-fadeOut animate-fadeIn');
409
- $('#course-author span').text('');
410
- $('#course-date span').text('');
411
- $(this).off('animationend');
412
- });
413
- }
414
- }
415
-
416
- } else {
417
- // Course deselected (placeholder selected)
418
- console.log("Course deselected."); // Debug log
419
- // Original animation: Fade out if visible, then hide
420
- if (!$courseMeta.hasClass('hidden')) {
421
- $courseMeta.removeClass('animate-fadeIn').addClass('animate-fadeOut').on('animationend', function() {
422
- $(this).addClass('hidden').removeClass('animate-fadeOut');
423
- $('#course-author span').text('');
424
- $('#course-date span').text('');
425
- $(this).off('animationend'); // Unbind animationend
426
- });
427
- }
428
- }
429
  });
430
-
431
-
432
- // 10. Submit Button Click Handler (Standard Generation - Original Logic)
433
- $('#submit-btn').click(function() {
434
- const question = $('#question').val().trim();
435
-
436
- if (!question) {
437
- Toast.fire({ icon: 'warning', title: 'Sujet Requis', text: 'Veuillez saisir un sujet de dissertation.' });
438
- $('#question').addClass('animate-shake border-red-500').focus();
439
- setTimeout(() => $('#question').removeClass('animate-shake border-red-500 border-gray-200'), 600); // Reset border
440
- return;
441
- }
442
-
443
- // Original Sophisticated loading animation
444
- Swal.fire({
445
- title: 'Génération en cours',
446
- html: `<div class="space-y-4"><div class="flex justify-center"><div class="w-16 h-16 relative"><div class="absolute inset-0 rounded-full border-4 border-violet-200 animate-ping"></div><div class="absolute inset-0 rounded-full border-4 border-violet-500 animate-pulse"></div></div></div><div class="text-gray-600"><p class="animate-pulse">Analyse philosophique en cours...</p><p class="text-sm mt-2 text-gray-500">Veuillez patienter quelques instants</p></div></div>`,
447
- allowOutsideClick: false, showConfirmButton: false, customClass: { popup: 'rounded-2xl' }
 
 
 
448
  });
449
-
450
- const data = { question: question, type: $('#type-select').val(), courseId: $courseSelect.val() || null };
451
-
452
- $.ajax({
453
- url: '/submit_philo', method: 'POST', contentType: 'application/json', data: JSON.stringify(data),
454
- success: function(data) {
455
- Swal.close();
456
- const htmlContent = marked.parse(data.response);
457
- $('#response > div').html(htmlContent);
458
- $('#response').removeClass('hidden').addClass('animate-fadeIn');
459
- $('#copy-btn').removeClass('hidden').addClass('animate-slideUp');
460
- saveDissertation(question, data.response); // Save locally
461
- Toast.fire({ icon: 'success', title: 'Dissertation générée et sauvegardée', timer: 2000 });
462
- },
463
- error: function(jqXHR) {
464
- Swal.close();
465
- let errorMsg = 'Une erreur est survenue lors de la génération.';
466
- if (jqXHR.responseJSON && jqXHR.responseJSON.error) { errorMsg = jqXHR.responseJSON.error; }
467
- Swal.fire({ icon: 'error', title: 'Erreur de génération', text: errorMsg, customClass: { popup: 'rounded-2xl', confirmButton: 'bg-violet-600 hover:bg-violet-700 text-white font-medium py-2 px-4 rounded-lg' } });
468
- }
469
  });
 
470
  });
471
-
472
- // --- START: DeepThink Functionality (Original Logic) ---
473
- const DEEPTHINK_COOLDOWN_MS = 24 * 60 * 60 * 1000; // 24 hours
474
- const DEEPTHINK_STORAGE_KEY = 'lastDeepThinkUsage';
475
- const $deepThinkBtn = $('#deepthink-btn');
476
-
477
- function checkDeepThinkCooldown() {
478
- const lastUsage = localStorage.getItem(DEEPTHINK_STORAGE_KEY);
479
- if (!lastUsage) {
480
- $deepThinkBtn.prop('disabled', false).attr('title', 'Utiliser DeepThink (1 fois/jour)');
481
- return true; // Available
482
- }
483
- const lastUsageTime = parseInt(lastUsage, 10);
484
- const now = Date.now();
485
- const timeElapsed = now - lastUsageTime;
486
- if (timeElapsed >= DEEPTHINK_COOLDOWN_MS) {
487
- $deepThinkBtn.prop('disabled', false).attr('title', 'Utiliser DeepThink (1 fois/jour)');
488
- return true; // Available
489
- } else {
490
- const remainingTime = DEEPTHINK_COOLDOWN_MS - timeElapsed;
491
- const hours = Math.floor(remainingTime / (3600 * 1000));
492
- const minutes = Math.floor((remainingTime % (3600 * 1000)) / (60 * 1000));
493
- $deepThinkBtn.prop('disabled', true).attr('title', `DeepThink disponible dans ${hours}h ${minutes}m`);
494
- return false; // Not available
495
- }
 
 
496
  }
497
- checkDeepThinkCooldown(); // Check on load
498
- setInterval(checkDeepThinkCooldown, 60000); // Re-check periodically
499
-
500
- // Click handler for DeepThink button
501
- $deepThinkBtn.click(function() {
502
- if (!checkDeepThinkCooldown()) {
503
- Toast.fire({ icon: 'warning', title: 'DeepThink Non Disponible', text: $deepThinkBtn.attr('title') });
504
- return;
 
 
505
  }
506
- const question = $('#question').val().trim();
507
- if (!question) {
508
- Toast.fire({ icon: 'warning', title: 'Sujet Requis', text: 'Veuillez saisir un sujet avant d\'utiliser DeepThink.' });
509
- $('#question').addClass('animate-shake border-red-500').focus();
510
- setTimeout(() => $('#question').removeClass('animate-shake border-red-500 border-gray-200'), 600);
511
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
  }
513
-
514
- // Original DeepThink loading animation
515
- Swal.fire({
516
- title: 'Génération DeepThink en cours',
517
- html: `<div class="space-y-4"><div class="flex justify-center"><div class="w-16 h-16 relative"><div class="absolute inset-0 rounded-full border-4 border-purple-200 animate-ping"></div><div class="absolute inset-0 rounded-full border-4 border-purple-500 animate-pulse"></div></div></div><div class="text-gray-600"><p class="animate-pulse">Analyse philosophique approfondie...</p><p class="text-sm mt-2 text-gray-500">Cela peut prendre un peu plus de temps</p></div></div>`,
518
- allowOutsideClick: false, showConfirmButton: false, customClass: { popup: 'rounded-2xl' }
519
- });
520
-
521
- const data = { question: question, type: $('#type-select').val(), courseId: $courseSelect.val() || null };
522
-
523
- $.ajax({
524
- url: '/submit_philo_deepthink', method: 'POST', contentType: 'application/json', data: JSON.stringify(data),
525
- success: function(data) {
526
- Swal.close();
527
- const htmlContent = marked.parse(data.response);
528
- $('#response > div').html(htmlContent);
529
- $('#response').removeClass('hidden').addClass('animate-fadeIn');
530
- $('#copy-btn').removeClass('hidden').addClass('animate-slideUp');
531
- saveDissertation(question + " (DeepThink)", data.response); // Save locally
532
- localStorage.setItem(DEEPTHINK_STORAGE_KEY, Date.now().toString()); // Record usage
533
- checkDeepThinkCooldown(); // Update button state
534
- Toast.fire({ icon: 'success', title: 'DeepThink terminé!', text: 'Dissertation générée et sauvegardée.', timer: 2500 });
535
- },
536
- error: function(jqXHR) {
537
- Swal.close();
538
- let errorMsg = 'Erreur lors de la génération DeepThink.';
539
- if (jqXHR.responseJSON && jqXHR.responseJSON.error) { errorMsg = jqXHR.responseJSON.error; }
540
- Swal.fire({ icon: 'error', title: 'Erreur DeepThink', text: errorMsg, customClass: { popup: 'rounded-2xl', confirmButton: 'bg-violet-600 hover:bg-violet-700 text-white font-medium py-2 px-4 rounded-lg' } });
541
- }
542
  });
543
- });
544
- // --- END: DeepThink Functionality ---
545
-
546
-
547
- // --- START: Saved Dissertations (Original Logic) ---
548
- function saveDissertation(title, content) {
549
- let savedDissertations = JSON.parse(localStorage.getItem('dissertations')) || [];
550
- const MAX_SAVED = 20; // Limit saved items
551
- if (savedDissertations.length >= MAX_SAVED) { savedDissertations.shift(); }
552
- savedDissertations.push({ title, content, timestamp: Date.now() });
553
- localStorage.setItem('dissertations', JSON.stringify(savedDissertations));
554
- updateSavedDissertationsList();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
555
  }
556
-
557
- function deleteDissertation(index) {
558
- // Original confirmation dialog
559
- Swal.fire({
560
- title: 'Êtes-vous sûr?', text: "Cette action est irréversible!", icon: 'warning',
561
- showCancelButton: true, confirmButtonColor: '#d33', cancelButtonColor: '#3085d6',
562
- confirmButtonText: 'Oui, supprimer!', cancelButtonText: 'Annuler',
563
- customClass: { popup: 'rounded-2xl' }
564
- }).then((result) => {
565
- if (result.isConfirmed) {
566
- let savedDissertations = JSON.parse(localStorage.getItem('dissertations')) || [];
567
- savedDissertations.splice(index, 1);
568
- localStorage.setItem('dissertations', JSON.stringify(savedDissertations));
569
- updateSavedDissertationsList(); // Refresh list UI
570
- Toast.fire({ icon: 'success', title: 'Dissertation supprimée' });
571
- }
572
- });
573
  }
574
-
575
- // Original function to display saved dissertations with collapsible sections
576
- function updateSavedDissertationsList() {
577
- const dissertationsList = $('#dissertations-list');
578
- dissertationsList.empty(); // Clear current list
579
-
580
- let savedDissertations = JSON.parse(localStorage.getItem('dissertations')) || [];
581
- savedDissertations.sort((a, b) => b.timestamp - a.timestamp); // Sort newest first
582
-
583
- if (savedDissertations.length === 0) {
584
- dissertationsList.append('<p class="text-center text-gray-500 italic py-4">Aucune dissertation sauvegardée.</p>');
585
- return;
586
- }
587
-
588
- savedDissertations.forEach((diss, index) => {
589
- const date = moment(diss.timestamp).format('LLL');
590
- const uniqueId = `diss-content-${index}`; // Unique ID for content
591
-
592
- // Original Collapsible structure with Tailwind & custom classes
593
- const $collapsibleContainer = $(`
594
- <div class="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden mb-4">
595
- <button class="collapsible bg-gray-50 hover:bg-gray-100 transition-colors duration-150 p-4 flex justify-between items-center w-full text-left focus:outline-none focus:ring-2 focus:ring-violet-300" aria-expanded="false" aria-controls="${uniqueId}">
596
- <span class="font-medium text-gray-800 flex-1 mr-4 truncate" title="${diss.title}">${diss.title}</span>
597
- <div class="flex items-center space-x-3 flex-shrink-0">
598
- <span class="text-gray-500 text-xs">${date}</span>
599
- <button class="delete-dissertation text-red-500 hover:text-red-700 p-1 rounded hover:bg-red-100 transition-colors duration-150" data-index="${index}" title="Supprimer">
600
- <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
601
- </button>
602
- <svg class="h-5 w-5 text-gray-400 transform transition-transform duration-200 chevron" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path></svg>
603
- </div>
604
- </button>
605
- <div id="${uniqueId}" class="content prose prose-violet max-w-none p-6 border-t border-gray-200" style="display: none;">
606
- <!-- Content added below -->
607
- </div>
608
- </div>`);
609
-
610
- // Add parsed markdown content to the content div
611
- $collapsibleContainer.find('.content').html(marked.parse(diss.content));
612
-
613
- dissertationsList.append($collapsibleContainer);
614
-
615
- // Event handler for the collapsible button (excluding the delete button)
616
- $collapsibleContainer.find('.collapsible').click(function(event) {
617
- if ($(event.target).closest('.delete-dissertation').length) { return; } // Ignore clicks on delete button
618
-
619
- const $contentDiv = $(this).next('.content');
620
- const $chevron = $(this).find('.chevron');
621
- const isExpanded = $(this).attr('aria-expanded') === 'true';
622
-
623
- $contentDiv.slideToggle("fast", () => { // Use slideToggle for smooth animation
624
- $(this).attr('aria-expanded', !isExpanded);
625
- $chevron.toggleClass('rotate-180', !isExpanded);
626
- // Add/remove 'active' class AFTER animation if needed for styling
627
- // $(this).toggleClass('active', !isExpanded);
628
- });
629
- });
630
-
631
- // Event handler for the delete button
632
- $collapsibleContainer.find('.delete-dissertation').click(function() {
633
- const indexToDelete = $(this).data('index');
634
- deleteDissertation(indexToDelete);
635
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
636
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
637
  }
638
- updateSavedDissertationsList(); // Initial display
639
- // --- END: Saved Dissertations ---
640
-
641
-
642
- // --- START: Copy Button (Original Logic) ---
643
- $('#copy-btn').click(function() {
644
- const responseDiv = document.querySelector('#response > div');
645
- let textToCopy = '';
646
- const temp = document.createElement('div');
647
- temp.innerHTML = responseDiv.innerHTML;
648
-
649
- function extractText(node) { // Original extraction logic
650
- let text = '';
651
- node.childNodes.forEach(child => {
652
- if (child.nodeType === 3) { text += child.textContent; }
653
- else if (child.nodeType === 1) {
654
- const isBlock = window.getComputedStyle(child).display === 'block';
655
- if (isBlock && text.length > 0 && !text.endsWith('\n\n')) { text += '\n'; }
656
- if (child.tagName === 'BR') { text += '\n'; }
657
- else if (child.tagName === 'LI') { text += '* ' + extractText(child) + '\n'; }
658
- else { text += extractText(child); }
659
- if (isBlock && !text.endsWith('\n') && child.tagName !== 'LI') { text += '\n'; }
660
- }
661
- });
662
- return text.replace(/\n{3,}/g, '\n\n').trim();
663
  }
664
- textToCopy = extractText(temp);
665
-
666
- $(this).addClass('scale-95 bg-violet-100'); // Press animation
667
-
668
- navigator.clipboard.writeText(textToCopy)
669
- .then(() => { // Success
670
- $(this).removeClass('scale-95 bg-violet-100').addClass('bg-green-50 text-green-700 border-green-200');
671
- setTimeout(() => $(this).removeClass('bg-green-50 text-green-700 border-green-200'), 1200);
672
- Toast.fire({ icon: 'success', title: 'Copié avec succès', timer: 2000 });
673
- })
674
- .catch((err) => { // Fallback
675
- console.error("Clipboard API failed: ", err);
676
- try {
677
- const textarea = document.createElement('textarea');
678
- /* Fallback textarea logic as before */
679
- document.execCommand('copy');
680
- document.body.removeChild(textarea);
681
- $(this).removeClass('scale-95 bg-violet-100').addClass('bg-green-50 text-green-700 border-green-200');
682
- setTimeout(() => $(this).removeClass('bg-green-50 text-green-700 border-green-200'), 1200);
683
- Toast.fire({ icon: 'success', title: 'Copié avec succès (Fallback)', timer: 2000 });
684
- } catch (fallbackErr) { // Fallback failed
685
- console.error("Fallback copy failed: ", fallbackErr);
686
- $(this).removeClass('scale-95 bg-violet-100').addClass('bg-red-50 text-red-700 border-red-200');
687
- setTimeout(() => $(this).removeClass('bg-red-50 text-red-700 border-red-200'), 1200);
688
- Toast.fire({ icon: 'error', title: 'Erreur de copie', text: 'Impossible de copier.', timer: 3000 });
689
- }
690
- });
691
  });
692
- // --- END: Copy Button ---
693
-
694
- // --- Animation Styles (Ensure they are present) ---
695
- const styleCheck = document.getElementById('custom-animations-style');
696
- if (!styleCheck) {
697
- const style = document.createElement('style'); style.id = 'custom-animations-style';
698
- style.textContent = `
699
- @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
700
- @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } }
701
- @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
702
- @keyframes shake { 0%, 100% { transform: translateX(0); } 10%, 30%, 50%, 70%, 90% { transform: translateX(-3px); } 20%, 40%, 60%, 80% { transform: translateX(3px); } }
703
- .animate-fadeIn { animation: fadeIn 0.5s ease-out forwards; }
704
- .animate-fadeOut { animation: fadeOut 0.3s ease-out forwards; }
705
- .animate-slideUp { animation: slideUp 0.5s ease-out forwards; }
706
- .animate-shake { animation: shake 0.5s ease-in-out; }
707
- .rotate-180 { transform: rotate(180deg); }
708
- .chevron { transition: transform 0.2s ease-in-out; }
709
- `;
710
- document.head.appendChild(style);
711
- }
712
- // --- END Animation Styles ---
713
-
714
- }); // --- FIN de $(document).ready ---
715
- </script>
716
-
717
  </body>
718
- </html>
 
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
+ /* Nouveaux styles pour le Markdown responsive */
32
+ .prose {
33
+ max-width: 100% !important;
34
+ }
35
+ .prose p {
36
+ margin-top: 1.25em;
37
+ margin-bottom: 1.25em;
38
+ line-height: 1.75;
39
+ }
40
+ .prose ul {
41
+ margin-top: 1.25em;
42
+ margin-bottom: 1.25em;
43
+ padding-left: 1.625em;
44
+ }
45
+ .prose li {
46
+ margin-top: 0.5em;
47
+ margin-bottom: 0.5em;
48
+ padding-left: 0.375em;
49
+ }
50
+ .prose h1, .prose h2, .prose h3 {
51
+ margin-top: 2em;
52
+ margin-bottom: 1em;
53
+ line-height: 1.3;
54
+ }
55
+ /* Styles spécifiques pour mobile */
56
+ @media (max-width: 640px) {
57
+ .prose {
58
+ font-size: 0.95rem;
59
+ }
60
+ .prose p {
61
+ margin-top: 1em;
62
+ margin-bottom: 1em;
63
+ }
64
+ .prose ul {
65
+ padding-left: 1.25em;
66
+ }
67
+ .prose li {
68
+ margin-top: 0.375em;
69
+ margin-bottom: 0.375em;
70
+ }
71
+ .prose h1, .prose h2, .prose h3 {
72
+ margin-top: 1.5em;
73
+ margin-bottom: 0.75em;
74
+ }
75
+ }
76
+ /* Styles pour améliorer la lisibilité du texte */
77
+ #response .prose {
78
+ color: #374151;
79
+ word-wrap: break-word;
80
+ overflow-wrap: break-word;
81
+ hyphens: auto;
82
+ }
83
+ #response .prose > * + * {
84
+ margin-top: 1em;
85
+ }
86
+ #response .prose blockquote {
87
+ margin: 1.5em 0;
88
+ padding-left: 1em;
89
+ border-left: 4px solid #e5e7eb;
90
+ font-style: italic;
91
+ }
92
+ #response .prose code {
93
+ background-color: #f3f4f6;
94
+ padding: 0.2em 0.4em;
95
+ border-radius: 0.25em;
96
+ font-size: 0.875em;
97
+ }
98
+ .active, .collapsible:hover {
99
+ background-color: #ddd;
100
+ }
101
+ .content {
102
+ padding: 0 18px;
103
+ display: none;
104
+ overflow: hidden;
105
+ background-color: white;
106
+ }
107
+ .animate-fadeIn {
108
+ animation: fadeIn 0.5s ease-out forwards;
109
+ }
110
+ .animate-slideUp {
111
+ animation: slideUp 0.5s ease-out forwards;
112
+ }
113
+ .animate-shake {
114
+ animation: shake 0.5s ease-in-out;
115
+ }
116
+ @keyframes fadeIn {
117
+ from { opacity: 0; transform: translateY(10px); }
118
+ to { opacity: 1; transform: translateY(0); }
119
+ }
120
+ @keyframes slideUp {
121
+ from { opacity: 0; transform: translateY(20px); }
122
+ to { opacity: 1; transform: translateY(0); }
123
+ }
124
+ @keyframes shake {
125
+ 0%, 100% { transform: translateX(0); }
126
+ 25% { transform: translateX(-5px); }
127
+ 75% { transform: translateX(5px); }
128
+ }
129
+ /* Styles pour Select2 */
130
+ .select2-container--default .select2-selection--single {
131
+ border: 1px solid #e5e7eb;
132
+ border-radius: 0.75rem;
133
+ height: auto;
134
+ padding: 0.625rem 1rem;
135
+ background-color: white;
136
+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
137
+ display: flex;
138
+ align-items: center;
139
+ }
140
+ .select2-container--default .select2-selection--single .select2-selection__rendered {
141
+ color: #374151;
142
+ line-height: inherit;
143
+ padding-right: 1.5rem;
144
+ }
145
+ .select2-container--default .select2-selection--single .select2-selection__arrow {
146
+ top: 50%;
147
+ transform: translateY(-50%);
148
+ right: 0.75rem;
149
+ }
150
+ .select2-dropdown {
151
+ border: 1px solid #e5e7eb;
152
+ border-radius: 0.75rem;
153
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
154
+ padding: 0.25rem;
155
+ }
156
+ .select2-search--dropdown .select2-search__field {
157
+ border: 1px solid #e5e7eb;
158
+ border-radius: 0.375rem;
159
+ padding: 0.5rem;
160
+ }
161
+ .select2-results__options {
162
+ padding: 0.25rem;
163
+ }
164
+ .select2-results__option {
165
+ padding: 0.5rem 0.75rem;
166
+ }
167
+ .select2-container--default .select2-results__option--highlighted.select2-results__option--selectable {
168
+ background-color: #ede9fe;
169
+ color:#374151;
170
+ }
171
+ .select2-container--default .select2-results__option--selected {
172
+ background-color: #f3f4f6;
173
+ color:#374151;
174
+ }
175
+ .select2-results__option .course-author {
176
+ font-size: 0.875rem;
177
+ color: #6b7280;
178
+ display: block;
179
+ margin-top: 0.25rem;
180
+ }
181
+ .select2-container--open .select2-selection--single .select2-selection__arrow {
182
+ border-color: transparent transparent #a8a29e;
183
+ }
184
+ </style>
185
  </head>
 
186
  <body class="bg-gradient-to-br from-violet-50 to-indigo-50 min-h-screen">
187
+ <!-- Navbar -->
188
+ <nav class="bg-white/80 backdrop-blur-md border-b border-gray-200 fixed w-full z-50">
189
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
190
+ <div class="flex justify-between items-center h-16">
191
+ <div class="flex items-center">
192
+ <div class="text-2xl font-bold bg-gradient-to-r from-violet-600 to-indigo-600 text-transparent bg-clip-text">
193
+ Mariam AI
194
+ </div>
 
 
 
 
 
 
 
 
195
  </div>
196
+ <div class="flex items-center space-x-4">
197
+ <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-violet-100 text-violet-800">
198
+ <span class="w-2 h-2 bg-violet-400 rounded-full animate-pulse mr-2"></span>
199
+ Assistant Philosophique
200
+ </span>
201
+ </div>
202
+ </div>
203
+ </div>
204
+ </nav>
205
+
206
+ <!-- Main Content -->
207
+ <div class="pt-24 pb-12 px-4 sm:px-6 lg:px-8">
208
+ <div class="max-w-4xl mx-auto">
209
+ <!-- Main Card -->
210
+ <div class="bg-white/80 backdrop-blur-md rounded-2xl shadow-xl border border-gray-100 overflow-hidden">
211
+ <!-- Header Section -->
212
+ <div class="bg-gradient-to-r from-violet-600 to-indigo-600 p-6 text-white">
213
+ <h2 class="text-2xl font-bold">Gen'Dissertation</h2>
214
+ <p class="mt-2 opacity-90">Créez des dissertations philosophiques pertinentes et structurées</p>
215
+ </div>
216
+ <!-- Content Section -->
217
+ <div class="p-8 space-y-8">
218
+ <!-- Type Selection -->
219
+ <div class="space-y-3">
220
+ <label class="block text-sm font-medium text-gray-700">Type de dissertation</label>
221
+ <div class="relative">
222
+ <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">
223
+ <option value="1">Type 1</option>
224
+ <option value="2">Type 2</option>
225
+ <option value="3">Synthèse</option>
226
+ </select>
227
+ <div class="absolute inset-y-0 right-0 flex items-center px-4 pointer-events-none">
228
+ <svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
229
+ <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" />
230
+ </svg>
231
+ </div>
232
+ </div>
233
+ <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">
234
+ Sujet de type 1
235
+ </div>
236
+ </div>
237
+ <!-- Course Selection -->
238
+ <div class="space-y-3">
239
+ <label class="block text-sm font-medium text-gray-700">Sélection du cours</label>
240
+ <select id="course-select" class="w-full">
241
+ <option value="">Choisir un cours...</option>
242
+ </select>
243
+ <div class="course-meta hidden">
244
+ <div class="bg-gradient-to-r from-gray-50 to-white rounded-xl p-4 border border-gray-100">
245
+ <div class="flex justify-between items-center">
246
+ <span id="course-author" class="flex items-center space-x-2">
247
+ <svg class="h-5 w-5 text-violet-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
248
+ <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" />
249
+ </svg>
250
+ <span class="text-gray-600"></span>
251
+ </span>
252
+ <span id="course-date" class="flex items-center space-x-2">
253
+ <svg class="h-5 w-5 text-violet-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
254
+ <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" />
255
+ </svg>
256
+ <span class="text-gray-600"></span>
257
+ </span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  </div>
259
+ </div>
260
+ </div>
261
+ </div>
262
+ <!-- Question Input -->
263
+ <div class="space-y-3">
264
+ <label class="block text-sm font-medium text-gray-700">Sujet de dissertation</label>
265
+ <div class="relative">
266
+ <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>
267
+ <div class="absolute bottom-3 right-3 flex items-center space-x-2 text-gray-400">
268
+ <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
269
+ <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" />
270
+ </svg>
271
+ </div>
272
+ </div>
273
+ </div>
274
+ <!-- Submit Button -->
275
+ <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">
276
+ <span class="flex items-center justify-center space-x-2">
277
+ <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
278
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
279
+ </svg>
280
+ <span>Générer la dissertation</span>
281
+ </span>
282
+ </button>
283
+ <!-- Response Section -->
284
+ <div id="response" class="hidden mt-8 prose prose-violet max-w-none">
285
+ <div class="bg-gradient-to-r from-gray-50 to-white rounded-xl p-6 border border-gray-100">
286
+ <!-- La réponse sera insérée ici -->
287
+ </div>
288
+ </div>
289
+ <!-- Copy Button -->
290
+ <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">
291
+ <span class="flex items-center justify-center space-x-2">
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="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" />
294
+ </svg>
295
+ <span>Copier la dissertation</span>
296
+ </span>
297
+ </button>
298
+ <!-- Saved Dissertations Section -->
299
+ <div id="saved-dissertations" class="mt-8">
300
+ <h3 class="text-lg font-medium text-gray-700 mb-4">Dissertations Sauvegardées</h3>
301
+ <div id="dissertations-list" class="space-y-4">
302
+ <!-- Les dissertations sauvegardées seront insérées ici -->
303
  </div>
304
+ </div>
305
  </div>
306
+ </div>
307
  </div>
308
+ </div>
309
+
310
+ <!-- Bouton flottant DeepThink -->
311
+ <button id="deepthink-btn" style="
312
+ position: fixed;
313
+ bottom: 20px;
314
+ right: 20px;
315
+ z-index: 1000;
316
+ background-color: #4f46e5;
317
+ color: white;
318
+ border: none;
319
+ border-radius: 50px;
320
+ padding: 15px 20px;
321
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
322
+ cursor: pointer;
323
+ transition: background-color 0.3s;
324
+ ">
325
+ DeepThink
326
+ </button>
327
+
328
+ <script>
329
  $(document).ready(function() {
330
+ // Initialisation de Select2
331
+ $('#course-select').select2({
332
+ placeholder: 'Choisir un cours...',
333
+ templateResult: function (course) {
334
+ if (!course.id) {
335
+ return course.text;
336
+ }
337
+ const author = $(course.element).data('author');
338
+ return $(`
339
+ <span>${course.text}</span>
340
+ <span class="course-author">Pr. ${author}</span>
341
+ `);
342
+ },
343
+ templateSelection: function (course) {
344
+ return course.text;
345
+ }
346
+ });
347
+
348
+ // Configuration de marked
349
+ marked.setOptions({
350
+ breaks: true,
351
+ gfm: true,
352
+ headerIds: true,
353
+ langPrefix: 'language-',
354
+ smartLists: true,
355
+ smartypants: true
356
+ });
357
+
358
+ moment.locale('fr');
359
+
360
+ // Configuration des notifications
361
+ const Toast = Swal.mixin({
362
+ toast: true,
363
+ position: 'top-end',
364
+ showConfirmButton: false,
365
+ timer: 3000,
366
+ timerProgressBar: true,
367
+ customClass: {
368
+ popup: 'rounded-lg shadow-xl border border-gray-100'
369
+ }
370
+ });
371
+
372
+ // Animation des boutons
373
+ function addButtonAnimation(buttonId) {
374
+ $(`#${buttonId}`).on('mousedown', function() {
375
+ $(this).addClass('scale-95');
376
+ }).on('mouseup mouseleave', function() {
377
+ $(this).removeClass('scale-95');
378
  });
379
+ }
380
+ addButtonAnimation('submit-btn');
381
+ addButtonAnimation('copy-btn');
382
+
383
+ // Gestion du changement de type avec affichage personnalisé
384
+ $('#type-select').change(function() {
385
+ const selectedOption = $(this).find('option:selected');
386
+ const typeLabel = selectedOption.text();
387
+ const type = $(this).val();
388
+ let labelText;
389
+ if(type === '3') {
390
+ labelText = 'S - Synthèse';
391
+ } else {
392
+ labelText = `Type ${type} - ${typeLabel}`;
393
+ }
394
+ $('#current-type-label').text(labelText);
395
+ });
396
+
397
+ // Chargement des cours avec animation
398
+ function loadCourses() {
399
+ return $.ajax({
400
+ url: '/api/philosophy/courses',
401
+ method: 'GET',
402
+ beforeSend: function() {
403
+ $('#course-select').prop('disabled', true).addClass('animate-pulse');
404
+ },
405
+ complete: function() {
406
+ $('#course-select').prop('disabled', false).removeClass('animate-pulse');
407
+ }
408
  });
409
+ }
410
+ loadCourses()
411
+ .done(function(courses) {
412
+ const select = $('#course-select');
413
+ courses.forEach(course => {
414
+ const newOption = new Option(course.title, course.id, false, false);
415
+ $(newOption).data('author', course.author);
416
+ select.append(newOption);
417
+ });
418
+ })
419
+ .fail(function() {
420
+ Toast.fire({
421
+ icon: 'error',
422
+ title: 'Erreur de chargement des cours',
423
+ text: 'Veuillez réessayer ultérieurement'
424
+ });
425
  });
426
 
427
+ // Gestion du changement de cours avec animations
428
+ $('#course-select').on('change', function() {
429
+ const courseId = $(this).val();
430
+ if (courseId) {
431
+ $.ajax({
432
+ url: `/api/philosophy/courses/${courseId}`,
433
+ method: 'GET',
434
+ beforeSend: function() {
435
+ $('.course-meta').addClass('animate-pulse');
436
+ },
437
+ success: function(course) {
438
+ $('.course-meta').removeClass('hidden animate-pulse').addClass('animate-fadeIn');
439
+ $('#course-author span').text(`Pr. ${course.author}`);
440
+ $('#course-date span').text(new Date(course.updated_at).toLocaleDateString('fr-FR', {
441
+ day: 'numeric',
442
+ month: 'long',
443
+ year: 'numeric'
444
+ }));
445
+ Toast.fire({
446
+ icon: 'success',
447
+ title: 'Cours chargé avec succès'
448
+ });
449
+ },
450
+ error: function() {
451
+ Toast.fire({
452
+ icon: 'error',
453
+ title: 'Erreur',
454
+ text: 'Impossible de charger les détails du cours'
455
+ });
456
+ }
457
+ });
458
+ } else {
459
+ $('.course-meta').addClass('animate-fadeOut').on('animationend', function() {
460
+ $(this).addClass('hidden').removeClass('animate-fadeOut');
461
+ });
462
  }
463
+ });
 
 
464
 
465
+ // Gestion de la soumission
466
+ $('#submit-btn').click(function() {
467
+ const question = $('#question').val().trim();
468
+ if (!question) {
469
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
470
  }
471
+ Swal.fire({
472
+ title: 'Génération en cours',
473
+ html: `
474
+ <div class="space-y-4">
475
+ <div class="flex justify-center">
476
+ <div class="w-16 h-16 relative">
477
+ <div class="absolute inset-0 rounded-full border-4 border-violet-200 animate-ping"></div>
478
+ <div class="absolute inset-0 rounded-full border-4 border-violet-500 animate-pulse"></div>
479
+ </div>
480
+ </div>
481
+ <div class="text-gray-600">
482
+ <p class="animate-pulse">Analyse philosophique en cours...</p>
483
+ <p class="text-sm mt-2 text-gray-500">Veuillez patienter quelques instants</p>
484
+ </div>
485
+ </div>
486
+ `,
487
+ allowOutsideClick: false,
488
+ showConfirmButton: false,
489
+ customClass: {
490
+ popup: 'rounded-2xl'
491
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492
  });
493
+ const data = {
494
+ question: question,
495
+ type: $('#type-select').val(),
496
+ courseId: $('#course-select').val() || null
497
+ };
498
+ $.ajax({
499
+ url: '/submit_philo',
500
+ method: 'POST',
501
+ contentType: 'application/json',
502
+ data: JSON.stringify(data),
503
+ success: function(data) {
504
+ Swal.close();
505
+ const htmlContent = marked.parse(data.response);
506
+ $('#response > div').html(htmlContent);
507
+ $('#response').removeClass('hidden').addClass('animate-fadeIn');
508
+ $('#copy-btn').removeClass('hidden').addClass('animate-slideUp');
509
+ saveDissertation(question, data.response);
510
+ Toast.fire({
511
+ icon: 'success',
512
+ title: 'Dissertation générée et sauvegardée avec succès',
513
+ timer: 2000
514
  });
515
+ },
516
+ error: function() {
517
+ Swal.fire({
518
+ icon: 'error',
519
+ title: 'Erreur de génération',
520
+ text: 'Une erreur est survenue lors de la génération de votre dissertation.',
521
+ customClass: {
522
+ popup: 'rounded-2xl',
523
+ confirmButton: 'bg-violet-600 hover:bg-violet-700 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200'
524
+ }
 
 
 
 
 
 
 
 
 
 
525
  });
526
+ }
527
  });
528
+ });
529
+
530
+ function saveDissertation(title, content) {
531
+ let savedDissertations = JSON.parse(localStorage.getItem('dissertations')) || [];
532
+ savedDissertations.push({ title, content, timestamp: Date.now() });
533
+ localStorage.setItem('dissertations', JSON.stringify(savedDissertations));
534
+ updateSavedDissertationsList();
535
+ }
536
+
537
+ function deleteDissertation(index) {
538
+ let savedDissertations = JSON.parse(localStorage.getItem('dissertations')) || [];
539
+ savedDissertations.splice(index, 1);
540
+ localStorage.setItem('dissertations', JSON.stringify(savedDissertations));
541
+ updateSavedDissertationsList();
542
+ Toast.fire({
543
+ icon: 'success',
544
+ title: 'Dissertation supprimée avec succès'
545
+ });
546
+ }
547
+
548
+ function updateSavedDissertationsList() {
549
+ const dissertationsList = $('#dissertations-list');
550
+ dissertationsList.empty();
551
+ let savedDissertations = JSON.parse(localStorage.getItem('dissertations')) || [];
552
+ if (savedDissertations.length === 0) {
553
+ dissertationsList.append('<p class="text-gray-500">Aucune dissertation sauvegardée.</p>');
554
+ return;
555
  }
556
+ savedDissertations.forEach((diss, index) => {
557
+ const date = moment(diss.timestamp).format('LLL');
558
+ 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>`);
559
+ const deleteButton = $('<button class="text-red-500 hover:text-red-700 ml-2">Supprimer</button>');
560
+ const content = $('<div class="content prose prose-violet max-w-none p-4"></div>').html(marked.parse(diss.content));
561
+ collapsible.append(deleteButton);
562
+ dissertationsList.append(collapsible, content);
563
+ collapsible.click(function(event) {
564
+ if (event.target === deleteButton[0]) {
565
+ return;
566
  }
567
+ content.slideToggle("fast");
568
+ collapsible.toggleClass("active");
569
+ });
570
+ deleteButton.click(function() {
571
+ deleteDissertation(index);
572
+ });
573
+ });
574
+ }
575
+ updateSavedDissertationsList();
576
+
577
+ $('#copy-btn').click(function() {
578
+ const responseDiv = document.querySelector('#response > div');
579
+ let textToCopy = '';
580
+ const temp = document.createElement('div');
581
+ temp.innerHTML = responseDiv.innerHTML;
582
+ function extractText(node) {
583
+ let text = '';
584
+ node.childNodes.forEach(child => {
585
+ if (child.nodeType === 3) {
586
+ text += child.textContent;
587
+ } else if (child.nodeType === 1) {
588
+ if (window.getComputedStyle(child).display === 'block') {
589
+ text += '\n';
590
+ }
591
+ text += extractText(child);
592
+ if (window.getComputedStyle(child).display === 'block') {
593
+ text += '\n';
594
+ }
595
  }
596
+ });
597
+ return text;
598
+ }
599
+ textToCopy = extractText(temp).trim();
600
+ $(this).addClass('scale-95 bg-violet-100');
601
+ navigator.clipboard.writeText(textToCopy)
602
+ .then(() => {
603
+ $(this).removeClass('scale-95 bg-violet-100').addClass('bg-green-50 text-green-700');
604
+ setTimeout(() => {
605
+ $(this).removeClass('bg-green-50 text-green-700');
606
+ }, 1000);
607
+ Toast.fire({
608
+ icon: 'success',
609
+ title: 'Copié avec succès',
610
+ text: 'Le contenu a été copié dans votre presse-papiers',
611
+ timer: 2000
 
 
 
 
 
 
 
 
 
 
 
 
 
612
  });
613
+ })
614
+ .catch((err) => {
615
+ try {
616
+ const textarea = document.createElement('textarea');
617
+ textarea.value = textToCopy;
618
+ textarea.style.position = 'fixed';
619
+ textarea.style.opacity = '0';
620
+ document.body.appendChild(textarea);
621
+ textarea.select();
622
+ document.execCommand('copy');
623
+ document.body.removeChild(textarea);
624
+ $(this).removeClass('scale-95 bg-violet-100').addClass('bg-green-50 text-green-700');
625
+ setTimeout(() => {
626
+ $(this).removeClass('bg-green-50 text-green-700');
627
+ }, 1000);
628
+ Toast.fire({
629
+ icon: 'success',
630
+ title: 'Copié avec succès',
631
+ timer: 2000
632
+ });
633
+ } catch (fallbackErr) {
634
+ $(this).removeClass('scale-95 bg-violet-100').addClass('bg-red-50 text-red-700');
635
+ setTimeout(() => {
636
+ $(this).removeClass('bg-red-50 text-red-700');
637
+ }, 1000);
638
+ Toast.fire({
639
+ icon: 'error',
640
+ title: 'Erreur de copie',
641
+ text: 'Impossible de copier le contenu',
642
+ timer: 3000
643
+ });
644
+ }
645
+ });
646
+ });
647
+
648
+ // Ajout des styles d'animation personnalisés
649
+ const style = document.createElement('style');
650
+ style.textContent = `
651
+ @keyframes fadeIn {
652
+ from { opacity: 0; transform: translateY(10px); }
653
+ to { opacity: 1; transform: translateY(0); }
654
  }
655
+ @keyframes slideUp {
656
+ from { opacity: 0; transform: translateY(20px); }
657
+ to { opacity: 1; transform: translateY(0); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
658
  }
659
+ @keyframes shake {
660
+ 0%, 100% { transform: translateX(0); }
661
+ 25% { transform: translateX(-5px); }
662
+ 75% { transform: translateX(5px); }
663
+ }
664
+ .animate-fadeIn {
665
+ animation: fadeIn 0.5s ease-out forwards;
666
+ }
667
+ .animate-slideUp {
668
+ animation: slideUp 0.5s ease-out forwards;
669
+ }
670
+ .animate-shake {
671
+ animation: shake 0.5s ease-in-out;
672
+ }
673
+ `;
674
+ document.head.appendChild(style);
675
+ const initialSelectValue = $('#type-select').val();
676
+ const initialSelectedText = $('#type-select option:selected').text();
677
+ let initialLabelText;
678
+ if(initialSelectValue === '3') {
679
+ initialLabelText = 'S - Synthèse';
680
+ } else {
681
+ initialLabelText = `Type ${initialSelectValue} - ${initialSelectedText}`;
682
+ }
683
+ $('#current-type-label').text(initialLabelText);
684
+
685
+ /* --- Code pour le bouton DeepThink --- */
686
+ // Vérifie si DeepThink a déjà été utilisé aujourd'hui
687
+ function canUseDeepThink() {
688
+ const lastUsed = localStorage.getItem('deepthink_last_used');
689
+ if (!lastUsed) return true;
690
+ const lastDate = new Date(lastUsed);
691
+ const now = new Date();
692
+ return (now - lastDate) > (24 * 60 * 60 * 1000);
693
+ }
694
+
695
+ // Met à jour la date d'utilisation de DeepThink dans localStorage
696
+ function updateDeepThinkUsage() {
697
+ localStorage.setItem('deepthink_last_used', new Date().toISOString());
698
+ }
699
+
700
+ // Fonction d'appel à la route DeepThink
701
+ function callDeepThink(question, type, courseId) {
702
+ const data = {
703
+ question: question,
704
+ type: type,
705
+ courseId: courseId
706
+ };
707
+ $.ajax({
708
+ url: '/submit_philo_deepthink',
709
+ method: 'POST',
710
+ contentType: 'application/json',
711
+ data: JSON.stringify(data),
712
+ beforeSend: function() {
713
+ Swal.fire({
714
+ title: 'Génération DeepThink en cours',
715
+ html: 'Veuillez patienter...',
716
+ allowOutsideClick: false,
717
+ didOpen: () => {
718
+ Swal.showLoading();
719
+ }
720
+ });
721
+ },
722
+ success: function(response) {
723
+ Swal.close();
724
+ Swal.fire({
725
+ title: 'DeepThink',
726
+ html: `<div style="text-align: left; max-height: 300px; overflow-y: auto;">${response.response}</div>`,
727
+ width: '600px'
728
+ });
729
+ updateDeepThinkUsage();
730
+ },
731
+ error: function() {
732
+ Swal.close();
733
+ Swal.fire({
734
+ icon: 'error',
735
+ title: 'Erreur',
736
+ text: 'Une erreur est survenue lors de l\'appel de DeepThink.'
737
  });
738
+ }
739
+ });
740
+ }
741
+
742
+ // Action du clic sur le bouton DeepThink
743
+ $('#deepthink-btn').click(function(){
744
+ if (!canUseDeepThink()) {
745
+ Swal.fire({
746
+ icon: 'warning',
747
+ title: 'Limite atteinte',
748
+ text: 'Vous ne pouvez utiliser DeepThink qu\'une seule fois par jour.'
749
+ });
750
+ return;
751
  }
752
+ Swal.fire({
753
+ title: 'DeepThink',
754
+ input: 'textarea',
755
+ inputLabel: 'Saisissez votre sujet pour DeepThink',
756
+ inputPlaceholder: 'Entrez ici votre sujet...',
757
+ inputAttributes: {
758
+ 'aria-label': 'Saisissez votre sujet pour DeepThink'
759
+ },
760
+ showCancelButton: true,
761
+ confirmButtonText: 'Valider',
762
+ cancelButtonText: 'Annuler',
763
+ preConfirm: (question) => {
764
+ if (!question || question.trim() === '') {
765
+ Swal.showValidationMessage('Veuillez saisir un sujet');
 
 
 
 
 
 
 
 
 
 
 
766
  }
767
+ return question;
768
+ }
769
+ }).then((result) => {
770
+ if (result.isConfirmed) {
771
+ const question = result.value;
772
+ const type = '1';
773
+ const courseId = $('#course-select').val() || null;
774
+ callDeepThink(question, type, courseId);
775
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
776
  });
777
+ });
778
+ /* --- Fin du code DeepThink --- */
779
+ });
780
+ </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
781
  </body>
782
+ </html>