import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import * as z from 'zod'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Textarea } from '@/components/ui/textarea'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { DatePicker } from '@/components/ui/date-picker'; import { Star, Send } from 'lucide-react'; import { useAuth } from '@/hooks/useAuth'; import { supabase } from '@/lib/supabaseClient'; import { toast } from '@/hooks/use-toast'; import { PhotoUpload } from '@/components/upload/PhotoUpload'; import { StarRating } from './StarRating'; import { toDateOnly, parseDateOnly } from '@/lib/dateUtils'; import { getErrorMessage, handleNonCriticalError } from '@/lib/errorHandler'; const reviewSchema = z.object({ rating: z.number().min(0.5).max(5).multipleOf(0.5), title: z.string().optional(), content: z.string().min(10, 'Review must be at least 10 characters long'), visit_date: z.string().optional(), wait_time_minutes: z.number().positive().optional(), photos: z.array(z.string()).optional() }); type ReviewFormData = z.infer; interface ReviewFormProps { entityType: 'park' | 'ride'; entityId: string; entityName: string; onReviewSubmitted: () => void; } export function ReviewForm({ entityType, entityId, entityName, onReviewSubmitted }: ReviewFormProps) { const { user } = useAuth(); const [rating, setRating] = useState(0); const [submitting, setSubmitting] = useState(false); const [photos, setPhotos] = useState([]); const { register, handleSubmit, reset, setValue, watch, formState: { errors } } = useForm({ resolver: zodResolver(reviewSchema) }); const handleRatingChange = (selectedRating: number) => { setRating(selectedRating); setValue('rating', selectedRating); }; const onSubmit = async (data: ReviewFormData) => { if (!user) { toast({ title: "Authentication Required", description: "Please sign in to submit a review.", variant: "destructive" }); return; } setSubmitting(true); try { // Insert review first const reviewData = { user_id: user.id, rating: data.rating, title: data.title || null, content: data.content, visit_date: data.visit_date || null, wait_time_minutes: data.wait_time_minutes || null, moderation_status: 'pending' as const, ...(entityType === 'park' ? { park_id: entityId } : { ride_id: entityId }) }; const { data: review, error: reviewError } = await supabase .from('reviews') .insert([reviewData]) .select() .single(); if (reviewError) throw reviewError; // Insert photos into review_photos table if any if (photos.length > 0 && review) { const photoRecords = photos.map((url, index) => ({ review_id: review.id, cloudflare_image_id: url.split('/').slice(-2, -1)[0] || '', // Extract ID from URL cloudflare_image_url: url, order_index: index, })); const { error: photosError } = await supabase .from('review_photos') .insert(photoRecords); if (photosError) { handleNonCriticalError(photosError, { action: 'Insert review photos', userId: user?.id, metadata: { reviewId: review.id, photoCount: photos.length } }); // Don't throw - review is already created } } toast({ title: "Review Submitted!", description: "Thank you for your review. It will be published after moderation." }); reset(); setRating(0); setPhotos([]); onReviewSubmitted(); } catch (error: unknown) { toast({ title: "Error", description: getErrorMessage(error), variant: "destructive" }); } finally { setSubmitting(false); } }; if (!user) { return

Share Your Experience

Sign in to write a review for {entityName}

; } return Write a Review for {entityName}
{/* Rating */}
{errors.rating &&

Please select a rating

}
{/* Title */}
{/* Content */}