| @@ -30,9 +30,11 @@ | ||
| 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 | 39 | // Country list — pulls from the canonical FormatHelper source so every |
| 38 | 40 | // dropdown (admin, public booking, Pro modules) shows the same full |
| @@ -65,9 +67,9 @@ | ||
| 65 | 67 | } elseif (($field['width'] ?? '') === 'third') { |
| 66 | 68 | $tb_width = 'yatra-field-third'; |
| 67 | 69 | } |
| 68 | 70 | ?> |
| 69 | - <div class="yatra-form-group yatra-form-text-block <?php echo esc_attr($tb_width); ?>"> | |
| 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'); ?>"> | |
| 70 | 72 | <?php echo wpautop(wp_kses_post(yatra_translate_form_string($field['content'] ?? ''))); ?> |
| 71 | 73 | </div> |
| 72 | 74 | <?php |
| 73 | 75 | return; |
| @@ -104,9 +106,9 @@ | ||
| 104 | 106 | } |
| 105 | 107 | } |
| 106 | 108 | $field_type = $field['type'] ?? 'text'; |
| 107 | 109 | ?> |
| 108 | - <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'); ?>"> | |
| 109 | 111 | <?php if ($field_type !== 'checkbox') : ?> |
| 110 | 112 | <label for="<?php echo $field_id; ?>"> |
| 111 | 113 | <?php echo esc_html(yatra_translate_form_string($field['label'])); ?> <?php echo $required_star; ?> |
| 112 | 114 | </label> |
| @@ -144,10 +146,17 @@ | ||
| 144 | 146 | <?php |
| 145 | 147 | break; |
| 146 | 148 | |
| 147 | 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/' : ''; | |
| 148 | 155 | ?> |
| 149 | - <select id="<?php echo $field_id; ?>" name="<?php echo $field_name; ?>" <?php echo $required_attr; ?>> | |
| 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); ?>"> | |
| 150 | 159 | <option value=""><?php echo esc_html(yatra_translate_form_string($field['placeholder'] ?? '') ?: __('Select Country', 'yatra')); ?></option> |
| 151 | 160 | <?php foreach ($countries as $code => $name) : ?> |
| 152 | 161 | <option value="<?php echo esc_attr($code); ?>" <?php echo selected($prefill_value, (string) $code, false); ?>> |
| 153 | 162 | <?php echo esc_html($name); ?> |
| @@ -170,14 +179,33 @@ | ||
| 170 | 179 | break; |
| 171 | 180 | |
| 172 | 181 | case 'date': |
| 173 | 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'; | |
| 174 | 199 | ?> |
| 175 | - <input | |
| 176 | - type="date" | |
| 177 | - id="<?php echo $field_id; ?>" | |
| 200 | + <input | |
| 201 | + type="date" | |
| 202 | + class="yatra-js-date" | |
| 203 | + id="<?php echo $field_id; ?>" | |
| 178 | 204 | name="<?php echo $field_name; ?>" |
| 205 | + placeholder="<?php echo esc_attr($date_placeholder); ?>" | |
| 179 | 206 | <?php echo $min_attr; ?> |
| 207 | + <?php echo $dob_attr; ?> | |
| 180 | 208 | <?php echo $required_attr; ?> |
| 181 | 209 | value="<?php echo esc_attr($prefill_value); ?>" |
| 182 | 210 | > |
| 183 | 211 | <?php |
| @@ -183,18 +211,81 @@ | ||
| 183 | 211 | <?php |
| 184 | 212 | break; |
| 185 | 213 | |
| 186 | 214 | default: // text, email, tel, number |
| 187 | - ?> | |
| 188 | - <input | |
| 189 | - type="<?php echo esc_attr($field['type']); ?>" | |
| 190 | - id="<?php echo $field_id; ?>" | |
| 191 | - name="<?php echo $field_name; ?>" | |
| 192 | - placeholder="<?php echo esc_attr(yatra_translate_form_string($field['placeholder'] ?? '')); ?>" | |
| 193 | - <?php echo $required_attr; ?> | |
| 194 | - value="<?php echo esc_attr($prefill_value); ?>" | |
| 195 | - > | |
| 196 | - <?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 | + } | |
| 197 | 288 | break; |
| 198 | 289 | } |
| 199 | 290 | ?> |
| 200 | 291 | </div> |
| @@ -515,8 +606,22 @@ | ||
| 515 | 606 | } else { |
| 516 | 607 | /* translators: %d: traveler sequence number. */ |
| 517 | 608 | $traveler_label = ($i === 1) ? __('Traveler 1 (Lead Traveler)', 'yatra') : sprintf(__('Traveler %d', 'yatra'), $i); |
| 518 | 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 | + } | |
| 519 | 624 | ?> |
| 520 | 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; ?>> |
| 521 | 626 | <div class="yatra-traveler-header"> |
| 522 | 627 | <h3 class="yatra-traveler-title"><?php echo esc_html($traveler_label); ?></h3> |
| @@ -524,9 +629,9 @@ | ||
| 524 | 629 | <span class="yatra-traveler-note"><?php esc_html_e('Additional traveler', 'yatra'); ?></span> |
| 525 | 630 | <?php endif; ?> |
| 526 | 631 | </div> |
| 527 | 632 | |
| 528 | - <?php foreach ($grouped_traveler_fields as $section_key => $section_fields) : ?> | |
| 633 | + <?php foreach ($traveler_grouped_fields as $section_key => $section_fields) : ?> | |
| 529 | 634 | <?php if ($section_key !== 'main') : ?> |
| 530 | 635 | <div class="yatra-traveler-subsection"> |
| 531 | 636 | <h4 class="yatra-subsection-title"> |
| 532 | 637 | <?php |
| @@ -585,8 +690,11 @@ | ||
| 585 | 690 | 'deposit_required' => $deposit_required, |
| 586 | 691 | 'deposit_percentage' => $deposit_percentage, |
| 587 | 692 | 'partial_payment' => $partial_payment, |
| 588 | 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 : '', | |
| 589 | 697 | ]); |
| 590 | 698 | |
| 591 | 699 | $has_flexible_options = $flexible_payments_enabled && !empty($payment_method_options); |
| 592 | 700 | ?> |
| @@ -681,20 +789,28 @@ | ||
| 681 | 789 | </div> |
| 682 | 790 | <?php endif; ?> |
| 683 | 791 | |
| 684 | 792 | <!-- Account Creation Section (only for guests) --> |
| 685 | -<?php | |
| 793 | +<?php | |
| 686 | 794 | $allow_guest_checkout = \Yatra\Services\SettingsService::isEnabled('allow_guest_checkout'); |
| 687 | 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'); | |
| 688 | 800 | |
| 689 | -// Show account section if user is not logged in | |
| 690 | -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)) : | |
| 691 | 807 | ?> |
| 692 | 808 | <div class="yatra-booking-section yatra-account-section"> |
| 693 | 809 | <h2 class="yatra-section-title"><?php esc_html_e('Account', 'yatra'); ?></h2> |
| 694 | 810 | |
| 695 | - <?php if ($require_login) : ?> | |
| 696 | - <!-- 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) --> | |
| 697 | 813 | <div class="yatra-login-required-notice" style="background: #fef3c7; border: 1px solid #f59e0b; padding: 16px; border-radius: 8px; margin-bottom: 16px;"> |
| 698 | 814 | <div style="display: flex; align-items: flex-start; gap: 12px;"> |
| 699 | 815 | <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#d97706" stroke-width="2" style="flex-shrink: 0;"> |
| 700 | 816 | <circle cx="12" cy="12" r="10"></circle> |
| @@ -703,9 +819,11 @@ | ||
| 703 | 819 | </svg> |
| 704 | 820 | <div> |
| 705 | 821 | <p style="font-weight: 600; color: #92400e; margin: 0 0 8px 0;"><?php esc_html_e('Login Required', 'yatra'); ?></p> |
| 706 | 822 | <p style="color: #a16207; margin: 0 0 12px 0; font-size: 14px;"> |
| 707 | - <?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'); ?> | |
| 708 | 826 | </p> |
| 709 | 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;"> |
| 710 | 828 | <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| 711 | 829 | <path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path> |
| @@ -713,19 +831,21 @@ | ||
| 713 | 831 | <line x1="15" y1="12" x2="3" y2="12"></line> |
| 714 | 832 | </svg> |
| 715 | 833 | <?php esc_html_e('Log In', 'yatra'); ?> |
| 716 | 834 | </a> |
| 835 | + <?php if ($registration_enabled) : ?> | |
| 717 | 836 | <span style="margin: 0 12px; color: #a16207;"><?php esc_html_e('or', 'yatra'); ?></span> |
| 718 | 837 | <a href="<?php echo esc_url(wp_registration_url()); ?>" style="color: #3b82f6; text-decoration: none; font-weight: 500;"> |
| 719 | 838 | <?php esc_html_e('Create an Account', 'yatra'); ?> |
| 720 | 839 | </a> |
| 840 | + <?php endif; ?> | |
| 721 | 841 | </div> |
| 722 | 842 | </div> |
| 723 | 843 | </div> |
| 724 | 844 | <input type="hidden" name="login_required" value="1"> |
| 725 | 845 | |
| 726 | - <?php elseif (!$allow_guest_checkout) : ?> | |
| 727 | - <!-- Account Required - Must Create Account --> | |
| 846 | + <?php elseif (!$allow_guest_checkout && $registration_enabled) : ?> | |
| 847 | + <!-- Account Required - Must Create Account (registration enabled) --> | |
| 728 | 848 | <p class="yatra-section-description" style="margin-bottom: 16px;"> |
| 729 | 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'); ?> |
| 730 | 850 | </p> |
| 731 | 851 | |
| @@ -754,10 +874,10 @@ | ||
| 754 | 874 | <?php esc_html_e('Your email address will be used as your username.', 'yatra'); ?> |
| 755 | 875 | </p> |
| 756 | 876 | <input type="hidden" name="create_account" value="1"> |
| 757 | 877 | |
| 758 | - <?php else : ?> | |
| 759 | - <!-- Guest Checkout Allowed - Optional Account Creation --> | |
| 878 | + <?php elseif ($registration_enabled) : ?> | |
| 879 | + <!-- Guest Checkout Allowed - Optional Account Creation (registration enabled) --> | |
| 760 | 880 | <p class="yatra-section-description" style="margin-bottom: 16px;"> |
| 761 | 881 | <?php esc_html_e('Create an account to easily manage your bookings and receive travel updates.', 'yatra'); ?> |
| 762 | 882 | </p> |
| 763 | 883 | |
| @@ -772,8 +892,9 @@ | ||
| 772 | 892 | <label for="account_password"> |
| 773 | 893 | <?php esc_html_e('Password', 'yatra'); ?> <span class="required">*</span> |
| 774 | 894 | </label> |
| 775 | 895 | <input type="password" id="account_password" name="account_password" minlength="8" |
| 896 | + autocomplete="new-password" | |
| 776 | 897 | placeholder="<?php esc_attr_e('Minimum 8 characters', 'yatra'); ?>"> |
| 777 | 898 | </div> |
| 778 | 899 | <div class="yatra-form-group yatra-field-half"> |
| 779 | 900 | <label for="account_password_confirm"> |
| @@ -779,8 +900,9 @@ | ||
| 779 | 900 | <label for="account_password_confirm"> |
| 780 | 901 | <?php esc_html_e('Confirm Password', 'yatra'); ?> <span class="required">*</span> |
| 781 | 902 | </label> |
| 782 | 903 | <input type="password" id="account_password_confirm" name="account_password_confirm" minlength="8" |
| 904 | + autocomplete="new-password" | |
| 783 | 905 | placeholder="<?php esc_attr_e('Re-enter password', 'yatra'); ?>"> |
| 784 | 906 | </div> |
| 785 | 907 | </div> |
| 786 | 908 | <p class="yatra-field-hint" style="font-size: 13px; color: #6b7280; margin-top: 8px;"> |
| @@ -793,9 +915,9 @@ | ||
| 793 | 915 | </p> |
| 794 | 916 | </div> |
| 795 | 917 | <?php endif; ?> |
| 796 | 918 | </div> |
| 797 | -<?php elseif (!is_user_logged_in()) : ?> | |
| 919 | +<?php elseif (is_user_logged_in()) : ?> | |
| 798 | 920 | <!-- User is logged in --> |
| 799 | 921 | <div class="yatra-booking-section yatra-logged-in-notice"> |
| 800 | 922 | <div style="display: flex; align-items: center; gap: 12px; padding: 12px 16px; background: #ecfdf5; border: 1px solid #10b981; border-radius: 8px;"> |
| 801 | 923 | <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#059669" stroke-width="2"> |