wip
This commit is contained in:
@@ -88,48 +88,77 @@ import {
|
||||
JoinLeagueUseCase,
|
||||
RegisterForRaceUseCase,
|
||||
WithdrawFromRaceUseCase,
|
||||
IsDriverRegisteredForRaceQuery,
|
||||
GetRaceRegistrationsQuery,
|
||||
CreateTeamUseCase,
|
||||
JoinTeamUseCase,
|
||||
LeaveTeamUseCase,
|
||||
ApproveTeamJoinRequestUseCase,
|
||||
RejectTeamJoinRequestUseCase,
|
||||
UpdateTeamUseCase,
|
||||
GetAllTeamsQuery,
|
||||
GetTeamDetailsQuery,
|
||||
GetTeamMembersQuery,
|
||||
GetTeamJoinRequestsQuery,
|
||||
GetDriverTeamQuery,
|
||||
GetLeagueStandingsQuery,
|
||||
GetLeagueDriverSeasonStatsQuery,
|
||||
GetAllLeaguesWithCapacityQuery,
|
||||
GetAllLeaguesWithCapacityAndScoringQuery,
|
||||
ListLeagueScoringPresetsQuery,
|
||||
GetLeagueScoringConfigQuery,
|
||||
GetAllTeamsUseCase,
|
||||
GetTeamDetailsUseCase,
|
||||
GetTeamMembersUseCase,
|
||||
GetTeamJoinRequestsUseCase,
|
||||
GetDriverTeamUseCase,
|
||||
CreateLeagueWithSeasonAndScoringUseCase,
|
||||
GetLeagueFullConfigQuery,
|
||||
GetRaceWithSOFQuery,
|
||||
GetLeagueStatsQuery,
|
||||
FileProtestUseCase,
|
||||
ReviewProtestUseCase,
|
||||
ApplyPenaltyUseCase,
|
||||
GetRaceProtestsQuery,
|
||||
GetRacePenaltiesQuery,
|
||||
RequestProtestDefenseUseCase,
|
||||
SubmitProtestDefenseUseCase,
|
||||
GetSponsorDashboardQuery,
|
||||
GetSponsorSponsorshipsQuery,
|
||||
GetPendingSponsorshipRequestsQuery,
|
||||
GetEntitySponsorshipPricingQuery,
|
||||
GetSponsorDashboardUseCase,
|
||||
GetSponsorSponsorshipsUseCase,
|
||||
GetPendingSponsorshipRequestsUseCase,
|
||||
GetEntitySponsorshipPricingUseCase,
|
||||
ApplyForSponsorshipUseCase,
|
||||
AcceptSponsorshipRequestUseCase,
|
||||
RejectSponsorshipRequestUseCase,
|
||||
} from '@gridpilot/racing/application';
|
||||
import { IsDriverRegisteredForRaceUseCase } from '@gridpilot/racing/application/use-cases/IsDriverRegisteredForRaceQuery';
|
||||
import { GetRaceRegistrationsUseCase } from '@gridpilot/racing/application/use-cases/GetRaceRegistrationsQuery';
|
||||
import { GetRaceWithSOFUseCase } from '@gridpilot/racing/application/use-cases/GetRaceWithSOFQuery';
|
||||
import { GetRaceProtestsUseCase } from '@gridpilot/racing/application/use-cases/GetRaceProtestsQuery';
|
||||
import { GetRacePenaltiesUseCase } from '@gridpilot/racing/application/use-cases/GetRacePenaltiesQuery';
|
||||
import { GetLeagueStandingsUseCase } from '@gridpilot/racing/application/use-cases/GetLeagueStandingsQuery';
|
||||
import { GetLeagueDriverSeasonStatsUseCase } from '@gridpilot/racing/application/use-cases/GetLeagueDriverSeasonStatsQuery';
|
||||
import { GetAllLeaguesWithCapacityUseCase } from '@gridpilot/racing/application/use-cases/GetAllLeaguesWithCapacityQuery';
|
||||
import { GetAllLeaguesWithCapacityAndScoringUseCase } from '@gridpilot/racing/application/use-cases/GetAllLeaguesWithCapacityAndScoringQuery';
|
||||
import { ListLeagueScoringPresetsUseCase } from '@gridpilot/racing/application/use-cases/ListLeagueScoringPresetsQuery';
|
||||
import { GetLeagueScoringConfigUseCase } from '@gridpilot/racing/application/use-cases/GetLeagueScoringConfigQuery';
|
||||
import { GetLeagueFullConfigUseCase } from '@gridpilot/racing/application/use-cases/GetLeagueFullConfigQuery';
|
||||
import { GetLeagueStatsUseCase } from '@gridpilot/racing/application/use-cases/GetLeagueStatsQuery';
|
||||
import { GetRacesPageDataUseCase } from '@gridpilot/racing/application/use-cases/GetRacesPageDataUseCase';
|
||||
import { GetDriversLeaderboardUseCase } from '@gridpilot/racing/application/use-cases/GetDriversLeaderboardUseCase';
|
||||
import { GetTeamsLeaderboardUseCase } from '@gridpilot/racing/application/use-cases/GetTeamsLeaderboardUseCase';
|
||||
import { TransferLeagueOwnershipUseCase } from '@gridpilot/racing/application/use-cases/TransferLeagueOwnershipUseCase';
|
||||
import { DriversLeaderboardPresenter } from '../../lib/presenters/DriversLeaderboardPresenter';
|
||||
import { TeamsLeaderboardPresenter } from '../../lib/presenters/TeamsLeaderboardPresenter';
|
||||
import { RacesPagePresenter } from '../../lib/presenters/RacesPagePresenter';
|
||||
import { AllTeamsPresenter } from '../../lib/presenters/AllTeamsPresenter';
|
||||
import { TeamDetailsPresenter } from '../../lib/presenters/TeamDetailsPresenter';
|
||||
import { TeamMembersPresenter } from '../../lib/presenters/TeamMembersPresenter';
|
||||
import { TeamJoinRequestsPresenter } from '../../lib/presenters/TeamJoinRequestsPresenter';
|
||||
import { DriverTeamPresenter } from '../../lib/presenters/DriverTeamPresenter';
|
||||
import { AllLeaguesWithCapacityPresenter } from '../../lib/presenters/AllLeaguesWithCapacityPresenter';
|
||||
import { AllLeaguesWithCapacityAndScoringPresenter } from '../../lib/presenters/AllLeaguesWithCapacityAndScoringPresenter';
|
||||
import { LeagueStatsPresenter } from '../../lib/presenters/LeagueStatsPresenter';
|
||||
import { LeagueScoringConfigPresenter } from '../../lib/presenters/LeagueScoringConfigPresenter';
|
||||
import { LeagueFullConfigPresenter } from '../../lib/presenters/LeagueFullConfigPresenter';
|
||||
import { LeagueDriverSeasonStatsPresenter } from '../../lib/presenters/LeagueDriverSeasonStatsPresenter';
|
||||
import { LeagueStandingsPresenter } from '../../lib/presenters/LeagueStandingsPresenter';
|
||||
import { LeagueScoringPresetsPresenter } from '../../lib/presenters/LeagueScoringPresetsPresenter';
|
||||
import { RaceWithSOFPresenter } from '../../lib/presenters/RaceWithSOFPresenter';
|
||||
import { RaceProtestsPresenter } from '../../lib/presenters/RaceProtestsPresenter';
|
||||
import { RacePenaltiesPresenter } from '../../lib/presenters/RacePenaltiesPresenter';
|
||||
import { RaceRegistrationsPresenter } from '../../lib/presenters/RaceRegistrationsPresenter';
|
||||
import { DriverRegistrationStatusPresenter } from '../../lib/presenters/DriverRegistrationStatusPresenter';
|
||||
import type { DriverRatingProvider } from '@gridpilot/racing/application';
|
||||
import type { LeagueScoringPresetProvider } from '@gridpilot/racing/application/ports/LeagueScoringPresetProvider';
|
||||
import { PreviewLeagueScheduleQuery } from '@gridpilot/racing/application';
|
||||
import { PreviewLeagueScheduleUseCase } from '@gridpilot/racing/application';
|
||||
import { SponsorDashboardPresenter } from '../../lib/presenters/SponsorDashboardPresenter';
|
||||
import { SponsorSponsorshipsPresenter } from '../../lib/presenters/SponsorSponsorshipsPresenter';
|
||||
import { PendingSponsorshipRequestsPresenter } from '../../lib/presenters/PendingSponsorshipRequestsPresenter';
|
||||
import { EntitySponsorshipPricingPresenter } from '../../lib/presenters/EntitySponsorshipPricingPresenter';
|
||||
import { LeagueSchedulePreviewPresenter } from '../../lib/presenters/LeagueSchedulePreviewPresenter';
|
||||
|
||||
// Testing support
|
||||
import {
|
||||
@@ -840,24 +869,28 @@ export function configureDIContainer(): void {
|
||||
);
|
||||
|
||||
// Register queries - Racing
|
||||
const driverRegistrationStatusPresenter = new DriverRegistrationStatusPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.IsDriverRegisteredForRaceQuery,
|
||||
new IsDriverRegisteredForRaceQuery(raceRegistrationRepository)
|
||||
DI_TOKENS.IsDriverRegisteredForRaceUseCase,
|
||||
new IsDriverRegisteredForRaceUseCase(raceRegistrationRepository, driverRegistrationStatusPresenter)
|
||||
);
|
||||
|
||||
const raceRegistrationsPresenter = new RaceRegistrationsPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetRaceRegistrationsQuery,
|
||||
new GetRaceRegistrationsQuery(raceRegistrationRepository)
|
||||
DI_TOKENS.GetRaceRegistrationsUseCase,
|
||||
new GetRaceRegistrationsUseCase(raceRegistrationRepository, raceRegistrationsPresenter)
|
||||
);
|
||||
|
||||
const leagueStandingsPresenter = new LeagueStandingsPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetLeagueStandingsQuery,
|
||||
new GetLeagueStandingsQuery(standingRepository)
|
||||
DI_TOKENS.GetLeagueStandingsUseCase,
|
||||
new GetLeagueStandingsUseCase(standingRepository, leagueStandingsPresenter)
|
||||
);
|
||||
|
||||
const leagueDriverSeasonStatsPresenter = new LeagueDriverSeasonStatsPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetLeagueDriverSeasonStatsQuery,
|
||||
new GetLeagueDriverSeasonStatsQuery(
|
||||
DI_TOKENS.GetLeagueDriverSeasonStatsUseCase,
|
||||
new GetLeagueDriverSeasonStatsUseCase(
|
||||
standingRepository,
|
||||
resultRepository,
|
||||
penaltyRepository,
|
||||
@@ -875,113 +908,200 @@ export function configureDIContainer(): void {
|
||||
ratingChange: delta !== 0 ? delta : null,
|
||||
};
|
||||
},
|
||||
}
|
||||
},
|
||||
leagueDriverSeasonStatsPresenter
|
||||
)
|
||||
);
|
||||
|
||||
const allLeaguesWithCapacityPresenter = new AllLeaguesWithCapacityPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetAllLeaguesWithCapacityQuery,
|
||||
new GetAllLeaguesWithCapacityQuery(leagueRepository, leagueMembershipRepository)
|
||||
DI_TOKENS.GetAllLeaguesWithCapacityUseCase,
|
||||
new GetAllLeaguesWithCapacityUseCase(
|
||||
leagueRepository,
|
||||
leagueMembershipRepository,
|
||||
allLeaguesWithCapacityPresenter
|
||||
)
|
||||
);
|
||||
|
||||
const allLeaguesWithCapacityAndScoringPresenter = new AllLeaguesWithCapacityAndScoringPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetAllLeaguesWithCapacityAndScoringQuery,
|
||||
new GetAllLeaguesWithCapacityAndScoringQuery(
|
||||
DI_TOKENS.GetAllLeaguesWithCapacityAndScoringUseCase,
|
||||
new GetAllLeaguesWithCapacityAndScoringUseCase(
|
||||
leagueRepository,
|
||||
leagueMembershipRepository,
|
||||
seasonRepository,
|
||||
leagueScoringConfigRepository,
|
||||
gameRepository,
|
||||
leagueScoringPresetProvider
|
||||
leagueScoringPresetProvider,
|
||||
allLeaguesWithCapacityAndScoringPresenter
|
||||
)
|
||||
);
|
||||
|
||||
const leagueScoringPresetsPresenter = new LeagueScoringPresetsPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.ListLeagueScoringPresetsQuery,
|
||||
new ListLeagueScoringPresetsQuery(leagueScoringPresetProvider)
|
||||
DI_TOKENS.ListLeagueScoringPresetsUseCase,
|
||||
new ListLeagueScoringPresetsUseCase(leagueScoringPresetProvider, leagueScoringPresetsPresenter)
|
||||
);
|
||||
|
||||
const leagueScoringConfigPresenter = new LeagueScoringConfigPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetLeagueScoringConfigQuery,
|
||||
new GetLeagueScoringConfigQuery(
|
||||
DI_TOKENS.GetLeagueScoringConfigUseCase,
|
||||
new GetLeagueScoringConfigUseCase(
|
||||
leagueRepository,
|
||||
seasonRepository,
|
||||
leagueScoringConfigRepository,
|
||||
gameRepository,
|
||||
leagueScoringPresetProvider
|
||||
leagueScoringPresetProvider,
|
||||
leagueScoringConfigPresenter
|
||||
)
|
||||
);
|
||||
|
||||
const leagueFullConfigPresenter = new LeagueFullConfigPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetLeagueFullConfigQuery,
|
||||
new GetLeagueFullConfigQuery(
|
||||
DI_TOKENS.GetLeagueFullConfigUseCase,
|
||||
new GetLeagueFullConfigUseCase(
|
||||
leagueRepository,
|
||||
seasonRepository,
|
||||
leagueScoringConfigRepository,
|
||||
gameRepository
|
||||
gameRepository,
|
||||
leagueFullConfigPresenter
|
||||
)
|
||||
);
|
||||
|
||||
const leagueSchedulePreviewPresenter = new LeagueSchedulePreviewPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.PreviewLeagueScheduleQuery,
|
||||
new PreviewLeagueScheduleQuery()
|
||||
DI_TOKENS.PreviewLeagueScheduleUseCase,
|
||||
new PreviewLeagueScheduleUseCase(undefined, leagueSchedulePreviewPresenter)
|
||||
);
|
||||
|
||||
const raceWithSOFPresenter = new RaceWithSOFPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetRaceWithSOFQuery,
|
||||
new GetRaceWithSOFQuery(
|
||||
DI_TOKENS.GetRaceWithSOFUseCase,
|
||||
new GetRaceWithSOFUseCase(
|
||||
raceRepository,
|
||||
raceRegistrationRepository,
|
||||
resultRepository,
|
||||
driverRatingProvider
|
||||
driverRatingProvider,
|
||||
raceWithSOFPresenter
|
||||
)
|
||||
);
|
||||
|
||||
const leagueStatsPresenter = new LeagueStatsPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetLeagueStatsQuery,
|
||||
new GetLeagueStatsQuery(
|
||||
DI_TOKENS.GetLeagueStatsUseCase,
|
||||
new GetLeagueStatsUseCase(
|
||||
leagueRepository,
|
||||
raceRepository,
|
||||
resultRepository,
|
||||
driverRatingProvider
|
||||
driverRatingProvider,
|
||||
leagueStatsPresenter
|
||||
)
|
||||
);
|
||||
|
||||
// Register queries - Teams
|
||||
const racesPresenter = new RacesPagePresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetAllTeamsQuery,
|
||||
new GetAllTeamsQuery(teamRepository)
|
||||
DI_TOKENS.GetRacesPageDataUseCase,
|
||||
new GetRacesPageDataUseCase(raceRepository, leagueRepository, racesPresenter)
|
||||
);
|
||||
|
||||
// Create services for driver leaderboard query
|
||||
const rankingService = {
|
||||
getAllDriverRankings: () => {
|
||||
const stats = getDIContainer().resolve<Record<string, any>>(DI_TOKENS.DriverStats);
|
||||
return Object.entries(stats).map(([driverId, stat]) => ({
|
||||
driverId,
|
||||
rating: stat.rating,
|
||||
overallRank: stat.overallRank,
|
||||
})).sort((a, b) => b.rating - a.rating);
|
||||
}
|
||||
};
|
||||
|
||||
const driverStatsService = {
|
||||
getDriverStats: (driverId: string) => {
|
||||
const stats = getDIContainer().resolve<Record<string, any>>(DI_TOKENS.DriverStats);
|
||||
return stats[driverId] || null;
|
||||
}
|
||||
};
|
||||
|
||||
const imageService = getDIContainer().resolve<ImageServicePort>(DI_TOKENS.ImageService);
|
||||
|
||||
const driversPresenter = new DriversLeaderboardPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetTeamDetailsQuery,
|
||||
new GetTeamDetailsQuery(teamRepository, teamMembershipRepository)
|
||||
DI_TOKENS.GetDriversLeaderboardUseCase,
|
||||
new GetDriversLeaderboardUseCase(
|
||||
driverRepository,
|
||||
rankingService as any,
|
||||
driverStatsService as any,
|
||||
imageService,
|
||||
driversPresenter
|
||||
)
|
||||
);
|
||||
|
||||
const getDriverStatsAdapter = (driverId: string) => {
|
||||
const stats = getDIContainer().resolve<Record<string, any>>(DI_TOKENS.DriverStats);
|
||||
const stat = stats[driverId];
|
||||
if (!stat) return null;
|
||||
return {
|
||||
rating: stat.rating ?? null,
|
||||
wins: stat.wins ?? 0,
|
||||
totalRaces: stat.totalRaces ?? 0,
|
||||
};
|
||||
};
|
||||
|
||||
const teamsPresenter = new TeamsLeaderboardPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetTeamMembersQuery,
|
||||
new GetTeamMembersQuery(teamMembershipRepository)
|
||||
DI_TOKENS.GetTeamsLeaderboardUseCase,
|
||||
new GetTeamsLeaderboardUseCase(
|
||||
teamRepository,
|
||||
teamMembershipRepository,
|
||||
driverRepository,
|
||||
getDriverStatsAdapter,
|
||||
teamsPresenter
|
||||
)
|
||||
);
|
||||
|
||||
// Register use cases - Teams (Query-like with Presenters)
|
||||
const allTeamsPresenter = new AllTeamsPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetTeamJoinRequestsQuery,
|
||||
new GetTeamJoinRequestsQuery(teamMembershipRepository)
|
||||
DI_TOKENS.GetAllTeamsUseCase,
|
||||
new GetAllTeamsUseCase(teamRepository, teamMembershipRepository, allTeamsPresenter)
|
||||
);
|
||||
|
||||
const teamDetailsPresenter = new TeamDetailsPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetDriverTeamQuery,
|
||||
new GetDriverTeamQuery(teamRepository, teamMembershipRepository)
|
||||
DI_TOKENS.GetTeamDetailsUseCase,
|
||||
new GetTeamDetailsUseCase(teamRepository, teamMembershipRepository, teamDetailsPresenter)
|
||||
);
|
||||
|
||||
const teamMembersPresenter = new TeamMembersPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetTeamMembersUseCase,
|
||||
new GetTeamMembersUseCase(teamMembershipRepository, driverRepository, imageService, teamMembersPresenter)
|
||||
);
|
||||
|
||||
const teamJoinRequestsPresenter = new TeamJoinRequestsPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetTeamJoinRequestsUseCase,
|
||||
new GetTeamJoinRequestsUseCase(teamMembershipRepository, driverRepository, imageService, teamJoinRequestsPresenter)
|
||||
);
|
||||
|
||||
const driverTeamPresenter = new DriverTeamPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetDriverTeamUseCase,
|
||||
new GetDriverTeamUseCase(teamRepository, teamMembershipRepository, driverTeamPresenter)
|
||||
);
|
||||
|
||||
// Register queries - Stewarding
|
||||
const raceProtestsPresenter = new RaceProtestsPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetRaceProtestsQuery,
|
||||
new GetRaceProtestsQuery(protestRepository, driverRepository)
|
||||
DI_TOKENS.GetRaceProtestsUseCase,
|
||||
new GetRaceProtestsUseCase(protestRepository, driverRepository, raceProtestsPresenter)
|
||||
);
|
||||
|
||||
const racePenaltiesPresenter = new RacePenaltiesPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetRacePenaltiesQuery,
|
||||
new GetRacePenaltiesQuery(penaltyRepository, driverRepository)
|
||||
DI_TOKENS.GetRacePenaltiesUseCase,
|
||||
new GetRacePenaltiesUseCase(penaltyRepository, driverRepository, racePenaltiesPresenter)
|
||||
);
|
||||
|
||||
// Register queries - Notifications
|
||||
@@ -990,31 +1110,35 @@ export function configureDIContainer(): void {
|
||||
new GetUnreadNotificationsQuery(notificationRepository)
|
||||
);
|
||||
|
||||
// Register queries - Sponsors
|
||||
// Register use cases - Sponsors
|
||||
const sponsorRepository = container.resolve<ISponsorRepository>(DI_TOKENS.SponsorRepository);
|
||||
const seasonSponsorshipRepository = container.resolve<ISeasonSponsorshipRepository>(DI_TOKENS.SeasonSponsorshipRepository);
|
||||
|
||||
const sponsorDashboardPresenter = new SponsorDashboardPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetSponsorDashboardQuery,
|
||||
new GetSponsorDashboardQuery(
|
||||
DI_TOKENS.GetSponsorDashboardUseCase,
|
||||
new GetSponsorDashboardUseCase(
|
||||
sponsorRepository,
|
||||
seasonSponsorshipRepository,
|
||||
seasonRepository,
|
||||
leagueRepository,
|
||||
leagueMembershipRepository,
|
||||
raceRepository
|
||||
raceRepository,
|
||||
sponsorDashboardPresenter
|
||||
)
|
||||
);
|
||||
|
||||
const sponsorSponsorshipsPresenter = new SponsorSponsorshipsPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetSponsorSponsorshipsQuery,
|
||||
new GetSponsorSponsorshipsQuery(
|
||||
DI_TOKENS.GetSponsorSponsorshipsUseCase,
|
||||
new GetSponsorSponsorshipsUseCase(
|
||||
sponsorRepository,
|
||||
seasonSponsorshipRepository,
|
||||
seasonRepository,
|
||||
leagueRepository,
|
||||
leagueMembershipRepository,
|
||||
raceRepository
|
||||
raceRepository,
|
||||
sponsorSponsorshipsPresenter
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1022,20 +1146,24 @@ export function configureDIContainer(): void {
|
||||
const sponsorshipRequestRepository = container.resolve<ISponsorshipRequestRepository>(DI_TOKENS.SponsorshipRequestRepository);
|
||||
const sponsorshipPricingRepository = container.resolve<ISponsorshipPricingRepository>(DI_TOKENS.SponsorshipPricingRepository);
|
||||
|
||||
const pendingSponsorshipRequestsPresenter = new PendingSponsorshipRequestsPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetPendingSponsorshipRequestsQuery,
|
||||
new GetPendingSponsorshipRequestsQuery(
|
||||
DI_TOKENS.GetPendingSponsorshipRequestsUseCase,
|
||||
new GetPendingSponsorshipRequestsUseCase(
|
||||
sponsorshipRequestRepository,
|
||||
sponsorRepository
|
||||
sponsorRepository,
|
||||
pendingSponsorshipRequestsPresenter
|
||||
)
|
||||
);
|
||||
|
||||
const entitySponsorshipPricingPresenter = new EntitySponsorshipPricingPresenter();
|
||||
container.registerInstance(
|
||||
DI_TOKENS.GetEntitySponsorshipPricingQuery,
|
||||
new GetEntitySponsorshipPricingQuery(
|
||||
DI_TOKENS.GetEntitySponsorshipPricingUseCase,
|
||||
new GetEntitySponsorshipPricingUseCase(
|
||||
sponsorshipPricingRepository,
|
||||
sponsorshipRequestRepository,
|
||||
seasonSponsorshipRepository
|
||||
seasonSponsorshipRepository,
|
||||
entitySponsorshipPricingPresenter
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -41,51 +41,54 @@ import type {
|
||||
JoinLeagueUseCase,
|
||||
RegisterForRaceUseCase,
|
||||
WithdrawFromRaceUseCase,
|
||||
IsDriverRegisteredForRaceQuery,
|
||||
GetRaceRegistrationsQuery,
|
||||
CreateTeamUseCase,
|
||||
JoinTeamUseCase,
|
||||
LeaveTeamUseCase,
|
||||
ApproveTeamJoinRequestUseCase,
|
||||
RejectTeamJoinRequestUseCase,
|
||||
UpdateTeamUseCase,
|
||||
GetAllTeamsQuery,
|
||||
GetTeamDetailsQuery,
|
||||
GetTeamMembersQuery,
|
||||
GetTeamJoinRequestsQuery,
|
||||
GetDriverTeamQuery,
|
||||
GetLeagueStandingsQuery,
|
||||
GetLeagueDriverSeasonStatsQuery,
|
||||
GetAllLeaguesWithCapacityQuery,
|
||||
GetAllLeaguesWithCapacityAndScoringQuery,
|
||||
ListLeagueScoringPresetsQuery,
|
||||
GetLeagueScoringConfigQuery,
|
||||
GetAllTeamsUseCase,
|
||||
GetTeamDetailsUseCase,
|
||||
GetTeamMembersUseCase,
|
||||
GetTeamJoinRequestsUseCase,
|
||||
GetDriverTeamUseCase,
|
||||
CreateLeagueWithSeasonAndScoringUseCase,
|
||||
GetLeagueFullConfigQuery,
|
||||
GetRaceWithSOFQuery,
|
||||
GetLeagueStatsQuery,
|
||||
FileProtestUseCase,
|
||||
ReviewProtestUseCase,
|
||||
ApplyPenaltyUseCase,
|
||||
GetRaceProtestsQuery,
|
||||
GetRacePenaltiesQuery,
|
||||
RequestProtestDefenseUseCase,
|
||||
SubmitProtestDefenseUseCase,
|
||||
GetSponsorDashboardQuery,
|
||||
GetSponsorSponsorshipsQuery,
|
||||
GetSponsorDashboardUseCase,
|
||||
GetSponsorSponsorshipsUseCase,
|
||||
ApplyForSponsorshipUseCase,
|
||||
AcceptSponsorshipRequestUseCase,
|
||||
RejectSponsorshipRequestUseCase,
|
||||
GetPendingSponsorshipRequestsQuery,
|
||||
GetEntitySponsorshipPricingQuery,
|
||||
GetPendingSponsorshipRequestsUseCase,
|
||||
GetEntitySponsorshipPricingUseCase,
|
||||
} from '@gridpilot/racing/application';
|
||||
import type { IsDriverRegisteredForRaceUseCase } from '@gridpilot/racing/application/use-cases/IsDriverRegisteredForRaceQuery';
|
||||
import type { GetRaceRegistrationsUseCase } from '@gridpilot/racing/application/use-cases/GetRaceRegistrationsQuery';
|
||||
import type { GetRaceWithSOFUseCase } from '@gridpilot/racing/application/use-cases/GetRaceWithSOFQuery';
|
||||
import type { GetRaceProtestsUseCase } from '@gridpilot/racing/application/use-cases/GetRaceProtestsQuery';
|
||||
import type { GetRacePenaltiesUseCase } from '@gridpilot/racing/application/use-cases/GetRacePenaltiesQuery';
|
||||
import type { GetRacesPageDataUseCase } from '@gridpilot/racing/application/use-cases/GetRacesPageDataUseCase';
|
||||
import type { GetDriversLeaderboardUseCase } from '@gridpilot/racing/application/use-cases/GetDriversLeaderboardUseCase';
|
||||
import type { GetTeamsLeaderboardUseCase } from '@gridpilot/racing/application/use-cases/GetTeamsLeaderboardUseCase';
|
||||
import type { GetLeagueStandingsUseCase } from '@gridpilot/racing/application/use-cases/GetLeagueStandingsQuery';
|
||||
import type { GetLeagueDriverSeasonStatsUseCase } from '@gridpilot/racing/application/use-cases/GetLeagueDriverSeasonStatsQuery';
|
||||
import type { GetAllLeaguesWithCapacityUseCase } from '@gridpilot/racing/application/use-cases/GetAllLeaguesWithCapacityQuery';
|
||||
import type { GetAllLeaguesWithCapacityAndScoringUseCase } from '@gridpilot/racing/application/use-cases/GetAllLeaguesWithCapacityAndScoringQuery';
|
||||
import type { ListLeagueScoringPresetsUseCase } from '@gridpilot/racing/application/use-cases/ListLeagueScoringPresetsQuery';
|
||||
import type { GetLeagueScoringConfigUseCase } from '@gridpilot/racing/application/use-cases/GetLeagueScoringConfigQuery';
|
||||
import type { GetLeagueFullConfigUseCase } from '@gridpilot/racing/application/use-cases/GetLeagueFullConfigQuery';
|
||||
import type { GetLeagueStatsUseCase } from '@gridpilot/racing/application/use-cases/GetLeagueStatsQuery';
|
||||
import type { ISponsorRepository } from '@gridpilot/racing/domain/repositories/ISponsorRepository';
|
||||
import type { ISeasonSponsorshipRepository } from '@gridpilot/racing/domain/repositories/ISeasonSponsorshipRepository';
|
||||
import type { ISponsorshipRequestRepository } from '@gridpilot/racing/domain/repositories/ISponsorshipRequestRepository';
|
||||
import type { ISponsorshipPricingRepository } from '@gridpilot/racing/domain/repositories/ISponsorshipPricingRepository';
|
||||
import type { TransferLeagueOwnershipUseCase } from '@gridpilot/racing/application/use-cases/TransferLeagueOwnershipUseCase';
|
||||
import type { DriverRatingProvider } from '@gridpilot/racing/application';
|
||||
import type { PreviewLeagueScheduleQuery } from '@gridpilot/racing/application';
|
||||
import type { PreviewLeagueScheduleUseCase } from '@gridpilot/racing/application';
|
||||
import type { LeagueScoringPresetProvider } from '@gridpilot/racing/application/ports/LeagueScoringPresetProvider';
|
||||
import { createDemoDriverStats, getDemoLeagueRankings, type DriverStats } from '@gridpilot/testing-support';
|
||||
|
||||
@@ -211,64 +214,79 @@ class DIContainer {
|
||||
return getDIContainer().resolve<WithdrawFromRaceUseCase>(DI_TOKENS.WithdrawFromRaceUseCase);
|
||||
}
|
||||
|
||||
get isDriverRegisteredForRaceQuery(): IsDriverRegisteredForRaceQuery {
|
||||
get isDriverRegisteredForRaceUseCase(): IsDriverRegisteredForRaceUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<IsDriverRegisteredForRaceQuery>(DI_TOKENS.IsDriverRegisteredForRaceQuery);
|
||||
return getDIContainer().resolve<IsDriverRegisteredForRaceUseCase>(DI_TOKENS.IsDriverRegisteredForRaceUseCase);
|
||||
}
|
||||
|
||||
get getRaceRegistrationsQuery(): GetRaceRegistrationsQuery {
|
||||
get getRaceRegistrationsUseCase(): GetRaceRegistrationsUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetRaceRegistrationsQuery>(DI_TOKENS.GetRaceRegistrationsQuery);
|
||||
return getDIContainer().resolve<GetRaceRegistrationsUseCase>(DI_TOKENS.GetRaceRegistrationsUseCase);
|
||||
}
|
||||
|
||||
get getLeagueStandingsQuery(): GetLeagueStandingsQuery {
|
||||
get getLeagueStandingsUseCase(): GetLeagueStandingsUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetLeagueStandingsQuery>(DI_TOKENS.GetLeagueStandingsQuery);
|
||||
return getDIContainer().resolve<GetLeagueStandingsUseCase>(DI_TOKENS.GetLeagueStandingsUseCase);
|
||||
}
|
||||
|
||||
get getLeagueDriverSeasonStatsQuery(): GetLeagueDriverSeasonStatsQuery {
|
||||
get getLeagueDriverSeasonStatsUseCase(): GetLeagueDriverSeasonStatsUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetLeagueDriverSeasonStatsQuery>(DI_TOKENS.GetLeagueDriverSeasonStatsQuery);
|
||||
return getDIContainer().resolve<GetLeagueDriverSeasonStatsUseCase>(DI_TOKENS.GetLeagueDriverSeasonStatsUseCase);
|
||||
}
|
||||
|
||||
get getAllLeaguesWithCapacityQuery(): GetAllLeaguesWithCapacityQuery {
|
||||
get getAllLeaguesWithCapacityUseCase(): GetAllLeaguesWithCapacityUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetAllLeaguesWithCapacityQuery>(DI_TOKENS.GetAllLeaguesWithCapacityQuery);
|
||||
return getDIContainer().resolve<GetAllLeaguesWithCapacityUseCase>(DI_TOKENS.GetAllLeaguesWithCapacityUseCase);
|
||||
}
|
||||
|
||||
get getAllLeaguesWithCapacityAndScoringQuery(): GetAllLeaguesWithCapacityAndScoringQuery {
|
||||
get getAllLeaguesWithCapacityAndScoringUseCase(): GetAllLeaguesWithCapacityAndScoringUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetAllLeaguesWithCapacityAndScoringQuery>(DI_TOKENS.GetAllLeaguesWithCapacityAndScoringQuery);
|
||||
return getDIContainer().resolve<GetAllLeaguesWithCapacityAndScoringUseCase>(DI_TOKENS.GetAllLeaguesWithCapacityAndScoringUseCase);
|
||||
}
|
||||
|
||||
get listLeagueScoringPresetsQuery(): ListLeagueScoringPresetsQuery {
|
||||
get listLeagueScoringPresetsUseCase(): ListLeagueScoringPresetsUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<ListLeagueScoringPresetsQuery>(DI_TOKENS.ListLeagueScoringPresetsQuery);
|
||||
return getDIContainer().resolve<ListLeagueScoringPresetsUseCase>(DI_TOKENS.ListLeagueScoringPresetsUseCase);
|
||||
}
|
||||
|
||||
get getLeagueScoringConfigQuery(): GetLeagueScoringConfigQuery {
|
||||
get getLeagueScoringConfigUseCase(): GetLeagueScoringConfigUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetLeagueScoringConfigQuery>(DI_TOKENS.GetLeagueScoringConfigQuery);
|
||||
return getDIContainer().resolve<GetLeagueScoringConfigUseCase>(DI_TOKENS.GetLeagueScoringConfigUseCase);
|
||||
}
|
||||
|
||||
get getLeagueFullConfigQuery(): GetLeagueFullConfigQuery {
|
||||
get getLeagueFullConfigUseCase(): GetLeagueFullConfigUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetLeagueFullConfigQuery>(DI_TOKENS.GetLeagueFullConfigQuery);
|
||||
return getDIContainer().resolve<GetLeagueFullConfigUseCase>(DI_TOKENS.GetLeagueFullConfigUseCase);
|
||||
}
|
||||
|
||||
get previewLeagueScheduleQuery(): PreviewLeagueScheduleQuery {
|
||||
get previewLeagueScheduleUseCase(): PreviewLeagueScheduleUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<PreviewLeagueScheduleQuery>(DI_TOKENS.PreviewLeagueScheduleQuery);
|
||||
return getDIContainer().resolve<PreviewLeagueScheduleUseCase>(DI_TOKENS.PreviewLeagueScheduleUseCase);
|
||||
}
|
||||
|
||||
get getRaceWithSOFQuery(): GetRaceWithSOFQuery {
|
||||
get getRaceWithSOFUseCase(): GetRaceWithSOFUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetRaceWithSOFQuery>(DI_TOKENS.GetRaceWithSOFQuery);
|
||||
return getDIContainer().resolve<GetRaceWithSOFUseCase>(DI_TOKENS.GetRaceWithSOFUseCase);
|
||||
}
|
||||
|
||||
get getLeagueStatsQuery(): GetLeagueStatsQuery {
|
||||
get getLeagueStatsUseCase(): GetLeagueStatsUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetLeagueStatsQuery>(DI_TOKENS.GetLeagueStatsQuery);
|
||||
return getDIContainer().resolve<GetLeagueStatsUseCase>(DI_TOKENS.GetLeagueStatsUseCase);
|
||||
}
|
||||
|
||||
get getRacesPageDataUseCase(): GetRacesPageDataUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetRacesPageDataUseCase>(DI_TOKENS.GetRacesPageDataUseCase);
|
||||
}
|
||||
|
||||
get getDriversLeaderboardUseCase(): GetDriversLeaderboardUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetDriversLeaderboardUseCase>(DI_TOKENS.GetDriversLeaderboardUseCase);
|
||||
}
|
||||
|
||||
get getTeamsLeaderboardUseCase(): GetTeamsLeaderboardUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetTeamsLeaderboardUseCase>(DI_TOKENS.GetTeamsLeaderboardUseCase);
|
||||
}
|
||||
|
||||
get driverRatingProvider(): DriverRatingProvider {
|
||||
@@ -311,29 +329,29 @@ class DIContainer {
|
||||
return getDIContainer().resolve<UpdateTeamUseCase>(DI_TOKENS.UpdateTeamUseCase);
|
||||
}
|
||||
|
||||
get getAllTeamsQuery(): GetAllTeamsQuery {
|
||||
get getAllTeamsUseCase(): GetAllTeamsUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetAllTeamsQuery>(DI_TOKENS.GetAllTeamsQuery);
|
||||
return getDIContainer().resolve<GetAllTeamsUseCase>(DI_TOKENS.GetAllTeamsUseCase);
|
||||
}
|
||||
|
||||
get getTeamDetailsQuery(): GetTeamDetailsQuery {
|
||||
get getTeamDetailsUseCase(): GetTeamDetailsUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetTeamDetailsQuery>(DI_TOKENS.GetTeamDetailsQuery);
|
||||
return getDIContainer().resolve<GetTeamDetailsUseCase>(DI_TOKENS.GetTeamDetailsUseCase);
|
||||
}
|
||||
|
||||
get getTeamMembersQuery(): GetTeamMembersQuery {
|
||||
get getTeamMembersUseCase(): GetTeamMembersUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetTeamMembersQuery>(DI_TOKENS.GetTeamMembersQuery);
|
||||
return getDIContainer().resolve<GetTeamMembersUseCase>(DI_TOKENS.GetTeamMembersUseCase);
|
||||
}
|
||||
|
||||
get getTeamJoinRequestsQuery(): GetTeamJoinRequestsQuery {
|
||||
get getTeamJoinRequestsUseCase(): GetTeamJoinRequestsUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetTeamJoinRequestsQuery>(DI_TOKENS.GetTeamJoinRequestsQuery);
|
||||
return getDIContainer().resolve<GetTeamJoinRequestsUseCase>(DI_TOKENS.GetTeamJoinRequestsUseCase);
|
||||
}
|
||||
|
||||
get getDriverTeamQuery(): GetDriverTeamQuery {
|
||||
get getDriverTeamUseCase(): GetDriverTeamUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetDriverTeamQuery>(DI_TOKENS.GetDriverTeamQuery);
|
||||
return getDIContainer().resolve<GetDriverTeamUseCase>(DI_TOKENS.GetDriverTeamUseCase);
|
||||
}
|
||||
|
||||
get teamRepository(): ITeamRepository {
|
||||
@@ -411,14 +429,14 @@ class DIContainer {
|
||||
return getDIContainer().resolve<ApplyPenaltyUseCase>(DI_TOKENS.ApplyPenaltyUseCase);
|
||||
}
|
||||
|
||||
get getRaceProtestsQuery(): GetRaceProtestsQuery {
|
||||
get getRaceProtestsUseCase(): GetRaceProtestsUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetRaceProtestsQuery>(DI_TOKENS.GetRaceProtestsQuery);
|
||||
return getDIContainer().resolve<GetRaceProtestsUseCase>(DI_TOKENS.GetRaceProtestsUseCase);
|
||||
}
|
||||
|
||||
get getRacePenaltiesQuery(): GetRacePenaltiesQuery {
|
||||
get getRacePenaltiesUseCase(): GetRacePenaltiesUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetRacePenaltiesQuery>(DI_TOKENS.GetRacePenaltiesQuery);
|
||||
return getDIContainer().resolve<GetRacePenaltiesUseCase>(DI_TOKENS.GetRacePenaltiesUseCase);
|
||||
}
|
||||
|
||||
get requestProtestDefenseUseCase(): RequestProtestDefenseUseCase {
|
||||
@@ -446,14 +464,14 @@ class DIContainer {
|
||||
return getDIContainer().resolve<ISeasonSponsorshipRepository>(DI_TOKENS.SeasonSponsorshipRepository);
|
||||
}
|
||||
|
||||
get getSponsorDashboardQuery(): GetSponsorDashboardQuery {
|
||||
get getSponsorDashboardUseCase(): GetSponsorDashboardUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetSponsorDashboardQuery>(DI_TOKENS.GetSponsorDashboardQuery);
|
||||
return getDIContainer().resolve<GetSponsorDashboardUseCase>(DI_TOKENS.GetSponsorDashboardUseCase);
|
||||
}
|
||||
|
||||
get getSponsorSponsorshipsQuery(): GetSponsorSponsorshipsQuery {
|
||||
get getSponsorSponsorshipsUseCase(): GetSponsorSponsorshipsUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetSponsorSponsorshipsQuery>(DI_TOKENS.GetSponsorSponsorshipsQuery);
|
||||
return getDIContainer().resolve<GetSponsorSponsorshipsUseCase>(DI_TOKENS.GetSponsorSponsorshipsUseCase);
|
||||
}
|
||||
|
||||
get sponsorshipRequestRepository(): ISponsorshipRequestRepository {
|
||||
@@ -481,14 +499,14 @@ class DIContainer {
|
||||
return getDIContainer().resolve<RejectSponsorshipRequestUseCase>(DI_TOKENS.RejectSponsorshipRequestUseCase);
|
||||
}
|
||||
|
||||
get getPendingSponsorshipRequestsQuery(): GetPendingSponsorshipRequestsQuery {
|
||||
get getPendingSponsorshipRequestsUseCase(): GetPendingSponsorshipRequestsUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetPendingSponsorshipRequestsQuery>(DI_TOKENS.GetPendingSponsorshipRequestsQuery);
|
||||
return getDIContainer().resolve<GetPendingSponsorshipRequestsUseCase>(DI_TOKENS.GetPendingSponsorshipRequestsUseCase);
|
||||
}
|
||||
|
||||
get getEntitySponsorshipPricingQuery(): GetEntitySponsorshipPricingQuery {
|
||||
get getEntitySponsorshipPricingUseCase(): GetEntitySponsorshipPricingUseCase {
|
||||
this.ensureInitialized();
|
||||
return getDIContainer().resolve<GetEntitySponsorshipPricingQuery>(DI_TOKENS.GetEntitySponsorshipPricingQuery);
|
||||
return getDIContainer().resolve<GetEntitySponsorshipPricingUseCase>(DI_TOKENS.GetEntitySponsorshipPricingUseCase);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,57 +561,68 @@ export function getWithdrawFromRaceUseCase(): WithdrawFromRaceUseCase {
|
||||
return DIContainer.getInstance().withdrawFromRaceUseCase;
|
||||
}
|
||||
|
||||
export function getIsDriverRegisteredForRaceQuery(): IsDriverRegisteredForRaceQuery {
|
||||
return DIContainer.getInstance().isDriverRegisteredForRaceQuery;
|
||||
export function getIsDriverRegisteredForRaceUseCase(): IsDriverRegisteredForRaceUseCase {
|
||||
return DIContainer.getInstance().isDriverRegisteredForRaceUseCase;
|
||||
}
|
||||
|
||||
export function getGetRaceRegistrationsQuery(): GetRaceRegistrationsQuery {
|
||||
return DIContainer.getInstance().getRaceRegistrationsQuery;
|
||||
export function getGetRaceRegistrationsUseCase(): GetRaceRegistrationsUseCase {
|
||||
return DIContainer.getInstance().getRaceRegistrationsUseCase;
|
||||
}
|
||||
|
||||
export function getGetLeagueStandingsQuery(): GetLeagueStandingsQuery {
|
||||
return DIContainer.getInstance().getLeagueStandingsQuery;
|
||||
export function getGetLeagueStandingsUseCase(): GetLeagueStandingsUseCase {
|
||||
return DIContainer.getInstance().getLeagueStandingsUseCase;
|
||||
}
|
||||
|
||||
export function getGetLeagueDriverSeasonStatsQuery(): GetLeagueDriverSeasonStatsQuery {
|
||||
return DIContainer.getInstance().getLeagueDriverSeasonStatsQuery;
|
||||
export function getGetLeagueDriverSeasonStatsUseCase(): GetLeagueDriverSeasonStatsUseCase {
|
||||
return DIContainer.getInstance().getLeagueDriverSeasonStatsUseCase;
|
||||
}
|
||||
|
||||
export function getGetAllLeaguesWithCapacityQuery(): GetAllLeaguesWithCapacityQuery {
|
||||
return DIContainer.getInstance().getAllLeaguesWithCapacityQuery;
|
||||
export function getGetAllLeaguesWithCapacityUseCase(): GetAllLeaguesWithCapacityUseCase {
|
||||
return DIContainer.getInstance().getAllLeaguesWithCapacityUseCase;
|
||||
}
|
||||
|
||||
export function getGetAllLeaguesWithCapacityAndScoringQuery(): GetAllLeaguesWithCapacityAndScoringQuery {
|
||||
return DIContainer.getInstance().getAllLeaguesWithCapacityAndScoringQuery;
|
||||
export function getGetAllLeaguesWithCapacityAndScoringUseCase(): GetAllLeaguesWithCapacityAndScoringUseCase {
|
||||
return DIContainer.getInstance().getAllLeaguesWithCapacityAndScoringUseCase;
|
||||
}
|
||||
|
||||
export function getGetLeagueScoringConfigQuery(): GetLeagueScoringConfigQuery {
|
||||
return DIContainer.getInstance().getLeagueScoringConfigQuery;
|
||||
export function getGetLeagueScoringConfigUseCase(): GetLeagueScoringConfigUseCase {
|
||||
return DIContainer.getInstance().getLeagueScoringConfigUseCase;
|
||||
}
|
||||
|
||||
export function getGetLeagueFullConfigQuery(): GetLeagueFullConfigQuery {
|
||||
return DIContainer.getInstance().getLeagueFullConfigQuery;
|
||||
export function getGetLeagueFullConfigUseCase(): GetLeagueFullConfigUseCase {
|
||||
return DIContainer.getInstance().getLeagueFullConfigUseCase;
|
||||
}
|
||||
|
||||
// Placeholder export for future schedule preview API wiring.
|
||||
export function getPreviewLeagueScheduleQuery(): PreviewLeagueScheduleQuery {
|
||||
return DIContainer.getInstance().previewLeagueScheduleQuery;
|
||||
export function getPreviewLeagueScheduleUseCase(): PreviewLeagueScheduleUseCase {
|
||||
return DIContainer.getInstance().previewLeagueScheduleUseCase;
|
||||
}
|
||||
|
||||
export function getListLeagueScoringPresetsQuery(): ListLeagueScoringPresetsQuery {
|
||||
return DIContainer.getInstance().listLeagueScoringPresetsQuery;
|
||||
export function getListLeagueScoringPresetsUseCase(): ListLeagueScoringPresetsUseCase {
|
||||
return DIContainer.getInstance().listLeagueScoringPresetsUseCase;
|
||||
}
|
||||
|
||||
export function getCreateLeagueWithSeasonAndScoringUseCase(): CreateLeagueWithSeasonAndScoringUseCase {
|
||||
return DIContainer.getInstance().createLeagueWithSeasonAndScoringUseCase;
|
||||
}
|
||||
|
||||
export function getGetRaceWithSOFQuery(): GetRaceWithSOFQuery {
|
||||
return DIContainer.getInstance().getRaceWithSOFQuery;
|
||||
export function getGetRaceWithSOFUseCase(): GetRaceWithSOFUseCase {
|
||||
return DIContainer.getInstance().getRaceWithSOFUseCase;
|
||||
}
|
||||
|
||||
export function getGetLeagueStatsQuery(): GetLeagueStatsQuery {
|
||||
return DIContainer.getInstance().getLeagueStatsQuery;
|
||||
export function getGetLeagueStatsUseCase(): GetLeagueStatsUseCase {
|
||||
return DIContainer.getInstance().getLeagueStatsUseCase;
|
||||
}
|
||||
|
||||
export function getGetRacesPageDataUseCase(): GetRacesPageDataUseCase {
|
||||
return DIContainer.getInstance().getRacesPageDataUseCase;
|
||||
}
|
||||
|
||||
export function getGetDriversLeaderboardUseCase(): GetDriversLeaderboardUseCase {
|
||||
return DIContainer.getInstance().getDriversLeaderboardUseCase;
|
||||
}
|
||||
|
||||
export function getGetTeamsLeaderboardUseCase(): GetTeamsLeaderboardUseCase {
|
||||
return DIContainer.getInstance().getTeamsLeaderboardUseCase;
|
||||
}
|
||||
|
||||
export function getDriverRatingProvider(): DriverRatingProvider {
|
||||
@@ -632,24 +661,24 @@ export function getUpdateTeamUseCase(): UpdateTeamUseCase {
|
||||
return DIContainer.getInstance().updateTeamUseCase;
|
||||
}
|
||||
|
||||
export function getGetAllTeamsQuery(): GetAllTeamsQuery {
|
||||
return DIContainer.getInstance().getAllTeamsQuery;
|
||||
export function getGetAllTeamsUseCase(): GetAllTeamsUseCase {
|
||||
return DIContainer.getInstance().getAllTeamsUseCase;
|
||||
}
|
||||
|
||||
export function getGetTeamDetailsQuery(): GetTeamDetailsQuery {
|
||||
return DIContainer.getInstance().getTeamDetailsQuery;
|
||||
export function getGetTeamDetailsUseCase(): GetTeamDetailsUseCase {
|
||||
return DIContainer.getInstance().getTeamDetailsUseCase;
|
||||
}
|
||||
|
||||
export function getGetTeamMembersQuery(): GetTeamMembersQuery {
|
||||
return DIContainer.getInstance().getTeamMembersQuery;
|
||||
export function getGetTeamMembersUseCase(): GetTeamMembersUseCase {
|
||||
return DIContainer.getInstance().getTeamMembersUseCase;
|
||||
}
|
||||
|
||||
export function getGetTeamJoinRequestsQuery(): GetTeamJoinRequestsQuery {
|
||||
return DIContainer.getInstance().getTeamJoinRequestsQuery;
|
||||
export function getGetTeamJoinRequestsUseCase(): GetTeamJoinRequestsUseCase {
|
||||
return DIContainer.getInstance().getTeamJoinRequestsUseCase;
|
||||
}
|
||||
|
||||
export function getGetDriverTeamQuery(): GetDriverTeamQuery {
|
||||
return DIContainer.getInstance().getDriverTeamQuery;
|
||||
export function getGetDriverTeamUseCase(): GetDriverTeamUseCase {
|
||||
return DIContainer.getInstance().getDriverTeamUseCase;
|
||||
}
|
||||
|
||||
export function getFeedRepository(): IFeedRepository {
|
||||
@@ -708,12 +737,12 @@ export function getApplyPenaltyUseCase(): ApplyPenaltyUseCase {
|
||||
return DIContainer.getInstance().applyPenaltyUseCase;
|
||||
}
|
||||
|
||||
export function getGetRaceProtestsQuery(): GetRaceProtestsQuery {
|
||||
return DIContainer.getInstance().getRaceProtestsQuery;
|
||||
export function getGetRaceProtestsUseCase(): GetRaceProtestsUseCase {
|
||||
return DIContainer.getInstance().getRaceProtestsUseCase;
|
||||
}
|
||||
|
||||
export function getGetRacePenaltiesQuery(): GetRacePenaltiesQuery {
|
||||
return DIContainer.getInstance().getRacePenaltiesQuery;
|
||||
export function getGetRacePenaltiesUseCase(): GetRacePenaltiesUseCase {
|
||||
return DIContainer.getInstance().getRacePenaltiesUseCase;
|
||||
}
|
||||
|
||||
export function getRequestProtestDefenseUseCase(): RequestProtestDefenseUseCase {
|
||||
@@ -736,12 +765,12 @@ export function getSeasonSponsorshipRepository(): ISeasonSponsorshipRepository {
|
||||
return DIContainer.getInstance().seasonSponsorshipRepository;
|
||||
}
|
||||
|
||||
export function getGetSponsorDashboardQuery(): GetSponsorDashboardQuery {
|
||||
return DIContainer.getInstance().getSponsorDashboardQuery;
|
||||
export function getGetSponsorDashboardUseCase(): GetSponsorDashboardUseCase {
|
||||
return DIContainer.getInstance().getSponsorDashboardUseCase;
|
||||
}
|
||||
|
||||
export function getGetSponsorSponsorshipsQuery(): GetSponsorSponsorshipsQuery {
|
||||
return DIContainer.getInstance().getSponsorSponsorshipsQuery;
|
||||
export function getGetSponsorSponsorshipsUseCase(): GetSponsorSponsorshipsUseCase {
|
||||
return DIContainer.getInstance().getSponsorSponsorshipsUseCase;
|
||||
}
|
||||
|
||||
export function getSponsorshipRequestRepository(): ISponsorshipRequestRepository {
|
||||
@@ -764,12 +793,12 @@ export function getRejectSponsorshipRequestUseCase(): RejectSponsorshipRequestUs
|
||||
return DIContainer.getInstance().rejectSponsorshipRequestUseCase;
|
||||
}
|
||||
|
||||
export function getGetPendingSponsorshipRequestsQuery(): GetPendingSponsorshipRequestsQuery {
|
||||
return DIContainer.getInstance().getPendingSponsorshipRequestsQuery;
|
||||
export function getGetPendingSponsorshipRequestsUseCase(): GetPendingSponsorshipRequestsUseCase {
|
||||
return DIContainer.getInstance().getPendingSponsorshipRequestsUseCase;
|
||||
}
|
||||
|
||||
export function getGetEntitySponsorshipPricingQuery(): GetEntitySponsorshipPricingQuery {
|
||||
return DIContainer.getInstance().getEntitySponsorshipPricingQuery;
|
||||
export function getGetEntitySponsorshipPricingUseCase(): GetEntitySponsorshipPricingUseCase {
|
||||
return DIContainer.getInstance().getEntitySponsorshipPricingUseCase;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -64,38 +64,41 @@ export const DI_TOKENS = {
|
||||
MarkNotificationReadUseCase: Symbol.for('MarkNotificationReadUseCase'),
|
||||
|
||||
// Queries - Racing
|
||||
IsDriverRegisteredForRaceQuery: Symbol.for('IsDriverRegisteredForRaceQuery'),
|
||||
GetRaceRegistrationsQuery: Symbol.for('GetRaceRegistrationsQuery'),
|
||||
GetLeagueStandingsQuery: Symbol.for('GetLeagueStandingsQuery'),
|
||||
GetLeagueDriverSeasonStatsQuery: Symbol.for('GetLeagueDriverSeasonStatsQuery'),
|
||||
GetAllLeaguesWithCapacityQuery: Symbol.for('GetAllLeaguesWithCapacityQuery'),
|
||||
GetAllLeaguesWithCapacityAndScoringQuery: Symbol.for('GetAllLeaguesWithCapacityAndScoringQuery'),
|
||||
ListLeagueScoringPresetsQuery: Symbol.for('ListLeagueScoringPresetsQuery'),
|
||||
GetLeagueScoringConfigQuery: Symbol.for('GetLeagueScoringConfigQuery'),
|
||||
GetLeagueFullConfigQuery: Symbol.for('GetLeagueFullConfigQuery'),
|
||||
PreviewLeagueScheduleQuery: Symbol.for('PreviewLeagueScheduleQuery'),
|
||||
GetRaceWithSOFQuery: Symbol.for('GetRaceWithSOFQuery'),
|
||||
GetLeagueStatsQuery: Symbol.for('GetLeagueStatsQuery'),
|
||||
IsDriverRegisteredForRaceUseCase: Symbol.for('IsDriverRegisteredForRaceUseCase'),
|
||||
GetRaceRegistrationsUseCase: Symbol.for('GetRaceRegistrationsUseCase'),
|
||||
GetLeagueStandingsUseCase: Symbol.for('GetLeagueStandingsUseCase'),
|
||||
GetLeagueDriverSeasonStatsUseCase: Symbol.for('GetLeagueDriverSeasonStatsUseCase'),
|
||||
GetAllLeaguesWithCapacityUseCase: Symbol.for('GetAllLeaguesWithCapacityUseCase'),
|
||||
GetAllLeaguesWithCapacityAndScoringUseCase: Symbol.for('GetAllLeaguesWithCapacityAndScoringUseCase'),
|
||||
ListLeagueScoringPresetsUseCase: Symbol.for('ListLeagueScoringPresetsUseCase'),
|
||||
GetLeagueScoringConfigUseCase: Symbol.for('GetLeagueScoringConfigUseCase'),
|
||||
GetLeagueFullConfigUseCase: Symbol.for('GetLeagueFullConfigUseCase'),
|
||||
PreviewLeagueScheduleUseCase: Symbol.for('PreviewLeagueScheduleUseCase'),
|
||||
GetRaceWithSOFUseCase: Symbol.for('GetRaceWithSOFUseCase'),
|
||||
GetLeagueStatsUseCase: Symbol.for('GetLeagueStatsUseCase'),
|
||||
GetRacesPageDataUseCase: Symbol.for('GetRacesPageDataUseCase'),
|
||||
GetDriversLeaderboardUseCase: Symbol.for('GetDriversLeaderboardUseCase'),
|
||||
GetTeamsLeaderboardUseCase: Symbol.for('GetTeamsLeaderboardUseCase'),
|
||||
|
||||
// Queries - Teams
|
||||
GetAllTeamsQuery: Symbol.for('GetAllTeamsQuery'),
|
||||
GetTeamDetailsQuery: Symbol.for('GetTeamDetailsQuery'),
|
||||
GetTeamMembersQuery: Symbol.for('GetTeamMembersQuery'),
|
||||
GetTeamJoinRequestsQuery: Symbol.for('GetTeamJoinRequestsQuery'),
|
||||
GetDriverTeamQuery: Symbol.for('GetDriverTeamQuery'),
|
||||
// Use Cases - Teams (Query-like)
|
||||
GetAllTeamsUseCase: Symbol.for('GetAllTeamsUseCase'),
|
||||
GetTeamDetailsUseCase: Symbol.for('GetTeamDetailsUseCase'),
|
||||
GetTeamMembersUseCase: Symbol.for('GetTeamMembersUseCase'),
|
||||
GetTeamJoinRequestsUseCase: Symbol.for('GetTeamJoinRequestsUseCase'),
|
||||
GetDriverTeamUseCase: Symbol.for('GetDriverTeamUseCase'),
|
||||
|
||||
// Queries - Stewarding
|
||||
GetRaceProtestsQuery: Symbol.for('GetRaceProtestsQuery'),
|
||||
GetRacePenaltiesQuery: Symbol.for('GetRacePenaltiesQuery'),
|
||||
GetRaceProtestsUseCase: Symbol.for('GetRaceProtestsUseCase'),
|
||||
GetRacePenaltiesUseCase: Symbol.for('GetRacePenaltiesUseCase'),
|
||||
|
||||
// Queries - Notifications
|
||||
GetUnreadNotificationsQuery: Symbol.for('GetUnreadNotificationsQuery'),
|
||||
|
||||
// Queries - Sponsors
|
||||
GetSponsorDashboardQuery: Symbol.for('GetSponsorDashboardQuery'),
|
||||
GetSponsorSponsorshipsQuery: Symbol.for('GetSponsorSponsorshipsQuery'),
|
||||
GetPendingSponsorshipRequestsQuery: Symbol.for('GetPendingSponsorshipRequestsQuery'),
|
||||
GetEntitySponsorshipPricingQuery: Symbol.for('GetEntitySponsorshipPricingQuery'),
|
||||
// Use Cases - Sponsors
|
||||
GetSponsorDashboardUseCase: Symbol.for('GetSponsorDashboardUseCase'),
|
||||
GetSponsorSponsorshipsUseCase: Symbol.for('GetSponsorSponsorshipsUseCase'),
|
||||
GetPendingSponsorshipRequestsUseCase: Symbol.for('GetPendingSponsorshipRequestsUseCase'),
|
||||
GetEntitySponsorshipPricingUseCase: Symbol.for('GetEntitySponsorshipPricingUseCase'),
|
||||
|
||||
// Use Cases - Sponsorship
|
||||
ApplyForSponsorshipUseCase: Symbol.for('ApplyForSponsorshipUseCase'),
|
||||
@@ -104,6 +107,20 @@ export const DI_TOKENS = {
|
||||
|
||||
// Data
|
||||
DriverStats: Symbol.for('DriverStats'),
|
||||
|
||||
// Presenters - Racing
|
||||
RaceWithSOFPresenter: Symbol.for('IRaceWithSOFPresenter'),
|
||||
RaceProtestsPresenter: Symbol.for('IRaceProtestsPresenter'),
|
||||
RacePenaltiesPresenter: Symbol.for('IRacePenaltiesPresenter'),
|
||||
RaceRegistrationsPresenter: Symbol.for('IRaceRegistrationsPresenter'),
|
||||
DriverRegistrationStatusPresenter: Symbol.for('IDriverRegistrationStatusPresenter'),
|
||||
|
||||
// Presenters - Sponsors
|
||||
SponsorDashboardPresenter: Symbol.for('ISponsorDashboardPresenter'),
|
||||
SponsorSponsorshipsPresenter: Symbol.for('ISponsorSponsorshipsPresenter'),
|
||||
PendingSponsorshipRequestsPresenter: Symbol.for('IPendingSponsorshipRequestsPresenter'),
|
||||
EntitySponsorshipPricingPresenter: Symbol.for('IEntitySponsorshipPricingPresenter'),
|
||||
LeagueSchedulePreviewPresenter: Symbol.for('ILeagueSchedulePreviewPresenter'),
|
||||
} as const;
|
||||
|
||||
export type DITokens = typeof DI_TOKENS;
|
||||
@@ -0,0 +1,112 @@
|
||||
import type { League } from '@gridpilot/racing/domain/entities/League';
|
||||
import type {
|
||||
IAllLeaguesWithCapacityAndScoringPresenter,
|
||||
LeagueEnrichedData,
|
||||
LeagueSummaryViewModel,
|
||||
AllLeaguesWithCapacityAndScoringViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/IAllLeaguesWithCapacityAndScoringPresenter';
|
||||
|
||||
export class AllLeaguesWithCapacityAndScoringPresenter implements IAllLeaguesWithCapacityAndScoringPresenter {
|
||||
private viewModel: AllLeaguesWithCapacityAndScoringViewModel | null = null;
|
||||
|
||||
present(enrichedLeagues: LeagueEnrichedData[]): AllLeaguesWithCapacityAndScoringViewModel {
|
||||
const leagueItems: LeagueSummaryViewModel[] = enrichedLeagues.map((data) => {
|
||||
const { league, usedDriverSlots, season, scoringConfig, game, preset } = data;
|
||||
|
||||
const configuredMaxDrivers = league.settings.maxDrivers ?? usedDriverSlots;
|
||||
const safeMaxDrivers = Math.max(configuredMaxDrivers, usedDriverSlots);
|
||||
|
||||
const structureSummary = `Solo • ${safeMaxDrivers} drivers`;
|
||||
|
||||
const qualifyingMinutes = 30;
|
||||
const mainRaceMinutes =
|
||||
typeof league.settings.sessionDuration === 'number'
|
||||
? league.settings.sessionDuration
|
||||
: 40;
|
||||
const timingSummary = `${qualifyingMinutes} min Quali • ${mainRaceMinutes} min Race`;
|
||||
|
||||
let scoringSummary: LeagueSummaryViewModel['scoring'] | undefined;
|
||||
let scoringPatternSummary: string | undefined;
|
||||
|
||||
if (season && scoringConfig && game) {
|
||||
const dropPolicySummary =
|
||||
preset?.dropPolicySummary ?? this.deriveDropPolicySummary(scoringConfig);
|
||||
const primaryChampionshipType =
|
||||
preset?.primaryChampionshipType ??
|
||||
(scoringConfig.championships[0]?.type ?? 'driver');
|
||||
|
||||
const scoringPresetName = preset?.name ?? 'Custom';
|
||||
scoringPatternSummary = `${scoringPresetName} • ${dropPolicySummary}`;
|
||||
|
||||
scoringSummary = {
|
||||
gameId: game.id,
|
||||
gameName: game.name,
|
||||
primaryChampionshipType,
|
||||
scoringPresetId: scoringConfig.scoringPresetId ?? 'custom',
|
||||
scoringPresetName,
|
||||
dropPolicySummary,
|
||||
scoringPatternSummary,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id: league.id,
|
||||
name: league.name,
|
||||
description: league.description,
|
||||
ownerId: league.ownerId,
|
||||
createdAt: league.createdAt,
|
||||
maxDrivers: safeMaxDrivers,
|
||||
usedDriverSlots,
|
||||
maxTeams: undefined,
|
||||
usedTeamSlots: undefined,
|
||||
structureSummary,
|
||||
scoringPatternSummary,
|
||||
timingSummary,
|
||||
scoring: scoringSummary,
|
||||
};
|
||||
});
|
||||
|
||||
this.viewModel = {
|
||||
leagues: leagueItems,
|
||||
totalCount: leagueItems.length,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): AllLeaguesWithCapacityAndScoringViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
private deriveDropPolicySummary(config: {
|
||||
championships: Array<{
|
||||
dropScorePolicy: { strategy: string; count?: number; dropCount?: number };
|
||||
}>;
|
||||
}): string {
|
||||
const championship = config.championships[0];
|
||||
if (!championship) {
|
||||
return 'All results count';
|
||||
}
|
||||
|
||||
const policy = championship.dropScorePolicy;
|
||||
if (!policy || policy.strategy === 'none') {
|
||||
return 'All results count';
|
||||
}
|
||||
|
||||
if (policy.strategy === 'bestNResults' && typeof policy.count === 'number') {
|
||||
return `Best ${policy.count} results count`;
|
||||
}
|
||||
|
||||
if (
|
||||
policy.strategy === 'dropWorstN' &&
|
||||
typeof policy.dropCount === 'number'
|
||||
) {
|
||||
return `Worst ${policy.dropCount} results are dropped`;
|
||||
}
|
||||
|
||||
return 'Custom drop score rules';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import type { League } from '@gridpilot/racing/domain/entities/League';
|
||||
import type {
|
||||
IAllLeaguesWithCapacityPresenter,
|
||||
LeagueWithCapacityViewModel,
|
||||
AllLeaguesWithCapacityViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/IAllLeaguesWithCapacityPresenter';
|
||||
|
||||
export class AllLeaguesWithCapacityPresenter implements IAllLeaguesWithCapacityPresenter {
|
||||
private viewModel: AllLeaguesWithCapacityViewModel | null = null;
|
||||
|
||||
present(
|
||||
leagues: League[],
|
||||
memberCounts: Map<string, number>
|
||||
): AllLeaguesWithCapacityViewModel {
|
||||
const leagueItems: LeagueWithCapacityViewModel[] = leagues.map((league) => {
|
||||
const usedSlots = memberCounts.get(league.id) ?? 0;
|
||||
|
||||
// Ensure we never expose an impossible state like 26/24:
|
||||
// clamp maxDrivers to at least usedSlots at the application boundary.
|
||||
const configuredMax = league.settings.maxDrivers ?? usedSlots;
|
||||
const safeMaxDrivers = Math.max(configuredMax, usedSlots);
|
||||
|
||||
return {
|
||||
id: league.id,
|
||||
name: league.name,
|
||||
description: league.description,
|
||||
ownerId: league.ownerId,
|
||||
settings: {
|
||||
...league.settings,
|
||||
maxDrivers: safeMaxDrivers,
|
||||
},
|
||||
createdAt: league.createdAt.toISOString(),
|
||||
socialLinks: league.socialLinks
|
||||
? {
|
||||
discordUrl: league.socialLinks.discordUrl,
|
||||
youtubeUrl: league.socialLinks.youtubeUrl,
|
||||
websiteUrl: league.socialLinks.websiteUrl,
|
||||
}
|
||||
: undefined,
|
||||
usedSlots,
|
||||
};
|
||||
});
|
||||
|
||||
this.viewModel = {
|
||||
leagues: leagueItems,
|
||||
totalCount: leagueItems.length,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): AllLeaguesWithCapacityViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
38
apps/website/lib/presenters/AllTeamsPresenter.ts
Normal file
38
apps/website/lib/presenters/AllTeamsPresenter.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { Team } from '@gridpilot/racing/domain/entities/Team';
|
||||
import type {
|
||||
IAllTeamsPresenter,
|
||||
TeamListItemViewModel,
|
||||
AllTeamsViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/IAllTeamsPresenter';
|
||||
|
||||
export class AllTeamsPresenter implements IAllTeamsPresenter {
|
||||
private viewModel: AllTeamsViewModel | null = null;
|
||||
|
||||
present(teams: Array<Team & { memberCount?: number }>): AllTeamsViewModel {
|
||||
const teamItems: TeamListItemViewModel[] = teams.map((team) => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
tag: team.tag,
|
||||
description: team.description,
|
||||
memberCount: team.memberCount ?? 0,
|
||||
leagues: team.leagues,
|
||||
specialization: team.specialization as 'endurance' | 'sprint' | 'mixed' | undefined,
|
||||
region: team.region,
|
||||
languages: team.languages,
|
||||
}));
|
||||
|
||||
this.viewModel = {
|
||||
teams: teamItems,
|
||||
totalCount: teamItems.length,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): AllTeamsViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import type {
|
||||
IDriverRegistrationStatusPresenter,
|
||||
DriverRegistrationStatusViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/IDriverRegistrationStatusPresenter';
|
||||
|
||||
export class DriverRegistrationStatusPresenter implements IDriverRegistrationStatusPresenter {
|
||||
private viewModel: DriverRegistrationStatusViewModel | null = null;
|
||||
|
||||
present(
|
||||
isRegistered: boolean,
|
||||
raceId: string,
|
||||
driverId: string
|
||||
): DriverRegistrationStatusViewModel {
|
||||
this.viewModel = {
|
||||
isRegistered,
|
||||
raceId,
|
||||
driverId,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): DriverRegistrationStatusViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
48
apps/website/lib/presenters/DriverTeamPresenter.ts
Normal file
48
apps/website/lib/presenters/DriverTeamPresenter.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { Team, TeamMembership } from '@gridpilot/racing/domain/entities/Team';
|
||||
import type {
|
||||
IDriverTeamPresenter,
|
||||
DriverTeamViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/IDriverTeamPresenter';
|
||||
|
||||
export class DriverTeamPresenter implements IDriverTeamPresenter {
|
||||
private viewModel: DriverTeamViewModel | null = null;
|
||||
|
||||
present(
|
||||
team: Team,
|
||||
membership: TeamMembership,
|
||||
driverId: string
|
||||
): DriverTeamViewModel {
|
||||
const isOwner = team.ownerId === driverId;
|
||||
const canManage = membership.role === 'owner' || membership.role === 'manager';
|
||||
|
||||
this.viewModel = {
|
||||
team: {
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
tag: team.tag,
|
||||
description: team.description,
|
||||
ownerId: team.ownerId,
|
||||
leagues: team.leagues,
|
||||
specialization: team.specialization as 'endurance' | 'sprint' | 'mixed' | undefined,
|
||||
region: team.region,
|
||||
languages: team.languages,
|
||||
},
|
||||
membership: {
|
||||
role: membership.role,
|
||||
joinedAt: membership.joinedAt.toISOString(),
|
||||
isActive: membership.isActive,
|
||||
},
|
||||
isOwner,
|
||||
canManage,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): DriverTeamViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
81
apps/website/lib/presenters/DriversLeaderboardPresenter.ts
Normal file
81
apps/website/lib/presenters/DriversLeaderboardPresenter.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import type { Driver } from '@gridpilot/racing/domain/entities/Driver';
|
||||
import type { SkillLevel } from '@gridpilot/racing/domain/services/SkillLevelService';
|
||||
import { SkillLevelService } from '@gridpilot/racing/domain/services/SkillLevelService';
|
||||
import type {
|
||||
IDriversLeaderboardPresenter,
|
||||
DriverLeaderboardItemViewModel,
|
||||
DriversLeaderboardViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/IDriversLeaderboardPresenter';
|
||||
|
||||
export class DriversLeaderboardPresenter implements IDriversLeaderboardPresenter {
|
||||
private viewModel: DriversLeaderboardViewModel | null = null;
|
||||
|
||||
present(
|
||||
drivers: Driver[],
|
||||
rankings: Array<{ driverId: string; rating: number; overallRank: number }>,
|
||||
stats: Record<string, { rating: number; wins: number; podiums: number; totalRaces: number; overallRank: number }>,
|
||||
avatarUrls: Record<string, string>
|
||||
): DriversLeaderboardViewModel {
|
||||
const items: DriverLeaderboardItemViewModel[] = drivers.map((driver) => {
|
||||
const driverStats = stats[driver.id];
|
||||
const rating = driverStats?.rating ?? 0;
|
||||
const wins = driverStats?.wins ?? 0;
|
||||
const podiums = driverStats?.podiums ?? 0;
|
||||
const totalRaces = driverStats?.totalRaces ?? 0;
|
||||
|
||||
let effectiveRank = Number.POSITIVE_INFINITY;
|
||||
if (typeof driverStats?.overallRank === 'number' && driverStats.overallRank > 0) {
|
||||
effectiveRank = driverStats.overallRank;
|
||||
} else {
|
||||
const indexInGlobal = rankings.findIndex((entry) => entry.driverId === driver.id);
|
||||
if (indexInGlobal !== -1) {
|
||||
effectiveRank = indexInGlobal + 1;
|
||||
}
|
||||
}
|
||||
|
||||
const skillLevel = SkillLevelService.getSkillLevel(rating);
|
||||
const isActive = rankings.some((r) => r.driverId === driver.id);
|
||||
|
||||
return {
|
||||
id: driver.id,
|
||||
name: driver.name,
|
||||
rating,
|
||||
skillLevel,
|
||||
nationality: driver.country,
|
||||
racesCompleted: totalRaces,
|
||||
wins,
|
||||
podiums,
|
||||
isActive,
|
||||
rank: effectiveRank,
|
||||
avatarUrl: avatarUrls[driver.id] ?? '',
|
||||
};
|
||||
});
|
||||
|
||||
items.sort((a, b) => {
|
||||
const rankA = Number.isFinite(a.rank) && a.rank > 0 ? a.rank : Number.POSITIVE_INFINITY;
|
||||
const rankB = Number.isFinite(b.rank) && b.rank > 0 ? b.rank : Number.POSITIVE_INFINITY;
|
||||
if (rankA !== rankB) return rankA - rankB;
|
||||
return b.rating - a.rating;
|
||||
});
|
||||
|
||||
const totalRaces = items.reduce((sum, d) => sum + d.racesCompleted, 0);
|
||||
const totalWins = items.reduce((sum, d) => sum + d.wins, 0);
|
||||
const activeCount = items.filter((d) => d.isActive).length;
|
||||
|
||||
this.viewModel = {
|
||||
drivers: items,
|
||||
totalRaces,
|
||||
totalWins,
|
||||
activeCount,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): DriversLeaderboardViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { IEntitySponsorshipPricingPresenter } from '@racing/application/presenters/IEntitySponsorshipPricingPresenter';
|
||||
import type { GetEntitySponsorshipPricingResultDTO } from '@racing/application/use-cases/GetEntitySponsorshipPricingQuery';
|
||||
|
||||
export class EntitySponsorshipPricingPresenter implements IEntitySponsorshipPricingPresenter {
|
||||
private data: GetEntitySponsorshipPricingResultDTO | null = null;
|
||||
|
||||
present(data: GetEntitySponsorshipPricingResultDTO | null): void {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
getData(): GetEntitySponsorshipPricingResultDTO | null {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import type {
|
||||
ILeagueDriverSeasonStatsPresenter,
|
||||
LeagueDriverSeasonStatsItemViewModel,
|
||||
LeagueDriverSeasonStatsViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/ILeagueDriverSeasonStatsPresenter';
|
||||
|
||||
export class LeagueDriverSeasonStatsPresenter implements ILeagueDriverSeasonStatsPresenter {
|
||||
private viewModel: LeagueDriverSeasonStatsViewModel | null = null;
|
||||
|
||||
present(
|
||||
leagueId: string,
|
||||
standings: Array<{
|
||||
driverId: string;
|
||||
position: number;
|
||||
points: number;
|
||||
racesCompleted: number;
|
||||
}>,
|
||||
penalties: Map<string, { baseDelta: number; bonusDelta: number }>,
|
||||
driverResults: Map<string, Array<{ position: number }>>,
|
||||
driverRatings: Map<string, { rating: number | null; ratingChange: number | null }>
|
||||
): LeagueDriverSeasonStatsViewModel {
|
||||
const stats: LeagueDriverSeasonStatsItemViewModel[] = standings.map((standing) => {
|
||||
const penalty = penalties.get(standing.driverId) ?? { baseDelta: 0, bonusDelta: 0 };
|
||||
const totalPenaltyPoints = penalty.baseDelta;
|
||||
const bonusPoints = penalty.bonusDelta;
|
||||
|
||||
const racesCompleted = standing.racesCompleted;
|
||||
const pointsPerRace = racesCompleted > 0 ? standing.points / racesCompleted : 0;
|
||||
|
||||
const ratingInfo = driverRatings.get(standing.driverId) ?? { rating: null, ratingChange: null };
|
||||
|
||||
const results = driverResults.get(standing.driverId) ?? [];
|
||||
let avgFinish: number | null = null;
|
||||
if (results.length > 0) {
|
||||
const totalPositions = results.reduce((sum, r) => sum + r.position, 0);
|
||||
const avg = totalPositions / results.length;
|
||||
avgFinish = Number.isFinite(avg) ? Number(avg.toFixed(2)) : null;
|
||||
}
|
||||
|
||||
return {
|
||||
leagueId,
|
||||
driverId: standing.driverId,
|
||||
position: standing.position,
|
||||
driverName: '',
|
||||
teamId: undefined,
|
||||
teamName: undefined,
|
||||
totalPoints: standing.points + totalPenaltyPoints + bonusPoints,
|
||||
basePoints: standing.points,
|
||||
penaltyPoints: Math.abs(totalPenaltyPoints),
|
||||
bonusPoints,
|
||||
pointsPerRace,
|
||||
racesStarted: results.length,
|
||||
racesFinished: results.length,
|
||||
dnfs: 0,
|
||||
noShows: 0,
|
||||
avgFinish,
|
||||
rating: ratingInfo.rating,
|
||||
ratingChange: ratingInfo.ratingChange,
|
||||
};
|
||||
});
|
||||
|
||||
stats.sort((a, b) => a.position - b.position);
|
||||
|
||||
this.viewModel = {
|
||||
leagueId,
|
||||
stats,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): LeagueDriverSeasonStatsViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
119
apps/website/lib/presenters/LeagueFullConfigPresenter.ts
Normal file
119
apps/website/lib/presenters/LeagueFullConfigPresenter.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type { DropScorePolicy } from '@gridpilot/racing/domain/value-objects/DropScorePolicy';
|
||||
import type {
|
||||
ILeagueFullConfigPresenter,
|
||||
LeagueFullConfigData,
|
||||
LeagueConfigFormViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/ILeagueFullConfigPresenter';
|
||||
|
||||
export class LeagueFullConfigPresenter implements ILeagueFullConfigPresenter {
|
||||
private viewModel: LeagueConfigFormViewModel | null = null;
|
||||
|
||||
present(data: LeagueFullConfigData): LeagueConfigFormViewModel {
|
||||
const { league, activeSeason, scoringConfig, game } = data;
|
||||
|
||||
const patternId = scoringConfig?.scoringPresetId;
|
||||
|
||||
const primaryChampionship =
|
||||
scoringConfig && scoringConfig.championships && scoringConfig.championships.length > 0
|
||||
? scoringConfig.championships[0]
|
||||
: undefined;
|
||||
|
||||
const dropPolicy = primaryChampionship?.dropScorePolicy ?? undefined;
|
||||
const dropPolicyForm = this.mapDropPolicy(dropPolicy);
|
||||
|
||||
const defaultQualifyingMinutes = 30;
|
||||
const defaultMainRaceMinutes = 40;
|
||||
const mainRaceMinutes =
|
||||
typeof league.settings.sessionDuration === 'number'
|
||||
? league.settings.sessionDuration
|
||||
: defaultMainRaceMinutes;
|
||||
const qualifyingMinutes = defaultQualifyingMinutes;
|
||||
|
||||
const roundsPlanned = 8;
|
||||
|
||||
let sessionCount = 2;
|
||||
if (
|
||||
primaryChampionship &&
|
||||
Array.isArray((primaryChampionship as any).sessionTypes) &&
|
||||
(primaryChampionship as any).sessionTypes.length > 0
|
||||
) {
|
||||
sessionCount = (primaryChampionship as any).sessionTypes.length;
|
||||
}
|
||||
|
||||
const practiceMinutes = 20;
|
||||
const sprintRaceMinutes = patternId === 'sprint-main-driver' ? 20 : undefined;
|
||||
|
||||
this.viewModel = {
|
||||
leagueId: league.id,
|
||||
basics: {
|
||||
name: league.name,
|
||||
description: league.description,
|
||||
visibility: 'public',
|
||||
gameId: game?.id ?? 'iracing',
|
||||
},
|
||||
structure: {
|
||||
mode: 'solo',
|
||||
maxDrivers: league.settings.maxDrivers ?? 32,
|
||||
maxTeams: undefined,
|
||||
driversPerTeam: undefined,
|
||||
multiClassEnabled: false,
|
||||
},
|
||||
championships: {
|
||||
enableDriverChampionship: true,
|
||||
enableTeamChampionship: false,
|
||||
enableNationsChampionship: false,
|
||||
enableTrophyChampionship: false,
|
||||
},
|
||||
scoring: {
|
||||
patternId: patternId ?? undefined,
|
||||
customScoringEnabled: !patternId,
|
||||
},
|
||||
dropPolicy: dropPolicyForm,
|
||||
timings: {
|
||||
practiceMinutes,
|
||||
qualifyingMinutes,
|
||||
sprintRaceMinutes,
|
||||
mainRaceMinutes,
|
||||
sessionCount,
|
||||
roundsPlanned,
|
||||
},
|
||||
stewarding: {
|
||||
decisionMode: 'admin_only',
|
||||
requireDefense: true,
|
||||
defenseTimeLimit: 48,
|
||||
voteTimeLimit: 72,
|
||||
protestDeadlineHours: 72,
|
||||
stewardingClosesHours: 168,
|
||||
notifyAccusedOnProtest: true,
|
||||
notifyOnVoteRequired: true,
|
||||
},
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): LeagueConfigFormViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
private mapDropPolicy(policy: DropScorePolicy | undefined): { strategy: string; n?: number } {
|
||||
if (!policy || policy.strategy === 'none') {
|
||||
return { strategy: 'none' };
|
||||
}
|
||||
|
||||
if (policy.strategy === 'bestNResults') {
|
||||
const n = typeof policy.count === 'number' ? policy.count : undefined;
|
||||
return n !== undefined ? { strategy: 'bestNResults', n } : { strategy: 'none' };
|
||||
}
|
||||
|
||||
if (policy.strategy === 'dropWorstN') {
|
||||
const n = typeof policy.dropCount === 'number' ? policy.dropCount : undefined;
|
||||
return n !== undefined ? { strategy: 'dropWorstN', n } : { strategy: 'none' };
|
||||
}
|
||||
|
||||
return { strategy: 'none' };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { ILeagueSchedulePreviewPresenter } from '@racing/application/presenters/ILeagueSchedulePreviewPresenter';
|
||||
import type { LeagueSchedulePreviewDTO } from '@racing/application/dto/LeagueScheduleDTO';
|
||||
|
||||
export class LeagueSchedulePreviewPresenter implements ILeagueSchedulePreviewPresenter {
|
||||
private data: LeagueSchedulePreviewDTO | null = null;
|
||||
|
||||
present(data: LeagueSchedulePreviewDTO): void {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
getData(): LeagueSchedulePreviewDTO | null {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
149
apps/website/lib/presenters/LeagueScoringConfigPresenter.ts
Normal file
149
apps/website/lib/presenters/LeagueScoringConfigPresenter.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import type { ChampionshipConfig } from '@gridpilot/racing/domain/value-objects/ChampionshipConfig';
|
||||
import type { BonusRule } from '@gridpilot/racing/domain/value-objects/BonusRule';
|
||||
import type {
|
||||
ILeagueScoringConfigPresenter,
|
||||
LeagueScoringConfigData,
|
||||
LeagueScoringConfigViewModel,
|
||||
LeagueScoringChampionshipViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/ILeagueScoringConfigPresenter';
|
||||
|
||||
export class LeagueScoringConfigPresenter implements ILeagueScoringConfigPresenter {
|
||||
private viewModel: LeagueScoringConfigViewModel | null = null;
|
||||
|
||||
present(data: LeagueScoringConfigData): LeagueScoringConfigViewModel {
|
||||
const championships: LeagueScoringChampionshipViewModel[] =
|
||||
data.championships.map((champ) => this.mapChampionship(champ));
|
||||
|
||||
const dropPolicySummary =
|
||||
data.preset?.dropPolicySummary ??
|
||||
this.deriveDropPolicyDescriptionFromChampionships(data.championships);
|
||||
|
||||
this.viewModel = {
|
||||
leagueId: data.leagueId,
|
||||
seasonId: data.seasonId,
|
||||
gameId: data.gameId,
|
||||
gameName: data.gameName,
|
||||
scoringPresetId: data.scoringPresetId,
|
||||
scoringPresetName: data.preset?.name,
|
||||
dropPolicySummary,
|
||||
championships,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): LeagueScoringConfigViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
private mapChampionship(championship: ChampionshipConfig): LeagueScoringChampionshipViewModel {
|
||||
const sessionTypes = championship.sessionTypes.map((s) => s.toString());
|
||||
const pointsPreview = this.buildPointsPreview(championship.pointsTableBySessionType);
|
||||
const bonusSummary = this.buildBonusSummary(
|
||||
championship.bonusRulesBySessionType ?? {},
|
||||
);
|
||||
const dropPolicyDescription = this.deriveDropPolicyDescription(
|
||||
championship.dropScorePolicy,
|
||||
);
|
||||
|
||||
return {
|
||||
id: championship.id,
|
||||
name: championship.name,
|
||||
type: championship.type,
|
||||
sessionTypes,
|
||||
pointsPreview,
|
||||
bonusSummary,
|
||||
dropPolicyDescription,
|
||||
};
|
||||
}
|
||||
|
||||
private buildPointsPreview(
|
||||
tables: Record<string, any>,
|
||||
): Array<{ sessionType: string; position: number; points: number }> {
|
||||
const preview: Array<{
|
||||
sessionType: string;
|
||||
position: number;
|
||||
points: number;
|
||||
}> = [];
|
||||
|
||||
const maxPositions = 10;
|
||||
|
||||
for (const [sessionType, table] of Object.entries(tables)) {
|
||||
for (let pos = 1; pos <= maxPositions; pos++) {
|
||||
const points = table.getPointsForPosition(pos);
|
||||
if (points && points !== 0) {
|
||||
preview.push({
|
||||
sessionType,
|
||||
position: pos,
|
||||
points,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return preview;
|
||||
}
|
||||
|
||||
private buildBonusSummary(
|
||||
bonusRulesBySessionType: Record<string, BonusRule[]>,
|
||||
): string[] {
|
||||
const summaries: string[] = [];
|
||||
|
||||
for (const [sessionType, rules] of Object.entries(bonusRulesBySessionType)) {
|
||||
for (const rule of rules) {
|
||||
if (rule.type === 'fastestLap') {
|
||||
const base = `Fastest lap in ${sessionType}`;
|
||||
if (rule.requiresFinishInTopN) {
|
||||
summaries.push(
|
||||
`${base} +${rule.points} points if finishing P${rule.requiresFinishInTopN} or better`,
|
||||
);
|
||||
} else {
|
||||
summaries.push(`${base} +${rule.points} points`);
|
||||
}
|
||||
} else {
|
||||
summaries.push(
|
||||
`${rule.type} bonus in ${sessionType} worth ${rule.points} points`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return summaries;
|
||||
}
|
||||
|
||||
private deriveDropPolicyDescriptionFromChampionships(
|
||||
championships: ChampionshipConfig[],
|
||||
): string {
|
||||
const first = championships[0];
|
||||
if (!first) {
|
||||
return 'All results count';
|
||||
}
|
||||
return this.deriveDropPolicyDescription(first.dropScorePolicy);
|
||||
}
|
||||
|
||||
private deriveDropPolicyDescription(policy: {
|
||||
strategy: string;
|
||||
count?: number;
|
||||
dropCount?: number;
|
||||
}): string {
|
||||
if (!policy || policy.strategy === 'none') {
|
||||
return 'All results count';
|
||||
}
|
||||
|
||||
if (policy.strategy === 'bestNResults' && typeof policy.count === 'number') {
|
||||
return `Best ${policy.count} results count towards the championship`;
|
||||
}
|
||||
|
||||
if (
|
||||
policy.strategy === 'dropWorstN' &&
|
||||
typeof policy.dropCount === 'number'
|
||||
) {
|
||||
return `Worst ${policy.dropCount} results are dropped from the championship total`;
|
||||
}
|
||||
|
||||
return 'Custom drop score rules apply';
|
||||
}
|
||||
}
|
||||
25
apps/website/lib/presenters/LeagueScoringPresetsPresenter.ts
Normal file
25
apps/website/lib/presenters/LeagueScoringPresetsPresenter.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { LeagueScoringPresetDTO } from '@gridpilot/racing/application/ports/LeagueScoringPresetProvider';
|
||||
import type {
|
||||
ILeagueScoringPresetsPresenter,
|
||||
LeagueScoringPresetsViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/ILeagueScoringPresetsPresenter';
|
||||
|
||||
export class LeagueScoringPresetsPresenter implements ILeagueScoringPresetsPresenter {
|
||||
private viewModel: LeagueScoringPresetsViewModel | null = null;
|
||||
|
||||
present(presets: LeagueScoringPresetDTO[]): LeagueScoringPresetsViewModel {
|
||||
this.viewModel = {
|
||||
presets,
|
||||
totalCount: presets.length,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): LeagueScoringPresetsViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
38
apps/website/lib/presenters/LeagueStandingsPresenter.ts
Normal file
38
apps/website/lib/presenters/LeagueStandingsPresenter.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { Standing } from '@gridpilot/racing/domain/entities/Standing';
|
||||
import type {
|
||||
ILeagueStandingsPresenter,
|
||||
StandingItemViewModel,
|
||||
LeagueStandingsViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/ILeagueStandingsPresenter';
|
||||
|
||||
export class LeagueStandingsPresenter implements ILeagueStandingsPresenter {
|
||||
private viewModel: LeagueStandingsViewModel | null = null;
|
||||
|
||||
present(standings: Standing[]): LeagueStandingsViewModel {
|
||||
const standingItems: StandingItemViewModel[] = standings.map((standing) => ({
|
||||
id: standing.id,
|
||||
leagueId: standing.leagueId,
|
||||
seasonId: standing.seasonId,
|
||||
driverId: standing.driverId,
|
||||
position: standing.position,
|
||||
points: standing.points,
|
||||
wins: standing.wins,
|
||||
podiums: standing.podiums,
|
||||
racesCompleted: standing.racesCompleted,
|
||||
}));
|
||||
|
||||
this.viewModel = {
|
||||
leagueId: standings[0]?.leagueId ?? '',
|
||||
standings: standingItems,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): LeagueStandingsViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
42
apps/website/lib/presenters/LeagueStatsPresenter.ts
Normal file
42
apps/website/lib/presenters/LeagueStatsPresenter.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import type {
|
||||
ILeagueStatsPresenter,
|
||||
LeagueStatsViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/ILeagueStatsPresenter';
|
||||
|
||||
export class LeagueStatsPresenter implements ILeagueStatsPresenter {
|
||||
private viewModel: LeagueStatsViewModel | null = null;
|
||||
|
||||
present(
|
||||
leagueId: string,
|
||||
totalRaces: number,
|
||||
completedRaces: number,
|
||||
scheduledRaces: number,
|
||||
sofValues: number[]
|
||||
): LeagueStatsViewModel {
|
||||
const averageSOF = sofValues.length > 0
|
||||
? Math.round(sofValues.reduce((a, b) => a + b, 0) / sofValues.length)
|
||||
: null;
|
||||
|
||||
const highestSOF = sofValues.length > 0 ? Math.max(...sofValues) : null;
|
||||
const lowestSOF = sofValues.length > 0 ? Math.min(...sofValues) : null;
|
||||
|
||||
this.viewModel = {
|
||||
leagueId,
|
||||
totalRaces,
|
||||
completedRaces,
|
||||
scheduledRaces,
|
||||
averageSOF,
|
||||
highestSOF,
|
||||
lowestSOF,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): LeagueStatsViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { IPendingSponsorshipRequestsPresenter } from '@racing/application/presenters/IPendingSponsorshipRequestsPresenter';
|
||||
import type { GetPendingSponsorshipRequestsResultDTO } from '@racing/application/use-cases/GetPendingSponsorshipRequestsQuery';
|
||||
|
||||
export class PendingSponsorshipRequestsPresenter implements IPendingSponsorshipRequestsPresenter {
|
||||
private data: GetPendingSponsorshipRequestsResultDTO | null = null;
|
||||
|
||||
present(data: GetPendingSponsorshipRequestsResultDTO): void {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
getData(): GetPendingSponsorshipRequestsResultDTO | null {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
60
apps/website/lib/presenters/RacePenaltiesPresenter.ts
Normal file
60
apps/website/lib/presenters/RacePenaltiesPresenter.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import type {
|
||||
IRacePenaltiesPresenter,
|
||||
RacePenaltyViewModel,
|
||||
RacePenaltiesViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/IRacePenaltiesPresenter';
|
||||
import type { PenaltyType, PenaltyStatus } from '@gridpilot/racing/domain/entities/Penalty';
|
||||
|
||||
export class RacePenaltiesPresenter implements IRacePenaltiesPresenter {
|
||||
private viewModel: RacePenaltiesViewModel | null = null;
|
||||
|
||||
present(
|
||||
penalties: Array<{
|
||||
id: string;
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
type: PenaltyType;
|
||||
value?: number;
|
||||
reason: string;
|
||||
protestId?: string;
|
||||
issuedBy: string;
|
||||
status: PenaltyStatus;
|
||||
issuedAt: Date;
|
||||
appliedAt?: Date;
|
||||
notes?: string;
|
||||
getDescription(): string;
|
||||
}>,
|
||||
driverMap: Map<string, string>
|
||||
): RacePenaltiesViewModel {
|
||||
const penaltyViewModels: RacePenaltyViewModel[] = penalties.map(penalty => ({
|
||||
id: penalty.id,
|
||||
raceId: penalty.raceId,
|
||||
driverId: penalty.driverId,
|
||||
driverName: driverMap.get(penalty.driverId) || 'Unknown',
|
||||
type: penalty.type,
|
||||
value: penalty.value,
|
||||
reason: penalty.reason,
|
||||
protestId: penalty.protestId,
|
||||
issuedBy: penalty.issuedBy,
|
||||
issuedByName: driverMap.get(penalty.issuedBy) || 'Unknown',
|
||||
status: penalty.status,
|
||||
description: penalty.getDescription(),
|
||||
issuedAt: penalty.issuedAt.toISOString(),
|
||||
appliedAt: penalty.appliedAt?.toISOString(),
|
||||
notes: penalty.notes,
|
||||
}));
|
||||
|
||||
this.viewModel = {
|
||||
penalties: penaltyViewModels,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): RacePenaltiesViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
59
apps/website/lib/presenters/RaceProtestsPresenter.ts
Normal file
59
apps/website/lib/presenters/RaceProtestsPresenter.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import type {
|
||||
IRaceProtestsPresenter,
|
||||
RaceProtestViewModel,
|
||||
RaceProtestsViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/IRaceProtestsPresenter';
|
||||
import type { ProtestStatus, ProtestIncident } from '@gridpilot/racing/domain/entities/Protest';
|
||||
|
||||
export class RaceProtestsPresenter implements IRaceProtestsPresenter {
|
||||
private viewModel: RaceProtestsViewModel | null = null;
|
||||
|
||||
present(
|
||||
protests: Array<{
|
||||
id: string;
|
||||
raceId: string;
|
||||
protestingDriverId: string;
|
||||
accusedDriverId: string;
|
||||
incident: ProtestIncident;
|
||||
comment?: string;
|
||||
proofVideoUrl?: string;
|
||||
status: ProtestStatus;
|
||||
reviewedBy?: string;
|
||||
decisionNotes?: string;
|
||||
filedAt: Date;
|
||||
reviewedAt?: Date;
|
||||
}>,
|
||||
driverMap: Map<string, string>
|
||||
): RaceProtestsViewModel {
|
||||
const protestViewModels: RaceProtestViewModel[] = protests.map(protest => ({
|
||||
id: protest.id,
|
||||
raceId: protest.raceId,
|
||||
protestingDriverId: protest.protestingDriverId,
|
||||
protestingDriverName: driverMap.get(protest.protestingDriverId) || 'Unknown',
|
||||
accusedDriverId: protest.accusedDriverId,
|
||||
accusedDriverName: driverMap.get(protest.accusedDriverId) || 'Unknown',
|
||||
incident: protest.incident,
|
||||
comment: protest.comment,
|
||||
proofVideoUrl: protest.proofVideoUrl,
|
||||
status: protest.status,
|
||||
reviewedBy: protest.reviewedBy,
|
||||
reviewedByName: protest.reviewedBy ? driverMap.get(protest.reviewedBy) : undefined,
|
||||
decisionNotes: protest.decisionNotes,
|
||||
filedAt: protest.filedAt.toISOString(),
|
||||
reviewedAt: protest.reviewedAt?.toISOString(),
|
||||
}));
|
||||
|
||||
this.viewModel = {
|
||||
protests: protestViewModels,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): RaceProtestsViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
24
apps/website/lib/presenters/RaceRegistrationsPresenter.ts
Normal file
24
apps/website/lib/presenters/RaceRegistrationsPresenter.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type {
|
||||
IRaceRegistrationsPresenter,
|
||||
RaceRegistrationsViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/IRaceRegistrationsPresenter';
|
||||
|
||||
export class RaceRegistrationsPresenter implements IRaceRegistrationsPresenter {
|
||||
private viewModel: RaceRegistrationsViewModel | null = null;
|
||||
|
||||
present(registeredDriverIds: string[]): RaceRegistrationsViewModel {
|
||||
this.viewModel = {
|
||||
registeredDriverIds,
|
||||
count: registeredDriverIds.length,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): RaceRegistrationsViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
49
apps/website/lib/presenters/RaceWithSOFPresenter.ts
Normal file
49
apps/website/lib/presenters/RaceWithSOFPresenter.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type {
|
||||
IRaceWithSOFPresenter,
|
||||
RaceWithSOFViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/IRaceWithSOFPresenter';
|
||||
|
||||
export class RaceWithSOFPresenter implements IRaceWithSOFPresenter {
|
||||
private viewModel: RaceWithSOFViewModel | null = null;
|
||||
|
||||
present(
|
||||
raceId: string,
|
||||
leagueId: string,
|
||||
scheduledAt: Date,
|
||||
track: string,
|
||||
trackId: string,
|
||||
car: string,
|
||||
carId: string,
|
||||
sessionType: string,
|
||||
status: string,
|
||||
strengthOfField: number | null,
|
||||
registeredCount: number,
|
||||
maxParticipants: number,
|
||||
participantCount: number
|
||||
): RaceWithSOFViewModel {
|
||||
this.viewModel = {
|
||||
id: raceId,
|
||||
leagueId,
|
||||
scheduledAt: scheduledAt.toISOString(),
|
||||
track,
|
||||
trackId,
|
||||
car,
|
||||
carId,
|
||||
sessionType,
|
||||
status,
|
||||
strengthOfField,
|
||||
registeredCount,
|
||||
maxParticipants,
|
||||
participantCount,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): RaceWithSOFViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
64
apps/website/lib/presenters/RacesPagePresenter.ts
Normal file
64
apps/website/lib/presenters/RacesPagePresenter.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import type {
|
||||
IRacesPagePresenter,
|
||||
RacesPageViewModel,
|
||||
RaceListItemViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/IRacesPagePresenter';
|
||||
|
||||
export class RacesPagePresenter implements IRacesPagePresenter {
|
||||
private viewModel: RacesPageViewModel | null = null;
|
||||
|
||||
present(races: any[]): void {
|
||||
const now = new Date();
|
||||
const nextWeek = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
|
||||
|
||||
const raceViewModels: RaceListItemViewModel[] = races.map(race => ({
|
||||
id: race.id,
|
||||
track: race.track,
|
||||
car: race.car,
|
||||
scheduledAt: race.scheduledAt,
|
||||
status: race.status,
|
||||
leagueId: race.leagueId,
|
||||
leagueName: race.leagueName,
|
||||
strengthOfField: race.strengthOfField,
|
||||
isUpcoming: race.isUpcoming,
|
||||
isLive: race.isLive,
|
||||
isPast: race.isPast,
|
||||
}));
|
||||
|
||||
const stats = {
|
||||
total: raceViewModels.length,
|
||||
scheduled: raceViewModels.filter(r => r.status === 'scheduled').length,
|
||||
running: raceViewModels.filter(r => r.status === 'running').length,
|
||||
completed: raceViewModels.filter(r => r.status === 'completed').length,
|
||||
};
|
||||
|
||||
const liveRaces = raceViewModels.filter(r => r.isLive);
|
||||
|
||||
const upcomingThisWeek = raceViewModels
|
||||
.filter(r => {
|
||||
const scheduledDate = new Date(r.scheduledAt);
|
||||
return r.isUpcoming && scheduledDate >= now && scheduledDate <= nextWeek;
|
||||
})
|
||||
.slice(0, 5);
|
||||
|
||||
const recentResults = raceViewModels
|
||||
.filter(r => r.status === 'completed')
|
||||
.sort((a, b) => new Date(b.scheduledAt).getTime() - new Date(a.scheduledAt).getTime())
|
||||
.slice(0, 3);
|
||||
|
||||
this.viewModel = {
|
||||
races: raceViewModels,
|
||||
stats,
|
||||
liveRaces,
|
||||
upcomingThisWeek,
|
||||
recentResults,
|
||||
};
|
||||
}
|
||||
|
||||
getViewModel(): RacesPageViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('ViewModel not yet generated. Call present() first.');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
14
apps/website/lib/presenters/SponsorDashboardPresenter.ts
Normal file
14
apps/website/lib/presenters/SponsorDashboardPresenter.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { ISponsorDashboardPresenter } from '@racing/application/presenters/ISponsorDashboardPresenter';
|
||||
import type { SponsorDashboardDTO } from '@racing/application/use-cases/GetSponsorDashboardQuery';
|
||||
|
||||
export class SponsorDashboardPresenter implements ISponsorDashboardPresenter {
|
||||
private data: SponsorDashboardDTO | null = null;
|
||||
|
||||
present(data: SponsorDashboardDTO | null): void {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
getData(): SponsorDashboardDTO | null {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
14
apps/website/lib/presenters/SponsorSponsorshipsPresenter.ts
Normal file
14
apps/website/lib/presenters/SponsorSponsorshipsPresenter.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { ISponsorSponsorshipsPresenter } from '@racing/application/presenters/ISponsorSponsorshipsPresenter';
|
||||
import type { SponsorSponsorshipsDTO } from '@racing/application/use-cases/GetSponsorSponsorshipsQuery';
|
||||
|
||||
export class SponsorSponsorshipsPresenter implements ISponsorSponsorshipsPresenter {
|
||||
private data: SponsorSponsorshipsDTO | null = null;
|
||||
|
||||
present(data: SponsorSponsorshipsDTO | null): void {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
getData(): SponsorSponsorshipsDTO | null {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
48
apps/website/lib/presenters/TeamDetailsPresenter.ts
Normal file
48
apps/website/lib/presenters/TeamDetailsPresenter.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { Team, TeamMembership } from '@gridpilot/racing/domain/entities/Team';
|
||||
import type {
|
||||
ITeamDetailsPresenter,
|
||||
TeamDetailsViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/ITeamDetailsPresenter';
|
||||
|
||||
export class TeamDetailsPresenter implements ITeamDetailsPresenter {
|
||||
private viewModel: TeamDetailsViewModel | null = null;
|
||||
|
||||
present(
|
||||
team: Team,
|
||||
membership: TeamMembership | null,
|
||||
driverId: string
|
||||
): TeamDetailsViewModel {
|
||||
const canManage = membership?.role === 'owner' || membership?.role === 'manager';
|
||||
|
||||
this.viewModel = {
|
||||
team: {
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
tag: team.tag,
|
||||
description: team.description,
|
||||
ownerId: team.ownerId,
|
||||
leagues: team.leagues,
|
||||
specialization: team.specialization as 'endurance' | 'sprint' | 'mixed' | undefined,
|
||||
region: team.region,
|
||||
languages: team.languages,
|
||||
},
|
||||
membership: membership
|
||||
? {
|
||||
role: membership.role,
|
||||
joinedAt: membership.joinedAt.toISOString(),
|
||||
isActive: membership.isActive,
|
||||
}
|
||||
: null,
|
||||
canManage,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): TeamDetailsViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
43
apps/website/lib/presenters/TeamJoinRequestsPresenter.ts
Normal file
43
apps/website/lib/presenters/TeamJoinRequestsPresenter.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { TeamJoinRequest } from '@gridpilot/racing/domain/entities/Team';
|
||||
import type {
|
||||
ITeamJoinRequestsPresenter,
|
||||
TeamJoinRequestViewModel,
|
||||
TeamJoinRequestsViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/ITeamJoinRequestsPresenter';
|
||||
|
||||
export class TeamJoinRequestsPresenter implements ITeamJoinRequestsPresenter {
|
||||
private viewModel: TeamJoinRequestsViewModel | null = null;
|
||||
|
||||
present(
|
||||
requests: TeamJoinRequest[],
|
||||
driverNames: Record<string, string>,
|
||||
avatarUrls: Record<string, string>
|
||||
): TeamJoinRequestsViewModel {
|
||||
const requestItems: TeamJoinRequestViewModel[] = requests.map((request) => ({
|
||||
requestId: request.id,
|
||||
driverId: request.driverId,
|
||||
driverName: driverNames[request.driverId] ?? 'Unknown Driver',
|
||||
teamId: request.teamId,
|
||||
status: request.status,
|
||||
requestedAt: request.requestedAt.toISOString(),
|
||||
avatarUrl: avatarUrls[request.driverId] ?? '',
|
||||
}));
|
||||
|
||||
const pendingCount = requestItems.filter((r) => r.status === 'pending').length;
|
||||
|
||||
this.viewModel = {
|
||||
requests: requestItems,
|
||||
pendingCount,
|
||||
totalCount: requestItems.length,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): TeamJoinRequestsViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
46
apps/website/lib/presenters/TeamMembersPresenter.ts
Normal file
46
apps/website/lib/presenters/TeamMembersPresenter.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { TeamMembership } from '@gridpilot/racing/domain/entities/Team';
|
||||
import type {
|
||||
ITeamMembersPresenter,
|
||||
TeamMemberViewModel,
|
||||
TeamMembersViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/ITeamMembersPresenter';
|
||||
|
||||
export class TeamMembersPresenter implements ITeamMembersPresenter {
|
||||
private viewModel: TeamMembersViewModel | null = null;
|
||||
|
||||
present(
|
||||
memberships: TeamMembership[],
|
||||
driverNames: Record<string, string>,
|
||||
avatarUrls: Record<string, string>
|
||||
): TeamMembersViewModel {
|
||||
const members: TeamMemberViewModel[] = memberships.map((membership) => ({
|
||||
driverId: membership.driverId,
|
||||
driverName: driverNames[membership.driverId] ?? 'Unknown Driver',
|
||||
role: membership.role,
|
||||
joinedAt: membership.joinedAt.toISOString(),
|
||||
isActive: membership.isActive,
|
||||
avatarUrl: avatarUrls[membership.driverId] ?? '',
|
||||
}));
|
||||
|
||||
const ownerCount = members.filter((m) => m.role === 'owner').length;
|
||||
const managerCount = members.filter((m) => m.role === 'manager').length;
|
||||
const memberCount = members.filter((m) => m.role === 'member').length;
|
||||
|
||||
this.viewModel = {
|
||||
members,
|
||||
totalCount: members.length,
|
||||
ownerCount,
|
||||
managerCount,
|
||||
memberCount,
|
||||
};
|
||||
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
getViewModel(): TeamMembersViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('Presenter has not been called yet');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
42
apps/website/lib/presenters/TeamsLeaderboardPresenter.ts
Normal file
42
apps/website/lib/presenters/TeamsLeaderboardPresenter.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import type {
|
||||
ITeamsLeaderboardPresenter,
|
||||
TeamsLeaderboardViewModel,
|
||||
TeamLeaderboardItemViewModel,
|
||||
SkillLevel,
|
||||
} from '@gridpilot/racing/application/presenters/ITeamsLeaderboardPresenter';
|
||||
|
||||
export class TeamsLeaderboardPresenter implements ITeamsLeaderboardPresenter {
|
||||
private viewModel: TeamsLeaderboardViewModel | null = null;
|
||||
|
||||
present(teams: any[], recruitingCount: number): void {
|
||||
this.viewModel = {
|
||||
teams: teams.map((team) => this.transformTeam(team)),
|
||||
recruitingCount,
|
||||
};
|
||||
}
|
||||
|
||||
getViewModel(): TeamsLeaderboardViewModel {
|
||||
if (!this.viewModel) {
|
||||
throw new Error('ViewModel not yet generated. Call present() first.');
|
||||
}
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
private transformTeam(team: any): TeamLeaderboardItemViewModel {
|
||||
return {
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
memberCount: team.memberCount,
|
||||
rating: team.rating,
|
||||
totalWins: team.totalWins,
|
||||
totalRaces: team.totalRaces,
|
||||
performanceLevel: team.performanceLevel as SkillLevel,
|
||||
isRecruiting: team.isRecruiting,
|
||||
createdAt: team.createdAt,
|
||||
description: team.description,
|
||||
specialization: team.specialization,
|
||||
region: team.region,
|
||||
languages: team.languages,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user