PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.6
Yatra – Travel Booking & Tour Operator Software v3.0.2.6
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 / RecentBookings.tsx

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

181 lines 6.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Recent Bookings Widget
3 * Displays recent bookings list
4 */
5
6 import React from "react";
7 import { __ } from "../../lib/i18n";
8 import { Card, CardContent, CardHeader, CardTitle } from "../ui/card";
9 import { Badge } from "../ui/badge";
10 import { Calendar, User, ArrowRight } from "lucide-react";
11 import { getCurrencySymbol } from "../../data/currencies";
12
13 interface Booking {
14 id: number;
15 booking_id: string;
16 customer_name: string;
17 trip_title: string;
18 booking_date: string;
19 total_amount: number;
20 status: "confirmed" | "pending" | "cancelled" | "completed";
21 }
22
23 interface RecentBookingsProps {
24 bookings: Booking[];
25 loading?: boolean;
26 onView?: (booking: Booking) => void;
27 }
28
29 /**
30 * Recent Bookings Widget
31 */
32 export const RecentBookings: React.FC<RecentBookingsProps> = ({
33 bookings,
34 loading = false,
35 onView,
36 }) => {
37 const formatDate = (dateString: string) => {
38 return new Date(dateString).toLocaleDateString("en-US", {
39 month: "short",
40 day: "numeric",
41 year: "numeric",
42 });
43 };
44
45 // Get global currency settings
46 const globalCurrency = (window as any)?.yatraAdmin?.currency || "USD";
47 const currencyPosition =
48 (window as any)?.yatraAdmin?.currencyPosition ||
49 (window as any)?.yatraAdmin?.currency_position ||
50 "before";
51 const decimalPlaces = Number(
52 (window as any)?.yatraAdmin?.decimalPlaces ||
53 (window as any)?.yatraAdmin?.currency_decimals ||
54 2,
55 );
56 const thousandSeparator =
57 (window as any)?.yatraAdmin?.thousandSeparator || ",";
58 const decimalSeparator = (window as any)?.yatraAdmin?.decimalSeparator || ".";
59
60 const formatCurrency = (amount: number) => {
61 if (!amount || amount === 0) return getCurrencySymbol(globalCurrency) + "0";
62
63 const numPrice = Number(amount) || 0;
64
65 // Format the number with proper separators
66 const formattedAmount = new Intl.NumberFormat(undefined, {
67 minimumFractionDigits: decimalPlaces,
68 maximumFractionDigits: decimalPlaces,
69 })
70 .format(numPrice)
71 .replace(/,/g, "TEMP_THOUSAND")
72 .replace(/\./g, decimalSeparator)
73 .replace(/TEMP_THOUSAND/g, thousandSeparator);
74
75 // Get currency symbol
76 const currencySymbol = getCurrencySymbol(globalCurrency);
77
78 // Apply currency position
79 if (currencyPosition === "after" || currencyPosition === "right") {
80 return `${formattedAmount} ${currencySymbol}`;
81 } else {
82 return `${currencySymbol}${formattedAmount}`;
83 }
84 };
85
86 const getStatusColor = (status: string) => {
87 switch (status) {
88 case "confirmed":
89 return "bg-green-100 text-green-700 dark:bg-green-900/20 dark:text-green-400";
90 case "pending":
91 return "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/20 dark:text-yellow-400";
92 case "cancelled":
93 return "bg-red-100 text-red-700 dark:bg-red-900/20 dark:text-red-400";
94 case "completed":
95 return "bg-blue-100 text-blue-700 dark:bg-blue-900/20 dark:text-blue-400";
96 default:
97 return "bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-400";
98 }
99 };
100
101 if (loading) {
102 return (
103 <Card>
104 <CardHeader>
105 <CardTitle>{__("Recent Bookings", "yatra")}</CardTitle>
106 </CardHeader>
107 <CardContent>
108 <div className="text-sm text-gray-500 dark:text-gray-400">
109 {__("Loading...", "yatra")}
110 </div>
111 </CardContent>
112 </Card>
113 );
114 }
115
116 return (
117 <Card>
118 <CardHeader>
119 <CardTitle>{__("Recent Bookings", "yatra")}</CardTitle>
120 </CardHeader>
121 <CardContent>
122 {bookings && bookings.length > 0 ? (
123 <div className="space-y-2">
124 {bookings.slice(0, 5).map((booking) => (
125 <div
126 key={booking.id}
127 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 cursor-pointer group"
128 onClick={() => onView && onView(booking)}
129 >
130 <div className="flex items-start justify-between mb-2">
131 <div className="flex-1 min-w-0">
132 <div className="flex items-center gap-2 mb-1">
133 <h4 className="text-sm font-medium text-gray-900 dark:text-white truncate">
134 {booking.trip_title}
135 </h4>
136 <Badge
137 className={`text-xs ${getStatusColor(booking.status)}`}
138 >
139 {booking.status}
140 </Badge>
141 </div>
142 <div className="flex items-center gap-3 text-sm text-gray-500 dark:text-gray-400">
143 <div className="flex items-center gap-1">
144 <User className="w-3 h-3" />
145 <span className="truncate">
146 {booking.customer_name}
147 </span>
148 </div>
149 <div className="flex items-center gap-1">
150 <Calendar className="w-3 h-3" />
151 <span>{formatDate(booking.booking_date)}</span>
152 </div>
153 </div>
154 </div>
155 <div className="flex items-center gap-2 ml-2">
156 <div className="text-right">
157 <p className="text-sm font-semibold text-gray-900 dark:text-white">
158 {formatCurrency(booking.total_amount)}
159 </p>
160 <p className="text-xs text-gray-500 dark:text-gray-400">
161 {booking.booking_id}
162 </p>
163 </div>
164 <ArrowRight className="w-4 h-4 text-gray-400 group-hover:text-gray-600 dark:group-hover:text-gray-300 transition-colors" />
165 </div>
166 </div>
167 </div>
168 ))}
169 </div>
170 ) : (
171 <p className="text-sm text-gray-500 dark:text-gray-400">
172 {__("No recent bookings", "yatra")}
173 </p>
174 )}
175 </CardContent>
176 </Card>
177 );
178 };
179
180 export default RecentBookings;
181