'use client'; import { Calendar, Award, UserPlus, UserMinus, Shield, Flag, AlertTriangle } from 'lucide-react'; import { Race, Penalty } from '@core/racing'; import type { LeagueMembership } from '@core/racing/domain/entities/LeagueMembership'; import { useEffect, useState } from 'react'; import type { Driver } from '@core/racing'; export type LeagueActivity = | { type: 'race_completed'; raceId: string; raceName: string; timestamp: Date } | { type: 'race_scheduled'; raceId: string; raceName: string; timestamp: Date } | { type: 'penalty_applied'; penaltyId: string; driverName: string; reason: string; points: number; timestamp: Date } | { type: 'member_joined'; driverId: string; driverName: string; timestamp: Date } | { type: 'member_left'; driverId: string; driverName: string; timestamp: Date } | { type: 'role_changed'; driverId: string; driverName: string; oldRole: string; newRole: string; timestamp: Date }; interface LeagueActivityFeedProps { leagueId: string; limit?: number; } function timeAgo(timestamp: Date): string { const diffMs = Date.now() - timestamp.getTime(); const diffMinutes = Math.floor(diffMs / 60000); if (diffMinutes < 1) return 'Just now'; if (diffMinutes < 60) return `${diffMinutes} min ago`; const diffHours = Math.floor(diffMinutes / 60); if (diffHours < 24) return `${diffHours}h ago`; const diffDays = Math.floor(diffHours / 24); if (diffDays === 1) return 'Yesterday'; if (diffDays < 7) return `${diffDays}d ago`; return timestamp.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } export default function LeagueActivityFeed({ leagueId, limit = 10 }: LeagueActivityFeedProps) { const [activities, setActivities] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { async function loadActivities() { try { const raceRepo = getRaceRepository(); const penaltyRepo = getPenaltyRepository(); const driverRepo = getDriverRepository(); const races = await raceRepo.findByLeagueId(leagueId); const drivers = await driverRepo.findAll(); const driversMap = new Map(drivers.map(d => [d.id, d])); const activityList: LeagueActivity[] = []; // Add completed races const completedRaces = races.filter(r => r.status === 'completed') .sort((a, b) => b.scheduledAt.getTime() - a.scheduledAt.getTime()) .slice(0, 5); for (const race of completedRaces) { activityList.push({ type: 'race_completed', raceId: race.id, raceName: `${race.track} - ${race.car}`, timestamp: race.scheduledAt, }); // Add penalties from this race const racePenalties = await penaltyRepo.findByRaceId(race.id); const appliedPenalties = racePenalties.filter(p => p.status === 'applied' && p.type === 'points_deduction'); for (const penalty of appliedPenalties) { const driver = driversMap.get(penalty.driverId); if (driver && penalty.value) { activityList.push({ type: 'penalty_applied', penaltyId: penalty.id, driverName: driver.name, reason: penalty.reason, points: penalty.value, timestamp: penalty.appliedAt || penalty.issuedAt, }); } } } // Add scheduled races const upcomingRaces = races.filter(r => r.status === 'scheduled') .sort((a, b) => b.scheduledAt.getTime() - a.scheduledAt.getTime()) .slice(0, 3); for (const race of upcomingRaces) { activityList.push({ type: 'race_scheduled', raceId: race.id, raceName: `${race.track} - ${race.car}`, timestamp: new Date(race.scheduledAt.getTime() - 7 * 24 * 60 * 60 * 1000), // Simulate schedule announcement }); } // Sort all activities by timestamp activityList.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); setActivities(activityList.slice(0, limit)); } catch (err) { console.error('Failed to load activities:', err); } finally { setLoading(false); } } loadActivities(); }, [leagueId, limit]); if (loading) { return (
Loading activities...
); } if (activities.length === 0) { return (
No recent activity
); } return (
{activities.map((activity, index) => ( ))}
); } function ActivityItem({ activity }: { activity: LeagueActivity }) { const getIcon = () => { switch (activity.type) { case 'race_completed': return ; case 'race_scheduled': return ; case 'penalty_applied': return ; case 'member_joined': return ; case 'member_left': return ; case 'role_changed': return ; } }; const getContent = () => { switch (activity.type) { case 'race_completed': return ( <> Race Completed · {activity.raceName} ); case 'race_scheduled': return ( <> Race Scheduled · {activity.raceName} ); case 'penalty_applied': return ( <> {activity.driverName} received a {activity.points}-point penalty · {activity.reason} ); case 'member_joined': return ( <> {activity.driverName} joined the league ); case 'member_left': return ( <> {activity.driverName} left the league ); case 'role_changed': return ( <> {activity.driverName} promoted to {activity.newRole} ); } }; return (
{getIcon()}

{getContent()}

{timeAgo(activity.timestamp)}

); }