import type { DriverStats } from '@core/racing/application/use-cases/DriverStatsUseCase'; import { Driver } from '@core/racing/domain/entities/Driver'; import { Result } from '@core/racing/domain/entities/result/Result'; import type { Season } from '@core/racing/domain/entities/season/Season'; import { Standing } from '@core/racing/domain/entities/Standing'; import { Team } from '@core/racing/domain/entities/Team'; import type { DriverRepository } from '@core/racing/domain/repositories/DriverRepository'; import type { LeagueRepository } from '@core/racing/domain/repositories/LeagueRepository'; import type { SeasonRepository } from '@core/racing/domain/repositories/SeasonRepository'; import type { LeagueScoringConfigRepository } from '@core/racing/domain/repositories/LeagueScoringConfigRepository'; import type { SeasonSponsorshipRepository } from '@core/racing/domain/repositories/SeasonSponsorshipRepository'; import type { SponsorshipRequestRepository } from '@core/racing/domain/repositories/SponsorshipRequestRepository'; import type { LeagueWalletRepository } from '@core/racing/domain/repositories/LeagueWalletRepository'; import type { TransactionRepository } from '@core/racing/domain/repositories/TransactionRepository'; import type { ProtestRepository } from '@core/racing/domain/repositories/ProtestRepository'; import type { PenaltyRepository } from '@core/racing/domain/repositories/PenaltyRepository'; import type { RaceRepository } from '@core/racing/domain/repositories/RaceRepository'; import type { ResultRepository } from '@core/racing/domain/repositories/ResultRepository'; import type { StandingRepository } from '@core/racing/domain/repositories/StandingRepository'; import type { LeagueMembershipRepository } from '@core/racing/domain/repositories/LeagueMembershipRepository'; import type { RaceRegistrationRepository } from '@core/racing/domain/repositories/RaceRegistrationRepository'; import type { TeamRepository } from '@core/racing/domain/repositories/TeamRepository'; import type { TeamMembershipRepository } from '@core/racing/domain/repositories/TeamMembershipRepository'; import type { SponsorRepository } from '@core/racing/domain/repositories/SponsorRepository'; import type { FeedRepository } from '@core/social/domain/repositories/FeedRepository'; import type { SocialGraphRepository } from '@core/social/domain/repositories/SocialGraphRepository'; import type { DriverStatsRepository } from '@core/racing/domain/repositories/DriverStatsRepository'; import type { TeamStatsRepository, TeamStats } from '@core/racing/domain/repositories/TeamStatsRepository'; import type { MediaRepository } from '@core/racing/domain/repositories/MediaRepository'; import type { AuthRepository } from '@core/identity/domain/repositories/AuthRepository'; import type { PasswordHashingService } from '@core/identity/domain/services/PasswordHashingService'; import type { AdminUserRepository } from '@core/admin/domain/repositories/AdminUserRepository'; import type { Logger } from '@core/shared/domain/Logger'; import { getLeagueScoringPresetById } from './LeagueScoringPresets'; import { createRacingSeed } from './racing/RacingSeed'; import { seedId } from './racing/SeedIdHelper'; export type RacingSeedDependencies = { driverRepository: DriverRepository; leagueRepository: LeagueRepository; seasonRepository: SeasonRepository; leagueScoringConfigRepository: LeagueScoringConfigRepository; seasonSponsorshipRepository: SeasonSponsorshipRepository; sponsorshipRequestRepository: SponsorshipRequestRepository; leagueWalletRepository: LeagueWalletRepository; transactionRepository: TransactionRepository; protestRepository: ProtestRepository; penaltyRepository: PenaltyRepository; raceRepository: RaceRepository; resultRepository: ResultRepository; standingRepository: StandingRepository; leagueMembershipRepository: LeagueMembershipRepository; raceRegistrationRepository: RaceRegistrationRepository; teamRepository: TeamRepository; teamMembershipRepository: TeamMembershipRepository; sponsorRepository: SponsorRepository; feedRepository: FeedRepository; socialGraphRepository: SocialGraphRepository; driverStatsRepository: DriverStatsRepository; teamStatsRepository: TeamStatsRepository; mediaRepository: MediaRepository; // Identity dependencies for demo user seed authRepository: AuthRepository; passwordHashingService: PasswordHashingService; adminUserRepository: AdminUserRepository; }; export class SeedRacingData { constructor( private readonly logger: Logger, private readonly seedDeps: RacingSeedDependencies, ) {} private getApiPersistence(): 'postgres' | 'inmemory' { const configured = process.env.GRIDPILOT_API_PERSISTENCE?.toLowerCase(); if (configured === 'postgres' || configured === 'inmemory') { return configured; } if (process.env.NODE_ENV === 'test') { return 'inmemory'; } return process.env.DATABASE_URL ? 'postgres' : 'inmemory'; } async execute(): Promise { const existingDrivers = await this.seedDeps.driverRepository.findAll(); const existingTeams = await this.seedDeps.teamRepository.findAll().catch(() => []); const existingRaces = await this.seedDeps.raceRepository.findAll().catch(() => []); const persistence = this.getApiPersistence(); // Check for force reseed via environment variable const forceReseedRaw = process.env.GRIDPILOT_API_FORCE_RESEED; const forceReseed = forceReseedRaw !== undefined && forceReseedRaw !== '0' && forceReseedRaw.toLowerCase() !== 'false'; this.logger.info( `[Bootstrap] Racing seed precheck: forceReseed=${forceReseed}, drivers=${existingDrivers.length}, teams=${existingTeams.length}, races=${existingRaces.length}, persistence=${persistence}`, ); if (existingDrivers.length > 0 && !forceReseed) { this.logger.info('[Bootstrap] Racing seed skipped (drivers already exist), ensuring scoring configs'); await this.ensureScoringConfigsForExistingData(); // Even when skipping full seed, ensure stats are computed and stored this.logger.info('[Bootstrap] Computing and storing driver/team stats for existing data'); await this.computeAndStoreDriverStats(); await this.computeAndStoreTeamStats(); return; } // IMPORTANT: // Force reseed must clear even when drivers are already empty. // Otherwise stale teams can remain (e.g. with logoRef=system-default/logo), // and the seed will "ignore duplicates" on create, leaving stale logoRefs in Postgres. if (forceReseed) { this.logger.info( `[Bootstrap] Force reseed enabled - clearing existing racing data (drivers=${existingDrivers.length}, teams=${existingTeams.length})`, ); await this.clearExistingRacingData(); } const seed = createRacingSeed({ persistence, driverCount: 150 // Expanded from 100 to 150 }); // Clear existing stats repositories await this.seedDeps.driverStatsRepository.clear(); await this.seedDeps.teamStatsRepository.clear(); this.logger.info('[Bootstrap] Cleared existing stats repositories'); let sponsorshipRequestsSeededViaRepo = false; const seedableSponsorshipRequests = this.seedDeps .sponsorshipRequestRepository as unknown as { seed?: (input: unknown) => void }; if (typeof seedableSponsorshipRequests.seed === 'function') { seedableSponsorshipRequests.seed(seed.sponsorshipRequests); sponsorshipRequestsSeededViaRepo = true; } for (const driver of seed.drivers) { try { await this.seedDeps.driverRepository.create(driver); } catch { // ignore duplicates } } for (const league of seed.leagues) { try { await this.seedDeps.leagueRepository.create(league); } catch { // ignore duplicates } } for (const season of seed.seasons) { try { await this.seedDeps.seasonRepository.create(season); } catch { // ignore duplicates } } const activeSeasons = seed.seasons.filter((season) => season.status.isActive()); for (const season of activeSeasons) { const presetId = this.selectScoringPresetIdForSeason(season, persistence); const preset = getLeagueScoringPresetById(presetId); if (!preset) { this.logger.warn( `[Bootstrap] Scoring preset not found (presetId=${presetId}, seasonId=${season.id}, leagueId=${season.leagueId})`, ); continue; } const scoringConfig = preset.createConfig({ seasonId: season.id }); try { await this.seedDeps.leagueScoringConfigRepository.save(scoringConfig); } catch { // ignore duplicates } } for (const sponsorship of seed.seasonSponsorships) { try { await this.seedDeps.seasonSponsorshipRepository.create(sponsorship); } catch { // ignore duplicates } } if (!sponsorshipRequestsSeededViaRepo) { for (const request of seed.sponsorshipRequests) { try { await this.seedDeps.sponsorshipRequestRepository.create(request); } catch { // ignore duplicates } } } for (const wallet of seed.leagueWallets) { try { await this.seedDeps.leagueWalletRepository.create(wallet); } catch { // ignore duplicates } } for (const tx of seed.leagueWalletTransactions) { try { await this.seedDeps.transactionRepository.create(tx); } catch { // ignore duplicates } } for (const protest of seed.protests) { try { await this.seedDeps.protestRepository.create(protest); } catch { // ignore duplicates } } for (const penalty of seed.penalties) { try { await this.seedDeps.penaltyRepository.create(penalty); } catch { // ignore duplicates } } for (const race of seed.races) { try { await this.seedDeps.raceRepository.create(race); } catch { // ignore duplicates } } try { await this.seedDeps.resultRepository.createMany(seed.results); } catch { // ignore duplicates } for (const membership of seed.leagueMemberships) { try { await this.seedDeps.leagueMembershipRepository.saveMembership(membership); } catch { // ignore duplicates } } for (const request of seed.leagueJoinRequests) { try { await this.seedDeps.leagueMembershipRepository.saveJoinRequest(request); } catch { // ignore duplicates } } for (const team of seed.teams) { try { await this.seedDeps.teamRepository.create(team); } catch { // ignore duplicates } } for (const sponsor of seed.sponsors) { try { await this.seedDeps.sponsorRepository.create(sponsor); } catch { // ignore duplicates } } for (const membership of seed.teamMemberships) { try { await this.seedDeps.teamMembershipRepository.saveMembership(membership); } catch { // ignore duplicates } } for (const request of seed.teamJoinRequests) { try { await this.seedDeps.teamMembershipRepository.saveJoinRequest(request); } catch { // ignore duplicates } } for (const registration of seed.raceRegistrations) { try { await this.seedDeps.raceRegistrationRepository.register(registration); } catch { // ignore duplicates } } try { await this.seedDeps.standingRepository.saveMany(seed.standings); } catch { // ignore duplicates } const seedableFeed = this.seedDeps.feedRepository as unknown as { seed?: (input: any) => void }; if (typeof seedableFeed.seed === 'function') { seedableFeed.seed({ drivers: seed.drivers, friendships: seed.friendships, feedEvents: seed.feedEvents, }); } const seedableSocial = this.seedDeps.socialGraphRepository as unknown as { seed?: (input: any) => void }; if (typeof seedableSocial.seed === 'function') { seedableSocial.seed({ drivers: seed.drivers, friendships: seed.friendships, feedEvents: seed.feedEvents, }); } // Compute and store driver stats from real data await this.computeAndStoreDriverStats(); // Compute and store team stats from real data await this.computeAndStoreTeamStats(); // Log race distribution for transparency const raceStatusCounts = seed.races.reduce((acc, race) => { const status = race.status.toString(); acc[status] = (acc[status] || 0) + 1; return acc; }, {} as Record); const upcomingRaces = seed.races.filter((r) => r.status.toString() === 'scheduled' && r.scheduledAt > new Date()); this.logger.info( `[Bootstrap] Seeded racing data: drivers=${seed.drivers.length}, leagues=${seed.leagues.length}, races=${seed.races.length} (scheduled=${raceStatusCounts.scheduled || 0}, running=${raceStatusCounts.running || 0}, completed=${raceStatusCounts.completed || 0}, cancelled=${raceStatusCounts.cancelled || 0})`, ); this.logger.info( `[Bootstrap] Upcoming races: ${upcomingRaces.length} scheduled in the future`, ); } private async computeAndStoreDriverStats(): Promise { const drivers = await this.seedDeps.driverRepository.findAll(); const standings = await this.seedDeps.standingRepository.findAll(); const results = await this.seedDeps.resultRepository.findAll(); this.logger.info(`[Bootstrap] Computing stats for ${drivers.length} drivers from ${standings.length} standings and ${results.length} results`); for (const driver of drivers) { const driverResults = results.filter((r: Result) => r.driverId.toString() === driver.id); const driverStandings = standings.filter((s: Standing) => s.driverId.toString() === driver.id); if (driverResults.length === 0) continue; const stats = this.calculateDriverStats(driver, driverResults, driverStandings); await this.seedDeps.driverStatsRepository.saveDriverStats(driver.id, stats); } this.logger.info(`[Bootstrap] Computed and stored stats for ${drivers.length} drivers`); } private calculateDriverStats(driver: Driver, results: Result[], standings: Standing[]): DriverStats { const wins = results.filter(r => r.position.toNumber() === 1).length; const podiums = results.filter(r => r.position.toNumber() <= 3).length; const dnfs = results.filter(r => r.position.toNumber() > 20).length; const totalRaces = results.length; const positions = results.map(r => r.position.toNumber()); const avgFinish = positions.reduce((sum, pos) => sum + pos, 0) / totalRaces; const bestFinish = Math.min(...positions); const worstFinish = Math.max(...positions); // Calculate rating based on performance let rating = 1000; const driverStanding = standings.find(s => s.driverId.toString() === driver.id); if (driverStanding) { const pointsBonus = driverStanding.points.toNumber() * 2; const positionBonus = Math.max(0, 50 - (driverStanding.position.toNumber() * 2)); const winBonus = driverStanding.wins * 100; rating = Math.round(1000 + pointsBonus + positionBonus + winBonus); } else { const performanceBonus = ((totalRaces - wins) * 5) + ((totalRaces - podiums) * 2); rating = Math.round(1000 + (wins * 100) + (podiums * 50) - performanceBonus); } // Calculate consistency const avgPosition = avgFinish; const variance = positions.reduce((sum, pos) => sum + Math.pow(pos - avgPosition, 2), 0) / totalRaces; const consistency = Math.round(Math.max(0, 100 - (variance * 2))); // Safety rating (based on incidents) const totalIncidents = results.reduce((sum, r) => sum + r.incidents.toNumber(), 0); const safetyRating = Math.round(Math.max(0, 100 - (totalIncidents / totalRaces))); // Sportsmanship rating (placeholder) const sportsmanshipRating = 4.5; // Experience level const experienceLevel = this.determineExperienceLevel(totalRaces); // Overall rank const overallRank = driverStanding ? driverStanding.position.toNumber() : null; return { rating, safetyRating, sportsmanshipRating, totalRaces, wins, podiums, dnfs, avgFinish: Math.round(avgFinish * 10) / 10, bestFinish, worstFinish, consistency, experienceLevel, overallRank }; } private async computeAndStoreTeamStats(): Promise { const teams = await this.seedDeps.teamRepository.findAll(); const results = await this.seedDeps.resultRepository.findAll(); const drivers = await this.seedDeps.driverRepository.findAll(); this.logger.info(`[Bootstrap] Computing stats for ${teams.length} teams`); for (const team of teams) { // Get team members using the correct method const teamMemberships = await this.seedDeps.teamMembershipRepository.getTeamMembers(team.id); const teamMemberIds = teamMemberships.map((m: any) => m.driverId.toString()); // Get results for team members const teamResults = results.filter((r: Result) => teamMemberIds.includes(r.driverId.toString())); // Get team drivers for name resolution const teamDrivers = drivers.filter((d: Driver) => teamMemberIds.includes(d.id)); const stats = this.calculateTeamStats(team, teamResults, teamDrivers); await this.seedDeps.teamStatsRepository.saveTeamStats(team.id, stats); } this.logger.info(`[Bootstrap] Computed and stored stats for ${teams.length} teams`); } private calculateTeamStats(_team: Team, results: Result[], drivers: Driver[]): TeamStats { const wins = results.filter(r => r.position.toNumber() === 1).length; const totalRaces = results.length; // Calculate rating const baseRating = 1000; const winBonus = wins * 50; const raceBonus = Math.min(totalRaces * 5, 200); const rating = Math.round(baseRating + winBonus + raceBonus); // Determine performance level let performanceLevel: 'beginner' | 'intermediate' | 'advanced' | 'pro'; if (wins >= 20) performanceLevel = 'pro'; else if (wins >= 10) performanceLevel = 'advanced'; else if (wins >= 5) performanceLevel = 'intermediate'; else performanceLevel = 'beginner'; // Determine specialization (based on race types - simplified) const specialization: 'endurance' | 'sprint' | 'mixed' = 'mixed'; // Get region from team name or first driver const region = drivers.length > 0 && drivers[0] ? drivers[0].country.toString() : 'International'; // Languages (based on drivers) const languages = Array.from(new Set(drivers.map(d => { // Simplified language mapping based on country const country = d.country.toString().toLowerCase(); if (country === 'us' || country === 'gb' || country === 'ca') return 'en'; if (country === 'de') return 'de'; if (country === 'fr') return 'fr'; if (country === 'es') return 'es'; if (country === 'it') return 'it'; if (country === 'jp') return 'ja'; return 'en'; }))); return { performanceLevel, specialization, region, languages, totalWins: wins, totalRaces, rating }; } private determineExperienceLevel(totalRaces: number): string { if (totalRaces >= 100) return 'Veteran'; if (totalRaces >= 50) return 'Experienced'; if (totalRaces >= 20) return 'Intermediate'; if (totalRaces >= 10) return 'Rookie'; return 'Beginner'; } private async clearExistingRacingData(): Promise { this.logger.info('[Bootstrap] Starting comprehensive clearing of all racing data'); // Clear stats repositories first try { await this.seedDeps.driverStatsRepository.clear(); await this.seedDeps.teamStatsRepository.clear(); this.logger.info('[Bootstrap] Cleared stats repositories'); } catch (error) { this.logger.warn('[Bootstrap] Could not clear stats repositories:', error); } // Clear race registrations - get all races first, then clear their registrations try { const races = await this.seedDeps.raceRepository.findAll(); for (const race of races) { try { await this.seedDeps.raceRegistrationRepository.clearRaceRegistrations(race.id.toString()); } catch { // Ignore } } this.logger.info('[Bootstrap] Cleared race registrations'); } catch (error) { this.logger.warn('[Bootstrap] Could not clear race registrations:', error); } // Clear team join requests - get all teams first, then clear their join requests try { const teams = await this.seedDeps.teamRepository.findAll(); for (const team of teams) { const joinRequests = await this.seedDeps.teamMembershipRepository.getJoinRequests(team.id.toString()); for (const request of joinRequests) { try { await this.seedDeps.teamMembershipRepository.removeJoinRequest(request.id); } catch { // Ignore } } } this.logger.info('[Bootstrap] Cleared team join requests'); } catch (error) { this.logger.warn('[Bootstrap] Could not clear team join requests:', error); } // Clear team memberships try { const teams = await this.seedDeps.teamRepository.findAll(); for (const team of teams) { const memberships = await this.seedDeps.teamMembershipRepository.getTeamMembers(team.id.toString()); for (const membership of memberships) { try { await this.seedDeps.teamMembershipRepository.removeMembership(team.id.toString(), membership.driverId.toString()); } catch { // Ignore } } } this.logger.info('[Bootstrap] Cleared team memberships'); } catch (error) { this.logger.warn('[Bootstrap] Could not clear team memberships:', error); } // Clear teams (this is critical - teams have stale logoRef) try { const teams = await this.seedDeps.teamRepository.findAll(); for (const team of teams) { try { await this.seedDeps.teamRepository.delete(team.id.toString()); } catch { // Ignore } } this.logger.info('[Bootstrap] Cleared teams'); } catch (error) { this.logger.warn('[Bootstrap] Could not clear teams:', error); } // Clear results try { const results = await this.seedDeps.resultRepository.findAll(); for (const result of results) { try { await this.seedDeps.resultRepository.delete(result.id.toString()); } catch { // Ignore } } this.logger.info('[Bootstrap] Cleared results'); } catch (error) { this.logger.warn('[Bootstrap] Could not clear results:', error); } // Clear standings try { const standings = await this.seedDeps.standingRepository.findAll(); for (const standing of standings) { try { await this.seedDeps.standingRepository.delete(standing.leagueId.toString(), standing.driverId.toString()); } catch { // Ignore } } this.logger.info('[Bootstrap] Cleared standings'); } catch (error) { this.logger.warn('[Bootstrap] Could not clear standings:', error); } // Clear races try { const races = await this.seedDeps.raceRepository.findAll(); for (const race of races) { try { await this.seedDeps.raceRepository.delete(race.id.toString()); } catch { // Ignore } } this.logger.info('[Bootstrap] Cleared races'); } catch (error) { this.logger.warn('[Bootstrap] Could not clear races:', error); } // Clear league join requests try { const leagues = await this.seedDeps.leagueRepository.findAll(); for (const league of leagues) { const joinRequests = await this.seedDeps.leagueMembershipRepository.getJoinRequests(league.id.toString()); for (const request of joinRequests) { try { await this.seedDeps.leagueMembershipRepository.removeJoinRequest(request.id); } catch { // Ignore } } } this.logger.info('[Bootstrap] Cleared league join requests'); } catch (error) { this.logger.warn('[Bootstrap] Could not clear league join requests:', error); } // Clear league memberships try { const leagues = await this.seedDeps.leagueRepository.findAll(); for (const league of leagues) { const memberships = await this.seedDeps.leagueMembershipRepository.getLeagueMembers(league.id.toString()); for (const membership of memberships) { try { await this.seedDeps.leagueMembershipRepository.removeMembership(league.id.toString(), membership.driverId.toString()); } catch { // Ignore } } } this.logger.info('[Bootstrap] Cleared league memberships'); } catch (error) { this.logger.warn('[Bootstrap] Could not clear league memberships:', error); } // Note: Some repositories don't support direct deletion methods // The key fix is clearing teams, team memberships, and join requests // which resolves the logoRef issue // Clear leagues try { const leagues = await this.seedDeps.leagueRepository.findAll(); for (const league of leagues) { try { await this.seedDeps.leagueRepository.delete(league.id.toString()); } catch { // Ignore } } this.logger.info('[Bootstrap] Cleared leagues'); } catch (error) { this.logger.warn('[Bootstrap] Could not clear leagues:', error); } // Clear drivers (do this last as other data depends on it) try { const drivers = await this.seedDeps.driverRepository.findAll(); for (const driver of drivers) { try { await this.seedDeps.driverRepository.delete(driver.id); } catch { // Ignore } } this.logger.info('[Bootstrap] Cleared drivers'); } catch (error) { this.logger.warn('[Bootstrap] Could not clear drivers:', error); } // Clear social data if repositories support it try { const seedableFeed = this.seedDeps.feedRepository as unknown as { clear?: () => void }; if (typeof seedableFeed.clear === 'function') { seedableFeed.clear(); this.logger.info('[Bootstrap] Cleared feed repository'); } } catch (error) { this.logger.warn('[Bootstrap] Could not clear feed repository:', error); } try { const seedableSocial = this.seedDeps.socialGraphRepository as unknown as { clear?: () => void }; if (typeof seedableSocial.clear === 'function') { seedableSocial.clear(); this.logger.info('[Bootstrap] Cleared social graph repository'); } } catch (error) { this.logger.warn('[Bootstrap] Could not clear social graph repository:', error); } this.logger.info('[Bootstrap] Completed comprehensive clearing of all racing data'); } private async ensureScoringConfigsForExistingData(): Promise { const leagues = await this.seedDeps.leagueRepository.findAll(); for (const league of leagues) { const seasons = await this.seedDeps.seasonRepository.findByLeagueId(league.id.toString()); const activeSeasons = seasons.filter((season) => season.status.isActive()); for (const season of activeSeasons) { const existing = await this.seedDeps.leagueScoringConfigRepository.findBySeasonId(season.id); if (existing) continue; const presetId = this.selectScoringPresetIdForSeason(season, 'postgres'); const preset = getLeagueScoringPresetById(presetId); if (!preset) { this.logger.warn( `[Bootstrap] Scoring preset not found (presetId=${presetId}, seasonId=${season.id}, leagueId=${season.leagueId})`, ); continue; } const scoringConfig = preset.createConfig({ seasonId: season.id }); try { await this.seedDeps.leagueScoringConfigRepository.save(scoringConfig); } catch { // ignore duplicates } } } } private selectScoringPresetIdForSeason(season: Season, persistence: 'postgres' | 'inmemory'): string { const expectedLeagueId = seedId('league-5', persistence); const expectedSeasonId = seedId('season-1-b', persistence); if (season.leagueId === expectedLeagueId && season.status.isActive()) { return 'sprint-main-driver'; } if (season.leagueId === seedId('league-3', persistence)) { return season.id === expectedSeasonId ? 'sprint-main-team' : 'club-default-nations'; } const match = /^league-(\d+)$/.exec(season.leagueId); const leagueNumber = match ? Number(match[1]) : undefined; if (leagueNumber !== undefined) { switch (leagueNumber % 4) { case 0: return 'sprint-main-team'; case 1: return 'endurance-main-trophy'; case 2: return 'sprint-main-driver'; case 3: return 'club-default-nations'; } } return 'club-default'; } }