function sanitizeHTML(str) { var temp = document.createElement('div'); temp.textContent = str; return temp.innerHTML; } // Firebase Configuration const firebaseConfig = { apiKey: "AIzaSyDO0aES6rW38p1NPJp26ylEkZf0x0Rp3JI", authDomain: "bus-manage-437a8.firebaseapp.com", projectId: "bus-manage-437a8", storageBucket: "bus-manage-437a8.firebasestorage.app", messagingSenderId: "827423480903", appId: "1:827423480903:web:7a514468dce182c7fb6562", measurementId: "G-WEN616B6JV" }; // Initialize Firebase firebase.initializeApp(firebaseConfig); const auth = firebase.auth(); const db = firebase.firestore(); // Global State let currentUser = null; let selectedBus = null; let selectedSeats = []; let busesSnapshot = null; // Constants const SEAT_LAYOUT = { regularRows: 10, leftSeatsPerRow: 2, rightSeatsPerRow: 3, backRowSeats: 6, totalSeats: 56 }; // Initialize App auth.onAuthStateChanged(async (user) => { if (user) { currentUser = user; await loadUserProfile(); showApp(); } else { showAuth(); } }); // Auth Functions function showAuth() { document.getElementById('authContainer').classList.remove('hidden'); document.getElementById('appContainer').classList.add('hidden'); } function showApp() { document.getElementById('authContainer').classList.add('hidden'); document.getElementById('appContainer').classList.remove('hidden'); document.getElementById('userDisplayName').textContent = sanitizeHTML(currentUser.displayName || currentUser.email); } function showLogin() { document.getElementById('loginForm').classList.remove('hidden'); document.getElementById('signupForm').classList.add('hidden'); } function showSignup() { document.getElementById('loginForm').classList.add('hidden'); document.getElementById('signupForm').classList.remove('hidden'); } async function handleLogin() { const email = document.getElementById('loginEmail').value.trim(); const password = document.getElementById('loginPassword').value; if (!email || !password) { // Strong password check const strongPwd = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$/; if (!strongPwd.test(password)) { showNotification('Password must be 8+ chars with uppercase, lowercase, digit, symbol.', 'error'); return; } showNotification('Please fill in all fields', 'error'); return; } showLoading(true); try { await auth.signInWithEmailAndPassword(email, password); showNotification('Login successful!', 'success'); } catch (error) { showNotification(error.message, 'error'); } finally { showLoading(false); } } async function handleSignup() { const name = document.getElementById('signupName').value.trim(); const email = document.getElementById('signupEmail').value.trim(); const password = document.getElementById('signupPassword').value; const role = document.getElementById('signupRole').value; if (!name || !email || !password) { // Strong password check const strongPwd = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$/; if (!strongPwd.test(password)) { showNotification('Password must be 8+ chars with uppercase, lowercase, digit, symbol.', 'error'); return; } showNotification('Please fill in all fields', 'error'); return; } if (password.length < 6) { showNotification('Password must be at least 6 characters', 'error'); return; } showLoading(true); try { const userCredential = await auth.createUserWithEmailAndPassword(email, password); await userCredential.user.updateProfile({ displayName: name }); // Create user document await db.collection('users').doc(userCredential.user.uid).set({ displayName: name, email: email, role: role, createdAt: firebase.firestore.FieldValue.serverTimestamp() }); showNotification('Account created successfully!', 'success'); } catch (error) { showNotification(error.message, 'error'); } finally { showLoading(false); } } async function handleLogout() { try { await auth.signOut(); showNotification('Logged out successfully', 'success'); resetAppState(); } catch (error) { showNotification(error.message, 'error'); } } // User Profile async function loadUserProfile() { try { const userDoc = await db.collection('users').doc(currentUser.uid).get(); if (userDoc.exists) { const userData = userDoc.data(); if (userData.role === 'admin') { showAdminDashboard(); } else { showBusList(); } } else { showBusList(); } } catch (error) { console.error('Error loading user profile:', error); showBusList(); } } // Navigation function showAdminDashboard() { hideAllViews(); document.getElementById('adminDashboard').classList.remove('hidden'); loadAdminDashboard(); } function showBusList() { hideAllViews(); document.getElementById('busSelection').classList.remove('hidden'); loadBuses(); } function showSeatSelection(bus) { hideAllViews(); selectedBus = bus; selectedSeats = []; document.getElementById('seatSelection').classList.remove('hidden'); document.getElementById('selectedBusName').textContent = bus.busName; document.getElementById('selectedBusInfo').textContent = `Departure: ${formatDateTime(bus.departureDateTime)} | Destination: ${bus.destinationName}`; loadSeatMap(); } function showBookingForm() { hideAllViews(); document.getElementById('bookingForm').classList.remove('hidden'); generatePassengerForms(); updateFinalSummary(); } function showConfirmation(bookingData) { hideAllViews(); document.getElementById('bookingConfirmation').classList.remove('hidden'); displayConfirmation(bookingData); } function hideAllViews() { document.getElementById('adminDashboard').classList.add('hidden'); document.getElementById('busSelection').classList.add('hidden'); document.getElementById('seatSelection').classList.add('hidden'); document.getElementById('bookingForm').classList.add('hidden'); document.getElementById('bookingConfirmation').classList.add('hidden'); } function backToBusList() { selectedBus = null; selectedSeats = []; showBusList(); } function backToSeatSelection() { showSeatSelection(selectedBus); } // Admin Dashboard async function loadAdminDashboard() { showLoading(true); try { // Load statistics const busesSnapshot = await db.collection('buses').get(); const bookingsSnapshot = await db.collection('bookings').where('status', '==', 'confirmed').get(); const totalBuses = busesSnapshot.size; const totalBookings = bookingsSnapshot.size; let totalSeats = 0; let bookedSeats = 0; busesSnapshot.forEach(doc => { totalSeats += SEAT_LAYOUT.totalSeats; }); bookingsSnapshot.forEach(doc => { bookedSeats += doc.data().seatNumbers.length; }); document.getElementById('totalBuses').textContent = totalBuses; document.getElementById('totalBookings').textContent = totalBookings; document.getElementById('availableSeats').textContent = totalSeats - bookedSeats; document.getElementById('totalRevenue').textContent = '-'; // Load buses for admin loadAdminBuses(); // Load report bus select loadReportBusSelect(); } catch (error) { console.error('Error loading admin dashboard:', error); showNotification('Error loading dashboard', 'error'); } finally { showLoading(false); } } async function loadAdminBuses() { const container = document.getElementById('adminBusList'); container.innerHTML = '
Loading buses...
'; try { const snapshot = await db.collection('buses').orderBy('departureDateTime', 'asc').get(); if (snapshot.empty) { container.innerHTML = 'No buses added yet. Click "Add New Bus" to get started.
'; return; } container.innerHTML = ''; for (const doc of snapshot.docs) { const bus = { id: doc.id, ...doc.data() }; const bookedSeats = await getBookedSeatsCount(bus.id); const availableSeats = SEAT_LAYOUT.totalSeats - bookedSeats; const card = document.createElement('div'); card.className = 'bus-card'; card.innerHTML = `${bus.destinationName}
Error loading buses
'; } } async function loadReportBusSelect() { const select = document.getElementById('reportBusSelect'); select.innerHTML = ''; try { const snapshot = await db.collection('buses').orderBy('departureDateTime', 'asc').get(); snapshot.forEach(doc => { const bus = doc.data(); const option = document.createElement('option'); option.value = doc.id; option.textContent = `${bus.busName} - ${formatDateTime(bus.departureDateTime)}`; select.appendChild(option); }); } catch (error) { console.error('Error loading bus select:', error); } } // Bus Management async function loadBuses() { const container = document.getElementById('busList'); container.innerHTML = 'Loading available buses...
'; try { const now = new Date(); const snapshot = await db.collection('buses') .orderBy('departureDateTime', 'asc') .get(); if (snapshot.empty) { container.innerHTML = 'No buses available at the moment. Please check back later.
'; return; } container.innerHTML = ''; for (const doc of snapshot.docs) { const bus = { id: doc.id, ...doc.data() }; const departureDate = new Date(bus.departureDateTime); if (departureDate < now) continue; // Skip past buses const bookedSeats = await getBookedSeatsCount(bus.id); const availableSeats = SEAT_LAYOUT.totalSeats - bookedSeats; const card = document.createElement('div'); card.className = 'bus-card'; card.onclick = () => availableSeats > 0 && showSeatSelection(bus); card.style.cursor = availableSeats > 0 ? 'pointer' : 'not-allowed'; card.style.opacity = availableSeats > 0 ? '1' : '0.6'; card.innerHTML = `${bus.destinationName}
Error loading buses. Please try again.
'; } } function showAddBusModal() { document.getElementById('addBusModal').classList.remove('hidden'); // Set minimum date to today const now = new Date(); const dateStr = now.toISOString().slice(0, 16); document.getElementById('departureDateTime').min = dateStr; } function closeAddBusModal() { document.getElementById('addBusModal').classList.add('hidden'); document.getElementById('busName').value = ''; document.getElementById('driverName').value = ''; document.getElementById('departureDateTime').value = ''; } async function addNewBus() { const busName = document.getElementById('busName').value.trim(); const driverName = document.getElementById('driverName').value.trim(); const departureDateTime = document.getElementById('departureDateTime').value; const destination = document.getElementById('destination').value.trim(); if (!busName || !departureDateTime || !destination) { showNotification('Please fill in all required fields', 'error'); return; } showLoading(true); try { await db.collection('buses').add({ busName, driverName, departureDateTime, destinationName: destination, totalSeats: SEAT_LAYOUT.totalSeats, createdBy: currentUser.uid, createdAt: firebase.firestore.FieldValue.serverTimestamp() }); showNotification('Bus added successfully!', 'success'); closeAddBusModal(); loadAdminDashboard(); } catch (error) { console.error('Error adding bus:', error); showNotification('Error adding bus. Please try again.', 'error'); } finally { showLoading(false); } } // Seat Management async function loadSeatMap() { const container = document.getElementById('seatMap'); container.innerHTML = 'Loading seat layout...
'; try { const bookedSeats = await getBookedSeats(selectedBus.id); container.innerHTML = ''; let seatNumber = 1; // Regular rows (10 rows with 2+3 layout) for (let row = 0; row < SEAT_LAYOUT.regularRows; row++) { const rowDiv = document.createElement('div'); rowDiv.className = 'seat-row'; // Left side (2 seats) const leftGroup = document.createElement('div'); leftGroup.className = 'seat-group'; for (let i = 0; i < SEAT_LAYOUT.leftSeatsPerRow; i++) { leftGroup.appendChild(createSeat(seatNumber++, bookedSeats)); } rowDiv.appendChild(leftGroup); // Aisle const aisle = document.createElement('div'); aisle.className = 'aisle'; rowDiv.appendChild(aisle); // Right side (3 seats) const rightGroup = document.createElement('div'); rightGroup.className = 'seat-group'; for (let i = 0; i < SEAT_LAYOUT.rightSeatsPerRow; i++) { rightGroup.appendChild(createSeat(seatNumber++, bookedSeats)); } rowDiv.appendChild(rightGroup); container.appendChild(rowDiv); } // Back row (6 seats) const backRow = document.createElement('div'); backRow.className = 'back-row'; for (let i = 0; i < SEAT_LAYOUT.backRowSeats; i++) { backRow.appendChild(createSeat(seatNumber++, bookedSeats)); } container.appendChild(backRow); updateSeatSummary(); } catch (error) { console.error('Error loading seat map:', error); container.innerHTML = 'Error loading seat layout
'; } } function createSeat(number, bookedSeats) { const seat = document.createElement('div'); seat.className = 'seat'; seat.textContent = number; seat.dataset.seatNumber = number; if (bookedSeats.includes(number)) { seat.classList.add('booked'); } else { seat.classList.add('available'); seat.onclick = () => toggleSeat(number); } return seat; } function toggleSeat(seatNumber) { const seatElement = document.querySelector(`[data-seat-number="${seatNumber}"]`); if (selectedSeats.includes(seatNumber)) { selectedSeats = selectedSeats.filter(s => s !== seatNumber); seatElement.classList.remove('selected'); seatElement.classList.add('available'); } else { selectedSeats.push(seatNumber); seatElement.classList.remove('available'); seatElement.classList.add('selected'); } selectedSeats.sort((a, b) => a - b); updateSeatSummary(); } function updateSeatSummary() { const display = document.getElementById('selectedSeatsDisplay'); const count = document.getElementById('totalSeatsSelected'); const button = document.getElementById('proceedBtn'); if (selectedSeats.length > 0) { display.textContent = selectedSeats.join(', '); count.textContent = selectedSeats.length; button.disabled = false; } else { display.textContent = 'None'; count.textContent = '0'; button.disabled = true; } } function proceedToBooking() { if (selectedSeats.length === 0) { showNotification('Please select at least one seat', 'warning'); return; } showBookingForm(); } // Booking Functions function generatePassengerForms() { const container = document.getElementById('passengerForms'); container.innerHTML = ''; selectedSeats.forEach((seatNumber, index) => { const form = document.createElement('div'); form.className = 'passenger-form'; form.innerHTML = `Booking ID: ${bookingData.bookingId}
Bus: ${bookingData.busName}
Departure: ${formatDateTime(bookingData.departureDateTime)}
Seats: ${bookingData.seatNumbers.join(', ')}
Passengers: ${bookingData.passengers.length}
Payment Method: ${bookingData.paymentMethod}
No bookings for this bus yet.
'; } else { let html = '| Booking ID | Seats | Passengers | Payment | Booked By | Date | '; html += '
|---|---|---|---|---|---|
| ${booking.bookingId.substring(0, 8)}... | `; html += `${booking.seatNumbers.join(', ')} | `; html += `${booking.passengers.map(p => p.name).join(', ')} | `; html += `${booking.paymentMethod} | `; html += `${booking.bookedByName} | `; html += `${booking.bookedAt ? new Date(booking.bookedAt.toDate()).toLocaleDateString() : 'N/A'} | `; html += '
Departure: ${formatDateTime(bus.departureDateTime)}
`; if (bookingsSnapshot.empty) { html += 'No passengers booked yet.
'; } else { html += '| Seat | Name | Phone | Payment | '; html += '|
|---|---|---|---|---|
| ${p.seatNumber} | `; html += `${p.name} | `; html += `${p.phone} | `; html += `${p.email || '-'} | `; html += `${p.paymentMethod} | `; html += '
Departure: ${formatDateTime(bus.departureDateTime)}
`; html += '| Payment Method | Bookings | Seats | '; html += '
|---|---|---|
| Cash | '; html += `${cashCount} | `; html += `${cashSeats} | `; html += '
| UPI | '; html += `${upiCount} | `; html += `${upiSeats} | `; html += '
| Total | '; html += `${cashCount + upiCount} | `; html += `${cashSeats + upiSeats} | `; html += '