Passa al contenuto principale

Messaggi e FAQ

Invio Messaggi con useCallback

Il nuovo widget utilizza useCallback per ottimizzare la funzione di invio messaggi:

resources/js/shadowDomWidgetPreact.jsx
// Helper function to send a message
const sendMessage = useCallback(
async (messageText) => {
if (!messageText.trim() || isRunning) return;

const text = messageText.trim();

// Hide FAQ
setShowFAQ(false);

// Hide all previous FAQ and add user message
setMessages((prev) => {
// First hide all FAQ from previous messages
const messagesWithHiddenFAQ = prev.map((msg) => ({
...msg,
showFAQ: false,
}));

// Add the new user message
return [
...messagesWithHiddenFAQ,
{
type: "user",
content: text,
textClass: userTextClass,
},
];
});

// Add loading indicator
setMessages((prev) => [
...prev,
{
type: "loading",
textClass: assistantTextClass,
},
]);

setIsRunning(true);

// Ensure thread exists and get the actual threadId to use
let actualThreadId = currentThreadId;

let agentId = widgetAssistantId;
if (!agentId.split("_")[1]) {
agentId =
type === "assistant"
? `assistant_${agentId}`
: `agent_${agentId}`;
}

if (!actualThreadId) {
socketRef.current.emit("create_thread", {
agentId,
company_name: companyName,
company_description: companyDescription,
language: language,
laravelAppUrl: appUrl,
type: type,
});

// Wait for thread creation and get the threadId
actualThreadId = await new Promise((resolve) => {
const handleThreadCreated = ({ threadId }) => {
setCurrentThreadId(threadId);
socketRef.current.off(
"thread_created",
handleThreadCreated
);
resolve(threadId); // Return the threadId directly
};
socketRef.current.on("thread_created", handleThreadCreated);
});
}

// `create_thread` usa `agentId` normalizzato (prefisso assistant_/agent_).
// I messaggi successivi inviano `widgetAssistantId` così com'è passato al widget.

// Send message with the actual threadId
const msgObj = {
message: text,
threadId: actualThreadId, // Use the actual threadId instead of state
agentId: widgetAssistantId,
collection_name: collectionName,
companyName: companyName,
companyDescription: companyDescription,
baseFeedUrl: baseFeedUrl,
hasDocuments: hasDocuments,
language: language,
laravelAppUrl: appUrl,
websiteUrl: websiteUrl,
type: type,
assistantInstructions: assistantInstructions,
};

if (payload) msgObj.payload = payload;
lastFailedMessageRef.current = msgObj;
socketRef.current.emit("chat_message", msgObj);

// Loading indicator will be removed when chat_response_end is received
},
[
isRunning,
currentThreadId,
userTextClass,
assistantTextClass,
widgetAssistantId,
collectionName,
companyName,
companyDescription,
baseFeedUrl,
hasDocuments,
language,
appUrl,
websiteUrl,
type,
assistantInstructions,
payload,
setShowFAQ,
setMessages,
setIsRunning,
setInputDisabled,
setCurrentThreadId,
ensureOnlyLastFAQVisible,
]
);

Rendering Messaggi

resources/js/shadowDomWidget.js
addMessageToChat(type, content, isHistory = false) {
const frag = document.createDocumentFragment();
const msgDiv = this.createMessageElement(type, content);
frag.appendChild(msgDiv);

if (type === "user" && !isHistory) {
frag.appendChild(this.createLoadingIndicator());
}

this.dom.chatHistory.appendChild(frag);
this.scrollChatToBottomIfNeeded();
return msgDiv;
}

createMessageElement(role, content) {
const div = document.createElement("div");
div.className = `message ${role}-message`;

// Aggiunta classi colore testo
if (role === "user" && this.userTextClass) {
div.classList.add(this.userTextClass);
} else if (role === "system" && this.assistantTextClass) {
div.classList.add(this.assistantTextClass);
}

if (role === "system") {
div.innerHTML = marked.parse(this.cleanTmpMsg(content));
} else {
div.textContent = content;
}

return div;
}

🎯 Sistema FAQ

Gestione Domande Frequenti

resources/js/shadowDomWidget.js
_initFAQ() {
if (!this.options.faqMessages ||
!this.options.faqMessages.length ||
this.currentThreadId) {
return;
}

const wrapper = document.createElement("div");
wrapper.classList.add("faq-wrapper", "initial");
this.displayQuestions(this.options.faqMessages, wrapper);
this.dom.chatHistory.appendChild(wrapper);
}

displayQuestions(questions, wrapper) {
const title = document.createElement("p");
title.classList.add("faq-title");
title.textContent = "Prova a chiedere";
wrapper.appendChild(title);

const container = document.createElement("div");
container.classList.add("faq-container");

for (const q of questions) {
const btn = document.createElement("button");
btn.type = "button";
btn.classList.add("faq-single");
btn.textContent = q;
btn.addEventListener("click", () => {
if (this.isRunning) return;
this.dom.messageInput.value = q;
this.sendMessage();
wrapper.remove();
});
container.appendChild(btn);
}

wrapper.appendChild(container);
}