PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.6
Yatra – Travel Booking & Tour Operator Software v3.0.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 / UpcomingDepartures.tsx

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

168 lines 5.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Upcoming Departures Widget
3 * Displays trips with upcoming departure dates
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, MapPin, Users } from "lucide-react";
11
12 interface Departure {
13 id: number;
14 trip_id?: number;
15 trip_title: string;
16 departure_date: string;
17 available_spots: number;
18 total_spots: number;
19 status: string;
20 destination?: string;
21 }
22
23 interface UpcomingDeparturesProps {
24 departures: Departure[];
25 loading?: boolean;
26 onView?: (departure: Departure) => void;
27 }
28
29 /**
30 * Upcoming Departures Widget
31 */
32 export const UpcomingDepartures: React.FC<UpcomingDeparturesProps> = ({
33 departures,
34 loading = false,
35 onView,
36 }) => {
37 const formatDate = (dateString: string) => {
38 const date = new Date(dateString);
39 return date.toLocaleDateString("en-US", {
40 month: "short",
41 day: "numeric",
42 year: "numeric",
43 });
44 };
45
46 const getDaysUntil = (dateString: string) => {
47 const today = new Date();
48 const departure = new Date(dateString);
49 const diffTime = departure.getTime() - today.getTime();
50 const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
51 return diffDays;
52 };
53
54 if (loading) {
55 return (
56 <Card>
57 <CardHeader>
58 <CardTitle>{__("Upcoming Departures", "yatra")}</CardTitle>
59 </CardHeader>
60 <CardContent>
61 <div className="text-sm text-gray-500 dark:text-gray-400">
62 {__("Loading...", "yatra")}
63 </div>
64 </CardContent>
65 </Card>
66 );
67 }
68
69 return (
70 <Card>
71 <CardHeader>
72 <CardTitle>{__("Upcoming Departures", "yatra")}</CardTitle>
73 </CardHeader>
74 <CardContent>
75 {departures && departures.length > 0 ? (
76 <div className="space-y-2">
77 {departures.slice(0, 5).map((departure) => {
78 const daysUntil = getDaysUntil(departure.departure_date);
79 const total = Number(departure.total_spots) || 0;
80 let occupancy = 0;
81 if (total > 0) {
82 occupancy =
83 ((total - Number(departure.available_spots || 0)) / total) *
84 100;
85 }
86 // Clamp to [0, 100] to avoid negative or >100 values
87 occupancy = Math.max(0, Math.min(100, occupancy));
88
89 return (
90 <div
91 key={departure.id}
92 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"
93 onClick={() => {
94 if (onView) {
95 onView(departure);
96 return;
97 }
98 const admin = (window as any)?.yatraAdmin;
99 const baseUrl = admin?.siteUrl || "";
100 const tripId = departure.trip_id || "";
101 const query = `?page=yatra&subpage=departures&action=view&id=${departure.id}${tripId ? `&trip_id=${tripId}` : ""}`;
102 window.location.href = `${baseUrl}/wp-admin/admin.php${query}`;
103 }}
104 >
105 <div className="flex items-start justify-between mb-2">
106 <div className="flex-1">
107 <h4 className="font-medium text-gray-900 dark:text-white mb-1">
108 {departure.trip_title}
109 </h4>
110 {departure.destination && (
111 <div className="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-400 mb-2">
112 <MapPin className="w-3 h-3" />
113 <span>{departure.destination}</span>
114 </div>
115 )}
116 </div>
117 {daysUntil >= 0 && daysUntil <= 7 && (
118 <Badge variant={daysUntil <= 3 ? "error" : "warning"}>
119 {daysUntil === 0
120 ? __("Today", "yatra")
121 : daysUntil === 1
122 ? __("Tomorrow", "yatra")
123 : `${daysUntil} ${__("days", "yatra")}`}
124 </Badge>
125 )}
126 </div>
127
128 <div className="flex items-center gap-4 text-sm text-gray-600 dark:text-gray-400">
129 <div className="flex items-center gap-1">
130 <Calendar className="w-4 h-4" />
131 <span>{formatDate(departure.departure_date)}</span>
132 </div>
133 <div className="flex items-center gap-1">
134 <Users className="w-4 h-4" />
135 <span>
136 {departure.available_spots} / {departure.total_spots}{" "}
137 {__("available", "yatra")}
138 </span>
139 </div>
140 </div>
141
142 <div className="mt-2">
143 <div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
144 <div
145 className="bg-blue-600 h-2 rounded-full transition-all"
146 style={{ width: `${occupancy}%` }}
147 />
148 </div>
149 <span className="text-xs text-gray-500 dark:text-gray-400 mt-1 block">
150 {occupancy.toFixed(0)}% {__("occupied", "yatra")}
151 </span>
152 </div>
153 </div>
154 );
155 })}
156 </div>
157 ) : (
158 <p className="text-sm text-gray-500 dark:text-gray-400">
159 {__("No upcoming departures", "yatra")}
160 </p>
161 )}
162 </CardContent>
163 </Card>
164 );
165 };
166
167 export default UpcomingDepartures;
168