PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.17
Yatra – Travel Booking & Tour Operator Software v3.0.17
3.0.17 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 All 85 releases
← All changes | resources/js/pages/ViewEnquiry.tsx +34 -9 3.0.3 → 3.0.17 View file →
@@ -18,8 +18,9 @@
18 18 Send,
19 19 Loader2,
20 20 } from "lucide-react";
21 21 import { __ } from "../lib/i18n";
22 +import { toDateValue } from "../lib/dateFormat";
22 23 import { apiService } from "../lib/api-client";
23 24 import { usePermissions } from "../hooks/usePermissions";
24 25 import { Button } from "../components/ui/button";
25 26 import { PageHeader } from "../components/common/PageHeader";
@@ -29,8 +30,9 @@
29 30 CardHeader,
30 31 CardTitle,
31 32 } from "../components/ui/card";
32 33 import { ConditionalRender } from "../components/ui/conditional-render";
34 +import { EnquiryReplyAffordance } from "../components/ai/EnquiryReplyAffordance";
33 35 import { Badge } from "../components/ui/badge";
34 36 import { useNavigate } from "../hooks/useNavigate";
35 37 import { Skeleton } from "../components/ui/skeleton";
36 38 import { Modal } from "../components/ui/modal";
@@ -86,9 +88,9 @@
86 88 const response = await apiService.getEnquiry(enquiryId!);
87 89 // Some endpoints return { success, data }, others return the object directly
88 90 return (response as any)?.data ?? response;
89 91 },
90 - enabled: !!enquiryId && can("yatra_view_bookings"),
92 + enabled: !!enquiryId && can("yatra_view_enquiries"),
91 93 });
92 94
93 95 // Respond mutation
94 96 const respondMutation = useMutation({
@@ -103,9 +105,11 @@
103 105 },
104 106 });
105 107
106 108 const formatDate = (dateString: string) => {
107 - return new Date(dateString).toLocaleDateString("en-US", {
109 + // toDateValue: a date-only travel_date must not roll back a day in a
110 + // behind-UTC zone (datetime values like created_at pass through unchanged).
111 + return toDateValue(dateString).toLocaleDateString("en-US", {
108 112 weekday: "long",
109 113 year: "numeric",
110 114 month: "long",
111 115 day: "numeric",
@@ -128,8 +132,13 @@
128 132 className:
129 133 "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/20 dark:text-yellow-400",
130 134 label: __("Responded", "yatra"),
131 135 },
136 + completed: {
137 + className:
138 + "bg-teal-100 text-teal-700 dark:bg-teal-900/20 dark:text-teal-400",
139 + label: __("Completed", "yatra"),
140 + },
132 141 converted: {
133 142 className:
134 143 "bg-green-100 text-green-700 dark:bg-green-900/20 dark:text-green-400",
135 144 label: __("Converted", "yatra"),
@@ -297,11 +306,12 @@
297 306 >
298 307 <ArrowLeft className="w-4 h-4" />
299 308 {__("Back", "yatra")}
300 309 </Button>
301 - <ConditionalRender capability="yatra_edit_bookings">
310 + <ConditionalRender capability="yatra_respond_to_enquiries">
302 311 <div className="flex items-center gap-2">
303 312 {enquiry.status !== "responded" &&
313 + enquiry.status !== "completed" &&
304 314 enquiry.status !== "closed" && (
305 315 <Button
306 316 variant="outline"
307 317 onClick={handleRespond}
@@ -323,9 +333,9 @@
323 333 </div>
324 334 }
325 335 />
326 336
327 - <ConditionalRender capability="yatra_view_bookings">
337 + <ConditionalRender capability="yatra_view_enquiries">
328 338 <div className="grid grid-cols-1 lg:grid-cols-3 gap-3">
329 339 {/* Main Content */}
330 340 <div className="lg:col-span-2 space-y-3">
331 341 {/* Enquiry Information */}
@@ -560,14 +570,29 @@
560 570 </p>
561 571 </div>
562 572 </div>
563 573
564 - {/* Response Message */}
574 + {/* Response Message — label + AI sparkle on the same
575 + row so the operator sees the "Generate with AI"
576 + option right where they're about to type. Uses the
577 + same EnquiryReplyAffordance component the admin
578 + EnquiryForm uses, so behavior (Generate / Warmer /
579 + Shorter variants, sensitive-data toggle) is identical
580 + across the two surfaces. */}
565 581 <div>
566 - <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5">
567 - {__("Your Response", "yatra")}{" "}
568 - <span className="text-red-500">*</span>
569 - </label>
582 + <div className="mb-1.5 flex items-center justify-between gap-2">
583 + <label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
584 + {__("Your Response", "yatra")}{" "}
585 + <span className="text-red-500">*</span>
586 + </label>
587 + {enquiryId && (
588 + <EnquiryReplyAffordance
589 + enquiryId={Number(enquiryId)}
590 + value={responseMessage}
591 + onAccept={(text) => setResponseMessage(text)}
592 + />
593 + )}
594 + </div>
570 595 <textarea
571 596 value={responseMessage}
572 597 onChange={(e) => setResponseMessage(e.target.value)}
573 598 placeholder={__("Type your response here...", "yatra")}