PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.17
Yatra – Travel Booking & Tour Operator Software v3.0.17
3.0.17 3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 All 85 releases
← All changes | resources/js/lib/email-templates-catalog.ts +201 -20 3.0.3 → 3.0.17 View file →
@@ -5,9 +5,16 @@
5 5 * Module-gated templates (e.g. trip consent, abandoned recovery) stay listed with real
6 6 * on/off + content from site settings while the module is off — view-only until the module is active.
7 7 */
8 8
9 -import { __ } from "./i18n";
9 +import { __, sprintf, brandName } from "./i18n";
10 +
11 +// Pre-compute "{brand} Pro" once at module load. Used by the
12 +// operator-facing template descriptions below so a white-labelled
13 +// site shows the operator's brand instead of literal "Yatra Pro".
14 +// brandName() reads window.yatraAdmin.brandName which is injected
15 +// before any React module loads, so this value is always correct.
16 +const BRAND_PRO_NAME = brandName() + " Pro";
10 17 import type { EmailSettingsValues } from "../components/settings/email-settings-types";
11 18
12 19 /** Rich booking merge tags (gateway, schedule, travelers, booking meta, special requests). */
13 20 const BOOKING_RICH_MERGE_TAGS =
@@ -25,8 +32,10 @@
25 32 isCoreFree: boolean;
26 33 /** If set, subject/body cannot be edited until Pro + this module slug is active (row still listed). */
27 34 requiresModule?: string;
28 35 settingsFlag?: keyof EmailSettingsValues;
36 + /** Only relevant when deposits / partial payments are enabled. */
37 + requiresPartialPayments?: boolean;
29 38 settingsSubject?: keyof EmailSettingsValues;
30 39 settingsBody?: keyof EmailSettingsValues;
31 40 /** Shown in core editor sidebar for merge tags */
32 41 mergeTags?: string;
@@ -34,12 +43,16 @@
34 43
35 44 export const CORE_FREE_TEMPLATE_KEYS = [
36 45 "booking_confirmation",
37 46 "payment_received",
47 + "partial_payment_received",
38 48 "booking_cancelled",
39 49 "trip_reminder",
40 50 "trip_consent_request",
41 51 "customer_email_verification",
52 + "guest_email_verification",
53 + "account_email_change_request",
54 + "account_email_changed",
42 55 "booking_completed",
43 56 "booking_expired_customer",
44 57 "admin_booking_expired",
45 58 "scheduled_payment_reminder",
@@ -49,9 +62,11 @@
49 62 "enquiry_received",
50 63 "enquiry_admin",
51 64 "enquiry_response",
52 65 "review_request",
53 - "abandoned_booking_recovery",
66 + "abandoned_booking_recovery_first",
67 + "abandoned_booking_recovery_second",
68 + "abandoned_booking_recovery_final",
54 69 "admin_new_booking",
55 70 "admin_payment_received",
56 71 "admin_booking_cancelled",
57 72 ] as const;
@@ -102,8 +117,29 @@
102 117 "{{site_name}}, {{customer_name}}, {{customer_first_name}}, {{booking_reference}}, {{trip_name}}, {{travel_date}}, {{payment_amount_formatted}}, {{payment_method}}, {{transaction_id}}, {{total_amount_formatted}}, {{currency}}, " +
103 118 BOOKING_RICH_MERGE_TAGS,
104 119 },
105 120 {
121 + template_key: "partial_payment_received",
122 + event_key: "payment.partial_received",
123 + name: __("Partial Payment Received", "yatra"),
124 + description: __(
125 + "Sent when a payment is recorded but a balance is still outstanding. Only used when deposits or partial payments are enabled and this template is switched on — otherwise the standard Payment Received email is sent for every payment.",
126 + "yatra",
127 + ),
128 + category: "payment",
129 + recipient_type: "customer",
130 + to_email: "{customer_email}",
131 + isCoreFree: true,
132 + // Shown only when the site actually takes deposits / partial payments.
133 + requiresPartialPayments: true,
134 + settingsFlag: "email_template_partial_payment",
135 + settingsSubject: "email_tpl_partial_payment_subject",
136 + settingsBody: "email_tpl_partial_payment_body",
137 + mergeTags:
138 + "{{site_name}}, {{customer_name}}, {{customer_first_name}}, {{booking_reference}}, {{trip_name}}, {{travel_date}}, {{payment_amount_formatted}}, {{amount_paid_formatted}}, {{amount_due_formatted}}, {{total_amount_formatted}}, {{payment_method}}, {{transaction_id}}, {{currency}}, " +
139 + BOOKING_RICH_MERGE_TAGS,
140 + },
141 + {
106 142 template_key: "booking_cancelled",
107 143 event_key: "booking.cancelled",
108 144 name: __("Booking Cancellation", "yatra"),
109 145 description: __("Sent when a booking is marked cancelled.", "yatra"),
@@ -174,8 +210,62 @@
174 210 mergeTags:
175 211 "{{site_name}}, {{site_url}}, {{customer_first_name}}, {{customer_name}}, {{verification_link}}, {{intro_paragraph}}, {{expiry_notice_html}}, {{footer_note}}",
176 212 },
177 213 {
214 + template_key: "guest_email_verification",
215 + event_key: "account.email_verification",
216 + name: __("Guest email verification (checkout)", "yatra"),
217 + description: __(
218 + "Sent to a guest at checkout who must verify their email before the booking is finalized (when 'Require guest email verification' is enabled). Distinct from the account-registration verification above.",
219 + "yatra",
220 + ),
221 + category: "account",
222 + recipient_type: "customer",
223 + to_email: "{customer_email}",
224 + isCoreFree: true,
225 + settingsFlag: "email_template_guest_verification",
226 + settingsSubject: "email_tpl_guest_verification_subject",
227 + settingsBody: "email_tpl_guest_verification_body",
228 + mergeTags:
229 + "{{site_name}}, {{site_url}}, {{customer_first_name}}, {{customer_name}}, {{verification_link}}, {{intro_paragraph}}, {{expiry_notice_html}}, {{footer_note}}",
230 + },
231 + {
232 + template_key: "account_email_change_request",
233 + event_key: "account.email_change_request",
234 + name: __("Account email change request", "yatra"),
235 + description: __(
236 + "Sent to the NEW address when a customer changes their account email. Contains the confirmation link; the change only applies once they click it.",
237 + "yatra",
238 + ),
239 + category: "account",
240 + recipient_type: "customer",
241 + to_email: "{new_email}",
242 + isCoreFree: true,
243 + settingsFlag: "email_template_account_email_change",
244 + settingsSubject: "email_tpl_account_email_change_subject",
245 + settingsBody: "email_tpl_account_email_change_body",
246 + mergeTags:
247 + "{{site_name}}, {{site_url}}, {{customer_first_name}}, {{customer_name}}, {{new_email}}, {{verification_link}}, {{intro_paragraph}}, {{footer_note}}",
248 + },
249 + {
250 + template_key: "account_email_changed",
251 + event_key: "account.email_changed",
252 + name: __("Account email changed", "yatra"),
253 + description: __(
254 + "Security notice sent to the OLD address once a customer's account email change is confirmed.",
255 + "yatra",
256 + ),
257 + category: "account",
258 + recipient_type: "customer",
259 + to_email: "{customer_email}",
260 + isCoreFree: true,
261 + settingsFlag: "email_template_account_email_changed",
262 + settingsSubject: "email_tpl_account_email_changed_subject",
263 + settingsBody: "email_tpl_account_email_changed_body",
264 + mergeTags:
265 + "{{site_name}}, {{site_url}}, {{customer_first_name}}, {{customer_name}}, {{new_email}}, {{intro_paragraph}}, {{footer_note}}",
266 + },
267 + {
178 268 template_key: "admin_new_booking",
179 269 event_key: "booking.created",
180 270 name: __("Admin: New Booking", "yatra"),
181 271 description: __(
@@ -235,9 +325,9 @@
235 325 template_key: "new_booking",
236 326 event_key: "booking.created",
237 327 name: __("New Booking Confirmation", "yatra"),
238 328 description: __(
239 - "Pro automation template. Enable Email Automation to edit and send.",
329 + "Pro automation alternative for the free Booking Confirmation template. Enable Email Automation to edit and send.",
240 330 "yatra",
241 331 ),
242 332 category: "booking",
243 333 recipient_type: "customer",
@@ -242,8 +332,13 @@
242 332 category: "booking",
243 333 recipient_type: "customer",
244 334 to_email: "{customer_email}",
245 335 isCoreFree: false,
336 + // Gate behind Email Automation so the free / Pro versions don't appear as
337 + // a confusing duplicate in the customer's view (e.g. two "Payment Received"
338 + // rows). isModuleGatedLocked() reads this and locks the row when the
339 + // module isn't active.
340 + requiresModule: "email_automation",
246 341 },
247 342 {
248 343 template_key: "booking_payment",
249 344 event_key: "payment.received",
@@ -248,9 +343,9 @@
248 343 template_key: "booking_payment",
249 344 event_key: "payment.received",
250 345 name: __("Booking payment notice", "yatra"),
251 346 description: __(
252 - "Pro automation template. Enable Email Automation to edit and send.",
347 + "Pro automation alternative for the free Payment Received template. Enable Email Automation to edit and send.",
253 348 "yatra",
254 349 ),
255 350 category: "payment",
256 351 recipient_type: "customer",
@@ -255,8 +350,9 @@
255 350 category: "payment",
256 351 recipient_type: "customer",
257 352 to_email: "{customer_email}",
258 353 isCoreFree: false,
354 + requiresModule: "email_automation",
259 355 },
260 356 {
261 357 template_key: "booking_confirmed",
262 358 event_key: "booking.confirmed",
@@ -261,9 +357,9 @@
261 357 template_key: "booking_confirmed",
262 358 event_key: "booking.confirmed",
263 359 name: __("Booking Confirmed", "yatra"),
264 360 description: __(
265 - "Pro automation template. Enable Email Automation to edit and send.",
361 + "Pro automation template fired on booking confirmation. Enable Email Automation to edit and send.",
266 362 "yatra",
267 363 ),
268 364 category: "booking",
269 365 recipient_type: "customer",
@@ -268,8 +364,9 @@
268 364 category: "booking",
269 365 recipient_type: "customer",
270 366 to_email: "{customer_email}",
271 367 isCoreFree: false,
368 + requiresModule: "email_automation",
272 369 },
273 370 {
274 371 template_key: "booking_completed",
275 372 event_key: "booking.completed",
@@ -350,9 +447,12 @@
350 447 {
351 448 template_key: "scheduled_payment_succeeded",
352 449 event_key: "scheduled.payment.succeeded",
353 450 name: __("Scheduled payment succeeded", "yatra"),
354 - description: __("Sent after a scheduled / installment charge succeeds.", "yatra"),
451 + description: __(
452 + "Sent after a scheduled / installment charge succeeds.",
453 + "yatra",
454 + ),
355 455 category: "payment",
356 456 recipient_type: "customer",
357 457 to_email: "{customer_email}",
358 458 isCoreFree: true,
@@ -420,9 +520,12 @@
420 520 {
421 521 template_key: "enquiry_received",
422 522 event_key: "enquiry.created",
423 523 name: __("Enquiry Received", "yatra"),
424 - description: __("Confirmation sent to the customer after they submit an enquiry.", "yatra"),
524 + description: __(
525 + "Confirmation sent to the customer after they submit an enquiry.",
526 + "yatra",
527 + ),
425 528 category: "enquiry",
426 529 recipient_type: "customer",
427 530 to_email: "{customer_email}",
428 531 isCoreFree: true,
@@ -435,9 +538,12 @@
435 538 {
436 539 template_key: "enquiry_admin",
437 540 event_key: "enquiry.created",
438 541 name: __("Admin: New Enquiry", "yatra"),
439 - description: __("Sent to your admin email when a new enquiry is submitted.", "yatra"),
542 + description: __(
543 + "Sent to your admin email when a new enquiry is submitted.",
544 + "yatra",
545 + ),
440 546 category: "enquiry",
441 547 recipient_type: "admin",
442 548 to_email: "{{admin_email}}",
443 549 isCoreFree: true,
@@ -450,9 +556,12 @@
450 556 {
451 557 template_key: "enquiry_response",
452 558 event_key: "enquiry.responded",
453 559 name: __("Enquiry Response", "yatra"),
454 - description: __("Sent to the customer when you reply to their enquiry.", "yatra"),
560 + description: __(
561 + "Sent to the customer when you reply to their enquiry.",
562 + "yatra",
563 + ),
455 564 category: "enquiry",
456 565 recipient_type: "customer",
457 566 to_email: "{customer_email}",
458 567 isCoreFree: true,
@@ -481,14 +590,18 @@
481 590 "{{site_name}}, {{customer_name}}, {{trip_name}}, {{review_url}}, {{booking_reference}}, " +
482 591 BOOKING_RICH_MERGE_TAGS,
483 592 },
484 593 {
485 - template_key: "abandoned_booking_recovery",
594 + template_key: "abandoned_booking_recovery_first",
486 595 event_key: "booking.abandoned_recovery",
487 - name: __("Abandoned booking recovery", "yatra"),
488 - description: __(
489 - "Used by Yatra Pro abandoned checkout recovery (merge tags include module copy in {{recovery_intro_html}}).",
490 - "yatra",
596 + name: __("Abandoned booking recovery (First)", "yatra"),
597 + description: sprintf(
598 + /* translators: %s: brand name (e.g. "Yatra Pro" or operator's white-labeled brand) */
599 + __(
600 + "First reminder sent by %s abandoned checkout recovery (module copy in {{recovery_intro_html}}).",
601 + "yatra",
602 + ),
603 + BRAND_PRO_NAME,
491 604 ),
492 605 category: "booking",
493 606 recipient_type: "customer",
494 607 to_email: "{customer_email}",
@@ -493,15 +606,63 @@
493 606 recipient_type: "customer",
494 607 to_email: "{customer_email}",
495 608 isCoreFree: true,
496 609 requiresModule: "abandoned_booking_recovery",
497 - settingsFlag: "email_template_abandoned_booking_recovery",
498 - settingsSubject: "email_tpl_abandoned_booking_recovery_subject",
499 - settingsBody: "email_tpl_abandoned_booking_recovery_body",
610 + settingsFlag: "email_template_abandoned_booking_recovery_first",
611 + settingsSubject: "email_tpl_abandoned_booking_recovery_first_subject",
612 + settingsBody: "email_tpl_abandoned_booking_recovery_first_body",
500 613 mergeTags:
501 614 "{{site_name}}, {{customer_name}}, {{trip_name}}, {{recovery_link}}, {{booking_reference}}, {{recovery_reminder_label}}, {{recovery_intro_html}}, " +
502 615 BOOKING_RICH_MERGE_TAGS,
503 616 },
617 + {
618 + template_key: "abandoned_booking_recovery_second",
619 + event_key: "booking.abandoned_recovery",
620 + name: __("Abandoned booking recovery (Second)", "yatra"),
621 + description: sprintf(
622 + /* translators: %s: brand name */
623 + __(
624 + "Second reminder sent by %s abandoned checkout recovery (module copy in {{recovery_intro_html}}).",
625 + "yatra",
626 + ),
627 + BRAND_PRO_NAME,
628 + ),
629 + category: "booking",
630 + recipient_type: "customer",
631 + to_email: "{customer_email}",
632 + isCoreFree: true,
633 + requiresModule: "abandoned_booking_recovery",
634 + settingsFlag: "email_template_abandoned_booking_recovery_second",
635 + settingsSubject: "email_tpl_abandoned_booking_recovery_second_subject",
636 + settingsBody: "email_tpl_abandoned_booking_recovery_second_body",
637 + mergeTags:
638 + "{{site_name}}, {{customer_name}}, {{trip_name}}, {{recovery_link}}, {{booking_reference}}, {{recovery_reminder_label}}, {{recovery_intro_html}}, " +
639 + BOOKING_RICH_MERGE_TAGS,
640 + },
641 + {
642 + template_key: "abandoned_booking_recovery_final",
643 + event_key: "booking.abandoned_recovery",
644 + name: __("Abandoned booking recovery (Final)", "yatra"),
645 + description: sprintf(
646 + /* translators: %s: brand name */
647 + __(
648 + "Final reminder sent by %s abandoned checkout recovery (module copy in {{recovery_intro_html}}).",
649 + "yatra",
650 + ),
651 + BRAND_PRO_NAME,
652 + ),
653 + category: "booking",
654 + recipient_type: "customer",
655 + to_email: "{customer_email}",
656 + isCoreFree: true,
657 + requiresModule: "abandoned_booking_recovery",
658 + settingsFlag: "email_template_abandoned_booking_recovery_final",
659 + settingsSubject: "email_tpl_abandoned_booking_recovery_final_subject",
660 + settingsBody: "email_tpl_abandoned_booking_recovery_final_body",
661 + mergeTags:
662 + "{{site_name}}, {{customer_name}}, {{trip_name}}, {{recovery_link}}, {{booking_reference}}, {{recovery_reminder_label}}, {{recovery_intro_html}}, " +
663 + BOOKING_RICH_MERGE_TAGS,
664 + },
504 665 ];
505 666
506 667 export function getCatalogEntryByTemplateKey(
507 668 key: string,
@@ -536,8 +697,21 @@
536 697 is_locked: boolean;
537 698 variables: string[];
538 699 created_at?: string;
539 700 updated_at?: string;
701 + /**
702 + * Pro Email Automation trip-specific overrides. `overrides` is the global
703 + * template's key when this row is an override ("" otherwise); `targets`
704 + * says which trips it applies to; `priority` orders overrides of the same
705 + * global template; `effective_event_key` is the event the row fires on
706 + * (an override's own event_key is stored empty).
707 + */
708 + overrides?: string;
709 + /** True when this global template may have trip-specific overrides. */
710 + overridable?: boolean;
711 + targets?: { trips?: number[]; categories?: number[]; trip_types?: string[] };
712 + priority?: number;
713 + effective_event_key?: string;
540 714 };
541 715
542 716 export type BuildLocalTemplateRowsOptions = {
543 717 isProPluginActive: boolean;
@@ -551,15 +725,22 @@
551 725 const pro = options?.isProPluginActive ?? false;
552 726 const moduleOk = (slug: string) =>
553 727 options?.isModuleActive ? options.isModuleActive(slug) : false;
554 728
555 - return EMAIL_TEMPLATES_CATALOG.map((entry) => {
729 + // Sites that never take a deposit or part payment have no use for the
730 + // part-payment template, so it is hidden rather than shown inert.
731 + const partialPaymentsOn = Boolean(
732 + values.partial_payment || values.enable_deposit || values.deposit_required,
733 + );
734 +
735 + return EMAIL_TEMPLATES_CATALOG.filter(
736 + (entry) => !entry.requiresPartialPayments || partialPaymentsOn,
737 + ).map((entry) => {
556 738 const moduleGateOk =
557 739 !entry.requiresModule || (pro && moduleOk(entry.requiresModule));
558 740
559 741 const proCatalogLocked = !entry.isCoreFree;
560 - const moduleContentLocked =
561 - Boolean(entry.requiresModule) && !moduleGateOk;
742 + const moduleContentLocked = Boolean(entry.requiresModule) && !moduleGateOk;
562 743
563 744 const is_locked = proCatalogLocked || moduleContentLocked;
564 745
565 746 const hasSettingsKeys = Boolean(