*/ (function () { 'use strict'; // ── Config from data attributes ────────────────────────────────────────── const script = document.currentScript || document.querySelector('script[data-endpoint]'); const ext = window.PROMARINE_CONFIG || {}; const CONFIG = { endpoint: script?.dataset?.endpoint || ext.endpoint || 'http://localhost:8000/chat', botName: script?.dataset?.botName || ext.botName || 'Promarine Assistant', greeting: script?.dataset?.greeting || ext.greeting || 'Hi! How can I help you today?', primaryColor: script?.dataset?.primaryColor || ext.primaryColor || null, token: script?.dataset?.token || ext.token || '', }; // Apply custom primary color if provided if (CONFIG.primaryColor) { document.documentElement.style.setProperty('--pm-primary', CONFIG.primaryColor); } // ── Quick reply topics ──────────────────────────────────────────────────── const QUICK_REPLIES = [ 'Tarvitsen apua tuotteen löytämisessä', 'Haluan muuttaa tai peruuttaa tilaukseni', 'Tilauksen palautus ja hyvitykset', 'Tuotetakuu ja korjaus', 'Ongelmia kassalla', 'Aukioloajat ja yhteystiedot', ]; // ── State ───────────────────────────────────────────────────────────────── let isOpen = false; let isLoading = false; const history = []; // [{role, content}] — full conversation history // ── Build DOM ───────────────────────────────────────────────────────────── function buildWidget() { // Toggle button const toggle = document.createElement('button'); toggle.id = 'pm-toggle'; toggle.setAttribute('aria-label', 'Open chat'); toggle.innerHTML = ` `; // Chat panel const panel = document.createElement('div'); panel.id = 'pm-panel'; panel.setAttribute('role', 'dialog'); panel.setAttribute('aria-label', CONFIG.botName); panel.innerHTML = `

${escapeHtml(CONFIG.botName)}

Online

`; document.body.appendChild(toggle); document.body.appendChild(panel); // Wire up events toggle.addEventListener('click', togglePanel); document.getElementById('pm-header-contact').addEventListener('click', function () { showEscalationButton(''); }); document.getElementById('pm-send').addEventListener('click', handleSend); const input = document.getElementById('pm-input'); input.addEventListener('keydown', function (e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend(); } }); input.addEventListener('input', autoResize); // Show greeting appendMessage('bot', CONFIG.greeting); // Show quick reply buttons showQuickReplies(); } // ── Contact form ────────────────────────────────────────────────────────── function showEscalationButton(userQuestion) { const messages = document.getElementById('pm-messages'); const card = document.createElement('div'); card.className = 'pm-contact-card'; card.innerHTML = `

Jätä viesti asiakaspalveluun / Leave a message for support

`; messages.appendChild(card); scrollToBottom(); document.getElementById('pm-contact-submit').addEventListener('click', async function () { const name = document.getElementById('pm-contact-name').value.trim(); const email = document.getElementById('pm-contact-email').value.trim(); const question = document.getElementById('pm-contact-question').value.trim(); const errorEl = document.getElementById('pm-contact-error'); if (!name || !email || !question) { errorEl.textContent = 'Täytä kaikki kentät / Please fill in all fields'; return; } const btn = document.getElementById('pm-contact-submit'); btn.disabled = true; btn.textContent = '...'; errorEl.textContent = ''; try { const contactEndpoint = CONFIG.endpoint.replace('/chat', '/contact'); const res = await fetch(contactEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(CONFIG.token && { 'X-Widget-Token': CONFIG.token }), }, body: JSON.stringify({ name, email, question, hp: document.getElementById('pm-hp').value }), }); const data = await res.json(); if (data.success) { card.innerHTML = '

✓ Viestisi on lähetetty! / Your message has been sent!

'; } else { errorEl.textContent = 'Jokin meni pieleen / Something went wrong. Please try again.'; btn.disabled = false; btn.textContent = 'Lähetä / Send'; } } catch (e) { errorEl.textContent = 'Jokin meni pieleen / Something went wrong. Please try again.'; btn.disabled = false; btn.textContent = 'Lähetä / Send'; } }); } // ── Quick replies ───────────────────────────────────────────────────────── function showQuickReplies() { const messages = document.getElementById('pm-messages'); const container = document.createElement('div'); container.id = 'pm-quick-replies'; QUICK_REPLIES.forEach(function (label) { const btn = document.createElement('button'); btn.className = 'pm-quick-btn'; btn.textContent = label; btn.addEventListener('click', function () { container.remove(); document.getElementById('pm-input').value = label; handleSend(); }); container.appendChild(btn); }); messages.appendChild(container); scrollToBottom(); } // ── Panel toggle ────────────────────────────────────────────────────────── function togglePanel() { isOpen = !isOpen; const toggle = document.getElementById('pm-toggle'); const panel = document.getElementById('pm-panel'); toggle.classList.toggle('pm-open', isOpen); panel.classList.toggle('pm-visible', isOpen); toggle.setAttribute('aria-label', isOpen ? 'Close chat' : 'Open chat'); if (isOpen) { setTimeout(() => document.getElementById('pm-input').focus(), 220); } } // ── Send message ────────────────────────────────────────────────────────── async function handleSend() { if (isLoading) return; const input = document.getElementById('pm-input'); const text = input.value.trim(); if (!text) return; input.value = ''; autoResize.call(input); // Remove quick replies once user sends any message const qr = document.getElementById('pm-quick-replies'); if (qr) qr.remove(); // Show user message appendMessage('user', text); // Add to history history.push({ role: 'user', content: text }); // Show typing indicator const typingId = showTyping(); isLoading = true; setSendDisabled(true); try { const response = await fetch(CONFIG.endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(CONFIG.token && { 'X-Widget-Token': CONFIG.token }), }, body: JSON.stringify({ message: text, history: history.slice(0, -1), }), }); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const data = await response.json(); const reply = data.reply || 'Sorry, I could not process your request.'; removeTyping(typingId); appendMessage('bot', reply); history.push({ role: 'assistant', content: reply }); if (data.escalate) showEscalationButton(text); } catch (err) { console.error('[Promarine chatbot] Error:', err); removeTyping(typingId); appendMessage('bot', 'Sorry, something went wrong. Please try again in a moment.'); } finally { isLoading = false; setSendDisabled(false); document.getElementById('pm-input').focus(); } } // ── DOM helpers ─────────────────────────────────────────────────────────── function appendMessage(role, text) { const messages = document.getElementById('pm-messages'); const wrapper = document.createElement('div'); wrapper.className = `pm-msg pm-${role}`; const bubble = document.createElement('div'); bubble.className = 'pm-bubble'; // Use innerHTML with sanitized content to preserve line breaks bubble.innerHTML = formatMessage(text); wrapper.appendChild(bubble); messages.appendChild(wrapper); scrollToBottom(); return wrapper; } function showTyping() { const messages = document.getElementById('pm-messages'); const wrapper = document.createElement('div'); const id = 'pm-typing-' + Date.now(); wrapper.id = id; wrapper.className = 'pm-msg pm-bot pm-typing'; wrapper.innerHTML = `
`; messages.appendChild(wrapper); scrollToBottom(); return id; } function removeTyping(id) { const el = document.getElementById(id); if (el) el.remove(); } function setSendDisabled(disabled) { document.getElementById('pm-send').disabled = disabled; } function scrollToBottom() { const messages = document.getElementById('pm-messages'); messages.scrollTop = messages.scrollHeight; } function autoResize() { this.style.height = 'auto'; this.style.height = Math.min(this.scrollHeight, 100) + 'px'; } function formatMessage(text) { // Escape HTML, linkify URLs, then convert newlines to
return escapeHtml(text) .replace(/(https?:\/\/[^\s<]+)/g, '$1') .replace(/\n/g, '
'); } function escapeHtml(text) { const div = document.createElement('div'); div.appendChild(document.createTextNode(text)); return div.innerHTML; } // ── Init ────────────────────────────────────────────────────────────────── if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', buildWidget); } else { buildWidget(); } })();// Paste your JS code here, don't include script tags