'use client'; import React, { useState, useMemo } from 'react'; import { GroupRideSummary, RidePaceFilter, RideSortOption } from './types'; interface GroupRidesDirectoryProps { rides: GroupRideSummary[]; onRequestSlot: (rideId: string) => void; } export function GroupRidesDirectory({ rides, onRequestSlot }: GroupRidesDirectoryProps) { const [filterPace, setFilterPace] = useState('all'); const [searchQuery, setSearchQuery] = useState(''); const [sortOption, setSortOption] = useState('time'); // Count badges for filters const counts = useMemo(() => { return { all: rides.length, brisk: rides.filter((r) => r.paceClassification === 'Brisk Pace').length, track: rides.filter((r) => r.paceClassification === 'Track Pace').length, tour: rides.filter( (r) => r.paceClassification === 'Weekend Tours' || r.paceClassification === 'Endurance / Chill' ).length, }; }, [rides]); // Filtered & sorted rides const filteredRides = useMemo(() => { let result = rides.filter((ride) => { // Pace classification filter if (filterPace === 'brisk' && ride.paceClassification !== 'Brisk Pace') return false; if (filterPace === 'track' && ride.paceClassification !== 'Track Pace') return false; if ( filterPace === 'tour' && ride.paceClassification !== 'Weekend Tours' && ride.paceClassification !== 'Endurance / Chill' ) return false; // Search query filter if (searchQuery.trim()) { const q = searchQuery.toLowerCase(); const matchesTitle = ride.title.toLowerCase().includes(q); const matchesRegion = ride.region.toLowerCase().includes(q); const matchesLead = ride.leadHandle.toLowerCase().includes(q); const matchesDesc = ride.description.toLowerCase().includes(q); if (!matchesTitle && !matchesRegion && !matchesLead && !matchesDesc) return false; } return true; }); // Sorting result.sort((a, b) => { if (sortOption === 'distance') { const distA = parseFloat(a.distance) || 0; const distB = parseFloat(b.distance) || 0; return distB - distA; } if (sortOption === 'riders') { return b.participantCount - a.participantCount; } // default: time / alphabetical id return a.title.localeCompare(b.title); }); return result; }, [rides, filterPace, searchQuery, sortOption]); return (
Regional Operations

Active & Upcoming Pack Runs

{/* Filter Toggles */}
{/* Search and Region Filters Bar */}
search setSearchQuery(e.target.value)} placeholder="Search by region, canyon, or road marshal..." className="w-full bg-transparent font-body-md text-body-md text-on-surface placeholder:text-on-surface-variant outline-none" /> {searchQuery && ( )}
Sort by:
{/* Rides Cards Grid */} {filteredRides.length === 0 ? (
search_off

No Matching Pack Runs

No group rides matched your search for "{searchQuery}". Try adjusting your filters or organize a new group ride.

) : (
{filteredRides.map((ride) => { const isFull = ride.participantCount >= ride.participantLimit; const isJoined = ride.userStatus === 'joined'; const isRequested = ride.userStatus === 'requested'; const isWaitlisted = ride.userStatus === 'waitlisted'; return (
{/* Card Header Banner */}
{/* Atmospheric decorative lines */}
calendar_today {ride.scheduledDate} • {ride.scheduledTime}
{ride.paceClassification}
location_on {ride.region} {ride.distance}
{/* Card Body */}

{ride.title}

{ride.description}

flag Rally: {ride.meetingPoint}
{/* Lead Marshal & Participant Indicator */}
{ride.leadAvatarText}
Lead: {ride.leadHandle}
group {isFull ? `Full (${ride.participantCount}/${ride.participantLimit})` : `${ride.participantCount}/${ride.participantLimit} Riders`}
{/* Action Button */}
{isJoined ? ( ) : isRequested ? ( ) : isWaitlisted ? ( ) : isFull ? ( ) : ( )}
); })}
)}
); }