288 lines
15 KiB
Plaintext
288 lines
15 KiB
Plaintext
---
|
||
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, '&').replace(/</g, '<').replace(/>/g, '>');
|
||
}
|
||
|
||
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>
|