Docfile commited on
Commit
9012a0c
·
verified ·
1 Parent(s): cc914b6

Update templates/philosophie.html

Browse files
Files changed (1) hide show
  1. templates/philosophie.html +85 -53
templates/philosophie.html CHANGED
@@ -6,13 +6,11 @@
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 */
@@ -46,10 +44,13 @@
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); }
@@ -90,32 +91,44 @@
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
 
@@ -151,99 +164,118 @@
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;
 
6
  <title>Assistant de Philosophie (Vue.js)</title>
7
 
8
  <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
 
9
 
10
  <link rel="preconnect" href="https://fonts.googleapis.com">
11
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
  <link href="https://fonts.googleapis.com/css2?family=Kalam&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
13
 
 
14
  <style>
15
  :root {
16
  --primary-color: #6366f1; /* Indigo moderne */
 
44
  .form-container { background: var(--background); border-radius: 20px; padding: 2.5rem; margin-bottom: 2rem; border: 1px solid var(--border); backdrop-filter: blur(10px); }
45
  .form-group { margin-bottom: 2rem; }
46
  label { display: block; margin-bottom: 0.75rem; font-weight: 600; color: var(--text-primary); font-size: 0.95rem; letter-spacing: 0.01em; }
47
+ /* Style ajouté pour le champ de fichier */
48
+ textarea, .form-select, .file-input { 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; }
49
+ .file-input { cursor: pointer; }
50
+ .file-input:hover { border-color: var(--border-focus); }
51
  .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; }
52
  textarea { min-height: 120px; resize: vertical; }
53
+ textarea:focus, .form-select:focus, .file-input:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1); transform: translateY(-1px); }
54
  .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; }
55
  .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; }
56
  .primary-button:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 10px 30px rgba(99, 102, 241, 0.3); }
 
91
  <div class="form-group">
92
  <label for="dissertation-type">Choisir la méthodologie</label>
93
  <select id="dissertation-type" v-model="dissertationType" class="form-select">
94
+ <option value="type1">Type 1</option>
95
  <option value="type2">Type 2 (citation)</option>
96
+ <option value="type3">Type 3 (commentaire de texte)</option>
97
  </select>
98
  </div>
99
+
100
+ <!-- AFFICHAGE CONDITIONNEL : Champs pour Type 1 et 2 -->
101
+ <div v-if="dissertationType !== 'type3'">
102
+ <div class="form-group">
103
+ <label for="course-context">Choisir un cours comme contexte (optionnel)</label>
104
+ <select id="course-context" v-model="selectedCourse" class="form-select">
105
+ <option value="">-- Aucun cours en contexte --</option>
106
+ <option v-for="course in courses" :key="course.id" :value="course.id">
107
+ [[ course.title ]]
108
+ </option>
109
+ </select>
110
+ </div>
111
+ <div class="form-group">
112
+ <label for="question">Entrez votre sujet de dissertation</label>
113
+ <textarea id="question" v-model="question" placeholder="Entrez votre sujet ou la citation à analyser ici..."></textarea>
114
+ </div>
115
  </div>
116
+
117
+ <!-- AFFICHAGE CONDITIONNEL : Champ pour Type 3 (Upload d'image) -->
118
+ <div v-if="dissertationType === 'type3'" class="form-group">
119
+ <label for="text-image">Téléchargez une image du texte à commenter</label>
120
+ <input type="file" @change="handleFileUpload" id="text-image" accept="image/*" class="file-input">
121
  </div>
122
+
123
  <button type="submit" class="primary-button" :disabled="isLoading">
124
+ [[ isLoading ? 'Génération en cours...' : 'Générer' ]]
125
  </button>
126
  </form>
127
  </div>
128
 
129
  <div v-if="dissertation" class="download-container">
130
  <button class="secondary-button" @click="generatePDF" :disabled="isDownloading">
131
+ [[ isDownloading ? 'Téléchargement...' : 'Télécharger en PDF' ]]
132
  </button>
133
  </div>
134
 
 
164
  dissertationType: 'type1',
165
  courses: [],
166
  selectedCourse: '',
167
+ uploadedFile: null, // <-- NOUVEL état pour le fichier
168
  isLoading: false,
169
+ isDownloading: false,
170
  errorMessage: null,
171
  dissertation: null
172
  }
173
  },
174
  computed: {
175
+ // Propriété calculée pour afficher le bon libellé en haut
176
  dissertationTypeLabel() {
177
+ const labels = {
178
+ 'type1': 'Type 1',
179
+ 'type2': 'Type 2 (Citation)',
180
+ 'type3': 'Type 3 (Commentaire de texte)'
181
+ };
182
+ return labels[this.dissertationType] || 'Inconnu';
183
  }
184
  },
185
  mounted() {
186
  this.fetchCourses();
187
  },
188
  methods: {
189
+ // NOUVELLE méthode pour gérer la sélection du fichier
190
+ handleFileUpload(event) {
191
+ this.uploadedFile = event.target.files[0];
192
+ this.errorMessage = null; // Réinitialiser l'erreur
193
+ },
194
  async fetchCourses() {
195
  try {
196
  const response = await fetch('/api/philosophy/courses');
197
  if (!response.ok) throw new Error('Impossible de charger les cours.');
198
+ this.courses = await response.json();
 
 
199
  } catch (error) {
200
  this.errorMessage = error.message;
201
  }
202
  },
203
+ // MÉTHODE MISE À JOUR pour gérer les deux types de soumission
204
  async generateDissertation() {
 
 
 
 
205
  this.isLoading = true;
206
  this.errorMessage = null;
207
  this.dissertation = null;
208
+
209
  try {
210
+ let response;
211
+ // CAS 1 : C'est un upload d'image (Type 3)
212
+ if (this.dissertationType === 'type3') {
213
+ if (!this.uploadedFile) {
214
+ throw new Error("Veuillez sélectionner une image pour le commentaire de texte.");
215
+ }
216
+ const formData = new FormData();
217
+ formData.append('image', this.uploadedFile);
218
+ formData.append('type', this.dissertationType);
219
+
220
+ response = await fetch('/api/generate_dissertation', {
221
+ method: 'POST',
222
+ body: formData, // Le navigateur gère le Content-Type pour FormData
223
+ });
224
+
225
+ // CAS 2 : C'est une soumission de texte (Type 1 ou 2)
226
+ } else {
227
+ if (!this.question.trim()) {
228
+ throw new Error("Veuillez entrer un sujet de dissertation.");
229
+ }
230
+ response = await fetch('/api/generate_dissertation', {
231
+ method: 'POST',
232
+ headers: { 'Content-Type': 'application/json' },
233
+ body: JSON.stringify({
234
+ question: this.question,
235
+ type: this.dissertationType,
236
+ courseId: this.selectedCourse
237
+ })
238
+ });
239
+ }
240
+
241
+ // Le traitement de la réponse est commun aux deux cas
242
  const data = await response.json();
243
  if (!response.ok) {
244
  throw new Error(data.error || "Une erreur inconnue est survenue.");
245
  }
246
  this.dissertation = data;
247
+
248
  } catch (error) {
249
  this.errorMessage = error.message;
250
  } finally {
251
  this.isLoading = false;
252
  }
253
  },
254
+ // La méthode de génération PDF reste identique car elle travaille sur l'objet 'dissertation' final
 
255
  async generatePDF() {
256
+ if (!this.dissertation) return;
 
 
 
257
  this.isDownloading = true;
258
  this.errorMessage = null;
259
  try {
260
  const response = await fetch('/api/generate_pdf', {
261
  method: 'POST',
262
+ headers: { 'Content-Type': 'application/json' },
 
 
 
263
  body: JSON.stringify(this.dissertation),
264
  });
 
265
  if (!response.ok) {
266
  const errorData = await response.json();
267
+ throw new Error(errorData.error || "Erreur serveur lors de la création du PDF.");
268
  }
 
 
269
  const blob = await response.blob();
270
  const url = window.URL.createObjectURL(blob);
271
  const a = document.createElement('a');
 
272
  a.href = url;
273
  a.download = 'dissertation-philosophie.pdf';
274
  document.body.appendChild(a);
275
  a.click();
276
  window.URL.revokeObjectURL(url);
277
  a.remove();
 
278
  } catch (err) {
 
279
  this.errorMessage = "Erreur lors de la génération du PDF : " + err.message;
280
  } finally {
281
  this.isDownloading = false;