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/DynamicPricing.tsx +17 -10 3.0.4 → 3.0.17 View file →
@@ -28,8 +28,9 @@
28 28 import { Table as SharedTable } from "../components/shared/Table";
29 29 import { SearchFilterToolbar, BulkActionToolbar } from "../components/shared";
30 30 import { apiClient } from "../lib/api-client";
31 31 import { __ } from "../lib/i18n";
32 +import { toDateValue } from "../lib/dateFormat";
32 33 import { formatYatraMoney } from "../lib/currency-display";
33 34 import PremiumUpgradeCard from "./premium-pages/DynamicPricing";
34 35 import {
35 36 ResponsiveContainer,
@@ -63,8 +64,13 @@
63 64 };
64 65
65 66 // Main Component
66 67 const DynamicPricingPage: React.FC = () => {
68 + // Pro gate is a stable, server-injected flag — but we compute it once
69 + // at the top so every hook below can read it and the early return can
70 + // live below all hook calls (rules-of-hooks).
71 + const moduleAvailable = isModuleAvailable();
72 +
67 73 const [activeTab, setActiveTab] = useState("rules");
68 74 const [showRuleTypeModal, setShowRuleTypeModal] = useState(false);
69 75 const [settings, setSettings] = useState({
70 76 rule_priority_mode: "highest",
@@ -144,11 +150,8 @@
144 150 const baseUrl = window.location.href.split("&action=")[0];
145 151 window.location.href = `${baseUrl}&action=create-pricing-rule&rule_type=${ruleType}`;
146 152 };
147 153
148 - // Show premium upgrade content if module is not available
149 - if (!isModuleAvailable()) return <PremiumUpgradeCard />;
150 -
151 154 // Fetch settings from backend
152 155 const { data: settingsData } = useQuery({
153 156 queryKey: ["dynamic-pricing-settings"],
154 157 queryFn: async () => {
@@ -156,8 +159,9 @@
156 159 const body = (response as any)?.data ?? response;
157 160 const payload = (body as any)?.data ?? body;
158 161 return payload && typeof payload === "object" ? payload : {};
159 162 },
163 + enabled: moduleAvailable,
160 164 });
161 165
162 166 // Update settings state when data is loaded
163 167 React.useEffect(() => {
@@ -180,8 +184,9 @@
180 184 const response = await apiClient.get("/dynamic-pricing/rules");
181 185
182 186 return response;
183 187 },
188 + enabled: moduleAvailable,
184 189 });
185 190
186 191 // Fetch statistics
187 192 const {
@@ -194,8 +199,9 @@
194 199 const response = await apiClient.get("/dynamic-pricing/statistics");
195 200
196 201 return response;
197 202 },
203 + enabled: moduleAvailable,
198 204 });
199 205
200 206 // The API client returns the decoded JSON body.
201 207 // Some endpoints return { data: {...} } while others might return { data: { data: {...} } }.
@@ -203,8 +209,9 @@
203 209 (rulesData as any)?.data?.data ?? (rulesData as any)?.data ?? [];
204 210 const statsPayload =
205 211 (statsData as any)?.data?.data ?? (statsData as any)?.data ?? {};
206 212
213 + // eslint-disable-next-line react-hooks/exhaustive-deps
207 214 const rules = rulesPayload || [];
208 215 const stats = statsPayload || {};
209 216
210 217 const globalCurrency = (window as any)?.yatraAdmin?.currency || "USD";
@@ -217,9 +224,9 @@
217 224 (stats as any)?.pricing_history_trend_last_30_days,
218 225 )
219 226 ? (stats as any).pricing_history_trend_last_30_days.map((row: any) => {
220 227 const day = String(row.day || "");
221 - const dateLabel = day ? new Date(day).toLocaleDateString() : "";
228 + const dateLabel = day ? toDateValue(day).toLocaleDateString() : "";
222 229 return {
223 230 day,
224 231 dateLabel,
225 232 events: Number(row.events) || 0,
@@ -341,8 +348,11 @@
341 348 }
342 349 bulkMutation.mutate({ action: bulkAction, ids: selectedIds });
343 350 };
344 351
352 + // Gate after every hook is registered so hook order stays consistent.
353 + if (!moduleAvailable) return <PremiumUpgradeCard />;
354 +
345 355 return (
346 356 <div className="space-y-6">
347 357 <PageHeader
348 358 title={__("Dynamic Pricing")}
@@ -934,12 +944,9 @@
934 944 setConfirmDialog((prev) => ({
935 945 ...prev,
936 946 isLoading: false,
937 947 }));
938 - showToast(
939 - __("Failed to delete rule"),
940 - "error",
941 - );
948 + showToast(__("Failed to delete rule"), "error");
942 949 }
943 950 },
944 951 });
945 952 },
@@ -1310,9 +1317,9 @@
1310 1317 <XAxis
1311 1318 dataKey="day"
1312 1319 tick={{ fontSize: 12 }}
1313 1320 tickFormatter={(v: any) =>
1314 - v ? new Date(String(v)).toLocaleDateString() : ""
1321 + v ? toDateValue(String(v)).toLocaleDateString() : ""
1315 1322 }
1316 1323 />
1317 1324 <YAxis
1318 1325 tick={{ fontSize: 12 }}
@@ -1376,9 +1383,9 @@
1376 1383 className="border-b border-gray-100 dark:border-gray-800"
1377 1384 >
1378 1385 <td className="py-3 px-4 text-gray-900 dark:text-white">
1379 1386 {row.day
1380 - ? new Date(
1387 + ? toDateValue(
1381 1388 String(row.day),
1382 1389 ).toLocaleDateString()
1383 1390 : ""}
1384 1391 </td>