wip
This commit is contained in:
@@ -9,8 +9,9 @@ import Heading from '@/components/ui/Heading';
|
||||
import Breadcrumbs from '@/components/layout/Breadcrumbs';
|
||||
import FileProtestModal from '@/components/races/FileProtestModal';
|
||||
import SponsorInsightsCard, { useSponsorMode, MetricBuilders, SlotTemplates } from '@/components/sponsors/SponsorInsightsCard';
|
||||
import { getGetRaceDetailUseCase, getRegisterForRaceUseCase, getWithdrawFromRaceUseCase, getCancelRaceUseCase } from '@/lib/di-container';
|
||||
import { getGetRaceDetailUseCase, getRegisterForRaceUseCase, getWithdrawFromRaceUseCase, getCancelRaceUseCase, getCompleteRaceUseCase } from '@/lib/di-container';
|
||||
import { useEffectiveDriverId } from '@/lib/currentDriver';
|
||||
import { getMembership, isOwnerOrAdmin } from '@/lib/leagueMembership';
|
||||
import type {
|
||||
RaceDetailViewModel,
|
||||
RaceDetailEntryViewModel,
|
||||
@@ -49,6 +50,7 @@ export default function RaceDetailPage() {
|
||||
const [ratingChange, setRatingChange] = useState<number | null>(null);
|
||||
const [animatedRatingChange, setAnimatedRatingChange] = useState(0);
|
||||
const [showProtestModal, setShowProtestModal] = useState(false);
|
||||
const [membership, setMembership] = useState<any>(null);
|
||||
|
||||
const currentDriverId = useEffectiveDriverId();
|
||||
const isSponsorMode = useSponsorMode();
|
||||
@@ -65,6 +67,13 @@ export default function RaceDetailPage() {
|
||||
throw new Error('Race detail not available');
|
||||
}
|
||||
setViewModel(vm);
|
||||
|
||||
// Fetch league membership for admin controls
|
||||
if (vm.league) {
|
||||
const leagueMembership = getMembership(vm.league.id, currentDriverId);
|
||||
setMembership(leagueMembership);
|
||||
}
|
||||
|
||||
const userResultRatingChange = vm.userResult?.ratingChange ?? null;
|
||||
setRatingChange(userResultRatingChange);
|
||||
if (userResultRatingChange === null) {
|
||||
@@ -529,7 +538,7 @@ export default function RaceDetailPage() {
|
||||
{animatedRatingChange > 0 ? '+' : ''}
|
||||
{animatedRatingChange}
|
||||
</div>
|
||||
<div className="text-xs text-gray-400 mt-0.5">iRating</div>
|
||||
<div className="text-xs text-gray-400 mt-0.5">Rating</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -717,11 +726,11 @@ export default function RaceDetailPage() {
|
||||
className={`
|
||||
flex items-center justify-center w-8 h-8 rounded-lg font-bold text-sm
|
||||
${
|
||||
index === 0
|
||||
race.status === 'completed' && index === 0
|
||||
? 'bg-yellow-500/20 text-yellow-400'
|
||||
: index === 1
|
||||
: race.status === 'completed' && index === 1
|
||||
? 'bg-gray-400/20 text-gray-300'
|
||||
: index === 2
|
||||
: race.status === 'completed' && index === 2
|
||||
? 'bg-amber-600/20 text-amber-500'
|
||||
: 'bg-iron-gray text-gray-500'
|
||||
}
|
||||
@@ -892,9 +901,55 @@ export default function RaceDetailPage() {
|
||||
<Scale className="w-4 h-4" />
|
||||
Stewarding
|
||||
</Button>
|
||||
{membership && isOwnerOrAdmin(viewModel.league?.id || '', currentDriverId) && (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full flex items-center justify-center gap-2"
|
||||
onClick={async () => {
|
||||
const confirmed = window.confirm(
|
||||
'Re-open this race? This will allow re-registration and re-running. Results will be archived.'
|
||||
);
|
||||
if (!confirmed) return;
|
||||
|
||||
// TODO: Implement re-open race functionality
|
||||
alert('Re-open race functionality not yet implemented');
|
||||
}}
|
||||
>
|
||||
<PlayCircle className="w-4 h-4" />
|
||||
Re-open Race
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{race.status === 'running' && membership && isOwnerOrAdmin(viewModel.league?.id || '', currentDriverId) && (
|
||||
<Button
|
||||
variant="primary"
|
||||
className="w-full flex items-center justify-center gap-2"
|
||||
onClick={async () => {
|
||||
const confirmed = window.confirm(
|
||||
'Are you sure you want to end this race and process results?\n\nThis will mark the race as completed and calculate final standings.'
|
||||
);
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
const completeRace = getCompleteRaceUseCase();
|
||||
await completeRace.execute({ raceId: race.id });
|
||||
// Reload race data to reflect the completed race
|
||||
await loadRaceData();
|
||||
} catch (err) {
|
||||
alert(err instanceof Error ? err.message : 'Failed to complete race');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CheckCircle2 className="w-4 h-4" />
|
||||
End Race & Process Results
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{race.status === 'scheduled' && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
|
||||
Reference in New Issue
Block a user