This commit is contained in:
2025-12-13 18:39:20 +01:00
parent bb0497f429
commit e53af6a0e7
20 changed files with 762 additions and 503 deletions

View File

@@ -6,6 +6,7 @@ import Button from '@/components/ui/Button';
import Card from '@/components/ui/Card';
import JoinLeagueButton from '@/components/leagues/JoinLeagueButton';
import LeagueActivityFeed from '@/components/leagues/LeagueActivityFeed';
import EndRaceModal from '@/components/leagues/EndRaceModal';
import DriverIdentity from '@/components/drivers/DriverIdentity';
import DriverSummaryPill from '@/components/profile/DriverSummaryPill';
import SponsorInsightsCard, {
@@ -68,6 +69,7 @@ export default function LeagueDetailPage() {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [refreshKey, setRefreshKey] = useState(0);
const [endRaceModalRaceId, setEndRaceModalRaceId] = useState<string | null>(null);
const currentDriverId = useEffectiveDriverId();
const membership = getMembership(leagueId, currentDriverId);
@@ -347,22 +349,7 @@ export default function LeagueDetailPage() {
{membership?.role === 'admin' && (
<Button
variant="secondary"
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 league data to reflect the completed race
await loadLeagueData();
} catch (err) {
alert(err instanceof Error ? err.message : 'Failed to complete race');
}
}}
onClick={() => setEndRaceModalRaceId(race.id)}
className="border-performance-green/50 text-performance-green hover:bg-performance-green/10"
>
End Race & Process Results
@@ -684,6 +671,28 @@ export default function LeagueDetailPage() {
)}
</div>
</div>
{/* End Race Modal */}
{endRaceModalRaceId && (() => {
const race = runningRaces.find(r => r.id === endRaceModalRaceId);
return race ? (
<EndRaceModal
raceId={race.id}
raceName={race.track}
onConfirm={async () => {
try {
const completeRace = getCompleteRaceUseCase();
await completeRace.execute({ raceId: race.id });
await loadLeagueData();
setEndRaceModalRaceId(null);
} catch (err) {
alert(err instanceof Error ? err.message : 'Failed to complete race');
}
}}
onCancel={() => setEndRaceModalRaceId(null)}
/>
) : null;
})()}
</>
);
}

View File

@@ -6,6 +6,8 @@ import Link from 'next/link';
import Card from '@/components/ui/Card';
import Button from '@/components/ui/Button';
import { ReviewProtestModal } from '@/components/leagues/ReviewProtestModal';
import QuickPenaltyModal from '@/components/leagues/QuickPenaltyModal';
import PenaltyFAB from '@/components/leagues/PenaltyFAB';
import {
getRaceRepository,
getProtestRepository,
@@ -43,11 +45,13 @@ export default function LeagueStewardingPage() {
const [protestsByRace, setProtestsByRace] = useState<Record<string, Protest[]>>({});
const [penaltiesByRace, setPenaltiesByRace] = useState<Record<string, Penalty[]>>({});
const [driversById, setDriversById] = useState<Record<string, DriverDTO>>({});
const [allDrivers, setAllDrivers] = useState<DriverDTO[]>([]);
const [loading, setLoading] = useState(true);
const [isAdmin, setIsAdmin] = useState(false);
const [activeTab, setActiveTab] = useState<'pending' | 'history'>('pending');
const [selectedProtest, setSelectedProtest] = useState<Protest | null>(null);
const [expandedRaces, setExpandedRaces] = useState<Set<string>>(new Set());
const [showQuickPenaltyModal, setShowQuickPenaltyModal] = useState(false);
useEffect(() => {
async function checkAdmin() {
@@ -110,6 +114,7 @@ export default function LeagueStewardingPage() {
}
});
setDriversById(byId);
setAllDrivers(Object.values(byId));
// Auto-expand races with pending protests
const racesWithPending = new Set<string>();
@@ -495,6 +500,10 @@ export default function LeagueStewardingPage() {
)}
</Card>
{activeTab === 'history' && (
<PenaltyFAB onClick={() => setShowQuickPenaltyModal(true)} />
)}
{selectedProtest && (
<ReviewProtestModal
protest={selectedProtest}
@@ -503,6 +512,15 @@ export default function LeagueStewardingPage() {
onReject={handleRejectProtest}
/>
)}
{showQuickPenaltyModal && (
<QuickPenaltyModal
drivers={allDrivers}
onClose={() => setShowQuickPenaltyModal(false)}
adminId={currentDriverId}
races={races.map(r => ({ id: r.id, track: r.track, scheduledAt: r.scheduledAt }))}
/>
)}
</div>
);
}