PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.9
Yatra – Travel Booking & Tour Operator Software v3.0.9
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 / components / dashboard / PendingPayments.tsx

PendingPayments.tsx in Yatra – Travel Booking & Tour Operator Software 3.0.9, at resources/js/components/dashboard/PendingPayments.tsx

164 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Pending Payments Widget
3 * Displays bookings with pending payments
4 */
5
6 import React from "react";
7 import { __ } from "../../lib/i18n";
8 import { toDateValue } from "../../lib/dateFormat";
9 import { Card, CardContent, CardHeader, CardTitle } from "../ui/card";
10 import { Badge } from "../ui/badge";
11 import { Button } from "../ui/button";
12 import { DollarSign, Clock, User } from "lucide-react";
13 import { ConditionalRender } from "../ui/conditional-render";
14 import { formatYatraMoney } from "../../lib/currency-display";
15
16 interface PendingPayment {
17 id: number;
18 booking_id: number;
19 customer_name: string;
20 trip_title: string;
21 amount: number;
22 due_date: string;
23 days_overdue?: number;
24 payment_method?: string;
25 }
26
27 interface PendingPaymentsProps {
28 payments: PendingPayment[];
29 loading?: boolean;
30 onView?: (payment: PendingPayment) => void;
31 onCollect?: (payment: PendingPayment) => void;
32 }
33
34 /**
35 * Pending Payments Widget
36 */
37 export const PendingPayments: React.FC<PendingPaymentsProps> = ({
38 payments,
39 loading = false,
40 onView,
41 onCollect,
42 }) => {
43 const globalCurrency = (window as any)?.yatraAdmin?.currency || "USD";
44 const formatCurrency = (amount: number) =>
45 formatYatraMoney(Number(amount) || 0, globalCurrency, {
46 zeroAsUnknown: false,
47 });
48
49 const formatDate = (dateString: string) => {
50 // toDateValue: a date-only due_date must not roll back a day in behind-UTC zones.
51 return toDateValue(dateString).toLocaleDateString();
52 };
53
54 const totalPending = payments?.reduce((sum, p) => sum + p.amount, 0) || 0;
55
56 if (loading) {
57 return (
58 <Card>
59 <CardHeader>
60 <CardTitle>{__("Pending Payments", "yatra")}</CardTitle>
61 </CardHeader>
62 <CardContent>
63 <div className="text-sm text-gray-500 dark:text-gray-400">
64 {__("Loading...", "yatra")}
65 </div>
66 </CardContent>
67 </Card>
68 );
69 }
70
71 return (
72 <Card>
73 <CardHeader>
74 <div className="flex items-center justify-between">
75 <CardTitle>{__("Pending Payments", "yatra")}</CardTitle>
76 {totalPending > 0 && (
77 <Badge variant="warning" className="text-sm">
78 {formatCurrency(totalPending)}
79 </Badge>
80 )}
81 </div>
82 </CardHeader>
83 <CardContent>
84 {payments && payments.length > 0 ? (
85 <div className="space-y-2">
86 {payments.slice(0, 2).map((payment) => (
87 <div
88 key={payment.id}
89 className="p-3 rounded-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors"
90 >
91 <div className="flex items-start justify-between mb-2">
92 <div className="flex-1">
93 <div className="flex items-center gap-2 mb-1">
94 <h4 className="text-sm font-medium text-gray-900 dark:text-white">
95 {payment.trip_title}
96 </h4>
97 {payment.days_overdue && payment.days_overdue > 0 && (
98 <Badge variant="error" className="text-xs">
99 {payment.days_overdue} {__("days overdue", "yatra")}
100 </Badge>
101 )}
102 </div>
103 <div className="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-400 mb-1">
104 <User className="w-3 h-3" />
105 <span>{payment.customer_name}</span>
106 </div>
107 <div className="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-400">
108 <Clock className="w-3 h-3" />
109 <span>
110 {__("Due", "yatra")}: {formatDate(payment.due_date)}
111 </span>
112 </div>
113 </div>
114 <div className="text-right">
115 <p className="text-base font-semibold text-gray-900 dark:text-white">
116 {formatCurrency(payment.amount)}
117 </p>
118 {payment.payment_method && (
119 <p className="text-xs text-gray-500 dark:text-gray-400">
120 {payment.payment_method}
121 </p>
122 )}
123 </div>
124 </div>
125
126 <ConditionalRender capability="yatra_edit_bookings">
127 <div className="flex gap-2 mt-2">
128 {onView && (
129 <Button
130 variant="outline"
131 size="sm"
132 onClick={() => onView(payment)}
133 className="flex-1"
134 >
135 {__("View", "yatra")}
136 </Button>
137 )}
138 {onCollect && (
139 <Button
140 size="sm"
141 onClick={() => onCollect(payment)}
142 className="flex-1"
143 >
144 <DollarSign className="w-4 h-4 mr-1" />
145 {__("Collect", "yatra")}
146 </Button>
147 )}
148 </div>
149 </ConditionalRender>
150 </div>
151 ))}
152 </div>
153 ) : (
154 <p className="text-sm text-gray-500 dark:text-gray-400">
155 {__("No pending payments", "yatra")}
156 </p>
157 )}
158 </CardContent>
159 </Card>
160 );
161 };
162
163 export default PendingPayments;
164