wip
This commit is contained in:
@@ -19,7 +19,7 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
const presenter = new SponsorDashboardPresenter();
|
||||
const useCase = getGetSponsorDashboardUseCase();
|
||||
await useCase.execute({ sponsorId });
|
||||
await useCase.execute({ sponsorId }, presenter);
|
||||
const dashboard = presenter.getData();
|
||||
|
||||
if (!dashboard) {
|
||||
|
||||
@@ -19,7 +19,7 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
const presenter = new SponsorSponsorshipsPresenter();
|
||||
const useCase = getGetSponsorSponsorshipsUseCase();
|
||||
await useCase.execute({ sponsorId });
|
||||
await useCase.execute({ sponsorId }, presenter);
|
||||
const sponsorships = presenter.getData();
|
||||
|
||||
if (!sponsorships) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState, FormEvent } from 'react';
|
||||
import { useState, FormEvent, type ChangeEvent } from 'react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import {
|
||||
@@ -145,7 +145,7 @@ export default function LoginPage() {
|
||||
id="email"
|
||||
type="email"
|
||||
value={formData.email}
|
||||
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setFormData({ ...formData, email: e.target.value })}
|
||||
error={!!errors.email}
|
||||
errorMessage={errors.email}
|
||||
placeholder="you@example.com"
|
||||
@@ -172,7 +172,7 @@ export default function LoginPage() {
|
||||
id="password"
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={formData.password}
|
||||
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setFormData({ ...formData, password: e.target.value })}
|
||||
error={!!errors.password}
|
||||
errorMessage={errors.password}
|
||||
placeholder="••••••••"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, FormEvent } from 'react';
|
||||
import { useState, useEffect, FormEvent, type ChangeEvent } from 'react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import {
|
||||
@@ -226,7 +226,7 @@ export default function SignupPage() {
|
||||
id="displayName"
|
||||
type="text"
|
||||
value={formData.displayName}
|
||||
onChange={(e) => setFormData({ ...formData, displayName: e.target.value })}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setFormData({ ...formData, displayName: e.target.value })}
|
||||
error={!!errors.displayName}
|
||||
errorMessage={errors.displayName}
|
||||
placeholder="SpeedyRacer42"
|
||||
@@ -249,7 +249,7 @@ export default function SignupPage() {
|
||||
id="email"
|
||||
type="email"
|
||||
value={formData.email}
|
||||
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setFormData({ ...formData, email: e.target.value })}
|
||||
error={!!errors.email}
|
||||
errorMessage={errors.email}
|
||||
placeholder="you@example.com"
|
||||
@@ -271,7 +271,7 @@ export default function SignupPage() {
|
||||
id="password"
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={formData.password}
|
||||
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setFormData({ ...formData, password: e.target.value })}
|
||||
error={!!errors.password}
|
||||
errorMessage={errors.password}
|
||||
placeholder="••••••••"
|
||||
@@ -336,7 +336,7 @@ export default function SignupPage() {
|
||||
id="confirmPassword"
|
||||
type={showConfirmPassword ? 'text' : 'password'}
|
||||
value={formData.confirmPassword}
|
||||
onChange={(e) => setFormData({ ...formData, confirmPassword: e.target.value })}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setFormData({ ...formData, confirmPassword: e.target.value })}
|
||||
error={!!errors.confirmPassword}
|
||||
errorMessage={errors.confirmPassword}
|
||||
placeholder="••••••••"
|
||||
|
||||
@@ -27,6 +27,7 @@ import Card from '@/components/ui/Card';
|
||||
import Button from '@/components/ui/Button';
|
||||
import { getAuthService } from '@/lib/auth';
|
||||
import { getGetDashboardOverviewUseCase } from '@/lib/di-container';
|
||||
import { DashboardOverviewPresenter } from '@/lib/presenters/DashboardOverviewPresenter';
|
||||
import type {
|
||||
DashboardOverviewViewModel,
|
||||
DashboardFeedItemSummaryViewModel,
|
||||
@@ -94,8 +95,9 @@ export default async function DashboardPage() {
|
||||
const currentDriverId = session.user.primaryDriverId ?? '';
|
||||
|
||||
const useCase = getGetDashboardOverviewUseCase();
|
||||
await useCase.execute({ driverId: currentDriverId });
|
||||
const viewModel = useCase.presenter.getViewModel() as DashboardOverviewViewModel | null;
|
||||
const presenter = new DashboardOverviewPresenter();
|
||||
await useCase.execute({ driverId: currentDriverId }, presenter);
|
||||
const viewModel = presenter.getViewModel();
|
||||
|
||||
if (!viewModel) {
|
||||
return null;
|
||||
|
||||
@@ -28,6 +28,7 @@ import Input from '@/components/ui/Input';
|
||||
import Card from '@/components/ui/Card';
|
||||
import Heading from '@/components/ui/Heading';
|
||||
import { getGetDriversLeaderboardUseCase } from '@/lib/di-container';
|
||||
import { DriversLeaderboardPresenter } from '@/lib/presenters/DriversLeaderboardPresenter';
|
||||
import type { DriverLeaderboardItemViewModel, SkillLevel } from '@gridpilot/racing/application/presenters/IDriversLeaderboardPresenter';
|
||||
import Image from 'next/image';
|
||||
|
||||
@@ -387,13 +388,16 @@ export default function DriversPage() {
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
const useCase = getGetDriversLeaderboardUseCase();
|
||||
await useCase.execute();
|
||||
const viewModel = useCase.presenter.getViewModel();
|
||||
const presenter = new DriversLeaderboardPresenter();
|
||||
await useCase.execute(undefined as void, presenter);
|
||||
const viewModel = presenter.getViewModel();
|
||||
|
||||
setDrivers(viewModel.drivers);
|
||||
setTotalRaces(viewModel.totalRaces);
|
||||
setTotalWins(viewModel.totalWins);
|
||||
setActiveCount(viewModel.activeCount);
|
||||
if (viewModel) {
|
||||
setDrivers(viewModel.drivers);
|
||||
setTotalRaces(viewModel.totalRaces);
|
||||
setTotalWins(viewModel.totalWins);
|
||||
setActiveCount(viewModel.activeCount);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import Button from '@/components/ui/Button';
|
||||
import Input from '@/components/ui/Input';
|
||||
import Heading from '@/components/ui/Heading';
|
||||
import { getGetDriversLeaderboardUseCase } from '@/lib/di-container';
|
||||
import { DriversLeaderboardPresenter } from '@/lib/presenters/DriversLeaderboardPresenter';
|
||||
import type { DriverLeaderboardItemViewModel, SkillLevel } from '@gridpilot/racing/application/presenters/IDriversLeaderboardPresenter';
|
||||
import Image from 'next/image';
|
||||
|
||||
@@ -181,9 +182,12 @@ export default function DriverLeaderboardPage() {
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
const useCase = getGetDriversLeaderboardUseCase();
|
||||
await useCase.execute();
|
||||
const viewModel = useCase.presenter.getViewModel();
|
||||
setDrivers(viewModel.drivers);
|
||||
const presenter = new DriversLeaderboardPresenter();
|
||||
await useCase.execute(undefined as void, presenter);
|
||||
const viewModel = presenter.getViewModel();
|
||||
if (viewModel) {
|
||||
setDrivers(viewModel.drivers);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
import Button from '@/components/ui/Button';
|
||||
import Heading from '@/components/ui/Heading';
|
||||
import { getGetDriversLeaderboardUseCase, getGetTeamsLeaderboardUseCase } from '@/lib/di-container';
|
||||
import { DriversLeaderboardPresenter } from '@/lib/presenters/DriversLeaderboardPresenter';
|
||||
import { TeamsLeaderboardPresenter } from '@/lib/presenters/TeamsLeaderboardPresenter';
|
||||
import type { DriverLeaderboardItemViewModel, SkillLevel } from '@gridpilot/racing/application/presenters/IDriversLeaderboardPresenter';
|
||||
import type { TeamLeaderboardItemViewModel } from '@gridpilot/racing/application/presenters/ITeamsLeaderboardPresenter';
|
||||
@@ -287,15 +288,16 @@ export default function LeaderboardsPage() {
|
||||
try {
|
||||
const driversUseCase = getGetDriversLeaderboardUseCase();
|
||||
const teamsUseCase = getGetTeamsLeaderboardUseCase();
|
||||
const driversPresenter = new DriversLeaderboardPresenter();
|
||||
const teamsPresenter = new TeamsLeaderboardPresenter();
|
||||
|
||||
await driversUseCase.execute();
|
||||
await driversUseCase.execute(undefined as void, driversPresenter);
|
||||
await teamsUseCase.execute(undefined as void, teamsPresenter);
|
||||
|
||||
const driversViewModel = driversUseCase.presenter.getViewModel();
|
||||
const driversViewModel = driversPresenter.getViewModel();
|
||||
const teamsViewModel = teamsPresenter.getViewModel();
|
||||
|
||||
setDrivers(driversViewModel.drivers);
|
||||
setDrivers(driversViewModel?.drivers ?? []);
|
||||
setTeams(teamsViewModel ? teamsViewModel.teams : []);
|
||||
} catch (error) {
|
||||
console.error('Failed to load leaderboard data:', error);
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
getSponsorRepository,
|
||||
getSeasonSponsorshipRepository,
|
||||
} from '@/lib/di-container';
|
||||
import { LeagueScoringConfigPresenter } from '@/lib/presenters/LeagueScoringConfigPresenter';
|
||||
import { Trophy, Star, ExternalLink } from 'lucide-react';
|
||||
import { getMembership, getLeagueMembers } from '@/lib/leagueMembership';
|
||||
import { useEffectiveDriverId } from '@/lib/currentDriver';
|
||||
@@ -125,8 +126,9 @@ export default function LeagueDetailPage() {
|
||||
|
||||
// Load scoring configuration for the active season
|
||||
const getLeagueScoringConfigUseCase = getGetLeagueScoringConfigUseCase();
|
||||
await getLeagueScoringConfigUseCase.execute({ leagueId });
|
||||
const scoringViewModel = getLeagueScoringConfigUseCase.presenter.getViewModel();
|
||||
const scoringPresenter = new LeagueScoringConfigPresenter();
|
||||
await getLeagueScoringConfigUseCase.execute({ leagueId }, scoringPresenter);
|
||||
const scoringViewModel = scoringPresenter.getViewModel();
|
||||
setScoringConfig(scoringViewModel as unknown as LeagueScoringConfigDTO);
|
||||
|
||||
// Load all drivers for standings and map to DTOs for UI components
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getLeagueRepository,
|
||||
getGetLeagueScoringConfigUseCase
|
||||
} from '@/lib/di-container';
|
||||
import { LeagueScoringConfigPresenter } from '@/lib/presenters/LeagueScoringConfigPresenter';
|
||||
import type { LeagueScoringConfigDTO } from '@gridpilot/racing/application/dto/LeagueScoringConfigDTO';
|
||||
import type { League } from '@gridpilot/racing/domain/entities/League';
|
||||
|
||||
@@ -35,8 +36,9 @@ export default function LeagueRulebookPage() {
|
||||
|
||||
setLeague(leagueData);
|
||||
|
||||
await scoringUseCase.execute({ leagueId });
|
||||
const scoringViewModel = scoringUseCase.presenter.getViewModel();
|
||||
const scoringPresenter = new LeagueScoringConfigPresenter();
|
||||
await scoringUseCase.execute({ leagueId }, scoringPresenter);
|
||||
const scoringViewModel = scoringPresenter.getViewModel();
|
||||
setScoringConfig(scoringViewModel as unknown as LeagueScoringConfigDTO);
|
||||
} catch (err) {
|
||||
console.error('Failed to load scoring config:', err);
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
getDriverRepository,
|
||||
getLeagueMembershipRepository
|
||||
} from '@/lib/di-container';
|
||||
import { LeagueDriverSeasonStatsPresenter } from '@/lib/presenters/LeagueDriverSeasonStatsPresenter';
|
||||
import { useEffectiveDriverId } from '@/lib/currentDriver';
|
||||
import { isLeagueAdminOrHigherRole } from '@/lib/leagueRoles';
|
||||
import type { MembershipRole, LeagueMembership } from '@/lib/leagueMembership';
|
||||
@@ -36,15 +37,9 @@ export default function LeagueStandingsPage() {
|
||||
const driverRepo = getDriverRepository();
|
||||
const membershipRepo = getLeagueMembershipRepository();
|
||||
|
||||
await getLeagueDriverSeasonStatsUseCase.execute({ leagueId });
|
||||
type GetLeagueDriverSeasonStatsUseCaseType = {
|
||||
presenter: {
|
||||
getViewModel(): { stats: LeagueDriverSeasonStatsDTO[] };
|
||||
};
|
||||
};
|
||||
const typedUseCase =
|
||||
getLeagueDriverSeasonStatsUseCase as GetLeagueDriverSeasonStatsUseCaseType;
|
||||
const standingsViewModel = typedUseCase.presenter.getViewModel();
|
||||
const presenter = new LeagueDriverSeasonStatsPresenter();
|
||||
await getLeagueDriverSeasonStatsUseCase.execute({ leagueId }, presenter);
|
||||
const standingsViewModel = presenter.getViewModel();
|
||||
setStandings(standingsViewModel.stats);
|
||||
|
||||
const allDrivers = await driverRepo.findAll();
|
||||
|
||||
@@ -30,7 +30,8 @@ import Button from '@/components/ui/Button';
|
||||
import Card from '@/components/ui/Card';
|
||||
import Input from '@/components/ui/Input';
|
||||
import Heading from '@/components/ui/Heading';
|
||||
import type { LeagueSummaryDTO } from '@gridpilot/racing/application/dto/LeagueSummaryDTO';
|
||||
import type { LeagueSummaryViewModel } from '@gridpilot/racing/application/presenters/IAllLeaguesWithCapacityAndScoringPresenter';
|
||||
import { AllLeaguesWithCapacityAndScoringPresenter } from '@/lib/presenters/AllLeaguesWithCapacityAndScoringPresenter';
|
||||
import { getGetAllLeaguesWithCapacityAndScoringUseCase } from '@/lib/di-container';
|
||||
|
||||
// ============================================================================
|
||||
@@ -57,7 +58,7 @@ interface Category {
|
||||
label: string;
|
||||
icon: React.ElementType;
|
||||
description: string;
|
||||
filter: (league: LeagueSummaryDTO) => boolean;
|
||||
filter: (league: LeagueSummaryViewModel) => boolean;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
@@ -175,7 +176,7 @@ interface LeagueSliderProps {
|
||||
title: string;
|
||||
icon: React.ElementType;
|
||||
description: string;
|
||||
leagues: LeagueSummaryDTO[];
|
||||
leagues: LeagueSummaryViewModel[];
|
||||
onLeagueClick: (id: string) => void;
|
||||
autoScroll?: boolean;
|
||||
iconColor?: string;
|
||||
@@ -377,25 +378,23 @@ function LeagueSlider({
|
||||
|
||||
export default function LeaguesPage() {
|
||||
const router = useRouter();
|
||||
const [realLeagues, setRealLeagues] = useState<LeagueSummaryDTO[]>([]);
|
||||
const [realLeagues, setRealLeagues] = useState<LeagueSummaryViewModel[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [activeCategory, setActiveCategory] = useState<CategoryId>('all');
|
||||
const [showFilters, setShowFilters] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
loadLeagues();
|
||||
void loadLeagues();
|
||||
}, []);
|
||||
|
||||
const loadLeagues = async () => {
|
||||
try {
|
||||
const useCase = getGetAllLeaguesWithCapacityAndScoringUseCase();
|
||||
await useCase.execute();
|
||||
const presenter = useCase.presenter as unknown as {
|
||||
getViewModel(): { leagues: LeagueSummaryDTO[] };
|
||||
};
|
||||
const presenter = new AllLeaguesWithCapacityAndScoringPresenter();
|
||||
await useCase.execute(undefined as void, presenter);
|
||||
const viewModel = presenter.getViewModel();
|
||||
setRealLeagues(viewModel.leagues);
|
||||
setRealLeagues(viewModel?.leagues ?? []);
|
||||
} catch (error) {
|
||||
console.error('Failed to load leagues:', error);
|
||||
} finally {
|
||||
@@ -434,7 +433,7 @@ export default function LeaguesPage() {
|
||||
acc[category.id] = searchFilteredLeagues.filter(category.filter);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<CategoryId, LeagueSummaryDTO[]>,
|
||||
{} as Record<CategoryId, LeagueSummaryViewModel[]>,
|
||||
);
|
||||
|
||||
// Featured categories to show as sliders with different scroll speeds and alternating directions
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
ArrowLeft,
|
||||
Scale,
|
||||
} from 'lucide-react';
|
||||
import { RaceDetailPresenter } from '@/lib/presenters/RaceDetailPresenter';
|
||||
|
||||
export default function RaceDetailPage() {
|
||||
const router = useRouter();
|
||||
@@ -57,8 +58,9 @@ export default function RaceDetailPage() {
|
||||
setError(null);
|
||||
try {
|
||||
const useCase = getGetRaceDetailUseCase();
|
||||
await useCase.execute({ raceId, driverId: currentDriverId });
|
||||
const vm = useCase.presenter.getViewModel();
|
||||
const presenter = new RaceDetailPresenter();
|
||||
await useCase.execute({ raceId, driverId: currentDriverId }, presenter);
|
||||
const vm = presenter.getViewModel();
|
||||
if (!vm) {
|
||||
throw new Error('Race detail not available');
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
getGetRaceResultsDetailUseCase,
|
||||
getImportRaceResultsUseCase,
|
||||
} from '@/lib/di-container';
|
||||
import { RaceWithSOFPresenter } from '@/lib/presenters/RaceWithSOFPresenter';
|
||||
import { RaceResultsDetailPresenter } from '@/lib/presenters/RaceResultsDetailPresenter';
|
||||
import type {
|
||||
RaceResultsHeaderViewModel,
|
||||
RaceResultsLeagueViewModel,
|
||||
@@ -71,7 +73,7 @@ export default function RaceResultsPage() {
|
||||
const [currentDriverId, setCurrentDriverId] = useState<string | undefined>(undefined);
|
||||
const [raceSOF, setRaceSOF] = useState<number | null>(null);
|
||||
const [penalties, setPenalties] = useState<PenaltyData[]>([]);
|
||||
const [pointsSystem, setPointsSystem] = useState<Record<number, number>>({});
|
||||
const [pointsSystem, setPointsSystem] = useState<Record<number, number> | undefined>(undefined);
|
||||
const [fastestLapTime, setFastestLapTime] = useState<number | undefined>(undefined);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -81,9 +83,10 @@ export default function RaceResultsPage() {
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const raceResultsUseCase = getGetRaceResultsDetailUseCase();
|
||||
await raceResultsUseCase.execute({ raceId });
|
||||
const raceResultsPresenter = new RaceResultsDetailPresenter();
|
||||
await raceResultsUseCase.execute({ raceId }, raceResultsPresenter);
|
||||
|
||||
const viewModel = raceResultsUseCase.presenter.getViewModel();
|
||||
const viewModel = raceResultsPresenter.getViewModel();
|
||||
|
||||
if (!viewModel) {
|
||||
setError('Failed to load race data');
|
||||
@@ -130,8 +133,9 @@ export default function RaceResultsPage() {
|
||||
|
||||
try {
|
||||
const raceWithSOFUseCase = getGetRaceWithSOFUseCase();
|
||||
await raceWithSOFUseCase.execute({ raceId });
|
||||
const raceViewModel = raceWithSOFUseCase.presenter.getViewModel();
|
||||
const sofPresenter = new RaceWithSOFPresenter();
|
||||
await raceWithSOFUseCase.execute({ raceId }, sofPresenter);
|
||||
const raceViewModel = sofPresenter.getViewModel();
|
||||
if (raceViewModel) {
|
||||
setRaceSOF(raceViewModel.strengthOfField);
|
||||
}
|
||||
@@ -290,7 +294,7 @@ export default function RaceResultsPage() {
|
||||
<ResultsTable
|
||||
results={results}
|
||||
drivers={drivers}
|
||||
pointsSystem={pointsSystem}
|
||||
pointsSystem={pointsSystem ?? {}}
|
||||
fastestLapTime={fastestLapTime ?? 0}
|
||||
penalties={penalties}
|
||||
currentDriverId={currentDriverId ?? ''}
|
||||
|
||||
@@ -8,6 +8,7 @@ import Button from '@/components/ui/Button';
|
||||
import Heading from '@/components/ui/Heading';
|
||||
import Breadcrumbs from '@/components/layout/Breadcrumbs';
|
||||
import { getGetAllRacesPageDataUseCase } from '@/lib/di-container';
|
||||
import { AllRacesPagePresenter } from '@/lib/presenters/AllRacesPagePresenter';
|
||||
import type {
|
||||
AllRacesPageViewModel,
|
||||
AllRacesListItemViewModel,
|
||||
@@ -53,8 +54,9 @@ export default function AllRacesPage() {
|
||||
const loadRaces = async () => {
|
||||
try {
|
||||
const useCase = getGetAllRacesPageDataUseCase();
|
||||
await useCase.execute();
|
||||
const viewModel = useCase.presenter.getViewModel();
|
||||
const presenter = new AllRacesPagePresenter();
|
||||
await useCase.execute(undefined, presenter);
|
||||
const viewModel = presenter.getViewModel();
|
||||
setPageData(viewModel);
|
||||
} catch (err) {
|
||||
console.error('Failed to load races:', err);
|
||||
|
||||
@@ -7,6 +7,7 @@ import Card from '@/components/ui/Card';
|
||||
import Button from '@/components/ui/Button';
|
||||
import Heading from '@/components/ui/Heading';
|
||||
import { getGetRacesPageDataUseCase } from '@/lib/di-container';
|
||||
import { RacesPagePresenter } from '@/lib/presenters/RacesPagePresenter';
|
||||
import type {
|
||||
RacesPageViewModel,
|
||||
RaceListItemViewModel,
|
||||
@@ -46,8 +47,9 @@ export default function RacesPage() {
|
||||
const loadRaces = async () => {
|
||||
try {
|
||||
const useCase = getGetRacesPageDataUseCase();
|
||||
await useCase.execute();
|
||||
const data = useCase.presenter.getViewModel();
|
||||
const presenter = new RacesPagePresenter();
|
||||
await useCase.execute(undefined, presenter);
|
||||
const data = presenter.getViewModel();
|
||||
setPageData(data);
|
||||
} catch (err) {
|
||||
console.error('Failed to load races:', err);
|
||||
|
||||
@@ -10,6 +10,8 @@ import Breadcrumbs from '@/components/layout/Breadcrumbs';
|
||||
import SponsorInsightsCard, { useSponsorMode, MetricBuilders, SlotTemplates } from '@/components/sponsors/SponsorInsightsCard';
|
||||
import { getImageService } from '@/lib/di-container';
|
||||
import { TeamMembersPresenter } from '@/lib/presenters/TeamMembersPresenter';
|
||||
import { TeamDetailsPresenter } from '@/lib/presenters/TeamDetailsPresenter';
|
||||
import type { TeamDetailsViewModel } from '@gridpilot/racing/application/presenters/ITeamDetailsPresenter';
|
||||
import TeamRoster from '@/components/teams/TeamRoster';
|
||||
import TeamStandings from '@/components/teams/TeamStandings';
|
||||
import TeamAdmin from '@/components/teams/TeamAdmin';
|
||||
@@ -20,7 +22,6 @@ import {
|
||||
getTeamMembershipRepository,
|
||||
} from '@/lib/di-container';
|
||||
import { useEffectiveDriverId } from '@/lib/currentDriver';
|
||||
import type { Team } from '@gridpilot/racing';
|
||||
import { Users, Trophy, TrendingUp, Star, Zap } from 'lucide-react';
|
||||
|
||||
type TeamRole = 'owner' | 'manager' | 'driver';
|
||||
@@ -36,8 +37,10 @@ type Tab = 'overview' | 'roster' | 'standings' | 'admin';
|
||||
export default function TeamDetailPage() {
|
||||
const params = useParams();
|
||||
const teamId = params.id as string;
|
||||
|
||||
const [team, setTeam] = useState<Team | null>(null);
|
||||
|
||||
type TeamViewModel = TeamDetailsViewModel['team'];
|
||||
|
||||
const [team, setTeam] = useState<TeamViewModel | null>(null);
|
||||
const [memberships, setMemberships] = useState<TeamMembership[]>([]);
|
||||
const [activeTab, setActiveTab] = useState<Tab>('overview');
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -51,11 +54,9 @@ export default function TeamDetailPage() {
|
||||
const detailsUseCase = getGetTeamDetailsUseCase();
|
||||
const membersUseCase = getGetTeamMembersUseCase();
|
||||
|
||||
await detailsUseCase.execute(teamId, currentDriverId);
|
||||
const detailsPresenter = detailsUseCase.presenter;
|
||||
const detailsViewModel = detailsPresenter
|
||||
? (detailsPresenter as any).getViewModel?.() as { team: Team } | null
|
||||
: null;
|
||||
const detailsPresenter = new TeamDetailsPresenter();
|
||||
await detailsUseCase.execute({ teamId, driverId: currentDriverId }, detailsPresenter);
|
||||
const detailsViewModel = detailsPresenter.getViewModel();
|
||||
|
||||
if (!detailsViewModel) {
|
||||
setTeam(null);
|
||||
|
||||
Reference in New Issue
Block a user