PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.3
Yatra – Travel Booking & Tour Operator Software v3.0.3
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
yatra / resources / js / pages / account / utils.ts

utils.ts in Yatra – Travel Booking & Tour Operator Software 3.0.3, at resources/js/pages/account/utils.ts

235 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { __, sprintf } from "../../lib/i18n";
2 import {
3 applyCurrencyPosition,
4 formatYatraMoney,
5 } from "../../lib/currency-display";
6
7 export const formatDate = (value: string | undefined | null) => {
8 if (!value) {
9 return __("N/A", "yatra");
10 }
11
12 try {
13 const date = new Date(value);
14
15 // Check if date is valid
16 if (isNaN(date.getTime())) {
17 return __("Invalid date", "yatra");
18 }
19
20 return date.toLocaleDateString(undefined, {
21 year: "numeric",
22 month: "long",
23 day: "numeric",
24 });
25 } catch (error) {
26 return __("Invalid date", "yatra");
27 }
28 };
29
30 /** Leading Y-m-d from API datetime strings for stable range comparison. */
31 function extractYmd(value: string | undefined | null): string | null {
32 if (!value) {
33 return null;
34 }
35 const m = String(value).trim().match(/^(\d{4}-\d{2}-\d{2})/);
36 return m ? m[1] : null;
37 }
38
39 /**
40 * Format trip window for the account UI: one localized date, or start–end when different.
41 * translators: %1$s: trip start date, %2$s: trip end date (localized).
42 */
43 export const formatTravelDateRange = (
44 travelDate?: string | null,
45 endDate?: string | null,
46 ): string => {
47 const startKey = extractYmd(travelDate);
48 const endKey = extractYmd(endDate);
49 if (!startKey) {
50 return formatDate(travelDate);
51 }
52 if (!endKey || endKey === startKey) {
53 return formatDate(travelDate);
54 }
55 return sprintf(
56 __("%1$s – %2$s", "yatra"),
57 formatDate(travelDate),
58 formatDate(endDate),
59 );
60 };
61
62 export const getBadge = (status: string | undefined | null) => {
63 const base = "px-2.5 py-0.5 rounded-full text-xs font-medium";
64
65 // Handle undefined/null/empty status
66 if (!status || typeof status !== "string") {
67 return `${base} bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-300`;
68 }
69
70 switch (status.toLowerCase()) {
71 case "paid":
72 case "confirmed":
73 case "resolved":
74 return `${base} bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300`;
75 case "pending":
76 case "partial":
77 case "awaiting_response":
78 return `${base} bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300`;
79 case "failed":
80 case "cancelled":
81 return `${base} bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300`;
82 default:
83 return `${base} bg-yatra-chip-bg text-yatra-primary-darker dark:bg-yatra-surface-dark dark:text-yatra-primary-light`;
84 }
85 };
86
87 type PriceConfigWindow = Window & {
88 yatraAdmin?: Record<string, unknown>;
89 yatraAccountPage?: Record<string, unknown>;
90 };
91
92 function readPriceConfig() {
93 if (typeof window === "undefined") {
94 return {
95 globalCurrency: "USD",
96 currencyPosition: "before",
97 decimalPlaces: 2,
98 thousandSeparator: ",",
99 decimalSeparator: ".",
100 };
101 }
102 const w = window as PriceConfigWindow;
103 const a = (w.yatraAdmin || {}) as Record<string, unknown>;
104 const p = (w.yatraAccountPage || {}) as Record<string, unknown>;
105 return {
106 globalCurrency: (a.currency || p.currency || "USD") as string,
107 currencyPosition: (a.currencyPosition ||
108 a.currency_position ||
109 p.currencyPosition ||
110 p.currency_position ||
111 "before") as string,
112 decimalPlaces: Number(a.decimalPlaces ?? p.decimalPlaces ?? 2) || 2,
113 thousandSeparator: (a.thousandSeparator ||
114 p.thousandSeparator ||
115 ",") as string,
116 decimalSeparator: (a.decimalSeparator ||
117 p.decimalSeparator ||
118 ".") as string,
119 };
120 }
121
122 export const formatPrice = (price: number) => {
123 const {
124 globalCurrency,
125 currencyPosition,
126 decimalPlaces,
127 thousandSeparator,
128 decimalSeparator,
129 } = readPriceConfig();
130
131 if (!price || price === 0) {
132 return __("Contact for pricing", "yatra");
133 }
134
135 const formattedAmount = new Intl.NumberFormat(undefined, {
136 minimumFractionDigits: decimalPlaces,
137 maximumFractionDigits: decimalPlaces,
138 })
139 .format(price)
140 .replace(/,/g, "TEMP_THOUSAND")
141 .replace(/\./g, decimalSeparator)
142 .replace(/TEMP_THOUSAND/g, thousandSeparator);
143
144 const currencySymbol = new Intl.NumberFormat(undefined, {
145 style: "currency",
146 currency: globalCurrency,
147 })
148 .format(0)
149 .replace(/[\d\s.,]/g, "")
150 .trim();
151
152 return applyCurrencyPosition(
153 formattedAmount,
154 currencySymbol,
155 currencyPosition,
156 );
157 };
158
159 export const formatPriceForBooking = (price: number, currency?: string) => {
160 const cfg = readPriceConfig();
161 const globalCurrency = currency || cfg.globalCurrency;
162 const {
163 currencyPosition,
164 decimalPlaces,
165 thousandSeparator,
166 decimalSeparator,
167 } = cfg;
168
169 const currencyToUse = globalCurrency;
170
171 // Always format the price, even if 0 - don't show "Contact for pricing" for bookings
172 const numPrice = Number(price) || 0;
173
174 const formattedAmount = new Intl.NumberFormat(undefined, {
175 minimumFractionDigits: decimalPlaces,
176 maximumFractionDigits: decimalPlaces,
177 })
178 .format(numPrice)
179 .replace(/,/g, "TEMP_THOUSAND")
180 .replace(/\./g, decimalSeparator)
181 .replace(/TEMP_THOUSAND/g, thousandSeparator);
182
183 const currencySymbol = new Intl.NumberFormat(undefined, {
184 style: "currency",
185 currency: currencyToUse,
186 })
187 .format(0)
188 .replace(/[\d\s.,]/g, "")
189 .trim();
190
191 return applyCurrencyPosition(
192 formattedAmount,
193 currencySymbol,
194 currencyPosition,
195 );
196 };
197
198 export const currency = (value: number, currencyCode: string = "USD") =>
199 formatYatraMoney(Number(value) || 0, currencyCode, { zeroAsUnknown: false });
200
201 /** Values from `wp_localize_script(…, 'yatraAccountPage', …)` on the account page. */
202 export function getYatraAccountPageGlobals(): {
203 logoutUrl: string;
204 companyPhone: string;
205 companyName: string;
206 companyEmail: string;
207 } {
208 if (typeof window === "undefined") {
209 return {
210 logoutUrl: "",
211 companyPhone: "",
212 companyName: "",
213 companyEmail: "",
214 };
215 }
216 const p = (window as unknown as { yatraAccountPage?: Record<string, string> })
217 .yatraAccountPage;
218 const raw = p || {};
219 return {
220 logoutUrl: String(raw.logoutUrl || "").trim(),
221 companyPhone: String(raw.companyPhone || "").trim(),
222 companyName: String(raw.companyName || "").trim(),
223 companyEmail: String(raw.companyEmail || "").trim(),
224 };
225 }
226
227 export function phoneToTelHref(phone: string): string {
228 const t = String(phone).trim();
229 if (!t) {
230 return "";
231 }
232 const compact = t.replace(/[^\d+]/g, "");
233 return compact ? `tel:${compact}` : `tel:${encodeURIComponent(t)}`;
234 }
235