PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
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
← All changes | resources/js/pages/ViewCustomer.tsx +14 -14 3.0.2.7 → 3.0.16 View file →
@@ -32,8 +32,9 @@
32 32 CardHeader,
33 33 CardTitle,
34 34 } from "../components/ui/card";
35 35 import { ConditionalRender } from "../components/ui/conditional-render";
36 +import { CustomerSummaryCard } from "../components/ai/CustomerSummaryCard";
36 37 import {
37 38 Table,
38 39 TableBody,
39 40 TableCell,
@@ -41,9 +42,9 @@
41 42 TableHeader,
42 43 TableRow,
43 44 } from "../components/ui/table";
44 45 import { Skeleton } from "../components/ui/skeleton";
45 -import { getCurrencySymbol, getCurrency } from "../data/currencies";
46 +import { formatYatraMoney } from "../lib/currency-display";
46 47
47 48 interface Customer {
48 49 id: number;
49 50 user_id: number;
@@ -132,9 +133,9 @@
132 133 emergency.relationship ??
133 134 null,
134 135 } as Customer;
135 136 },
136 - enabled: !!customerId && can("yatra_view_bookings"),
137 + enabled: !!customerId && can("yatra_view_customers"),
137 138 });
138 139
139 140 // Fetch customer bookings
140 141 const { data: bookings, isLoading: isLoadingBookings } = useQuery<Booking[]>({
@@ -161,19 +162,13 @@
161 162 const formatShortDate = (dateString: string | null | undefined) => {
162 163 return formatDateUtil(dateString);
163 164 };
164 165
165 - const formatPrice = (price: number, currencyCode: string = "USD") => {
166 - const symbol = getCurrencySymbol(currencyCode);
167 - const currencyData = getCurrency(currencyCode);
168 - const decimals = currencyData?.decimalDigits ?? 2;
166 + const formatPrice = (price: number, currencyCode: string = "USD") =>
167 + formatYatraMoney(Number(price) || 0, currencyCode, {
168 + zeroAsUnknown: false,
169 + });
169 170
170 - return `${symbol}${new Intl.NumberFormat(undefined, {
171 - minimumFractionDigits: decimals,
172 - maximumFractionDigits: decimals,
173 - }).format(price)}`;
174 - };
175 -
176 171 const getStatusBadge = (status: string) => {
177 172 const statusMap: Record<string, { className: string; label: string }> = {
178 173 active: {
179 174 className:
@@ -404,9 +399,9 @@
404 399 title={__("Customer Details", "yatra")}
405 400 description={__("View complete customer information", "yatra")}
406 401 actions={
407 402 <div className="flex gap-2">
408 - <ConditionalRender capability="yatra_edit_bookings">
403 + <ConditionalRender capability="yatra_edit_customers">
409 404 <Button onClick={handleEdit} className="flex items-center gap-2">
410 405 <Edit className="w-4 h-4" />
411 406 {__("Edit Customer", "yatra")}
412 407 </Button>
@@ -422,12 +417,17 @@
422 417 </div>
423 418 }
424 419 />
425 420
426 - <ConditionalRender capability="yatra_view_bookings">
421 + <ConditionalRender capability="yatra_view_customers">
427 422 <div className="grid grid-cols-1 lg:grid-cols-3 gap-3">
428 423 {/* Main Content */}
429 424 <div className="lg:col-span-2 space-y-3">
425 + {/* AI Customer Summary — operator-triggered 3-line
426 + snapshot. Self-gates on AI eligibility + module
427 + state, so renders nothing in non-eligible tiers. */}
428 + {customer?.id && <CustomerSummaryCard customerId={customer.id} />}
429 +
430 430 {/* Customer Overview */}
431 431 <Card>
432 432 <CardHeader className="pb-2">
433 433 <div className="flex items-center justify-between">