wip league admin tools
This commit is contained in:
@@ -95,7 +95,7 @@ export class SeedRacingData {
|
||||
}
|
||||
}
|
||||
|
||||
const activeSeasons = seed.seasons.filter((season) => season.status === 'active');
|
||||
const activeSeasons = seed.seasons.filter((season) => season.status.isActive());
|
||||
for (const season of activeSeasons) {
|
||||
const presetId = this.selectScoringPresetIdForSeason(season);
|
||||
const preset = getLeagueScoringPresetById(presetId);
|
||||
@@ -270,7 +270,7 @@ export class SeedRacingData {
|
||||
|
||||
for (const league of leagues) {
|
||||
const seasons = await this.seedDeps.seasonRepository.findByLeagueId(league.id.toString());
|
||||
const activeSeasons = seasons.filter((season) => season.status === 'active');
|
||||
const activeSeasons = seasons.filter((season) => season.status.isActive());
|
||||
|
||||
for (const season of activeSeasons) {
|
||||
const existing = await this.seedDeps.leagueScoringConfigRepository.findBySeasonId(season.id);
|
||||
@@ -298,7 +298,7 @@ export class SeedRacingData {
|
||||
}
|
||||
|
||||
private selectScoringPresetIdForSeason(season: Season): string {
|
||||
if (season.leagueId === 'league-5' && season.status === 'active') {
|
||||
if (season.leagueId === 'league-5' && season.status.isActive()) {
|
||||
return 'sprint-main-driver';
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ export class RacingFeedFactory {
|
||||
const items: FeedItem[] = [];
|
||||
const friendMap = new Map(friendships.map((f) => [`${f.driverId}:${f.friendId}`, true] as const));
|
||||
|
||||
const completedRace = races.find((r) => r.status === 'completed');
|
||||
const upcomingRace = races.find((r) => r.status === 'scheduled');
|
||||
const completedRace = races.find((r) => r.status.toString() === 'completed');
|
||||
const upcomingRace = races.find((r) => r.status.toString() === 'scheduled');
|
||||
const league = leagues.find((l) => l.id.toString() === 'league-5') ?? leagues[0]!;
|
||||
|
||||
const now = this.addMinutes(this.baseDate, 10);
|
||||
|
||||
@@ -61,7 +61,8 @@ export class RacingLeagueFactory {
|
||||
settings: config,
|
||||
createdAt,
|
||||
// Start with some participants for ranked leagues to meet minimum requirements
|
||||
participantCount: i % 3 === 0 ? 12 : i % 3 === 1 ? 8 : 0,
|
||||
// Note: ranked leagues require >= 10 participants (see LeagueVisibility)
|
||||
participantCount: i % 3 === 0 ? 12 : 0,
|
||||
};
|
||||
|
||||
// Add social links with varying completeness
|
||||
|
||||
@@ -249,7 +249,7 @@ export class RacingMembershipFactory {
|
||||
.map(m => `${m.leagueId.toString()}:${m.driverId.toString()}`),
|
||||
);
|
||||
|
||||
const scheduled = races.filter((r) => r.status === 'scheduled');
|
||||
const scheduled = races.filter((r) => r.status.toString() === 'scheduled');
|
||||
|
||||
for (const race of scheduled) {
|
||||
const leagueId = race.leagueId.toString();
|
||||
@@ -311,7 +311,7 @@ export class RacingMembershipFactory {
|
||||
}
|
||||
|
||||
// Keep a tiny curated "happy path" for the demo league as well
|
||||
const upcomingDemoLeague = races.filter((r) => r.status === 'scheduled' && r.leagueId === 'league-5').slice(0, 3);
|
||||
const upcomingDemoLeague = races.filter((r) => r.status.toString() === 'scheduled' && r.leagueId === 'league-5').slice(0, 3);
|
||||
for (const race of upcomingDemoLeague) {
|
||||
registrations.push(
|
||||
RaceRegistration.create({
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Result as RaceResult } from '@core/racing/domain/entities/result/Result
|
||||
export class RacingResultFactory {
|
||||
create(drivers: Driver[], races: Race[]): RaceResult[] {
|
||||
const results: RaceResult[] = [];
|
||||
const completed = races.filter((r) => r.status === 'completed');
|
||||
const completed = races.filter((r) => r.status.toString() === 'completed');
|
||||
|
||||
for (const race of completed) {
|
||||
if (drivers.length === 0) continue;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { SponsorshipRequest } from '@core/racing/domain/entities/SponsorshipRequ
|
||||
import { Season } from '@core/racing/domain/entities/season/Season';
|
||||
import { SeasonSponsorship } from '@core/racing/domain/entities/season/SeasonSponsorship';
|
||||
import type { Sponsor } from '@core/racing/domain/entities/sponsor/Sponsor';
|
||||
import type { SeasonStatusValue } from '@core/racing/domain/value-objects/SeasonStatus';
|
||||
import { Money } from '@core/racing/domain/value-objects/Money';
|
||||
|
||||
export class RacingSeasonSponsorshipFactory {
|
||||
@@ -85,7 +86,7 @@ export class RacingSeasonSponsorshipFactory {
|
||||
const id = `${leagueId}-season-${i + 1}`;
|
||||
const isFirst = i === 0;
|
||||
|
||||
const status: Season['status'] =
|
||||
const status: SeasonStatusValue =
|
||||
leagueId === 'league-1' && isFirst
|
||||
? 'active'
|
||||
: leagueId === 'league-2'
|
||||
@@ -133,7 +134,7 @@ export class RacingSeasonSponsorshipFactory {
|
||||
const sponsorshipCount =
|
||||
season.id === 'season-1'
|
||||
? 2
|
||||
: season.status === 'active'
|
||||
: season.status.isActive()
|
||||
? faker.number.int({ min: 0, max: 2 })
|
||||
: faker.number.int({ min: 0, max: 1 });
|
||||
|
||||
@@ -162,12 +163,12 @@ export class RacingSeasonSponsorshipFactory {
|
||||
),
|
||||
createdAt: faker.date.recent({ days: 120, refDate: this.baseDate }),
|
||||
description: tier === 'main' ? 'Main sponsor slot' : 'Secondary sponsor slot',
|
||||
...(season.status === 'active'
|
||||
...(season.status.isActive()
|
||||
? {
|
||||
status: faker.helpers.arrayElement(['active', 'pending'] as const),
|
||||
activatedAt: faker.date.recent({ days: 30, refDate: this.baseDate }),
|
||||
}
|
||||
: season.status === 'completed' || season.status === 'archived'
|
||||
: season.status.isCompleted() || season.status.isArchived()
|
||||
? {
|
||||
status: faker.helpers.arrayElement(['ended', 'cancelled'] as const),
|
||||
endedAt: faker.date.recent({ days: 200, refDate: this.baseDate }),
|
||||
@@ -191,7 +192,11 @@ export class RacingSeasonSponsorshipFactory {
|
||||
for (const season of seasons) {
|
||||
const isHighTrafficDemo = season.id === 'season-1';
|
||||
const maxRequests =
|
||||
isHighTrafficDemo ? 8 : season.status === 'active' ? faker.number.int({ min: 0, max: 4 }) : faker.number.int({ min: 0, max: 1 });
|
||||
isHighTrafficDemo
|
||||
? 8
|
||||
: season.status.isActive()
|
||||
? faker.number.int({ min: 0, max: 4 })
|
||||
: faker.number.int({ min: 0, max: 1 });
|
||||
|
||||
for (let i = 0; i < maxRequests; i++) {
|
||||
const tier: SponsorshipRequest['tier'] =
|
||||
@@ -219,7 +224,7 @@ export class RacingSeasonSponsorshipFactory {
|
||||
|
||||
// A mix of statuses for edge cases (pending is what the UI lists)
|
||||
const status =
|
||||
season.status === 'active'
|
||||
season.status.isActive()
|
||||
? faker.helpers.arrayElement(['pending', 'pending', 'pending', 'rejected', 'withdrawn'] as const)
|
||||
: faker.helpers.arrayElement(['pending', 'rejected'] as const);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export class RacingStandingFactory {
|
||||
|
||||
const racesByLeague = new Map<string, Set<string>>();
|
||||
for (const race of races) {
|
||||
if (race.status !== 'completed') continue;
|
||||
if (!race.status.isCompleted()) continue;
|
||||
|
||||
const set = racesByLeague.get(race.leagueId) ?? new Set<string>();
|
||||
set.add(race.id);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
import { InMemoryRaceRepository } from './InMemoryRaceRepository';
|
||||
import { Race, RaceStatus } from '@core/racing/domain/entities/Race';
|
||||
import { Race, type RaceStatusValue } from '@core/racing/domain/entities/Race';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
|
||||
describe('InMemoryRaceRepository', () => {
|
||||
@@ -23,7 +23,7 @@ describe('InMemoryRaceRepository', () => {
|
||||
track: string,
|
||||
car: string,
|
||||
scheduledAt: Date,
|
||||
status: RaceStatus = 'scheduled'
|
||||
status: RaceStatusValue = 'scheduled',
|
||||
) => {
|
||||
return Race.create({
|
||||
id,
|
||||
@@ -178,7 +178,7 @@ describe('InMemoryRaceRepository', () => {
|
||||
const race = createTestRace('1', 'league1', 'Track1', 'Car1', new Date());
|
||||
await repository.create(race);
|
||||
|
||||
const updatedRace = race.complete();
|
||||
const updatedRace = race.start().complete();
|
||||
const result = await repository.update(updatedRace);
|
||||
expect(result).toEqual(updatedRace);
|
||||
expect(mockLogger.info).toHaveBeenCalledWith('Race 1 updated successfully.');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
|
||||
import { Race, RaceStatus } from '@core/racing/domain/entities/Race';
|
||||
import { Race, type RaceStatusValue } from '@core/racing/domain/entities/Race';
|
||||
import { Logger } from '@core/shared/application';
|
||||
|
||||
export class InMemoryRaceRepository implements IRaceRepository {
|
||||
@@ -36,7 +36,7 @@ export class InMemoryRaceRepository implements IRaceRepository {
|
||||
this.logger.debug(`[InMemoryRaceRepository] Finding upcoming races by league ID: ${leagueId}`);
|
||||
const now = new Date();
|
||||
const upcomingRaces = Array.from(this.races.values()).filter(race =>
|
||||
race.leagueId === leagueId && race.status === 'scheduled' && race.scheduledAt > now
|
||||
race.leagueId === leagueId && race.status.isScheduled() && race.scheduledAt > now
|
||||
);
|
||||
this.logger.info(`Found ${upcomingRaces.length} upcoming races for league ID: ${leagueId}.`);
|
||||
return Promise.resolve(upcomingRaces);
|
||||
@@ -45,15 +45,17 @@ export class InMemoryRaceRepository implements IRaceRepository {
|
||||
async findCompletedByLeagueId(leagueId: string): Promise<Race[]> {
|
||||
this.logger.debug(`[InMemoryRaceRepository] Finding completed races by league ID: ${leagueId}`);
|
||||
const completedRaces = Array.from(this.races.values()).filter(race =>
|
||||
race.leagueId === leagueId && race.status === 'completed'
|
||||
race.leagueId === leagueId && race.status.isCompleted()
|
||||
);
|
||||
this.logger.info(`Found ${completedRaces.length} completed races for league ID: ${leagueId}.`);
|
||||
return Promise.resolve(completedRaces);
|
||||
}
|
||||
|
||||
async findByStatus(status: RaceStatus): Promise<Race[]> {
|
||||
async findByStatus(status: RaceStatusValue): Promise<Race[]> {
|
||||
this.logger.debug(`[InMemoryRaceRepository] Finding races by status: ${status}.`);
|
||||
const races = Array.from(this.races.values()).filter(race => race.status === status);
|
||||
const races = Array.from(this.races.values()).filter(
|
||||
race => race.status.toString() === status,
|
||||
);
|
||||
this.logger.info(`Found ${races.length} races with status: ${status}.`);
|
||||
return Promise.resolve(races);
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ export class InMemorySeasonRepository implements ISeasonRepository {
|
||||
this.logger.debug(`Listing active seasons by league id: ${leagueId}`);
|
||||
try {
|
||||
const seasons = this.seasons.filter(
|
||||
(s) => s.leagueId === leagueId && s.status === 'active',
|
||||
(s) => s.leagueId === leagueId && s.status.isActive(),
|
||||
);
|
||||
this.logger.info(`Found ${seasons.length} active seasons for league id: ${leagueId}.`);
|
||||
return seasons;
|
||||
|
||||
@@ -78,7 +78,9 @@ export class InMemorySeasonRepository implements ISeasonRepository {
|
||||
|
||||
async listActiveByLeague(leagueId: string): Promise<Season[]> {
|
||||
this.logger.debug(`[InMemorySeasonRepository] Listing active seasons by league ID: ${leagueId}`);
|
||||
const activeSeasons = Array.from(this.seasons.values()).filter(season => season.leagueId === leagueId && season.status === 'active');
|
||||
const activeSeasons = Array.from(this.seasons.values()).filter(
|
||||
season => season.leagueId === leagueId && season.status.isActive(),
|
||||
);
|
||||
return Promise.resolve(activeSeasons);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@ export class InMemoryTrackRepository implements ITrackRepository {
|
||||
this.logger.debug(`Finding tracks by game id: ${gameId}`);
|
||||
try {
|
||||
const tracks = Array.from(this.tracks.values())
|
||||
.filter(track => track.gameId.props === gameId)
|
||||
.sort((a, b) => a.name.props.localeCompare(b.name.props));
|
||||
.filter(track => track.gameId.toString() === gameId)
|
||||
.sort((a, b) => a.name.toString().localeCompare(b.name.toString()));
|
||||
this.logger.info(`Found ${tracks.length} tracks for game id: ${gameId}.`);
|
||||
return tracks;
|
||||
} catch (error) {
|
||||
@@ -67,7 +67,7 @@ export class InMemoryTrackRepository implements ITrackRepository {
|
||||
try {
|
||||
const tracks = Array.from(this.tracks.values())
|
||||
.filter(track => track.category === category)
|
||||
.sort((a, b) => a.name.props.localeCompare(b.name.props));
|
||||
.sort((a, b) => a.name.toString().localeCompare(b.name.toString()));
|
||||
this.logger.info(`Found ${tracks.length} tracks for category: ${category}.`);
|
||||
return tracks;
|
||||
} catch (error) {
|
||||
@@ -80,8 +80,8 @@ export class InMemoryTrackRepository implements ITrackRepository {
|
||||
this.logger.debug(`Finding tracks by country: ${country}`);
|
||||
try {
|
||||
const tracks = Array.from(this.tracks.values())
|
||||
.filter(track => track.country.props.toLowerCase() === country.toLowerCase())
|
||||
.sort((a, b) => a.name.props.localeCompare(b.name.props));
|
||||
.filter(track => track.country.toString().toLowerCase() === country.toLowerCase())
|
||||
.sort((a, b) => a.name.toString().localeCompare(b.name.toString()));
|
||||
this.logger.info(`Found ${tracks.length} tracks for country: ${country}.`);
|
||||
return tracks;
|
||||
} catch (error) {
|
||||
@@ -96,10 +96,10 @@ export class InMemoryTrackRepository implements ITrackRepository {
|
||||
const lowerQuery = query.toLowerCase();
|
||||
const tracks = Array.from(this.tracks.values())
|
||||
.filter(track =>
|
||||
track.name.props.toLowerCase().includes(lowerQuery) ||
|
||||
track.shortName.props.toLowerCase().includes(lowerQuery)
|
||||
track.name.toString().toLowerCase().includes(lowerQuery) ||
|
||||
track.shortName.toString().toLowerCase().includes(lowerQuery)
|
||||
)
|
||||
.sort((a, b) => a.name.props.localeCompare(b.name.props));
|
||||
.sort((a, b) => a.name.toString().localeCompare(b.name.toString()));
|
||||
this.logger.info(`Found ${tracks.length} tracks matching search query: ${query}.`);
|
||||
return tracks;
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user