PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.13
Yatra – Travel Booking & Tour Operator Software v3.0.13
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 / trips / TripsTable.tsx

TripsTable.tsx in Yatra – Travel Booking & Tour Operator Software 3.0.13, at resources/js/components/trips/TripsTable.tsx

226 lines 7.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Trips Table Component
3 * Modular table component for displaying trips
4 * Supports extensibility for Pro features
5 */
6
7 import React, { useState } from "react";
8 import { __ } from "../../lib/i18n";
9 import { formatYatraMoney } from "../../lib/currency-display";
10 import { ConditionalRender } from "../ui/conditional-render";
11 import {
12 Table,
13 TableBody,
14 TableCell,
15 TableHead,
16 TableHeader,
17 TableRow,
18 } from "../ui/table";
19 import { Button } from "../ui/button";
20 import { Badge } from "../ui/badge";
21 import { Edit, Trash2, Eye } from "lucide-react";
22 import { ConfirmationDialog } from "../ui/confirmation-dialog";
23
24 interface Trip {
25 id: number;
26 title: string;
27 slug: string;
28 price: number;
29 status: string;
30 created_at: string;
31 bookings_count?: number;
32 featured?: boolean;
33 }
34
35 interface TripsTableProps {
36 trips: Trip[];
37 loading?: boolean;
38 onEdit?: (trip: Trip) => void;
39 onDelete?: (trip: Trip) => void;
40 onView?: (trip: Trip) => void;
41 showProFeatures?: boolean;
42 }
43
44 /**
45 * Trips Table Component
46 * Displays trips in a table format with actions
47 */
48 export const TripsTable: React.FC<TripsTableProps> = ({
49 trips,
50 loading = false,
51 onEdit,
52 onDelete,
53 onView,
54 showProFeatures = false,
55 }) => {
56 // Uses the shared ConfirmationDialog like the rest of the admin, instead of
57 // the browser's native confirm() which ignores the admin theme.
58 const [tripToDelete, setTripToDelete] = useState<any | null>(null);
59
60 const formatDate = (dateString: string) => {
61 return new Date(dateString).toLocaleDateString();
62 };
63
64 const defaultCurrency =
65 (typeof window !== "undefined" &&
66 ((window as unknown as { yatraAdmin?: { currency?: string } }).yatraAdmin
67 ?.currency ||
68 (window as unknown as { yatraBookingData?: { currency?: string } })
69 .yatraBookingData?.currency)) ||
70 "USD";
71
72 const formatPrice = (price: number) =>
73 formatYatraMoney(Number(price) || 0, defaultCurrency, {
74 zeroAsUnknown: false,
75 });
76
77 const getStatusBadge = (status: string) => {
78 const statusMap: Record<
79 string,
80 {
81 variant: "default" | "success" | "warning" | "error" | "info";
82 label: string;
83 }
84 > = {
85 active: { variant: "success", label: __("Active", "yatra") },
86 draft: { variant: "default", label: __("Draft", "yatra") },
87 inactive: { variant: "error", label: __("Inactive", "yatra") },
88 pending: { variant: "warning", label: __("Pending", "yatra") },
89 };
90
91 const statusInfo = statusMap[status] || {
92 variant: "default" as const,
93 label: status,
94 };
95 return <Badge variant={statusInfo.variant}>{statusInfo.label}</Badge>;
96 };
97
98 if (loading) {
99 return (
100 <div className="p-8 text-center text-gray-500 dark:text-gray-400">
101 {__("Loading trips...", "yatra")}
102 </div>
103 );
104 }
105
106 if (trips.length === 0) {
107 return (
108 <div className="p-8 text-center text-gray-500 dark:text-gray-400">
109 {__("No trips found", "yatra")}
110 </div>
111 );
112 }
113
114 return (
115 <div className="border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden">
116 <Table>
117 <TableHeader>
118 <TableRow>
119 <TableHead>{__("Title", "yatra")}</TableHead>
120 <TableHead>{__("Slug", "yatra")}</TableHead>
121 <TableHead>{__("Price", "yatra")}</TableHead>
122 <TableHead>{__("Status", "yatra")}</TableHead>
123 {showProFeatures && (
124 <TableHead>{__("Bookings", "yatra")}</TableHead>
125 )}
126 <TableHead>{__("Created", "yatra")}</TableHead>
127 <TableHead className="text-right">
128 {__("Actions", "yatra")}
129 </TableHead>
130 </TableRow>
131 </TableHeader>
132 <TableBody>
133 {trips.map((trip) => (
134 <TableRow key={trip.id}>
135 <TableCell className="font-medium">
136 <div className="flex items-center gap-2">
137 {trip.title}
138 {trip.featured && showProFeatures && (
139 <Badge variant="info" className="text-xs">
140 {__("Featured", "yatra")}
141 </Badge>
142 )}
143 </div>
144 </TableCell>
145 <TableCell className="text-gray-500 dark:text-gray-400">
146 {trip.slug}
147 </TableCell>
148 <TableCell>{formatPrice(trip.price)}</TableCell>
149 <TableCell>{getStatusBadge(trip.status)}</TableCell>
150 {showProFeatures && (
151 <TableCell>{trip.bookings_count || 0}</TableCell>
152 )}
153 <TableCell className="text-gray-500 dark:text-gray-400">
154 {formatDate(trip.created_at)}
155 </TableCell>
156 <TableCell className="text-right">
157 <div className="flex items-center justify-end gap-2">
158 <ConditionalRender capability="yatra_view_trips">
159 {onView && (
160 <Button
161 variant="ghost"
162 size="sm"
163 onClick={() => onView(trip)}
164 aria-label={__("View trip", "yatra")}
165 >
166 <Eye className="w-4 h-4" />
167 </Button>
168 )}
169 </ConditionalRender>
170
171 <ConditionalRender capability="yatra_edit_trips">
172 {onEdit && (
173 <Button
174 variant="ghost"
175 size="sm"
176 onClick={() => onEdit(trip)}
177 aria-label={__("Edit trip", "yatra")}
178 >
179 <Edit className="w-4 h-4" />
180 </Button>
181 )}
182 </ConditionalRender>
183
184 <ConditionalRender capability="yatra_delete_trips">
185 {onDelete && (
186 <Button
187 variant="ghost"
188 size="sm"
189 onClick={() => setTripToDelete(trip)}
190 aria-label={__("Delete trip", "yatra")}
191 >
192 <Trash2 className="w-4 h-4 text-red-500" />
193 </Button>
194 )}
195 </ConditionalRender>
196 </div>
197 </TableCell>
198 </TableRow>
199 ))}
200 </TableBody>
201 </Table>
202
203 <ConfirmationDialog
204 isOpen={tripToDelete !== null}
205 onClose={() => setTripToDelete(null)}
206 onConfirm={() => {
207 if (tripToDelete && onDelete) {
208 onDelete(tripToDelete);
209 }
210 setTripToDelete(null);
211 }}
212 title={__("Delete Trip", "yatra")}
213 message={__(
214 "Are you sure you want to delete this trip? This action cannot be undone.",
215 "yatra",
216 )}
217 confirmText={__("Delete", "yatra")}
218 cancelText={__("Cancel", "yatra")}
219 variant="danger"
220 />
221 </div>
222 );
223 };
224
225 export default TripsTable;
226