PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.4
Yatra – Travel Booking & Tour Operator Software v3.0.4
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 / Support.tsx

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

334 lines 13.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import React, { useState } from "react";
2 import {
3 LifeBuoy,
4 AlertTriangle,
5 CheckCircle,
6 Clock as ClockIcon,
7 FileText as FileTextIcon,
8 Eye,
9 Phone as PhoneIcon,
10 Mail,
11 } from "lucide-react";
12 import { __ } from "../../lib/i18n";
13 import {
14 formatDate,
15 getBadge,
16 phoneToTelHref,
17 getYatraAccountPageGlobals,
18 } from "./utils";
19 import type { SupportTicket } from "./types";
20
21 interface SupportProps {
22 tickets: SupportTicket[];
23 }
24
25 const Support: React.FC<SupportProps> = ({ tickets }) => {
26 const [ticketSubject, setTicketSubject] = useState("");
27 const [ticketMessage, setTicketMessage] = useState("");
28 const accountShell = getYatraAccountPageGlobals();
29 const supportTel = phoneToTelHref(accountShell.companyPhone);
30 const supportMail = String(accountShell.companyEmail || "").trim();
31
32 const displayTickets = tickets;
33
34 const handleSubmitTicket = (e: React.FormEvent) => {
35 e.preventDefault();
36 // TODO: Implement ticket submission
37 setTicketSubject("");
38 setTicketMessage("");
39 };
40
41 const getStatusIcon = (status: string) => {
42 switch (status) {
43 case "resolved":
44 return (
45 <CheckCircle className="w-4 h-4 text-emerald-600 dark:text-emerald-400" />
46 );
47 case "awaiting_response":
48 return (
49 <ClockIcon className="w-4 h-4 text-amber-600 dark:text-amber-400" />
50 );
51 case "open":
52 return (
53 <AlertTriangle className="w-4 h-4 text-yatra-primary dark:text-yatra-on-dark" />
54 );
55 default:
56 return (
57 <LifeBuoy className="w-4 h-4 text-gray-600 dark:text-gray-400" />
58 );
59 }
60 };
61
62 return (
63 <div className="yatra-support-page space-y-6">
64 {/* Header */}
65 <div className="yatra-support-header bg-white dark:bg-gray-800 border border-gray-100 dark:border-gray-700 rounded-xl shadow-sm p-6">
66 <div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
67 <div>
68 <h2 className="text-lg font-bold text-gray-900 dark:text-white flex items-center gap-3">
69 <div className="p-2 bg-yatra-soft dark:bg-yatra-surface-dark-muted rounded-lg">
70 <LifeBuoy className="w-6 h-6 text-yatra-primary dark:text-yatra-on-dark" />
71 </div>
72 {__("Support & Help Center", "yatra")}
73 </h2>
74 <p className="text-sm text-gray-500 dark:text-gray-400 mt-2">
75 {__(
76 "Our team is available 24/7 for urgent requests. Submit a ticket or contact us directly.",
77 "yatra",
78 )}
79 </p>
80 </div>
81 </div>
82 </div>
83
84 {/* Support Statistics */}
85 <div className="yatra-support-stats flex flex-nowrap gap-6 overflow-x-auto">
86 <div className="flex-shrink-0 yatra-support-stat-card bg-white dark:bg-gray-800 border border-gray-100 dark:border-gray-700 rounded-xl p-6 min-w-0 flex-1">
87 <div className="flex items-center justify-between">
88 <div>
89 <p className="text-xs text-gray-500 dark:text-gray-400 mb-1">
90 {__("Total Tickets", "yatra")}
91 </p>
92 <p className="text-xl font-bold text-gray-900 dark:text-white">
93 {displayTickets.length}
94 </p>
95 </div>
96 <div className="p-2 bg-yatra-soft dark:bg-yatra-surface-dark-muted rounded-lg">
97 <LifeBuoy className="w-5 h-5 text-yatra-primary dark:text-yatra-on-dark" />
98 </div>
99 </div>
100 </div>
101 <div className="flex-shrink-0 yatra-support-stat-card bg-white dark:bg-gray-800 border border-gray-100 dark:border-gray-700 rounded-xl p-6 min-w-0 flex-1">
102 <div className="flex items-center justify-between">
103 <div>
104 <p className="text-xs text-gray-500 dark:text-gray-400 mb-1">
105 {__("Open", "yatra")}
106 </p>
107 <p className="text-xl font-bold text-yatra-primary dark:text-yatra-on-dark">
108 {
109 displayTickets.filter(
110 (t) =>
111 t.status === "open" || t.status === "awaiting_response",
112 ).length
113 }
114 </p>
115 </div>
116 <div className="p-2 bg-yatra-soft dark:bg-yatra-surface-dark-muted rounded-lg">
117 <AlertTriangle className="w-5 h-5 text-yatra-primary dark:text-yatra-on-dark" />
118 </div>
119 </div>
120 </div>
121 <div className="flex-shrink-0 yatra-support-stat-card bg-white dark:bg-gray-800 border border-gray-100 dark:border-gray-700 rounded-xl p-6 min-w-0 flex-1">
122 <div className="flex items-center justify-between">
123 <div>
124 <p className="text-xs text-gray-500 dark:text-gray-400 mb-1">
125 {__("Resolved", "yatra")}
126 </p>
127 <p className="text-xl font-bold text-emerald-600 dark:text-emerald-400">
128 {displayTickets.filter((t) => t.status === "resolved").length}
129 </p>
130 </div>
131 <div className="p-2 bg-emerald-50 dark:bg-emerald-900/20 rounded-lg">
132 <CheckCircle className="w-5 h-5 text-emerald-600 dark:text-emerald-400" />
133 </div>
134 </div>
135 </div>
136 </div>
137
138 {/* Create New Ticket */}
139 <div className="yatra-support-form-card bg-white dark:bg-gray-800 border border-gray-100 dark:border-gray-700 rounded-xl shadow-sm p-6">
140 <div className="yatra-support-form-header flex items-center gap-3 mb-6">
141 <div className="p-2 bg-yatra-soft dark:bg-yatra-surface-dark-muted rounded-lg">
142 <LifeBuoy className="w-6 h-6 text-yatra-primary dark:text-yatra-on-dark" />
143 </div>
144 <div>
145 <h3 className="text-base font-semibold text-gray-900 dark:text-white">
146 {__("Create a new support ticket", "yatra")}
147 </h3>
148 <p className="text-sm text-gray-500 dark:text-gray-400">
149 {__(
150 "Please describe your issue and we will get back to you as soon as possible.",
151 "yatra",
152 )}
153 </p>
154 </div>
155 </div>
156 <form
157 onSubmit={handleSubmitTicket}
158 className="yatra-support-form space-y-4"
159 >
160 <div>
161 <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
162 {__("Subject", "yatra")} <span className="text-red-500">*</span>
163 </label>
164 <input
165 type="text"
166 value={ticketSubject}
167 onChange={(e) => setTicketSubject(e.target.value)}
168 placeholder={__("Enter ticket subject", "yatra")}
169 required
170 className="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white px-4 py-2 text-sm focus:ring-2 focus:ring-yatra-primary focus:border-transparent"
171 />
172 </div>
173 <div>
174 <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
175 {__("Message", "yatra")} <span className="text-red-500">*</span>
176 </label>
177 <textarea
178 value={ticketMessage}
179 onChange={(e) => setTicketMessage(e.target.value)}
180 placeholder={__("How can we help?", "yatra")}
181 required
182 rows={6}
183 className="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white px-4 py-2 text-sm focus:ring-2 focus:ring-yatra-primary focus:border-transparent"
184 />
185 </div>
186 <button
187 type="submit"
188 className="yatra-support-submit-btn inline-flex items-center gap-2 px-6 py-2 rounded-lg bg-yatra-primary text-white hover:bg-yatra-primary-dark transition-colors text-sm font-medium"
189 >
190 <LifeBuoy className="w-4 h-4" /> {__("Submit Ticket", "yatra")}
191 </button>
192 </form>
193 </div>
194
195 {/* Support Tickets List */}
196 <div className="yatra-support-tickets bg-white dark:bg-gray-800 border border-gray-100 dark:border-gray-700 rounded-xl shadow-sm p-6">
197 <div className="flex items-center justify-between mb-6">
198 <h3 className="text-base font-semibold text-gray-900 dark:text-white flex items-center gap-2">
199 <FileTextIcon className="w-5 h-5 text-gray-600 dark:text-gray-400" />
200 {__("Your Support Tickets", "yatra")}
201 </h3>
202 </div>
203 {displayTickets.length === 0 ? (
204 <div className="text-center py-12">
205 <LifeBuoy className="w-16 h-16 text-gray-300 dark:text-gray-600 mx-auto mb-4" />
206 <p className="text-gray-500 dark:text-gray-400 font-medium mb-1">
207 {__("No support tickets yet", "yatra")}
208 </p>
209 <p className="text-sm text-gray-400 dark:text-gray-500">
210 {__("Create a ticket above to get started.", "yatra")}
211 </p>
212 </div>
213 ) : (
214 <div className="yatra-support-tickets-list space-y-4">
215 {displayTickets.map((ticket) => {
216 const isResolved = ticket.status === "resolved";
217 const isAwaiting = ticket.status === "awaiting_response";
218
219 return (
220 <div
221 key={ticket.id}
222 className="yatra-support-ticket-card group bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-6 hover:shadow-md transition-all"
223 >
224 <div className="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-4">
225 <div className="flex-1">
226 <div className="flex items-start gap-3 mb-3">
227 <div
228 className={`p-2 rounded-lg ${
229 isResolved
230 ? "bg-emerald-50 dark:bg-emerald-900/20"
231 : isAwaiting
232 ? "bg-amber-50 dark:bg-amber-900/20"
233 : "bg-yatra-soft dark:bg-yatra-surface-dark-muted"
234 }`}
235 >
236 {getStatusIcon(ticket.status)}
237 </div>
238 <div className="flex-1">
239 <h4 className="text-base font-semibold text-gray-900 dark:text-white mb-1">
240 {ticket.subject}
241 </h4>
242 <p className="text-sm text-gray-500 dark:text-gray-400 flex items-center gap-1">
243 <ClockIcon className="w-4 h-4" />
244 {__("Last updated", "yatra")}:{" "}
245 {formatDate(ticket.updated_at)}
246 </p>
247 </div>
248 </div>
249 </div>
250 <div className="flex items-center gap-3">
251 <span className={getBadge(ticket.status)}>
252 {__(ticket.status, ticket.status)}
253 </span>
254 <div
255 role="button"
256 tabIndex={0}
257 onClick={() => {}}
258 className="yatra-support-ticket-view inline-flex items-center gap-2 px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors text-sm font-medium cursor-pointer"
259 >
260 <Eye className="w-4 h-4" />
261 {__("View", "yatra")}
262 </div>
263 </div>
264 </div>
265 </div>
266 );
267 })}
268 </div>
269 )}
270 </div>
271
272 {/* Contact Information */}
273 <div
274 className="yatra-support-contact rounded-xl p-6 text-white shadow-xl"
275 style={{
276 backgroundImage:
277 "linear-gradient(to bottom right, var(--yatra-primary-dark, #2563eb), var(--yatra-primary-darker, #1e40af))",
278 }}
279 >
280 <div className="flex items-start gap-4">
281 <div className="p-3 bg-white/20 rounded-lg flex-shrink-0">
282 <PhoneIcon className="w-6 h-6 text-white" />
283 </div>
284 <div className="flex-1">
285 <h3 className="text-base font-semibold text-white mb-2">
286 {__("Need Immediate Assistance?", "yatra")}
287 </h3>
288 <p className="text-sm text-white/90 mb-4">
289 {__(
290 "Call our 24/7 concierge desk for urgent travel support.",
291 "yatra",
292 )}
293 </p>
294 <div className="flex flex-wrap gap-4">
295 {supportTel ? (
296 <div className="flex items-center gap-2">
297 <PhoneIcon className="w-4 h-4 text-white flex-shrink-0" />
298 <a
299 href={supportTel}
300 className="text-sm font-medium text-white hover:underline"
301 >
302 {accountShell.companyPhone}
303 </a>
304 </div>
305 ) : null}
306 {supportMail ? (
307 <div className="flex items-center gap-2">
308 <Mail className="w-4 h-4 text-white flex-shrink-0" />
309 <a
310 href={`mailto:${encodeURIComponent(supportMail)}`}
311 className="text-sm font-medium text-white hover:underline break-all"
312 >
313 {supportMail}
314 </a>
315 </div>
316 ) : null}
317 {!supportTel && !supportMail ? (
318 <p className="text-sm text-white/80">
319 {__(
320 "Contact details are not configured. Please reach out through your booking confirmation email.",
321 "yatra",
322 )}
323 </p>
324 ) : null}
325 </div>
326 </div>
327 </div>
328 </div>
329 </div>
330 );
331 };
332
333 export default Support;
334