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/Dashboard.tsx +26 -8 3.0.8 → 3.0.17 View file →
@@ -39,8 +39,9 @@
39 39 Plus,
40 40 X,
41 41 } from "lucide-react";
42 42 import { __ } from "../lib/i18n";
43 +import { formatDateForInput, toDateValue } from "../lib/dateFormat";
43 44 import { usePermissions } from "../hooks/usePermissions";
44 45 import { TodaysBriefCard } from "../components/ai/TodaysBriefCard";
45 46 import { StatCard } from "../components/common/StatCard";
46 47 import {
@@ -162,11 +163,10 @@
162 163 // below the hook calls; every useQuery now reads `enabled: isWpAdmin`
163 164 // so non-admin renders don't fire the admin-only endpoints, and the
164 165 // RoleDashboard branch returns immediately after all hooks are
165 166 // registered.
166 - const isWpAdmin = !!(
167 - window.yatraAdmin as { isWpAdmin?: boolean } | undefined
168 - )?.isWpAdmin;
167 + const isWpAdmin = !!(window.yatraAdmin as { isWpAdmin?: boolean } | undefined)
168 + ?.isWpAdmin;
169 169
170 170 const { can } = usePermissions();
171 171
172 172 const [range, setRange] = useState<DashboardRange>("all_time");
@@ -283,9 +283,11 @@
283 283 const amounts: Record<string, number> = {};
284 284
285 285 items.forEach((b) => {
286 286 const dateStr = b.created_at || b.travel_date;
287 - const date = dateStr ? new Date(dateStr) : null;
287 + // toDateValue: a date-only travel_date fallback (e.g. "2026-08-01") must
288 + // bucket in its LOCAL month, not roll back to July in a behind-UTC zone.
289 + const date = dateStr ? toDateValue(dateStr) : null;
288 290 if (!date || Number.isNaN(date.getTime())) return;
289 291 const ym = `${date.getFullYear()}-${pad(date.getMonth() + 1)}`;
290 292 counts[ym] = (counts[ym] || 0) + 1;
291 293 const amount = Number(b.total_amount ?? 0) || 0;
@@ -392,17 +394,25 @@
392 394 const { data: departures } = useQuery({
393 395 queryKey: ["dashboard-upcoming-departures"],
394 396 queryFn: async () => {
395 397 const today = new Date();
396 - const todayStr = today.toISOString().split("T")[0];
398 + const todayStr = formatDateForInput(today);
399 + // Fetch all FUTURE departures, not only status === "upcoming". A departure
400 + // that a booking has filled flips to status "full" (booked >= capacity),
401 + // and a single group-of-x booking fills a small/private departure in one
402 + // go — so filtering to "upcoming" here silently dropped fully-booked
403 + // upcoming tours from the dashboard while partially-booked ones stayed. We
404 + // keep upcoming + full (a full trip is still an upcoming trip, just sold
405 + // out) and exclude only cancelled/trash/past.
397 406 const response = await apiClient.get("/departures", {
398 407 params: {
399 - status: "upcoming",
400 408 date_from: todayStr,
401 409 include_past: false,
402 410 },
403 411 });
404 - const items = response?.data || [];
412 + const items = (response?.data || []).filter(
413 + (d: any) => d?.status === "upcoming" || d?.status === "full",
414 + );
405 415 return items.map((d: any) => {
406 416 const tripTitle = d?.trip?.title || d?.trip_title || d?.title || "";
407 417 const destination =
408 418 (d?.trip?.destinations && d.trip.destinations[0]?.name) ||
@@ -407,9 +417,16 @@
407 417 const destination =
408 418 (d?.trip?.destinations && d.trip.destinations[0]?.name) ||
409 419 d?.destination ||
410 420 undefined;
421 + // `/departures` returns max_capacity / available_capacity. Neither was
422 + // listed here, so every card fell back to 0 capacity — which made
423 + // available go negative (0 - bookings) and pinned occupancy at 0%, even
424 + // though the trip page computed it correctly from the same rows. The
425 + // API's own names come first; the rest remain as fallbacks for any other
426 + // shape that reaches this mapper.
411 427 const totalSpots =
428 + d?.max_capacity ??
412 429 d?.total_spots ??
413 430 d?.capacity ??
414 431 d?.total_seats ??
415 432 d?.max_travelers ??
@@ -414,12 +431,13 @@
414 431 d?.total_seats ??
415 432 d?.max_travelers ??
416 433 0;
417 434 const availableSpots =
435 + d?.available_capacity ??
418 436 d?.available_spots ??
419 437 d?.available_seats ??
420 438 d?.remaining_slots ??
421 - totalSpots - (d?.bookings_count || 0);
439 + totalSpots - (d?.booked_count ?? d?.bookings_count ?? 0);
422 440 return {
423 441 id: d.id,
424 442 trip_id: d.trip_id || d?.trip?.id,
425 443 trip_title: tripTitle,