Files
casa-daenzo-web/src/components/EnzinoChat.astro
T

288 lines
15 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
import {
ENZINO_INITIAL_GREETING_DE,
ENZINO_INITIAL_GREETING_EN,
ENZINO_QUICK_QUESTIONS_DE,
ENZINO_QUICK_QUESTIONS_EN
} from '../data/enzinoPrompt';
interface Props {
lang?: 'de' | 'en';
}
const { lang = 'de' } = Astro.props;
const initialGreeting = lang === 'de' ? ENZINO_INITIAL_GREETING_DE : ENZINO_INITIAL_GREETING_EN;
const quickQuestions = lang === 'de' ? ENZINO_QUICK_QUESTIONS_DE : ENZINO_QUICK_QUESTIONS_EN;
---
<div id="enzino-widget" class="fixed bottom-6 right-6 z-50 font-sans" data-lang={lang}>
<!-- Floating Launcher Button -->
<button
id="enzino-launcher"
class="relative group flex items-center gap-3 bg-[#24211D] border border-[#D4A373]/30 hover:border-[#D4A373] text-[#E6D5C3] px-4 py-3 rounded-full shadow-2xl transition-all duration-300 hover:scale-105"
aria-label="Enzino Concierge Chat öffnen"
>
<div class="relative w-10 h-10 rounded-full bg-gradient-to-br from-[#C85A32] to-[#D4A373] p-0.5 flex items-center justify-center shadow-inner">
<div class="w-full h-full rounded-full bg-[#1A1816] flex items-center justify-center text-lg font-serif font-bold text-[#D4A373]">
🇮🇹
</div>
<span class="absolute -top-0.5 -right-0.5 w-3 h-3 bg-emerald-500 rounded-full border-2 border-[#1A1816] animate-pulse"></span>
</div>
<div class="text-left hidden sm:block">
<div class="text-xs font-serif font-bold text-[#D4A373] tracking-wide">Enzino Concierge</div>
<div class="text-[10px] text-[#A3978B]">Online • Frag mich was!</div>
</div>
</button>
<!-- Chat Dialog Window -->
<div
id="enzino-chat-window"
class="hidden fixed bottom-6 right-6 w-[calc(100vw-3rem)] sm:w-[380px] max-h-[580px] h-[85vh] bg-[#24211D] border border-[#D4A373]/30 rounded-3xl shadow-2xl flex-col overflow-hidden transition-all duration-300 z-50"
>
<!-- Header -->
<div class="bg-[#1A1816] border-b border-[#D4A373]/20 p-4 flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="relative w-10 h-10 rounded-full bg-gradient-to-br from-[#C85A32] to-[#D4A373] p-0.5 flex items-center justify-center">
<div class="w-full h-full rounded-full bg-[#24211D] flex items-center justify-center text-lg">
👨‍🍳
</div>
<span class="absolute bottom-0 right-0 w-2.5 h-2.5 bg-emerald-500 rounded-full border border-[#1A1816]"></span>
</div>
<div>
<h3 class="font-serif text-sm font-bold text-[#E6D5C3]">Enzino da Enzo</h3>
<p class="text-[10px] text-[#D4A373]">Digitaler Host & Concierge</p>
</div>
</div>
<div class="flex items-center gap-2">
<!-- Session Disable Button -->
<button
id="enzino-disable-btn"
title={lang === 'de' ? 'Enzino für diese Session deaktivieren' : 'Disable Enzino for this session'}
class="p-1.5 text-[#A3978B] hover:text-amber-400 rounded-lg hover:bg-[#2A2622] text-xs transition-colors"
>
🚫
</button>
<!-- Close Button -->
<button
id="enzino-close-btn"
class="p-1.5 text-[#A3978B] hover:text-white rounded-lg hover:bg-[#2A2622] transition-colors"
aria-label="Chat schließen"
>
✕
</button>
</div>
</div>
<!-- Messages Body -->
<div id="enzino-messages" class="flex-1 p-4 overflow-y-auto space-y-3.5 text-xs text-[#E6D5C3]">
<!-- Initial Bot Message -->
<div class="flex gap-2.5 items-start">
<div class="w-7 h-7 rounded-full bg-[#C85A32]/20 border border-[#C85A32]/40 flex items-center justify-center text-xs shrink-0">
🇮🇹
</div>
<div class="bg-[#2E2A25] border border-[#D4A373]/15 p-3 rounded-2xl rounded-tl-none max-w-[85%] leading-relaxed shadow-sm">
{initialGreeting}
</div>
</div>
<!-- Quick Chips -->
<div id="enzino-chips" class="flex flex-wrap gap-1.5 pt-2 pl-9">
{quickQuestions.map((q) => (
<button
class="enzino-chip text-[11px] bg-[#1A1816] hover:bg-[#C85A32]/20 text-[#D4A373] border border-[#D4A373]/30 hover:border-[#D4A373] px-3 py-1.5 rounded-full transition-all text-left"
data-question={q}
>
{q}
</button>
))}
</div>
</div>
<!-- Typing Indicator (hidden by default) -->
<div id="enzino-typing" class="hidden px-4 py-2 text-[11px] text-[#D4A373] italic flex items-center gap-2 bg-[#1A1816]/50">
<span class="w-2 h-2 bg-[#D4A373] rounded-full animate-ping"></span>
<span>Enzino tippt...</span>
</div>
<!-- Input Footer -->
<form id="enzino-form" class="bg-[#1A1816] border-t border-[#D4A373]/20 p-3 flex gap-2">
<input
type="text"
id="enzino-input"
placeholder={lang === 'de' ? 'Stelle Enzino eine Frage...' : 'Ask Enzino a question...'}
class="flex-1 bg-[#24211D] border border-[#D4A373]/20 focus:border-[#D4A373] text-[#E6D5C3] placeholder-[#A3978B] text-xs rounded-xl px-3 py-2.5 outline-none transition-all"
autocomplete="off"
/>
<button
type="submit"
class="bg-[#C85A32] hover:bg-[#b04d2a] text-white px-3.5 py-2.5 rounded-xl text-xs font-bold transition-all shrink-0"
>
➔
</button>
</form>
</div>
</div>
<script>
function initEnzino() {
const launcher = document.getElementById('enzino-launcher');
const windowEl = document.getElementById('enzino-chat-window');
const closeBtn = document.getElementById('enzino-close-btn');
const disableBtn = document.getElementById('enzino-disable-btn');
const form = document.getElementById('enzino-form') as HTMLFormElement;
const input = document.getElementById('enzino-input') as HTMLInputElement;
const messagesEl = document.getElementById('enzino-messages');
const typingEl = document.getElementById('enzino-typing');
const widget = document.getElementById('enzino-widget');
if (!launcher || !windowEl || !closeBtn || !messagesEl) return;
// Check if user disabled Enzino for session
if (sessionStorage.getItem('enzino_disabled') === 'true') {
if (widget) widget.style.display = 'none';
return;
}
const lang = widget?.getAttribute('data-lang') || 'de';
// Toggle Chat
launcher.addEventListener('click', () => {
windowEl.classList.remove('hidden');
windowEl.classList.add('flex');
launcher.classList.add('hidden');
if (input) input.focus();
});
closeBtn.addEventListener('click', () => {
windowEl.classList.add('hidden');
windowEl.classList.remove('flex');
launcher.classList.remove('hidden');
});
// Disable for session
if (disableBtn) {
disableBtn.addEventListener('click', () => {
if (confirm(lang === 'de' ? 'Enzino für diese Browser-Sitzung ausblenden?' : 'Disable Enzino for this session?')) {
sessionStorage.setItem('enzino_disabled', 'true');
if (widget) widget.style.display = 'none';
}
});
}
// Quick chips click
document.querySelectorAll('.enzino-chip').forEach((chip) => {
chip.addEventListener('click', (e) => {
const q = (e.currentTarget as HTMLElement).getAttribute('data-question');
if (q) {
handleUserQuery(q);
}
});
});
// Form submit
if (form) {
form.addEventListener('submit', (e) => {
e.preventDefault();
const text = input.value.trim();
if (text) {
input.value = '';
handleUserQuery(text);
}
});
}
function appendUserMessage(text: string) {
const div = document.createElement('div');
div.className = 'flex justify-end';
div.innerHTML = `
<div class="bg-[#2D5A43] text-[#E6D5C3] p-3 rounded-2xl rounded-tr-none max-w-[85%] leading-relaxed shadow-sm">
${escapeHtml(text)}
</div>
`;
messagesEl.appendChild(div);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
function appendBotMessage(text: string) {
const div = document.createElement('div');
div.className = 'flex gap-2.5 items-start';
div.innerHTML = `
<div class="w-7 h-7 rounded-full bg-[#C85A32]/20 border border-[#C85A32]/40 flex items-center justify-center text-xs shrink-0">
🇮🇹
</div>
<div class="bg-[#2E2A25] border border-[#D4A373]/15 p-3 rounded-2xl rounded-tl-none max-w-[85%] leading-relaxed shadow-sm">
${text}
</div>
`;
messagesEl.appendChild(div);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
function escapeHtml(str: string) {
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
function handleUserQuery(text: string) {
appendUserMessage(text);
// Show typing
if (typingEl) typingEl.classList.remove('hidden');
setTimeout(() => {
if (typingEl) typingEl.classList.add('hidden');
const reply = generateEnzinoReply(text, lang);
appendBotMessage(reply);
}, 700);
}
function generateEnzinoReply(query: string, language: string): string {
const q = query.toLowerCase();
if (q.includes('check-in') || q.includes('checkin') || q.includes('anreise') || q.includes('nuki') || q.includes('schlüssel') || q.includes('key')) {
return language === 'de'
? "<strong>Perfetto!</strong> Der Check-in verläuft 100% kontaktlos und digital. Du erhältst vor deiner Anreise deinen persönlichen Nuki-PIN-Code per WhatsApp oder E-Mail. Check-in ist ab <strong>15:00 Uhr</strong> möglich. Bei Fragen erreichst du Alja direkt über <a href='https://wa.me/4915207483196' target='_blank' class='text-[#D4A373] underline'>WhatsApp (+49 1520 7483196)</a>."
: "<strong>Perfetto!</strong> Check-in is 100% digital & contactless. You'll receive your personal Nuki PIN code via WhatsApp or email prior to arrival. Check-in starts at <strong>3:00 PM</strong>. For queries, text Alja directly via <a href='https://wa.me/4915207483196' target='_blank' class='text-[#D4A373] underline'>WhatsApp (+49 1520 7483196)</a>.";
}
if (q.includes('tisch') || q.includes('trattoria') || q.includes('essen') || q.includes('pasta') || q.includes('reservier') || q.includes('restaurant') || q.includes('speisekarte')) {
return language === 'de'
? "<strong>Mamma Mia!</strong> In Alessandras Trattoria wartet ehrliche sizilianische Küche auf dich! Öffnungszeiten: Di–So 17:00–22:30 Uhr (Montag Ruhetag). Hotelgäste genießen bevorzugte Tischreservierungen! Für Reservierungen rufe direkt unser Trattoria-Festnetz an: <a href='tel:+49532123456' class='text-[#D4A373] underline'>+49 5321 23456</a>."
: "<strong>Mamma Mia!</strong> Honest Sicilian cuisine awaits you at Alessandra's Trattoria! Hours: Tue–Sun 5:00 PM–10:30 PM (Mon closed). Apartment guests get priority table reservations! Call our Trattoria landline: <a href='tel:+49532123456' class='text-[#D4A373] underline'>+49 5321 23456</a>.";
}
if (q.includes('bestpreis') || q.includes('preis') || q.includes('rabatt') || q.includes('booking') || q.includes('direkt')) {
return language === 'de'
? "<strong>Bellissimo!</strong> Wenn du direkt auf unserer Website buchst, sparst du dir <strong>15% OTA-Gebühren</strong> gegenüber Booking.com oder Airbnb. Wir garantieren dir den besten verfügbaren Preis!"
: "<strong>Bellissimo!</strong> Booking directly on our website saves you <strong>15% in OTA commissions</strong> compared to Booking.com or Airbnb. We guarantee the best available rate!";
}
if (q.includes('eviivo') || q.includes('gästebuch') || q.includes('guest') || q.includes('directory') || q.includes('mappe')) {
return language === 'de'
? "<strong>Prego!</strong> In unserem <a href='https://casa-daenzo.de/eviivo-guest' target='_blank' class='text-[#D4A373] underline'>Eviivo Digital Guest Directory</a> findest du alle Details zu deinem Aufenthalt, WLAN-Passwort und Tipps für Goslar. Logge dich einfach mit deiner Buchungsnummer und deinem Nachnamen ein!"
: "<strong>Prego!</strong> In our <a href='https://casa-daenzo.de/eviivo-guest' target='_blank' class='text-[#D4A373] underline'>Eviivo Digital Guest Directory</a> you'll find all details about your stay, Wi-Fi password, and local tips. Simply log in with your booking number and last name!";
}
if (q.includes('kontakt') || q.includes('telefon') || q.includes('nummer') || q.includes('whatsapp') || q.includes('anja') || q.includes('alja')) {
return language === 'de'
? "<strong>Hier ist unsere Kontakttrennung:</strong><br/>• <strong>Apartments & Buchungen:</strong> Alja per <a href='https://wa.me/4915207483196' target='_blank' class='text-[#D4A373] underline'>WhatsApp (+49 1520 7483196)</a><br/>• <strong>Trattoria & Tische:</strong> Festnetz <a href='tel:+49532123456' class='text-[#D4A373] underline'>+49 5321 23456</a>"
: "<strong>Here is our contact separation:</strong><br/>• <strong>Apartments & Bookings:</strong> Alja via <a href='https://wa.me/4915207483196' target='_blank' class='text-[#D4A373] underline'>WhatsApp (+49 1520 7483196)</a><br/>• <strong>Trattoria & Tables:</strong> Landline <a href='tel:+49532123456' class='text-[#D4A373] underline'>+49 5321 23456</a>";
}
if (q.includes('parken') || q.includes('parking') || q.includes('auto') || q.includes('garagen')) {
return language === 'de'
? "<strong>Molto bene!</strong> Zum Ausladen deines Gepäcks kannst du direkt vor das Haus (Bäckerstraße 18) fahren. Öffentliche Parkplätze & das Parkhaus 'Kaiserpassage' liegen nur 2-3 Gehminuten entfernt."
: "<strong>Molto bene!</strong> You can stop directly in front of the building (Bäckerstraße 18) to unload luggage. Public parking and 'Kaiserpassage' parking garage are just 2-3 minutes walk away.";
}
return language === 'de'
? "<strong>Grazie für deine Frage!</strong> Bei allen Anliegen zu den Apartments schreib Alja per <a href='https://wa.me/4915207483196' target='_blank' class='text-[#D4A373] underline'>WhatsApp (+49 1520 7483196)</a>. Für Tischreservierungen rufe die Trattoria an (<a href='tel:+49532123456' class='text-[#D4A373] underline'>+49 5321 23456</a>)!"
: "<strong>Grazie for your question!</strong> For apartment inquiries, message Alja via <a href='https://wa.me/4915207483196' target='_blank' class='text-[#D4A373] underline'>WhatsApp (+49 1520 7483196)</a>. For table reservations, call the Trattoria (<a href='tel:+49532123456' class='text-[#D4A373] underline'>+49 5321 23456</a>)!";
}
}
// Run on page load and view transitions
document.addEventListener('DOMContentLoaded', initEnzino);
document.addEventListener('astro:page-load', initEnzino);
</script>