'use client'; import { useState, useEffect, useRef, useCallback } from 'react'; import { useRouter } from 'next/navigation'; import { Users, Trophy, Search, Plus, ChevronLeft, ChevronRight, Sparkles, Filter, Flame, Target, Star, TrendingUp, Shield, Zap, Award, Crown, UserPlus, } from 'lucide-react'; import TeamCard from '@/components/teams/TeamCard'; import Button from '@/components/ui/Button'; import Card from '@/components/ui/Card'; import Input from '@/components/ui/Input'; import Heading from '@/components/ui/Heading'; import CreateTeamForm from '@/components/teams/CreateTeamForm'; import { getGetAllTeamsQuery, getGetTeamMembersQuery, getDriverStats } from '@/lib/di-container'; import type { Team } from '@gridpilot/racing'; // ============================================================================ // TYPES // ============================================================================ type CategoryId = | 'all' | 'recruiting' | 'popular' | 'new' | 'pro' | 'advanced' | 'intermediate' | 'beginner' | 'endurance' | 'sprint'; interface Category { id: CategoryId; label: string; icon: React.ElementType; description: string; filter: (team: TeamDisplayData) => boolean; color?: string; } interface TeamDisplayData { id: string; name: string; memberCount: number; rating: number | null; totalWins: number; totalRaces: number; performanceLevel: 'beginner' | 'intermediate' | 'advanced' | 'pro'; isRecruiting: boolean; createdAt: Date; description?: string; specialization?: 'endurance' | 'sprint' | 'mixed'; } // ============================================================================ // DEMO TEAMS DATA // ============================================================================ const DEMO_TEAMS: TeamDisplayData[] = [ // Pro Teams { id: 'demo-team-1', name: 'Apex Predators Racing', description: 'Elite GT3 team competing at the highest level. Multiple championship winners seeking consistent drivers.', memberCount: 8, rating: 4850, totalWins: 47, totalRaces: 156, performanceLevel: 'pro', isRecruiting: true, createdAt: new Date(Date.now() - 180 * 24 * 60 * 60 * 1000), specialization: 'mixed', }, { id: 'demo-team-2', name: 'Velocity Esports', description: 'Professional sim racing team with sponsors. Competing in major endurance events worldwide.', memberCount: 12, rating: 5200, totalWins: 63, totalRaces: 198, performanceLevel: 'pro', isRecruiting: false, createdAt: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000), specialization: 'endurance', }, { id: 'demo-team-3', name: 'Nitro Motorsport', description: 'Championship-winning sprint specialists. Fast, consistent, and always fighting for podiums.', memberCount: 6, rating: 4720, totalWins: 38, totalRaces: 112, performanceLevel: 'pro', isRecruiting: true, createdAt: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000), specialization: 'sprint', }, // Advanced Teams { id: 'demo-team-4', name: 'Horizon Racing Collective', description: 'Ambitious team on the rise. Building towards professional competition with dedicated drivers.', memberCount: 10, rating: 3800, totalWins: 24, totalRaces: 89, performanceLevel: 'advanced', isRecruiting: true, createdAt: new Date(Date.now() - 60 * 24 * 60 * 60 * 1000), specialization: 'mixed', }, { id: 'demo-team-5', name: 'Phoenix Rising eSports', description: 'From the ashes to the podium. A team built on improvement and teamwork.', memberCount: 7, rating: 3650, totalWins: 19, totalRaces: 76, performanceLevel: 'advanced', isRecruiting: true, createdAt: new Date(Date.now() - 45 * 24 * 60 * 60 * 1000), specialization: 'endurance', }, { id: 'demo-team-6', name: 'Thunderbolt Racing', description: 'Fast and furious sprint racing. We live for wheel-to-wheel battles.', memberCount: 5, rating: 3420, totalWins: 15, totalRaces: 54, performanceLevel: 'advanced', isRecruiting: false, createdAt: new Date(Date.now() - 120 * 24 * 60 * 60 * 1000), specialization: 'sprint', }, // Intermediate Teams { id: 'demo-team-7', name: 'Grid Starters', description: 'Growing together as racers. Friendly competition with a focus on learning and fun.', memberCount: 9, rating: 2800, totalWins: 11, totalRaces: 67, performanceLevel: 'intermediate', isRecruiting: true, createdAt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), specialization: 'mixed', }, { id: 'demo-team-8', name: 'Midnight Racers', description: 'Night owls who love endurance racing. Join us for late-night stints and good vibes.', memberCount: 6, rating: 2650, totalWins: 8, totalRaces: 42, performanceLevel: 'intermediate', isRecruiting: true, createdAt: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000), specialization: 'endurance', }, { id: 'demo-team-9', name: 'Casual Speedsters', description: 'Racing for fun, improving together. No pressure, just clean racing.', memberCount: 4, rating: 2400, totalWins: 5, totalRaces: 31, performanceLevel: 'intermediate', isRecruiting: true, createdAt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), specialization: 'sprint', }, // Beginner Teams { id: 'demo-team-10', name: 'Fresh Rubber Racing', description: 'New team for new racers! Learn the basics together in a supportive environment.', memberCount: 3, rating: 1800, totalWins: 2, totalRaces: 18, performanceLevel: 'beginner', isRecruiting: true, createdAt: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000), specialization: 'mixed', }, { id: 'demo-team-11', name: 'Rookie Revolution', description: 'First time racers welcome! We all start somewhere.', memberCount: 5, rating: 1650, totalWins: 1, totalRaces: 12, performanceLevel: 'beginner', isRecruiting: true, createdAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000), specialization: 'sprint', }, { id: 'demo-team-12', name: 'Pit Lane Pioneers', description: 'Learning endurance racing from scratch. Long races, longer friendships.', memberCount: 4, rating: 1500, totalWins: 0, totalRaces: 8, performanceLevel: 'beginner', isRecruiting: true, createdAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000), specialization: 'endurance', }, // Recently Added { id: 'demo-team-13', name: 'Shadow Squadron', description: 'Elite drivers emerging from the shadows. Watch out for us this season.', memberCount: 6, rating: 4100, totalWins: 12, totalRaces: 34, performanceLevel: 'advanced', isRecruiting: true, createdAt: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000), specialization: 'mixed', }, { id: 'demo-team-14', name: 'Turbo Collective', description: 'Fast, furious, and friendly. Sprint racing specialists looking for quick racers.', memberCount: 4, rating: 3200, totalWins: 7, totalRaces: 28, performanceLevel: 'intermediate', isRecruiting: true, createdAt: new Date(Date.now() - 12 * 60 * 60 * 1000), specialization: 'sprint', }, ]; // ============================================================================ // CATEGORIES // ============================================================================ const CATEGORIES: Category[] = [ { id: 'all', label: 'All', icon: Users, description: 'Browse all teams', filter: () => true, }, { id: 'recruiting', label: 'Recruiting', icon: UserPlus, description: 'Teams looking for drivers', filter: (team) => team.isRecruiting, color: 'text-performance-green', }, { id: 'popular', label: 'Popular', icon: Flame, description: 'Most active teams', filter: (team) => team.totalRaces >= 50, color: 'text-orange-400', }, { id: 'new', label: 'New', icon: Sparkles, description: 'Recently formed teams', filter: (team) => { const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); return team.createdAt > oneWeekAgo; }, color: 'text-neon-aqua', }, { id: 'pro', label: 'Pro', icon: Crown, description: 'Professional-level teams', filter: (team) => team.performanceLevel === 'pro', color: 'text-yellow-400', }, { id: 'advanced', label: 'Advanced', icon: Star, description: 'High-skill teams', filter: (team) => team.performanceLevel === 'advanced', color: 'text-purple-400', }, { id: 'intermediate', label: 'Intermediate', icon: TrendingUp, description: 'Growing teams', filter: (team) => team.performanceLevel === 'intermediate', color: 'text-primary-blue', }, { id: 'beginner', label: 'Beginner', icon: Shield, description: 'New racer friendly', filter: (team) => team.performanceLevel === 'beginner', color: 'text-green-400', }, { id: 'endurance', label: 'Endurance', icon: Trophy, description: 'Long-distance specialists', filter: (team) => team.specialization === 'endurance', }, { id: 'sprint', label: 'Sprint', icon: Zap, description: 'Short race experts', filter: (team) => team.specialization === 'sprint', }, ]; // ============================================================================ // TEAM SLIDER COMPONENT // ============================================================================ interface TeamSliderProps { title: string; icon: React.ElementType; description: string; teams: TeamDisplayData[]; onTeamClick: (id: string) => void; autoScroll?: boolean; iconColor?: string; scrollSpeedMultiplier?: number; scrollDirection?: 'left' | 'right'; } function TeamSlider({ title, icon: Icon, description, teams, onTeamClick, autoScroll = true, iconColor = 'text-primary-blue', scrollSpeedMultiplier = 1, scrollDirection = 'right', }: TeamSliderProps) { const scrollRef = useRef(null); const [canScrollLeft, setCanScrollLeft] = useState(false); const [canScrollRight, setCanScrollRight] = useState(true); const [isHovering, setIsHovering] = useState(false); const animationRef = useRef(null); const scrollPositionRef = useRef(0); const checkScrollButtons = useCallback(() => { if (scrollRef.current) { const { scrollLeft, scrollWidth, clientWidth } = scrollRef.current; setCanScrollLeft(scrollLeft > 0); setCanScrollRight(scrollLeft < scrollWidth - clientWidth - 10); } }, []); const scroll = useCallback((direction: 'left' | 'right') => { if (scrollRef.current) { const cardWidth = 340; const scrollAmount = direction === 'left' ? -cardWidth : cardWidth; scrollPositionRef.current = scrollRef.current.scrollLeft + scrollAmount; scrollRef.current.scrollBy({ left: scrollAmount, behavior: 'smooth' }); } }, []); // Initialize scroll position for left-scrolling sliders useEffect(() => { if (scrollDirection === 'left' && scrollRef.current) { const { scrollWidth, clientWidth } = scrollRef.current; scrollPositionRef.current = scrollWidth - clientWidth; scrollRef.current.scrollLeft = scrollPositionRef.current; } }, [scrollDirection, teams.length]); // Smooth continuous auto-scroll useEffect(() => { if (!autoScroll || teams.length <= 1) return; const scrollContainer = scrollRef.current; if (!scrollContainer) return; let lastTimestamp = 0; const baseSpeed = 0.025; const scrollSpeed = baseSpeed * scrollSpeedMultiplier; const directionMultiplier = scrollDirection === 'left' ? -1 : 1; const animate = (timestamp: number) => { if (!isHovering && scrollContainer) { const delta = lastTimestamp ? timestamp - lastTimestamp : 0; lastTimestamp = timestamp; scrollPositionRef.current += scrollSpeed * delta * directionMultiplier; const { scrollWidth, clientWidth } = scrollContainer; const maxScroll = scrollWidth - clientWidth; if (scrollDirection === 'right' && scrollPositionRef.current >= maxScroll) { scrollPositionRef.current = 0; } else if (scrollDirection === 'left' && scrollPositionRef.current <= 0) { scrollPositionRef.current = maxScroll; } scrollContainer.scrollLeft = scrollPositionRef.current; } else { lastTimestamp = timestamp; } animationRef.current = requestAnimationFrame(animate); }; animationRef.current = requestAnimationFrame(animate); return () => { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } }; }, [autoScroll, teams.length, isHovering, scrollSpeedMultiplier, scrollDirection]); useEffect(() => { const scrollContainer = scrollRef.current; if (!scrollContainer) return; const handleScroll = () => { scrollPositionRef.current = scrollContainer.scrollLeft; checkScrollButtons(); }; scrollContainer.addEventListener('scroll', handleScroll); return () => scrollContainer.removeEventListener('scroll', handleScroll); }, [checkScrollButtons]); if (teams.length === 0) return null; return (

{title}

{description}

{teams.length}
setIsHovering(true)} onMouseLeave={() => setIsHovering(false)} className="flex gap-4 overflow-x-auto pb-4 px-4" style={{ scrollbarWidth: 'none', msOverflowStyle: 'none', }} > {teams.map((team) => (
onTeamClick(team.id)} />
))}
); } // ============================================================================ // MAIN PAGE COMPONENT // ============================================================================ export default function TeamsPage() { const router = useRouter(); const [realTeams, setRealTeams] = useState([]); const [loading, setLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(''); const [activeCategory, setActiveCategory] = useState('all'); const [showFilters, setShowFilters] = useState(false); const [showCreateForm, setShowCreateForm] = useState(false); useEffect(() => { loadTeams(); }, []); const loadTeams = async () => { try { const allTeamsQuery = getGetAllTeamsQuery(); const teamMembersQuery = getGetTeamMembersQuery(); const allTeams = await allTeamsQuery.execute(); const teamData: TeamDisplayData[] = []; await Promise.all( allTeams.map(async (team: Team) => { const memberships = await teamMembersQuery.execute({ teamId: team.id }); const memberCount = memberships.length; let ratingSum = 0; let ratingCount = 0; let totalWins = 0; let totalRaces = 0; for (const membership of memberships) { const stats = getDriverStats(membership.driverId); if (!stats) continue; if (typeof stats.rating === 'number') { ratingSum += stats.rating; ratingCount += 1; } totalWins += stats.wins ?? 0; totalRaces += stats.totalRaces ?? 0; } const averageRating = ratingCount > 0 ? ratingSum / ratingCount : null; let performanceLevel: TeamDisplayData['performanceLevel'] = 'beginner'; if (averageRating !== null) { if (averageRating >= 4500) performanceLevel = 'pro'; else if (averageRating >= 3000) performanceLevel = 'advanced'; else if (averageRating >= 2000) performanceLevel = 'intermediate'; } teamData.push({ id: team.id, name: team.name, memberCount, rating: averageRating, totalWins, totalRaces, performanceLevel, isRecruiting: true, // Default for now createdAt: new Date(), // Would need to be stored in Team entity }); }), ); setRealTeams(teamData); } catch (error) { console.error('Failed to load teams:', error); } finally { setLoading(false); } }; // Combine real teams with demo teams const teams = [...realTeams, ...DEMO_TEAMS]; const handleTeamClick = (teamId: string) => { if (teamId.startsWith('demo-team-')) { return; } router.push(`/teams/${teamId}`); }; const handleCreateSuccess = (teamId: string) => { setShowCreateForm(false); void loadTeams(); router.push(`/teams/${teamId}`); }; // Filter by search query const searchFilteredTeams = teams.filter((team) => { if (!searchQuery) return true; const query = searchQuery.toLowerCase(); return ( team.name.toLowerCase().includes(query) || (team.description ?? '').toLowerCase().includes(query) ); }); // Get teams for active category const activeCategoryData = CATEGORIES.find((c) => c.id === activeCategory); const categoryFilteredTeams = activeCategoryData ? searchFilteredTeams.filter(activeCategoryData.filter) : searchFilteredTeams; // Group teams by category for slider view const teamsByCategory = CATEGORIES.reduce( (acc, category) => { acc[category.id] = searchFilteredTeams.filter(category.filter); return acc; }, {} as Record, ); // Featured categories with different scroll speeds and directions const featuredCategoriesWithSpeed: { id: CategoryId; speed: number; direction: 'left' | 'right' }[] = [ { id: 'recruiting', speed: 1.0, direction: 'right' }, { id: 'pro', speed: 0.8, direction: 'left' }, { id: 'advanced', speed: 1.1, direction: 'right' }, { id: 'intermediate', speed: 0.9, direction: 'left' }, { id: 'beginner', speed: 1.2, direction: 'right' }, { id: 'new', speed: 1.0, direction: 'left' }, ]; if (showCreateForm) { return (

Create New Team

setShowCreateForm(false)} onSuccess={handleCreateSuccess} />
); } if (loading) { return (

Loading teams...

); } return (
{/* Hero Section */}
Join a Team

Racing is better together. Find your crew, share strategies, and compete in endurance events as a team.

{/* Stats */}
{teams.length} active teams
{teamsByCategory.recruiting.length} recruiting
{teamsByCategory.pro.length} pro teams
{/* CTA */}

Start your own racing team

{/* Search and Filter Bar */}
setSearchQuery(e.target.value)} className="pl-11" />
{/* Category Tabs */}
{CATEGORIES.map((category) => { const Icon = category.icon; const count = teamsByCategory[category.id].length; const isActive = activeCategory === category.id; return ( ); })}
{/* Content */} {teams.length === 0 ? (
No teams yet

Be the first to create a racing team. Gather drivers and compete together in endurance events.

) : activeCategory === 'all' && !searchQuery ? ( /* Slider View */
{featuredCategoriesWithSpeed .map(({ id, speed, direction }) => { const category = CATEGORIES.find((c) => c.id === id)!; return { category, speed, direction }; }) .filter(({ category }) => teamsByCategory[category.id].length > 0) .map(({ category, speed, direction }) => ( ))}
) : ( /* Grid View */
{categoryFilteredTeams.length > 0 ? ( <>

Showing {categoryFilteredTeams.length}{' '} {categoryFilteredTeams.length === 1 ? 'team' : 'teams'} {searchQuery && ( {' '} for "{searchQuery}" )}

{categoryFilteredTeams.map((team) => ( handleTeamClick(team.id)} /> ))}
) : (

No teams found{searchQuery ? ` matching "${searchQuery}"` : ' in this category'}

)}
)}
); }