This commit is contained in:
2025-12-10 18:28:32 +01:00
parent 6d61be9c51
commit 1303a14493
108 changed files with 3366 additions and 1559 deletions

View File

@@ -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
)
);