| 1 |
<?php |
| 2 |
/** |
| 3 |
* Booking Form Fields Partial |
| 4 |
* |
| 5 |
* Renders form fields based on configuration from Settings -> Booking Form |
| 6 |
* Dynamic form customization requires the Dynamic Form Field module (Pro). |
| 7 |
* |
| 8 |
* @package Yatra |
| 9 |
* |
| 10 |
* Expected variables: |
| 11 |
* - $trip_id |
| 12 |
* - $trip_slug (optional) |
| 13 |
* - $total_travelers |
| 14 |
* - $deposit_required |
| 15 |
* - $deposit_percentage |
| 16 |
* - $partial_payment |
| 17 |
* - $partial_payment_percentage |
| 18 |
* - $enabled_gateways |
| 19 |
* |
| 20 |
* Form Configuration Structure: |
| 21 |
* - contact_form: Lead traveler contact information (prefix: contact_) |
| 22 |
* - emergency_contact_form: Emergency contact details (prefix: emergency_) |
| 23 |
* - traveler_form: Individual traveler details (array format: travelers[1][field_id]) |
| 24 |
*/ |
| 25 |
|
| 26 |
if (!defined('ABSPATH')) { |
| 27 |
exit; |
| 28 |
} |
| 29 |
|
| 30 |
// Check if Dynamic Form Field module is enabled (Pro feature) |
| 31 |
$is_dynamic_form_enabled = apply_filters('yatra_dynamic_form_field_enabled', false); |
| 32 |
|
| 33 |
// Get form configuration - returns default config if module disabled, custom config if enabled |
| 34 |
$form_config = yatra_get_booking_form_config(); |
| 35 |
|
| 36 |
|
| 37 |
// Country list — pulls from the canonical FormatHelper source so every |
| 38 |
// dropdown (admin, public booking, Pro modules) shows the same full |
| 39 |
// ISO-3166-1 list. Operators that want a "popular countries" subset |
| 40 |
// or custom ordering apply the `yatra_countries_list` filter. |
| 41 |
$countries = \Yatra\Helpers\FormatHelper::getCountries(); |
| 42 |
|
| 43 |
/** |
| 44 |
* Render a single form field based on configuration |
| 45 |
* |
| 46 |
* @param array $field Field configuration |
| 47 |
* @param string $prefix Field name prefix (e.g., 'contact_', 'emergency_') |
| 48 |
* @param array $countries Country list for country fields |
| 49 |
* @param string $custom_name Optional custom field name (for array-style names like travelers[1][field_id]) |
| 50 |
* @param string $custom_id Optional custom field ID |
| 51 |
*/ |
| 52 |
if (!function_exists('yatra_render_form_field')) { |
| 53 |
function yatra_render_form_field($field, $prefix = '', $countries = [], $custom_name = null, $custom_id = null) { |
| 54 |
if (empty($field['enabled'])) { |
| 55 |
return; |
| 56 |
} |
| 57 |
|
| 58 |
$field_id = $custom_id ? esc_attr($custom_id) : esc_attr($prefix . $field['id']); |
| 59 |
$field_name = $custom_name ? esc_attr($custom_name) : esc_attr($prefix . $field['id']); |
| 60 |
$required = !empty($field['required']); |
| 61 |
$required_attr = $required ? 'required' : ''; |
| 62 |
$required_star = $required ? '<span class="required">*</span>' : ''; |
| 63 |
|
| 64 |
$prefill_value = ''; |
| 65 |
if ($custom_name === null && $prefix === 'contact_') { |
| 66 |
global $booking; |
| 67 |
if (isset($booking) && is_object($booking)) { |
| 68 |
$prop = 'contact_' . ($field['id'] ?? ''); |
| 69 |
if (!empty($prop) && isset($booking->{$prop}) && $booking->{$prop} !== null && $booking->{$prop} !== '') { |
| 70 |
$prefill_value = (string) $booking->{$prop}; |
| 71 |
} |
| 72 |
} |
| 73 |
} |
| 74 |
|
| 75 |
$width_class = ''; |
| 76 |
if (!empty($field['width'])) { |
| 77 |
switch ($field['width']) { |
| 78 |
case 'half': |
| 79 |
$width_class = 'yatra-field-half'; |
| 80 |
break; |
| 81 |
case 'third': |
| 82 |
$width_class = 'yatra-field-third'; |
| 83 |
break; |
| 84 |
default: |
| 85 |
$width_class = 'yatra-field-full'; |
| 86 |
} |
| 87 |
} |
| 88 |
$field_type = $field['type'] ?? 'text'; |
| 89 |
?> |
| 90 |
<div class="yatra-form-group <?php echo esc_attr($width_class); ?> <?php echo $field_type === 'checkbox' ? 'yatra-form-group--checkbox' : ''; ?>"> |
| 91 |
<?php if ($field_type !== 'checkbox') : ?> |
| 92 |
<label for="<?php echo $field_id; ?>"> |
| 93 |
<?php echo esc_html($field['label']); ?> <?php echo $required_star; ?> |
| 94 |
</label> |
| 95 |
<?php endif; ?> |
| 96 |
<?php |
| 97 |
switch ($field['type']) { |
| 98 |
case 'checkbox': |
| 99 |
$is_checked = ($prefill_value === '1' || $prefill_value === 'on' || $prefill_value === 'true'); |
| 100 |
?> |
| 101 |
<label for="<?php echo $field_id; ?>" class="yatra-inline-checkbox-label"> |
| 102 |
<input |
| 103 |
type="checkbox" |
| 104 |
id="<?php echo $field_id; ?>" |
| 105 |
name="<?php echo $field_name; ?>" |
| 106 |
value="1" |
| 107 |
<?php checked($is_checked, true, false); ?> |
| 108 |
<?php echo $required_attr; ?> |
| 109 |
/> |
| 110 |
<span><?php echo esc_html($field['label']); ?><?php echo $required_star; ?></span> |
| 111 |
</label> |
| 112 |
<?php |
| 113 |
break; |
| 114 |
case 'select': |
| 115 |
?> |
| 116 |
<select id="<?php echo $field_id; ?>" name="<?php echo $field_name; ?>" <?php echo $required_attr; ?>> |
| 117 |
<option value=""><?php echo esc_html($field['placeholder'] ?? __('Select...', 'yatra')); ?></option> |
| 118 |
<?php if (!empty($field['options'])) : ?> |
| 119 |
<?php foreach ($field['options'] as $option) : ?> |
| 120 |
<option value="<?php echo esc_attr($option['value']); ?>" <?php echo selected($prefill_value, (string) ($option['value'] ?? ''), false); ?>> |
| 121 |
<?php echo esc_html($option['label']); ?> |
| 122 |
</option> |
| 123 |
<?php endforeach; ?> |
| 124 |
<?php endif; ?> |
| 125 |
</select> |
| 126 |
<?php |
| 127 |
break; |
| 128 |
|
| 129 |
case 'country': |
| 130 |
?> |
| 131 |
<select id="<?php echo $field_id; ?>" name="<?php echo $field_name; ?>" <?php echo $required_attr; ?>> |
| 132 |
<option value=""><?php echo esc_html($field['placeholder'] ?? __('Select Country', 'yatra')); ?></option> |
| 133 |
<?php foreach ($countries as $code => $name) : ?> |
| 134 |
<option value="<?php echo esc_attr($code); ?>" <?php echo selected($prefill_value, (string) $code, false); ?>> |
| 135 |
<?php echo esc_html($name); ?> |
| 136 |
</option> |
| 137 |
<?php endforeach; ?> |
| 138 |
</select> |
| 139 |
<?php |
| 140 |
break; |
| 141 |
|
| 142 |
case 'textarea': |
| 143 |
?> |
| 144 |
<textarea |
| 145 |
id="<?php echo $field_id; ?>" |
| 146 |
name="<?php echo $field_name; ?>" |
| 147 |
placeholder="<?php echo esc_attr($field['placeholder'] ?? ''); ?>" |
| 148 |
<?php echo $required_attr; ?> |
| 149 |
rows="3" |
| 150 |
><?php echo esc_textarea($prefill_value); ?></textarea> |
| 151 |
<?php |
| 152 |
break; |
| 153 |
|
| 154 |
case 'date': |
| 155 |
$min_attr = ''; |
| 156 |
?> |
| 157 |
<input |
| 158 |
type="date" |
| 159 |
id="<?php echo $field_id; ?>" |
| 160 |
name="<?php echo $field_name; ?>" |
| 161 |
<?php echo $min_attr; ?> |
| 162 |
<?php echo $required_attr; ?> |
| 163 |
value="<?php echo esc_attr($prefill_value); ?>" |
| 164 |
> |
| 165 |
<?php |
| 166 |
break; |
| 167 |
|
| 168 |
default: // text, email, tel, number |
| 169 |
?> |
| 170 |
<input |
| 171 |
type="<?php echo esc_attr($field['type']); ?>" |
| 172 |
id="<?php echo $field_id; ?>" |
| 173 |
name="<?php echo $field_name; ?>" |
| 174 |
placeholder="<?php echo esc_attr($field['placeholder'] ?? ''); ?>" |
| 175 |
<?php echo $required_attr; ?> |
| 176 |
value="<?php echo esc_attr($prefill_value); ?>" |
| 177 |
> |
| 178 |
<?php |
| 179 |
break; |
| 180 |
} |
| 181 |
?> |
| 182 |
</div> |
| 183 |
<?php |
| 184 |
} |
| 185 |
} // End function_exists check for yatra_render_form_field |
| 186 |
|
| 187 |
/** |
| 188 |
* Render a form section with all its fields |
| 189 |
*/ |
| 190 |
if (!function_exists('yatra_render_form_section')) { |
| 191 |
function yatra_render_form_section($section_config, $prefix = '', $countries = []) { |
| 192 |
if (isset($section_config['enabled']) && !$section_config['enabled']) { |
| 193 |
return; |
| 194 |
} |
| 195 |
|
| 196 |
$fields = $section_config['fields'] ?? []; |
| 197 |
|
| 198 |
// Sort fields by order |
| 199 |
usort($fields, function($a, $b) { |
| 200 |
return ($a['order'] ?? 0) - ($b['order'] ?? 0); |
| 201 |
}); |
| 202 |
|
| 203 |
// Group fields by section (for subsections like dietary) |
| 204 |
$grouped_fields = []; |
| 205 |
$current_section = null; |
| 206 |
|
| 207 |
foreach ($fields as $field) { |
| 208 |
if (!empty($field['enabled'])) { |
| 209 |
$section = $field['section'] ?? 'main'; |
| 210 |
if (!isset($grouped_fields[$section])) { |
| 211 |
$grouped_fields[$section] = []; |
| 212 |
} |
| 213 |
$grouped_fields[$section][] = $field; |
| 214 |
} |
| 215 |
} |
| 216 |
?> |
| 217 |
<div class="yatra-booking-section"> |
| 218 |
<h2 class="yatra-section-title"><?php echo esc_html($section_config['title'] ?? ''); ?></h2> |
| 219 |
<?php if (!empty($section_config['description'])) : ?> |
| 220 |
<p class="yatra-section-description"><?php echo esc_html($section_config['description']); ?></p> |
| 221 |
<?php endif; ?> |
| 222 |
|
| 223 |
<?php foreach ($grouped_fields as $section_key => $section_fields) : ?> |
| 224 |
<?php if ($section_key !== 'main') : ?> |
| 225 |
<div class="yatra-traveler-subsection"> |
| 226 |
<h4 class="yatra-subsection-title"> |
| 227 |
<?php |
| 228 |
switch ($section_key) { |
| 229 |
case 'passport': |
| 230 |
esc_html_e('Additional details', 'yatra'); |
| 231 |
break; |
| 232 |
case 'dietary_medical': |
| 233 |
esc_html_e('Dietary & Medical Requirements', 'yatra'); |
| 234 |
break; |
| 235 |
default: |
| 236 |
echo esc_html(ucwords(str_replace('_', ' ', $section_key))); |
| 237 |
} |
| 238 |
?> |
| 239 |
</h4> |
| 240 |
<?php endif; ?> |
| 241 |
|
| 242 |
<div class="yatra-form-row"> |
| 243 |
<?php foreach ($section_fields as $field) : ?> |
| 244 |
<?php yatra_render_form_field($field, $prefix, $countries); ?> |
| 245 |
<?php endforeach; ?> |
| 246 |
</div> |
| 247 |
|
| 248 |
<?php if ($section_key !== 'main') : ?> |
| 249 |
</div> |
| 250 |
<?php endif; ?> |
| 251 |
<?php endforeach; ?> |
| 252 |
</div> |
| 253 |
<?php |
| 254 |
} |
| 255 |
} // End function_exists check for yatra_render_form_section |
| 256 |
?> |
| 257 |
|
| 258 |
<!-- Hidden Fields --> |
| 259 |
<input type="hidden" name="trip_id" value="<?php echo esc_attr($trip_id); ?>"> |
| 260 |
<?php if (!empty($trip_slug)) : ?> |
| 261 |
<input type="hidden" name="trip_slug" value="<?php echo esc_attr($trip_slug); ?>"> |
| 262 |
<?php endif; ?> |
| 263 |
|
| 264 |
<?php if ($is_remaining_payment) : ?> |
| 265 |
<input type="hidden" name="is_remaining_payment" value="1"> |
| 266 |
<?php if (!empty($existing_booking_id)) : ?> |
| 267 |
<input type="hidden" name="existing_booking_id" value="<?php echo esc_attr($existing_booking_id); ?>"> |
| 268 |
<?php endif; ?> |
| 269 |
<?php if (!empty($booking_reference)) : ?> |
| 270 |
<input type="hidden" name="booking_reference" value="<?php echo esc_attr($booking_reference); ?>"> |
| 271 |
<?php endif; ?> |
| 272 |
<?php if ($remaining_amount !== null) : ?> |
| 273 |
<input type="hidden" name="remaining_amount" value="<?php echo esc_attr($remaining_amount); ?>"> |
| 274 |
<?php endif; ?> |
| 275 |
<?php if (!empty($booking->amount_paid)) : ?> |
| 276 |
<input type="hidden" name="amount_paid" value="<?php echo esc_attr($booking->amount_paid); ?>"> |
| 277 |
<?php endif; ?> |
| 278 |
<?php if (!empty($booking->total_amount)) : ?> |
| 279 |
<input type="hidden" name="total_amount" value="<?php echo esc_attr($booking->total_amount); ?>"> |
| 280 |
<?php endif; ?> |
| 281 |
<?php endif; ?> |
| 282 |
|
| 283 |
<?php if (!$is_remaining_payment) : ?> |
| 284 |
<!-- Contact Form Section --> |
| 285 |
<?php |
| 286 |
$contact_config = $form_config['contact_form'] ?? []; |
| 287 |
if (!empty($contact_config)) { |
| 288 |
yatra_render_form_section($contact_config, 'contact_', $countries); |
| 289 |
} |
| 290 |
?> |
| 291 |
|
| 292 |
<!-- Emergency Contact Section --> |
| 293 |
<?php |
| 294 |
$emergency_config = $form_config['emergency_contact_form'] ?? []; |
| 295 |
if (!empty($emergency_config) && (!isset($emergency_config['enabled']) || $emergency_config['enabled'])) { |
| 296 |
yatra_render_form_section($emergency_config, 'emergency_', $countries); |
| 297 |
} |
| 298 |
?> |
| 299 |
|
| 300 |
<!-- Traveler Information Section --> |
| 301 |
<?php |
| 302 |
$traveler_config = $form_config['traveler_form'] ?? []; |
| 303 |
?> |
| 304 |
<?php endif; ?> |
| 305 |
|
| 306 |
<?php |
| 307 |
$traveler_config = $form_config['traveler_form'] ?? []; |
| 308 |
$traveler_count = isset($total_travelers) ? max(1, (int)$total_travelers) : 1; |
| 309 |
|
| 310 |
// Check if we have traveler-based pricing info |
| 311 |
$pricing_type = isset($pricing_type) ? $pricing_type : 'regular'; |
| 312 |
$price_types = isset($price_types) ? $price_types : []; |
| 313 |
$traveler_counts = isset($traveler_counts) ? $traveler_counts : []; |
| 314 |
|
| 315 |
/** |
| 316 |
* Coerce JSON / serialized / object values to a list for foreach-safe iteration. |
| 317 |
* |
| 318 |
* @param mixed $value |
| 319 |
* @return array<int|string, mixed> |
| 320 |
*/ |
| 321 |
$yatra_normalize_booking_arrayish = static function ($value): array { |
| 322 |
if ($value === null || $value === false || $value === '') { |
| 323 |
return []; |
| 324 |
} |
| 325 |
if (is_array($value)) { |
| 326 |
return $value; |
| 327 |
} |
| 328 |
if ($value instanceof \stdClass) { |
| 329 |
return (array) $value; |
| 330 |
} |
| 331 |
if (is_string($value)) { |
| 332 |
$trim = trim($value); |
| 333 |
if ($trim === '') { |
| 334 |
return []; |
| 335 |
} |
| 336 |
$decoded = json_decode($value, true); |
| 337 |
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) { |
| 338 |
return $decoded; |
| 339 |
} |
| 340 |
if (function_exists('maybe_unserialize')) { |
| 341 |
$un = maybe_unserialize($value); |
| 342 |
if (is_array($un)) { |
| 343 |
return $un; |
| 344 |
} |
| 345 |
} |
| 346 |
} |
| 347 |
|
| 348 |
return []; |
| 349 |
}; |
| 350 |
|
| 351 |
$price_types = $yatra_normalize_booking_arrayish($price_types); |
| 352 |
$traveler_counts = $yatra_normalize_booking_arrayish($traveler_counts); |
| 353 |
|
| 354 |
// Build traveler-to-category mapping for traveler-based pricing |
| 355 |
$traveler_category_map = []; |
| 356 |
if ($pricing_type === 'traveler_based' && !empty($price_types) && !empty($traveler_counts)) { |
| 357 |
$traveler_index = 1; |
| 358 |
foreach ($price_types as $index => $pt) { |
| 359 |
$pt = (object) $pt; |
| 360 |
$category_id = $pt->category_id ?? $index; |
| 361 |
$category_label = $pt->category_label ?? __('Traveler', 'yatra'); |
| 362 |
$count = isset($traveler_counts[$category_id]) ? (int) $traveler_counts[$category_id] : 0; |
| 363 |
|
| 364 |
for ($c = 1; $c <= $count; $c++) { |
| 365 |
$traveler_category_map[$traveler_index] = [ |
| 366 |
'category_id' => $category_id, |
| 367 |
'category_label' => $category_label, |
| 368 |
'category_index' => $c, |
| 369 |
]; |
| 370 |
$traveler_index++; |
| 371 |
} |
| 372 |
} |
| 373 |
} |
| 374 |
|
| 375 |
$price_candidates = [ |
| 376 |
isset($trip->price) ? (float) $trip->price : 0, |
| 377 |
isset($booking->session_price) ? (float) $booking->session_price : 0, |
| 378 |
isset($trip->sale_price) ? (float) $trip->sale_price : 0, |
| 379 |
isset($trip->original_price) ? (float) $trip->original_price : 0, |
| 380 |
]; |
| 381 |
|
| 382 |
$effective_trip_price = 0; |
| 383 |
foreach ($price_candidates as $candidate) { |
| 384 |
if ($candidate > 0) { |
| 385 |
$effective_trip_price = $candidate; |
| 386 |
break; |
| 387 |
} |
| 388 |
} |
| 389 |
|
| 390 |
$traveler_price_rows = []; |
| 391 |
$calculated_total = 0; |
| 392 |
|
| 393 |
if ($pricing_type === 'traveler_based' && !empty($price_types)) { |
| 394 |
foreach ($price_types as $index => $pt) { |
| 395 |
$pt = (object) $pt; |
| 396 |
$category_id = $pt->category_id ?? $index; |
| 397 |
$category_label = $pt->category_label ?? __('Traveler', 'yatra'); |
| 398 |
$category_price = isset($pt->effective_price) ? (float) $pt->effective_price : ($pt->sale_price ?? $pt->discounted_price ?? $pt->original_price ?? 0); |
| 399 |
$count = isset($traveler_counts[$category_id]) ? (int) $traveler_counts[$category_id] : ($index === 0 ? 1 : 0); |
| 400 |
$subtotal = $category_price * $count; |
| 401 |
|
| 402 |
$age_info = ''; |
| 403 |
if (isset($pt->age_min) || isset($pt->age_max)) { |
| 404 |
if (isset($pt->age_min) && isset($pt->age_max)) { |
| 405 |
/* translators: 1: minimum age, 2: maximum age. */ |
| 406 |
$age_info = sprintf(__('(Age %1$d-%2$d)', 'yatra'), $pt->age_min, $pt->age_max); |
| 407 |
} elseif (isset($pt->age_min)) { |
| 408 |
/* translators: %d: minimum age. */ |
| 409 |
$age_info = sprintf(__('(Age %d+)', 'yatra'), $pt->age_min); |
| 410 |
} else { |
| 411 |
/* translators: %d: maximum age. */ |
| 412 |
$age_info = sprintf(__('(Up to age %d)', 'yatra'), $pt->age_max); |
| 413 |
} |
| 414 |
} |
| 415 |
|
| 416 |
$traveler_price_rows[] = [ |
| 417 |
'category_id' => $category_id, |
| 418 |
'category_label' => $category_label, |
| 419 |
'category_price' => $category_price, |
| 420 |
'count' => $count, |
| 421 |
'subtotal' => $subtotal, |
| 422 |
'age_info' => $age_info, |
| 423 |
]; |
| 424 |
|
| 425 |
$calculated_total += $subtotal; |
| 426 |
|
| 427 |
if ($effective_trip_price <= 0 && $category_price > 0) { |
| 428 |
$effective_trip_price = $category_price; |
| 429 |
} |
| 430 |
} |
| 431 |
} |
| 432 |
|
| 433 |
if ($effective_trip_price <= 0) { |
| 434 |
$effective_trip_price = 0; |
| 435 |
} |
| 436 |
|
| 437 |
$initial_total_amount = $calculated_total > 0 |
| 438 |
? $calculated_total |
| 439 |
: ($effective_trip_price * max(1, (int) $total_travelers)); |
| 440 |
|
| 441 |
if ($initial_total_amount <= 0 && $effective_trip_price > 0) { |
| 442 |
$initial_total_amount = $effective_trip_price * max(1, (int) $total_travelers); |
| 443 |
} |
| 444 |
|
| 445 |
$initial_due_amount = $initial_total_amount; |
| 446 |
|
| 447 |
?> |
| 448 |
|
| 449 |
<?php if (!empty($traveler_config) && !$is_remaining_payment) : ?> |
| 450 |
<div class="yatra-booking-section"> |
| 451 |
<h2 class="yatra-section-title"><?php echo esc_html($traveler_config['title'] ?? __('Traveler Information', 'yatra')); ?></h2> |
| 452 |
<?php if (!empty($traveler_config['description'])) : ?> |
| 453 |
<p class="yatra-section-description"><?php echo esc_html($traveler_config['description']); ?></p> |
| 454 |
<?php endif; ?> |
| 455 |
|
| 456 |
<div id="yatra-travelers-container"> |
| 457 |
<?php |
| 458 |
$traveler_fields = $traveler_config['fields'] ?? []; |
| 459 |
usort($traveler_fields, function($a, $b) { |
| 460 |
return ($a['order'] ?? 0) - ($b['order'] ?? 0); |
| 461 |
}); |
| 462 |
|
| 463 |
// Group fields by section |
| 464 |
$grouped_traveler_fields = []; |
| 465 |
foreach ($traveler_fields as $field) { |
| 466 |
if (!empty($field['enabled'])) { |
| 467 |
$section = $field['section'] ?? 'main'; |
| 468 |
if (!isset($grouped_traveler_fields[$section])) { |
| 469 |
$grouped_traveler_fields[$section] = []; |
| 470 |
} |
| 471 |
$grouped_traveler_fields[$section][] = $field; |
| 472 |
} |
| 473 |
} |
| 474 |
|
| 475 |
for ($i = 1; $i <= $traveler_count; $i++) : |
| 476 |
// Determine traveler label based on category if traveler-based pricing |
| 477 |
if (!empty($traveler_category_map[$i])) { |
| 478 |
$category_info = $traveler_category_map[$i]; |
| 479 |
$category_label = $category_info['category_label']; |
| 480 |
$category_index = $category_info['category_index']; |
| 481 |
/* translators: 1: traveler category label (e.g. Adult, Child), 2: sequential index within that category. */ |
| 482 |
$traveler_label = sprintf(__('%1$s %2$d', 'yatra'), $category_label, $category_index); |
| 483 |
if ($i === 1) { |
| 484 |
$traveler_label .= ' (' . __('Lead Traveler', 'yatra') . ')'; |
| 485 |
} |
| 486 |
} else { |
| 487 |
/* translators: %d: traveler sequence number. */ |
| 488 |
$traveler_label = ($i === 1) ? __('Traveler 1 (Lead Traveler)', 'yatra') : sprintf(__('Traveler %d', 'yatra'), $i); |
| 489 |
} |
| 490 |
?> |
| 491 |
<div class="yatra-traveler-form" data-traveler-index="<?php echo esc_attr($i); ?>" <?php if (!empty($traveler_category_map[$i])): ?>data-category-id="<?php echo esc_attr($traveler_category_map[$i]['category_id']); ?>" data-category-label="<?php echo esc_attr($traveler_category_map[$i]['category_label']); ?>"<?php endif; ?>> |
| 492 |
<div class="yatra-traveler-header"> |
| 493 |
<h3 class="yatra-traveler-title"><?php echo esc_html($traveler_label); ?></h3> |
| 494 |
<?php if ($i > 1) : ?> |
| 495 |
<span class="yatra-traveler-note"><?php esc_html_e('Additional traveler', 'yatra'); ?></span> |
| 496 |
<?php endif; ?> |
| 497 |
</div> |
| 498 |
|
| 499 |
<?php foreach ($grouped_traveler_fields as $section_key => $section_fields) : ?> |
| 500 |
<?php if ($section_key !== 'main') : ?> |
| 501 |
<div class="yatra-traveler-subsection"> |
| 502 |
<h4 class="yatra-subsection-title"> |
| 503 |
<?php |
| 504 |
switch ($section_key) { |
| 505 |
case 'passport': |
| 506 |
esc_html_e('Additional details', 'yatra'); |
| 507 |
break; |
| 508 |
case 'dietary_medical': |
| 509 |
esc_html_e('Dietary & Medical Requirements', 'yatra'); |
| 510 |
break; |
| 511 |
default: |
| 512 |
echo esc_html(ucwords(str_replace('_', ' ', $section_key))); |
| 513 |
} |
| 514 |
?> |
| 515 |
</h4> |
| 516 |
<?php endif; ?> |
| 517 |
|
| 518 |
<div class="yatra-form-row"> |
| 519 |
<?php foreach ($section_fields as $field) : |
| 520 |
$field_id = "traveler-{$i}-" . esc_attr($field['id']); |
| 521 |
$field_name = "travelers[{$i}][" . esc_attr($field['id']) . "]"; |
| 522 |
yatra_render_form_field($field, '', $countries, $field_name, $field_id); |
| 523 |
endforeach; ?> |
| 524 |
</div> |
| 525 |
|
| 526 |
<?php if ($section_key !== 'main') : ?> |
| 527 |
</div> |
| 528 |
<?php endif; ?> |
| 529 |
<?php endforeach; ?> |
| 530 |
</div> |
| 531 |
<?php endfor; ?> |
| 532 |
</div> |
| 533 |
</div> |
| 534 |
<?php endif; ?> |
| 535 |
|
| 536 |
<!-- Payment Method Section --> |
| 537 |
<?php |
| 538 |
/** |
| 539 |
* Filter to check if flexible payments module is enabled (Pro feature) |
| 540 |
* When enabled, deposit and partial payment options will be available |
| 541 |
*/ |
| 542 |
$flexible_payments_enabled = apply_filters('yatra_flexible_payments_enabled', false); |
| 543 |
|
| 544 |
/** |
| 545 |
* Filter to get payment method options. |
| 546 |
* |
| 547 |
* Pro module (FlexiblePayments) can add deposit/partial payment options here. |
| 548 |
* We pass `trip_id` so the Pro module can look up per-trip overrides |
| 549 |
* (trip.deposit_amount / trip.deposit_percentage) and expose the deposit |
| 550 |
* option even when the site-wide "deposit_required" flag is off — i.e. a |
| 551 |
* single trip can opt in to a custom deposit without enabling deposits for |
| 552 |
* the whole catalogue. |
| 553 |
*/ |
| 554 |
$payment_method_options = apply_filters('yatra_payment_method_options', [], [ |
| 555 |
'trip_id' => isset($trip_id) ? (int) $trip_id : 0, |
| 556 |
'deposit_required' => $deposit_required, |
| 557 |
'deposit_percentage' => $deposit_percentage, |
| 558 |
'partial_payment' => $partial_payment, |
| 559 |
'partial_payment_percentage' => $partial_payment_percentage, |
| 560 |
]); |
| 561 |
|
| 562 |
$has_flexible_options = $flexible_payments_enabled && !empty($payment_method_options); |
| 563 |
?> |
| 564 |
<?php if (!$is_remaining_payment && $has_flexible_options) : ?> |
| 565 |
<div class="yatra-booking-section"> |
| 566 |
<h2 class="yatra-section-title"><?php esc_html_e('Payment Method', 'yatra'); ?></h2> |
| 567 |
|
| 568 |
<div class="yatra-payment-methods"> |
| 569 |
<label class="yatra-payment-option"> |
| 570 |
<input type="radio" name="payment_method" value="full" checked> |
| 571 |
<span class="yatra-payment-label"> |
| 572 |
<strong><?php esc_html_e('Pay in Full', 'yatra'); ?></strong> |
| 573 |
<span><?php esc_html_e('Pay the total amount now', 'yatra'); ?></span> |
| 574 |
</span> |
| 575 |
</label> |
| 576 |
|
| 577 |
<?php foreach ($payment_method_options as $option_key => $option) : ?> |
| 578 |
<label class="yatra-payment-option"> |
| 579 |
<input type="radio" name="payment_method" value="<?php echo esc_attr($option['value']); ?>"> |
| 580 |
<span class="yatra-payment-label"> |
| 581 |
<strong><?php echo esc_html($option['label']); ?></strong> |
| 582 |
<span><?php echo esc_html($option['description']); ?></span> |
| 583 |
</span> |
| 584 |
</label> |
| 585 |
<?php endforeach; ?> |
| 586 |
</div> |
| 587 |
</div> |
| 588 |
<?php endif; ?> |
| 589 |
|
| 590 |
<!-- Payment Gateway Section --> |
| 591 |
<?php |
| 592 |
// Always use the live registry list (same as BookingPageHandler). Do not intersect with $enabled_gateways |
| 593 |
// from older templates/sessions — that caused only stale gateways (e.g. Pay Later) to show. |
| 594 |
$display_gateways = []; |
| 595 |
if (class_exists('Yatra\PaymentGateways\PaymentGatewayRegistry')) { |
| 596 |
$registry = \Yatra\PaymentGateways\PaymentGatewayRegistry::getInstance(); |
| 597 |
foreach ($registry->getForCheckout() as $gateway) { |
| 598 |
if (!empty($gateway['id'])) { |
| 599 |
$display_gateways[$gateway['id']] = $gateway; |
| 600 |
} |
| 601 |
} |
| 602 |
} |
| 603 |
?> |
| 604 |
<?php if (!empty($display_gateways)) : ?> |
| 605 |
<div class="yatra-booking-section"> |
| 606 |
<h2 class="yatra-section-title"><?php esc_html_e('Select Payment Gateway', 'yatra'); ?></h2> |
| 607 |
|
| 608 |
<div class="yatra-gateway-options"> |
| 609 |
<?php |
| 610 |
$first = true; |
| 611 |
foreach ($display_gateways as $gateway_id => $gateway) : |
| 612 |
$icon = !empty($gateway['icon']) ? $gateway['icon'] : plugins_url('assets/images/payment-placeholder.png', dirname(__DIR__)); |
| 613 |
?> |
| 614 |
<div class="yatra-gateway-option-wrapper"> |
| 615 |
<label class="yatra-gateway-option"> |
| 616 |
<input type="radio" name="payment_gateway" value="<?php echo esc_attr($gateway_id); ?>" <?php checked($first); ?>> |
| 617 |
<span class="yatra-gateway-icon-wrap"> |
| 618 |
<img src="<?php echo esc_url($icon ?? ''); ?>" alt="<?php echo esc_attr($gateway['title'] ?? ''); ?>" class="yatra-gateway-icon"> |
| 619 |
</span> |
| 620 |
<span class="yatra-gateway-content"> |
| 621 |
<strong class="yatra-gateway-title"><?php echo esc_html($gateway['title']); ?></strong> |
| 622 |
<span class="yatra-gateway-desc"><?php echo esc_html($gateway['description']); ?></span> |
| 623 |
<?php if (empty($gateway['is_configured'])) : ?> |
| 624 |
<span class="yatra-gateway-desc yatra-gateway-setup-required"><?php esc_html_e('Setup incomplete — finish configuration under Yatra → Settings → Payment, or choose another method.', 'yatra'); ?></span> |
| 625 |
<?php endif; ?> |
| 626 |
</span> |
| 627 |
</label> |
| 628 |
<div class="yatra-gateway-extra" id="yatra-gateway-extra-<?php echo esc_attr($gateway_id); ?>" data-gateway="<?php echo esc_attr($gateway_id); ?>"></div> |
| 629 |
</div> |
| 630 |
<?php |
| 631 |
$first = false; |
| 632 |
endforeach; |
| 633 |
?> |
| 634 |
</div> |
| 635 |
</div> |
| 636 |
<?php endif; ?> |
| 637 |
|
| 638 |
<?php if (!$is_remaining_payment) : ?> |
| 639 |
<!-- Special Requests --> |
| 640 |
<div class="yatra-booking-section"> |
| 641 |
<h2 class="yatra-section-title"><?php esc_html_e('Special Requests', 'yatra'); ?></h2> |
| 642 |
<p class="yatra-section-description"><?php esc_html_e('Any special requests or notes for your trip (optional)', 'yatra'); ?></p> |
| 643 |
|
| 644 |
<div class="yatra-form-group yatra-field-full"> |
| 645 |
<textarea |
| 646 |
id="special-requests" |
| 647 |
name="special_requests" |
| 648 |
rows="4" |
| 649 |
placeholder="<?php esc_attr_e('E.g., dietary requirements, accessibility needs, celebration requests...', 'yatra'); ?>" |
| 650 |
></textarea> |
| 651 |
</div> |
| 652 |
</div> |
| 653 |
<?php endif; ?> |
| 654 |
|
| 655 |
<!-- Account Creation Section (only for guests) --> |
| 656 |
<?php |
| 657 |
$allow_guest_checkout = \Yatra\Services\SettingsService::isEnabled('allow_guest_checkout'); |
| 658 |
$require_login = \Yatra\Services\SettingsService::isEnabled('require_login'); |
| 659 |
|
| 660 |
// Show account section if user is not logged in |
| 661 |
if (!is_user_logged_in() && !$is_remaining_payment) : |
| 662 |
?> |
| 663 |
<div class="yatra-booking-section yatra-account-section"> |
| 664 |
<h2 class="yatra-section-title"><?php esc_html_e('Account', 'yatra'); ?></h2> |
| 665 |
|
| 666 |
<?php if ($require_login) : ?> |
| 667 |
<!-- Login Required Message --> |
| 668 |
<div class="yatra-login-required-notice" style="background: #fef3c7; border: 1px solid #f59e0b; padding: 16px; border-radius: 8px; margin-bottom: 16px;"> |
| 669 |
<div style="display: flex; align-items: flex-start; gap: 12px;"> |
| 670 |
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#d97706" stroke-width="2" style="flex-shrink: 0;"> |
| 671 |
<circle cx="12" cy="12" r="10"></circle> |
| 672 |
<line x1="12" y1="8" x2="12" y2="12"></line> |
| 673 |
<line x1="12" y1="16" x2="12.01" y2="16"></line> |
| 674 |
</svg> |
| 675 |
<div> |
| 676 |
<p style="font-weight: 600; color: #92400e; margin: 0 0 8px 0;"><?php esc_html_e('Login Required', 'yatra'); ?></p> |
| 677 |
<p style="color: #a16207; margin: 0 0 12px 0; font-size: 14px;"> |
| 678 |
<?php esc_html_e('You must be logged in to complete this booking. Please log in or create an account.', 'yatra'); ?> |
| 679 |
</p> |
| 680 |
<a href="<?php echo esc_url(wp_login_url(get_permalink())); ?>" class="yatra-login-btn" style="display: inline-flex; align-items: center; gap: 8px; padding: 10px 20px; background: #3b82f6; color: #fff; text-decoration: none; border-radius: 6px; font-weight: 500; font-size: 14px;"> |
| 681 |
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| 682 |
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path> |
| 683 |
<polyline points="10 17 15 12 10 7"></polyline> |
| 684 |
<line x1="15" y1="12" x2="3" y2="12"></line> |
| 685 |
</svg> |
| 686 |
<?php esc_html_e('Log In', 'yatra'); ?> |
| 687 |
</a> |
| 688 |
<span style="margin: 0 12px; color: #a16207;"><?php esc_html_e('or', 'yatra'); ?></span> |
| 689 |
<a href="<?php echo esc_url(wp_registration_url()); ?>" style="color: #3b82f6; text-decoration: none; font-weight: 500;"> |
| 690 |
<?php esc_html_e('Create an Account', 'yatra'); ?> |
| 691 |
</a> |
| 692 |
</div> |
| 693 |
</div> |
| 694 |
</div> |
| 695 |
<input type="hidden" name="login_required" value="1"> |
| 696 |
|
| 697 |
<?php elseif (!$allow_guest_checkout) : ?> |
| 698 |
<!-- Account Required - Must Create Account --> |
| 699 |
<p class="yatra-section-description" style="margin-bottom: 16px;"> |
| 700 |
<?php esc_html_e('Please create an account to complete your booking. This allows you to manage your bookings and receive important updates.', 'yatra'); ?> |
| 701 |
</p> |
| 702 |
|
| 703 |
<div class="yatra-form-row"> |
| 704 |
<div class="yatra-form-group yatra-field-half"> |
| 705 |
<label for="account_password"> |
| 706 |
<?php esc_html_e('Password', 'yatra'); ?> <span class="required">*</span> |
| 707 |
</label> |
| 708 |
<input type="password" id="account_password" name="account_password" required minlength="8" |
| 709 |
placeholder="<?php esc_attr_e('Minimum 8 characters', 'yatra'); ?>"> |
| 710 |
</div> |
| 711 |
<div class="yatra-form-group yatra-field-half"> |
| 712 |
<label for="account_password_confirm"> |
| 713 |
<?php esc_html_e('Confirm Password', 'yatra'); ?> <span class="required">*</span> |
| 714 |
</label> |
| 715 |
<input type="password" id="account_password_confirm" name="account_password_confirm" required minlength="8" |
| 716 |
placeholder="<?php esc_attr_e('Re-enter password', 'yatra'); ?>"> |
| 717 |
</div> |
| 718 |
</div> |
| 719 |
<p class="yatra-field-hint" style="font-size: 13px; color: #6b7280; margin-top: 8px;"> |
| 720 |
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: inline-block; vertical-align: middle; margin-right: 4px;"> |
| 721 |
<circle cx="12" cy="12" r="10"></circle> |
| 722 |
<path d="M12 16v-4"></path> |
| 723 |
<path d="M12 8h.01"></path> |
| 724 |
</svg> |
| 725 |
<?php esc_html_e('Your email address will be used as your username.', 'yatra'); ?> |
| 726 |
</p> |
| 727 |
<input type="hidden" name="create_account" value="1"> |
| 728 |
|
| 729 |
<?php else : ?> |
| 730 |
<!-- Guest Checkout Allowed - Optional Account Creation --> |
| 731 |
<p class="yatra-section-description" style="margin-bottom: 16px;"> |
| 732 |
<?php esc_html_e('Create an account to easily manage your bookings and receive travel updates.', 'yatra'); ?> |
| 733 |
</p> |
| 734 |
|
| 735 |
<label class="yatra-checkbox-label" style="margin-bottom: 16px;"> |
| 736 |
<input type="checkbox" name="create_account" id="create-account" value="1"> |
| 737 |
<span><?php esc_html_e('Create an account for easier booking management', 'yatra'); ?></span> |
| 738 |
</label> |
| 739 |
|
| 740 |
<div class="yatra-account-fields" id="yatra-account-fields" style="display: none;"> |
| 741 |
<div class="yatra-form-row"> |
| 742 |
<div class="yatra-form-group yatra-field-half"> |
| 743 |
<label for="account_password"> |
| 744 |
<?php esc_html_e('Password', 'yatra'); ?> <span class="required">*</span> |
| 745 |
</label> |
| 746 |
<input type="password" id="account_password" name="account_password" minlength="8" |
| 747 |
placeholder="<?php esc_attr_e('Minimum 8 characters', 'yatra'); ?>"> |
| 748 |
</div> |
| 749 |
<div class="yatra-form-group yatra-field-half"> |
| 750 |
<label for="account_password_confirm"> |
| 751 |
<?php esc_html_e('Confirm Password', 'yatra'); ?> <span class="required">*</span> |
| 752 |
</label> |
| 753 |
<input type="password" id="account_password_confirm" name="account_password_confirm" minlength="8" |
| 754 |
placeholder="<?php esc_attr_e('Re-enter password', 'yatra'); ?>"> |
| 755 |
</div> |
| 756 |
</div> |
| 757 |
<p class="yatra-field-hint" style="font-size: 13px; color: #6b7280; margin-top: 8px;"> |
| 758 |
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: inline-block; vertical-align: middle; margin-right: 4px;"> |
| 759 |
<circle cx="12" cy="12" r="10"></circle> |
| 760 |
<path d="M12 16v-4"></path> |
| 761 |
<path d="M12 8h.01"></path> |
| 762 |
</svg> |
| 763 |
<?php esc_html_e('Your email address will be used as your username.', 'yatra'); ?> |
| 764 |
</p> |
| 765 |
</div> |
| 766 |
<?php endif; ?> |
| 767 |
</div> |
| 768 |
<?php elseif (!is_user_logged_in()) : ?> |
| 769 |
<!-- User is logged in --> |
| 770 |
<div class="yatra-booking-section yatra-logged-in-notice"> |
| 771 |
<div style="display: flex; align-items: center; gap: 12px; padding: 12px 16px; background: #ecfdf5; border: 1px solid #10b981; border-radius: 8px;"> |
| 772 |
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#059669" stroke-width="2"> |
| 773 |
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path> |
| 774 |
<polyline points="22 4 12 14.01 9 11.01"></polyline> |
| 775 |
</svg> |
| 776 |
<div> |
| 777 |
<span style="font-weight: 500; color: #065f46;"> |
| 778 |
<?php |
| 779 |
$current_user = wp_get_current_user(); |
| 780 |
printf( |
| 781 |
/* translators: %s: User display name */ |
| 782 |
esc_html__('Logged in as %s', 'yatra'), |
| 783 |
'<strong>' . esc_html($current_user->display_name) . '</strong>' |
| 784 |
); |
| 785 |
?> |
| 786 |
</span> |
| 787 |
<a href="<?php echo esc_url(wp_logout_url(get_permalink())); ?>" style="margin-left: 12px; color: #059669; font-size: 13px;"> |
| 788 |
<?php esc_html_e('Log out', 'yatra'); ?> |
| 789 |
</a> |
| 790 |
</div> |
| 791 |
</div> |
| 792 |
</div> |
| 793 |
<?php endif; ?> |
| 794 |
|
| 795 |
<!-- Terms & Conditions --> |
| 796 |
<div class="yatra-booking-section"> |
| 797 |
<div class="yatra-terms-container"> |
| 798 |
<?php |
| 799 |
$termsPageId = 0; |
| 800 |
$privacyPageId = 0; |
| 801 |
if (class_exists('\\Yatra\\Services\\SettingsService')) { |
| 802 |
$termsPageId = \Yatra\Services\SettingsService::getInt('terms_page_id', 0); |
| 803 |
$privacyPageId = \Yatra\Services\SettingsService::getInt('privacy_policy_page_id', 0); |
| 804 |
} else { |
| 805 |
$termsPageId = (int) get_option('yatra_terms_page_id', 0); |
| 806 |
$privacyPageId = (int) get_option('yatra_privacy_policy_page_id', 0); |
| 807 |
} |
| 808 |
|
| 809 |
$termsUrl = $termsPageId > 0 ? get_permalink($termsPageId) : ''; |
| 810 |
$privacyUrl = $privacyPageId > 0 ? get_permalink($privacyPageId) : ''; |
| 811 |
if ($privacyUrl === '' && function_exists('get_privacy_policy_url')) { |
| 812 |
$privacyUrl = (string) get_privacy_policy_url(); |
| 813 |
} |
| 814 |
?> |
| 815 |
|
| 816 |
<label class="yatra-checkbox-label"> |
| 817 |
<input type="checkbox" name="accept_terms" id="accept-terms" required> |
| 818 |
<span> |
| 819 |
<?php |
| 820 |
printf( |
| 821 |
/* translators: %s: Terms and Conditions link */ |
| 822 |
esc_html__('I have read and agree to the %s', 'yatra'), |
| 823 |
$termsUrl |
| 824 |
? '<a href="' . esc_url($termsUrl) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('Terms and Conditions', 'yatra') . '</a>' |
| 825 |
: '<span>' . esc_html__('Terms and Conditions', 'yatra') . '</span>' |
| 826 |
); |
| 827 |
?> |
| 828 |
<span class="required">*</span> |
| 829 |
</span> |
| 830 |
</label> |
| 831 |
|
| 832 |
<label class="yatra-checkbox-label"> |
| 833 |
<input type="checkbox" name="accept_privacy" id="accept-privacy" required> |
| 834 |
<span> |
| 835 |
<?php |
| 836 |
printf( |
| 837 |
/* translators: %s: Privacy Policy link */ |
| 838 |
esc_html__('I agree to the %s and consent to my data being processed', 'yatra'), |
| 839 |
$privacyUrl |
| 840 |
? '<a href="' . esc_url($privacyUrl) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('Privacy Policy', 'yatra') . '</a>' |
| 841 |
: '<span>' . esc_html__('Privacy Policy', 'yatra') . '</span>' |
| 842 |
); |
| 843 |
?> |
| 844 |
<span class="required">*</span> |
| 845 |
</span> |
| 846 |
</label> |
| 847 |
|
| 848 |
<label class="yatra-checkbox-label"> |
| 849 |
<input type="checkbox" name="subscribe_newsletter" id="subscribe-newsletter"> |
| 850 |
<span><?php esc_html_e('Subscribe to our newsletter for travel tips and exclusive offers', 'yatra'); ?></span> |
| 851 |
</label> |
| 852 |
</div> |
| 853 |
</div> |
| 854 |
|