PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
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 2.0.1 All 84 releases
← All changes | resources/js/pages/Modules.tsx +49 -8 3.0.5.1 → 3.0.16 View file →
@@ -4,8 +4,9 @@
4 4 Search,
5 5 Filter,
6 6 ArrowUpDown,
7 7 ExternalLink,
8 + ArrowUpRight,
8 9 } from "lucide-react";
9 10 import { __ } from "../lib/i18n";
10 11 import { PageHeader } from "../components/common/PageHeader";
11 12 import {
@@ -28,8 +29,25 @@
28 29 } from "../hooks/useModules";
29 30 import { usePermissions } from "../hooks/usePermissions";
30 31 import { PremiumUpgradeDialog } from "../components/modules/PremiumUpgradeDialog";
31 32
33 +/**
34 + * Build the "Learn more" link to a module's dedicated feature page on
35 + * wpyatra.com (e.g. https://wpyatra.com/features/dynamic-pricing/), tagged with
36 + * UTM params. The feature-page slug matches the docs slug, so we derive it from
37 + * the module's docs_url last segment (defined once per module on the backend).
38 + * Returns null when there's no docs_url to derive a slug from.
39 + */
40 +const featureUrlFromDocs = (docsUrl?: string): string | null => {
41 + if (!docsUrl) return null;
42 + const slug = docsUrl.replace(/\/+$/, "").split("/").pop();
43 + if (!slug) return null;
44 + const params =
45 + "utm_source=plugin&utm_medium=modules&utm_campaign=learn-more&utm_content=" +
46 + encodeURIComponent(slug);
47 + return `https://wpyatra.com/features/${slug}/?${params}`;
48 +};
49 +
32 50 const Modules: React.FC = () => {
33 51 const { can } = usePermissions();
34 52 const canManageModules = can("yatra_edit_trips");
35 53 const { data: modules = [], isLoading, error } = useModulesQuery();
@@ -67,13 +85,16 @@
67 85 const order: ModulePlan[] = ["free", "personal", "growth", "agency"];
68 86 return order.filter((tier) => set.has(tier));
69 87 }, [modules]);
70 88
89 + // 2026 plan rename: display labels changed (Personal→Starter, Agency→Scale)
90 + // but the plan *slugs* ('personal'/'agency') stay — they mirror the Pro
91 + // internal tier slugs and keep existing-user feature gating intact.
71 92 const planLabels: Record<ModulePlan, string> = {
72 93 free: __("Free", "yatra"),
73 - personal: __("Personal", "yatra"),
94 + personal: __("Starter", "yatra"),
74 95 growth: __("Growth", "yatra"),
75 - agency: __("Agency", "yatra"),
96 + agency: __("Scale", "yatra"),
76 97 };
77 98
78 99 const filteredModules = useMemo(() => {
79 100 let list = modules;
@@ -524,17 +545,18 @@
524 545 {module.name}
525 546 {module.is_core && (
526 547 <Badge variant="outline">{__("Core", "yatra")}</Badge>
527 548 )}
528 - {/* Plan badge — Personal for any Pro module, Agency
549 + {/* Plan badge — Starter for any Pro module, Scale
529 550 for white-label-tier modules. Always visible so
530 - customers know which plan unlocks the module. */}
551 + customers know which plan unlocks the module.
552 + (Slug stays 'agency'; label is the new "Scale".) */}
531 553 {module.plan === "agency" && (
532 554 <Badge
533 555 variant="outline"
534 556 className="px-2 py-0.5 rounded-full bg-gradient-to-r from-purple-600 to-indigo-500 border-purple-700 text-white shadow-sm dark:from-purple-500 dark:to-indigo-400 dark:border-purple-400 dark:text-white"
535 557 >
536 - {__("Agency", "yatra")}
558 + {__("Scale", "yatra")}
537 559 </Badge>
538 560 )}
539 561 {/* Growth tier — AI Assistant + any future module
540 562 requiring the Growth-or-Agency band. Without
@@ -553,9 +575,9 @@
553 575 <Badge
554 576 variant="outline"
555 577 className="flex items-center gap-1 px-2 py-0.5 rounded-full bg-blue-50 border-blue-200 text-blue-700 dark:bg-blue-900/30 dark:border-blue-500/40 dark:text-blue-200"
556 578 >
557 - {__("Personal", "yatra")}
579 + {__("Starter", "yatra")}
558 580 </Badge>
559 581 )}
560 582 </CardTitle>
561 583 <CardDescription className="mt-1 text-sm line-clamp-3">
@@ -572,9 +594,12 @@
572 594 </div>
573 595 </div>
574 596 </CardHeader>
575 597 <CardContent className="flex items-center justify-between text-xs text-gray-500 dark:text-gray-400">
576 - <div className="flex items-center gap-2">
598 + <div
599 + className="flex items-center gap-2"
600 + onClick={(e) => e.stopPropagation()}
601 + >
577 602 {module.video_url && (
578 603 <a
579 604 href={module.video_url}
580 605 target="_blank"
@@ -596,9 +621,12 @@
596 621 {__("Video", "yatra")}
597 622 </a>
598 623 )}
599 624 </div>
600 - <div className="flex items-center gap-2">
625 + <div
626 + className="flex items-center gap-2"
627 + onClick={(e) => e.stopPropagation()}
628 + >
601 629 {module.slug === "dynamic_form_field" && module.enabled && (
602 630 <a
603 631 href={`${window.location.origin}/wp-admin/admin.php?page=yatra&subpage=settings`}
604 632 className="flex items-center gap-1 text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200 text-xs font-medium"
@@ -616,8 +644,21 @@
616 644 <circle cx="12" cy="12" r="3"></circle>
617 645 <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
618 646 </svg>
619 647 {__("Settings", "yatra")}
648 + </a>
649 + )}
650 + {featureUrlFromDocs(module.docs_url) && (
651 + <a
652 + href={featureUrlFromDocs(module.docs_url) as string}
653 + target="_blank"
654 + rel="noopener noreferrer"
655 + onClick={(e) => e.stopPropagation()}
656 + className="flex items-center gap-1 text-blue-600 hover:text-blue-500 dark:text-blue-400 text-xs font-medium"
657 + title={__("Learn more about this feature", "yatra")}
658 + >
659 + <ArrowUpRight className="w-3 h-3" />
660 + {__("Learn more", "yatra")}
620 661 </a>
621 662 )}
622 663 {module.docs_url && (
623 664 <a