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/PaymentForm.tsx +42 -31 3.0.3 → 3.0.17 View file →
@@ -7,13 +7,16 @@
7 7 import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
8 8 import { ArrowLeft, Save, Loader2, Info } from "lucide-react";
9 9 import { __ } from "../lib/i18n";
10 10 import { apiService } from "../lib/api-client";
11 +import { todayYmd } from "../lib/dateFormat";
11 12 import { usePermissions } from "../hooks/usePermissions";
12 13 import { Button } from "../components/ui/button";
13 14 import { Input } from "../components/ui/input";
14 15 import { Select } from "../components/ui/select";
16 +import { DatePicker } from "../components/ui/date-picker";
15 17 import { PageHeader } from "../components/common/PageHeader";
18 +import { BookingPicker } from "../components/common/BookingPicker";
16 19 import {
17 20 Card,
18 21 CardContent,
19 22 CardHeader,
@@ -26,9 +29,9 @@
26 29 interface PaymentFormData {
27 30 booking_id: string;
28 31 amount: string;
29 32 payment_method: string;
30 - payment_status: "pending" | "completed" | "failed" | "refunded" | "partial";
33 + payment_status: "pending" | "completed" | "failed" | "refunded";
31 34 payment_date: string;
32 35 transaction_id: string;
33 36 notes: string;
34 37 }
@@ -39,10 +42,13 @@
39 42 const [formData, setFormData] = useState<PaymentFormData>({
40 43 booking_id: "",
41 44 amount: "",
42 45 payment_method: "Credit Card",
43 - payment_status: "pending",
44 - payment_date: new Date().toISOString().split("T")[0],
46 + // A payment recorded by hand is money already received. Only completed
47 + // payments count towards the booking's paid amount / balance and trigger
48 + // the "payment received" emails and events, so that is the default.
49 + payment_status: "completed",
50 + payment_date: todayYmd(),
45 51 transaction_id: "",
46 52 notes: "",
47 53 });
48 54 const [errors, setErrors] = useState<Record<string, string>>({});
@@ -81,9 +87,9 @@
81 87 const processedAt = data.processed_at;
82 88 const processedDate =
83 89 typeof processedAt === "string" && processedAt.includes(" ")
84 90 ? processedAt.split(" ")[0]
85 - : new Date().toISOString().split("T")[0];
91 + : todayYmd();
86 92 return {
87 93 id: data.id,
88 94 booking_id: data.booking_id,
89 95 amount: data.amount,
@@ -105,11 +111,9 @@
105 111 amount: String(paymentData.amount ?? ""),
106 112 payment_method: String(paymentData.payment_method || "Credit Card"),
107 113 payment_status: (String(paymentData.payment_status || "pending") ||
108 114 "pending") as PaymentFormData["payment_status"],
109 - payment_date: String(
110 - paymentData.payment_date || new Date().toISOString().split("T")[0],
111 - ),
115 + payment_date: String(paymentData.payment_date || todayYmd()),
112 116 transaction_id: String(paymentData.transaction_id ?? ""),
113 117 notes: String(paymentData.notes ?? ""),
114 118 });
115 119 }
@@ -255,9 +259,9 @@
255 259 {__("Payment Information", "yatra")}
256 260 </CardTitle>
257 261 </CardHeader>
258 262 <CardContent className="space-y-3">
259 - {/* Booking ID */}
263 + {/* Booking */}
260 264 <div>
261 265 <label
262 266 htmlFor="booking_id"
263 267 className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5"
@@ -266,23 +270,23 @@
266 270 <span className="text-red-500">*</span>
267 271 </label>
268 272 <HelpText
269 273 text={__(
270 - "Select the booking this payment is for. You can search by booking number or customer name.",
274 + "Search by booking code (e.g. YTR-123), customer name, or email. Results load from your existing bookings as you type.",
271 275 "yatra",
272 276 )}
273 277 className="mb-2"
274 278 />
275 - <Input
276 - id="booking_id"
277 - type="text"
279 + <BookingPicker
278 280 value={formData.booking_id}
279 - onChange={(e) =>
280 - handleFieldChange("booking_id", e.target.value)
281 - }
282 - placeholder={__("Enter booking ID or search...", "yatra")}
283 - className={errors.booking_id ? "border-red-500" : ""}
284 - required
281 + onChange={(id) => handleFieldChange("booking_id", id)}
282 + error={Boolean(errors.booking_id)}
283 + disabled={isEditMode}
284 + placeholder={__("Select a booking…", "yatra")}
285 + searchPlaceholder={__(
286 + "Search by booking code, name, or email…",
287 + "yatra",
288 + )}
285 289 />
286 290 {errors.booking_id && (
287 291 <p className="mt-1.5 text-sm text-red-600 dark:text-red-400 flex items-center gap-1">
288 292 <Info className="w-4 h-4" />
@@ -288,8 +292,16 @@
288 292 <Info className="w-4 h-4" />
289 293 {errors.booking_id}
290 294 </p>
291 295 )}
296 + {isEditMode && (
297 + <p className="mt-1.5 text-xs text-gray-500 dark:text-gray-400">
298 + {__(
299 + "The booking linked to a payment cannot be changed after creation.",
300 + "yatra",
301 + )}
302 + </p>
303 + )}
292 304 </div>
293 305
294 306 {/* Amount and Payment Method */}
295 307 <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
@@ -399,17 +411,16 @@
399 411 "yatra",
400 412 )}
401 413 className="mb-2"
402 414 />
403 - <Input
404 - id="payment_date"
405 - type="date"
415 + <DatePicker
406 416 value={formData.payment_date}
407 - onChange={(e) =>
408 - handleFieldChange("payment_date", e.target.value)
417 + onChange={(value) =>
418 + handleFieldChange("payment_date", value)
409 419 }
410 - className={errors.payment_date ? "border-red-500" : ""}
411 - required
420 + placeholder={__("Select payment date", "yatra")}
421 + maxDate={new Date()}
422 + error={Boolean(errors.payment_date)}
412 423 />
413 424 {errors.payment_date && (
414 425 <p className="mt-1.5 text-sm text-red-600 dark:text-red-400 flex items-center gap-1">
415 426 <Info className="w-4 h-4" />
@@ -505,24 +516,24 @@
505 516 e.target.value as PaymentFormData["payment_status"],
506 517 )
507 518 }
508 519 >
509 - <option value="pending">{__("Pending", "yatra")}</option>
510 520 <option value="completed">
511 521 {__("Completed", "yatra")}
512 522 </option>
513 - <option value="partial">{__("Partial", "yatra")}</option>
523 + <option value="pending">{__("Pending", "yatra")}</option>
514 524 <option value="failed">{__("Failed", "yatra")}</option>
515 525 <option value="refunded">{__("Refunded", "yatra")}</option>
516 526 </Select>
517 527 <p className="text-xs text-gray-500 dark:text-gray-400">
518 528 {formData.payment_status === "completed" &&
519 - __("Payment has been successfully processed.", "yatra")}
529 + __(
530 + "Money received. Counts towards the booking's paid amount and remaining balance, and sends the payment-received emails (partial or full, depending on what is still due).",
531 + "yatra",
532 + )}
520 533 {formData.payment_status === "pending" &&
521 - __("Payment is pending confirmation.", "yatra")}
522 - {formData.payment_status === "partial" &&
523 534 __(
524 - "Partial payment recorded. Balance may be pending.",
535 + "Expected but not received yet. Does not change the booking's balance and sends nothing — mark it Completed when the money arrives.",
525 536 "yatra",
526 537 )}
527 538 {formData.payment_status === "failed" &&
528 539 __(