451 lines
14 KiB
TypeScript
451 lines
14 KiB
TypeScript
import { League } from '@core/racing/domain/entities/League';
|
|
import { Race } from '@core/racing/domain/entities/Race';
|
|
import { Driver } from '@core/racing/domain/entities/Driver';
|
|
import { Money } from '@core/racing/domain/value-objects/Money';
|
|
import { SponsorshipPricing } from '@core/racing/domain/value-objects/SponsorshipPricing';
|
|
import {
|
|
SponsorshipRequest,
|
|
type SponsorableEntityType,
|
|
} from '@core/racing/domain/entities/SponsorshipRequest';
|
|
|
|
import type { DemoTeamDTO } from './RacingSeedCore';
|
|
|
|
/**
|
|
* Demo sponsor data for seeding.
|
|
*/
|
|
export interface DemoSponsorDTO {
|
|
id: string;
|
|
name: string;
|
|
contactEmail: string;
|
|
logoUrl: string;
|
|
websiteUrl: string;
|
|
tagline: string;
|
|
}
|
|
|
|
/**
|
|
* Demo season sponsorship data.
|
|
* This remains a simple DTO since the SeasonSponsorship
|
|
* domain entity is instantiated in the DI config.
|
|
*/
|
|
export interface DemoSeasonSponsorshipDTO {
|
|
id: string;
|
|
seasonId: string;
|
|
sponsorId: string;
|
|
tier: 'main' | 'secondary';
|
|
pricingAmount: number;
|
|
pricingCurrency: 'USD' | 'EUR' | 'GBP';
|
|
status: 'pending' | 'active' | 'cancelled';
|
|
description?: string;
|
|
}
|
|
|
|
/**
|
|
* Demo sponsorship request data for seeding.
|
|
* Backed directly by the SponsorshipRequest domain entity.
|
|
*/
|
|
export type DemoSponsorshipRequestDTO = SponsorshipRequest;
|
|
|
|
/**
|
|
* Demo sponsorship pricing configuration for entities, using the
|
|
* SponsorshipPricing value object to keep pricing logic in the domain.
|
|
*/
|
|
export interface DemoSponsorshipPricingDTO {
|
|
entityType: SponsorableEntityType;
|
|
entityId: string;
|
|
pricing: SponsorshipPricing;
|
|
}
|
|
|
|
/**
|
|
* Demo sponsors data - realistic sim racing sponsors.
|
|
*/
|
|
export const DEMO_SPONSORS: DemoSponsorDTO[] = [
|
|
{
|
|
id: 'sponsor-fanatec',
|
|
name: 'Fanatec',
|
|
contactEmail: 'partnerships@fanatec.com',
|
|
logoUrl: '/images/sponsors/fanatec.svg',
|
|
websiteUrl: 'https://fanatec.com',
|
|
tagline: "The world's leading sim racing hardware",
|
|
},
|
|
{
|
|
id: 'sponsor-simucube',
|
|
name: 'Simucube',
|
|
contactEmail: 'sponsors@simucube.com',
|
|
logoUrl: '/images/sponsors/simucube.svg',
|
|
websiteUrl: 'https://simucube.com',
|
|
tagline: 'Professional Direct Drive Wheels',
|
|
},
|
|
{
|
|
id: 'sponsor-heusinkveld',
|
|
name: 'Heusinkveld',
|
|
contactEmail: 'info@heusinkveld.com',
|
|
logoUrl: '/images/sponsors/heusinkveld.svg',
|
|
websiteUrl: 'https://heusinkveld.com',
|
|
tagline: 'Sim Racing Pedals & Hardware',
|
|
},
|
|
{
|
|
id: 'sponsor-trak-racer',
|
|
name: 'Trak Racer',
|
|
contactEmail: 'partnerships@trakracer.com',
|
|
logoUrl: '/images/sponsors/trak-racer.svg',
|
|
websiteUrl: 'https://trakracer.com',
|
|
tagline: 'Premium Racing Simulators & Cockpits',
|
|
},
|
|
{
|
|
id: 'sponsor-simlab',
|
|
name: 'Sim-Lab',
|
|
contactEmail: 'sponsor@sim-lab.eu',
|
|
logoUrl: '/images/sponsors/simlab.svg',
|
|
websiteUrl: 'https://sim-lab.eu',
|
|
tagline: 'Aluminum Profile Sim Racing Rigs',
|
|
},
|
|
{
|
|
id: 'sponsor-motionrig',
|
|
name: 'MotionRig Pro',
|
|
contactEmail: 'business@motionrigpro.com',
|
|
logoUrl: '/images/sponsors/motionrig.svg',
|
|
websiteUrl: 'https://motionrigpro.com',
|
|
tagline: 'Feel every turn, every bump',
|
|
},
|
|
];
|
|
|
|
/**
|
|
* Create season sponsorships linking sponsors to leagues.
|
|
*/
|
|
export function createSeasonSponsorships(
|
|
leagues: League[],
|
|
sponsors: DemoSponsorDTO[],
|
|
): DemoSeasonSponsorshipDTO[] {
|
|
const sponsorships: DemoSeasonSponsorshipDTO[] = [];
|
|
|
|
const FANATEC_ID = sponsors.find((s) => s.id === 'sponsor-fanatec')?.id ?? 'sponsor-fanatec';
|
|
const HEUSINKVELD_ID =
|
|
sponsors.find((s) => s.id === 'sponsor-heusinkveld')?.id ?? 'sponsor-heusinkveld';
|
|
const SIMUCUBE_ID = sponsors.find((s) => s.id === 'sponsor-simucube')?.id ?? 'sponsor-simucube';
|
|
const TRAK_RACER_ID =
|
|
sponsors.find((s) => s.id === 'sponsor-trak-racer')?.id ?? 'sponsor-trak-racer';
|
|
const SIMLAB_ID = sponsors.find((s) => s.id === 'sponsor-simlab')?.id ?? 'sponsor-simlab';
|
|
const MOTIONRIG_ID =
|
|
sponsors.find((s) => s.id === 'sponsor-motionrig')?.id ?? 'sponsor-motionrig';
|
|
|
|
// GridPilot Sprint Series - sponsored by Fanatec (main) + Heusinkveld & Simucube (secondary)
|
|
const sprintLeague = leagues.find((l) => l.name === 'GridPilot Sprint Series');
|
|
if (sprintLeague) {
|
|
sponsorships.push({
|
|
id: `sponsorship-${sprintLeague.id}-fanatec`,
|
|
seasonId: `season-${sprintLeague.id}-demo`,
|
|
sponsorId: FANATEC_ID,
|
|
tier: 'main',
|
|
pricingAmount: 5000,
|
|
pricingCurrency: 'USD',
|
|
status: 'active',
|
|
description: 'Main sponsor for the Sprint Series - premium wheel branding',
|
|
});
|
|
sponsorships.push({
|
|
id: `sponsorship-${sprintLeague.id}-heusinkveld`,
|
|
seasonId: `season-${sprintLeague.id}-demo`,
|
|
sponsorId: HEUSINKVELD_ID,
|
|
tier: 'secondary',
|
|
pricingAmount: 2000,
|
|
pricingCurrency: 'USD',
|
|
status: 'active',
|
|
});
|
|
sponsorships.push({
|
|
id: `sponsorship-${sprintLeague.id}-simucube`,
|
|
seasonId: `season-${sprintLeague.id}-demo`,
|
|
sponsorId: SIMUCUBE_ID,
|
|
tier: 'secondary',
|
|
pricingAmount: 2000,
|
|
pricingCurrency: 'USD',
|
|
status: 'active',
|
|
});
|
|
}
|
|
|
|
// GridPilot Endurance Cup - sponsored by Trak Racer (main) + Sim-Lab (secondary)
|
|
const enduranceLeague = leagues.find((l) => l.name === 'GridPilot Endurance Cup');
|
|
if (enduranceLeague) {
|
|
sponsorships.push({
|
|
id: `sponsorship-${enduranceLeague.id}-trakracer`,
|
|
seasonId: `season-${enduranceLeague.id}-demo`,
|
|
sponsorId: TRAK_RACER_ID,
|
|
tier: 'main',
|
|
pricingAmount: 7500,
|
|
pricingCurrency: 'USD',
|
|
status: 'active',
|
|
description: 'Endurance series naming rights',
|
|
});
|
|
sponsorships.push({
|
|
id: `sponsorship-${enduranceLeague.id}-simlab`,
|
|
seasonId: `season-${enduranceLeague.id}-demo`,
|
|
sponsorId: SIMLAB_ID,
|
|
tier: 'secondary',
|
|
pricingAmount: 3000,
|
|
pricingCurrency: 'USD',
|
|
status: 'active',
|
|
});
|
|
}
|
|
|
|
// GridPilot Club Ladder - sponsored by MotionRig Pro (main)
|
|
const clubLeague = leagues.find((l) => l.name === 'GridPilot Club Ladder');
|
|
if (clubLeague) {
|
|
sponsorships.push({
|
|
id: `sponsorship-${clubLeague.id}-motionrig`,
|
|
seasonId: `season-${clubLeague.id}-demo`,
|
|
sponsorId: MOTIONRIG_ID,
|
|
tier: 'main',
|
|
pricingAmount: 3500,
|
|
pricingCurrency: 'USD',
|
|
status: 'active',
|
|
description: 'Club ladder motion platform showcase',
|
|
});
|
|
}
|
|
|
|
return sponsorships;
|
|
}
|
|
|
|
/**
|
|
* Create sponsorship pricing configurations for demo entities.
|
|
* Uses the SponsorshipPricing value object to ensure domain consistency
|
|
* and to provide a mix of season, team, driver and race offerings.
|
|
*/
|
|
export function createSponsorshipPricings(
|
|
leagues: League[],
|
|
teams: DemoTeamDTO[],
|
|
drivers: Driver[],
|
|
races: Race[],
|
|
): DemoSponsorshipPricingDTO[] {
|
|
const pricings: DemoSponsorshipPricingDTO[] = [];
|
|
|
|
// League/Season pricing - all leagues can accept sponsorships, with varied configs
|
|
leagues.forEach((league, index) => {
|
|
let pricing = SponsorshipPricing.defaultLeague();
|
|
|
|
// Vary league pricing/availability for demo richness
|
|
if (index % 3 === 1) {
|
|
// Some leagues closed for applications
|
|
pricing = pricing.setAcceptingApplications(false);
|
|
} else if (index % 3 === 2) {
|
|
// Some leagues with main-only sponsorship
|
|
pricing = pricing.updateSecondarySlot({ available: false, maxSlots: 0 });
|
|
} else {
|
|
// Slightly higher price for featured leagues
|
|
pricing = pricing.updateMainSlot({
|
|
price: Money.create(1000 + index * 50, 'USD'),
|
|
});
|
|
}
|
|
|
|
pricings.push({
|
|
entityType: 'season',
|
|
entityId: `season-${league.id}-demo`,
|
|
pricing,
|
|
});
|
|
});
|
|
|
|
// Team pricing - first 10 teams accept sponsorships using team defaults,
|
|
// with some teams pausing applications.
|
|
teams.slice(0, 10).forEach((team, index) => {
|
|
let pricing = SponsorshipPricing.defaultTeam();
|
|
|
|
if (index % 4 === 1) {
|
|
// Teams with main + secondary but not currently accepting
|
|
pricing = pricing.setAcceptingApplications(false);
|
|
} else if (index % 4 === 2) {
|
|
// Teams with only secondary slots
|
|
pricing = pricing.updateMainSlot({ available: false, maxSlots: 0 });
|
|
} else if (index % 4 === 3) {
|
|
// Teams with premium main slot pricing
|
|
pricing = pricing.updateMainSlot({
|
|
price: Money.create(750 + index * 25, 'USD'),
|
|
});
|
|
}
|
|
|
|
pricings.push({
|
|
entityType: 'team',
|
|
entityId: team.id,
|
|
pricing,
|
|
});
|
|
});
|
|
|
|
// Driver pricing - first 20 drivers accept sponsorships with varied availability.
|
|
drivers.slice(0, 20).forEach((driver, index) => {
|
|
let pricing = SponsorshipPricing.defaultDriver();
|
|
|
|
if (index % 3 === 0) {
|
|
// Higher profile drivers
|
|
pricing = pricing.updateMainSlot({
|
|
price: Money.create(250 + index * 10, 'USD'),
|
|
});
|
|
} else if (index % 3 === 1) {
|
|
// Drivers temporarily not accepting sponsorships
|
|
pricing = pricing.setAcceptingApplications(false);
|
|
}
|
|
|
|
pricings.push({
|
|
entityType: 'driver',
|
|
entityId: driver.id,
|
|
pricing,
|
|
});
|
|
});
|
|
|
|
// Race pricing - upcoming races can have title sponsors with different tiers
|
|
const upcomingRaces = races.filter((r) => r.status === 'scheduled').slice(0, 10);
|
|
upcomingRaces.forEach((race, index) => {
|
|
let pricing = SponsorshipPricing.defaultRace();
|
|
|
|
if (index % 2 === 0) {
|
|
// Premium events with higher pricing
|
|
pricing = pricing.updateMainSlot({
|
|
price: Money.create(350 + index * 30, 'USD'),
|
|
});
|
|
}
|
|
|
|
pricings.push({
|
|
entityType: 'race',
|
|
entityId: race.id,
|
|
pricing,
|
|
});
|
|
});
|
|
|
|
return pricings;
|
|
}
|
|
|
|
/**
|
|
* Create demo sponsorship requests (some pending, some accepted/rejected).
|
|
* Uses the SponsorshipRequest domain entity and Money value object so that
|
|
* all downstream sponsor flows can rely on domain behavior.
|
|
*/
|
|
export function createSponsorshipRequests(
|
|
sponsors: DemoSponsorDTO[],
|
|
leagues: League[],
|
|
teams: DemoTeamDTO[],
|
|
drivers: Driver[],
|
|
races: Race[],
|
|
): DemoSponsorshipRequestDTO[] {
|
|
const requests: DemoSponsorshipRequestDTO[] = [];
|
|
const now = new Date();
|
|
|
|
const SIMUCUBE_ID = sponsors.find((s) => s.id === 'sponsor-simucube')?.id ?? 'sponsor-simucube';
|
|
const HEUSINKVELD_ID =
|
|
sponsors.find((s) => s.id === 'sponsor-heusinkveld')?.id ?? 'sponsor-heusinkveld';
|
|
const TRAK_RACER_ID =
|
|
sponsors.find((s) => s.id === 'sponsor-trak-racer')?.id ?? 'sponsor-trak-racer';
|
|
const MOTIONRIG_ID =
|
|
sponsors.find((s) => s.id === 'sponsor-motionrig')?.id ?? 'sponsor-motionrig';
|
|
const SIMLAB_ID = sponsors.find((s) => s.id === 'sponsor-simlab')?.id ?? 'sponsor-simlab';
|
|
|
|
// Pending request: Simucube wants to sponsor a driver
|
|
if (drivers.length > 6) {
|
|
const targetDriver = drivers[5];
|
|
if (targetDriver) {
|
|
requests.push(
|
|
SponsorshipRequest.create({
|
|
id: 'req-simucube-driver-1',
|
|
sponsorId: SIMUCUBE_ID,
|
|
entityType: 'driver',
|
|
entityId: targetDriver.id,
|
|
tier: 'main',
|
|
offeredAmount: Money.create(250, 'USD'),
|
|
message:
|
|
'We would love to sponsor your racing career! Simucube offers the best direct drive wheels in sim racing.',
|
|
createdAt: new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000), // 2 days ago
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
// Pending request: Heusinkveld wants to sponsor a team
|
|
if (teams.length > 3) {
|
|
const targetTeam = teams[2];
|
|
if (targetTeam) {
|
|
requests.push(
|
|
SponsorshipRequest.create({
|
|
id: 'req-heusinkveld-team-1',
|
|
sponsorId: HEUSINKVELD_ID,
|
|
entityType: 'team',
|
|
entityId: targetTeam.id,
|
|
tier: 'main',
|
|
offeredAmount: Money.create(550, 'USD'),
|
|
message:
|
|
'Heusinkveld pedals are known for their precision. We believe your team embodies the same values.',
|
|
createdAt: new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000), // 3 days ago
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
// Pending request: Trak Racer wants to sponsor a race
|
|
const upcomingRace = races.find((r) => r.status === 'scheduled');
|
|
if (upcomingRace) {
|
|
requests.push(
|
|
SponsorshipRequest.create({
|
|
id: 'req-trakracer-race-1',
|
|
sponsorId: TRAK_RACER_ID,
|
|
entityType: 'race',
|
|
entityId: upcomingRace.id,
|
|
tier: 'main',
|
|
offeredAmount: Money.create(350, 'USD'),
|
|
message: 'We would like to be the title sponsor for this exciting race event!',
|
|
createdAt: new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000), // 1 day ago
|
|
}),
|
|
);
|
|
}
|
|
|
|
// Pending request: MotionRig Pro wants secondary spot on a league season
|
|
const clubLeague = leagues.find((l) => l.name === 'Sprint Challenge League');
|
|
if (clubLeague) {
|
|
requests.push(
|
|
SponsorshipRequest.create({
|
|
id: 'req-motionrig-league-1',
|
|
sponsorId: MOTIONRIG_ID,
|
|
entityType: 'season',
|
|
entityId: `season-${clubLeague.id}-demo`,
|
|
tier: 'secondary',
|
|
offeredAmount: Money.create(1500, 'USD'),
|
|
message:
|
|
'MotionRig Pro would love to be a secondary sponsor. Our motion platforms are perfect for your competitive drivers.',
|
|
createdAt: new Date(now.getTime() - 5 * 24 * 60 * 60 * 1000), // 5 days ago
|
|
}),
|
|
);
|
|
}
|
|
|
|
// Already accepted request (for history)
|
|
if (teams.length > 0) {
|
|
const acceptedTeam = teams[0];
|
|
if (acceptedTeam) {
|
|
requests.push(
|
|
SponsorshipRequest.create({
|
|
id: 'req-simlab-team-accepted',
|
|
sponsorId: SIMLAB_ID,
|
|
entityType: 'team',
|
|
entityId: acceptedTeam.id,
|
|
tier: 'secondary',
|
|
offeredAmount: Money.create(300, 'USD'),
|
|
message: 'Sim-Lab rigs are the foundation of any competitive setup.',
|
|
status: 'accepted',
|
|
createdAt: new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000), // 30 days ago
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
// Already rejected request (for history)
|
|
if (drivers.length > 10) {
|
|
const rejectedDriver = drivers[10];
|
|
if (rejectedDriver) {
|
|
requests.push(
|
|
SponsorshipRequest.create({
|
|
id: 'req-motionrig-driver-rejected',
|
|
sponsorId: MOTIONRIG_ID,
|
|
entityType: 'driver',
|
|
entityId: rejectedDriver.id,
|
|
tier: 'main',
|
|
offeredAmount: Money.create(150, 'USD'),
|
|
message: 'Would you like to represent MotionRig Pro?',
|
|
status: 'rejected',
|
|
createdAt: new Date(now.getTime() - 20 * 24 * 60 * 60 * 1000), // 20 days ago
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
return requests;
|
|
} |