Docfile commited on
Commit
64ff4b2
·
verified ·
1 Parent(s): d865223

Update templates/philosophie.html

Browse files
Files changed (1) hide show
  1. templates/philosophie.html +457 -245
templates/philosophie.html CHANGED
@@ -3,267 +3,454 @@
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
- <title>Assistant de Philosophie Élégant</title>
7
 
8
  <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
9
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
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=Lato:wght@400;700&family=Inter:wght@400;500;700&display=swap" rel="stylesheet">
14
 
15
  <style>
16
  :root {
17
- --primary-color: #4f46e5;
18
- --primary-hover: #4338ca;
19
- --text-color-dark: #111827;
20
- --text-color-medium: #4b5563;
21
- --text-color-light: #6b7280;
22
- --background-color: #f7f8fc; /* Fond très légèrement bleuté */
23
- --container-bg: #ffffff;
24
- --border-color: #e5e7eb;
25
- --input-bg: #f9fafb;
26
  }
27
 
28
- body {
29
- font-family: 'Inter', sans-serif;
30
- background-color: var(--background-color);
31
  margin: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  padding: 2rem;
33
- color: var(--text-color-medium);
34
- -webkit-font-smoothing: antialiased;
35
- -moz-osx-font-smoothing: grayscale;
36
  }
37
 
38
- .container {
39
- max-width: 800px; /* Un peu plus resserré pour l'élégance */
40
- margin: 0 auto;
41
- background: var(--container-bg);
42
- padding: 48px;
43
  border-radius: 24px;
44
- box-shadow: 0 10px 30px -15px rgba(0,0,0,0.07);
45
- border: 1px solid var(--border-color);
46
  }
47
 
48
- h1 {
 
 
49
  text-align: center;
50
- color: var(--text-color-dark);
51
- font-size: 2.25rem;
52
- letter-spacing: -0.02em;
53
- margin-bottom: 0.5em;
54
  }
55
 
56
- .subtitle {
57
- text-align: center;
58
- color: var(--text-color-light);
59
- font-size: 1.125rem;
60
- margin-bottom: 3em;
 
 
 
 
61
  }
62
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  .form-group {
64
- margin-bottom: 1.75rem;
65
  }
66
-
67
- label {
68
- display: block;
69
- margin-bottom: 0.75rem;
70
- font-weight: 500;
71
- font-size: 0.875rem;
72
- color: var(--text-color-medium);
73
  }
74
 
75
- textarea, .form-select {
76
  width: 100%;
77
- padding: 16px;
 
78
  border-radius: 12px;
79
- border: 1px solid var(--border-color);
80
- background-color: var(--input-bg);
81
  font-size: 1rem;
82
- line-height: 1.5;
83
- color: var(--text-color-dark);
84
- transition: border-color 0.2s, box-shadow 0.2s;
85
- -webkit-appearance: none;
86
- -moz-appearance: none;
87
- appearance: none;
88
- box-sizing: border-box;
 
 
89
  }
90
-
91
  .form-select {
 
92
  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='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
93
  background-position: right 1rem center;
94
  background-repeat: no-repeat;
95
- background-size: 1.5em 1.5em;
96
  padding-right: 3rem;
97
- }
98
-
99
- textarea {
100
- min-height: 120px;
101
- resize: vertical;
102
  }
103
 
104
- textarea:focus, .form-select:focus {
105
  outline: none;
106
- border-color: var(--primary-color);
107
- box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15);
 
108
  }
109
 
110
- .main-button, .secondary-button {
111
  display: inline-flex;
112
  align-items: center;
113
  justify-content: center;
114
- gap: 0.75rem;
115
- box-sizing: border-box;
116
- width: 100%;
117
- padding: 16px 24px;
118
  border: none;
119
  border-radius: 12px;
120
  font-size: 1rem;
121
- font-weight: 700;
122
  cursor: pointer;
123
- transition: all 0.2s ease-in-out;
 
 
 
124
  }
125
 
126
- .main-button {
127
- background-color: var(--primary-color);
128
  color: white;
129
- box-shadow: 0 4px 14px -4px var(--primary-color);
 
 
 
 
 
 
130
  }
131
-
132
- .main-button:hover:not(:disabled) {
133
- background-color: var(--primary-hover);
134
- transform: translateY(-2px);
135
- box-shadow: 0 6px 16px -4px var(--primary-color);
 
 
136
  }
137
 
138
- .main-button:disabled {
139
- background-color: #9ca3af;
 
 
 
 
 
 
140
  cursor: not-allowed;
141
- box-shadow: none;
 
 
 
 
 
 
 
 
 
142
  }
143
-
144
- .download-container {
145
- margin-top: 1rem;
 
 
146
  }
147
- .secondary-button {
148
- background-color: transparent;
149
- color: var(--text-color-medium);
150
- border: 1px solid var(--border-color);
 
151
  }
152
- .secondary-button:hover:not(:disabled) {
153
- background-color: var(--input-bg);
154
- border-color: #d1d5db;
155
- color: var(--text-color-dark);
 
156
  }
157
 
158
- .error {
159
- color: #ef4444;
160
- background-color: #fee2e2;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  text-align: center;
162
- margin-top: 20px;
163
- padding: 15px;
 
 
 
 
 
164
  border-radius: 12px;
 
 
165
  font-weight: 500;
166
  }
167
 
168
- /* NOUVEAU LOADER AVEC POURCENTAGE */
169
- .progress-container {
170
- width: 100%;
171
- margin: 30px auto;
 
 
 
 
 
 
 
 
172
  }
173
- .progress-bar {
174
- height: 12px;
175
- background-color: var(--primary-color);
176
- border-radius: 6px;
177
- width: 0%;
178
- transition: width 0.4s ease-in-out;
179
- box-shadow: 0 0 15px -2px rgba(79, 70, 229, 0.5);
 
180
  }
181
- .progress-text {
 
 
 
 
 
 
 
 
 
 
 
182
  text-align: center;
183
- margin-top: 0.75rem;
184
- font-weight: 700;
185
- color: var(--primary-color);
186
- font-size: 0.875rem;
187
- }
188
-
189
- /* Styles de la feuille de dissertation (inchangés car c'est un style voulu) */
190
- .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: 40px -48px -48px -48px; padding-top: 30px; padding-bottom: 40px; padding-right: 30px; border-radius: 0 0 24px 24px; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
191
- .dissertation-paper h2 { font-size: 1.5em; text-align: center; margin-bottom: 1.5em; }
192
- .dissertation-paper h3 { font-size: 1.2em; margin-top: 3em; margin-bottom: 1.5em; text-transform: uppercase; text-decoration: underline; }
193
- .dissertation-paper .development-block { margin-top: 3em; }
194
- .dissertation-paper p { text-align: justify; margin: 0; padding: 0; }
195
- .dissertation-paper .prof { text-align: center; font-style: italic; margin-bottom: 2em; }
196
- .dissertation-paper .indented { text-indent: 3em; }
197
- .dissertation-paper .transition { margin-top: 2em; margin-bottom: 2em; font-style: italic; color: #4a6a9c; }
198
- .dissertation-paper, .dissertation-paper * { box-sizing: border-box; }
199
- .avoid-page-break { page-break-inside: avoid; break-inside: avoid; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  </style>
201
  </head>
202
  <body>
203
- <div id="app" class="container">
204
- <h1>Assistant Philosophique</h1>
205
- <p class="subtitle">Construisez une dissertation structurée en quelques instants.</p>
206
-
207
- <form @submit.prevent="generateDissertation">
208
- <div class="form-group">
209
- <label for="dissertation-type">Méthodologie</label>
210
- <select id="dissertation-type" v-model="dissertationType" class="form-select">
211
- <option value="type1">Type 1 (Problématique en deux questions)</option>
212
- <option value="type2">Type 2 (Introduction autour d'une citation)</option>
213
- </select>
214
- </div>
215
-
216
- <div class="form-group">
217
- <label for="course-context">Contexte (optionnel)</label>
218
- <select id="course-context" v-model="selectedCourse" class="form-select">
219
- <option value="">-- Aucun cours en contexte --</option>
220
- <option v-for="course in courses" :key="course.id" :value="course.id">
221
- [[ course.title ]]
222
- </option>
223
- </select>
224
  </div>
225
 
226
- <div class="form-group">
227
- <label for="question">Sujet de la dissertation</label>
228
- <textarea id="question" v-model="question" placeholder="Entrez votre sujet ou la citation à analyser ici..."></textarea>
229
- </div>
230
- <button type="submit" :disabled="isLoading" class="main-button">
231
- <svg v-if="!isLoading" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.59a2 2 0 0 1-2.83-2.83l8.49-8.48"></path></svg>
232
- [[ isLoading ? 'Génération en cours...' : 'Générer la dissertation' ]]
233
- </button>
234
- </form>
235
-
236
- <div v-if="dissertation" class="download-container" data-html2canvas-ignore="true">
237
- <button class="secondary-button" @click="generatePDF">
238
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
239
- Télécharger en PDF
240
- </button>
241
- </div>
 
 
 
242
 
243
- <!-- NOUVEAU BLOC DE CHARGEMENT -->
244
- <div v-if="isLoading" class="progress-container">
245
- <div class="progress-bar" :style="{ width: progress + '%' }"></div>
246
- <p class="progress-text">Analyse du sujet... [[ Math.round(progress) ]]%</p>
247
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
249
- <p v-if="errorMessage" class="error">[[ errorMessage ]]</p>
250
-
251
- <div v-if="dissertation" id="dissertation-content" class="dissertation-paper">
252
- <h2>Sujet : [[ dissertation.sujet ]]</h2>
253
- <p class="prof">Prof : [[ dissertation.prof ]]</p>
254
- <h3>Introduction</h3>
255
- <p class="indented">[[ dissertation.introduction ]]</p>
256
- <div v-for="partie in dissertation.parties" :key="partie.chapeau" class="avoid-page-break">
257
- <div class="development-block">
258
- <p class="indented">[[ partie.chapeau ]]</p>
259
- <p v-for="(arg, idx) in partie.arguments" :key="idx" class="indented">
260
- [[ arg.paragraphe_argumentatif ]]
261
- </p>
 
 
 
 
 
 
262
  </div>
263
- <p v-if="partie.transition" class="indented transition">[[ partie.transition ]]</p>
264
  </div>
265
- <h3>Conclusion</h3>
266
- <p class="indented">[[ dissertation.conclusion ]]</p>
267
  </div>
268
  </div>
269
 
@@ -280,9 +467,8 @@
280
  isLoading: false,
281
  errorMessage: null,
282
  dissertation: null,
283
- // Nouvelles données pour la barre de progression
284
  progress: 0,
285
- progressInterval: null
286
  }
287
  },
288
  computed: {
@@ -294,18 +480,62 @@
294
  this.fetchCourses();
295
  },
296
  methods: {
297
- async fetchCourses() { /* ... méthode inchangée ... */ },
298
-
299
- // Méthode de génération MISE À JOUR avec la logique de progression
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
  async generateDissertation() {
301
  if (!this.question.trim()) {
302
  this.errorMessage = "Veuillez entrer un sujet de dissertation.";
303
  return;
304
  }
 
305
  this.isLoading = true;
306
  this.errorMessage = null;
307
  this.dissertation = null;
308
- this.startProgressSimulation();
 
 
309
 
310
  try {
311
  const response = await fetch('/api/generate_dissertation', {
@@ -317,80 +547,62 @@
317
  courseId: this.selectedCourse
318
  })
319
  });
 
320
  const data = await response.json();
321
  if (!response.ok) {
322
  throw new Error(data.error || "Une erreur inconnue est survenue.");
323
  }
324
- this.dissertation = data;
 
 
 
 
 
 
 
325
  } catch (error) {
326
  this.errorMessage = error.message;
327
  } finally {
328
- this.finishProgressSimulation();
329
- }
330
- },
331
-
332
- // NOUVELLES MÉTHODES pour gérer la simulation de la progression
333
- startProgressSimulation() {
334
- this.progress = 0;
335
- clearInterval(this.progressInterval); // Sécurité
336
- this.progressInterval = setInterval(() => {
337
- if (this.progress < 95) { // Ne jamais atteindre 100% tout seul
338
- const increment = Math.random() * 5;
339
- this.progress = Math.min(this.progress + increment, 95);
340
- }
341
- }, 400); // Mettre à jour toutes les 400ms
342
- },
343
-
344
- finishProgressSimulation() {
345
- clearInterval(this.progressInterval);
346
- // Si la requête a réussi, on va à 100% puis on cache la barre
347
- if(this.dissertation) {
348
- this.progress = 100;
349
  setTimeout(() => {
350
  this.isLoading = false;
351
- }, 500); // Laisser le temps à l'utilisateur de voir les 100%
352
- } else {
353
- // S'il y a eu une erreur, on cache directement
354
- this.isLoading = false;
355
  }
356
  },
357
 
358
- async generatePDF() { /* ... méthode inchangée ... */ }
359
- }
360
- });
361
-
362
- // Pour la simplicité, les méthodes fetchCourses et generatePDF sont omises ici
363
- // mais elles doivent être copiées depuis votre version précédente.
364
- // Voici les versions complètes pour être sûr.
365
- const originalMethods = app.config.globalProperties;
366
- originalMethods.fetchCourses = async function() {
367
- try {
368
- const response = await fetch('/api/philosophy/courses');
369
- if (!response.ok) throw new Error('Impossible de charger les cours.');
370
- const data = await response.json();
371
- if (data.error) throw new Error(data.error);
372
- this.courses = data;
373
- } catch (error) {
374
- this.errorMessage = error.message;
375
- }
376
- };
377
- originalMethods.generatePDF = async function() {
378
- const element = document.getElementById('dissertation-content');
379
- if (!element) return;
380
- try {
381
- if (document.fonts && document.fonts.ready) await document.fonts.ready;
382
  window.scrollTo(0, 0);
 
383
  const options = {
384
- margin: [15, 15, 15, 15], filename: 'dissertation-philosophie.pdf',
 
385
  image: { type: 'jpeg', quality: 0.98 },
386
- html2canvas: { scale: 2, useCORS: true, logging: true, backgroundColor: null },
 
 
 
 
 
387
  jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
388
  };
 
389
  await html2pdf().set(options).from(element).save();
390
- } catch (err) {
 
391
  this.errorMessage = "Erreur lors de la génération du PDF.";
 
392
  }
393
- };
 
394
 
395
  app.config.compilerOptions.delimiters = ['[[', ']]'];
396
  app.mount('#app');
 
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
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
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=Inter:wght@300;400;500;600;700&family=Playfair+Display:wght@400;600&display=swap" rel="stylesheet">
14
 
15
  <style>
16
  :root {
17
+ --primary: #1f2937;
18
+ --secondary: #6366f1;
19
+ --accent: #f59e0b;
20
+ --text-primary: #111827;
21
+ --text-secondary: #6b7280;
22
+ --background: #fafafa;
23
+ --surface: #ffffff;
24
+ --border: #e5e7eb;
25
+ --hover: #f3f4f6;
26
  }
27
 
28
+ * {
 
 
29
  margin: 0;
30
+ padding: 0;
31
+ box-sizing: border-box;
32
+ }
33
+
34
+ body {
35
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
36
+ background: var(--background);
37
+ color: var(--text-primary);
38
+ line-height: 1.6;
39
+ font-weight: 400;
40
+ }
41
+
42
+ .main-container {
43
+ min-height: 100vh;
44
  padding: 2rem;
45
+ display: flex;
46
+ align-items: flex-start;
47
+ justify-content: center;
48
  }
49
 
50
+ .app-wrapper {
51
+ width: 100%;
52
+ max-width: 900px;
53
+ background: var(--surface);
 
54
  border-radius: 24px;
55
+ overflow: hidden;
56
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02), 0 4px 25px rgba(0, 0, 0, 0.04);
57
  }
58
 
59
+ .header {
60
+ background: linear-gradient(135deg, var(--primary) 0%, #374151 100%);
61
+ padding: 3rem 3rem 2rem;
62
  text-align: center;
63
+ position: relative;
64
+ overflow: hidden;
 
 
65
  }
66
 
67
+ .header::before {
68
+ content: '';
69
+ position: absolute;
70
+ top: 0;
71
+ left: 0;
72
+ right: 0;
73
+ bottom: 0;
74
+ background: url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23ffffff' fill-opacity='0.02'%3E%3Cpath d='M20 20c0 11.046-8.954 20-20 20v-2c9.94 0 18-8.06 18-18s-8.06-18-18-18V0c11.046 0 20 8.954 20 20zM0 2v2a18 18 0 1 1 18 18h2a20 20 0 1 0-20-20z'/%3E%3C/g%3E%3C/svg%3E") repeat;
75
+ opacity: 0.5;
76
  }
77
+
78
+ .header-title {
79
+ font-family: 'Playfair Display', serif;
80
+ font-size: 2.5rem;
81
+ font-weight: 600;
82
+ color: white;
83
+ margin-bottom: 0.5rem;
84
+ position: relative;
85
+ z-index: 1;
86
+ }
87
+
88
+ .header-subtitle {
89
+ color: rgba(255, 255, 255, 0.8);
90
+ font-size: 1.1rem;
91
+ font-weight: 300;
92
+ position: relative;
93
+ z-index: 1;
94
+ }
95
+
96
+ .content {
97
+ padding: 3rem;
98
+ }
99
+
100
+ .form-section {
101
+ margin-bottom: 2rem;
102
+ }
103
+
104
  .form-group {
105
+ margin-bottom: 1.5rem;
106
  }
107
+
108
+ .form-label {
109
+ display: block;
110
+ font-weight: 500;
111
+ color: var(--text-primary);
112
+ margin-bottom: 0.75rem;
113
+ font-size: 0.95rem;
114
  }
115
 
116
+ .form-input, .form-select, .form-textarea {
117
  width: 100%;
118
+ padding: 1rem 1.25rem;
119
+ border: 1px solid var(--border);
120
  border-radius: 12px;
 
 
121
  font-size: 1rem;
122
+ background: var(--surface);
123
+ transition: all 0.2s ease;
124
+ font-family: inherit;
125
+ }
126
+
127
+ .form-textarea {
128
+ min-height: 120px;
129
+ resize: vertical;
130
+ line-height: 1.6;
131
  }
132
+
133
  .form-select {
134
+ cursor: pointer;
135
  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='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
136
  background-position: right 1rem center;
137
  background-repeat: no-repeat;
138
+ background-size: 1.25rem;
139
  padding-right: 3rem;
140
+ appearance: none;
 
 
 
 
141
  }
142
 
143
+ .form-input:focus, .form-select:focus, .form-textarea:focus {
144
  outline: none;
145
+ border-color: var(--secondary);
146
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
147
+ background: white;
148
  }
149
 
150
+ .btn {
151
  display: inline-flex;
152
  align-items: center;
153
  justify-content: center;
154
+ padding: 1rem 2rem;
 
 
 
155
  border: none;
156
  border-radius: 12px;
157
  font-size: 1rem;
158
+ font-weight: 500;
159
  cursor: pointer;
160
+ transition: all 0.2s ease;
161
+ text-decoration: none;
162
+ font-family: inherit;
163
+ min-height: 52px;
164
  }
165
 
166
+ .btn-primary {
167
+ background: var(--secondary);
168
  color: white;
169
+ width: 100%;
170
+ }
171
+
172
+ .btn-primary:hover:not(:disabled) {
173
+ background: #5855eb;
174
+ transform: translateY(-1px);
175
+ box-shadow: 0 4px 20px rgba(99, 102, 241, 0.3);
176
  }
177
+
178
+ .btn-secondary {
179
+ background: var(--hover);
180
+ color: var(--text-primary);
181
+ border: 1px solid var(--border);
182
+ width: 100%;
183
+ margin-top: 1rem;
184
  }
185
 
186
+ .btn-secondary:hover:not(:disabled) {
187
+ background: #e5e7eb;
188
+ border-color: #d1d5db;
189
+ transform: translateY(-1px);
190
+ }
191
+
192
+ .btn:disabled {
193
+ opacity: 0.6;
194
  cursor: not-allowed;
195
+ transform: none !important;
196
+ }
197
+
198
+ /* Loader avec pourcentage */
199
+ .loading-container {
200
+ display: flex;
201
+ flex-direction: column;
202
+ align-items: center;
203
+ padding: 3rem 0;
204
+ gap: 1.5rem;
205
  }
206
+
207
+ .progress-circle {
208
+ position: relative;
209
+ width: 120px;
210
+ height: 120px;
211
  }
212
+
213
+ .progress-ring {
214
+ width: 120px;
215
+ height: 120px;
216
+ transform: rotate(-90deg);
217
  }
218
+
219
+ .progress-ring-bg {
220
+ fill: transparent;
221
+ stroke: #f3f4f6;
222
+ stroke-width: 8;
223
  }
224
 
225
+ .progress-ring-fill {
226
+ fill: transparent;
227
+ stroke: var(--secondary);
228
+ stroke-width: 8;
229
+ stroke-linecap: round;
230
+ stroke-dasharray: 314;
231
+ stroke-dashoffset: 314;
232
+ transition: stroke-dashoffset 0.5s ease;
233
+ }
234
+
235
+ .progress-text {
236
+ position: absolute;
237
+ top: 50%;
238
+ left: 50%;
239
+ transform: translate(-50%, -50%);
240
+ font-size: 1.5rem;
241
+ font-weight: 600;
242
+ color: var(--secondary);
243
+ }
244
+
245
+ .loading-message {
246
+ color: var(--text-secondary);
247
+ font-size: 1.1rem;
248
  text-align: center;
249
+ font-weight: 500;
250
+ }
251
+
252
+ .error-message {
253
+ background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
254
+ color: #dc2626;
255
+ padding: 1.25rem;
256
  border-radius: 12px;
257
+ margin-top: 1.5rem;
258
+ border-left: 4px solid #ef4444;
259
  font-weight: 500;
260
  }
261
 
262
+ /* Styles pour la dissertation */
263
+ .dissertation-paper {
264
+ font-family: 'Inter', sans-serif;
265
+ font-size: 18px;
266
+ color: #1a202c;
267
+ background: #fefefe;
268
+ line-height: 1.8;
269
+ padding: 3rem;
270
+ margin: 2rem 0;
271
+ border-radius: 16px;
272
+ border: 1px solid #e2e8f0;
273
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02);
274
  }
275
+
276
+ .dissertation-paper h2 {
277
+ font-family: 'Playfair Display', serif;
278
+ font-size: 1.75rem;
279
+ text-align: center;
280
+ margin-bottom: 2rem;
281
+ color: var(--primary);
282
+ font-weight: 600;
283
  }
284
+
285
+ .dissertation-paper h3 {
286
+ font-size: 1.25rem;
287
+ margin: 2.5rem 0 1.5rem;
288
+ color: var(--primary);
289
+ font-weight: 600;
290
+ border-bottom: 2px solid var(--secondary);
291
+ padding-bottom: 0.5rem;
292
+ display: inline-block;
293
+ }
294
+
295
+ .dissertation-paper .prof {
296
  text-align: center;
297
+ font-style: italic;
298
+ margin-bottom: 2.5rem;
299
+ color: var(--text-secondary);
300
+ font-size: 1.1rem;
301
+ }
302
+
303
+ .dissertation-paper p {
304
+ text-align: justify;
305
+ margin-bottom: 1.5rem;
306
+ }
307
+
308
+ .dissertation-paper .indented {
309
+ text-indent: 2rem;
310
+ }
311
+
312
+ .dissertation-paper .transition {
313
+ font-style: italic;
314
+ color: var(--secondary);
315
+ border-left: 3px solid var(--secondary);
316
+ padding-left: 1rem;
317
+ margin: 2rem 0;
318
+ background: rgba(99, 102, 241, 0.05);
319
+ border-radius: 8px;
320
+ padding: 1rem 1rem 1rem 2rem;
321
+ }
322
+
323
+ .development-block {
324
+ margin: 2rem 0;
325
+ }
326
+
327
+ .avoid-page-break {
328
+ page-break-inside: avoid;
329
+ break-inside: avoid;
330
+ }
331
+
332
+ /* Responsive */
333
+ @media (max-width: 768px) {
334
+ .main-container {
335
+ padding: 1rem;
336
+ }
337
+
338
+ .header {
339
+ padding: 2rem 1.5rem;
340
+ }
341
+
342
+ .header-title {
343
+ font-size: 2rem;
344
+ }
345
+
346
+ .content {
347
+ padding: 2rem 1.5rem;
348
+ }
349
+
350
+ .dissertation-paper {
351
+ padding: 2rem 1.5rem;
352
+ }
353
+ }
354
+
355
+ /* Animation pour l'entrée */
356
+ @keyframes fadeInUp {
357
+ from {
358
+ opacity: 0;
359
+ transform: translateY(30px);
360
+ }
361
+ to {
362
+ opacity: 1;
363
+ transform: translateY(0);
364
+ }
365
+ }
366
+
367
+ .app-wrapper {
368
+ animation: fadeInUp 0.6s ease-out;
369
+ }
370
  </style>
371
  </head>
372
  <body>
373
+ <div class="main-container">
374
+ <div id="app" class="app-wrapper">
375
+ <div class="header">
376
+ <h1 class="header-title">Assistant Philosophique</h1>
377
+ <p class="header-subtitle">Méthodologie [[ dissertationTypeLabel ]] • Génération automatisée</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  </div>
379
 
380
+ <div class="content">
381
+ <form @submit.prevent="generateDissertation" class="form-section">
382
+ <div class="form-group">
383
+ <label for="dissertation-type" class="form-label">Méthodologie de dissertation</label>
384
+ <select id="dissertation-type" v-model="dissertationType" class="form-select">
385
+ <option value="type1">Type 1 Problématique en deux questions</option>
386
+ <option value="type2">Type 2 Introduction autour d'une citation</option>
387
+ </select>
388
+ </div>
389
+
390
+ <div class="form-group">
391
+ <label for="course-context" class="form-label">Contexte de cours (optionnel)</label>
392
+ <select id="course-context" v-model="selectedCourse" class="form-select">
393
+ <option value="">Aucun contexte spécifique</option>
394
+ <option v-for="course in courses" :key="course.id" :value="course.id">
395
+ [[ course.title ]]
396
+ </option>
397
+ </select>
398
+ </div>
399
 
400
+ <div class="form-group">
401
+ <label for="question" class="form-label">Sujet de dissertation</label>
402
+ <textarea id="question" v-model="question" class="form-textarea"
403
+ placeholder="Saisissez ici votre sujet de dissertation ou la citation à analyser..."></textarea>
404
+ </div>
405
+
406
+ <button type="submit" :disabled="isLoading" class="btn btn-primary">
407
+ [[ isLoading ? 'Génération en cours...' : 'Générer la dissertation' ]]
408
+ </button>
409
+ </form>
410
+
411
+ <div v-if="dissertation" data-html2canvas-ignore="true">
412
+ <button class="btn btn-secondary" @click="generatePDF">
413
+ 📄 Télécharger en PDF
414
+ </button>
415
+ </div>
416
+
417
+ <div v-if="isLoading" class="loading-container">
418
+ <div class="progress-circle">
419
+ <svg class="progress-ring">
420
+ <circle class="progress-ring-bg" cx="60" cy="60" r="50"></circle>
421
+ <circle class="progress-ring-fill" cx="60" cy="60" r="50"
422
+ :style="{ strokeDashoffset: 314 - (314 * progress / 100) }"></circle>
423
+ </svg>
424
+ <div class="progress-text">[[ Math.round(progress) ]]%</div>
425
+ </div>
426
+ <div class="loading-message">[[ loadingMessage ]]</div>
427
+ </div>
428
+
429
+ <div v-if="errorMessage" class="error-message">
430
+ ⚠️ [[ errorMessage ]]
431
+ </div>
432
 
433
+ <div v-if="dissertation" id="dissertation-content" class="dissertation-paper">
434
+ <h2>Sujet : [[ dissertation.sujet ]]</h2>
435
+ <p class="prof">Professeur : [[ dissertation.prof ]]</p>
436
+
437
+ <h3>Introduction</h3>
438
+ <p class="indented">[[ dissertation.introduction ]]</p>
439
+
440
+ <div v-for="partie in dissertation.parties" :key="partie.chapeau" class="avoid-page-break">
441
+ <div class="development-block">
442
+ <p class="indented">[[ partie.chapeau ]]</p>
443
+ <p v-for="(arg, idx) in partie.arguments" :key="idx" class="indented">
444
+ [[ arg.paragraphe_argumentatif ]]
445
+ </p>
446
+ </div>
447
+ <div v-if="partie.transition" class="transition">[[ partie.transition ]]</div>
448
+ </div>
449
+
450
+ <h3>Conclusion</h3>
451
+ <p class="indented">[[ dissertation.conclusion ]]</p>
452
  </div>
 
453
  </div>
 
 
454
  </div>
455
  </div>
456
 
 
467
  isLoading: false,
468
  errorMessage: null,
469
  dissertation: null,
 
470
  progress: 0,
471
+ loadingMessage: 'Initialisation...'
472
  }
473
  },
474
  computed: {
 
480
  this.fetchCourses();
481
  },
482
  methods: {
483
+ async fetchCourses() {
484
+ try {
485
+ const response = await fetch('/api/philosophy/courses');
486
+ if (!response.ok) throw new Error('Impossible de charger les cours. Vérifiez la connexion à la base de données.');
487
+ const data = await response.json();
488
+ if (data.error) throw new Error(data.error);
489
+ this.courses = data;
490
+ } catch (error) {
491
+ this.errorMessage = error.message;
492
+ console.error("Erreur de chargement des cours:", error);
493
+ }
494
+ },
495
+
496
+ simulateProgress() {
497
+ const messages = [
498
+ 'Initialisation...',
499
+ 'Analyse du sujet...',
500
+ 'Recherche de références...',
501
+ 'Construction de la problématique...',
502
+ 'Développement des arguments...',
503
+ 'Rédaction de l\'introduction...',
504
+ 'Élaboration du plan...',
505
+ 'Rédaction du développement...',
506
+ 'Formulation de la conclusion...',
507
+ 'Finalisation...'
508
+ ];
509
+
510
+ const intervals = [10, 20, 35, 50, 65, 75, 85, 95, 99, 100];
511
+ let step = 0;
512
+
513
+ const updateProgress = () => {
514
+ if (step < intervals.length && this.isLoading) {
515
+ this.progress = intervals[step];
516
+ this.loadingMessage = messages[step];
517
+ step++;
518
+
519
+ const delay = step < 3 ? 800 : step < 6 ? 1200 : 1000;
520
+ setTimeout(updateProgress, delay);
521
+ }
522
+ };
523
+
524
+ updateProgress();
525
+ },
526
+
527
  async generateDissertation() {
528
  if (!this.question.trim()) {
529
  this.errorMessage = "Veuillez entrer un sujet de dissertation.";
530
  return;
531
  }
532
+
533
  this.isLoading = true;
534
  this.errorMessage = null;
535
  this.dissertation = null;
536
+ this.progress = 0;
537
+
538
+ this.simulateProgress();
539
 
540
  try {
541
  const response = await fetch('/api/generate_dissertation', {
 
547
  courseId: this.selectedCourse
548
  })
549
  });
550
+
551
  const data = await response.json();
552
  if (!response.ok) {
553
  throw new Error(data.error || "Une erreur inconnue est survenue.");
554
  }
555
+
556
+ this.progress = 100;
557
+ this.loadingMessage = 'Terminé !';
558
+
559
+ setTimeout(() => {
560
+ this.dissertation = data;
561
+ }, 500);
562
+
563
  } catch (error) {
564
  this.errorMessage = error.message;
565
  } finally {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
566
  setTimeout(() => {
567
  this.isLoading = false;
568
+ }, 800);
 
 
 
569
  }
570
  },
571
 
572
+ async generatePDF() {
573
+ const element = document.getElementById('dissertation-content');
574
+ if (!element) {
575
+ this.errorMessage = "Contenu introuvable pour le PDF.";
576
+ return;
577
+ }
578
+
579
+ try {
580
+ if (document.fonts && document.fonts.ready) {
581
+ await document.fonts.ready;
582
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
583
  window.scrollTo(0, 0);
584
+
585
  const options = {
586
+ margin: [15, 15, 15, 15],
587
+ filename: 'dissertation-philosophie.pdf',
588
  image: { type: 'jpeg', quality: 0.98 },
589
+ html2canvas: {
590
+ scale: 2,
591
+ useCORS: true,
592
+ logging: false,
593
+ backgroundColor: '#ffffff',
594
+ },
595
  jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
596
  };
597
+
598
  await html2pdf().set(options).from(element).save();
599
+ } catch (err) {
600
+ console.error('Erreur html2pdf:', err);
601
  this.errorMessage = "Erreur lors de la génération du PDF.";
602
+ }
603
  }
604
+ }
605
+ });
606
 
607
  app.config.compilerOptions.delimiters = ['[[', ']]'];
608
  app.mount('#app');