Dragon Boat Festival in Vancouver Introduction to the Vancouver Dragon Boat Festival The Vancouver Dragon Boat Festival is a vibrant cultural event that pulses with energy, tradition, and competition. Held annually at the scenic False Creek, near the heart of downtown Vancouver, this festival not only showcases the thrilling sport of dragon boat racing but …
Ah, Vancouver! One of Canada’s most scenic spots, bustling with culture and nature, making it a perfect getaway for solo travelers. Summer in Vancouver, with its mild weather and long, sunny days, offers you an inviting backdrop to explore this vibrant city at your own pace. Whether you’re into the outdoors, gastronomy, or arts and …
Introduction to Vancouver’s Vibrant Weekend Scene As the cherry blossoms fade and the city blooms into full spring fervor, Vancouver’s weekend scene comes alive with an array of events and activities suitable for everyone. From cultural festivals and lively parades to exclusive craft fairs and engaging sports, the city is buzzing with excitement. Whether you’re …
In an awe-inspiring leap towards conserving biodiversity, scientists in the U.S. have pioneered cloning techniques with the successful birth of two black-footed ferrets, a species teetering on the brink of extinction. This achievement not only marks a glimmer of hope for the species, with only 300 individuals remaining in the wild, but also sets a …
😮💼 Each pod offers a cozy bed, lighting, and modern amenities, creating a blend of efficiency and comfort. Discover the two main players: Panda Pod Hotel in Richmond and PangeaPod in Whistler. Whether you seek adventure or convenience, these hotels cater to your needs. 💸 Traveling on a budget? Don’t miss out on money-saving tips …
Ultimate Spring Hiking Guide in Vancouver Image courtesy: Unsplash Vancouver’s Scenic Trails Let’s embark on a journey through Vancouver’s breathtaking landscapes, where every trail tells a story and every step brings a new adventure. Nestled between the Pacific Ocean and the Coast Mountains, Vancouver offers a hiking paradise for enthusiasts of all levels. From the …
Embarking on your first journey in the lavish realms of first and business class can be an exhilarating experience. With their promise of indulgence, relaxation, and superior service, these premium cabins offer more than just a travel service; they are an experience in themselves. However, stepping into this world of luxury comes with its unspoken …
🚗 By Car: Zoom to the park in 20-25 mins, taking Taylor Way exit for a direct route. Scenic drive takes 30-35 mins. 🚌 Public Transit: Bus 246 gets you there in 45 mins for $3.05. Bus 250 offers a scenic ride in 55 mins. 🚲 Cycling: For the adventurous, cycle there in 1.5 hours …
Exploring the nomadic lifestyle brings freedom but can feel lonely. Here are top tips for building connections: 💃 Attend Local Events: Dive into food festivals and cultural events to mingle and embrace local traditions. 📸 Visit Tourist Attractions: Meet fellow nomads at iconic spots. Don’t miss our guide on Vancouver’s attractions! 👥 **Join Social Media …
ultimate spring hiking guide in vancouver Ultimate Spring Hiking Guide in Vancouver Image courtesy: Unsplash Vancouver’s Scenic Trails Let’s embark on a journey through Vancouver’s breathtaking landscapes, where every trail tells a story and every step brings a new adventure. Nestled between the Pacific Ocean and the Coast Mountains, Vancouver offers a hiking paradise for …
${success ? "" : `Contact Us on WhatsApp`}`;
}
function showSameDayClosedMessage() {
const box = $("#ppc5-availability-result");
box.className = "ppc5-result ppc5-result--error";
box.innerHTML = resultCard("Same-Day Booking Closed", "It is after 6:00 PM in Vancouver. Please contact us on WhatsApp, mention that you are a crew member, and ask for Front Desk.");
box.focus();
}
function availabilityFailure(result) {
if (result.nights.some(night => night.crewAllocation === 0)) return ["Crew Offer Unavailable for Selected Date", "Crew self-booking is not available for one or more selected dates. Please contact us on WhatsApp for alternate availability."];
if (result.nights.some(night => night.crewRemaining === 0)) return ["Crew Allocation Reached", "The crew allocation for one or more selected dates has been reached. Please contact us and we will check whether additional pods can be offered."];
return ["Crew Availability Is Not Available", "Crew availability is not available for one or more selected dates. Please contact us and we will check other options."];
}
async function runAvailability(advance) {
if (!validateStep(1)) return false;
const data = getFormData();
const pod = selectedPod();
const box = $("#ppc5-availability-result");
const button = $("#ppc5-check-availability");
availability = null;
box.classList.add("ppc5-hidden");
if (!config) {
box.className = "ppc5-result ppc5-result--error";
box.innerHTML = resultCard("Crew Offer Is Still Loading", "Please wait a moment, then check availability again.");
box.focus();
return false;
}
if (!config.offerEnabled) {
box.className = "ppc5-result ppc5-result--error";
box.innerHTML = resultCard("Crew Offer Unavailable", "Crew self-booking is currently unavailable. Please contact us on WhatsApp.");
box.focus();
return false;
}
if (!findRatePeriod(data.checkInDate, data.checkOutDate)) {
box.className = "ppc5-result ppc5-result--error";
box.innerHTML = resultCard("Crew Offer Unavailable for Selected Date", "The crew offer does not cover all selected stay dates. Please contact us for alternate availability.");
box.focus();
return false;
}
button.disabled = true;
button.textContent = "Checking…";
try {
const result = await checkCrewAvailability({ checkInDate: data.checkInDate, checkOutDate: data.checkOutDate, roomTypeID: pod.roomTypeID });
availability = result;
if (result.available) {
const minimum = Math.min(...result.nights.map(night => Math.min(night.crewRemaining, night.cloudbedsRoomsAvailable)));
box.className = "ppc5-result ppc5-result--success";
box.innerHTML = resultCard("Crew Availability Confirmed", `${pod.label} is available for all ${result.nights.length} night${result.nights.length === 1 ? "" : "s"}. At least ${minimum} crew pod${minimum === 1 ? "" : "s"} remain per selected night.`, true);
$("#ppc5-live").textContent = "Crew availability confirmed for every selected night.";
if (advance) showStep(2);
} else {
const [title, message] = availabilityFailure(result);
box.className = "ppc5-result ppc5-result--error";
box.innerHTML = resultCard(title, message);
box.focus();
}
return result.available;
} catch (error) {
box.className = "ppc5-result ppc5-result--error";
box.innerHTML = resultCard("Availability Check Unavailable", "We could not safely confirm availability. No booking was made. Please retry or contact us on WhatsApp.");
box.focus();
return false;
} finally {
button.disabled = false;
button.textContent = "Check Availability";
}
}
function buildPayload() {
const data = getFormData();
const pod = selectedPod();
return {
checkInDate: data.checkInDate,
checkOutDate: data.checkOutDate,
roomTypeID: pod.roomTypeID,
podType: pod.type,
guest: {
firstName: data.firstName.trim(), lastName: data.lastName.trim(), email: data.email.trim(),
phone: data.phone.trim(), airline: data.airline.trim(), crewRole: data.crewRole,
returningCrew: data.returningCrew === "true"
},
arrival: {
eta: data.eta,
earlyCheckInRequested: data.earlyRequested !== "none",
earlyCheckInOption: data.earlyRequested,
earlyCheckInTime: null,
lateCheckOutRequested: data.lateRequested !== "none",
lateCheckOutOption: data.lateRequested,
lateCheckOutTime: null
},
damageDepositAuthorizationConfirmed: data.cardAuthorizationComplete === "on",
policyAccepted: true
};
}
async function submitBooking(event) {
event.preventDefault();
if (!validateStep(5)) return;
// Recheck immediately before creation in case the guest crossed 6:00 PM during the form.
const cutoff = getSameDayCutoffState();
if ($("#ppc5-checkin").value === cutoff.date && cutoff.closed) {
showStep(1);
setError($("#ppc5-checkin"), "Same-day booking is closed after 6:00 PM. Please contact us on WhatsApp.");
showSameDayClosedMessage();
return;
}
const button = $("#ppc5-create");
const errorBox = $("#ppc5-submit-error");
errorBox.classList.add("ppc5-hidden");
button.disabled = true;
button.textContent = "Creating Reservation…";
try {
// FAIL CLOSED: first-time crew images require an approved private upload API.
// Do not remove this guard until the backend securely stores the image and
// returns an opaque upload token that can be linked to the reservation.
const returningCrewSelection = $("input[name=returningCrew]:checked");
if (!DEMO_MODE && returningCrewSelection && returningCrewSelection.value === "false") {
throw new Error("Secure RAIC/Crew ID image storage is not connected. No image or reservation was sent.");
}
if (!(await runAvailability(false))) throw new Error("Availability changed. Please return to Step 1 and review the message.");
const payload = buildPayload();
const response = await createCrewReservation(payload);
if (!response || !response.success || !response.reservationNumber) throw new Error("The reservation could not be created.");
const pod = selectedPod();
const rows = [
["Reservation number", response.reservationNumber], ["Check-in", formatDate(payload.checkInDate)],
["Check-out", formatDate(payload.checkOutDate)], ["Pod type", pod.label],
["Amount due", response.amountDue != null ? `${money(response.amountDue, response.currency || "CAD")} CAD` : "Provided by Panda Pod payment request"],
["Payment status", response.paymentStatus || "PENDING"]
];
$("#ppc5-success-summary").innerHTML = rows.map(([key, value]) => `
Recent Comments