prepare("SELECT p.*, d.name AS dest_name, d.slug AS dest_slug FROM packages p JOIN destinations d ON d.id = p.dest_id WHERE p.slug = ? AND p.is_active = 1 LIMIT 1"); $stmt->bind_param('s', $slug); $stmt->execute(); $pkg = $stmt->get_result()->fetch_assoc(); } elseif ($pkg_id) { $stmt = $conn->prepare("SELECT p.*, d.name AS dest_name, d.slug AS dest_slug FROM packages p JOIN destinations d ON d.id = p.dest_id WHERE p.id = ? AND p.is_active = 1 LIMIT 1"); $stmt->bind_param('i', $pkg_id); $stmt->execute(); $pkg = $stmt->get_result()->fetch_assoc(); } if (!$pkg) { header('Location: destinations.php'); exit; } $pkg_id = $pkg['id']; // Fetch itinerary $itinerary = []; $res = $conn->query("SELECT * FROM itinerary WHERE package_id=$pkg_id ORDER BY day_number ASC"); if ($res) { while ($r = $res->fetch_assoc()) $itinerary[] = $r; } // Build per-day transfers lookup $day_transfers = []; foreach ($transfers_raw = [] as $_) {} // init $tres = @$conn->query("SELECT * FROM package_transfers WHERE package_id=$pkg_id ORDER BY day_number ASC"); if ($tres) { while ($r = $tres->fetch_assoc()) { $day_transfers[$r['day_number']][] = $r; } } // Fetch hotels with images $hotels = []; $res = $conn->query(" SELECT ph.*, h.name, h.star_rating, h.description AS h_desc, h.highlights, h.negatives, h.room_info, GROUP_CONCAT(hi.image_url ORDER BY hi.id ASC SEPARATOR '||') AS images FROM package_hotels ph JOIN hotels h ON h.id = ph.hotel_id LEFT JOIN hotel_images hi ON hi.hotel_id = h.id WHERE ph.package_id=$pkg_id GROUP BY ph.id ORDER BY ph.id ASC "); while ($r = $res->fetch_assoc()) { $r['images_arr'] = $r['images'] ? explode('||', $r['images']) : []; $hotels[] = $r; } // Fetch activities $activities = []; $res = @$conn->query(" SELECT pa.id, pa.package_id, pa.activity_id, pa.day_number, a.name, a.description AS a_desc, a.location, '' AS images FROM package_activities pa JOIN activities a ON pa.activity_id = a.id WHERE pa.package_id=$pkg_id ORDER BY pa.day_number ASC "); if ($res) { while ($r = $res->fetch_assoc()) { $r['images_arr'] = []; $r['description'] = $r['a_desc']; $r['is_included'] = 1; $activities[] = $r; } } // Generate dynamic Trip Experiences if none exist in the DB mapping or it has very few dummy mappings if (count($activities) < 3 && !empty($itinerary)) { echo ""; $activities = []; // Wipe generic if too few foreach ($itinerary as $j => $itin_day) { $dt = strtolower($itin_day['title'] ?? ''); $is_arr_dep = (strpos($dt, 'arrival') !== false || strpos($dt, 'departure') !== false || strpos($dt, 'depart') !== false); // Only skip if the description is totally empty and it's an arrival/departure. // Otherwise, everything becomes an experience. if ($is_arr_dep && empty($itin_day['description'])) { continue; } $activities[] = [ 'name' => $itin_day['title'] ?: 'Sightseeing & Excursions', 'description' => $itin_day['description'] ?? 'Enjoy a premium, highly-curated local experience perfectly designed for your arrival.', 'location' => $itin_day['city'] ?: $pkg['dest_name'], 'day_number' => $itin_day['day_number'] ?? ($j+1), 'images_arr' => [] ]; } } echo ""; // Fetch transfers $transfers = []; $res = @$conn->query("SELECT * FROM package_transfers WHERE package_id=$pkg_id ORDER BY day_number ASC"); if ($res) { while ($r = $res->fetch_assoc()) $transfers[] = $r; } // Flights not used per walkthrough schema $flights = []; // Fetch inclusions / exclusions $inclusions = []; $exclusions = []; $res = $conn->query("SELECT * FROM inclusions WHERE package_id=$pkg_id ORDER BY display_order ASC"); while ($r = $res->fetch_assoc()) { if ($r['is_exclusion']) $exclusions[] = $r['content']; else $inclusions[] = $r['content']; } // Similar packages (same destination, different package) $similar = []; $res = $conn->query(" SELECT p.id, p.title, p.slug, p.hero_image, p.price_per_person, p.nights, p.days, p.is_honeymoon FROM packages p WHERE p.dest_id = {$pkg['dest_id']} AND p.id != $pkg_id AND p.is_active = 1 ORDER BY ABS(p.nights - {$pkg['nights']}) ASC, p.price_per_person ASC LIMIT 6 "); while ($r = $res->fetch_assoc()) $similar[] = $r; $conn->close(); // Computed values $price_formatted = $pkg['price_per_person'] > 0 ? '₹' . number_format($pkg['price_per_person']) : 'View Deal'; $nights = $pkg['nights'] ?: (count($itinerary) > 1 ? count($itinerary) - 1 : 0); $days = $pkg['days'] ?: ($nights > 0 ? $nights + 1 : count($itinerary)); $cities = json_decode($pkg['cities'] ?: '[]', true) ?: []; // Derive cities from hotel data if cities array is empty if (empty($cities)) { $city_set = []; foreach ($hotels as $h) { $city_name = $h['city'] ?: ''; if (!$city_name && $h['name']) { // Extract city from name like "2 Nights Havelock-Island" $city_name = preg_replace('/^\d+\s*Nights?\s*/i', '', $h['name']); $city_name = str_replace('-', ' ', trim($city_name)); } if ($city_name && !in_array($city_name, $city_set)) $city_set[] = $city_name; } $cities = $city_set ?: [$pkg['dest_name']]; } // Dynamic highlights from actual data $has_ferry = false; $has_cab = false; $has_flight = false; $hotel_stars = []; foreach ($transfers as $t) { if (stripos($t['transfer_type'] ?? '', 'Ferry') !== false) $has_ferry = true; if (stripos($t['transfer_type'] ?? '', 'Cab') !== false || stripos($t['vehicle_name'] ?? '', 'Sedan') !== false) $has_cab = true; } foreach ($flights as $f) { if ($f['is_included']) $has_flight = true; } foreach ($hotels as $h) { $hotel_stars[] = $h['star_rating']; } $max_stars = $hotel_stars ? max($hotel_stars) : 3; $min_stars = $hotel_stars ? min($hotel_stars) : 3; // Build dynamic highlights $dynamic_highlights = []; if ($hotel_stars) $dynamic_highlights[] = "Handpicked {$min_stars}-{$max_stars} Star Stays"; if ($has_cab) $dynamic_highlights[] = "Private AC Vehicle Transfers"; if ($has_ferry) $dynamic_highlights[] = "Premium Ferry Tickets Included"; if ($has_flight) $dynamic_highlights[] = "Return Flights Included"; foreach ($inclusions as $inc) { if (count($dynamic_highlights) >= 5) break; if (stripos($inc, 'breakfast') !== false && !in_array('Daily Breakfast Included', $dynamic_highlights)) $dynamic_highlights[] = 'Daily Breakfast Included'; if (stripos($inc, 'sightseeing') !== false && !in_array('Guided Sightseeing Tours', $dynamic_highlights)) $dynamic_highlights[] = 'Guided Sightseeing Tours'; } if (empty($dynamic_highlights)) $dynamic_highlights = ['Premium Accommodation', 'Expert Travel Support', 'All Transfers Included']; ?>
= nl2br(htmlspecialchars($day['description'] ?? '')) ?>
= htmlspecialchars(substr($hotel['h_desc'] ?: 'Experience premium comfort and exceptional service at this highly-rated property.', 0, 150)) ?>...
Standard sightseeing inclusions apply as per itinerary.
$act): $img = !empty($act['images_arr']) ? $act['images_arr'][0] : ''; if ($img && strpos($img, 'http') === false && strpos($img, 'nexus_export') === false) { $img = 'nexus_export/images/' . basename($img); } $fallback = $act_placeholders[$ai % count($act_placeholders)]; $act_name = $act['name']; // Fix generic names like "Activity 1" ΓÇö use itinerary context if (preg_match('/^Activity\s+\d+$/i', $act_name) && isset($itinerary[$ai])) { $itin_title = $itinerary[$ai]['title'] ?? ''; if ($itin_title) $act_name = $itin_title; } $act_desc = $act['description'] ?? ''; if (!$act_desc && isset($itinerary[$ai])) { $act_desc = substr($itinerary[$ai]['description'] ?? '', 0, 200); } if (!$act_desc) { $act_desc = 'Enjoy this curated experience as part of your ' . htmlspecialchars($pkg['dest_name']) . ' journey.'; } $act_location = $act['location'] ?: ($act['city'] ?? $pkg['dest_name']); ?>= htmlspecialchars(substr($act_desc, 0, 200)) ?>= strlen($act_desc) > 200 ? '...' : '' ?>