/** * Review Form Page * Add/Edit Review form with clean, minimal SaaS-style design */ import React, { useState, useEffect, useMemo } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { ArrowLeft, Save, Loader2, Star } from "lucide-react"; import { __ } from "../lib/i18n"; import { usePermissions } from "../hooks/usePermissions"; import { apiClient } from "../lib/api-client"; 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"; interface ReviewFormData { trip_id: string; customer_name: string; customer_email: string; rating: string; title: string; comment: string; status: string; verified: boolean; } const ReviewForm: React.FC = () => { const queryClient = useQueryClient(); const { can } = usePermissions(); const [formData, setFormData] = useState({ trip_id: "", customer_name: "", customer_email: "", rating: "5", title: "", comment: "", status: "pending", verified: false, }); 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 reviewId = useMemo(() => { const params = new URLSearchParams(window.location.search); return params.get("id") ? parseInt(params.get("id") || "0") : null; }, []); const isEditMode = action === "edit" && reviewId !== null; // Fetch trips for dropdown const { data: tripsData } = useQuery({ queryKey: ["trips-list"], queryFn: async () => { const response = await apiClient.get("/trips", { params: { per_page: 100, status: "all", // Get all trips including drafts }, }); // Transform API response const trips = (response?.data || []).map((trip: any) => ({ id: trip.id, title: trip.title || "Untitled Trip", })); return { data: trips }; }, enabled: can("yatra_view_trips"), }); // Fetch review data if editing const { data: reviewData, isLoading: isLoadingReview } = useQuery({ queryKey: ["review", reviewId], queryFn: async () => { if (!reviewId) return null; const response = await apiClient.get(`/reviews/${reviewId}`); // Transform API response - WordPress REST API may wrap response const review = response?.data || response; return { id: review.id, trip_id: review.trip_id, customer_name: review.customer_name || review.author_name || "", customer_email: review.customer_email || review.author_email || "", rating: review.rating, title: review.title || "", comment: review.comment || review.content || "", status: review.status || "pending", verified: review.verified || false, }; }, enabled: isEditMode && can("yatra_view_reviews"), }); // Load review data into form when editing useEffect(() => { if (reviewData && isEditMode) { setFormData({ trip_id: reviewData.trip_id?.toString() || "", customer_name: reviewData.customer_name || "", customer_email: reviewData.customer_email || "", rating: reviewData.rating?.toString() || "5", title: reviewData.title || "", comment: reviewData.comment || "", status: reviewData.status || "pending", verified: reviewData.verified || false, }); } }, [reviewData, isEditMode]); const handleFieldChange = ( field: keyof ReviewFormData, value: string | boolean, ) => { setFormData((prev) => ({ ...prev, [field]: value })); if (errors[field]) { setErrors((prev) => ({ ...prev, [field]: "" })); } }; const renderStarRating = (rating: number) => { return (
{[1, 2, 3, 4, 5].map((star) => ( ))} {rating} {__("stars", "yatra")}
); }; const validateForm = (): boolean => { const newErrors: Record = {}; if (!formData.trip_id) { newErrors.trip_id = __("Trip is required", "yatra"); } if (!formData.customer_name.trim()) { newErrors.customer_name = __("Customer name is required", "yatra"); } if (!formData.customer_email.trim()) { newErrors.customer_email = __("Email is required", "yatra"); } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.customer_email)) { newErrors.customer_email = __("Invalid email address", "yatra"); } if ( !formData.rating || parseInt(formData.rating) < 1 || parseInt(formData.rating) > 5 ) { newErrors.rating = __("Rating must be between 1 and 5", "yatra"); } if (!formData.title.trim()) { newErrors.title = __("Review title is required", "yatra"); } if (!formData.comment.trim()) { newErrors.comment = __("Review comment is required", "yatra"); } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; // Create/Update mutation const saveMutation = useMutation({ mutationFn: async (data: ReviewFormData) => { const payload = { trip_id: parseInt(data.trip_id), customer_name: data.customer_name.trim(), customer_email: data.customer_email.trim(), rating: parseInt(data.rating), title: data.title.trim(), comment: data.comment.trim(), status: data.status, verified: data.verified, }; if (isEditMode && reviewId) { return await apiClient.put(`/reviews/${reviewId}`, payload); } else { return await apiClient.post("/reviews", payload); } }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["reviews"] }); // Redirect to reviews list window.location.href = `${window.yatraAdmin?.siteUrl || ""}/wp-admin/admin.php?page=yatra&subpage=reviews`; }, onError: (error: any) => { const errorMessage = error?.message || __("An error occurred while saving the review", "yatra"); setErrors({ submit: errorMessage }); setIsSubmitting(false); }, }); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!validateForm()) { return; } setIsSubmitting(true); setErrors({}); saveMutation.mutate(formData); }; const handleCancel = () => { window.location.href = `${window.yatraAdmin?.siteUrl || ""}/wp-admin/admin.php?page=yatra&subpage=reviews`; }; if (isEditMode && isLoadingReview) { return (
{__("Loading review...", "yatra")}
); } const trips = tripsData?.data || []; const currentRating = parseInt(formData.rating); return (
{__("Back", "yatra")} } />
{/* Main Form Fields */}
{/* Review Details */} {__("Review Details", "yatra")} {/* Trip Selection */}
{errors.trip_id && (

{errors.trip_id}

)}
{/* Rating */}
{renderStarRating(currentRating)} {errors.rating && (

{errors.rating}

)}
{/* Title */}
handleFieldChange("title", e.target.value) } placeholder={__("Enter review title", "yatra")} className={errors.title ? "border-red-500" : ""} required /> {errors.title && (

{errors.title}

)}
{/* Comment */}