maxxxi100 commited on
Commit
20143fb
·
verified ·
1 Parent(s): babc7aa

Update My_health.html

Browse files
Files changed (1) hide show
  1. My_health.html +219 -29
My_health.html CHANGED
@@ -15,8 +15,93 @@
15
 
16
  h1 {
17
  color: #005a9c;
 
18
  }
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  /* Estils del contenidor del xat */
21
  #chat-container {
22
  position: fixed;
@@ -110,13 +195,32 @@
110
  </head>
111
  <body>
112
  <h1>Benvingut a My Health Dashboard</h1>
113
- <p>Aquí pots interactuar amb el teu assistent virtual.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
  <!-- Contenidor del chat flotante -->
116
  <div id="chat-container">
117
  <div id="chat-messages">
118
  <!-- Missatge de benvinguda inicial -->
119
- <div class="message bot">Hola Max 000! Soc un assistent virtual de salut basat en IA. Com et puc ajudar avui?</div>
120
  </div>
121
  <div id="chat-input">
122
  <input type="text" id="user-input" placeholder="Escriu el teu missatge..." />
@@ -125,14 +229,29 @@
125
  </div>
126
 
127
  <script>
 
128
  const chatMessages = document.getElementById('chat-messages');
129
  const userInput = document.getElementById('user-input');
130
  const sendBtn = document.getElementById('send-btn');
131
 
132
- // Defineix el prompt del sistema per a l'assistent de salut
133
- const systemPrompt = "Ets un assistent de salut virtual anomenat 'My Health Assistant'. Respon sempre en català. Sigues amable, professional i concís. El teu propòsit és ajudar els usuaris amb preguntes generals de salut. No proporcionis diagnòstics mèdics ni consells mèdics específics. L'usuari amb qui parles es diu 'Max 000'.";
 
 
 
 
 
 
 
134
 
135
- // Manté un historial de la conversa
 
 
 
 
 
 
 
136
  let chatHistory = [
137
  {
138
  role: "user",
@@ -140,15 +259,14 @@
140
  },
141
  {
142
  role: "model",
143
- parts: [{ text: "Hola Max 000! Soc un assistent virtual de salut basat en IA. Com et puc ajudar avui?" }]
144
  }
145
  ];
146
 
 
 
147
  /**
148
  * Afegeix un missatge a la finestra del xat
149
- * @param {string} text - El text del missatge
150
- * @param {string} type - 'user', 'bot', o 'loading'
151
- * @returns {HTMLElement} - L'element del missatge creat
152
  */
153
  function appendMessage(text, type = 'bot') {
154
  const msgDiv = document.createElement('div');
@@ -160,35 +278,29 @@
160
  }
161
 
162
  /**
163
- * Envia el missatge de l'usuari i gestiona la resposta de la IA
164
  */
165
  async function handleSend() {
166
  const text = userInput.value.trim();
167
  if (!text) return;
168
 
169
- // Afegeix el missatge de l'usuari a la UI i a l'historial
170
  appendMessage(text, 'user');
171
  chatHistory.push({ role: "user", parts: [{ text: text }] });
172
  userInput.value = '';
173
 
174
- // Mostra un indicador de "escrivint..."
175
  const loadingMsg = appendMessage("...", 'loading');
176
  sendBtn.disabled = true;
177
 
178
  try {
179
- // Truca a l'API de Gemini
180
- const botResponse = await callGeminiAPI(chatHistory);
181
-
182
- // Elimina l'indicador de "escrivint..."
183
  chatMessages.removeChild(loadingMsg);
184
-
185
- // Afegeix la resposta del bot a la UI i a l'historial
186
  appendMessage(botResponse, 'bot');
187
  chatHistory.push({ role: "model", parts: [{ text: botResponse }] });
188
 
189
  } catch (error) {
190
- // Gestiona errors
191
- console.error("Error a l'API de Gemini:", error);
192
  chatMessages.removeChild(loadingMsg);
193
  appendMessage("Ho sento, he tingut un problema. Intenta-ho de nou.", 'bot');
194
  } finally {
@@ -197,19 +309,72 @@
197
  }
198
  }
199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  /**
201
- * Truca a l'API de Gemini amb reintents (exponential backoff)
202
- * @param {Array} history - L'historial de la conversa
 
203
  * @returns {Promise<string>} - La resposta de text del model
204
  */
205
- async function callGeminiAPI(history) {
206
  const apiKey = ""; // L'API key es gestionarà automàticament
207
  const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`;
208
 
209
  const payload = {
210
  contents: history,
211
  systemInstruction: {
212
- parts: [{ text: systemPrompt }]
213
  },
214
  // Opcional: eines de cerca per a informació actualitzada
215
  // tools: [{ "google_search": {} }],
@@ -233,7 +398,16 @@
233
  if (candidate && candidate.content?.parts?.[0]?.text) {
234
  return candidate.content.parts[0].text;
235
  } else {
236
- throw new Error("Resposta invàlida de l'API");
 
 
 
 
 
 
 
 
 
237
  }
238
  } else if (response.status === 429 || response.status >= 500) {
239
  // Error de límit de taxa o error del servidor, reintentar
@@ -251,17 +425,32 @@
251
  if (retries >= maxRetries) {
252
  throw error; // Llança l'error després de l'últim reintent
253
  }
254
- // Espera exponencial (1s, 2s, 4s, 8s, 16s)
255
- const delay = Math.pow(2, retries - 1) * 1000 + Math.random() * 1000;
256
  await new Promise(resolve => setTimeout(resolve, delay));
257
  }
258
  }
259
- // Aquest punt només s'assoleix si tots els reintents fallen
260
  throw new Error("No s'ha pogut obtenir resposta de l'API després de diversos intents.");
261
  }
262
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
 
264
- // Esdeveniments per enviar missatge
265
  sendBtn.addEventListener('click', handleSend);
266
  userInput.addEventListener('keydown', e => {
267
  if (e.key === 'Enter') {
@@ -272,3 +461,4 @@
272
  </script>
273
  </body>
274
  </html>
 
 
15
 
16
  h1 {
17
  color: #005a9c;
18
+ text-align: center;
19
  }
20
 
21
+ /* Contenidor principal per als ginys */
22
+ main {
23
+ max-width: 900px;
24
+ margin: 20px auto;
25
+ display: grid;
26
+ grid-template-columns: 1fr;
27
+ gap: 20px;
28
+ }
29
+
30
+ @media (min-width: 768px) {
31
+ main {
32
+ grid-template-columns: 1fr 1fr;
33
+ }
34
+ }
35
+
36
+ /* Estil de les targetes de giny */
37
+ .widget-card {
38
+ background: #fff;
39
+ border-radius: 12px;
40
+ padding: 20px 25px;
41
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
42
+ }
43
+
44
+ .widget-card h2 {
45
+ color: #004a80;
46
+ margin-top: 0;
47
+ border-bottom: 2px solid #e9ecef;
48
+ padding-bottom: 10px;
49
+ }
50
+
51
+ .widget-card p {
52
+ font-size: 15px;
53
+ color: #555;
54
+ line-height: 1.5;
55
+ }
56
+
57
+ .widget-card textarea {
58
+ width: 100%;
59
+ min-height: 120px;
60
+ border: 1px solid #ccc;
61
+ border-radius: 8px;
62
+ padding: 10px;
63
+ font-size: 15px;
64
+ font-family: inherit;
65
+ margin-bottom: 10px;
66
+ box-sizing: border-box; /* Assegura que el padding no afecti l'amplada */
67
+ }
68
+
69
+ .widget-card button {
70
+ display: inline-block;
71
+ width: 100%;
72
+ padding: 12px 18px;
73
+ font-size: 16px;
74
+ font-weight: 600;
75
+ border: none;
76
+ background-color: #007bff;
77
+ color: white;
78
+ cursor: pointer;
79
+ border-radius: 8px;
80
+ transition: background-color 0.3s ease;
81
+ }
82
+
83
+ .widget-card button:hover {
84
+ background-color: #0056b3;
85
+ }
86
+
87
+ /* Caixa de resultats */
88
+ .result-box {
89
+ margin-top: 15px;
90
+ padding: 15px;
91
+ background-color: #f8f9fa;
92
+ border-radius: 8px;
93
+ border: 1px solid #e9ecef;
94
+ min-height: 50px;
95
+ line-height: 1.6;
96
+ white-space: pre-wrap; /* Respecta els salts de línia */
97
+ }
98
+
99
+ .result-box .loading {
100
+ font-style: italic;
101
+ color: #777;
102
+ }
103
+
104
+
105
  /* Estils del contenidor del xat */
106
  #chat-container {
107
  position: fixed;
 
195
  </head>
196
  <body>
197
  <h1>Benvingut a My Health Dashboard</h1>
198
+
199
+ <!-- Nous ginys amb IA -->
200
+ <main>
201
+ <div class="widget-card">
202
+ <h2>✨ Generador de Benestar</h2>
203
+ <p>Demana un pla de menjars, una rutina d'exercicis, o consells de mindfulness.</p>
204
+ <textarea id="wellness-prompt" rows="5" placeholder="Ex: Un pla de sopars saludable i ràpid per a 3 dies..."></textarea>
205
+ <button id="generate-wellness-btn">Generar Pla</button>
206
+ <div id="wellness-result" class="result-box"></div>
207
+ </div>
208
+
209
+ <div class="widget-card">
210
+ <h2>✨ Resumidor de Textos de Salut</h2>
211
+ <p>Enganxa un article o text mèdic complicat per obtenir un resum senzill.</p>
212
+ <textarea id="text-to-summarize" rows="5" placeholder="Enganxa el text aquí..."></textarea>
213
+ <button id="summarize-text-btn">Resumir Text</button>
214
+ <div id="summarize-result" class="result-box"></div>
215
+ </div>
216
+ </main>
217
+
218
 
219
  <!-- Contenidor del chat flotante -->
220
  <div id="chat-container">
221
  <div id="chat-messages">
222
  <!-- Missatge de benvinguda inicial -->
223
+ <div class="message bot">Hola Max 000! Soc un assistent virtual. Fes-me una pregunta o utilitza les eines de la pàgina.</div>
224
  </div>
225
  <div id="chat-input">
226
  <input type="text" id="user-input" placeholder="Escriu el teu missatge..." />
 
229
  </div>
230
 
231
  <script>
232
+ // --- Elements del Xat ---
233
  const chatMessages = document.getElementById('chat-messages');
234
  const userInput = document.getElementById('user-input');
235
  const sendBtn = document.getElementById('send-btn');
236
 
237
+ // --- Elements del Giny de Benestar ---
238
+ const wellnessPromptEl = document.getElementById('wellness-prompt');
239
+ const generateWellnessBtn = document.getElementById('generate-wellness-btn');
240
+ const wellnessResultEl = document.getElementById('wellness-result');
241
+
242
+ // --- Elements del Giny de Resum ---
243
+ const textToSummarizeEl = document.getElementById('text-to-summarize');
244
+ const summarizeTextBtn = document.getElementById('summarize-text-btn');
245
+ const summarizeResultEl = document.getElementById('summarize-result');
246
 
247
+
248
+ // --- Prompts del Sistema per a Gemini ---
249
+ const chatSystemPrompt = "Ets un assistent de salut virtual anomenat 'My Health Assistant'. Respon sempre en català. Sigues amable, professional i concís. El teu propòsit és ajudar els usuaris amb preguntes generals de salut. No proporcionis diagnòstics mèdics ni consells mèdics específics. L'usuari amb qui parles es diu 'Max 000'.";
250
+ const wellnessSystemPrompt = "Ets un expert en benestar, nutrició i fitness. Respon sempre en català. La teva tasca és generar plans de menjars, rutines d'exercici o consells de mindfulness basats en la petició de l'usuari. Sigues encoratjador i clar. No proporcionis consells mèdics ni diagnòstics.";
251
+ const summarizeSystemPrompt = "Ets un expert en comunicació sanitària. La teva única tasca és agafar el text proporcionat per l'usuari i resumir-lo en català planer, senzill i fàcil d'entendre. Fes-ho en punts clau o un paràgraf curt. No afegeixis informació nova. Basa't només en el text proporcionat.";
252
+
253
+
254
+ // --- Historial del Xat ---
255
  let chatHistory = [
256
  {
257
  role: "user",
 
259
  },
260
  {
261
  role: "model",
262
+ parts: [{ text: "Hola Max 000! Soc un assistent virtual. Fes-me una pregunta o utilitza les eines de la pàgina." }]
263
  }
264
  ];
265
 
266
+ // --- Lògica del Xat ---
267
+
268
  /**
269
  * Afegeix un missatge a la finestra del xat
 
 
 
270
  */
271
  function appendMessage(text, type = 'bot') {
272
  const msgDiv = document.createElement('div');
 
278
  }
279
 
280
  /**
281
+ * Envia el missatge de l'usuari i gestiona la resposta de la IA del xat
282
  */
283
  async function handleSend() {
284
  const text = userInput.value.trim();
285
  if (!text) return;
286
 
 
287
  appendMessage(text, 'user');
288
  chatHistory.push({ role: "user", parts: [{ text: text }] });
289
  userInput.value = '';
290
 
 
291
  const loadingMsg = appendMessage("...", 'loading');
292
  sendBtn.disabled = true;
293
 
294
  try {
295
+ // Truca a l'API de Gemini amb l'historial del xat i el prompt del sistema del xat
296
+ const botResponse = await callGeminiAPI(chatHistory, chatSystemPrompt);
297
+
 
298
  chatMessages.removeChild(loadingMsg);
 
 
299
  appendMessage(botResponse, 'bot');
300
  chatHistory.push({ role: "model", parts: [{ text: botResponse }] });
301
 
302
  } catch (error) {
303
+ console.error("Error a l'API de Gemini (Xat):", error);
 
304
  chatMessages.removeChild(loadingMsg);
305
  appendMessage("Ho sento, he tingut un problema. Intenta-ho de nou.", 'bot');
306
  } finally {
 
309
  }
310
  }
311
 
312
+ // --- Lògica dels Ginys ---
313
+
314
+ // Giny de Benestar
315
+ generateWellnessBtn.addEventListener('click', async () => {
316
+ const prompt = wellnessPromptEl.value.trim();
317
+ if (!prompt) {
318
+ wellnessResultEl.textContent = "Si us plau, escriu què necessites.";
319
+ return;
320
+ }
321
+
322
+ wellnessResultEl.innerHTML = '<p class="loading">Generant el teu pla...</p>';
323
+ generateWellnessBtn.disabled = true;
324
+
325
+ try {
326
+ // Creem un "historial" temporal només per a aquesta trucada
327
+ const tempHistory = [{ role: "user", parts: [{ text: prompt }] }];
328
+ // Truquem a l'API amb el prompt de l'usuari i el system prompt de benestar
329
+ const result = await callGeminiAPI(tempHistory, wellnessSystemPrompt);
330
+ wellnessResultEl.innerHTML = formatResponse(result);
331
+ } catch (error) {
332
+ console.error("Error a l'API de Gemini (Benestar):", error);
333
+ wellnessResultEl.textContent = "Error en generar el pla. Intenta-ho de nou.";
334
+ } finally {
335
+ generateWellnessBtn.disabled = false;
336
+ }
337
+ });
338
+
339
+ // Giny de Resum
340
+ summarizeTextBtn.addEventListener('click', async () => {
341
+ const textToSummarize = textToSummarizeEl.value.trim();
342
+ if (!textToSummarize) {
343
+ summarizeResultEl.textContent = "Si us plau, enganxa el text que vols resumir.";
344
+ return;
345
+ }
346
+
347
+ summarizeResultEl.innerHTML = '<p class="loading">Resumint text...</p>';
348
+ summarizeTextBtn.disabled = true;
349
+
350
+ try {
351
+ // El prompt de l'usuari és el text que vol resumir
352
+ const tempHistory = [{ role: "user", parts: [{ text: textToSummarize }] }];
353
+ // Truquem a l'API amb el text i el system prompt de resum
354
+ const result = await callGeminiAPI(tempHistory, summarizeSystemPrompt);
355
+ summarizeResultEl.innerHTML = formatResponse(result);
356
+ } catch (error) {
357
+ console.error("Error a l'API de Gemini (Resum):", error);
358
+ summarizeResultEl.textContent = "Error en resumir el text. Intenta-ho de nou.";
359
+ } finally {
360
+ summarizeTextBtn.disabled = false;
361
+ }
362
+ });
363
+
364
  /**
365
+ * Funció genèrica per trucar a l'API de Gemini amb reintents (exponential backoff)
366
+ * @param {Array} history - L'historial de la conversa (o un prompt temporal)
367
+ * @param {string} systemInstructionText - El prompt del sistema a utilitzar
368
  * @returns {Promise<string>} - La resposta de text del model
369
  */
370
+ async function callGeminiAPI(history, systemInstructionText) {
371
  const apiKey = ""; // L'API key es gestionarà automàticament
372
  const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`;
373
 
374
  const payload = {
375
  contents: history,
376
  systemInstruction: {
377
+ parts: [{ text: systemInstructionText }]
378
  },
379
  // Opcional: eines de cerca per a informació actualitzada
380
  // tools: [{ "google_search": {} }],
 
398
  if (candidate && candidate.content?.parts?.[0]?.text) {
399
  return candidate.content.parts[0].text;
400
  } else {
401
+ // Gestionar contingut bloquejat o resposta buida
402
+ let errorMsg = "La resposta ha estat bloquejada o estava buida.";
403
+ if (candidate?.finishReason) {
404
+ errorMsg += ` Motiu: ${candidate.finishReason}`;
405
+ }
406
+ if (result.promptFeedback?.blockReason) {
407
+ errorMsg += ` (Feedback: ${result.promptFeedback.blockReason})`;
408
+ }
409
+ console.warn("Resposta invàlida de l'API:", result);
410
+ return errorMsg;
411
  }
412
  } else if (response.status === 429 || response.status >= 500) {
413
  // Error de límit de taxa o error del servidor, reintentar
 
425
  if (retries >= maxRetries) {
426
  throw error; // Llança l'error després de l'últim reintent
427
  }
428
+ // Espera exponencial (1s, 2s, 4s, 8s)
429
+ const delay = Math.pow(2, retries) * 1000 + Math.random() * 1000;
430
  await new Promise(resolve => setTimeout(resolve, delay));
431
  }
432
  }
 
433
  throw new Error("No s'ha pogut obtenir resposta de l'API després de diversos intents.");
434
  }
435
 
436
+ /**
437
+ * Formata la resposta de text (substitueix \n per <br>)
438
+ * @param {string} text - Text pla
439
+ * @returns {string} - Text HTML formatat
440
+ */
441
+ function formatResponse(text) {
442
+ // Escapa HTML bàsic per seguretat abans de fer reemplaços
443
+ let safeText = text.replace(/</g, "&lt;").replace(/>/g, "&gt;");
444
+
445
+ // Converteix negreta simple de markdown
446
+ safeText = safeText.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
447
+ // Converteix salts de línia
448
+ safeText = safeText.replace(/\n/g, '<br>');
449
+ return safeText;
450
+ }
451
+
452
 
453
+ // --- Esdeveniments del Xat ---
454
  sendBtn.addEventListener('click', handleSend);
455
  userInput.addEventListener('keydown', e => {
456
  if (e.key === 'Enter') {
 
461
  </script>
462
  </body>
463
  </html>
464
+