Docfile commited on
Commit
43b55c4
·
verified ·
1 Parent(s): e1b7241

Update templates/philosophie.html

Browse files
Files changed (1) hide show
  1. templates/philosophie.html +246 -850
templates/philosophie.html CHANGED
@@ -1,863 +1,259 @@
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>Générateur de Dissertations Philosophiques - Mariam AI</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com">
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Kalam:wght@400;700&display=swap" rel="stylesheet">
10
- <style>
11
- * {
12
- margin: 0;
13
- padding: 0;
14
- box-sizing: border-box;
15
- }
16
-
17
- :root {
18
- --primary: #667eea;
19
- --primary-dark: #5a6fd8;
20
- --secondary: #764ba2;
21
- --accent: #f093fb;
22
- --background: #0f0f23;
23
- --surface: #1a1a2e;
24
- --surface-light: #16213e;
25
- --text-primary: #ffffff;
26
- --text-secondary: #a0a9c1;
27
- --success: #10b981;
28
- --error: #ef4444;
29
- --warning: #f59e0b;
30
- }
31
-
32
- body {
33
- font-family: 'Inter', sans-serif;
34
- background: var(--background);
35
- color: var(--text-primary);
36
- line-height: 1.6;
37
- overflow-x: hidden;
38
- }
39
-
40
- .background-animation {
41
- position: fixed;
42
- top: 0;
43
- left: 0;
44
- width: 100%;
45
- height: 100%;
46
- z-index: -1;
47
- background: linear-gradient(-45deg, #667eea, #764ba2, #667eea, #764ba2);
48
- background-size: 400% 400%;
49
- animation: gradientShift 15s ease infinite;
50
- opacity: 0.1;
51
- }
52
-
53
- @keyframes gradientShift {
54
- 0% { background-position: 0% 50%; }
55
- 50% { background-position: 100% 50%; }
56
- 100% { background-position: 0% 50%; }
57
- }
58
-
59
- .container {
60
- max-width: 1200px;
61
- margin: 0 auto;
62
- padding: 0 20px;
63
- }
64
-
65
- .header {
66
- text-align: center;
67
- padding: 60px 0 40px;
68
- position: relative;
69
- }
70
-
71
- .header::before {
72
- content: '';
73
- position: absolute;
74
- top: 0;
75
- left: 50%;
76
- transform: translateX(-50%);
77
- width: 100px;
78
- height: 4px;
79
- background: linear-gradient(90deg, var(--primary), var(--accent));
80
- border-radius: 2px;
81
- }
82
-
83
- .header h1 {
84
- font-family: 'Kalam', cursive;
85
- font-size: 3rem;
86
- font-weight: 700;
87
- background: linear-gradient(135deg, var(--primary), var(--accent));
88
- -webkit-background-clip: text;
89
- background-clip: text;
90
- -webkit-text-fill-color: transparent;
91
- margin-bottom: 16px;
92
- animation: fadeInUp 1s ease;
93
- }
94
-
95
- .header p {
96
- font-size: 1.2rem;
97
- color: var(--text-secondary);
98
- animation: fadeInUp 1s ease 0.2s both;
99
- }
100
-
101
- @keyframes fadeInUp {
102
- from {
103
- opacity: 0;
104
- transform: translateY(30px);
105
- }
106
- to {
107
- opacity: 1;
108
- transform: translateY(0);
109
- }
110
- }
111
-
112
- .main-content {
113
- display: grid;
114
- grid-template-columns: 1fr;
115
- gap: 40px;
116
- margin-bottom: 60px;
117
- }
118
-
119
- @media (min-width: 1024px) {
120
- .main-content {
121
- grid-template-columns: 400px 1fr;
122
- gap: 60px;
123
- }
124
- }
125
-
126
- .form-section {
127
- background: var(--surface);
128
- border-radius: 20px;
129
- padding: 30px;
130
- border: 1px solid rgba(102, 126, 234, 0.2);
131
- backdrop-filter: blur(10px);
132
- animation: fadeInLeft 1s ease 0.4s both;
133
- }
134
-
135
- @keyframes fadeInLeft {
136
- from {
137
- opacity: 0;
138
- transform: translateX(-30px);
139
- }
140
- to {
141
- opacity: 1;
142
- transform: translateX(0);
143
- }
144
- }
145
-
146
- .form-group {
147
- margin-bottom: 24px;
148
- }
149
-
150
- .form-group label {
151
- display: block;
152
- margin-bottom: 8px;
153
- font-weight: 600;
154
- color: var(--text-primary);
155
- font-size: 0.95rem;
156
- }
157
-
158
- .form-input, .form-select, .form-textarea {
159
- width: 100%;
160
- padding: 12px 16px;
161
- background: var(--surface-light);
162
- border: 2px solid rgba(102, 126, 234, 0.3);
163
- border-radius: 12px;
164
- color: var(--text-primary);
165
- font-size: 16px;
166
- transition: all 0.3s ease;
167
- resize: vertical;
168
- }
169
-
170
- .form-input:focus, .form-select:focus, .form-textarea:focus {
171
- outline: none;
172
- border-color: var(--primary);
173
- box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
174
- transform: translateY(-2px);
175
- }
176
-
177
- .form-textarea {
178
- min-height: 120px;
179
- }
180
-
181
- .radio-group {
182
- display: grid;
183
- gap: 12px;
184
- }
185
-
186
- .radio-item {
187
- position: relative;
188
- cursor: pointer;
189
- }
190
-
191
- .radio-item input[type="radio"] {
192
- display: none;
193
- }
194
-
195
- .radio-label {
196
- display: block;
197
- padding: 16px;
198
- background: var(--surface-light);
199
- border: 2px solid rgba(102, 126, 234, 0.3);
200
- border-radius: 12px;
201
- transition: all 0.3s ease;
202
- position: relative;
203
- padding-left: 48px;
204
- }
205
-
206
- .radio-label::before {
207
- content: '';
208
- position: absolute;
209
- left: 16px;
210
- top: 50%;
211
- transform: translateY(-50%);
212
- width: 20px;
213
- height: 20px;
214
- border: 2px solid rgba(102, 126, 234, 0.5);
215
- border-radius: 50%;
216
- transition: all 0.3s ease;
217
- }
218
-
219
- .radio-item input[type="radio"]:checked + .radio-label {
220
- border-color: var(--primary);
221
- background: rgba(102, 126, 234, 0.1);
222
- }
223
-
224
- .radio-item input[type="radio"]:checked + .radio-label::before {
225
- border-color: var(--primary);
226
- background: var(--primary);
227
- box-shadow: inset 0 0 0 3px var(--surface-light);
228
- }
229
-
230
- .radio-label h4 {
231
- color: var(--text-primary);
232
- margin-bottom: 4px;
233
- font-weight: 600;
234
- }
235
-
236
- .radio-label p {
237
- color: var(--text-secondary);
238
- font-size: 0.9rem;
239
- }
240
-
241
- .generate-btn {
242
- width: 100%;
243
- padding: 16px;
244
- background: linear-gradient(135deg, var(--primary), var(--secondary));
245
- border: none;
246
- border-radius: 12px;
247
- color: white;
248
- font-size: 1.1rem;
249
- font-weight: 600;
250
- cursor: pointer;
251
- transition: all 0.3s ease;
252
- position: relative;
253
- overflow: hidden;
254
- }
255
-
256
- .generate-btn:hover {
257
- transform: translateY(-2px);
258
- box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
259
- }
260
-
261
- .generate-btn:active {
262
- transform: translateY(0);
263
- }
264
-
265
- .generate-btn:disabled {
266
- opacity: 0.7;
267
- cursor: not-allowed;
268
- transform: none;
269
- }
270
-
271
- .btn-loading {
272
- animation: pulse 2s ease-in-out infinite;
273
- }
274
-
275
- @keyframes pulse {
276
- 0% { opacity: 1; }
277
- 50% { opacity: 0.7; }
278
- 100% { opacity: 1; }
279
- }
280
-
281
- .result-section {
282
- background: var(--surface);
283
- border-radius: 20px;
284
- padding: 30px;
285
- border: 1px solid rgba(102, 126, 234, 0.2);
286
- backdrop-filter: blur(10px);
287
- animation: fadeInRight 1s ease 0.6s both;
288
- min-height: 400px;
289
- display: flex;
290
- flex-direction: column;
291
- }
292
-
293
- @keyframes fadeInRight {
294
- from {
295
- opacity: 0;
296
- transform: translateX(30px);
297
- }
298
- to {
299
- opacity: 1;
300
- transform: translateX(0);
301
- }
302
- }
303
-
304
- .result-header {
305
- display: flex;
306
- justify-content: space-between;
307
- align-items: center;
308
- margin-bottom: 24px;
309
- padding-bottom: 16px;
310
- border-bottom: 1px solid rgba(102, 126, 234, 0.2);
311
- }
312
-
313
- .result-title {
314
- font-size: 1.5rem;
315
- font-weight: 700;
316
- color: var(--text-primary);
317
- }
318
-
319
- .download-btn {
320
- padding: 10px 20px;
321
- background: linear-gradient(135deg, var(--success), #059669);
322
- border: none;
323
- border-radius: 8px;
324
- color: white;
325
- font-weight: 600;
326
- cursor: pointer;
327
- transition: all 0.3s ease;
328
- font-size: 0.9rem;
329
- }
330
-
331
- .download-btn:hover {
332
- transform: translateY(-2px);
333
- box-shadow: 0 8px 20px rgba(16, 185, 129, 0.3);
334
- }
335
-
336
- .loading-state {
337
- display: flex;
338
- flex-direction: column;
339
- align-items: center;
340
- justify-content: center;
341
- flex-grow: 1;
342
- text-align: center;
343
- }
344
-
345
- .spinner {
346
- width: 60px;
347
- height: 60px;
348
- border: 4px solid rgba(102, 126, 234, 0.3);
349
- border-top: 4px solid var(--primary);
350
- border-radius: 50%;
351
- animation: spin 1s linear infinite;
352
- margin-bottom: 20px;
353
- }
354
-
355
- @keyframes spin {
356
- 0% { transform: rotate(0deg); }
357
- 100% { transform: rotate(360deg); }
358
- }
359
-
360
- .loading-text {
361
- font-size: 1.1rem;
362
- color: var(--text-secondary);
363
- margin-bottom: 8px;
364
- }
365
-
366
- .loading-subtext {
367
- font-size: 0.9rem;
368
- color: var(--text-secondary);
369
- opacity: 0.7;
370
- }
371
-
372
- .empty-state {
373
- display: flex;
374
- flex-direction: column;
375
- align-items: center;
376
- justify-content: center;
377
- flex-grow: 1;
378
- text-align: center;
379
- color: var(--text-secondary);
380
- }
381
-
382
- .empty-icon {
383
- width: 80px;
384
- height: 80px;
385
- margin-bottom: 20px;
386
- opacity: 0.5;
387
- }
388
-
389
- .dissertation-content {
390
- flex-grow: 1;
391
- }
392
-
393
- .dissertation-paper {
394
- background: #fdfaf4;
395
- color: #1a2a4c;
396
- padding: 40px;
397
- border-radius: 12px;
398
- font-family: 'Kalam', cursive;
399
- font-size: 18px;
400
- line-height: 1.8;
401
- background-image: linear-gradient(transparent 97%, #d8e2ee 98%);
402
- background-size: 100% 32px;
403
- border-left: 4px solid #ffaaab;
404
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
405
- }
406
-
407
- .dissertation-title {
408
- text-align: center;
409
- font-size: 1.4rem;
410
- font-weight: 700;
411
- margin-bottom: 10px;
412
- color: #1a2a4c;
413
- }
414
-
415
- .dissertation-prof {
416
- text-align: center;
417
- font-style: italic;
418
- margin-bottom: 30px;
419
- color: #4a6a9c;
420
- }
421
-
422
- .dissertation-section {
423
- margin-bottom: 30px;
424
- }
425
-
426
- .section-title {
427
- font-size: 1.2rem;
428
- font-weight: 700;
429
- text-transform: uppercase;
430
- text-decoration: underline;
431
- margin-bottom: 16px;
432
- color: #1a2a4c;
433
- }
434
-
435
- .section-content {
436
- text-align: justify;
437
- }
438
-
439
- .paragraph {
440
- margin-bottom: 16px;
441
- text-indent: 2em;
442
- }
443
-
444
- .partie-block {
445
- margin-bottom: 24px;
446
- }
447
-
448
- .chapeau {
449
- font-weight: 600;
450
- margin-bottom: 12px;
451
- }
452
-
453
- .transition {
454
- font-style: italic;
455
- color: #4a6a9c;
456
- margin-top: 16px;
457
- }
458
-
459
- .error-state {
460
- background: rgba(239, 68, 68, 0.1);
461
- border: 1px solid rgba(239, 68, 68, 0.3);
462
- border-radius: 12px;
463
- padding: 20px;
464
- text-align: center;
465
- color: var(--error);
466
- }
467
-
468
- .error-icon {
469
- width: 48px;
470
- height: 48px;
471
- margin: 0 auto 16px;
472
- }
473
-
474
- .footer {
475
- text-align: center;
476
- padding: 40px 0;
477
- color: var(--text-secondary);
478
- font-size: 0.9rem;
479
- }
480
-
481
- @media (max-width: 768px) {
482
- .container {
483
- padding: 0 16px;
484
- }
485
-
486
- .header h1 {
487
- font-size: 2.2rem;
488
- }
489
-
490
- .form-section, .result-section {
491
- padding: 20px;
492
- }
493
-
494
- .main-content {
495
- gap: 24px;
496
- }
497
- }
498
-
499
- .notification {
500
- position: fixed;
501
- top: 20px;
502
- right: 20px;
503
- padding: 16px 20px;
504
- border-radius: 12px;
505
- color: white;
506
- font-weight: 600;
507
- z-index: 1000;
508
- transform: translateX(400px);
509
- opacity: 0;
510
- transition: all 0.4s ease;
511
- }
512
-
513
- .notification.show {
514
- transform: translateX(0);
515
- opacity: 1;
516
- }
517
-
518
- .notification.success {
519
- background: linear-gradient(135deg, var(--success), #059669);
520
- }
521
-
522
- .notification.error {
523
- background: linear-gradient(135deg, var(--error), #dc2626);
524
- }
525
- </style>
526
  </head>
527
  <body>
528
- <div class="background-animation"></div>
529
-
530
- <div class="container">
531
- <header class="header">
532
- <h1>Générateur de Dissertations</h1>
533
- <p>Créez des dissertations philosophiques avec l'intelligence artificielle Mariam AI</p>
534
- </header>
535
-
536
- <main class="main-content">
537
- <section class="form-section">
538
- <form id="dissertationForm">
539
- <div class="form-group">
540
- <label for="courseSelect">Cours de référence (optionnel)</label>
541
- <select id="courseSelect" class="form-select">
542
- <option value="">Sélectionner un cours...</option>
543
- </select>
544
- </div>
545
-
546
- <div class="form-group">
547
- <label for="questionInput">Sujet de dissertation *</label>
548
- <textarea
549
- id="questionInput"
550
- class="form-textarea"
551
- placeholder="Exemple: L'art nous détourne-t-il de la réalité ?"
552
- required
553
- ></textarea>
554
- </div>
555
-
556
- <div class="form-group">
557
- <label>Type de méthodologie</label>
558
- <div class="radio-group">
559
- <div class="radio-item">
560
- <input type="radio" id="type1" name="methodType" value="type1" checked>
561
- <label for="type1" class="radio-label">
562
- <h4>Méthodologie Type 1</h4>
563
- <p>Approche classique avec plan dialectique</p>
564
- </label>
565
- </div>
566
- <div class="radio-item">
567
- <input type="radio" id="type2" name="methodType" value="type2">
568
- <label for="type2" class="radio-label">
569
- <h4>Méthodologie Type 2</h4>
570
- <p>Approche thématique avec développement progressif</p>
571
- </label>
572
- </div>
573
- </div>
574
- </div>
575
-
576
- <button type="submit" class="generate-btn" id="generateBtn">
577
- <span id="btnText">Générer la dissertation</span>
578
- </button>
579
- </form>
580
- </section>
581
-
582
- <section class="result-section">
583
- <div class="result-header">
584
- <h2 class="result-title">Résultat</h2>
585
- <button class="download-btn" id="downloadBtn" style="display: none;">
586
- 📄 Télécharger PDF
587
- </button>
588
- </div>
589
-
590
- <div id="resultContent">
591
- <div class="empty-state">
592
- <svg class="empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
593
- <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
594
- <polyline points="14,2 14,8 20,8"></polyline>
595
- <line x1="16" y1="13" x2="8" y2="13"></line>
596
- <line x1="16" y1="17" x2="8" y2="17"></line>
597
- <polyline points="10,9 9,9 8,9"></polyline>
598
- </svg>
599
- <p>Votre dissertation apparaîtra ici</p>
600
- </div>
601
- </div>
602
- </section>
603
- </main>
604
-
605
- <footer class="footer">
606
- <p>&copy; 2024 Mariam AI - Générateur de Dissertations Philosophiques</p>
607
- </footer>
608
  </div>
609
 
610
- <div id="notification" class="notification"></div>
611
-
612
- <script>
613
- class DissertationGenerator {
614
- constructor() {
615
- this.currentDissertation = null;
616
- this.init();
617
- }
618
-
619
- init() {
620
- this.loadCourses();
621
- this.bindEvents();
622
- }
623
-
624
- async loadCourses() {
625
- try {
626
- const response = await fetch('/api/philosophy/courses');
627
- const courses = await response.json();
628
-
629
- const select = document.getElementById('courseSelect');
630
- select.innerHTML = '<option value="">Sélectionner un cours...</option>';
631
-
632
- courses.forEach(course => {
633
- const option = document.createElement('option');
634
- option.value = course.id;
635
- option.textContent = course.title;
636
- select.appendChild(option);
637
- });
638
- } catch (error) {
639
- console.error('Erreur lors du chargement des cours:', error);
640
- }
641
- }
642
-
643
- bindEvents() {
644
- const form = document.getElementById('dissertationForm');
645
- const downloadBtn = document.getElementById('downloadBtn');
646
-
647
- form.addEventListener('submit', (e) => {
648
- e.preventDefault();
649
- this.generateDissertation();
650
- });
651
-
652
- downloadBtn.addEventListener('click', () => {
653
- this.downloadPDF();
654
- });
655
- }
656
-
657
- async generateDissertation() {
658
- const form = document.getElementById('dissertationForm');
659
- const formData = new FormData(form);
660
-
661
- const data = {
662
- question: document.getElementById('questionInput').value.trim(),
663
- type: formData.get('methodType'),
664
- courseId: document.getElementById('courseSelect').value || null
665
- };
666
-
667
- if (!data.question) {
668
- this.showNotification('Veuillez saisir un sujet de dissertation', 'error');
669
- return;
670
- }
671
-
672
- this.showLoading();
673
-
674
- try {
675
- const response = await fetch('/api/generate_dissertation', {
676
- method: 'POST',
677
- headers: {
678
- 'Content-Type': 'application/json',
679
- },
680
- body: JSON.stringify(data)
681
- });
682
-
683
- const result = await response.json();
684
-
685
- if (response.ok) {
686
- this.currentDissertation = result;
687
- this.displayDissertation(result);
688
- this.showNotification('Dissertation générée avec succès !', 'success');
689
- } else {
690
- throw new Error(result.error || 'Erreur lors de la génération');
691
- }
692
- } catch (error) {
693
- console.error('Erreur:', error);
694
- this.showError(error.message);
695
- this.showNotification('Erreur lors de la génération', 'error');
696
- }
697
- }
698
-
699
- showLoading() {
700
- const resultContent = document.getElementById('resultContent');
701
- const downloadBtn = document.getElementById('downloadBtn');
702
- const generateBtn = document.getElementById('generateBtn');
703
- const btnText = document.getElementById('btnText');
704
-
705
- downloadBtn.style.display = 'none';
706
- generateBtn.disabled = true;
707
- generateBtn.classList.add('btn-loading');
708
- btnText.textContent = 'Génération en cours...';
709
-
710
- resultContent.innerHTML = `
711
- <div class="loading-state">
712
- <div class="spinner"></div>
713
- <div class="loading-text">Génération de votre dissertation</div>
714
- <div class="loading-subtext">Analyse du sujet et structuration des idées...</div>
715
- </div>
716
- `;
717
- }
718
-
719
- displayDissertation(dissertation) {
720
- const resultContent = document.getElementById('resultContent');
721
- const downloadBtn = document.getElementById('downloadBtn');
722
- const generateBtn = document.getElementById('generateBtn');
723
- const btnText = document.getElementById('btnText');
724
-
725
- // Réinitialiser le bouton
726
- generateBtn.disabled = false;
727
- generateBtn.classList.remove('btn-loading');
728
- btnText.textContent = 'Générer une nouvelle dissertation';
729
-
730
- // Afficher le bouton de téléchargement
731
- downloadBtn.style.display = 'block';
732
-
733
- let partiesHTML = '';
734
- dissertation.parties.forEach((partie, index) => {
735
- let argumentsHTML = '';
736
- partie.arguments.forEach(arg => {
737
- argumentsHTML += `<div class="paragraph">${arg.paragraphe_argumentatif}</div>`;
738
- });
739
-
740
- partiesHTML += `
741
- <div class="partie-block">
742
- <div class="paragraph chapeau">${partie.chapeau}</div>
743
- ${argumentsHTML}
744
- ${partie.transition ? `<div class="paragraph transition">${partie.transition}</div>` : ''}
745
- </div>
746
- `;
747
- });
748
-
749
- resultContent.innerHTML = `
750
- <div class="dissertation-content">
751
- <div class="dissertation-paper">
752
- <div class="dissertation-title">Sujet : ${dissertation.sujet}</div>
753
- <div class="dissertation-prof">Prof : ${dissertation.prof}</div>
754
-
755
- <div class="dissertation-section">
756
- <div class="section-title">Introduction</div>
757
- <div class="section-content">
758
- <div class="paragraph">${dissertation.introduction}</div>
759
- </div>
760
- </div>
761
-
762
- <div class="dissertation-section">
763
- <div class="section-title">Développement</div>
764
- <div class="section-content">
765
- ${partiesHTML}
766
- </div>
767
- </div>
768
-
769
- <div class="dissertation-section">
770
- <div class="section-title">Conclusion</div>
771
- <div class="section-content">
772
- <div class="paragraph">${dissertation.conclusion}</div>
773
- </div>
774
- </div>
775
- </div>
776
- </div>
777
- `;
778
- }
779
-
780
- showError(message) {
781
- const resultContent = document.getElementById('resultContent');
782
- const generateBtn = document.getElementById('generateBtn');
783
- const btnText = document.getElementById('btnText');
784
-
785
- generateBtn.disabled = false;
786
- generateBtn.classList.remove('btn-loading');
787
- btnText.textContent = 'Générer la dissertation';
788
-
789
- resultContent.innerHTML = `
790
- <div class="error-state">
791
- <svg class="error-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
792
- <circle cx="12" cy="12" r="10"></circle>
793
- <line x1="15" y1="9" x2="9" y2="15"></line>
794
- <line x1="9" y1="9" x2="15" y2="15"></line>
795
- </svg>
796
- <h3>Erreur de génération</h3>
797
- <p>${message}</p>
798
- </div>
799
- `;
800
- }
801
-
802
- async downloadPDF() {
803
- if (!this.currentDissertation) {
804
- this.showNotification('Aucune dissertation à télécharger', 'error');
805
- return;
806
- }
807
-
808
- try {
809
- const response = await fetch('/api/generate_pdf', {
810
- method: 'POST',
811
- headers: {
812
- 'Content-Type': 'application/json',
813
- },
814
- body: JSON.stringify(this.currentDissertation)
815
- });
816
 
817
- if (response.ok) {
818
- const blob = await response.blob();
819
- const url = window.URL.createObjectURL(blob);
820
- const a = document.createElement('a');
821
- a.href = url;
822
-
823
- // Créer un nom de fichier sécurisé
824
- const safeFilename = this.currentDissertation.sujet
825
- .substring(0, 50)
826
- .replace(/[^a-zA-Z0-9\s\-_]/g, '')
827
- .trim();
828
-
829
- a.download = `${safeFilename || 'dissertation'}.pdf`;
830
- document.body.appendChild(a);
831
- a.click();
832
- document.body.removeChild(a);
833
- window.URL.revokeObjectURL(url);
834
-
835
- this.showNotification('PDF téléchargé avec succès !', 'success');
836
- } else {
837
- throw new Error('Erreur lors de la génération du PDF');
838
- }
839
- } catch (error) {
840
- console.error('Erreur téléchargement PDF:', error);
841
- this.showNotification('Erreur lors du téléchargement PDF', 'error');
842
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
843
  }
844
-
845
- showNotification(message, type = 'success') {
846
- const notification = document.getElementById('notification');
847
- notification.textContent = message;
848
- notification.className = `notification ${type}`;
849
- notification.classList.add('show');
850
-
851
- setTimeout(() => {
852
- notification.classList.remove('show');
853
- }, 4000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
854
  }
855
- }
856
 
857
- // Initialiser l'application
858
- document.addEventListener('DOMContentLoaded', () => {
859
- new DissertationGenerator();
860
- });
861
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
862
  </body>
863
  </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>Assistant de Philosophie (Vue.js)</title>
7
+
8
+ <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
9
+ <!-- La ligne html2pdf.js a été SUPPRIMÉE -->
10
+
11
+ <link rel="preconnect" href="https://fonts.googleapis.com">
12
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
13
+ <link href="https://fonts.googleapis.com/css2?family=Kalam&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
14
+
15
+ <!-- Le CSS reste INCHANGÉ -->
16
+ <style>
17
+ :root {
18
+ --primary-color: #6366f1; /* Indigo moderne */
19
+ --primary-hover: #5b21b6;
20
+ --secondary-color: #8b5cf6; /* Violet */
21
+ --accent-color: #06b6d4; /* Cyan */
22
+ --text-primary: #0f172a; /* Slate-900 */
23
+ --text-secondary: #475569; /* Slate-600 */
24
+ --text-muted: #94a3b8; /* Slate-400 */
25
+ --background: #ffffff;
26
+ --surface: #f8fafc; /* Slate-50 */
27
+ --border: #e2e8f0; /* Slate-200 */
28
+ --border-focus: #cbd5e1; /* Slate-300 */
29
+ --success: #10b981; /* Emerald-500 */
30
+ --error: #ef4444; /* Red-500 */
31
+ }
32
+
33
+ * { box-sizing: border-box; }
34
+
35
+ body {
36
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
37
+ background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
38
+ margin: 0; padding: 0; color: var(--text-primary);
39
+ line-height: 1.6; font-weight: 400;
40
+ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
41
+ min-height: 100vh;
42
+ }
43
+ .container { max-width: 900px; margin: 0 auto; padding: 3rem 2rem; }
44
+ h1 { font-size: 3.5rem; font-weight: 700; text-align: center; background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; margin: 0 0 1rem 0; letter-spacing: -0.02em; }
45
+ .type-indicator { text-align: center; color: var(--text-muted); font-size: 1.125rem; font-weight: 500; margin-bottom: 3rem; letter-spacing: 0.05em; text-transform: uppercase; }
46
+ .form-container { background: var(--background); border-radius: 20px; padding: 2.5rem; margin-bottom: 2rem; border: 1px solid var(--border); backdrop-filter: blur(10px); }
47
+ .form-group { margin-bottom: 2rem; }
48
+ label { display: block; margin-bottom: 0.75rem; font-weight: 600; color: var(--text-primary); font-size: 0.95rem; letter-spacing: 0.01em; }
49
+ textarea, .form-select { width: 100%; padding: 1rem 1.25rem; border-radius: 12px; border: 2px solid var(--border); font-size: 1rem; line-height: 1.6; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); background-color: var(--background); color: var(--text-primary); font-family: inherit; -webkit-appearance: none; -moz-appearance: none; appearance: none; box-sizing: border-box; }
50
+ .form-select { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236366f1' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); background-position: right 1rem center; background-repeat: no-repeat; background-size: 1.25rem 1.25rem; padding-right: 3rem; cursor: pointer; }
51
+ textarea { min-height: 120px; resize: vertical; }
52
+ textarea:focus, .form-select:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1); transform: translateY(-1px); }
53
+ .primary-button { display: block; width: 100%; padding: 1.25rem; background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%); color: white; border: none; border-radius: 12px; font-size: 1.1rem; font-weight: 600; cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); letter-spacing: 0.01em; position: relative; overflow: hidden; }
54
+ .primary-button::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); transition: left 0.5s; }
55
+ .primary-button:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 10px 30px rgba(99, 102, 241, 0.3); }
56
+ .primary-button:hover:not(:disabled)::before { left: 100%; }
57
+ .primary-button:active { transform: translateY(0); }
58
+ .primary-button:disabled { background: var(--text-muted); cursor: not-allowed; transform: none; box-shadow: none; }
59
+ .download-container { margin-top: 2rem; text-align: center; }
60
+ .secondary-button { background: var(--surface); color: var(--text-primary); border: 2px solid var(--border); border-radius: 12px; padding: 1rem 2rem; font-size: 1rem; font-weight: 500; cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); display: inline-flex; align-items: center; gap: 0.5rem; }
61
+ .secondary-button::before { content: '⬇'; font-size: 1.2rem; }
62
+ .secondary-button:hover:not(:disabled) { background: var(--background); border-color: var(--primary-color); color: var(--primary-color); transform: translateY(-1px); box-shadow: 0 5px 15px rgba(99, 102, 241, 0.15); }
63
+ .loader { width: 60px; height: 60px; margin: 3rem auto; position: relative; }
64
+ .loader::before { content: ''; position: absolute; width: 60px; height: 60px; border-radius: 50%; background: conic-gradient(var(--primary-color), var(--secondary-color), var(--accent-color), var(--primary-color)); animation: spin 1.5s linear infinite; }
65
+ .loader::after { content: ''; position: absolute; top: 8px; left: 8px; width: 44px; height: 44px; border-radius: 50%; background: var(--background); }
66
+ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
67
+ .error { color: var(--error); background: rgba(239, 68, 68, 0.1); text-align: center; margin-top: 2rem; padding: 1.25rem; border-radius: 12px; font-weight: 500; border: 1px solid rgba(239, 68, 68, 0.2); }
68
+ .dissertation-paper { font-family: 'Kalam', cursive; font-size: 20px; color: #1a2a4c; background-color: #fdfaf4; line-height: 2; background-image: linear-gradient(transparent 97%, #d8e2ee 98%); background-size: 100% 40px; border-left: 3px solid #ffaaab; padding-left: 4em; margin: 2rem 0; padding-top: 30px; padding-bottom: 40px; padding-right: 30px; border-radius: 0 12px 12px 0; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
69
+ .dissertation-paper h2 { font-size: 1.5em; text-align: center; margin-bottom: 1.5em; color: #1a2a4c; }
70
+ .dissertation-paper h3 { font-size: 1.2em; margin-top: 3em; margin-bottom: 1.5em; text-transform: uppercase; text-decoration: underline; color: #1a2a4c; }
71
+ .dissertation-paper .development-block { margin-top: 3em; }
72
+ .dissertation-paper p { text-align: justify; margin: 0; padding: 0; }
73
+ .dissertation-paper .prof { text-align: center; font-style: italic; margin-bottom: 2em; }
74
+ .dissertation-paper .indented { text-indent: 3em; }
75
+ .dissertation-paper .transition { margin-top: 2em; margin-bottom: 2em; font-style: italic; color: #4a6a9c; }
76
+ .dissertation-paper, .dissertation-paper * { box-sizing: border-box; }
77
+ .avoid-page-break { page-break-inside: avoid; break-inside: avoid; }
78
+ @media (max-width: 768px) { .container { padding: 2rem 1rem; } h1 { font-size: 2.5rem; } .form-container { padding: 1.5rem; } .dissertation-paper { padding-left: 2em; padding-right: 1rem; font-size: 18px; } }
79
+ @keyframes fadeInUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
80
+ .container > * { animation: fadeInUp 0.6s ease-out; }
81
+ </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  </head>
83
  <body>
84
+ <div id="app" class="container">
85
+ <h1>Assistant de Dissertation Philosophique</h1>
86
+ <p class="type-indicator">Méthodologie : [[ dissertationTypeLabel ]]</p>
87
+
88
+ <div class="form-container">
89
+ <form @submit.prevent="generateDissertation">
90
+ <div class="form-group">
91
+ <label for="dissertation-type">Choisir la méthodologie</label>
92
+ <select id="dissertation-type" v-model="dissertationType" class="form-select">
93
+ <option value="type1">Type 1 </option>
94
+ <option value="type2">Type 2 (citation)</option>
95
+ </select>
96
+ </div>
97
+ <div class="form-group">
98
+ <label for="course-context">Choisir un cours comme contexte (optionnel)</label>
99
+ <select id="course-context" v-model="selectedCourse" class="form-select">
100
+ <option value="">-- Aucun cours en contexte --</option>
101
+ <option v-for="course in courses" :key="course.id" :value="course.id">
102
+ [[ course.title ]]
103
+ </option>
104
+ </select>
105
+ </div>
106
+ <div class="form-group">
107
+ <label for="question">Entrez votre sujet de dissertation</label>
108
+ <textarea id="question" v-model="question" placeholder="Entrez votre sujet ou la citation à analyser ici..."></textarea>
109
+ </div>
110
+ <button type="submit" class="primary-button" :disabled="isLoading">
111
+ [[ isLoading ? 'Génération en cours...' : 'Générer la dissertation' ]]
112
+ </button>
113
+ </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  </div>
115
 
116
+ <div v-if="dissertation" class="download-container">
117
+ <button class="secondary-button" @click="generatePDF" :disabled="isDownloading">
118
+ [[ isDownloading ? 'Téléchargement...' : 'Télécharger la dissertation en PDF' ]]
119
+ </button>
120
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
+ <div v-if="isLoading" class="loader"></div>
123
+ <p v-if="errorMessage" class="error">[[ errorMessage ]]</p>
124
+
125
+ <div v-if="dissertation" id="dissertation-content" class="dissertation-paper">
126
+ <h2>Sujet : [[ dissertation.sujet ]]</h2>
127
+ <p class="prof">Prof : [[ dissertation.prof ]]</p>
128
+ <h3>Introduction</h3>
129
+ <p class="indented">[[ dissertation.introduction ]]</p>
130
+ <div v-for="partie in dissertation.parties" :key="partie.chapeau" class="avoid-page-break">
131
+ <div class="development-block">
132
+ <p class="indented">[[ partie.chapeau ]]</p>
133
+ <p v-for="(arg, idx) in partie.arguments" :key="idx" class="indented">
134
+ [[ arg.paragraphe_argumentatif ]]
135
+ </p>
136
+ </div>
137
+ <p v-if="partie.transition" class="indented transition">[[ partie.transition ]]</p>
138
+ </div>
139
+ <h3>Conclusion</h3>
140
+ <p class="indented">[[ dissertation.conclusion ]]</p>
141
+ </div>
142
+ </div>
143
+
144
+ <script>
145
+ const { createApp } = Vue;
146
+
147
+ const app = createApp({
148
+ data() {
149
+ return {
150
+ question: '',
151
+ dissertationType: 'type1',
152
+ courses: [],
153
+ selectedCourse: '',
154
+ isLoading: false,
155
+ isDownloading: false, // <-- NOUVEL état pour le bouton de téléchargement
156
+ errorMessage: null,
157
+ dissertation: null
158
+ }
159
+ },
160
+ computed: {
161
+ dissertationTypeLabel() {
162
+ return this.dissertationType === 'type1' ? 'Type 1' : 'Type 2';
163
+ }
164
+ },
165
+ mounted() {
166
+ this.fetchCourses();
167
+ },
168
+ methods: {
169
+ async fetchCourses() {
170
+ try {
171
+ const response = await fetch('/api/philosophy/courses');
172
+ if (!response.ok) throw new Error('Impossible de charger les cours.');
173
+ const data = await response.json();
174
+ if (data.error) throw new Error(data.error);
175
+ this.courses = data;
176
+ } catch (error) {
177
+ this.errorMessage = error.message;
178
+ }
179
+ },
180
+ async generateDissertation() {
181
+ if (!this.question.trim()) {
182
+ this.errorMessage = "Veuillez entrer un sujet de dissertation.";
183
+ return;
184
+ }
185
+ this.isLoading = true;
186
+ this.errorMessage = null;
187
+ this.dissertation = null;
188
+ try {
189
+ const response = await fetch('/api/generate_dissertation', {
190
+ method: 'POST',
191
+ headers: { 'Content-Type': 'application/json' },
192
+ body: JSON.stringify({
193
+ question: this.question,
194
+ type: this.dissertationType,
195
+ courseId: this.selectedCourse
196
+ })
197
+ });
198
+ const data = await response.json();
199
+ if (!response.ok) {
200
+ throw new Error(data.error || "Une erreur inconnue est survenue.");
201
  }
202
+ this.dissertation = data;
203
+ } catch (error) {
204
+ this.errorMessage = error.message;
205
+ } finally {
206
+ this.isLoading = false;
207
+ }
208
+ },
209
+
210
+ // --- MÉTHODE generatePDF ENTIÈREMENT MISE À JOUR ---
211
+ async generatePDF() {
212
+ if (!this.dissertation) {
213
+ this.errorMessage = "Aucune dissertation à télécharger.";
214
+ return;
215
+ }
216
+ this.isDownloading = true;
217
+ this.errorMessage = null;
218
+ try {
219
+ const response = await fetch('/api/generate_pdf', {
220
+ method: 'POST',
221
+ headers: {
222
+ 'Content-Type': 'application/json',
223
+ },
224
+ // On envoie les données de la dissertation au serveur
225
+ body: JSON.stringify(this.dissertation),
226
+ });
227
+
228
+ if (!response.ok) {
229
+ const errorData = await response.json();
230
+ throw new Error(errorData.error || "Le serveur n'a pas pu générer le PDF.");
231
  }
 
232
 
233
+ // Le serveur renvoie le PDF, on le traite pour le télécharger
234
+ const blob = await response.blob();
235
+ const url = window.URL.createObjectURL(blob);
236
+ const a = document.createElement('a');
237
+ a.style.display = 'none';
238
+ a.href = url;
239
+ a.download = 'dissertation-philosophie.pdf';
240
+ document.body.appendChild(a);
241
+ a.click();
242
+ window.URL.revokeObjectURL(url);
243
+ a.remove();
244
+
245
+ } catch (err) {
246
+ console.error('Erreur de téléchargement du PDF:', err);
247
+ this.errorMessage = "Erreur lors de la génération du PDF : " + err.message;
248
+ } finally {
249
+ this.isDownloading = false;
250
+ }
251
+ }
252
+ }
253
+ });
254
+
255
+ app.config.compilerOptions.delimiters = ['[[', ']]'];
256
+ app.mount('#app');
257
+ </script>
258
  </body>
259
  </html>