| @@ -30,29 +30,18 @@ | ||
| 30 | 30 | // Check if Dynamic Form Field module is enabled (Pro feature) |
| 31 | 31 | $is_dynamic_form_enabled = apply_filters('yatra_dynamic_form_field_enabled', false); |
| 32 | 32 | |
| 33 | 33 | // Get form configuration - returns default config if module disabled, custom config if enabled |
| 34 | -$form_config = yatra_get_booking_form_config(); | |
| 34 | +// Scoped to the trip being booked, so a field or section limited to certain | |
| 35 | +// trips (Pro) is neither rendered nor required here. | |
| 36 | +$form_config = yatra_get_booking_form_config(!empty($trip_id) ? (int) $trip_id : null); | |
| 35 | 37 | |
| 36 | 38 | |
| 37 | -// Country list for country type fields | |
| 38 | -$countries = [ | |
| 39 | - 'AF' => 'Afghanistan', 'AL' => 'Albania', 'DZ' => 'Algeria', 'AR' => 'Argentina', | |
| 40 | - 'AU' => 'Australia', 'AT' => 'Austria', 'BD' => 'Bangladesh', 'BE' => 'Belgium', | |
| 41 | - 'BR' => 'Brazil', 'BT' => 'Bhutan', 'CA' => 'Canada', 'CN' => 'China', | |
| 42 | - 'CO' => 'Colombia', 'CZ' => 'Czech Republic', 'DK' => 'Denmark', 'EG' => 'Egypt', | |
| 43 | - 'FI' => 'Finland', 'FR' => 'France', 'DE' => 'Germany', 'GR' => 'Greece', | |
| 44 | - 'HK' => 'Hong Kong', 'HU' => 'Hungary', 'IS' => 'Iceland', 'IN' => 'India', | |
| 45 | - 'ID' => 'Indonesia', 'IE' => 'Ireland', 'IL' => 'Israel', 'IT' => 'Italy', | |
| 46 | - 'JP' => 'Japan', 'KE' => 'Kenya', 'KR' => 'South Korea', 'MY' => 'Malaysia', | |
| 47 | - 'MV' => 'Maldives', 'MX' => 'Mexico', 'NL' => 'Netherlands', 'NZ' => 'New Zealand', | |
| 48 | - 'NP' => 'Nepal', 'NO' => 'Norway', 'PK' => 'Pakistan', 'PE' => 'Peru', | |
| 49 | - 'PH' => 'Philippines', 'PL' => 'Poland', 'PT' => 'Portugal', 'RO' => 'Romania', | |
| 50 | - 'RU' => 'Russia', 'SA' => 'Saudi Arabia', 'SG' => 'Singapore', 'ZA' => 'South Africa', | |
| 51 | - 'ES' => 'Spain', 'LK' => 'Sri Lanka', 'SE' => 'Sweden', 'CH' => 'Switzerland', | |
| 52 | - 'TW' => 'Taiwan', 'TH' => 'Thailand', 'TR' => 'Turkey', 'AE' => 'United Arab Emirates', | |
| 53 | - 'GB' => 'United Kingdom', 'US' => 'United States', 'VN' => 'Vietnam' | |
| 54 | -]; | |
| 39 | +// Country list — pulls from the canonical FormatHelper source so every | |
| 40 | +// dropdown (admin, public booking, Pro modules) shows the same full | |
| 41 | +// ISO-3166-1 list. Operators that want a "popular countries" subset | |
| 42 | +// or custom ordering apply the `yatra_countries_list` filter. | |
| 43 | +$countries = \Yatra\Helpers\FormatHelper::getCountries(); | |
| 55 | 44 | |
| 56 | 45 | /** |
| 57 | 46 | * Render a single form field based on configuration |
| 58 | 47 | * |
| @@ -66,9 +55,27 @@ | ||
| 66 | 55 | function yatra_render_form_field($field, $prefix = '', $countries = [], $custom_name = null, $custom_id = null) { |
| 67 | 56 | if (empty($field['enabled'])) { |
| 68 | 57 | return; |
| 69 | 58 | } |
| 70 | - | |
| 59 | + | |
| 60 | + // Display-only text block: render admin-authored content (safe HTML) at this | |
| 61 | + // position between fields. It is NOT an input — no label, name, value or | |
| 62 | + // required handling — so it submits nothing and is never validated. | |
| 63 | + if (($field['type'] ?? '') === 'text_block') { | |
| 64 | + $tb_width = 'yatra-field-full'; | |
| 65 | + if (($field['width'] ?? '') === 'half') { | |
| 66 | + $tb_width = 'yatra-field-half'; | |
| 67 | + } elseif (($field['width'] ?? '') === 'third') { | |
| 68 | + $tb_width = 'yatra-field-third'; | |
| 69 | + } | |
| 70 | + ?> | |
| 71 | + <div class="yatra-form-group yatra-form-text-block <?php echo esc_attr($tb_width); ?>" data-applies-to="<?php echo esc_attr($field['applies_to'] ?? 'all'); ?>"> | |
| 72 | + <?php echo wpautop(wp_kses_post(yatra_translate_form_string($field['content'] ?? ''))); ?> | |
| 73 | + </div> | |
| 74 | + <?php | |
| 75 | + return; | |
| 76 | + } | |
| 77 | + | |
| 71 | 78 | $field_id = $custom_id ? esc_attr($custom_id) : esc_attr($prefix . $field['id']); |
| 72 | 79 | $field_name = $custom_name ? esc_attr($custom_name) : esc_attr($prefix . $field['id']); |
| 73 | 80 | $required = !empty($field['required']); |
| 74 | 81 | $required_attr = $required ? 'required' : ''; |
| @@ -99,12 +106,12 @@ | ||
| 99 | 106 | } |
| 100 | 107 | } |
| 101 | 108 | $field_type = $field['type'] ?? 'text'; |
| 102 | 109 | ?> |
| 103 | - <div class="yatra-form-group <?php echo esc_attr($width_class); ?> <?php echo $field_type === 'checkbox' ? 'yatra-form-group--checkbox' : ''; ?>"> | |
| 110 | + <div class="yatra-form-group <?php echo esc_attr($width_class); ?> <?php echo $field_type === 'checkbox' ? 'yatra-form-group--checkbox' : ''; ?>" data-applies-to="<?php echo esc_attr($field['applies_to'] ?? 'all'); ?>"> | |
| 104 | 111 | <?php if ($field_type !== 'checkbox') : ?> |
| 105 | 112 | <label for="<?php echo $field_id; ?>"> |
| 106 | - <?php echo esc_html($field['label']); ?> <?php echo $required_star; ?> | |
| 113 | + <?php echo esc_html(yatra_translate_form_string($field['label'])); ?> <?php echo $required_star; ?> | |
| 107 | 114 | </label> |
| 108 | 115 | <?php endif; ?> |
| 109 | 116 | <?php |
| 110 | 117 | switch ($field['type']) { |
| @@ -119,9 +126,9 @@ | ||
| 119 | 126 | value="1" |
| 120 | 127 | <?php checked($is_checked, true, false); ?> |
| 121 | 128 | <?php echo $required_attr; ?> |
| 122 | 129 | /> |
| 123 | - <span><?php echo esc_html($field['label']); ?><?php echo $required_star; ?></span> | |
| 130 | + <span><?php echo esc_html(yatra_translate_form_string($field['label'])); ?><?php echo $required_star; ?></span> | |
| 124 | 131 | </label> |
| 125 | 132 | <?php |
| 126 | 133 | break; |
| 127 | 134 | case 'select': |
| @@ -126,13 +133,13 @@ | ||
| 126 | 133 | break; |
| 127 | 134 | case 'select': |
| 128 | 135 | ?> |
| 129 | 136 | <select id="<?php echo $field_id; ?>" name="<?php echo $field_name; ?>" <?php echo $required_attr; ?>> |
| 130 | - <option value=""><?php echo esc_html($field['placeholder'] ?? 'Select...'); ?></option> | |
| 137 | + <option value=""><?php echo esc_html(yatra_translate_form_string($field['placeholder'] ?? '') ?: __('Select...', 'yatra')); ?></option> | |
| 131 | 138 | <?php if (!empty($field['options'])) : ?> |
| 132 | 139 | <?php foreach ($field['options'] as $option) : ?> |
| 133 | 140 | <option value="<?php echo esc_attr($option['value']); ?>" <?php echo selected($prefill_value, (string) ($option['value'] ?? ''), false); ?>> |
| 134 | - <?php echo esc_html($option['label']); ?> | |
| 141 | + <?php echo esc_html(yatra_translate_form_string($option['label'] ?? '')); ?> | |
| 135 | 142 | </option> |
| 136 | 143 | <?php endforeach; ?> |
| 137 | 144 | <?php endif; ?> |
| 138 | 145 | </select> |
| @@ -139,11 +146,18 @@ | ||
| 139 | 146 | <?php |
| 140 | 147 | break; |
| 141 | 148 | |
| 142 | 149 | case 'country': |
| 150 | + // Flag base is handed to the field (same contract as the phone | |
| 151 | + // widget) so country-select.js can build flag URLs without | |
| 152 | + // knowing the plugin URL. The select still renders and submits | |
| 153 | + // normally — the widget is a progressive enhancement over it. | |
| 154 | + $country_flag_base = \defined('YATRA_PLUGIN_URL') ? YATRA_PLUGIN_URL . 'assets/img/flags/' : ''; | |
| 143 | 155 | ?> |
| 144 | - <select id="<?php echo $field_id; ?>" name="<?php echo $field_name; ?>" <?php echo $required_attr; ?>> | |
| 145 | - <option value=""><?php echo esc_html($field['placeholder'] ?? 'Select Country'); ?></option> | |
| 156 | + <select id="<?php echo $field_id; ?>" name="<?php echo $field_name; ?>" <?php echo $required_attr; ?> | |
| 157 | + data-yatra-country-select | |
| 158 | + data-flag-base="<?php echo esc_url($country_flag_base); ?>"> | |
| 159 | + <option value=""><?php echo esc_html(yatra_translate_form_string($field['placeholder'] ?? '') ?: __('Select Country', 'yatra')); ?></option> | |
| 146 | 160 | <?php foreach ($countries as $code => $name) : ?> |
| 147 | 161 | <option value="<?php echo esc_attr($code); ?>" <?php echo selected($prefill_value, (string) $code, false); ?>> |
| 148 | 162 | <?php echo esc_html($name); ?> |
| 149 | 163 | </option> |
| @@ -156,9 +170,9 @@ | ||
| 156 | 170 | ?> |
| 157 | 171 | <textarea |
| 158 | 172 | id="<?php echo $field_id; ?>" |
| 159 | 173 | name="<?php echo $field_name; ?>" |
| 160 | - placeholder="<?php echo esc_attr($field['placeholder'] ?? ''); ?>" | |
| 174 | + placeholder="<?php echo esc_attr(yatra_translate_form_string($field['placeholder'] ?? '')); ?>" | |
| 161 | 175 | <?php echo $required_attr; ?> |
| 162 | 176 | rows="3" |
| 163 | 177 | ><?php echo esc_textarea($prefill_value); ?></textarea> |
| 164 | 178 | <?php |
| @@ -165,14 +179,33 @@ | ||
| 165 | 179 | break; |
| 166 | 180 | |
| 167 | 181 | case 'date': |
| 168 | 182 | $min_attr = ''; |
| 183 | + // Date-of-birth-style fields are bounded to today and flagged so | |
| 184 | + // booking.js can upgrade them to a picker with fast year jumping | |
| 185 | + // (a native date popup steps one month at a time). The class is | |
| 186 | + // the JS hook; if JS/flatpickr is unavailable the input stays a | |
| 187 | + // working native <input type="date">. | |
| 188 | + $is_dob = (stripos($field_id, 'birth') !== false || stripos($field_id, 'dob') !== false | |
| 189 | + || stripos($field_name, 'birth') !== false || stripos($field_name, 'dob') !== false); | |
| 190 | + $dob_attr = $is_dob ? ' data-yatra-dob="1" max="' . esc_attr(date('Y-m-d')) . '"' : ''; | |
| 191 | + // Native date inputs ignore the placeholder, but once booking.js | |
| 192 | + // upgrades the field to a flatpickr text input the placeholder is | |
| 193 | + // what keeps it visually consistent with the other text fields | |
| 194 | + // (otherwise it renders blank). Use the field's own placeholder | |
| 195 | + // when set, else a format hint matching flatpickr's Y-m-d output. | |
| 196 | + $date_placeholder = !empty($field['placeholder']) | |
| 197 | + ? yatra_translate_form_string($field['placeholder']) | |
| 198 | + : 'YYYY-MM-DD'; | |
| 169 | 199 | ?> |
| 170 | - <input | |
| 171 | - type="date" | |
| 172 | - id="<?php echo $field_id; ?>" | |
| 200 | + <input | |
| 201 | + type="date" | |
| 202 | + class="yatra-js-date" | |
| 203 | + id="<?php echo $field_id; ?>" | |
| 173 | 204 | name="<?php echo $field_name; ?>" |
| 205 | + placeholder="<?php echo esc_attr($date_placeholder); ?>" | |
| 174 | 206 | <?php echo $min_attr; ?> |
| 207 | + <?php echo $dob_attr; ?> | |
| 175 | 208 | <?php echo $required_attr; ?> |
| 176 | 209 | value="<?php echo esc_attr($prefill_value); ?>" |
| 177 | 210 | > |
| 178 | 211 | <?php |
| @@ -178,18 +211,81 @@ | ||
| 178 | 211 | <?php |
| 179 | 212 | break; |
| 180 | 213 | |
| 181 | 214 | default: // text, email, tel, number |
| 182 | - ?> | |
| 183 | - <input | |
| 184 | - type="<?php echo esc_attr($field['type']); ?>" | |
| 185 | - id="<?php echo $field_id; ?>" | |
| 186 | - name="<?php echo $field_name; ?>" | |
| 187 | - placeholder="<?php echo esc_attr($field['placeholder'] ?? ''); ?>" | |
| 188 | - <?php echo $required_attr; ?> | |
| 189 | - value="<?php echo esc_attr($prefill_value); ?>" | |
| 190 | - > | |
| 191 | - <?php | |
| 215 | + // Phone (tel) fields get the international country-code widget | |
| 216 | + // UNLESS it has been explicitly disabled in the (Pro) form | |
| 217 | + // builder. A missing key means ON, so every existing form and | |
| 218 | + // customized field keeps the widget without needing a re-save. | |
| 219 | + $is_phone_widget = (($field['type'] ?? '') === 'tel') | |
| 220 | + && (!array_key_exists('show_country_code', $field) || !empty($field['show_country_code'])); | |
| 221 | + | |
| 222 | + if ($is_phone_widget) { | |
| 223 | + // Split the stored value for display: an international value | |
| 224 | + // ("+9779806015400") is parsed into country + national part; | |
| 225 | + // a legacy bare number is left in the number box under the | |
| 226 | + // default country (never rewritten on display). | |
| 227 | + $pv = (string) $prefill_value; | |
| 228 | + $detected = \Yatra\Helpers\FormatHelper::detectPhoneCountry($pv); | |
| 229 | + if ($detected !== null) { | |
| 230 | + $p_iso = $detected['iso']; | |
| 231 | + $p_dial = $detected['dial']; | |
| 232 | + $p_national = $detected['national']; | |
| 233 | + } else { | |
| 234 | + $p_iso = \Yatra\Helpers\FormatHelper::getDefaultPhoneCountry(); | |
| 235 | + $p_dial = \Yatra\Helpers\FormatHelper::getDialingCode($p_iso); | |
| 236 | + $p_national = $pv; | |
| 237 | + } | |
| 238 | + | |
| 239 | + // Companion hidden field holding the chosen ISO. Array-style | |
| 240 | + // names (travelers[0][phone]) become travelers[0][phone_country]; | |
| 241 | + // the server combines dial code + national number on submit. | |
| 242 | + if (preg_match('/^(.*)\[([^\]]+)\]$/', $field_name, $mm)) { | |
| 243 | + $companion_name = $mm[1] . '[' . $mm[2] . '_country]'; | |
| 244 | + } else { | |
| 245 | + $companion_name = $field_name . '_country'; | |
| 246 | + } | |
| 247 | + | |
| 248 | + $flag_base = \defined('YATRA_PLUGIN_URL') ? YATRA_PLUGIN_URL . 'assets/img/flags/' : ''; | |
| 249 | + ?> | |
| 250 | + <div class="yatra-phone-field" data-yatra-phone | |
| 251 | + data-default-iso="<?php echo esc_attr($p_iso); ?>" | |
| 252 | + data-flag-base="<?php echo esc_url($flag_base); ?>"> | |
| 253 | + <button type="button" class="yatra-phone-country" aria-haspopup="listbox" aria-expanded="false" | |
| 254 | + aria-label="<?php esc_attr_e('Select country dialing code', 'yatra'); ?>"> | |
| 255 | + <img class="yatra-phone-flag" src="<?php echo esc_url($flag_base . strtolower($p_iso) . '.svg'); ?>" | |
| 256 | + alt="" width="22" height="16" loading="lazy"> | |
| 257 | + <span class="yatra-phone-dial">+<?php echo esc_html($p_dial); ?></span> | |
| 258 | + <span class="yatra-phone-caret" aria-hidden="true"></span> | |
| 259 | + </button> | |
| 260 | + <input | |
| 261 | + type="tel" | |
| 262 | + class="yatra-phone-number" | |
| 263 | + id="<?php echo $field_id; ?>" | |
| 264 | + name="<?php echo $field_name; ?>" | |
| 265 | + value="<?php echo esc_attr($p_national); ?>" | |
| 266 | + placeholder="<?php echo esc_attr(yatra_translate_form_string($field['placeholder'] ?? '')); ?>" | |
| 267 | + inputmode="tel" | |
| 268 | + autocomplete="tel-national" | |
| 269 | + <?php echo $required_attr; ?> | |
| 270 | + > | |
| 271 | + <input type="hidden" class="yatra-phone-iso" | |
| 272 | + name="<?php echo esc_attr($companion_name); ?>" | |
| 273 | + value="<?php echo esc_attr($p_iso); ?>"> | |
| 274 | + </div> | |
| 275 | + <?php | |
| 276 | + } else { | |
| 277 | + ?> | |
| 278 | + <input | |
| 279 | + type="<?php echo esc_attr($field['type']); ?>" | |
| 280 | + id="<?php echo $field_id; ?>" | |
| 281 | + name="<?php echo $field_name; ?>" | |
| 282 | + placeholder="<?php echo esc_attr(yatra_translate_form_string($field['placeholder'] ?? '')); ?>" | |
| 283 | + <?php echo $required_attr; ?> | |
| 284 | + value="<?php echo esc_attr($prefill_value); ?>" | |
| 285 | + > | |
| 286 | + <?php | |
| 287 | + } | |
| 192 | 288 | break; |
| 193 | 289 | } |
| 194 | 290 | ?> |
| 195 | 291 | </div> |
| @@ -227,11 +323,11 @@ | ||
| 227 | 323 | } |
| 228 | 324 | } |
| 229 | 325 | ?> |
| 230 | 326 | <div class="yatra-booking-section"> |
| 231 | - <h2 class="yatra-section-title"><?php echo esc_html($section_config['title'] ?? ''); ?></h2> | |
| 327 | + <h2 class="yatra-section-title"><?php echo esc_html(yatra_translate_form_string($section_config['title'] ?? '')); ?></h2> | |
| 232 | 328 | <?php if (!empty($section_config['description'])) : ?> |
| 233 | - <p class="yatra-section-description"><?php echo esc_html($section_config['description']); ?></p> | |
| 329 | + <p class="yatra-section-description"><?php echo esc_html(yatra_translate_form_string($section_config['description'])); ?></p> | |
| 234 | 330 | <?php endif; ?> |
| 235 | 331 | |
| 236 | 332 | <?php foreach ($grouped_fields as $section_key => $section_fields) : ?> |
| 237 | 333 | <?php if ($section_key !== 'main') : ?> |
| @@ -409,17 +505,24 @@ | ||
| 409 | 505 | $category_id = $pt->category_id ?? $index; |
| 410 | 506 | $category_label = $pt->category_label ?? __('Traveler', 'yatra'); |
| 411 | 507 | $category_price = isset($pt->effective_price) ? (float) $pt->effective_price : ($pt->sale_price ?? $pt->discounted_price ?? $pt->original_price ?? 0); |
| 412 | 508 | $count = isset($traveler_counts[$category_id]) ? (int) $traveler_counts[$category_id] : ($index === 0 ? 1 : 0); |
| 413 | - $subtotal = $category_price * $count; | |
| 509 | + // Single source of truth for the line amount (per-person × count, flat | |
| 510 | + // per-group, or per-block group pricing). Defaults to the prior flat/ | |
| 511 | + // multiply behaviour when group_overflow is absent. | |
| 512 | + $pt_pricing_mode = $pt->pricing_mode ?? 'per_person'; | |
| 513 | + $subtotal = \Yatra\Services\TripPricingService::categoryLineSubtotal($pt, $count, $category_price); | |
| 414 | 514 | |
| 415 | 515 | $age_info = ''; |
| 416 | 516 | if (isset($pt->age_min) || isset($pt->age_max)) { |
| 417 | 517 | if (isset($pt->age_min) && isset($pt->age_max)) { |
| 418 | - $age_info = sprintf(__('(Age %d-%d)', 'yatra'), $pt->age_min, $pt->age_max); | |
| 518 | + /* translators: 1: minimum age, 2: maximum age. */ | |
| 519 | + $age_info = sprintf(__('(Age %1$d-%2$d)', 'yatra'), $pt->age_min, $pt->age_max); | |
| 419 | 520 | } elseif (isset($pt->age_min)) { |
| 521 | + /* translators: %d: minimum age. */ | |
| 420 | 522 | $age_info = sprintf(__('(Age %d+)', 'yatra'), $pt->age_min); |
| 421 | 523 | } else { |
| 524 | + /* translators: %d: maximum age. */ | |
| 422 | 525 | $age_info = sprintf(__('(Up to age %d)', 'yatra'), $pt->age_max); |
| 423 | 526 | } |
| 424 | 527 | } |
| 425 | 528 | |
| @@ -428,8 +531,9 @@ | ||
| 428 | 531 | 'category_label' => $category_label, |
| 429 | 532 | 'category_price' => $category_price, |
| 430 | 533 | 'count' => $count, |
| 431 | 534 | 'subtotal' => $subtotal, |
| 535 | + 'pricing_mode' => $pt_pricing_mode, | |
| 432 | 536 | 'age_info' => $age_info, |
| 433 | 537 | ]; |
| 434 | 538 | |
| 435 | 539 | $calculated_total += $subtotal; |
| @@ -455,13 +559,19 @@ | ||
| 455 | 559 | $initial_due_amount = $initial_total_amount; |
| 456 | 560 | |
| 457 | 561 | ?> |
| 458 | 562 | |
| 459 | -<?php if (!empty($traveler_config) && !$is_remaining_payment) : ?> | |
| 563 | +<?php | |
| 564 | +// Honour the Traveler section's enable flag (Pro Dynamic Form module), mirroring | |
| 565 | +// the Contact/Emergency sections. The default config has no `enabled` key, so an | |
| 566 | +// un-customised/Free site treats the section as enabled and renders as before. | |
| 567 | +$traveler_section_enabled = !isset($traveler_config['enabled']) || (bool) $traveler_config['enabled']; | |
| 568 | +?> | |
| 569 | +<?php if (!empty($traveler_config) && $traveler_section_enabled && !$is_remaining_payment) : ?> | |
| 460 | 570 | <div class="yatra-booking-section"> |
| 461 | - <h2 class="yatra-section-title"><?php echo esc_html($traveler_config['title'] ?? __('Traveler Information', 'yatra')); ?></h2> | |
| 571 | + <h2 class="yatra-section-title"><?php echo esc_html(yatra_translate_form_string($traveler_config['title'] ?? '') ?: __('Traveler Information', 'yatra')); ?></h2> | |
| 462 | 572 | <?php if (!empty($traveler_config['description'])) : ?> |
| 463 | - <p class="yatra-section-description"><?php echo esc_html($traveler_config['description']); ?></p> | |
| 573 | + <p class="yatra-section-description"><?php echo esc_html(yatra_translate_form_string($traveler_config['description'])); ?></p> | |
| 464 | 574 | <?php endif; ?> |
| 465 | 575 | |
| 466 | 576 | <div id="yatra-travelers-container"> |
| 467 | 577 | <?php |
| @@ -487,15 +597,31 @@ | ||
| 487 | 597 | if (!empty($traveler_category_map[$i])) { |
| 488 | 598 | $category_info = $traveler_category_map[$i]; |
| 489 | 599 | $category_label = $category_info['category_label']; |
| 490 | 600 | $category_index = $category_info['category_index']; |
| 491 | - $traveler_label = sprintf(__('%s %d', 'yatra'), $category_label, $category_index); | |
| 601 | + /* translators: 1: traveler category label (e.g. Adult, Child), 2: sequential index within that category. */ | |
| 602 | + $traveler_label = sprintf(__('%1$s %2$d', 'yatra'), $category_label, $category_index); | |
| 492 | 603 | if ($i === 1) { |
| 493 | 604 | $traveler_label .= ' (' . __('Lead Traveler', 'yatra') . ')'; |
| 494 | 605 | } |
| 495 | 606 | } else { |
| 607 | + /* translators: %d: traveler sequence number. */ | |
| 496 | 608 | $traveler_label = ($i === 1) ? __('Traveler 1 (Lead Traveler)', 'yatra') : sprintf(__('Traveler %d', 'yatra'), $i); |
| 497 | 609 | } |
| 610 | + | |
| 611 | + // Fields targeted at the lead traveler (applies_to === 'lead') render | |
| 612 | + // only for Traveler 1; everything else ('all' / unset) renders for | |
| 613 | + // every traveler. Empty subsections are dropped so additional | |
| 614 | + // travelers don't get an orphan heading. | |
| 615 | + $traveler_grouped_fields = []; | |
| 616 | + foreach ($grouped_traveler_fields as $sk => $sf) { | |
| 617 | + $applicable = array_values(array_filter($sf, function ($f) use ($i) { | |
| 618 | + return ($f['applies_to'] ?? 'all') !== 'lead' || $i === 1; | |
| 619 | + })); | |
| 620 | + if (!empty($applicable)) { | |
| 621 | + $traveler_grouped_fields[$sk] = $applicable; | |
| 622 | + } | |
| 623 | + } | |
| 498 | 624 | ?> |
| 499 | 625 | <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; ?>> |
| 500 | 626 | <div class="yatra-traveler-header"> |
| 501 | 627 | <h3 class="yatra-traveler-title"><?php echo esc_html($traveler_label); ?></h3> |
| @@ -503,9 +629,9 @@ | ||
| 503 | 629 | <span class="yatra-traveler-note"><?php esc_html_e('Additional traveler', 'yatra'); ?></span> |
| 504 | 630 | <?php endif; ?> |
| 505 | 631 | </div> |
| 506 | 632 | |
| 507 | - <?php foreach ($grouped_traveler_fields as $section_key => $section_fields) : ?> | |
| 633 | + <?php foreach ($traveler_grouped_fields as $section_key => $section_fields) : ?> | |
| 508 | 634 | <?php if ($section_key !== 'main') : ?> |
| 509 | 635 | <div class="yatra-traveler-subsection"> |
| 510 | 636 | <h4 class="yatra-subsection-title"> |
| 511 | 637 | <?php |
| @@ -549,16 +675,26 @@ | ||
| 549 | 675 | */ |
| 550 | 676 | $flexible_payments_enabled = apply_filters('yatra_flexible_payments_enabled', false); |
| 551 | 677 | |
| 552 | 678 | /** |
| 553 | - * Filter to get payment method options | |
| 554 | - * Pro module can add deposit/partial payment options via this filter | |
| 679 | + * Filter to get payment method options. | |
| 680 | + * | |
| 681 | + * Pro module (FlexiblePayments) can add deposit/partial payment options here. | |
| 682 | + * We pass `trip_id` so the Pro module can look up per-trip overrides | |
| 683 | + * (trip.deposit_amount / trip.deposit_percentage) and expose the deposit | |
| 684 | + * option even when the site-wide "deposit_required" flag is off — i.e. a | |
| 685 | + * single trip can opt in to a custom deposit without enabling deposits for | |
| 686 | + * the whole catalogue. | |
| 555 | 687 | */ |
| 556 | 688 | $payment_method_options = apply_filters('yatra_payment_method_options', [], [ |
| 557 | - 'deposit_required' => $deposit_required, | |
| 558 | - 'deposit_percentage' => $deposit_percentage, | |
| 559 | - 'partial_payment' => $partial_payment, | |
| 689 | + 'trip_id' => isset($trip_id) ? (int) $trip_id : 0, | |
| 690 | + 'deposit_required' => $deposit_required, | |
| 691 | + 'deposit_percentage' => $deposit_percentage, | |
| 692 | + 'partial_payment' => $partial_payment, | |
| 560 | 693 | 'partial_payment_percentage' => $partial_payment_percentage, |
| 694 | + // Tour start (when a date is already chosen) → Pro hides deposit/partial for | |
| 695 | + // tour-anchored payments when the tour is inside the balance-due window. | |
| 696 | + 'travel_date' => isset($booking->travel_date) ? (string) $booking->travel_date : '', | |
| 561 | 697 | ]); |
| 562 | 698 | |
| 563 | 699 | $has_flexible_options = $flexible_payments_enabled && !empty($payment_method_options); |
| 564 | 700 | ?> |
| @@ -653,20 +789,28 @@ | ||
| 653 | 789 | </div> |
| 654 | 790 | <?php endif; ?> |
| 655 | 791 | |
| 656 | 792 | <!-- Account Creation Section (only for guests) --> |
| 657 | -<?php | |
| 793 | +<?php | |
| 658 | 794 | $allow_guest_checkout = \Yatra\Services\SettingsService::isEnabled('allow_guest_checkout'); |
| 659 | 795 | $require_login = \Yatra\Services\SettingsService::isEnabled('require_login'); |
| 796 | +// Account CREATION is gated by the same setting the backend enforces | |
| 797 | +// (AuthController rejects registration when this is off). When registration is | |
| 798 | +// disabled we never offer account-creation UI. | |
| 799 | +$registration_enabled = \Yatra\Services\SettingsService::isEnabled('customer_registration'); | |
| 660 | 800 | |
| 661 | -// Show account section if user is not logged in | |
| 662 | -if (!is_user_logged_in() && !$is_remaining_payment) : | |
| 801 | +// Show the account section only when it has something to render: login is | |
| 802 | +// required, guest checkout is off (must authenticate), or registration is on | |
| 803 | +// (optional/required account creation). When registration is OFF and guests are | |
| 804 | +// allowed, there is nothing to show — proceed as a pure guest. | |
| 805 | +if (!is_user_logged_in() && !$is_remaining_payment | |
| 806 | + && ($require_login || !$allow_guest_checkout || $registration_enabled)) : | |
| 663 | 807 | ?> |
| 664 | 808 | <div class="yatra-booking-section yatra-account-section"> |
| 665 | 809 | <h2 class="yatra-section-title"><?php esc_html_e('Account', 'yatra'); ?></h2> |
| 666 | 810 | |
| 667 | - <?php if ($require_login) : ?> | |
| 668 | - <!-- Login Required Message --> | |
| 811 | + <?php if ($require_login || (!$allow_guest_checkout && !$registration_enabled)) : ?> | |
| 812 | + <!-- Login Required Message (login required, OR guests off with registration disabled) --> | |
| 669 | 813 | <div class="yatra-login-required-notice" style="background: #fef3c7; border: 1px solid #f59e0b; padding: 16px; border-radius: 8px; margin-bottom: 16px;"> |
| 670 | 814 | <div style="display: flex; align-items: flex-start; gap: 12px;"> |
| 671 | 815 | <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#d97706" stroke-width="2" style="flex-shrink: 0;"> |
| 672 | 816 | <circle cx="12" cy="12" r="10"></circle> |
| @@ -675,9 +819,11 @@ | ||
| 675 | 819 | </svg> |
| 676 | 820 | <div> |
| 677 | 821 | <p style="font-weight: 600; color: #92400e; margin: 0 0 8px 0;"><?php esc_html_e('Login Required', 'yatra'); ?></p> |
| 678 | 822 | <p style="color: #a16207; margin: 0 0 12px 0; font-size: 14px;"> |
| 679 | - <?php esc_html_e('You must be logged in to complete this booking. Please log in or create an account.', 'yatra'); ?> | |
| 823 | + <?php echo $registration_enabled | |
| 824 | + ? esc_html__('You must be logged in to complete this booking. Please log in or create an account.', 'yatra') | |
| 825 | + : esc_html__('You must be logged in to complete this booking. Please log in to continue.', 'yatra'); ?> | |
| 680 | 826 | </p> |
| 681 | 827 | <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;"> |
| 682 | 828 | <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| 683 | 829 | <path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path> |
| @@ -685,19 +831,21 @@ | ||
| 685 | 831 | <line x1="15" y1="12" x2="3" y2="12"></line> |
| 686 | 832 | </svg> |
| 687 | 833 | <?php esc_html_e('Log In', 'yatra'); ?> |
| 688 | 834 | </a> |
| 835 | + <?php if ($registration_enabled) : ?> | |
| 689 | 836 | <span style="margin: 0 12px; color: #a16207;"><?php esc_html_e('or', 'yatra'); ?></span> |
| 690 | 837 | <a href="<?php echo esc_url(wp_registration_url()); ?>" style="color: #3b82f6; text-decoration: none; font-weight: 500;"> |
| 691 | 838 | <?php esc_html_e('Create an Account', 'yatra'); ?> |
| 692 | 839 | </a> |
| 840 | + <?php endif; ?> | |
| 693 | 841 | </div> |
| 694 | 842 | </div> |
| 695 | 843 | </div> |
| 696 | 844 | <input type="hidden" name="login_required" value="1"> |
| 697 | 845 | |
| 698 | - <?php elseif (!$allow_guest_checkout) : ?> | |
| 699 | - <!-- Account Required - Must Create Account --> | |
| 846 | + <?php elseif (!$allow_guest_checkout && $registration_enabled) : ?> | |
| 847 | + <!-- Account Required - Must Create Account (registration enabled) --> | |
| 700 | 848 | <p class="yatra-section-description" style="margin-bottom: 16px;"> |
| 701 | 849 | <?php esc_html_e('Please create an account to complete your booking. This allows you to manage your bookings and receive important updates.', 'yatra'); ?> |
| 702 | 850 | </p> |
| 703 | 851 | |
| @@ -726,10 +874,10 @@ | ||
| 726 | 874 | <?php esc_html_e('Your email address will be used as your username.', 'yatra'); ?> |
| 727 | 875 | </p> |
| 728 | 876 | <input type="hidden" name="create_account" value="1"> |
| 729 | 877 | |
| 730 | - <?php else : ?> | |
| 731 | - <!-- Guest Checkout Allowed - Optional Account Creation --> | |
| 878 | + <?php elseif ($registration_enabled) : ?> | |
| 879 | + <!-- Guest Checkout Allowed - Optional Account Creation (registration enabled) --> | |
| 732 | 880 | <p class="yatra-section-description" style="margin-bottom: 16px;"> |
| 733 | 881 | <?php esc_html_e('Create an account to easily manage your bookings and receive travel updates.', 'yatra'); ?> |
| 734 | 882 | </p> |
| 735 | 883 | |
| @@ -744,8 +892,9 @@ | ||
| 744 | 892 | <label for="account_password"> |
| 745 | 893 | <?php esc_html_e('Password', 'yatra'); ?> <span class="required">*</span> |
| 746 | 894 | </label> |
| 747 | 895 | <input type="password" id="account_password" name="account_password" minlength="8" |
| 896 | + autocomplete="new-password" | |
| 748 | 897 | placeholder="<?php esc_attr_e('Minimum 8 characters', 'yatra'); ?>"> |
| 749 | 898 | </div> |
| 750 | 899 | <div class="yatra-form-group yatra-field-half"> |
| 751 | 900 | <label for="account_password_confirm"> |
| @@ -751,8 +900,9 @@ | ||
| 751 | 900 | <label for="account_password_confirm"> |
| 752 | 901 | <?php esc_html_e('Confirm Password', 'yatra'); ?> <span class="required">*</span> |
| 753 | 902 | </label> |
| 754 | 903 | <input type="password" id="account_password_confirm" name="account_password_confirm" minlength="8" |
| 904 | + autocomplete="new-password" | |
| 755 | 905 | placeholder="<?php esc_attr_e('Re-enter password', 'yatra'); ?>"> |
| 756 | 906 | </div> |
| 757 | 907 | </div> |
| 758 | 908 | <p class="yatra-field-hint" style="font-size: 13px; color: #6b7280; margin-top: 8px;"> |
| @@ -765,9 +915,9 @@ | ||
| 765 | 915 | </p> |
| 766 | 916 | </div> |
| 767 | 917 | <?php endif; ?> |
| 768 | 918 | </div> |
| 769 | -<?php elseif (!is_user_logged_in()) : ?> | |
| 919 | +<?php elseif (is_user_logged_in()) : ?> | |
| 770 | 920 | <!-- User is logged in --> |
| 771 | 921 | <div class="yatra-booking-section yatra-logged-in-notice"> |
| 772 | 922 | <div style="display: flex; align-items: center; gap: 12px; padding: 12px 16px; background: #ecfdf5; border: 1px solid #10b981; border-radius: 8px;"> |
| 773 | 923 | <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#059669" stroke-width="2"> |