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/pages/AdditionalServices.tsx +14 -7 3.0.2.6 → 3.0.17 View file →
@@ -27,8 +27,9 @@
27 27 import { ConditionalRender } from "../components/ui/conditional-render";
28 28 import { useToast } from "../components/ui/toast";
29 29 import { apiClient } from "../lib/api-client";
30 30 import { __ } from "../lib/i18n";
31 +import { formatYatraMoney } from "../lib/currency-display";
31 32 import { Button } from "../components/ui/button";
32 33 import { PageHeader } from "../components/common/PageHeader";
33 34 import {
34 35 Edit,
@@ -82,15 +83,13 @@
82 83 };
83 84
84 85 // Main Component
85 86 const AdditionalServices: React.FC = () => {
87 + // Module-availability gate. Evaluated up front; the actual early
88 + // return runs AFTER every hook below so React's hook-order
89 + // invariant holds (rules-of-hooks).
86 90 const moduleAvailable = isModuleAvailable();
87 91
88 - // Show premium upgrade content if module is not available
89 - if (!moduleAvailable) {
90 - return <PremiumUpgradeCard />;
91 - }
92 -
93 92 const [searchTerm, setSearchTerm] = useState("");
94 93 const [statusFilter, setStatusFilter] = useState("all");
95 94 const [sortBy, setSortBy] = useState("name");
96 95 const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc");
@@ -178,8 +177,9 @@
178 177 },
179 178 enabled: isModuleAvailable(),
180 179 });
181 180
181 + // eslint-disable-next-line react-hooks/exhaustive-deps
182 182 const services = data?.data || [];
183 183 const total = data?.total || 0;
184 184 const totalPages = Math.ceil(total / 10);
185 185
@@ -236,9 +236,8 @@
236 236 priceType: string,
237 237 pricePer: string,
238 238 ) => {
239 239 const currency = (window as any)?.yatraAdmin?.currency || "USD";
240 - const symbol = currency === "USD" ? "$" : currency;
241 240 const numPrice = Number(price) || 0;
242 241 if (priceType === "percentage") return `${numPrice}%`;
243 242 const perLabel =
244 243 { person: __("/person"), booking: __("/booking"), day: __("/day") }[
@@ -243,9 +242,9 @@
243 242 const perLabel =
244 243 { person: __("/person"), booking: __("/booking"), day: __("/day") }[
245 244 pricePer
246 245 ] || "";
247 - return `${symbol}${numPrice.toFixed(2)}${perLabel}`;
246 + return `${formatYatraMoney(numPrice, currency, { zeroAsUnknown: false })}${perLabel}`;
248 247 };
249 248
250 249 const handleEdit = (service: AdditionalService) => {
251 250 navigateToForm("edit", service.id);
@@ -326,8 +325,15 @@
326 325 { key: "draft", label: __("Draft"), count: statusCounts.draft ?? 0 },
327 326 { key: "trash", label: __("Trash"), count: statusCounts.trash ?? 0 },
328 327 ];
329 328
329 + // Module-availability gate runs here so every hook above runs on
330 + // every render — see rules-of-hooks. The PremiumUpgradeCard render
331 + // matches what used to be above the hooks.
332 + if (!moduleAvailable) {
333 + return <PremiumUpgradeCard />;
334 + }
335 +
330 336 return (
331 337 <div className="space-y-3">
332 338 <PageHeader
333 339 title={__("Additional Services", "yatra")}
@@ -503,8 +509,9 @@
503 509 />
504 510 ) : (
505 511 <IconSelector
506 512 iconName={service.icon.value}
513 + provider={service.icon.provider ?? "yatra"}
507 514 className="w-5 h-5 text-purple-600 dark:text-purple-400"
508 515 />
509 516 )
510 517 ) : (