import { useState } from 'react'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Input } from '@/components/ui/input'; import { Calendar, MapPin, Edit, Trash2, Plus, Minus, Check, X, Star, Factory, Gauge, Ruler, Zap } from 'lucide-react'; import { Link } from 'react-router-dom'; import { format } from 'date-fns'; import { supabase } from '@/lib/supabaseClient'; import { toast } from 'sonner'; import { getErrorMessage } from '@/lib/errorHandler'; import { UserRideCredit } from '@/types/database'; import { convertValueFromMetric, getDisplayUnit } from '@/lib/units'; import { parseDateForDisplay } from '@/lib/dateUtils'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; interface RideCreditCardProps { credit: UserRideCredit; position: number; maxPosition?: number; viewMode: 'grid' | 'list'; isEditMode?: boolean; onUpdate: (creditId: string, updates: Partial) => void; onDelete: () => void; onReorder?: (creditId: string, newPosition: number) => Promise; } export function RideCreditCard({ credit, position, maxPosition, viewMode, isEditMode, onUpdate, onDelete, onReorder }: RideCreditCardProps) { const [isEditing, setIsEditing] = useState(false); const [editCount, setEditCount] = useState(credit.ride_count); const [editPosition, setEditPosition] = useState(position); const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [updating, setUpdating] = useState(false); const rideName = credit.rides?.name || 'Unknown Ride'; const parkName = credit.rides?.parks?.name || 'Unknown Park'; const parkSlug = credit.rides?.parks?.slug || ''; const rideSlug = credit.rides?.slug || ''; const imageUrl = credit.rides?.card_image_url; const category = credit.rides?.category || ''; const location = credit.rides?.parks?.locations; const manufacturer = credit.rides?.manufacturer; const coasterType = credit.rides?.coaster_type; const maxSpeed = credit.rides?.max_speed_kmh; const maxHeight = credit.rides?.max_height_meters; const inversions = credit.rides?.inversions || 0; const intensityLevel = credit.rides?.intensity_level; const handleUpdateCount = async () => { try { setUpdating(true); const { error } = await supabase .from('user_ride_credits') .update({ ride_count: editCount }) .eq('id', credit.id); if (error) throw error; toast.success('Ride count updated'); setIsEditing(false); // Optimistic update - pass specific changes onUpdate(credit.id, { ride_count: editCount }); } catch (error: unknown) { toast.error(getErrorMessage(error)); } finally { setUpdating(false); } }; const handleQuickIncrement = async () => { try { const newCount = credit.ride_count + 1; // Optimistic update first onUpdate(credit.id, { ride_count: newCount }); const { error } = await supabase .from('user_ride_credits') .update({ ride_count: newCount }) .eq('id', credit.id); if (error) throw error; toast.success('Ride count increased'); } catch (error: unknown) { toast.error(getErrorMessage(error)); // Rollback on error onUpdate(credit.id, { ride_count: credit.ride_count }); } }; const handlePositionChange = async () => { if (editPosition === position || !onReorder || !maxPosition) return; if (editPosition < 1 || editPosition > maxPosition) { toast.error(`Position must be between 1 and ${maxPosition}`); setEditPosition(position); return; } try { await onReorder(credit.id, editPosition); toast.success('Position updated'); } catch (error: unknown) { toast.error(getErrorMessage(error)); setEditPosition(position); } }; const getCategoryBadge = (category: string) => { const categoryMap: Record = { roller_coaster: { label: 'Coaster', variant: 'default' }, flat_ride: { label: 'Flat Ride', variant: 'secondary' }, water_ride: { label: 'Water Ride', variant: 'outline' }, dark_ride: { label: 'Dark Ride', variant: 'outline' }, }; const info = categoryMap[category] || { label: category, variant: 'outline' as const }; return {info.label}; }; const ridePath = parkSlug && rideSlug ? `/parks/${parkSlug}/rides/${rideSlug}` : '#'; if (viewMode === 'list') { return (
{imageUrl && ( {rideName} )}
{/* Header Row */}
{rideName} {isEditMode && maxPosition ? (
# setEditPosition(parseInt(e.target.value) || 1)} onBlur={handlePositionChange} onKeyDown={(e) => e.key === 'Enter' && handlePositionChange()} className="w-14 h-6 text-xs p-1" min="1" max={maxPosition} />
) : ( #{position} )} {getCategoryBadge(category)}
{parkName} {location?.city && `, ${location.city}`} {location?.state_province && `, ${location.state_province}`}
{/* Stats Grid - Enhanced */}
{manufacturer && (
{manufacturer.name}
)} {coasterType && (
Type: {coasterType.replace('_', ' ')}
)} {maxSpeed && (
{Math.round(maxSpeed)} km/h
)} {maxHeight && (
{Math.round(maxHeight)} m
)} {inversions > 0 && (
{inversions} inversions
)} {intensityLevel && (
Intensity: {intensityLevel}
)}
{/* Dates */}
{credit.first_ride_date && (
{/* ⚠️ Use parseDateForDisplay to prevent timezone shifts */} First: {format(parseDateForDisplay(credit.first_ride_date), 'MMM d, yyyy')}
)} {credit.last_ride_date && (
Last: {format(parseDateForDisplay(credit.last_ride_date), 'MMM d, yyyy')}
)}
{/* Personal Rating */} {credit.personal_rating && (
{Array.from({ length: 5 }, (_, i) => ( ))} Your Rating
)} {/* Personal Notes Preview */} {credit.personal_notes && (

"{credit.personal_notes}"

)}
{isEditing ? ( <> setEditCount(parseInt(e.target.value) || 1)} className="w-20" min="1" /> ) : ( <>
{credit.ride_count}
{credit.ride_count === 1 ? 'ride' : 'rides'}
)}
Delete Ride Credit? This will remove {rideName} from your ride credits. This action cannot be undone. Cancel Delete
); } // Grid view return ( {imageUrl && (
{rideName}
)}
{rideName}
{isEditMode && maxPosition ? (
# setEditPosition(parseInt(e.target.value) || 1)} onBlur={handlePositionChange} onKeyDown={(e) => e.key === 'Enter' && handlePositionChange()} className="w-14 h-6 text-xs p-1" min="1" max={maxPosition} />
) : ( #{position} )} {getCategoryBadge(category)}
{parkName}
{credit.first_ride_date && (
{format(parseDateForDisplay(credit.first_ride_date), 'MMM d, yyyy')}
)}
{isEditing ? (
setEditCount(parseInt(e.target.value) || 1)} className="w-20" min="1" />
) : ( <>
{credit.ride_count} {credit.ride_count === 1 ? 'ride' : 'rides'}
)}
Delete Ride Credit? This will remove {rideName} from your ride credits. This action cannot be undone. Cancel Delete
); }