| @@ -30,8 +30,50 @@ | ||
| 30 | 30 | add_action('wp_enqueue_scripts', [$this, 'enqueueAssets']); |
| 31 | 31 | } |
| 32 | 32 | |
| 33 | 33 | /** |
| 34 | + * Resolve the effective trip-listing card layout for one listing. | |
| 35 | + * | |
| 36 | + * The site-wide Design setting (`frontend_listing_card_layout`) is the | |
| 37 | + * default; a shortcode/block may pass a per-instance override. Anything that | |
| 38 | + * is not a concrete layout (empty, "inherit", or an unknown value) falls | |
| 39 | + * back to the global setting, so existing shortcodes/blocks are unchanged. | |
| 40 | + * | |
| 41 | + * @param string $override Per-instance override (card_layout / cardLayout). | |
| 42 | + * @return string One of: standard | compact_mobile | compact_all. | |
| 43 | + */ | |
| 44 | + public static function resolveListingLayout(string $override = ''): string | |
| 45 | + { | |
| 46 | + $override = strtolower(trim($override)); | |
| 47 | + if (in_array($override, ['standard', 'compact_mobile', 'compact_all'], true)) { | |
| 48 | + return $override; | |
| 49 | + } | |
| 50 | + | |
| 51 | + $global = \Yatra\Services\SettingsService::getString('frontend_listing_card_layout', 'standard'); | |
| 52 | + return in_array($global, ['standard', 'compact_mobile', 'compact_all'], true) ? $global : 'standard'; | |
| 53 | + } | |
| 54 | + | |
| 55 | + /** | |
| 56 | + * Map a resolved layout to the container CSS class(es) the listing CSS keys | |
| 57 | + * off. Placed on the listing container (archive .yatra-listing-page, or the | |
| 58 | + * shortcode/block wrapper) so each listing can carry its own layout and an | |
| 59 | + * override never leaks into other listings on the same page. | |
| 60 | + * | |
| 61 | + * @param string $layout standard | compact_mobile | compact_all | |
| 62 | + * @return string Space-separated class list ('' for standard). | |
| 63 | + */ | |
| 64 | + public static function listingLayoutClasses(string $layout): string | |
| 65 | + { | |
| 66 | + if ($layout === 'compact_mobile') { | |
| 67 | + return 'yatra-listing-compact'; | |
| 68 | + } | |
| 69 | + if ($layout === 'compact_all') { | |
| 70 | + return 'yatra-listing-compact yatra-listing-compact--all'; | |
| 71 | + } | |
| 72 | + return ''; | |
| 73 | + } | |
| 74 | + | |
| 75 | + /** | |
| 34 | 76 | * Register Font Awesome (optional) and common.css so block editor + shortcode styles can |
| 35 | 77 | * depend on `yatra-common` (shared @keyframes: yatra-spin, yatra-shimmer, etc.). |
| 36 | 78 | */ |
| 37 | 79 | /** |
| @@ -286,14 +328,22 @@ | ||
| 286 | 328 | 'yatra', |
| 287 | 329 | YATRA_PLUGIN_PATH . 'i18n/languages' |
| 288 | 330 | ); |
| 289 | 331 | } |
| 332 | + // Wishlist "Login" should send guests to the configured | |
| 333 | + // My Account page (Settings → Permalink slug), not the raw | |
| 334 | + // wp-login.php screen. Fall back to wp_login_url() only when | |
| 335 | + // no account base is configured so the button never dead-ends. | |
| 336 | + $yatraAccountBase = \Yatra\Services\SettingsService::getAccountBase(); | |
| 337 | + $yatraAccountLoginUrl = $yatraAccountBase !== '' | |
| 338 | + ? home_url('/' . trim($yatraAccountBase, '/') . '/') | |
| 339 | + : wp_login_url(); | |
| 290 | 340 | wp_localize_script('yatra-listing-wishlist', 'yatraWishlistConfig', [ |
| 291 | 341 | 'enabled' => true, |
| 292 | 342 | 'restUrl' => rest_url('yatra/v1'), |
| 293 | 343 | 'nonce' => wp_create_nonce('wp_rest'), |
| 294 | 344 | 'isLoggedIn' => is_user_logged_in(), |
| 295 | - 'loginUrl' => wp_login_url(), | |
| 345 | + 'loginUrl' => $yatraAccountLoginUrl, | |
| 296 | 346 | 'i18n' => [ |
| 297 | 347 | 'loginRequired' => __('Login Required', 'yatra'), |
| 298 | 348 | 'loginPrompt' => __('Please login to save trips to your wishlist.', 'yatra'), |
| 299 | 349 | 'login' => __('Login', 'yatra'), |
| @@ -386,8 +436,13 @@ | ||
| 386 | 436 | ); |
| 387 | 437 | } |
| 388 | 438 | } |
| 389 | 439 | |
| 440 | + // International phone-number widget (country flag + dial code) used by | |
| 441 | + // the booking form's tel fields. | |
| 442 | + $this->enqueuePhoneInputAssets(); | |
| 443 | + $this->enqueueCountrySelectAssets(); | |
| 444 | + | |
| 390 | 445 | // Mobile sticky-sidebar + flatpickr init for the single-trip page. Lives in a |
| 391 | 446 | // dedicated file rather than as inline <script> in the partial because |
| 392 | 447 | // WordPress core's `convert_chars` filter (hooked to the_content) rewrites the |
| 393 | 448 | // `&&` operators inside inline scripts as `&&` — JS parsers don't |
| @@ -437,8 +492,16 @@ | ||
| 437 | 492 | 'tripId' => $trip_id, |
| 438 | 493 | 'tripSlug' => $trip_slug, |
| 439 | 494 | 'wishlistEnabled' => \Yatra\Services\SettingsService::wishlistEnabled(), |
| 440 | 495 | 'isLoggedIn' => is_user_logged_in(), |
| 496 | + // Wishlist "Login" (guest) → the configured My Account page | |
| 497 | + // (Settings → Permalink slug), NOT wp-login.php. Without this key | |
| 498 | + // trip.js falls back to a hardcoded '/wp-login.php'. Falls back to | |
| 499 | + // wp_login_url() only when no account base slug is configured. | |
| 500 | + 'loginUrl' => (function () { | |
| 501 | + $base = \Yatra\Services\SettingsService::getAccountBase(); | |
| 502 | + return $base !== '' ? home_url('/' . trim($base, '/') . '/') : wp_login_url(); | |
| 503 | + })(), | |
| 441 | 504 | // Regional settings |
| 442 | 505 | 'timezone' => \Yatra\Services\SettingsService::getString('timezone', 'UTC'), |
| 443 | 506 | 'dateFormat' => \Yatra\Services\SettingsService::getString('date_format', 'Y-m-d'), |
| 444 | 507 | 'timeFormat' => \Yatra\Services\SettingsService::getString('time_format', 'H:i'), |
| @@ -518,8 +581,106 @@ | ||
| 518 | 581 | )); |
| 519 | 582 | } |
| 520 | 583 | |
| 521 | 584 | /** |
| 585 | + * Enqueue the country selector widget (assets/js/country-select.js + | |
| 586 | + * assets/css/country-select.css). | |
| 587 | + * | |
| 588 | + * Upgrades every `type => country` field (Country, Nationality, on both the | |
| 589 | + * contact and traveler sections) into a searchable dropdown showing the | |
| 590 | + * national flag, matching the phone country-code control. Purely additive: | |
| 591 | + * the underlying <select> still renders and submits, so a site that never | |
| 592 | + * loads this script behaves exactly as before. | |
| 593 | + * | |
| 594 | + * Idempotent, so it is safe to call from every path that renders the form. | |
| 595 | + * | |
| 596 | + * @return void | |
| 597 | + */ | |
| 598 | + private function enqueueCountrySelectAssets(): void | |
| 599 | + { | |
| 600 | + if (wp_script_is('yatra-country-select', 'enqueued')) { | |
| 601 | + return; | |
| 602 | + } | |
| 603 | + | |
| 604 | + $css = YATRA_PLUGIN_PATH . 'assets/css/country-select.css'; | |
| 605 | + if (file_exists($css)) { | |
| 606 | + wp_enqueue_style( | |
| 607 | + 'yatra-country-select', | |
| 608 | + YATRA_PLUGIN_URL . 'assets/css/country-select.css', | |
| 609 | + [], | |
| 610 | + YATRA_VERSION . '.' . filemtime($css) | |
| 611 | + ); | |
| 612 | + } | |
| 613 | + | |
| 614 | + $js = YATRA_PLUGIN_PATH . 'assets/js/country-select.js'; | |
| 615 | + if (!file_exists($js)) { | |
| 616 | + return; | |
| 617 | + } | |
| 618 | + | |
| 619 | + wp_enqueue_script( | |
| 620 | + 'yatra-country-select', | |
| 621 | + YATRA_PLUGIN_URL . 'assets/js/country-select.js', | |
| 622 | + [], | |
| 623 | + YATRA_VERSION . '.' . filemtime($js), | |
| 624 | + true | |
| 625 | + ); | |
| 626 | + | |
| 627 | + wp_localize_script('yatra-country-select', 'yatraCountrySelectData', [ | |
| 628 | + 'i18n' => [ | |
| 629 | + 'search' => __('Search country', 'yatra'), | |
| 630 | + 'noResults' => __('No matches', 'yatra'), | |
| 631 | + ], | |
| 632 | + ]); | |
| 633 | + } | |
| 634 | + | |
| 635 | + /** | |
| 636 | + * Enqueue the international phone-number widget (assets/js/phone-input.js + | |
| 637 | + * assets/css/phone-input.css) and localize its country + dial-code dataset. | |
| 638 | + * | |
| 639 | + * Self-contained (reads its own `yatraPhoneData` global) and idempotent, so | |
| 640 | + * it can be called from every path that renders the booking form. Country | |
| 641 | + * data is the single source of truth in {@see FormatHelper}. | |
| 642 | + * | |
| 643 | + * @return void | |
| 644 | + */ | |
| 645 | + private function enqueuePhoneInputAssets(): void | |
| 646 | + { | |
| 647 | + if (wp_script_is('yatra-phone-input', 'enqueued')) { | |
| 648 | + return; | |
| 649 | + } | |
| 650 | + | |
| 651 | + $css = YATRA_PLUGIN_PATH . 'assets/css/phone-input.css'; | |
| 652 | + if (file_exists($css)) { | |
| 653 | + wp_enqueue_style( | |
| 654 | + 'yatra-phone-input', | |
| 655 | + YATRA_PLUGIN_URL . 'assets/css/phone-input.css', | |
| 656 | + [], | |
| 657 | + YATRA_VERSION . '.' . filemtime($css) | |
| 658 | + ); | |
| 659 | + } | |
| 660 | + | |
| 661 | + $js = YATRA_PLUGIN_PATH . 'assets/js/phone-input.js'; | |
| 662 | + if (!file_exists($js)) { | |
| 663 | + return; | |
| 664 | + } | |
| 665 | + wp_enqueue_script( | |
| 666 | + 'yatra-phone-input', | |
| 667 | + YATRA_PLUGIN_URL . 'assets/js/phone-input.js', | |
| 668 | + [], | |
| 669 | + YATRA_VERSION . '.' . filemtime($js), | |
| 670 | + true | |
| 671 | + ); | |
| 672 | + wp_localize_script('yatra-phone-input', 'yatraPhoneData', [ | |
| 673 | + 'countries' => \Yatra\Helpers\FormatHelper::getPhoneCountries(), | |
| 674 | + 'priority' => \Yatra\Helpers\FormatHelper::getPhonePriority(), | |
| 675 | + 'i18n' => [ | |
| 676 | + 'search' => __('Search country', 'yatra'), | |
| 677 | + 'noResults' => __('No matches', 'yatra'), | |
| 678 | + ], | |
| 679 | + ]); | |
| 680 | + } | |
| 681 | + | |
| 682 | + /** | |
| 522 | 683 | * Enqueue activity listing specific assets |
| 523 | 684 | * |
| 524 | 685 | * @return void |
| 525 | 686 | */ |
| @@ -595,17 +756,35 @@ | ||
| 595 | 756 | |
| 596 | 757 | // Enqueue booking-specific JavaScript |
| 597 | 758 | $bookingJs = YATRA_PLUGIN_PATH . 'assets/js/booking.js'; |
| 598 | 759 | if (file_exists($bookingJs)) { |
| 760 | + // booking.js renders user-facing strings via wp.i18n.__() (the | |
| 761 | + // "Processing..." button label and the per-gateway info messages | |
| 762 | + // shown when a payment method is selected). It therefore needs | |
| 763 | + // wp-i18n as a dependency AND wp_set_script_translations so its | |
| 764 | + // Jed catalog loads — mirroring the trip-detail enqueue above. | |
| 765 | + // Without these, those strings stay English on the standalone | |
| 766 | + // booking page regardless of site locale. | |
| 599 | 767 | wp_enqueue_script( |
| 600 | 768 | 'yatra-booking', |
| 601 | 769 | YATRA_PLUGIN_URL . 'assets/js/booking.js', |
| 602 | - ['jquery', 'yatra-flatpickr'], | |
| 770 | + ['jquery', 'yatra-flatpickr', 'wp-i18n'], | |
| 603 | 771 | YATRA_VERSION . '.' . filemtime($bookingJs), |
| 604 | 772 | true |
| 605 | 773 | ); |
| 774 | + if (function_exists('wp_set_script_translations')) { | |
| 775 | + wp_set_script_translations( | |
| 776 | + 'yatra-booking', | |
| 777 | + 'yatra', | |
| 778 | + YATRA_PLUGIN_PATH . 'i18n/languages' | |
| 779 | + ); | |
| 780 | + } | |
| 606 | 781 | } |
| 607 | 782 | |
| 783 | + // International phone-number widget (country flag + dial code). | |
| 784 | + $this->enqueuePhoneInputAssets(); | |
| 785 | + $this->enqueueCountrySelectAssets(); | |
| 786 | + | |
| 608 | 787 | // Load each available gateway's own client scripts on the checkout page |
| 609 | 788 | // (e.g. Square Web Payments SDK + square.js, Authorize.Net Accept.js + |
| 610 | 789 | // its handler, Razorpay SDK + its handler). Every gateway's |
| 611 | 790 | // enqueueScripts() self-guards on isAvailable(), so only enabled + |
| @@ -753,8 +932,14 @@ | ||
| 753 | 932 | 'decimalPlaces' => \Yatra\Services\SettingsService::getPriceDecimals(), |
| 754 | 933 | 'thousandSeparator' => \Yatra\Services\SettingsService::getString('thousand_separator', ','), |
| 755 | 934 | 'decimalSeparator' => \Yatra\Services\SettingsService::getString('decimal_separator', '.'), |
| 756 | 935 | 'locale' => get_locale(), |
| 936 | + // Global date/time format so the customer account pages render dates | |
| 937 | + // in the operator's configured format (Settings → General), not a | |
| 938 | + // hardcoded browser style. Keys mirror what the admin app receives. | |
| 939 | + 'date_format' => \Yatra\Services\SettingsService::getString('date_format', 'Y-m-d'), | |
| 940 | + 'time_format' => \Yatra\Services\SettingsService::getString('time_format', 'H:i'), | |
| 941 | + 'timezone' => \Yatra\Services\SettingsService::getString('timezone', 'UTC'), | |
| 757 | 942 | // Full ISO country map (code => name) so the account profile can show |
| 758 | 943 | // full country names and render the country dropdown. Mirrors the |
| 759 | 944 | // admin (`yatraAdmin.countries`); honours the `yatra_countries_list` filter. |
| 760 | 945 | 'countries' => class_exists('\\Yatra\\Helpers\\FormatHelper') |
| @@ -894,8 +1079,34 @@ | ||
| 894 | 1079 | 'Booking Status' => __('Booking Status', 'yatra'), |
| 895 | 1080 | 'Total Amount' => __('Total Amount', 'yatra'), |
| 896 | 1081 | 'Payment Status' => __('Payment Status', 'yatra'), |
| 897 | 1082 | 'View Details' => __('View Details', 'yatra'), |
| 1083 | + | |
| 1084 | + // Traveler / contact / emergency field labels on the account page. | |
| 1085 | + // Keep in sync with the `fieldLabel()` map in account/BookingDetails.tsx. | |
| 1086 | + 'First Name' => __('First Name', 'yatra'), | |
| 1087 | + 'Last Name' => __('Last Name', 'yatra'), | |
| 1088 | + 'Full Name' => __('Full Name', 'yatra'), | |
| 1089 | + 'Name' => __('Name', 'yatra'), | |
| 1090 | + 'Email' => __('Email', 'yatra'), | |
| 1091 | + 'Phone' => __('Phone', 'yatra'), | |
| 1092 | + 'Mobile' => __('Mobile', 'yatra'), | |
| 1093 | + 'Date of Birth' => __('Date of Birth', 'yatra'), | |
| 1094 | + 'Gender' => __('Gender', 'yatra'), | |
| 1095 | + 'Nationality' => __('Nationality', 'yatra'), | |
| 1096 | + 'Country' => __('Country', 'yatra'), | |
| 1097 | + 'Address' => __('Address', 'yatra'), | |
| 1098 | + 'City' => __('City', 'yatra'), | |
| 1099 | + 'State' => __('State', 'yatra'), | |
| 1100 | + 'Postal Code' => __('Postal Code', 'yatra'), | |
| 1101 | + 'Zip Code' => __('Zip Code', 'yatra'), | |
| 1102 | + 'Passport' => __('Passport', 'yatra'), | |
| 1103 | + 'Passport Number' => __('Passport Number', 'yatra'), | |
| 1104 | + 'Passport Expiry' => __('Passport Expiry', 'yatra'), | |
| 1105 | + 'Dietary Requirements' => __('Dietary Requirements', 'yatra'), | |
| 1106 | + 'Special Requirements' => __('Special Requirements', 'yatra'), | |
| 1107 | + 'Relationship' => __('Relationship', 'yatra'), | |
| 1108 | + 'Company' => __('Company', 'yatra'), | |
| 898 | 1109 | |
| 899 | 1110 | // Common |
| 900 | 1111 | 'Loading...' => __('Loading...', 'yatra'), |
| 901 | 1112 | 'No data available' => __('No data available', 'yatra'), |