🎁 30 days full access — no card required. Use MyBungalow forever — upgrade when your business grows.

Owner login

Sign in with your password or receive a WhatsApp OTP.

New here? Create a bungalow

// AJAX submit for hotel service form: keep user in-place and prepend new card (function(){ function initServiceFormAjax(){ var form = document.getElementById('hotelServiceForm'); var list = document.getElementById('hotelServiceList'); if(!form || !list) return; form.addEventListener('submit', async function(evt){ evt.preventDefault(); var submitBtn = form.querySelector('button[type="submit"]'); if(submitBtn) submitBtn.disabled = true; try{ var fd = new FormData(form); // Mark as AJAX explicitly (server also checks X-Requested-With) fd.append('hotel_ajax','1'); // Preserve hotel slug in URL var hotelSlug = new URL(window.location.href).searchParams.get('hotel') || window.currentHotelSlug || ''; var submitUrl = window.location.href; if(hotelSlug && !submitUrl.includes('?hotel=')){ submitUrl = submitUrl + (submitUrl.includes('?') ? '&' : '?') + 'hotel=' + encodeURIComponent(hotelSlug); } var resp = await fetch(submitUrl, { method: 'POST', body: fd, headers: { 'X-Requested-With': 'XMLHttpRequest' } }); if(!resp.ok){ throw new Error('Server error: ' + resp.status); } var ct = resp.headers.get('content-type') || ''; if(ct.indexOf('application/json') === -1){ var text = await resp.text(); console.error('Service AJAX non-JSON response:', text.slice(0,200)); throw new Error('Unexpected response. Please try again.'); } var data = await resp.json(); if(!data || data.ok !== true){ throw new Error((data && data.error) ? data.error : 'Could not save service charge'); } if(!data.html){ throw new Error('Missing entry HTML in response'); } // Insert new card right after the section header, or at top var header = list.querySelector('h4'); if(header){ header.insertAdjacentHTML('afterend', data.html); } else { list.insertAdjacentHTML('afterbegin', data.html); } // Reset form values form.reset(); var bookingInput = document.getElementById('serviceBookingInput'); if(bookingInput) bookingInput.value = ''; var bookingHidden = document.getElementById('service_booking_id_hidden'); if(bookingHidden) bookingHidden.value = ''; var serviceInput = form.querySelector('input[name="service_label"]'); if(serviceInput) serviceInput.value = ''; var totalDisplay = form.querySelector('[data-service-total]'); if(totalDisplay) totalDisplay.textContent = '₹0.00'; // Success toast var toast = document.createElement('div'); toast.textContent = 'Service charge saved.'; toast.style.cssText = 'position:fixed;top:18px;left:50%;transform:translateX(-50%);z-index:200000;padding:10px 14px;background:#16a34a;color:#fff;border-radius:999px;font-size:14px;font-weight:500'; document.body.appendChild(toast); setTimeout(function(){ toast.remove(); }, 2000); }catch(err){ console.error('Service AJAX error:', err); if(window.hotelAlert){ window.hotelAlert(err.message || 'Error saving service charge', 'error'); } else { alert(err.message || 'Error saving service charge'); } }finally{ if(submitBtn) submitBtn.disabled = false; } }); } if(document.readyState === 'loading'){ document.addEventListener('DOMContentLoaded', initServiceFormAjax); } else { initServiceFormAjax(); } })(); } if (isset($_GET['forgot'])) { if (!empty($_POST['owner_reset_reset'])) { unset($_SESSION['reset_otp'], $_SESSION['reset_stage'], $_SESSION['reset_info']); $_SESSION['reset_old'] = ['mobile' => trim((string)($_POST['owner_mobile'] ?? ''))]; header('Location:?forgot=1'); exit; } if (!empty($_POST['owner_reset_send_otp'])) { $mobile = trim((string)($_POST['owner_mobile'] ?? '')); $_SESSION['reset_old'] = ['mobile' => $mobile]; if ($mobile === '') { $_SESSION['reset_error'] = 'Mobile is required to request OTP.'; $_SESSION['reset_stage'] = 'start'; } elseif (!preg_match('/^\+?[0-9\-\s]{6,20}$/', $mobile)) { $_SESSION['reset_error'] = 'Enter a valid mobile number before requesting OTP.'; $_SESSION['reset_stage'] = 'start'; } else { [$sent, $msg] = password_reset_send_otp($mobile); if ($sent) { $_SESSION['reset_stage'] = 'otp_sent'; } else { unset($_SESSION['reset_info']); $_SESSION['reset_error'] = $msg; $_SESSION['reset_stage'] = 'start'; } } header('Location:?forgot=1'); exit; } if (!empty($_POST['owner_reset_verify_otp'])) { $mobile = trim((string)($_POST['owner_mobile'] ?? '')); $otpInput = (string)($_POST['reset_otp_code'] ?? ''); $newPassword = (string)($_POST['new_password'] ?? ''); $confirmPassword = (string)($_POST['new_password_confirm'] ?? ''); $_SESSION['reset_old'] = ['mobile' => $mobile]; $mobileNorm = whatsapp_normalize_phone($mobile); $otpState = $_SESSION['reset_otp'] ?? null; $otpDigits = preg_replace('/\D+/', '', $otpInput); if ($mobile === '') { $_SESSION['reset_error'] = 'Mobile is required.'; $_SESSION['reset_stage'] = 'start'; } elseif (!preg_match('/^\+?[0-9\-\s]{6,20}$/', $mobile)) { $_SESSION['reset_error'] = 'Enter a valid mobile number before entering the OTP.'; $_SESSION['reset_stage'] = 'start'; } elseif ($mobileNorm === null) { $_SESSION['reset_error'] = 'Enter a valid mobile number before entering the OTP.'; $_SESSION['reset_stage'] = 'start'; } elseif (!$otpState) { $_SESSION['reset_error'] = 'Please tap "Send OTP" first.'; $_SESSION['reset_stage'] = 'start'; } elseif (($otpState['mobile_norm'] ?? null) !== $mobileNorm) { unset($_SESSION['reset_otp']); $_SESSION['reset_error'] = 'Mobile number changed. Please request a new OTP.'; $_SESSION['reset_stage'] = 'start'; } elseif ($otpDigits === '') { $_SESSION['reset_error'] = 'Enter the 4-digit OTP.'; $_SESSION['reset_stage'] = 'otp_sent'; } elseif (($otpState['expires'] ?? 0) < time()) { unset($_SESSION['reset_otp']); $_SESSION['reset_error'] = 'OTP expired. Please request a new code.'; $_SESSION['reset_stage'] = 'start'; } elseif ($otpDigits !== (string)$otpState['code']) { unset($_SESSION['reset_info']); $_SESSION['reset_error'] = 'Incorrect OTP. Please check the code sent on WhatsApp.'; $_SESSION['reset_stage'] = 'otp_sent'; } elseif (strlen($newPassword) < 8) { $_SESSION['reset_error'] = 'Password must be at least 8 characters long.'; $_SESSION['reset_stage'] = 'otp_sent'; } elseif (!preg_match('/[A-Z]/', $newPassword)) { $_SESSION['reset_error'] = 'Password must include at least one uppercase letter.'; $_SESSION['reset_stage'] = 'otp_sent'; } elseif (!preg_match('/[a-z]/', $newPassword)) { $_SESSION['reset_error'] = 'Password must include at least one lowercase letter.'; $_SESSION['reset_stage'] = 'otp_sent'; } elseif (!preg_match('/[0-9]/', $newPassword)) { $_SESSION['reset_error'] = 'Password must include at least one number.'; $_SESSION['reset_stage'] = 'otp_sent'; } elseif ($newPassword !== $confirmPassword) { $_SESSION['reset_error'] = 'Passwords do not match. Re-enter the new password.'; $_SESSION['reset_stage'] = 'otp_sent'; } else { $records = []; $infos = is_array($otpState['bungalows'] ?? null) ? $otpState['bungalows'] : []; foreach ($infos as $info) { $bid = (int)($info['id'] ?? 0); if ($bid <= 0) { continue; } $st = pdo()->prepare('SELECT * FROM bungalows WHERE id = ?'); $st->execute([$bid]); $row = $st->fetch(PDO::FETCH_ASSOC); if ($row) { $records[] = $row; } } if (!$records) { unset($_SESSION['reset_otp']); $_SESSION['reset_error'] = 'No active bungalow uses this mobile number. Please contact support.'; $_SESSION['reset_stage'] = 'start'; } else { $hash = password_hash($newPassword, PASSWORD_DEFAULT); $update = pdo()->prepare('UPDATE bungalows SET admin_password_hash=? WHERE id=?'); foreach ($records as &$row) { $id = (int)($row['id'] ?? 0); if ($id > 0) { $update->execute([$hash, $id]); $row['admin_password_hash'] = $hash; $_SESSION['bungadmin_'.$id] = true; owner_remember_login($row); } } unset($row); $primary = $records[0]; $slug = (string)($primary['slug'] ?? ''); if ($slug === '') { unset($_SESSION['reset_otp']); $_SESSION['reset_error'] = 'Could not complete login for this account. Please contact support.'; $_SESSION['reset_stage'] = 'start'; } else { unset($_SESSION['reset_otp'], $_SESSION['reset_stage'], $_SESSION['reset_info'], $_SESSION['reset_old']); $_SESSION['toast_success'] = 'Password reset successful. You are now logged in.'; header('Location:?bungalow='.rawurlencode($slug).'&admin=1'); exit; } } } header('Location:?forgot=1'); exit; } $title = 'Reset password | MyBungalow'; $metaDescription = 'Reset your MyBungalow owner password securely with a WhatsApp OTP and regain access to your dashboard.'; $metaKeywords = 'reset password, whatsapp otp reset, mybungalow owner account'; $bodyClass = 'saas-classic app-auth'; $extraHead = ''; require_once __DIR__ . '/../includes/header.php'; $resetStage = $_SESSION['reset_stage'] ?? 'start'; $resetOtpState = $_SESSION['reset_otp'] ?? null; $resetMobile = $_SESSION['reset_old']['mobile'] ?? ($resetOtpState['mobile_display'] ?? ''); $resetMasked = $resetOtpState ? mask_phone_display((string)$resetOtpState['mobile_display']) : ($resetMobile !== '' ? mask_phone_display($resetMobile) : ''); ?>

Reset password

We'll send a WhatsApp OTP to verify your number before you pick a new password.

We'll send a 4-digit OTP to this mobile number. You can request it up to 3 times every 15 minutes.

Remembered your password? Back to login