PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.7
Yatra – Travel Booking & Tour Operator Software v3.0.7
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 / Dashboard.tsx

Dashboard.tsx in Yatra – Travel Booking & Tour Operator Software 3.0.7, at resources/js/pages/account/Dashboard.tsx

583 lines 27.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import React from "react";
2 import {
3 Calendar,
4 CreditCard,
5 User,
6 LifeBuoy,
7 FileText,
8 Package,
9 MapPin,
10 ChevronRight,
11 ArrowRight,
12 Sparkles,
13 Plane,
14 Clock,
15 CheckCircle2,
16 AlertCircle,
17 Bell,
18 } from "lucide-react";
19 import { __, _n, sprintf } from "../../lib/i18n";
20 import {
21 formatDate,
22 formatTravelDateRange,
23 getBadge,
24 getStatusLabel,
25 currency,
26 phoneToTelHref,
27 } from "./utils";
28 import type { Booking, Payment, CustomerProfile } from "./types";
29
30 interface DashboardProps {
31 bookings: Booking[];
32 payments: Payment[];
33 displayProfile: CustomerProfile | null;
34 stats: Array<{
35 label: string;
36 value: string | number;
37 icon: React.ElementType;
38 badge?: string;
39 }>;
40 notifications: any[];
41 /** From Yatra Settings → General (company phone). */
42 conciergePhone?: string;
43 conciergeEmail?: string;
44 onSectionChange: (section: string) => void;
45 /**
46 * Open a specific booking's detail view. Used by the "Upcoming
47 * Trips" cards so clicking one deep-links to the booking detail
48 * instead of dumping the user on the generic Bookings list.
49 */
50 onBookingOpen?: (bookingId: number) => void;
51 }
52
53 const Dashboard: React.FC<DashboardProps> = ({
54 bookings,
55 payments,
56 displayProfile,
57 stats,
58 notifications,
59 conciergePhone = "",
60 conciergeEmail = "",
61 onSectionChange,
62 onBookingOpen,
63 }) => {
64 const conciergeTel = phoneToTelHref(conciergePhone);
65 const conciergeMail = String(conciergeEmail || "").trim();
66 const upcomingBookings =
67 bookings.filter((b) => new Date(b.travel_date) > new Date()).length > 0
68 ? bookings.filter((b) => new Date(b.travel_date) > new Date()).slice(0, 3)
69 : bookings
70 .filter((b) => new Date(b.travel_date) > new Date())
71 .slice(0, 3);
72
73 const recentBookings = bookings.length > 0 ? bookings.slice(0, 2) : [];
74
75 const pendingPayments =
76 payments.filter((p) => p.status === "pending").length > 0
77 ? payments.filter((p) => p.status === "pending").slice(0, 2)
78 : [];
79
80 const displayNotifications = notifications;
81
82 // Enhanced stats with colors and gradients
83 const enhancedStats = [
84 {
85 ...stats[0],
86 gradient: "from-yatra-primary to-yatra-primary-dark",
87 bgColor: "bg-yatra-soft dark:bg-yatra-surface-dark-muted",
88 iconColor: "text-yatra-primary dark:text-yatra-on-dark",
89 },
90 {
91 ...stats[1],
92 gradient: "from-emerald-500 to-emerald-600",
93 bgColor: "bg-emerald-50 dark:bg-emerald-900/20",
94 iconColor: "text-emerald-600 dark:text-emerald-400",
95 },
96 {
97 ...stats[2],
98 gradient: "from-amber-500 to-amber-600",
99 bgColor: "bg-amber-50 dark:bg-amber-900/20",
100 iconColor: "text-amber-600 dark:text-amber-400",
101 },
102 {
103 ...stats[3],
104 gradient: "from-purple-500 to-purple-600",
105 bgColor: "bg-purple-50 dark:bg-purple-900/20",
106 iconColor: "text-purple-600 dark:text-purple-400",
107 },
108 ];
109
110 return (
111 <div className="space-y-6">
112 {/* Welcome Banner */}
113 <div
114 className="yatra-dashboard-welcome relative overflow-hidden rounded-2xl p-8 shadow-xl"
115 style={{
116 background:
117 "linear-gradient(135deg, var(--yatra-primary, #3b82f6) 0%, var(--yatra-primary-dark, #2563eb) 45%, var(--yatra-primary-darker, #1e40af) 100%)",
118 color: "#ffffff",
119 }}
120 >
121 <div className="relative z-10">
122 <div className="flex items-start justify-between">
123 <div>
124 <h2
125 className="text-lg font-bold mb-2"
126 style={{ color: "#ffffff" }}
127 >
128 {sprintf(
129 // translators: %s: customer first name (or "Traveler" fallback).
130 __("Welcome back, %s!", "yatra"),
131 displayProfile?.name?.split(" ")[0] ||
132 __("Traveler", "yatra"),
133 )}
134 </h2>
135 <p className="text-sm mb-4 text-white/90">
136 {__("You have", "yatra")} {upcomingBookings.length}{" "}
137 {upcomingBookings.length === 1
138 ? __("upcoming adventure", "yatra")
139 : __("upcoming adventures", "yatra")}{" "}
140 {__("coming up", "yatra")}
141 </p>
142 <div className="flex flex-wrap gap-3 mt-4">
143 <div
144 role="button"
145 tabIndex={0}
146 onClick={() => onSectionChange("bookings")}
147 className="px-4 py-2 rounded-lg text-sm font-medium transition-all flex items-center gap-2 hover:opacity-90 cursor-pointer"
148 style={{
149 backgroundColor: "rgba(255, 255, 255, 0.2)",
150 color: "#ffffff",
151 backdropFilter: "blur(8px)",
152 }}
153 >
154 <Calendar className="w-4 h-4" style={{ color: "#ffffff" }} />
155 {__("View Calendar", "yatra")}
156 </div>
157 <div
158 role="button"
159 tabIndex={0}
160 onClick={() => onSectionChange("documents")}
161 className="px-4 py-2 rounded-lg text-sm font-medium transition-all flex items-center gap-2 hover:opacity-90 cursor-pointer"
162 style={{
163 backgroundColor: "rgba(255, 255, 255, 0.2)",
164 color: "#ffffff",
165 backdropFilter: "blur(8px)",
166 }}
167 >
168 <FileText className="w-4 h-4" style={{ color: "#ffffff" }} />
169 {__("My Documents", "yatra")}
170 </div>
171 </div>
172 </div>
173 <div className="hidden md:block">
174 <div
175 className="w-24 h-24 rounded-full flex items-center justify-center"
176 style={{
177 backgroundColor: "rgba(255, 255, 255, 0.1)",
178 backdropFilter: "blur(8px)",
179 }}
180 >
181 <Plane className="w-12 h-12" style={{ color: "#ffffff" }} />
182 </div>
183 </div>
184 </div>
185 </div>
186 <div
187 className="absolute top-0 right-0 w-64 h-64 rounded-full -mr-32 -mt-32"
188 style={{ backgroundColor: "rgba(255, 255, 255, 0.05)" }}
189 ></div>
190 <div
191 className="absolute bottom-0 left-0 w-48 h-48 rounded-full -ml-24 -mb-24"
192 style={{ backgroundColor: "rgba(255, 255, 255, 0.05)" }}
193 ></div>
194 </div>
195
196 {/* Stats Grid */}
197 <div className="yatra-dashboard-stats grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
198 {enhancedStats.map((stat) => (
199 <div
200 key={stat.label}
201 className={`yatra-stat-card yatra-stat-card-${stat.label.toLowerCase().replace(/\s+/g, "-")} group relative bg-white dark:bg-gray-800 border border-gray-100 dark:border-gray-700 rounded-xl shadow-sm hover:shadow-md transition-all overflow-hidden`}
202 >
203 <div className="p-6">
204 <div className="flex items-start justify-between mb-4">
205 <div className={`p-3 rounded-lg ${stat.bgColor}`}>
206 <stat.icon className={`w-6 h-6 ${stat.iconColor}`} />
207 </div>
208 {stat.badge && (
209 <span className="inline-flex text-xs px-2 py-1 rounded-full bg-yatra-chip-bg text-yatra-primary-dark dark:bg-yatra-surface-dark dark:text-yatra-primary-light font-medium">
210 {stat.badge}
211 </span>
212 )}
213 </div>
214 <div>
215 <p className="text-sm text-gray-500 dark:text-gray-400 mb-1">
216 {stat.label}
217 </p>
218 <p className="text-xl font-bold text-gray-900 dark:text-white">
219 {stat.value}
220 </p>
221 </div>
222 </div>
223 <div
224 className={`absolute bottom-0 left-0 right-0 h-1 bg-gradient-to-r ${stat.gradient} opacity-0 group-hover:opacity-100 transition-opacity`}
225 ></div>
226 </div>
227 ))}
228 </div>
229
230 {/* Main Content Grid */}
231 <div className="grid gap-6 lg:grid-cols-3">
232 {/* Upcoming Trips - Takes 2 columns */}
233 <div className="yatra-dashboard-upcoming-trips lg:col-span-2 bg-white dark:bg-gray-800 border border-gray-100 dark:border-gray-700 rounded-xl shadow-sm overflow-hidden">
234 <div className="yatra-dashboard-upcoming-trips-header p-6 border-b border-gray-100 dark:border-gray-700">
235 <div className="flex items-center justify-between">
236 <div className="flex items-center gap-3">
237 <div className="p-2 bg-emerald-50 dark:bg-emerald-900/20 rounded-lg">
238 <Calendar className="w-5 h-5 text-emerald-600 dark:text-emerald-400" />
239 </div>
240 <div>
241 <h3 className="text-base font-semibold text-gray-900 dark:text-white">
242 {__("Upcoming Trips", "yatra")}
243 </h3>
244 <p className="text-sm text-gray-500 dark:text-gray-400">
245 {__("Your next adventures", "yatra")}
246 </p>
247 </div>
248 </div>
249 {upcomingBookings.length > 0 && (
250 <div
251 role="button"
252 tabIndex={0}
253 onClick={() => onSectionChange("bookings")}
254 className="text-sm text-yatra-primary dark:text-yatra-on-dark hover:text-yatra-primary-dark dark:hover:text-yatra-primary-light font-medium flex items-center gap-1 transition-colors cursor-pointer"
255 >
256 {__("View all", "yatra")}
257 <ArrowRight className="w-4 h-4" />
258 </div>
259 )}
260 </div>
261 </div>
262 <div className="p-6">
263 {upcomingBookings.length > 0 ? (
264 <div className="space-y-4">
265 {upcomingBookings.map((booking) => {
266 // Each card was rendered with `cursor-pointer`, a
267 // hover-shadow and a `ChevronRight` affordance — i.e.
268 // styled as a clickable nav element — but had no
269 // onClick handler, so clicking did nothing. Deep-link
270 // into the booking detail view via the parent
271 // AccountPage so the user lands directly on the
272 // selected booking rather than the generic list.
273 const openBooking = () => {
274 const id = Number(booking.id);
275 if (!id) return;
276 if (onBookingOpen) {
277 onBookingOpen(id);
278 } else {
279 onSectionChange("bookings");
280 }
281 };
282 return (
283 <div
284 key={booking.id}
285 role="button"
286 tabIndex={0}
287 onClick={openBooking}
288 onKeyDown={(e) => {
289 if (e.key === "Enter" || e.key === " ") {
290 e.preventDefault();
291 openBooking();
292 }
293 }}
294 className="yatra-booking-card yatra-booking-card-upcoming group relative border border-gray-200 dark:border-gray-700 rounded-xl p-5 hover:border-yatra-border-hover dark:hover:border-yatra-border-hover-dark hover:shadow-md transition-all cursor-pointer bg-gradient-to-r from-white to-gray-50/50 dark:from-gray-800 dark:to-gray-800/50"
295 >
296 <div className="flex items-start gap-4">
297 <div className="flex-shrink-0">
298 <div className="w-12 h-12 bg-gradient-to-br from-yatra-primary to-yatra-primary-darker rounded-lg flex items-center justify-center shadow-sm">
299 <MapPin className="w-6 h-6 text-white" />
300 </div>
301 </div>
302 <div className="flex-1 min-w-0">
303 <div className="flex items-start justify-between gap-2 mb-2">
304 <h4 className="text-sm font-semibold text-gray-900 dark:text-white group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark transition-colors">
305 {booking.trip_title}
306 </h4>
307 <span className={getBadge(booking.booking_status)}>
308 {getStatusLabel(booking.booking_status)}
309 </span>
310 </div>
311 <p className="text-sm text-gray-600 dark:text-gray-400 mb-2 flex items-center gap-2">
312 <MapPin className="w-4 h-4" />
313 {booking.destination ||
314 __("Multiple destinations", "yatra")}
315 </p>
316 <div className="flex items-center gap-4 text-xs text-gray-500 dark:text-gray-400">
317 <span className="flex items-center gap-1">
318 <Calendar className="w-3.5 h-3.5" />
319 {formatTravelDateRange(
320 booking.travel_date,
321 booking.end_date,
322 )}
323 </span>
324 <span className="flex items-center gap-1">
325 <User className="w-3.5 h-3.5" />
326 {(() => {
327 // Use `travelers_count` for the count — see
328 // Booking interface comment in types.ts.
329 const n =
330 Number(
331 booking.travelers_count ??
332 booking.travelers ??
333 0,
334 ) || 0;
335 return sprintf(
336 // translators: %d: number of travelers on this booking
337 _n("%d Traveler", "%d Travelers", n, "yatra"),
338 n,
339 );
340 })()}
341 </span>
342 </div>
343 </div>
344 <ChevronRight className="w-5 h-5 text-gray-400 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark transition-colors flex-shrink-0" />
345 </div>
346 </div>
347 );
348 })}
349 </div>
350 ) : (
351 <div className="text-center py-12">
352 <div className="w-16 h-16 bg-gray-100 dark:bg-gray-700 rounded-full flex items-center justify-center mx-auto mb-4">
353 <Calendar className="w-8 h-8 text-gray-400" />
354 </div>
355 <p className="text-gray-500 dark:text-gray-400 font-medium mb-1">
356 {__("No upcoming trips", "yatra")}
357 </p>
358 <p className="text-sm text-gray-400 dark:text-gray-500">
359 {__("Start planning your next adventure!", "yatra")}
360 </p>
361 </div>
362 )}
363 </div>
364 </div>
365
366 {/* Quick Actions & Notifications - Takes 1 column */}
367 <div className="space-y-6">
368 {/* Quick Actions */}
369 <div className="yatra-dashboard-quick-actions bg-white dark:bg-gray-800 border border-gray-100 dark:border-gray-700 rounded-xl shadow-sm overflow-hidden">
370 <div className="yatra-dashboard-quick-actions-header p-6 border-b border-gray-100 dark:border-gray-700">
371 <div className="flex items-center gap-3">
372 <div className="p-2 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
373 <Sparkles className="w-5 h-5 text-purple-600 dark:text-purple-400" />
374 </div>
375 <h3 className="text-base font-semibold text-gray-900 dark:text-white">
376 {__("Quick Actions", "yatra")}
377 </h3>
378 </div>
379 </div>
380 <div className="p-6 space-y-3">
381 <div
382 role="button"
383 tabIndex={0}
384 onClick={() => onSectionChange("bookings")}
385 className="w-full flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:border-yatra-border-hover dark:hover:border-yatra-border-hover-dark hover:bg-yatra-hover-soft dark:hover:bg-yatra-hover-soft-dark transition-all group cursor-pointer text-sm"
386 >
387 <div className="flex items-center gap-3">
388 <Package className="w-5 h-5 text-gray-400 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark" />
389 <span className="font-medium text-gray-700 dark:text-gray-300 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark">
390 {__("View Bookings", "yatra")}
391 </span>
392 </div>
393 <ChevronRight className="w-4 h-4 text-gray-400 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark" />
394 </div>
395 <div
396 role="button"
397 tabIndex={0}
398 onClick={() => onSectionChange("payments")}
399 className="w-full flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:border-yatra-border-hover dark:hover:border-yatra-border-hover-dark hover:bg-yatra-hover-soft dark:hover:bg-yatra-hover-soft-dark transition-all group cursor-pointer text-sm"
400 >
401 <div className="flex items-center gap-3">
402 <CreditCard className="w-5 h-5 text-gray-400 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark" />
403 <span className="font-medium text-gray-700 dark:text-gray-300 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark">
404 {__("Make Payment", "yatra")}
405 </span>
406 </div>
407 <ChevronRight className="w-4 h-4 text-gray-400 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark" />
408 </div>
409 <div
410 role="button"
411 tabIndex={0}
412 onClick={() => onSectionChange("documents")}
413 className="w-full flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:border-yatra-border-hover dark:hover:border-yatra-border-hover-dark hover:bg-yatra-hover-soft dark:hover:bg-yatra-hover-soft-dark transition-all group cursor-pointer text-sm"
414 >
415 <div className="flex items-center gap-3">
416 <FileText className="w-5 h-5 text-gray-400 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark" />
417 <span className="font-medium text-gray-700 dark:text-gray-300 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark">
418 {__("My Documents", "yatra")}
419 </span>
420 </div>
421 <ChevronRight className="w-4 h-4 text-gray-400 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark" />
422 </div>
423 {conciergeTel ? (
424 <a
425 href={conciergeTel}
426 className="w-full flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:border-yatra-border-hover dark:hover:border-yatra-border-hover-dark hover:bg-yatra-hover-soft dark:hover:bg-yatra-hover-soft-dark transition-all group cursor-pointer text-sm no-underline text-inherit"
427 >
428 <div className="flex items-center gap-3">
429 <LifeBuoy className="w-5 h-5 text-gray-400 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark" />
430 <span className="font-medium text-gray-700 dark:text-gray-300 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark">
431 {__("Call us", "yatra")}
432 </span>
433 </div>
434 <ChevronRight className="w-4 h-4 text-gray-400 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark" />
435 </a>
436 ) : conciergeMail ? (
437 <a
438 href={`mailto:${encodeURIComponent(conciergeMail)}`}
439 className="w-full flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:border-yatra-border-hover dark:hover:border-yatra-border-hover-dark hover:bg-yatra-hover-soft dark:hover:bg-yatra-hover-soft-dark transition-all group cursor-pointer text-sm no-underline text-inherit"
440 >
441 <div className="flex items-center gap-3">
442 <LifeBuoy className="w-5 h-5 text-gray-400 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark" />
443 <span className="font-medium text-gray-700 dark:text-gray-300 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark">
444 {__("Email us", "yatra")}
445 </span>
446 </div>
447 <ChevronRight className="w-4 h-4 text-gray-400 group-hover:text-yatra-primary dark:group-hover:text-yatra-on-dark" />
448 </a>
449 ) : null}
450 </div>
451 </div>
452
453 {/* Notifications */}
454 <div className="yatra-dashboard-notifications bg-white dark:bg-gray-800 border border-gray-100 dark:border-gray-700 rounded-xl shadow-sm overflow-hidden">
455 <div className="yatra-dashboard-notifications-header p-6 border-b border-gray-100 dark:border-gray-700">
456 <div className="flex items-center gap-3">
457 <div className="p-2 bg-amber-50 dark:bg-amber-900/20 rounded-lg">
458 <Bell className="w-5 h-5 text-amber-600 dark:text-amber-400" />
459 </div>
460 <div>
461 <h3 className="text-base font-semibold text-gray-900 dark:text-white">
462 {__("Notifications", "yatra")}
463 </h3>
464 <p className="text-xs text-gray-500 dark:text-gray-400">
465 {__("Updates & reminders", "yatra")}
466 </p>
467 </div>
468 </div>
469 </div>
470 <div className="p-6 space-y-3">
471 {displayNotifications.length > 0 ? (
472 displayNotifications.map((notif) => (
473 <div
474 key={notif.id}
475 className={`p-4 rounded-lg border ${
476 notif.type === "warning"
477 ? "bg-amber-50 dark:bg-amber-900/10 border-amber-200 dark:border-amber-800"
478 : "bg-yatra-soft dark:bg-yatra-hover-soft-dark border-yatra-border-subtle dark:border-yatra-border-dark"
479 }`}
480 >
481 <div className="flex items-start gap-3">
482 {notif.type === "warning" ? (
483 <AlertCircle className="w-5 h-5 text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5" />
484 ) : (
485 <CheckCircle2 className="w-5 h-5 text-yatra-primary dark:text-yatra-on-dark flex-shrink-0 mt-0.5" />
486 )}
487 <div className="flex-1 min-w-0">
488 <p className="font-semibold text-sm text-gray-900 dark:text-white mb-1">
489 {notif.title}
490 </p>
491 <p className="text-xs text-gray-600 dark:text-gray-400 leading-relaxed">
492 {notif.message}
493 </p>
494 </div>
495 </div>
496 </div>
497 ))
498 ) : (
499 <div className="text-center py-6">
500 <Bell className="w-8 h-8 text-gray-300 dark:text-gray-600 mx-auto mb-2" />
501 <p className="text-sm text-gray-400 dark:text-gray-500">
502 {__("No new notifications", "yatra")}
503 </p>
504 </div>
505 )}
506 </div>
507 </div>
508 </div>
509 </div>
510
511 {/* Recent Activity Section */}
512 {(recentBookings.length > 0 || pendingPayments.length > 0) && (
513 <div className="yatra-dashboard-recent-activity bg-white dark:bg-gray-800 border border-gray-100 dark:border-gray-700 rounded-xl shadow-sm overflow-hidden">
514 <div className="yatra-dashboard-recent-activity-header p-6 border-b border-gray-100 dark:border-gray-700">
515 <div className="flex items-center gap-3">
516 <div className="p-2 bg-yatra-soft dark:bg-yatra-surface-dark-muted rounded-lg">
517 <Clock className="w-5 h-5 text-yatra-primary dark:text-yatra-on-dark" />
518 </div>
519 <div>
520 <h3 className="text-base font-semibold text-gray-900 dark:text-white">
521 {__("Recent Activity", "yatra")}
522 </h3>
523 <p className="text-sm text-gray-500 dark:text-gray-400">
524 {__("Your latest updates", "yatra")}
525 </p>
526 </div>
527 </div>
528 </div>
529 <div className="p-6">
530 <div className="space-y-4">
531 {recentBookings.map((booking) => (
532 <div
533 key={booking.id}
534 className="flex items-center gap-4 pb-4 border-b border-gray-100 dark:border-gray-700 last:border-0 last:pb-0"
535 >
536 <div className="w-10 h-10 bg-yatra-chip-bg dark:bg-yatra-surface-dark rounded-full flex items-center justify-center flex-shrink-0">
537 <CheckCircle2 className="w-5 h-5 text-yatra-primary dark:text-yatra-on-dark" />
538 </div>
539 <div className="flex-1 min-w-0">
540 <p className="text-sm font-medium text-gray-900 dark:text-white">
541 {__("Booking confirmed:", "yatra")} {booking.trip_title}
542 </p>
543 <p className="text-xs text-gray-500 dark:text-gray-400">
544 {formatDate(booking.booking_date || booking.created_at)}
545 </p>
546 </div>
547 <span className={getBadge(booking.booking_status)}>
548 {getStatusLabel(booking.booking_status)}
549 </span>
550 </div>
551 ))}
552 {pendingPayments.map((payment) => (
553 <div
554 key={payment.id}
555 className="flex items-center gap-4 pb-4 border-b border-gray-100 dark:border-gray-700 last:border-0 last:pb-0"
556 >
557 <div className="w-10 h-10 bg-amber-100 dark:bg-amber-900/30 rounded-full flex items-center justify-center flex-shrink-0">
558 <Clock className="w-5 h-5 text-amber-600 dark:text-amber-400" />
559 </div>
560 <div className="flex-1 min-w-0">
561 <p className="text-sm font-medium text-gray-900 dark:text-white">
562 {__("Payment pending:", "yatra")}{" "}
563 {currency(payment.amount)}
564 </p>
565 <p className="text-xs text-gray-500 dark:text-gray-400">
566 {formatDate(payment.date)}
567 </p>
568 </div>
569 <span className={getBadge(payment.status)}>
570 {getStatusLabel(payment.status)}
571 </span>
572 </div>
573 ))}
574 </div>
575 </div>
576 </div>
577 )}
578 </div>
579 );
580 };
581
582 export default Dashboard;
583