209 lines
5.4 KiB
TypeScript
209 lines
5.4 KiB
TypeScript
/**
|
|
* Value Object: SponsorshipPricing
|
|
*
|
|
* Represents the sponsorship slot configuration and pricing for any sponsorable entity.
|
|
* Used by drivers, teams, races, and leagues to define their sponsorship offerings.
|
|
*/
|
|
|
|
import { Money } from './Money';
|
|
import type { IValueObject } from '@gridpilot/shared/domain';
|
|
|
|
export interface SponsorshipSlotConfig {
|
|
tier: 'main' | 'secondary';
|
|
price: Money;
|
|
benefits: string[];
|
|
available: boolean;
|
|
maxSlots: number; // How many sponsors of this tier can exist (1 for main, 2 for secondary typically)
|
|
}
|
|
|
|
export interface SponsorshipPricingProps {
|
|
mainSlot?: SponsorshipSlotConfig;
|
|
secondarySlots?: SponsorshipSlotConfig;
|
|
acceptingApplications: boolean;
|
|
customRequirements?: string;
|
|
}
|
|
|
|
export class SponsorshipPricing implements IValueObject<SponsorshipPricingProps> {
|
|
readonly mainSlot?: SponsorshipSlotConfig;
|
|
readonly secondarySlots?: SponsorshipSlotConfig;
|
|
readonly acceptingApplications: boolean;
|
|
readonly customRequirements?: string;
|
|
|
|
private constructor(props: SponsorshipPricingProps) {
|
|
this.mainSlot = props.mainSlot;
|
|
this.secondarySlots = props.secondarySlots;
|
|
this.acceptingApplications = props.acceptingApplications;
|
|
this.customRequirements = props.customRequirements;
|
|
}
|
|
|
|
static create(props: Partial<SponsorshipPricingProps> = {}): SponsorshipPricing {
|
|
return new SponsorshipPricing({
|
|
mainSlot: props.mainSlot,
|
|
secondarySlots: props.secondarySlots,
|
|
acceptingApplications: props.acceptingApplications ?? true,
|
|
customRequirements: props.customRequirements,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create default pricing for a driver
|
|
*/
|
|
static defaultDriver(): SponsorshipPricing {
|
|
return new SponsorshipPricing({
|
|
mainSlot: {
|
|
tier: 'main',
|
|
price: Money.create(200, 'USD'),
|
|
benefits: ['Suit logo', 'Helmet branding', 'Social mentions'],
|
|
available: true,
|
|
maxSlots: 1,
|
|
},
|
|
acceptingApplications: true,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create default pricing for a team
|
|
*/
|
|
static defaultTeam(): SponsorshipPricing {
|
|
return new SponsorshipPricing({
|
|
mainSlot: {
|
|
tier: 'main',
|
|
price: Money.create(500, 'USD'),
|
|
benefits: ['Team name suffix', 'Car livery', 'All driver suits'],
|
|
available: true,
|
|
maxSlots: 1,
|
|
},
|
|
secondarySlots: {
|
|
tier: 'secondary',
|
|
price: Money.create(250, 'USD'),
|
|
benefits: ['Team page logo', 'Minor livery placement'],
|
|
available: true,
|
|
maxSlots: 2,
|
|
},
|
|
acceptingApplications: true,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create default pricing for a race
|
|
*/
|
|
static defaultRace(): SponsorshipPricing {
|
|
return new SponsorshipPricing({
|
|
mainSlot: {
|
|
tier: 'main',
|
|
price: Money.create(300, 'USD'),
|
|
benefits: ['Race title sponsor', 'Stream overlay', 'Results banner'],
|
|
available: true,
|
|
maxSlots: 1,
|
|
},
|
|
acceptingApplications: true,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create default pricing for a league/season
|
|
*/
|
|
static defaultLeague(): SponsorshipPricing {
|
|
return new SponsorshipPricing({
|
|
mainSlot: {
|
|
tier: 'main',
|
|
price: Money.create(800, 'USD'),
|
|
benefits: ['Hood placement', 'League banner', 'Prominent logo', 'League page URL'],
|
|
available: true,
|
|
maxSlots: 1,
|
|
},
|
|
secondarySlots: {
|
|
tier: 'secondary',
|
|
price: Money.create(250, 'USD'),
|
|
benefits: ['Side logo placement', 'League page listing'],
|
|
available: true,
|
|
maxSlots: 2,
|
|
},
|
|
acceptingApplications: true,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Check if a specific tier is available
|
|
*/
|
|
isSlotAvailable(tier: 'main' | 'secondary'): boolean {
|
|
if (tier === 'main') {
|
|
return !!this.mainSlot?.available;
|
|
}
|
|
return !!this.secondarySlots?.available;
|
|
}
|
|
|
|
/**
|
|
* Get price for a specific tier
|
|
*/
|
|
getPrice(tier: 'main' | 'secondary'): Money | null {
|
|
if (tier === 'main') {
|
|
return this.mainSlot?.price ?? null;
|
|
}
|
|
return this.secondarySlots?.price ?? null;
|
|
}
|
|
|
|
/**
|
|
* Get benefits for a specific tier
|
|
*/
|
|
getBenefits(tier: 'main' | 'secondary'): string[] {
|
|
if (tier === 'main') {
|
|
return this.mainSlot?.benefits ?? [];
|
|
}
|
|
return this.secondarySlots?.benefits ?? [];
|
|
}
|
|
|
|
/**
|
|
* Update main slot pricing
|
|
*/
|
|
updateMainSlot(config: Partial<SponsorshipSlotConfig>): SponsorshipPricing {
|
|
const currentMain = this.mainSlot ?? {
|
|
tier: 'main' as const,
|
|
price: Money.create(0, 'USD'),
|
|
benefits: [],
|
|
available: true,
|
|
maxSlots: 1,
|
|
};
|
|
|
|
return new SponsorshipPricing({
|
|
...this,
|
|
mainSlot: {
|
|
...currentMain,
|
|
...config,
|
|
tier: 'main',
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update secondary slot pricing
|
|
*/
|
|
updateSecondarySlot(config: Partial<SponsorshipSlotConfig>): SponsorshipPricing {
|
|
const currentSecondary = this.secondarySlots ?? {
|
|
tier: 'secondary' as const,
|
|
price: Money.create(0, 'USD'),
|
|
benefits: [],
|
|
available: true,
|
|
maxSlots: 2,
|
|
};
|
|
|
|
return new SponsorshipPricing({
|
|
...this,
|
|
secondarySlots: {
|
|
...currentSecondary,
|
|
...config,
|
|
tier: 'secondary',
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Enable/disable accepting applications
|
|
*/
|
|
setAcceptingApplications(accepting: boolean): SponsorshipPricing {
|
|
return new SponsorshipPricing({
|
|
...this,
|
|
acceptingApplications: accepting,
|
|
});
|
|
}
|
|
} |