Comunicació FrontEnd, BackEnd i Integració del Widget
En aquesta fase del projecte, he aconseguit que el meu portfoli deixi de ser una pàgina estàtica per convertir-se en una plataforma interactiva. He integrat un assistent d’IA que es comunica en temps real amb un servidor extern per resoldre dubtes dels usuaris.
1. Interacció entre FrontEnd i BackEnd
L’arquitectura del sistema es basa en un model de client-servidor:
FrontEnd (El Widget): He integrat un xat al WordPress que actua com a interfície d’usuari. Quan un visitant escriu una pregunta, el FrontEnd la recull i l’envia al servidor.
<style>
#chat-wrapper { font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; }
/* Botó flotant corregit (Icona recta i centrada) */
#chat-button {
position: fixed; bottom: 20px; right: 20px;
width: 60px; height: 60px; background: #0078ff;
color: white; border: none; border-radius: 50%;
font-size: 28px; cursor: pointer; box-shadow: 0 4px 15px rgba(0,0,0,0.2);
z-index: 10000; transition: all 0.3s ease;
/* Flexbox per centrar l'emoji */
display: flex; align-items: center; justify-content: center;
padding: 0; line-height: 1;
}
#chat-button:hover { transform: scale(1.1); background: #0056b3; }
/* Finestra de xat */
#chat-window {
display: none; position: fixed; bottom: 90px; right: 20px;
width: 350px; height: 500px; background: white;
border-radius: 20px; box-shadow: 0 12px 28px rgba(0,0,0,0.15);
z-index: 10000; flex-direction: column; overflow: hidden;
border: 1px solid #e1e4e8;
}
/* Capçalera personalitzada */
#chat-header {
background: #0078ff; color: white; padding: 20px;
font-weight: 600; text-align: center; font-size: 16px;
}
/* Missatges */
#chat-messages {
flex: 1; padding: 15px; overflow-y: auto;
display: flex; flex-direction: column; gap: 12px;
background: #ffffff;
}
.msg { padding: 10px 14px; border-radius: 18px; max-width: 80%; font-size: 14px; line-height: 1.5; }
.user { align-self: flex-end; background: #0078ff; color: white; border-bottom-right-radius: 4px; }
.bot { align-self: flex-start; background: #f0f2f5; color: #1c1e21; border-bottom-left-radius: 4px; }
/* Input */
#chat-footer { padding: 15px; border-top: 1px solid #f0f2f5; display: flex; align-items: center; }
#chat-input {
flex: 1; border: 1px solid #ccd0d5; padding: 10px 15px;
border-radius: 25px; outline: none; font-size: 14px;
}
#chat-send {
background: none; border: none; cursor: pointer;
color: #0078ff; font-size: 24px; margin-left: 10px;
}
</style>
<div id="chat-wrapper">
<div id="chat-window">
<div id="chat-header">Benvingut al WordPress de Martí Espinosa</div>
<div id="chat-messages">
<div class="msg bot">Hola! Soc l'IA d'en Martí. Vols saber alguna cosa sobre els seus projectes, estudis o habilitats?</div>
</div>
<div id="chat-footer">
<input type="text" id="chat-input" placeholder="Pregunta'm sobre en Martí...">
<button id="chat-send" onclick="sendChat()">➤</button>
</div>
</div>
<button id="chat-button" onclick="toggleChat()">💬</button>
</div>
<script>
function toggleChat() {
const win = document.getElementById('chat-window');
win.style.display = (win.style.display === 'none' || win.style.display === '') ? 'flex' : 'none';
}
async function sendChat() {
const input = document.getElementById('chat-input');
const box = document.getElementById('chat-messages');
const message = input.value.trim();
if (!message) return;
box.innerHTML += `<div class="msg user">${message}</div>`;
input.value = '';
box.scrollTop = box.scrollHeight;
const loadingId = "loading-" + Date.now();
box.innerHTML += `<div class="msg bot" id="${loadingId}">...</div>`;
box.scrollTop = box.scrollHeight;
try {
// RECORDA: Substitueix aquesta URL per la de ngrok del teu Colab
const URL_NGROK = 'Aqui va el link de Ngrok';
const response = await fetch(URL_NGROK, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: message })
});
const data = await response.json();
document.getElementById(loadingId).remove();
box.innerHTML += `<div class="msg bot">${data.reply}</div>`;
} catch (error) {
if(document.getElementById(loadingId)) document.getElementById(loadingId).remove();
box.innerHTML += `<div class="msg bot" style="color: #d93025;">⚠️ El servidor d'en Martí no respon ara mateix.</div>`;
}
box.scrollTop = box.scrollHeight;
}
document.getElementById('chat-input').addEventListener('keypress', function (e) {
if (e.key === 'Enter') sendChat();
});
</script>
BackEnd
El meu codi en Python processa la pregunta, cerca la informació rellevant en la base de dades que hem generat amb el scraping i consulta la IA (Gemini) per generar una resposta coherent.
Connectivitat mitjançant Ngrok
Com que el BackEnd s’executa en un entorn de desenvolupament (Google Colab), necessitava una manera de fer-lo accessible des de l’exterior.
He utilitzat Ngrok per crear un túnel segur que connecta el meu ordinador amb internet. Aquesta eina em permet generar un URL pública que el widget del WordPress fa servir per enviar les dades d’anada i tornada. Gràcies a això, la comunicació flueix tot i la complexitat de tenir el servidor en una xarxa privada.
Seguretat i Protecció de Dades (Claus API)
Un dels punts més crítics ha estat la seguretat. Per evitar riscos, he aplicat les millors pràctiques en gestió de claus:
Tant la clau de l’API de Gemini com el token de Ngrok estan guardats exclusivament al BackEnd (dins de les “secrets” de Google Colab).
Protecció del Client: El codi JavaScript que veu l’usuari al WordPress mai conté aquestes claus. Només coneix la URL de Ngrok. Així, evitem que qualsevol persona pugui extreure les meves credencials i fer-ne un ús indegut, garantint que només el meu servidor pugui fer consultes a la IA.

Integració i Coherència Visual
El xatbot no és un element aïllat, sinó que s’ha integrat de manera coherent amb el disseny del lloc web. He cuidat que els colors, la posició i el comportament del widget no interfereixin en la navegació, sinó que l’enriqueixin, oferint una experiència d’usuari (UX) professional i moderna.

Flux de funcionament del Xatbot
