PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.4
Yatra – Travel Booking & Tour Operator Software v3.0.4
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.4, at resources/js/pages/account/utils.ts

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