/** * Customer Form Page * Add/Edit Customer form with dynamic data from API */ import React, { useState, useEffect, useMemo } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { ArrowLeft, Save, Loader2 } from "lucide-react"; import { __ } from "../lib/i18n"; import { apiService } from "../lib/api-client"; import { useToast } from "../components/ui/toast"; import { Button } from "../components/ui/button"; import { Input } from "../components/ui/input"; import { Select } from "../components/ui/select"; import { PageHeader } from "../components/common/PageHeader"; import { Card, CardContent, CardHeader, CardTitle, } from "../components/ui/card"; import { ConditionalRender } from "../components/ui/conditional-render"; import { Skeleton } from "../components/ui/skeleton"; import { DatePicker } from "../components/ui/date-picker"; interface CustomerFormData { first_name: string; last_name: string; email: string; phone: string; secondary_phone: string; country: string; city: string; state: string; address: string; postal_code: string; nationality: string; date_of_birth: string; gender: string; emergency_name: string; emergency_phone: string; emergency_relationship: string; dietary_requirements: string; medical_conditions: string; special_needs: string; newsletter_optin: boolean; marketing_optin: boolean; status: string; notes: string; loyalty_tier: string; loyalty_points: number; } const CustomerForm: React.FC = () => { const queryClient = useQueryClient(); const { showToast } = useToast(); const baseAdminUrl = (window as any).yatraAdmin?.adminUrl || ""; const [formData, setFormData] = useState({ first_name: "", last_name: "", email: "", phone: "", secondary_phone: "", country: "", city: "", state: "", address: "", postal_code: "", nationality: "", date_of_birth: "", gender: "", emergency_name: "", emergency_phone: "", emergency_relationship: "", dietary_requirements: "", medical_conditions: "", special_needs: "", newsletter_optin: false, marketing_optin: false, status: "active", notes: "", loyalty_tier: "bronze", loyalty_points: 0, }); const [errors, setErrors] = useState>({}); const [isSubmitting, setIsSubmitting] = useState(false); // Get action and id from URL const action = useMemo(() => { const params = new URLSearchParams(window.location.search); return params.get("action") || "create"; }, []); const customerId = useMemo(() => { const params = new URLSearchParams(window.location.search); return params.get("id") ? parseInt(params.get("id") || "0") : null; }, []); const isEditMode = action === "edit" && customerId !== null; // Fetch customer data if editing const { data: customerData, isLoading: isLoadingCustomer } = useQuery< { data?: any } | CustomerFormData | null >({ queryKey: ["customer", customerId], queryFn: async () => { if (!customerId) return null; const response = await apiService.getCustomer(customerId); const data = (response as any)?.data ?? response; const emergency = (data as any).emergency_contact || {}; return { first_name: data.first_name || "", last_name: data.last_name || "", email: data.email || "", phone: data.phone || "", secondary_phone: data.secondary_phone || "", country: data.country || "", city: data.city || "", state: data.state || "", address: data.address || "", postal_code: data.postal_code || "", nationality: data.nationality || "", date_of_birth: data.date_of_birth || "", gender: data.gender || "", emergency_name: (data as any).emergency_name || emergency.name || "", emergency_phone: (data as any).emergency_phone || emergency.phone || "", emergency_relationship: (data as any).emergency_relationship || emergency.relationship || "", dietary_requirements: data.dietary_requirements || "", medical_conditions: data.medical_conditions || "", special_needs: data.special_needs || "", newsletter_optin: Boolean(data.newsletter_optin), marketing_optin: Boolean(data.marketing_optin), status: data.status || "active", notes: data.notes || "", loyalty_tier: data.loyalty_tier || "bronze", loyalty_points: typeof data.loyalty_points === "number" ? data.loyalty_points : parseInt(data.loyalty_points || "0", 10) || 0, } as CustomerFormData; }, enabled: isEditMode, }); // Load customer data into form when editing useEffect(() => { if (customerData && isEditMode) { setFormData(customerData as CustomerFormData); } }, [customerData, isEditMode]); const handleFieldChange = (field: keyof CustomerFormData, value: any) => { setFormData((prev) => ({ ...prev, [field]: value })); if (errors[field]) { setErrors((prev) => ({ ...prev, [field]: "" })); } }; const validateForm = (): boolean => { const newErrors: Record = {}; if (!formData.first_name.trim()) { newErrors.first_name = __("First name is required", "yatra"); } if (!formData.email.trim()) { newErrors.email = __("Email is required", "yatra"); } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { newErrors.email = __("Invalid email address", "yatra"); } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; // Create/Update mutation const saveMutation = useMutation({ mutationFn: async (data: CustomerFormData) => { const payload = { ...data }; if (isEditMode && customerId) { return await apiService.updateCustomer(customerId, payload); } else { // For creation, we need a different approach since CustomerController // doesn't have a create endpoint yet - customers are created during booking // For now, return error for create throw new Error( "Creating customers directly is not supported. Customers are created when bookings are made.", ); } }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["customers"] }); queryClient.invalidateQueries({ queryKey: ["customer", customerId] }); setIsSubmitting(false); showToast(__("Customer updated successfully", "yatra"), "success"); }, onError: (error: any) => { const errorMessage = error?.message || __("An error occurred while saving the customer", "yatra"); setErrors({ submit: errorMessage }); setIsSubmitting(false); showToast(errorMessage, "error"); }, }); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!validateForm()) { return; } setIsSubmitting(true); setErrors({}); saveMutation.mutate(formData); }; const handleCancel = () => { window.location.href = `${baseAdminUrl}?page=yatra&subpage=customers`; }; // Skeleton loader const renderSkeleton = () => (
); if (isEditMode && isLoadingCustomer) { return (
{__("Back", "yatra")} } /> {renderSkeleton()}
); } return (
{__("Back", "yatra")} } />
{/* Main Form Fields */}
{/* Personal Information */} {__("Personal Information", "yatra")}
{/* First Name */}
handleFieldChange("first_name", e.target.value) } placeholder={__("Enter first name", "yatra")} className={errors.first_name ? "border-red-500" : ""} required /> {errors.first_name && (

{errors.first_name}

)}
{/* Last Name */}
handleFieldChange("last_name", e.target.value) } placeholder={__("Enter last name", "yatra")} />
{/* Email */}
handleFieldChange("email", e.target.value) } placeholder={__("customer@example.com", "yatra")} className={errors.email ? "border-red-500" : ""} required disabled={isEditMode} // Can't change email /> {errors.email && (

{errors.email}

)}
{/* Phone Numbers */}
handleFieldChange("phone", e.target.value) } placeholder={__("+1234567890", "yatra")} />
handleFieldChange("secondary_phone", e.target.value) } placeholder={__("+1234567890", "yatra")} />
{/* Date of Birth & Gender */}
handleFieldChange("date_of_birth", value) } placeholder={__("Select date of birth", "yatra")} />
{/* Nationality */}
handleFieldChange("nationality", e.target.value) } placeholder={__("Enter nationality", "yatra")} />
{/* Address Information */} {__("Address Information", "yatra")} {/* Address */}
handleFieldChange("address", e.target.value) } placeholder={__("Enter street address", "yatra")} />
{/* City */}
handleFieldChange("city", e.target.value) } placeholder={__("Enter city", "yatra")} />
{/* State */}
handleFieldChange("state", e.target.value) } placeholder={__("Enter state", "yatra")} />
{/* Country */}
handleFieldChange("country", e.target.value) } placeholder={__("Enter country", "yatra")} />
{/* Postal Code */}
handleFieldChange("postal_code", e.target.value) } placeholder={__("Enter postal code", "yatra")} />
{/* Emergency Contact */} {__("Emergency Contact", "yatra")}
handleFieldChange("emergency_name", e.target.value) } placeholder={__("Full name", "yatra")} />
handleFieldChange("emergency_phone", e.target.value) } placeholder={__("+1234567890", "yatra")} />
handleFieldChange( "emergency_relationship", e.target.value, ) } placeholder={__("e.g., Spouse, Parent", "yatra")} />
{/* Special Requirements */} {__("Special Requirements", "yatra")}
handleFieldChange( "dietary_requirements", e.target.value, ) } placeholder={__("e.g., Vegetarian, Gluten-free", "yatra")} />