website refactor

This commit is contained in:
2026-01-15 17:12:24 +01:00
parent c3b308e960
commit f035cfe7ce
468 changed files with 24378 additions and 17324 deletions

View File

@@ -1,7 +1,11 @@
'use client';
import { Calendar, Award, UserPlus, UserMinus, Shield, Flag, AlertTriangle } from 'lucide-react';
import { useLeagueRaces } from "@/lib/hooks/league/useLeagueRaces";
import React, { useMemo } from 'react';
import { Calendar, UserPlus, UserMinus, Shield, Flag, AlertTriangle } from 'lucide-react';
import { useLeagueRaces } from "@/hooks/league/useLeagueRaces";
import { ActivityFeedItem } from '@/ui/ActivityFeedItem';
import { Icon } from '@/ui/Icon';
import { Text } from '@/ui/Text';
import { Stack } from '@/ui/Stack';
import { processLeagueActivities } from '@/lib/services/league/LeagueActivityService';
export type LeagueActivity =
| { type: 'race_completed'; raceId: string; raceName: string; timestamp: Date }
@@ -29,67 +33,36 @@ function timeAgo(timestamp: Date): string {
return timestamp.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
}
export default function LeagueActivityFeed({ leagueId, limit = 10 }: LeagueActivityFeedProps) {
export function LeagueActivityFeed({ leagueId, limit = 10 }: LeagueActivityFeedProps) {
const { data: raceList = [], isLoading } = useLeagueRaces(leagueId);
const activities: LeagueActivity[] = [];
if (!isLoading && raceList.length > 0) {
const completedRaces = raceList
.filter((r) => r.status === 'completed')
.sort((a, b) => new Date(b.scheduledAt).getTime() - new Date(a.scheduledAt).getTime())
.slice(0, 5);
const upcomingRaces = raceList
.filter((r) => r.status === 'scheduled')
.sort((a, b) => new Date(b.scheduledAt).getTime() - new Date(a.scheduledAt).getTime())
.slice(0, 3);
for (const race of completedRaces) {
activities.push({
type: 'race_completed',
raceId: race.id,
raceName: `${race.track} - ${race.car}`,
timestamp: new Date(race.scheduledAt),
});
}
for (const race of upcomingRaces) {
activities.push({
type: 'race_scheduled',
raceId: race.id,
raceName: `${race.track} - ${race.car}`,
timestamp: new Date(new Date(race.scheduledAt).getTime() - 7 * 24 * 60 * 60 * 1000), // Simulate schedule announcement
});
}
// Sort all activities by timestamp
activities.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
activities.splice(limit); // Limit results
}
const activities = useMemo(() => {
if (isLoading || raceList.length === 0) return [];
return processLeagueActivities(raceList, limit);
}, [raceList, isLoading, limit]);
if (isLoading) {
return (
<div className="text-center text-gray-400 py-8">
<Text color="text-gray-400" textAlign="center" block py={8}>
Loading activities...
</div>
</Text>
);
}
if (activities.length === 0) {
return (
<div className="text-center text-gray-400 py-8">
<Text color="text-gray-400" textAlign="center" block py={8}>
No recent activity
</div>
</Text>
);
}
return (
<div className="space-y-4">
<Stack gap={0}>
{activities.map((activity, index) => (
<ActivityItem key={`${activity.type}-${index}`} activity={activity} />
))}
</div>
</Stack>
);
}
@@ -97,17 +70,17 @@ function ActivityItem({ activity }: { activity: LeagueActivity }) {
const getIcon = () => {
switch (activity.type) {
case 'race_completed':
return <Flag className="w-4 h-4 text-performance-green" />;
return <Icon icon={Flag} size={4} color="var(--performance-green)" />;
case 'race_scheduled':
return <Calendar className="w-4 h-4 text-primary-blue" />;
return <Icon icon={Calendar} size={4} color="var(--primary-blue)" />;
case 'penalty_applied':
return <AlertTriangle className="w-4 h-4 text-warning-amber" />;
return <Icon icon={AlertTriangle} size={4} color="var(--warning-amber)" />;
case 'member_joined':
return <UserPlus className="w-4 h-4 text-performance-green" />;
return <Icon icon={UserPlus} size={4} color="var(--performance-green)" />;
case 'member_left':
return <UserMinus className="w-4 h-4 text-gray-400" />;
return <Icon icon={UserMinus} size={4} color="var(--text-gray-400)" />;
case 'role_changed':
return <Shield className="w-4 h-4 text-primary-blue" />;
return <Icon icon={Shield} size={4} color="var(--primary-blue)" />;
}
};
@@ -116,64 +89,56 @@ function ActivityItem({ activity }: { activity: LeagueActivity }) {
case 'race_completed':
return (
<>
<span className="text-white font-medium">Race Completed</span>
<span className="text-gray-400"> · {activity.raceName}</span>
<Text weight="medium" color="text-white">Race Completed</Text>
<Text color="text-gray-400"> · {activity.raceName}</Text>
</>
);
case 'race_scheduled':
return (
<>
<span className="text-white font-medium">Race Scheduled</span>
<span className="text-gray-400"> · {activity.raceName}</span>
<Text weight="medium" color="text-white">Race Scheduled</Text>
<Text color="text-gray-400"> · {activity.raceName}</Text>
</>
);
case 'penalty_applied':
return (
<>
<span className="text-white font-medium">{activity.driverName}</span>
<span className="text-gray-400"> received a </span>
<span className="text-warning-amber">{activity.points}-point penalty</span>
<span className="text-gray-400"> · {activity.reason}</span>
<Text weight="medium" color="text-white">{activity.driverName}</Text>
<Text color="text-gray-400"> received a </Text>
<Text color="text-warning-amber">{activity.points}-point penalty</Text>
<Text color="text-gray-400"> · {activity.reason}</Text>
</>
);
case 'member_joined':
return (
<>
<span className="text-white font-medium">{activity.driverName}</span>
<span className="text-gray-400"> joined the league</span>
<Text weight="medium" color="text-white">{activity.driverName}</Text>
<Text color="text-gray-400"> joined the league</Text>
</>
);
case 'member_left':
return (
<>
<span className="text-white font-medium">{activity.driverName}</span>
<span className="text-gray-400"> left the league</span>
<Text weight="medium" color="text-white">{activity.driverName}</Text>
<Text color="text-gray-400"> left the league</Text>
</>
);
case 'role_changed':
return (
<>
<span className="text-white font-medium">{activity.driverName}</span>
<span className="text-gray-400"> promoted to </span>
<span className="text-primary-blue">{activity.newRole}</span>
<Text weight="medium" color="text-white">{activity.driverName}</Text>
<Text color="text-gray-400"> promoted to </Text>
<Text color="text-primary-blue">{activity.newRole}</Text>
</>
);
}
};
return (
<div className="flex items-start gap-3 py-3 border-b border-charcoal-outline/30 last:border-0">
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-iron-gray/50 flex items-center justify-center">
{getIcon()}
</div>
<div className="flex-1 min-w-0">
<p className="text-sm leading-relaxed">
{getContent()}
</p>
<p className="text-xs text-gray-500 mt-1">
{timeAgo(activity.timestamp)}
</p>
</div>
</div>
<ActivityFeedItem
icon={getIcon()}
content={getContent()}
timestamp={timeAgo(activity.timestamp)}
/>
);
}