import React, { useState, useEffect, useCallback, createContext, useContext } from 'react'; import { initializeApp } from 'firebase/app'; // FIX: Explicitly import signInWithCustomToken to prevent 'is not a function' error import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from 'firebase/auth'; import { getFirestore, doc, setDoc, setLogLevel } from 'firebase/firestore'; // --- Global Setup (Required by Canvas) --- const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id'; const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : {}; const initialAuthToken = typeof __initial_auth_token !== 'undefined' ? __initial_auth_token : null; // Context for state sharing const AppContext = createContext(); // --- Language Data (English and Thai) --- const translations = { en: { title: "Your Top Pool & Hostess Bar", subtitle: "Best Spot on Sukhumvit Soi 7/1 (Near Nana BTS)", tagline: "Ice-cold beers, great company, and Bangkok's friendliest staff waiting for you.", findGame: "Find Your Game", vibeTitle: "What We Offer", vibeSubtitle: "The Experience", featurePool: "Professional Pool Table", featurePoolDesc: "Challenge staff or guests. Only 20 Baht per game for the best table on Soi 7/1.", featureHostess: "Friendly Hostesses", featureHostessDesc: "Enjoy warm, welcoming service and great conversation with our lovely staff.", menuTitle: "Bar Menu & Clear Prices", menuSubtitle: "Transparency is Key", beerLocal: "Local (Chang, Singha, Leo)", beerImport: "Import (Heineken, Asahi)", spirits: "Spirits & Mixers", hostessPolicy: "Hostess Drink Policy", hostessPrice: "300 THB", hostessInfo: "This price is non-negotiable and ensures excellent service and company.", specialsTitle: "Claim Your Free Game Time", specialsSubtitle: "Exclusive Online Offers", poolPackage: "Pool Master Package", poolDesc: "Pre-purchase a set of games online and maximize your time on the best table in the area.", poolOffer: "Buy 5 Games, Get 2 Games FREE!", poolPriceNote: "That's 7 games for only 100 THB.", poolRedeem: "Show this voucher to the staff on your next visit to redeem.", hostessVoucher: "VIP Hostess Welcome", hostessVoucherDesc: "Sign up to receive a digital voucher for 50% OFF your first Hostess Drink purchase.", loginAnon: "Claim Voucher", eventsTitle: "Upcoming Magic Table Events", eventsSubtitle: "What's Happening This Week", eventPool: "Soi 7/1 Pool Tournament", eventPoolTime: "Every Wednesday, 9:00 PM. Cash prizes for the winner. Sign up at the bar!", eventLadies: "Ladies' Pink Night", eventLadiesTime: "Every Friday, 7:00 PM to 11:00 PM. Half-price drinks for all ladies.", locationTitle: "Find Us Easily", location: "Location", locationAddress: "The Magic Table, 25 Sukhumvit Soi 7/1, Khlong Toei, Bangkok 10110.", locationNote: "Just a 5-minute walk from Nana BTS Station.", hours: "Hours", hoursTime: "Open Daily: 7:00 PM until late (approx. 2:00 AM)", contact: "Contact", contactNumber: "+66 81 123 4567", getDirections: "Get Directions", footer: "Designed for Bangkok nightlife." }, th: { title: "บาร์พูลและโฮสเตสอันดับ 1 ของคุณ", subtitle: "จุดที่ดีที่สุดบนสุขุมวิทซอย 7/1 (ใกล้ BTS นานา)", tagline: "เบียร์เย็นเจี๊ยบ บริษัทที่ดี และพนักงานที่เป็นมิตรที่สุดในกรุงเทพฯ รอคุณอยู่", findGame: "มาเล่นกันเลย", vibeTitle: "สิ่งที่เรานำเสนอ", vibeSubtitle: "ประสบการณ์", featurePool: "โต๊ะพูลมืออาชีพ", featurePoolDesc: "ท้าทายพนักงานหรือแขก ค่าบริการเพียง 20 บาทต่อเกม สำหรับโต๊ะที่ดีที่สุดในซอย 7/1", featureHostess: "โฮสเตสที่เป็นมิตร", featureHostessDesc: "เพลิดเพลินกับการบริการที่อบอุ่นและเป็นกันเอง และการสนทนาที่ยอดเยี่ยมกับพนักงานที่น่ารักของเรา", menuTitle: "เมนูบาร์และราคาที่ชัดเจน", menuSubtitle: "ความโปร่งใสคือกุญแจสำคัญ", beerLocal: "เบียร์ท้องถิ่น (ช้าง, สิงห์, ลีโอ)", beerImport: "เบียร์นำเข้า (ไฮเนเก้น, อาซาฮี)", spirits: "สุราและมิกเซอร์", hostessPolicy: "นโยบายเครื่องดื่มโฮสเตส", hostessPrice: "300 บาท", hostessInfo: "ราคานี้ไม่สามารถต่อรองได้และรับประกันบริการและบริษัทที่ยอดเยี่ยม", specialsTitle: "รับเวลาเล่นเกมฟรีของคุณ", specialsSubtitle: "ข้อเสนอพิเศษออนไลน์", poolPackage: "แพ็คเกจปรมาจารย์พูล", poolDesc: "ซื้อชุดเกมล่วงหน้าออนไลน์และใช้เวลาของคุณให้คุ้มค่าที่สุดบนโต๊ะที่ดีที่สุดในพื้นที่", poolOffer: "ซื้อ 5 เกม รับฟรี 2 เกม!", poolPriceNote: "นั่นคือ 7 เกมในราคาเพียง 100 บาท", poolRedeem: "แสดงบัตรกำนัลนี้ต่อพนักงานในการเยี่ยมชมครั้งต่อไปเพื่อแลกรับ", hostessVoucher: "การต้อนรับโฮสเตส VIP", hostessVoucherDesc: "ลงทะเบียนเพื่อรับบัตรกำนัลดิจิทัล ลด 50% สำหรับการซื้อเครื่องดื่มโฮสเตสครั้งแรกของคุณ", loginAnon: "รับบัตรกำนัล", eventsTitle: "กิจกรรม Magic Table ที่กำลังจะมาถึง", eventsSubtitle: "เกิดอะไรขึ้นในสัปดาห์นี้", eventPool: "ทัวร์นาเมนต์พูลซอย 7/1", eventPoolTime: "ทุกวันพุธ เวลา 21:00 น. รางวัลเงินสดสำหรับผู้ชนะ ลงทะเบียนได้ที่บาร์!", eventLadies: "คืนสีชมพูสำหรับสุภาพสตรี", eventLadiesTime: "ทุกวันศุกร์ เวลา 19:00 น. ถึง 23:00 น. เครื่องดื่มลดครึ่งราคาสำหรับสุภาพสตรีทุกคน", locationTitle: "ค้นหาเราได้ง่ายๆ", location: "ที่ตั้ง", locationAddress: "The Magic Table, 25 สุขุมวิทซอย 7/1, คลองเตย, กรุงเทพฯ 10110", locationNote: "เดินเพียง 5 นาทีจากสถานี BTS นานา", hours: "เวลาทำการ", hoursTime: "เปิดทุกวัน: 19:00 น. จนถึงดึก (ประมาณ 02:00 น.)", contact: "ติดต่อ", contactNumber: "+66 81 123 4567", getDirections: "ขอเส้นทาง", footer: "ออกแบบมาสำหรับสถานบันเทิงยามค่ำคืนของกรุงเทพฯ" } }; // --- Custom Hook for Language Context --- const useLanguage = () => { const context = useContext(AppContext); if (!context) { throw new Error('useLanguage must be used within an AppProvider'); } return context; }; // --- App Provider Component --- const AppProvider = ({ children }) => { const [lang, setLang] = useState('en'); const [t, setT] = useState(translations.en); // Function to change language and update state const changeLanguage = useCallback((newLang) => { setLang(newLang); setT(translations[newLang]); localStorage.setItem('magicTableLang', newLang); }, []); // Load language preference from local storage on mount useEffect(() => { const savedLang = localStorage.getItem('magicTableLang'); if (savedLang && translations[savedLang]) { changeLanguage(savedLang); } }, [changeLanguage]); return ( {children} ); }; // --- Firebase Context and Hook (Optimized for Anonymous Auth) --- const FirebaseContext = createContext(); const useFirebase = () => { const context = useContext(FirebaseContext); if (!context) { throw new Error('useFirebase must be used within FirebaseProvider'); } return context; }; const FirebaseProvider = ({ children }) => { const [db, setDb] = useState(null); const [auth, setAuth] = useState(null); const [userId, setUserId] = useState(null); const [isAuthReady, setIsAuthReady] = useState(false); useEffect(() => { let unsubscribe = () => {}; try { if (Object.keys(firebaseConfig).length > 0) { const app = initializeApp(firebaseConfig); const firestore = getFirestore(app); const authService = getAuth(app); setDb(firestore); setAuth(authService); setLogLevel('error'); // Sign in logic const attemptAuth = async () => { try { // FIX: Now calling the correctly imported function if (initialAuthToken) { await signInWithCustomToken(authService, initialAuthToken); } else { await signInAnonymously(authService); } } catch (e) { console.error("Initial Auth Failed, falling back to anonymous:", e); await signInAnonymously(authService); } }; attemptAuth(); unsubscribe = onAuthStateChanged(authService, (user) => { setUserId(user ? user.uid : null); setIsAuthReady(true); }); } else { // Mock ready if Firebase config is missing setUserId(crypto.randomUUID()); setIsAuthReady(true); } } catch (error) { console.error("Firebase Initialization Error:", error); setIsAuthReady(true); // Ensure app proceeds even on error } return () => unsubscribe(); }, []); const value = { db, auth, userId, isAuthReady }; return ( {children} ); }; // --- Utility Components --- const MessageBox = () => { const [message, setMessage] = useState(''); const [type, setType] = useState('success'); const [visible, setVisible] = useState(false); // Expose utility function to window object for global use useEffect(() => { window.showMessage = (msg, msgType = 'success') => { setMessage(msg); setType(msgType); setVisible(true); setTimeout(() => setVisible(false), 4000); }; }, []); if (!visible) return null; const bgColor = type === 'success' ? 'bg-emerald-500' : 'bg-red-500'; return (
{message}
); }; // --- Main App Components --- const Header = () => { const { lang, t, changeLanguage } = useLanguage(); return (

THE MAGIC TABLE

); }; const Hero = () => { const { t } = useLanguage(); return (

{t.subtitle}

{t.title}

{t.tagline}

{t.findGame}
); }; const Features = () => { const { t } = useLanguage(); return (

{t.vibeSubtitle}

{t.vibeTitle}

🎱

{t.featurePool}

{t.featurePoolDesc}

🥂

{t.featureHostess}

{t.featureHostessDesc}

🎵

Cold Drinks & Music

{t.tagline}

); }; const Menu = () => { const { t } = useLanguage(); return ( ); }; const Incentives = () => { const { t } = useLanguage(); const { db, userId, isAuthReady } = useFirebase(); const [email, setEmail] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const handleSignup = useCallback(async (e) => { e.preventDefault(); if (!email || isSubmitting || !isAuthReady) return; setIsSubmitting(true); try { if (!db) { window.showMessage("Database connection error. Try again later.", 'error'); return; } // Path: /artifacts/{appId}/users/{userId}/leads/{documentId} const leadsCollectionRef = doc(db, `artifacts/${appId}/users/${userId}/leads/${crypto.randomUUID()}`); await setDoc(leadsCollectionRef, { email: email, type: 'Hostess_Voucher', timestamp: new Date().toISOString(), // Anonymous UID is used as the user identifier }); window.showMessage("Success! Your voucher code has been saved.", 'success'); setEmail(''); } catch (error) { console.error("Signup Error:", error); window.showMessage("Failed to claim voucher. Try again later.", 'error'); } finally { setIsSubmitting(false); } }, [email, db, userId, isAuthReady]); return (

{t.specialsSubtitle}

{t.specialsTitle}

{/* Pool Special Card */}

{t.poolPackage}

{t.poolDesc}

{t.poolOffer}

{t.poolPriceNote}

{t.poolRedeem}

{/* Hostess Voucher & Lead Form */}

{t.hostessVoucher}

{t.hostessVoucherDesc}

Enter your email to claim:

setEmail(e.target.value)} className="w-full p-3 rounded-lg bg-gray-700 border border-gray-600 focus:ring-pink-500 focus:border-pink-500 text-white" />

Using anonymous sign-in to secure your voucher.

); }; const Events = () => { const { t } = useLanguage(); return (

{t.eventsSubtitle}

{t.eventsTitle}

{/* Event Card 1: Pool Tournament */}

{t.eventPool}

{t.eventPoolTime}

Challenge the best players in the area and show off your skills.

{/* Event Card 2: Ladies' Night */}

{t.eventLadies}

{t.eventLadiesTime}

Bring your friends and start your weekend with the best vibes.

{/* Event Card 3: Weekend DJ */}

Weekend Vibe Sessions

Saturday & Sunday. Live DJ spinning classics and feel-good music.

Keep the party going late into the night with our resident DJ.

); }; const Location = () => { const { t } = useLanguage(); return (
{/* Contact Info */}

{t.locationTitle}

{t.location}

{t.locationAddress}

{t.locationNote}

{t.hours}

{t.hoursTime}

{t.contact}

Call: {t.contactNumber}

{/* Google Map Embed & Directions Link */}
{/* Stable Google Map Embed */}
{/* Direct link for directions */} {t.getDirections}
); }; const Footer = () => { const { t } = useLanguage(); return ( ); }; // --- Main App Component --- const App = () => { // Inject custom styles globally for the neon effect useEffect(() => { const style = document.createElement('style'); style.innerHTML = ` body { font-family: 'Inter', sans-serif; background-color: #0F172A; } .neon-glow { text-shadow: 0 0 5px #22D3EE, 0 0 10px #22D3EE, 0 0 20px #22D3EE; letter-spacing: 0.05em; } `; document.head.appendChild(style); return () => document.head.removeChild(style); }, []); return (
); }; export default App;