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

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