wip
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import Image from 'next/image';
|
||||
import Card from '@/components/ui/Card';
|
||||
import RankBadge from '@/components/drivers/RankBadge';
|
||||
import { getDriverAvatarUrl } from '@/lib/racingLegacyFacade';
|
||||
|
||||
export interface DriverCardProps {
|
||||
id: string;
|
||||
@@ -16,6 +18,7 @@ export interface DriverCardProps {
|
||||
|
||||
export default function DriverCard(props: DriverCardProps) {
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
rating,
|
||||
nationality,
|
||||
@@ -35,8 +38,14 @@ export default function DriverCard(props: DriverCardProps) {
|
||||
<div className="flex items-center gap-4 flex-1">
|
||||
<RankBadge rank={rank} size="lg" />
|
||||
|
||||
<div className="w-16 h-16 rounded-full bg-primary-blue/20 flex items-center justify-center text-2xl font-bold text-white">
|
||||
{name.charAt(0)}
|
||||
<div className="w-16 h-16 rounded-full bg-primary-blue/20 overflow-hidden flex items-center justify-center">
|
||||
<Image
|
||||
src={getDriverAvatarUrl(id)}
|
||||
alt={name}
|
||||
width={64}
|
||||
height={64}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex-1">
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { League } from '@gridpilot/racing/domain/entities/League';
|
||||
import Card from '../ui/Card';
|
||||
import { getLeagueCoverClasses } from '@/lib/leagueCovers';
|
||||
|
||||
interface LeagueCardProps {
|
||||
league: League;
|
||||
@@ -15,27 +17,35 @@ export default function LeagueCard({ league, onClick }: LeagueCardProps) {
|
||||
onClick={onClick}
|
||||
>
|
||||
<Card>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start justify-between">
|
||||
<h3 className="text-xl font-semibold text-white">{league.name}</h3>
|
||||
<span className="text-xs text-gray-500">
|
||||
{new Date(league.createdAt).toLocaleDateString()}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p className="text-gray-400 text-sm line-clamp-2">
|
||||
{league.description}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-between pt-2 border-t border-charcoal-outline">
|
||||
<div className="text-xs text-gray-500">
|
||||
Owner ID: {league.ownerId.slice(0, 8)}...
|
||||
<div className="space-y-3">
|
||||
<div className={getLeagueCoverClasses(league.id)} aria-hidden="true" />
|
||||
|
||||
<div className="flex items-start justify-between">
|
||||
<h3 className="text-xl font-semibold text-white">{league.name}</h3>
|
||||
<span className="text-xs text-gray-500">
|
||||
{new Date(league.createdAt).toLocaleDateString()}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-primary-blue font-medium">
|
||||
{league.settings.pointsSystem.toUpperCase()}
|
||||
|
||||
<p className="text-gray-400 text-sm line-clamp-2">
|
||||
{league.description}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-between pt-2 border-t border-charcoal-outline">
|
||||
<div className="text-xs text-gray-500">
|
||||
Owner:{' '}
|
||||
<Link
|
||||
href={`/drivers/${league.ownerId}?from=league&leagueId=${league.id}`}
|
||||
className="text-primary-blue hover:underline"
|
||||
>
|
||||
{league.ownerId.slice(0, 8)}...
|
||||
</Link>
|
||||
</div>
|
||||
<div className="text-xs text-primary-blue font-medium">
|
||||
{league.settings.pointsSystem.toUpperCase()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
70
apps/website/components/leagues/LeagueHeader.tsx
Normal file
70
apps/website/components/leagues/LeagueHeader.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import MembershipStatus from '@/components/leagues/MembershipStatus';
|
||||
import FeatureLimitationTooltip from '@/components/alpha/FeatureLimitationTooltip';
|
||||
import { getLeagueCoverClasses } from '@/lib/leagueCovers';
|
||||
|
||||
interface LeagueHeaderProps {
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
description?: string | null;
|
||||
ownerId: string;
|
||||
ownerName: string;
|
||||
}
|
||||
|
||||
export default function LeagueHeader({
|
||||
leagueId,
|
||||
leagueName,
|
||||
description,
|
||||
ownerId,
|
||||
ownerName,
|
||||
}: LeagueHeaderProps) {
|
||||
const coverUrl = `https://picsum.photos/seed/${leagueId}/1200/280?blur=2`;
|
||||
|
||||
return (
|
||||
<div className="mb-8">
|
||||
<div className="mb-4">
|
||||
<div className={getLeagueCoverClasses(leagueId)} aria-hidden="true">
|
||||
<div className="relative w-full h-full">
|
||||
<Image
|
||||
src={coverUrl}
|
||||
alt="League cover placeholder"
|
||||
fill
|
||||
className="object-cover opacity-80"
|
||||
sizes="100vw"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<h1 className="text-3xl font-bold text-white">{leagueName}</h1>
|
||||
<MembershipStatus leagueId={leagueId} />
|
||||
</div>
|
||||
<FeatureLimitationTooltip message="Multi-league memberships coming in production">
|
||||
<span className="px-2 py-1 text-xs font-medium bg-primary-blue/10 text-primary-blue rounded border border-primary-blue/30">
|
||||
Alpha: Single League
|
||||
</span>
|
||||
</FeatureLimitationTooltip>
|
||||
</div>
|
||||
|
||||
{description && (
|
||||
<p className="text-gray-400 mb-2">{description}</p>
|
||||
)}
|
||||
|
||||
<div className="text-sm text-gray-400 mb-6">
|
||||
<span className="mr-2">Owner:</span>
|
||||
<Link
|
||||
href={`/drivers/${ownerId}?from=league&leagueId=${leagueId}`}
|
||||
className="text-primary-blue hover:underline"
|
||||
>
|
||||
{ownerName}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Driver } from '@gridpilot/racing/domain/entities/Driver';
|
||||
import { getDriverRepository, getDriverStats } from '@/lib/di-container';
|
||||
import {
|
||||
@@ -167,9 +168,12 @@ export default function LeagueMembers({
|
||||
>
|
||||
<td className="py-3 px-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-white font-medium">
|
||||
<Link
|
||||
href={`/drivers/${member.driverId}?from=league&leagueId=${leagueId}`}
|
||||
className="text-white font-medium hover:text-primary-blue transition-colors"
|
||||
>
|
||||
{getDriverName(member.driverId)}
|
||||
</span>
|
||||
</Link>
|
||||
{isCurrentUser && (
|
||||
<span className="text-xs text-gray-500">(You)</span>
|
||||
)}
|
||||
|
||||
@@ -182,7 +182,7 @@ export default function LeagueSchedule({ leagueId }: LeagueScheduleProps) {
|
||||
? 'bg-iron-gray/50 border-charcoal-outline/50 opacity-75'
|
||||
: 'bg-deep-graphite border-charcoal-outline hover:border-primary-blue'
|
||||
}`}
|
||||
onClick={() => router.push(`/races/${race.id}`)}
|
||||
onClick={() => router.push(`/leagues/${leagueId}/races/${race.id}`)}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div className="flex-1">
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { Standing } from '@gridpilot/racing/domain/entities/Standing';
|
||||
import { Driver } from '@gridpilot/racing/domain/entities/Driver';
|
||||
|
||||
interface StandingsTableProps {
|
||||
standings: Standing[];
|
||||
drivers: Driver[];
|
||||
leagueId: string;
|
||||
}
|
||||
|
||||
export default function StandingsTable({ standings, drivers }: StandingsTableProps) {
|
||||
export default function StandingsTable({ standings, drivers, leagueId }: StandingsTableProps) {
|
||||
const getDriverName = (driverId: string): string => {
|
||||
const driver = drivers.find(d => d.id === driverId);
|
||||
const driver = drivers.find((d) => d.id === driverId);
|
||||
return driver?.name || 'Unknown Driver';
|
||||
};
|
||||
|
||||
@@ -37,9 +39,9 @@ export default function StandingsTable({ standings, drivers }: StandingsTablePro
|
||||
<tbody>
|
||||
{standings.map((standing) => {
|
||||
const isLeader = standing.position === 1;
|
||||
|
||||
|
||||
return (
|
||||
<tr
|
||||
<tr
|
||||
key={`${standing.leagueId}-${standing.driverId}`}
|
||||
className="border-b border-charcoal-outline/50 hover:bg-iron-gray/20 transition-colors"
|
||||
>
|
||||
@@ -49,9 +51,16 @@ export default function StandingsTable({ standings, drivers }: StandingsTablePro
|
||||
</span>
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
<span className={isLeader ? 'text-white font-semibold' : 'text-white'}>
|
||||
<Link
|
||||
href={`/drivers/${standing.driverId}?from=league&leagueId=${leagueId}`}
|
||||
className={
|
||||
isLeader
|
||||
? 'text-white font-semibold hover:text-primary-blue transition-colors'
|
||||
: 'text-white hover:text-primary-blue transition-colors'
|
||||
}
|
||||
>
|
||||
{getDriverName(standing.driverId)}
|
||||
</span>
|
||||
</Link>
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
<span className="text-white font-medium">{standing.points}</span>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import type { DriverDTO } from '@gridpilot/racing/application/dto/DriverDTO';
|
||||
import Button from '../ui/Button';
|
||||
import { getDriverTeam } from '@/lib/racingLegacyFacade';
|
||||
import { getDriverTeam, getDriverAvatarUrl } from '@/lib/racingLegacyFacade';
|
||||
|
||||
interface ProfileHeaderProps {
|
||||
driver: DriverDTO;
|
||||
@@ -14,8 +15,14 @@ export default function ProfileHeader({ driver, isOwnProfile = false, onEditClic
|
||||
return (
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-20 h-20 rounded-full bg-gradient-to-br from-primary-blue to-purple-600 flex items-center justify-center text-3xl font-bold text-white">
|
||||
{driver.name.charAt(0).toUpperCase()}
|
||||
<div className="w-20 h-20 rounded-full bg-gradient-to-br from-primary-blue to-purple-600 overflow-hidden flex items-center justify-center">
|
||||
<Image
|
||||
src={getDriverAvatarUrl(driver.id)}
|
||||
alt={driver.name}
|
||||
width={80}
|
||||
height={80}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import Card from '../ui/Card';
|
||||
import { getTeamLogoUrl } from '@/lib/racingLegacyFacade';
|
||||
|
||||
interface TeamCardProps {
|
||||
id: string;
|
||||
@@ -36,14 +38,14 @@ export default function TeamCard({
|
||||
<Card>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-16 h-16 bg-charcoal-outline rounded-lg flex items-center justify-center flex-shrink-0">
|
||||
{logo ? (
|
||||
<img src={logo} alt={name} className="w-full h-full object-cover rounded-lg" />
|
||||
) : (
|
||||
<span className="text-2xl font-bold text-gray-500">
|
||||
{name.charAt(0)}
|
||||
</span>
|
||||
)}
|
||||
<div className="w-16 h-16 bg-charcoal-outline rounded-lg flex items-center justify-center flex-shrink-0 overflow-hidden">
|
||||
<Image
|
||||
src={logo || getTeamLogoUrl(id)}
|
||||
alt={name}
|
||||
width={64}
|
||||
height={64}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="text-lg font-semibold text-white truncate">
|
||||
|
||||
Reference in New Issue
Block a user