import React, { useState, useEffect, useMemo, useRef } from 'react'; import { initializeApp } from 'firebase/app'; import { getFirestore, collection, doc, setDoc, getDoc, onSnapshot, query, updateDoc, addDoc, deleteDoc } from 'firebase/firestore'; import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from 'firebase/auth'; import { Ticket, Clock, MapPin, Smartphone, CheckCircle2, X, ChevronRight, ChevronLeft, Banknote, Check, Plus, Trash2, Printer, Download, Film, Calendar, Building2, Settings as SettingsIcon, QrCode, Phone, Info, Loader2, Save, Edit3 } from 'lucide-react'; // --- FIREBASE SETUP --- const firebaseConfig = JSON.parse(__firebase_config); const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const appId = typeof __app_id !== 'undefined' ? __app_id : 'bioskop-etam-v3'; // --- CONFIG --- const ADMIN_PASSWORD = "etamadmin123"; const App = () => { const [user, setUser] = useState(null); const [view, setView] = useState('user'); const [isAdminLoggedIn, setIsAdminLoggedIn] = useState(false); const [loading, setLoading] = useState(true); // App Data const [locations, setLocations] = useState([]); const [days, setDays] = useState([]); const [movies, setMovies] = useState([]); const [bookings, setBookings] = useState([]); const [config, setConfig] = useState({ price: 35000, totalSeats: 100, qrisLink: "" }); // User Selection const [selectedLocation, setSelectedLocation] = useState(null); const [selectedDay, setSelectedDay] = useState(null); const [activeMovie, setActiveMovie] = useState(null); const [selectedSeats, setSelectedSeats] = useState([]); const [paymentMethod, setPaymentMethod] = useState("QRIS"); const [orderStep, setOrderStep] = useState(0); const [myBookingId, setMyBookingId] = useState(null); const [showTicketModal, setShowTicketModal] = useState(null); // --- INITIALIZATION --- useEffect(() => { const initAuth = async () => { try { if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) { await signInWithCustomToken(auth, __initial_auth_token); } else { await signInAnonymously(auth); } } catch (err) { console.error("Auth error", err); } }; initAuth(); const unsubAuth = onAuthStateChanged(auth, (u) => { setUser(u); setLoading(false); }); return () => unsubAuth(); }, []); // --- DATA FETCHING (FIRESTORE) --- useEffect(() => { if (!user) return; const paths = { locs: collection(db, 'artifacts', appId, 'public', 'data', 'locations'), days: collection(db, 'artifacts', appId, 'public', 'data', 'days'), movies: collection(db, 'artifacts', appId, 'public', 'data', 'movies'), bookings: collection(db, 'artifacts', appId, 'public', 'data', 'bookings') }; const unsubLocs = onSnapshot(paths.locs, (s) => setLocations(s.docs.map(d => ({id: d.id, ...d.data()}))), (e) => console.error(e)); const unsubDays = onSnapshot(paths.days, (s) => setDays(s.docs.map(d => ({id: d.id, ...d.data()}))), (e) => console.error(e)); const unsubMovies = onSnapshot(paths.movies, (s) => setMovies(s.docs.map(d => ({id: d.id, ...d.data()}))), (e) => console.error(e)); const unsubConfig = onSnapshot(doc(db, 'artifacts', appId, 'public', 'data', 'config', 'main'), (s) => { if(s.exists()) setConfig(s.data()); }, (e) => console.error(e)); const unsubBookings = onSnapshot(paths.bookings, (s) => setBookings(s.docs.map(d => ({id: d.id, ...d.data()}))), (e) => console.error(e)); return () => { unsubLocs(); unsubDays(); unsubMovies(); unsubConfig(); unsubBookings(); }; }, [user]); // --- ACTIONS --- const handleBookingSubmit = async (name, phone, refCode) => { if (!user) return; const bookingData = { bookingCode: 'ETM-' + Math.random().toString(36).substr(2, 6).toUpperCase(), customerName: name, customerPhone: phone, paymentRef: refCode || 'CASH_WAITING', locationName: selectedLocation.name, day: selectedDay, movieTitle: activeMovie.title, movieTime: activeMovie.time, seats: selectedSeats, total: selectedSeats.length * config.price, method: paymentMethod, status: 'Pending', createdAt: new Date().toISOString() }; try { const docRef = await addDoc(collection(db, 'artifacts', appId, 'public', 'data', 'bookings'), bookingData); setMyBookingId(docRef.id); setOrderStep(5); } catch (e) { console.error(e); } }; const updateStatus = async (id, status) => { const d = doc(db, 'artifacts', appId, 'public', 'data', 'bookings', id); await updateDoc(d, { status }); }; const deleteItem = async (col, id) => { if(!window.confirm("Hapus item ini?")) return; try { await deleteDoc(doc(db, 'artifacts', appId, 'public', 'data', col, id)); } catch (err) { console.error("Delete error:", err); } }; const saveItemEdit = async (col, id, data) => { try { const d = doc(db, 'artifacts', appId, 'public', 'data', col, id); await updateDoc(d, data); // Feedback visual bisa ditambahkan di sini jika perlu } catch (err) { console.error("Update error:", err); } }; // --- COMPONENTS --- const TicketModal = ({ booking }) => (
E-TICKET RESMI
Booking Code
{booking.bookingCode}
Lokasi
{booking.locationName}
Nama
{booking.customerName}
Hari
{booking.day}
Film
{booking.movieTitle}
Kursi
{booking.seats.join(', ')}
Lunas & Sah
Belum ada lokasi tersedia
} {locations.map(loc => (Lokasi Dipilih
{selectedLocation?.name}
Jadwal belum diatur
} {days.map(d => ( ))}Film belum tersedia
} {movies.map(m => ({m.title}
{m.time} WITA
Konfirmasi Pembayaran
Scan QRIS Berikut
Menunggu Validasi
Sistem sedang memproses pembayaran Anda.
Jangan tutup halaman ini
Tiket Digital akan otomatis muncul di sini.
Jika lebih dari 10 menit tiket belum muncul, segera hubungi Admin melalui tombol bantuan di bawah.
Etam Admin Panel
setPwd(e.target.value)} className="w-full bg-slate-800 border-none p-4 rounded-2xl text-white outline-none mb-4 font-bold text-center tracking-[0.5em]" placeholder="••••••" />Live Transactions
| Customer | Show Info | Status | Aksi |
|---|---|---|---|
| Belum ada transaksi | |||
|
{b.customerName} Ref: {b.paymentRef} |
{b.movieTitle} {b.locationName} | {b.seats?.join(', ')} |
{b.status} | {b.status === 'Pending' && ( )} |
Perubahan otomatis tersimpan saat berpindah input
Manage Schedule
Tekan Luar Kotak Setelah Edit Untuk Menyimpan
System Master
Sistem Realtime
Firestore Database v2.1
