PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.10
Yatra – Travel Booking & Tour Operator Software v3.0.10
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.10, at resources/js/pages/account/Support.tsx

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