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/ViewPayment.tsx +46 -12 3.0.2.8 → 3.0.17 View file →
@@ -12,11 +12,14 @@
12 12 Calendar,
13 13 CreditCard,
14 14 Edit,
15 15 ExternalLink,
16 + Download,
16 17 } from "lucide-react";
17 18 import { __ } from "../lib/i18n";
18 19 import { apiService } from "../lib/api-client";
20 +import { downloadAdminInvoice } from "../lib/invoice-download";
21 +import { useToast } from "../components/ui/toast";
19 22 import { usePermissions } from "../hooks/usePermissions";
20 23 import { Button } from "../components/ui/button";
21 24 import { PageHeader } from "../components/common/PageHeader";
22 25 import {
@@ -26,9 +29,9 @@
26 29 CardTitle,
27 30 } from "../components/ui/card";
28 31 import { ConditionalRender } from "../components/ui/conditional-render";
29 32 import { Skeleton } from "../components/ui/skeleton";
30 -import { getCurrencySymbol, getCurrency } from "../data/currencies";
33 +import { formatYatraMoney } from "../lib/currency-display";
31 34
32 35 function paymentStr(v: unknown, fallback = ""): string {
33 36 if (v == null || v === "") return fallback;
34 37 return String(v);
@@ -51,8 +54,9 @@
51 54 trip_id: number;
52 55 trip_title: string;
53 56 amount: number;
54 57 payment_method: string;
58 + payment_method_label?: string;
55 59 payment_status: string;
56 60 transaction_id: string;
57 61 payment_date: string;
58 62 notes: string;
@@ -61,8 +65,10 @@
61 65 }
62 66
63 67 const ViewPayment: React.FC = () => {
64 68 const { can } = usePermissions();
69 + const { showToast } = useToast();
70 + const [isDownloadingInvoice, setIsDownloadingInvoice] = React.useState(false);
65 71
66 72 // Get payment id from URL
67 73 const paymentId = useMemo(() => {
68 74 const params = new URLSearchParams(window.location.search);
@@ -125,19 +131,13 @@
125 131 day: "numeric",
126 132 });
127 133 };
128 134
129 - const formatPrice = (price: number, currencyCode: string = "USD") => {
130 - const symbol = getCurrencySymbol(currencyCode);
131 - const currencyData = getCurrency(currencyCode);
132 - const decimals = currencyData?.decimalDigits ?? 2;
135 + const formatPrice = (price: number, currencyCode: string = "USD") =>
136 + formatYatraMoney(Number(price) || 0, currencyCode, {
137 + zeroAsUnknown: false,
138 + });
133 139
134 - return `${symbol}${new Intl.NumberFormat(undefined, {
135 - minimumFractionDigits: decimals,
136 - maximumFractionDigits: decimals,
137 - }).format(price)}`;
138 - };
139 -
140 140 const getPaymentStatusBadge = (status: string) => {
141 141 const statusMap: Record<string, { className: string; label: string }> = {
142 142 completed: {
143 143 className:
@@ -194,8 +194,29 @@
194 194 window.location.href = `${window.yatraAdmin?.siteUrl || ""}/wp-admin/admin.php?page=yatra&subpage=bookings&action=view&id=${payment.booking_id}`;
195 195 }
196 196 };
197 197
198 + const handleDownloadInvoice = async () => {
199 + if (!paymentId) return;
200 + setIsDownloadingInvoice(true);
201 + try {
202 + await downloadAdminInvoice(paymentId);
203 + } catch (e: any) {
204 + showToast(
205 + e?.message || __("Failed to download invoice", "yatra"),
206 + "error",
207 + );
208 + } finally {
209 + setIsDownloadingInvoice(false);
210 + }
211 + };
212 +
213 + const canDownloadInvoice =
214 + !!payment &&
215 + ["completed", "paid"].includes(
216 + String(payment.payment_status).toLowerCase(),
217 + );
218 +
198 219 if (isLoading) {
199 220 return (
200 221 <div className="space-y-3">
201 222 <PageHeader
@@ -297,8 +318,21 @@
297 318 >
298 319 <ArrowLeft className="w-4 h-4" />
299 320 {__("Back", "yatra")}
300 321 </Button>
322 + {canDownloadInvoice && (
323 + <Button
324 + variant="outline"
325 + onClick={handleDownloadInvoice}
326 + disabled={isDownloadingInvoice}
327 + className="flex items-center gap-2"
328 + >
329 + <Download className="w-4 h-4" />
330 + {isDownloadingInvoice
331 + ? __("Downloading…", "yatra")
332 + : __("Download Invoice", "yatra")}
333 + </Button>
334 + )}
301 335 <ConditionalRender capability="yatra_edit_bookings">
302 336 <Button onClick={handleEdit} className="flex items-center gap-2">
303 337 <Edit className="w-4 h-4" />
304 338 {__("Edit Payment", "yatra")}
@@ -346,9 +380,9 @@
346 380 </label>
347 381 <div className="mt-1 flex items-center gap-2">
348 382 <CreditCard className="w-4 h-4 text-gray-400" />
349 383 <p className="text-sm font-medium text-gray-900 dark:text-white">
350 - {payment.payment_method}
384 + {payment.payment_method_label || payment.payment_method}
351 385 </p>
352 386 </div>
353 387 </div>
354 388 <div>