<style>
#fritz-widget-container {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 999999;
font-family: ‚Lato‘, Arial, sans-serif;
}
#fritz-toggle-btn {
width: 64px;
height: 64px;
border-radius: 50%;
background: #ff0000;
border: none;
box-shadow: 0 4px 12px rgba(0,0,0,0.25);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
transition: transform 0.2s ease;
}
#fritz-toggle-btn:hover { transform: scale(1.06); }
#fritz-toggle-btn img {
width: 44px;
height: 44px;
border-radius: 50%;
}
#fritz-chat-window {
display: none;
flex-direction: column;
position: absolute;
bottom: 80px;
right: 0;
width: 360px;
max-width: 90vw;
height: 520px;
max-height: 75vh;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 8px 30px rgba(0,0,0,0.25);
overflow: hidden;
}
#fritz-chat-window.open { display: flex; }
#fritz-header {
background: #ff0000;
color: #ffffff;
padding: 14px 16px;
display: flex;
align-items: center;
gap: 10px;
}
#fritz-header img {
width: 36px;
height: 36px;
border-radius: 50%;
background: #ffffff;
}
#fritz-header-text { flex: 1; }
#fritz-header-name { font-weight: 700; font-size: 15px; line-height: 1.2; }
#fritz-header-sub { font-size: 11px; opacity: 0.9; }
#fritz-close-btn {
background: none;
border: none;
color: #ffffff;
font-size: 20px;
cursor: pointer;
line-height: 1;
padding: 4px;
}
#fritz-messages {
flex: 1;
overflow-y: auto;
padding: 14px;
background: #f7f7f7;
display: flex;
flex-direction: column;
gap: 10px;
}
.fritz-msg {
max-width: 80%;
padding: 9px 13px;
border-radius: 14px;
font-size: 14px;
line-height: 1.4;
word-wrap: break-word;
}
.fritz-msg.bot {
background: #ffffff;
color: #565656;
align-self: flex-start;
border: 1px solid #e2e2e2;
border-bottom-left-radius: 4px;
}
.fritz-msg.user {
background: #ff0000;
color: #ffffff;
align-self: flex-end;
border-bottom-right-radius: 4px;
}
.fritz-msg.typing {
background: #ffffff;
color: #999999;
border: 1px solid #e2e2e2;
align-self: flex-start;
font-style: italic;
border-bottom-left-radius: 4px;
}
#fritz-privacy-note {
font-size: 11px;
color: #888888;
padding: 6px 14px;
background: #f7f7f7;
border-top: 1px solid #ececec;
}
#fritz-privacy-note a { color: #565656; }
#fritz-input-row {
display: flex;
border-top: 1px solid #e2e2e2;
padding: 10px;
gap: 8px;
background: #ffffff;
}
#fritz-input {
flex: 1;
border: 1px solid #cccccc;
border-radius: 20px;
padding: 9px 14px;
font-size: 14px;
outline: none;
font-family: ‚Lato‘, Arial, sans-serif;
}
#fritz-input:focus { border-color: #ff0000; }
#fritz-send-btn {
background: #ff0000;
color: #ffffff;
border: none;
border-radius: 50%;
width: 38px;
height: 38px;
cursor: pointer;
font-size: 16px;
flex-shrink: 0;
}
#fritz-send-btn:disabled { background: #cccccc; cursor: default; }
@media (max-width: 480px) {
#fritz-chat-window { width: 92vw; right: -4px; }
}
</style>
<div id="“fritz-widget-container“">
<div id="“fritz-chat-window“">
<div id="“fritz-header“">
<img src=“https://www.steinlen.de/fritz-data/Steinlen_Fritz_512x512.png“ alt=“Fritz“>
<div id="“fritz-header-text“">
<div id="“fritz-header-name“">Fritz</div>
<div id="“fritz-header-sub“">Digitaler Praktikant · Steinlen</div>
</div>
<button id=“fritz-close-btn“ aria-label=“Schliessen“>×</button>
</div>
<div id="“fritz-messages“"></div>
<div id="“fritz-privacy-note“">
Bitte keine personenbezogenen Daten eingeben. <a href=“https://steinlen.de/datenschutz/“ target=“_blank“ rel=“noopener“>Datenschutzhinweis</a>
</div>
<div id="“fritz-input-row“">
<input type=“text“ id=“fritz-input“ placeholder=“Frag Fritz etwas…“ autocomplete=“off“>
<button id=“fritz-send-btn“ aria-label=“Senden“>➤</button>
</div>
</div>
<button id=“fritz-toggle-btn“ aria-label=“Chat mit Fritz öffnen“>
<img src=“https://www.steinlen.de/fritz-data/Steinlen_Fritz_512x512.png“ alt=“Fritz“>
</button>
</div>
<script>
(function () {
// ============================================================
// SEITEN-BESCHRAENKUNG — Widget erscheint NUR auf dieser Seite
// ============================================================
const ALLOWED_PATH = ‚/konfigurator/‘;
if (window.location.pathname.indexOf(ALLOWED_PATH) === -1) {
return; // Auf allen anderen Seiten: Widget gar nicht erst aufbauen
}
// ============================================================
// KONFIGURATION — diese 3 Werte vor dem Einsatz pruefen!
// ============================================================
const WEBHOOK_URL = ‚http://10.56.4.56:5678/webhook/fritz-chat‘; // TODO: interne IP pruefen/anpassen
const AUTH_TOKEN = ‚C?=hK~8Jj!Z6:Ry=‘; // muss mit n8n-Node uebereinstimmen
const AUTO_POPUP_DELAY_MS = 4000; // Chat-Fenster nach 4s automatisch oeffnen (einmalig pro Sitzung)
// ============================================================
const container = document.getElementById(‚fritz-widget-container‘);
const toggleBtn = document.getElementById(‚fritz-toggle-btn‘);
const closeBtn = document.getElementById(‚fritz-close-btn‘);
const chatWindow = document.getElementById(‚fritz-chat-window‘);
const messagesEl = document.getElementById(‚fritz-messages‘);
const inputEl = document.getElementById(‚fritz-input‘);
const sendBtn = document.getElementById(‚fritz-send-btn‘);
// Einfache Session-ID pro Browser-Tab (kein Tracking, nur fuer Konversationskontext)
let sessionId = sessionStorage.getItem(‚fritz_session_id‘);
if (!sessionId) {
sessionId = ‚web-‚ + Date.now() + ‚-‚ + Math.random().toString(36).slice(2, 9);
sessionStorage.setItem(‚fritz_session_id‘, sessionId);
}
function detectLanguage() {
const htmlLang = (document.documentElement.lang || “).toLowerCase();
return htmlLang.startsWith(‚en‘) ? ‚en‘ : ‚de‘;
}
function addMessage(text, role) {
const div = document.createElement(‚div‘);
div.className = ‚fritz-msg ‚ + role;
div.textContent = text;
messagesEl.appendChild(div);
messagesEl.scrollTop = messagesEl.scrollHeight;
return div;
}
function openChat() {
chatWindow.classList.add(‚open‘);
if (messagesEl.children.length === 0) {
const greeting = detectLanguage() === ‚en‘
? „Hi, I’m Fritz! I’m still learning, but happy to help with questions about Steinlen. What can I do for you?“
: ‚Hallo, ich bin Fritz! Ich lerne noch, helfe aber gerne bei Fragen rund um Steinlen. Was kann ich für dich tun?‘;
addMessage(greeting, ‚bot‘);
}
inputEl.focus();
}
function closeChat() {
chatWindow.classList.remove(‚open‘);
}
toggleBtn.addEventListener(‚click‘, function () {
if (chatWindow.classList.contains(‚open‘)) closeChat();
else openChat();
});
closeBtn.addEventListener(‚click‘, closeChat);
async function sendMessage() {
const text = inputEl.value.trim();
if (!text) return;
addMessage(text, ‚user‘);
inputEl.value = “;
sendBtn.disabled = true;
const typingEl = addMessage(
detectLanguage() === ‚en‘ ? ‚Fritz is thinking…‘ : ‚Fritz überlegt…‘,
‚typing‘
);
try {
const response = await fetch(WEBHOOK_URL, {
method: ‚POST‘,
headers: { ‚Content-Type‘: ‚application/json‘ },
body: JSON.stringify({
sessionId: sessionId,
message: text,
language: detectLanguage(),
token: AUTH_TOKEN
})
});
const data = await response.json();
typingEl.remove();
if (data && data.ok !== false && data.reply) {
addMessage(data.reply, ‚bot‘);
} else {
const errText = (data && data.reply)
? data.reply
: (detectLanguage() === ‚en‘
? ‚Sorry, something went wrong. Please contact us directly.‘
: ‚Entschuldigung, da ist etwas schiefgelaufen. Bitte kontaktiere uns direkt.‘);
addMessage(errText, ‚bot‘);
}
} catch (err) {
typingEl.remove();
addMessage(
detectLanguage() === ‚en‘
? „Sorry, I can’t be reached right now. Please contact us directly.“
: ‚Entschuldigung, ich bin gerade nicht erreichbar. Bitte kontaktiere uns direkt.‘,
‚bot‘
);
} finally {
sendBtn.disabled = false;
inputEl.focus();
}
}
sendBtn.addEventListener(‚click‘, sendMessage);
inputEl.addEventListener(‚keydown‘, function (e) {
if (e.key === ‚Enter‘) sendMessage();
});
// Automatisches Aufpoppen (einmal pro Browser-Sitzung)
if (!sessionStorage.getItem(‚fritz_auto_popup_shown‘)) {
setTimeout(function () {
if (!chatWindow.classList.contains(‚open‘)) {
openChat();
sessionStorage.setItem(‚fritz_auto_popup_shown‘, ‚1‘);
}
}, AUTO_POPUP_DELAY_MS);
}
})();
</script>