← Back to Main

❄️🌱 LAWN2SNOW

Guest User

Welcome to LAWN2SNOW!

Professional lawn & snow services at your fingertips

💡 Sign in to track your bookings and earn rewards!

Available Services

❄️
Snow Removal
From $40
🌱
Lawn Care
From $35
🏠
House Cleaning
From $80
🔧
Handyman
From $60/hr

Choose Your Service

❄️

Snow Removal

Professional snow clearing for driveways and walkways

From $40
🌱

Lawn Care

Complete lawn maintenance and landscaping services

From $35
🏠

House Cleaning

Professional home cleaning with eco-friendly products

From $80
🔧

Handyman

General repairs and maintenance services

$60/hr

How to Book

💬

Chat Booking

Type your responses to our AI assistant for quick booking

🎙️

Voice Booking

Speak naturally with our voice AI for hands-free booking

📞

Call Direct

Prefer human interaction? Call us at (825) 255-5296

All booking methods available 24/7 • Emergency services available

My Bookings

Please sign in to view your bookings history

Our Service Pricing

Snow Removal

$40+
  • Driveway clearing
  • Walkway shoveling
  • Salt/sand application
  • 24/7 emergency service

Lawn Care

$35+
  • Grass cutting
  • Edge trimming
  • Leaf removal
  • Fertilization available

House Cleaning

$80+
  • Full house cleaning
  • Deep clean options
  • Eco-friendly products
  • Weekly/monthly plans

Handyman

$60/hr
  • General repairs
  • Furniture assembly
  • Minor plumbing/electrical
  • Picture hanging
--

User

--

Account Information

` : ''}
`; } // Update tracker when booking status changes function updateBookingTracker(booking) { const existingTracker = document.getElementById(`tracker-${booking.id}`); if (existingTracker) { existingTracker.outerHTML = renderBookingTracker(booking); } // Show notification for status changes const statusMessages = { 'matching': '🔍 Finding a provider near you...', 'assigned': '✓ Provider assigned! They\'re getting ready.', 'enroute': '🚗 Your provider is on the way!', 'arrived': '📍 Provider has arrived!', 'in_progress': '⚡ Service in progress...', 'completed': '✅ Service completed! Please rate your experience.' }; if (statusMessages[booking.status]) { showNotification(statusMessages[booking.status]); } } // Load and display active bookings with trackers async function loadActiveBookingTrackers() { const userData = JSON.parse(localStorage.getItem('customerUser') || '{}'); if (!userData.phone) return; const bookings = await getActiveBookings(userData.phone); const trackerContainer = document.getElementById('activeTrackersContainer'); if (trackerContainer && bookings.length > 0) { trackerContainer.innerHTML = bookings.map(b => renderBookingTracker(b)).join(''); trackerContainer.style.display = 'block'; // Subscribe to real-time updates for active bookings bookings.forEach(b => subscribeToBooking(b.id)); } else if (trackerContainer) { trackerContainer.style.display = 'none'; } } function showNotification(message) { const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background: var(--primary); color: white; padding: 15px 25px; border-radius: 10px; box-shadow: 0 5px 20px rgba(0,0,0,0.2); z-index: 10000; animation: slideDown 0.3s ease; `; notification.innerHTML = message; document.body.appendChild(notification); setTimeout(() => { notification.style.animation = 'slideUp 0.3s ease'; setTimeout(() => notification.remove(), 300); }, 4000); } function callProvider(phone) { window.location.href = `tel:${phone}`; } // AI Chat Widget function - Connected to your n8n webhook function openAIChat(service) { console.log('Opening AI chat for:', service); // Create chat modal const chatModal = document.createElement('div'); chatModal.id = 'aiChatModal'; chatModal.style.cssText = ` position: fixed; bottom: 20px; right: 20px; width: 380px; height: 600px; background: white; border-radius: 12px; box-shadow: 0 5px 40px rgba(0,0,0,0.3); z-index: 3000; display: flex; flex-direction: column; overflow: hidden; `; // Initialize secure session const sessionId = UTILS.generateSessionId(); let sessionData = { customerData: {}, productSelected: service, bookingStage: 'initial', conversationHistory: [], lastActivity: new Date().toISOString() }; chatModal.innerHTML = `

🤖 Book ${service.charAt(0).toUpperCase() + service.slice(1)} Service

AI Assistant:
Hi! I'm here to help you book ${service === 'snow' ? 'snow removal' : service === 'lawn' ? 'lawn care' : service === 'cleaning' ? 'house cleaning' : 'handyman'} service. What's your name?
`; document.body.appendChild(chatModal); // Add chat functionality connected to n8n document.getElementById('chatForm').addEventListener('submit', async function(e) { e.preventDefault(); const input = document.getElementById('chatInput'); const message = input.value.trim(); if (message) { // Add user message addChatMessage(message, 'user'); input.value = ''; // Send to n8n chat webhook try { const response = await fetch(`${WEBHOOK_BASE}/l2s-chat`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: message, sessionId: sessionId, channel: 'web', productSelected: service, sessionData: sessionData, customerData: sessionData.customerData, timestamp: new Date().toISOString(), isNewConversation: sessionData.conversationHistory.length === 0 }) }); const data = await response.json(); // Update session data if (data.sessionData) { sessionData = data.sessionData; } // Add AI response if (data.response) { addChatMessage(data.response, 'ai'); } // Check if booking is complete if (data.bookingComplete) { setTimeout(() => { alert('Booking confirmed! We will contact you shortly.'); closeChatModal(); if (isLoggedIn) { loadBookings(); } }, 2000); } } catch (error) { console.error('Chat error:', error); // Fallback: Offer to book directly addChatMessage('I apologize for the technical difficulty. Let me help you book directly! Please use the booking form in the "Book Service" tab, or call us at (825) 255-5296 for immediate assistance.', 'ai'); // Show book service button after a delay setTimeout(() => { addChatMessage('', 'ai'); }, 1000); } } }); // Focus on input document.getElementById('chatInput').focus(); } function addChatMessage(message, sender) { const chatMessages = document.getElementById('chatMessages'); const messageDiv = document.createElement('div'); messageDiv.style.cssText = ` background: ${sender === 'user' ? '#27ae60' : 'white'}; color: ${sender === 'user' ? 'white' : 'black'}; padding: 10px; border-radius: 8px; margin-bottom: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); ${sender === 'user' ? 'margin-left: 20%;' : 'margin-right: 20%;'} `; messageDiv.innerHTML = `${sender === 'user' ? 'You' : 'AI Assistant'}:
${message}`; chatMessages.appendChild(messageDiv); chatMessages.scrollTop = chatMessages.scrollHeight; } function closeChatModal() { const modal = document.getElementById('aiChatModal'); if (modal) { modal.remove(); } } // Call Agent function - Connected to Vapi function callAgent(service) { console.log('Calling Vapi agent for:', service); // Create call modal with Vapi integration option const callModal = document.createElement('div'); callModal.id = 'callModal'; callModal.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border-radius: 12px; padding: 30px; box-shadow: 0 5px 40px rgba(0,0,0,0.3); z-index: 3000; text-align: center; min-width: 300px; `; callModal.innerHTML = `

📞 Call Us

Speak directly with our ${service} service team

Call (825) 255-5296

Or use our AI Voice Assistant:

Available 24/7 for emergency services

`; document.body.appendChild(callModal); } // Start Vapi Voice Call function startVapiCall(service) { console.log('Starting Vapi call for:', service); // Close the call modal closeCallModal(); // Create Vapi interface const vapiModal = document.createElement('div'); vapiModal.id = 'vapiModal'; vapiModal.style.cssText = ` position: fixed; bottom: 20px; right: 20px; width: 350px; background: white; border-radius: 12px; box-shadow: 0 5px 40px rgba(0,0,0,0.3); z-index: 3000; padding: 20px; text-align: center; `; vapiModal.innerHTML = `

🎙️ Voice Assistant Active

Connecting to voice service...

Speak clearly to book your ${service} service

`; document.body.appendChild(vapiModal); // Send initial data to Vapi webhook initializeVapiCall(service); } // Track current VAPI session for staff scheduling let currentVapiSession = null; async function initializeVapiCall(service) { const vapiStatus = document.getElementById('vapiStatus'); try { // Generate session ID for tracking const sessionId = 'vapi_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); currentVapiSession = { sessionId: sessionId, service: service, startTime: new Date().toISOString(), customerData: { name: localStorage.getItem('customerName') || '', phone: localStorage.getItem('customerPhone') || '', email: localStorage.getItem('customerEmail') || '' } }; // Initialize Vapi session const response = await fetch(`${WEBHOOK_BASE}/vapi-booking`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'initialize', sessionId: sessionId, productSelected: service, channel: 'voice', needsStaffAssignment: true, customerData: currentVapiSession.customerData }) }); const data = await response.json(); if (response.ok) { vapiStatus.innerHTML = `

✓ Connected

You can now speak to book your service

Session: ${sessionId.slice(-8)}

`; // Start polling for booking completion to trigger staff scheduling startVapiCompletionPolling(sessionId, service); } else { throw new Error('Failed to connect'); } } catch (error) { console.error('Vapi initialization error:', error); vapiStatus.innerHTML = `

Connection failed

Please use the direct phone number instead

`; } } // Poll for VAPI booking completion and trigger staff scheduling let vapiPollingInterval = null; function startVapiCompletionPolling(sessionId, service) { // Clear any existing polling if (vapiPollingInterval) { clearInterval(vapiPollingInterval); } let pollCount = 0; const maxPolls = 60; // 5 minutes max (60 polls * 5 seconds) vapiPollingInterval = setInterval(async () => { pollCount++; if (pollCount > maxPolls) { clearInterval(vapiPollingInterval); console.log('VAPI polling timeout - session may have ended'); return; } try { const response = await fetch(`${WEBHOOK_BASE}/vapi-booking`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'check_status', sessionId: sessionId }) }); const data = await response.json(); if (data.bookingComplete) { clearInterval(vapiPollingInterval); console.log('VAPI booking complete, triggering staff scheduling'); // Trigger staff scheduling await scheduleStaffForVapiBooking(data, service); } } catch (error) { console.error('VAPI status check error:', error); } }, 5000); // Check every 5 seconds } // Schedule staff after VAPI booking completes async function scheduleStaffForVapiBooking(bookingData, service) { const vapiStatus = document.getElementById('vapiStatus'); try { const staffResponse = await fetch(`${WEBHOOK_BASE}/l2s-staff-schedule`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'schedule_staff_for_booking', bookingData: { bookingId: bookingData.bookingId || currentVapiSession?.sessionId || 'vapi_' + Date.now(), service: service, customerName: bookingData.customerName || currentVapiSession?.customerData?.name || '', customerPhone: bookingData.customerPhone || currentVapiSession?.customerData?.phone || '', customerEmail: bookingData.customerEmail || currentVapiSession?.customerData?.email || '', address: bookingData.address || '', date: bookingData.date || bookingData.serviceDate || 'ASAP', time: bookingData.time || bookingData.serviceTime || 'Flexible', notes: bookingData.notes || 'Booked via voice assistant', priority: bookingData.date === 'ASAP' ? 'high' : 'normal', source: 'vapi_voice_booking' } }) }); const staffData = await staffResponse.json(); console.log('Staff scheduling response for VAPI booking:', staffData); if (vapiStatus) { if (staffData.success && staffData.assignedStaff) { vapiStatus.innerHTML = `

✓ Booking Complete!

${staffData.assignedStaff.name || 'A team member'} has been assigned to your service.

`; } else { vapiStatus.innerHTML = `

✓ Booking Complete!

We will assign a team member and contact you shortly.

`; } } // Refresh bookings if logged in if (isLoggedIn) { loadBookings(); } } catch (error) { console.error('Staff scheduling error for VAPI booking:', error); if (vapiStatus) { vapiStatus.innerHTML = `

✓ Booking Received!

We will contact you shortly to confirm your appointment.

`; } } } function endVapiCall() { // Stop polling when call ends if (vapiPollingInterval) { clearInterval(vapiPollingInterval); vapiPollingInterval = null; } // If we have a session, check if booking was completed if (currentVapiSession) { // Final check for completion fetch(`${WEBHOOK_BASE}/vapi-booking`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'end_call', sessionId: currentVapiSession.sessionId, needsStaffAssignment: true }) }).then(response => response.json()) .then(data => { if (data.bookingComplete) { scheduleStaffForVapiBooking(data, currentVapiSession.service); } }).catch(console.error); currentVapiSession = null; } const modal = document.getElementById('vapiModal'); if (modal) { modal.remove(); } } function closeCallModal() { const modal = document.getElementById('callModal'); if (modal) { modal.remove(); } } // Initialize application document.addEventListener('DOMContentLoaded', function() { console.log('Initializing LAWN2SNOW Client Portal'); // Set minimum date for booking form const today = new Date().toISOString().split('T')[0]; const bookingDateInput = document.getElementById('bookingDate'); if (bookingDateInput) { bookingDateInput.setAttribute('min', today); } // Setup all event listeners setupEventListeners(); // Check authentication status checkAuth(); }); // Setup event listeners function setupEventListeners() { console.log('Setting up event listeners'); // Auth button const authBtn = document.getElementById('authButton'); if (authBtn) { authBtn.addEventListener('click', toggleAuth); } // Desktop navigation tabs document.querySelectorAll('.nav-tab').forEach(tab => { tab.addEventListener('click', function() { const tabId = this.id.replace('-nav', ''); switchTab(tabId); }); }); // Mobile navigation document.querySelectorAll('.bottom-nav-item').forEach(item => { item.addEventListener('click', function() { const tabName = this.dataset.tab; switchTab(tabName); }); }); // Service cards document.querySelectorAll('.service-card').forEach(card => { card.addEventListener('click', function() { const service = this.dataset.service; if (service) { bookService(service); } }); }); // Book service buttons document.querySelectorAll('.book-service-btn').forEach(btn => { btn.addEventListener('click', function() { const service = this.dataset.service; if (service) { bookService(service); } }); }); // Book service button in welcome card const bookServiceBtn = document.getElementById('bookServiceBtn'); if (bookServiceBtn) { bookServiceBtn.addEventListener('click', function() { switchTab('book'); }); } // Booking form const bookingForm = document.getElementById('bookingForm'); if (bookingForm) { bookingForm.addEventListener('submit', handleBookingSubmit); } // Profile form const profileForm = document.getElementById('profileForm'); if (profileForm) { profileForm.addEventListener('submit', handleProfileUpdate); } } // Switch tabs function switchTab(tabName) { console.log('Switching to tab:', tabName); // Check authentication for restricted tabs if (!isLoggedIn && ['bookings', 'profile'].includes(tabName)) { alert('Please sign in to access this feature'); return; } // Update desktop nav document.querySelectorAll('.nav-tab').forEach(tab => { tab.classList.remove('active'); }); const activeNav = document.getElementById(tabName + '-nav'); if (activeNav) { activeNav.classList.add('active'); } // Update mobile nav document.querySelectorAll('.bottom-nav-item').forEach(item => { item.classList.remove('active'); if (item.dataset.tab === tabName) { item.classList.add('active'); } }); // Update content document.querySelectorAll('.tab-content').forEach(content => { content.classList.remove('active'); }); const activeContent = document.getElementById(tabName + '-tab'); if (activeContent) { activeContent.classList.add('active'); } // Load tab-specific data if (tabName === 'bookings' && isLoggedIn) { loadBookings(); } } // Toggle authentication function toggleAuth() { console.log('Toggle auth called, isLoggedIn:', isLoggedIn); if (isLoggedIn) { logout(); } else { showLoginModal(); } } // Check authentication async function checkAuth() { const token = localStorage.getItem('userToken'); const customerPhone = localStorage.getItem('customerPhone'); if (token && customerPhone) { // For now, trust the stored token isLoggedIn = true; const userData = { name: localStorage.getItem('customerName') || 'Customer', phone: customerPhone, email: localStorage.getItem('customerEmail') || '' }; updateUI(true, userData); } else { updateUI(false); } } // Update UI based on auth status function updateUI(authenticated, userData = null) { console.log('Updating UI, authenticated:', authenticated); isLoggedIn = authenticated; // Update header document.getElementById('loggedInUser').style.display = authenticated ? 'flex' : 'none'; document.getElementById('guestUser').style.display = authenticated ? 'none' : 'block'; document.getElementById('authButton').textContent = authenticated ? 'Sign Out' : 'Sign In'; // Update dashboard document.getElementById('guestDashboard').style.display = authenticated ? 'none' : 'block'; document.getElementById('loggedInDashboard').style.display = authenticated ? 'block' : 'none'; // Update bookings tab const bookingsAlert = document.getElementById('bookingsLoginAlert'); const bookingsList = document.getElementById('bookingsList'); if (bookingsAlert && bookingsList) { bookingsAlert.style.display = authenticated ? 'none' : 'block'; bookingsList.style.display = authenticated ? 'block' : 'none'; } // Update user info if authenticated if (authenticated && userData) { const userName = userData.name || 'Customer'; const userPhone = userData.phone || ''; const userEmail = userData.email || ''; const initials = userName.split(' ').map(n => n[0]).join('').toUpperCase() || '--'; // Update header info document.getElementById('userName').textContent = userName; document.getElementById('userPhone').textContent = userPhone; document.getElementById('userAvatar').textContent = initials; // Update profile info document.getElementById('profileName').textContent = userName; document.getElementById('profileEmail').textContent = userEmail; document.getElementById('profileAvatar').textContent = initials; document.getElementById('profileFullName').value = userName; document.getElementById('profileEmailInput').value = userEmail; document.getElementById('profilePhone').value = userPhone; // Load user data if (authenticated) { loadBookings(); } } } // Show login modal function showLoginModal() { console.log('Showing login modal'); const modal = document.createElement('div'); modal.id = 'loginModal'; modal.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; z-index: 2000; `; modal.innerHTML = `

Welcome Back!

Sign in to access your account

`; document.body.appendChild(modal); // Add event listeners to modal document.getElementById('loginModalForm').addEventListener('submit', handleLogin); document.getElementById('cancelLoginBtn').addEventListener('click', closeLoginModal); } // Handle login async function handleLogin(e) { e.preventDefault(); console.log('Handling login'); const phone = document.getElementById('loginPhone').value; const password = document.getElementById('loginPassword').value; const submitBtn = document.getElementById('loginSubmitBtn'); submitBtn.disabled = true; submitBtn.textContent = 'Signing in...'; try { const response = await fetch(`${WEBHOOK_BASE}/l2s-customer-auth`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ phone, password }) }); console.log('Login response status:', response.status); const data = await response.json(); console.log('Login response data:', data); if (data.success) { // Store auth data localStorage.setItem('userToken', data.token || data.authToken || 'token-' + Date.now()); localStorage.setItem('customerPhone', phone); localStorage.setItem('customerName', data.name || data.customerName || 'Customer'); localStorage.setItem('customerEmail', data.email || data.customerEmail || ''); localStorage.setItem('customerId', data.customerId || phone); // Update UI isLoggedIn = true; updateUI(true, { name: data.name || data.customerName || 'Customer', phone: phone, email: data.email || data.customerEmail || '' }); // Initialize Supabase and load active booking trackers initSupabaseClient(); loadActiveBookingTrackers(); closeLoginModal(); alert('Login successful!'); } else { alert(data.message || 'Invalid credentials. Please try again.'); submitBtn.disabled = false; submitBtn.textContent = 'Sign In'; } } catch (error) { console.error('Login error:', error); alert('Unable to connect to server. Please try again.'); submitBtn.disabled = false; submitBtn.textContent = 'Sign In'; } } // Close login modal function closeLoginModal() { const modal = document.getElementById('loginModal'); if (modal) { modal.remove(); } } // Logout function logout() { if (confirm('Are you sure you want to sign out?')) { localStorage.removeItem('userToken'); localStorage.removeItem('customerPhone'); localStorage.removeItem('customerName'); localStorage.removeItem('customerEmail'); localStorage.removeItem('customerId'); isLoggedIn = false; updateUI(false); switchTab('dashboard'); } } // Book service function bookService(service) { console.log('Booking service:', service); switchTab('book'); document.getElementById('serviceSelect').value = service; } // Handle booking submission async function handleBookingSubmit(e) { e.preventDefault(); console.log('Submitting booking'); const booking = { service: document.getElementById('serviceSelect').value, name: document.getElementById('bookingName').value, phone: document.getElementById('bookingPhone').value, email: document.getElementById('bookingEmail').value, address: document.getElementById('bookingAddress').value, date: document.getElementById('bookingDate').value, time: document.getElementById('bookingTime').value, notes: document.getElementById('bookingNotes').value, token: localStorage.getItem('userToken') || null }; try { // Use staff-schedule endpoint directly - it saves booking data correctly const bookingId = 'BK' + Date.now() + Math.random().toString(36).substr(2, 5).toUpperCase(); const response = await fetch(`${WEBHOOK_BASE}/l2s-staff-schedule`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'schedule_staff_for_booking', bookingData: { bookingId: bookingId, service: booking.service, customerName: booking.name, customerPhone: booking.phone, customerEmail: booking.email, address: booking.address, date: booking.date, time: booking.time, notes: booking.notes, priority: booking.date === 'ASAP' ? 'high' : 'normal', source: 'client_portal_form' } }) }); const data = await response.json(); console.log('Booking response:', data); if (data.success) { if (data.assignedStaff && data.assignedStaff.name) { alert(`Booking confirmed! ${data.assignedStaff.name} has been assigned to your ${booking.service} service on ${booking.date}.`); } else { alert(`Booking submitted successfully! Your ${booking.service} service is scheduled for ${booking.date}. We will assign a team member and contact you shortly.`); } document.getElementById('bookingForm').reset(); if (isLoggedIn) { switchTab('bookings'); loadBookings(); } } else { alert(data.message || 'Booking failed. Please try again.'); } } catch (error) { console.error('Booking error:', error); alert('Unable to submit booking. Please try again.'); } } // Handle profile update async function handleProfileUpdate(e) { e.preventDefault(); console.log('Updating profile'); const profileData = { name: document.getElementById('profileFullName').value, email: document.getElementById('profileEmailInput').value, phone: document.getElementById('profilePhone').value, address: document.getElementById('profileAddress').value, customerId: localStorage.getItem('customerPhone') || localStorage.getItem('customerId'), token: localStorage.getItem('userToken') }; // Show loading state const submitBtn = e.target.querySelector('button[type="submit"]'); const originalText = submitBtn.textContent; submitBtn.disabled = true; submitBtn.textContent = 'Updating...'; try { const response = await fetch(`${WEBHOOK_BASE}/l2s-update-customer`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(profileData) }); const data = await response.json(); if (data.success || response.ok) { // Update local storage localStorage.setItem('customerName', profileData.name); localStorage.setItem('customerEmail', profileData.email); localStorage.setItem('customerPhone', profileData.phone); // Update UI updateUI(true, { name: profileData.name, phone: profileData.phone, email: profileData.email }); alert('Profile updated successfully!'); submitBtn.textContent = originalText; submitBtn.disabled = false; } else { throw new Error(data.message || 'Update failed'); } } catch (error) { console.error('Profile update error:', error); // Even if webhook fails, update locally for testing localStorage.setItem('customerName', profileData.name); localStorage.setItem('customerEmail', profileData.email); localStorage.setItem('customerPhone', profileData.phone); updateUI(true, { name: profileData.name, phone: profileData.phone, email: profileData.email }); alert('Profile updated locally. Server sync pending.'); submitBtn.textContent = originalText; submitBtn.disabled = false; } } // Load bookings async function loadBookings() { console.log('Loading bookings'); const token = localStorage.getItem('userToken'); const phone = localStorage.getItem('customerPhone'); if (!token && !phone) return; try { const response = await fetch(`${WEBHOOK_BASE}/l2s-customer-bookings`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token: token, customerId: phone || 'guest' }) }); const data = await response.json(); const bookingsList = document.getElementById('bookingsList'); const recentBookings = document.getElementById('recentBookings'); if (data.success && data.bookings && data.bookings.length > 0) { const bookingsHTML = data.bookings.map(booking => `

${booking.service || booking.Service_Type || 'Service'}

Date: ${booking.date || booking.Service_Date || 'TBD'}

Time: ${booking.time || booking.Service_Time || 'TBD'}

Address: ${booking.address || booking.Service_Address || 'Your Address'}

${booking.status || booking.Status || 'Pending'}
`).join(''); if (bookingsList) { bookingsList.innerHTML = `

Your Bookings

${bookingsHTML}`; } if (recentBookings) { recentBookings.innerHTML = bookingsHTML; } } else { if (bookingsList) { bookingsList.innerHTML = `
📋

No bookings found

`; } } } catch (error) { console.error('Error loading bookings:', error); } } // Log that script loaded successfully console.log('LAWN2SNOW Client Portal script loaded successfully');