rename to core
This commit is contained in:
262
core/racing/domain/value-objects/SponsorshipPricing.ts
Normal file
262
core/racing/domain/value-objects/SponsorshipPricing.ts
Normal file
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* 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 | undefined;
|
||||
secondarySlots?: SponsorshipSlotConfig | undefined;
|
||||
acceptingApplications: boolean;
|
||||
customRequirements?: string | undefined;
|
||||
}
|
||||
|
||||
export class SponsorshipPricing implements IValueObject<SponsorshipPricingProps> {
|
||||
readonly mainSlot: SponsorshipSlotConfig | undefined;
|
||||
readonly secondarySlots: SponsorshipSlotConfig | undefined;
|
||||
readonly acceptingApplications: boolean;
|
||||
readonly customRequirements: string | undefined;
|
||||
|
||||
private constructor(props: SponsorshipPricingProps) {
|
||||
this.mainSlot = props.mainSlot;
|
||||
this.secondarySlots = props.secondarySlots;
|
||||
this.acceptingApplications = props.acceptingApplications;
|
||||
this.customRequirements = props.customRequirements;
|
||||
}
|
||||
|
||||
get props(): SponsorshipPricingProps {
|
||||
return {
|
||||
mainSlot: this.mainSlot,
|
||||
secondarySlots: this.secondarySlots,
|
||||
acceptingApplications: this.acceptingApplications,
|
||||
customRequirements: this.customRequirements,
|
||||
};
|
||||
}
|
||||
|
||||
equals(other: IValueObject<SponsorshipPricingProps>): boolean {
|
||||
const a = this.props;
|
||||
const b = other.props;
|
||||
|
||||
const mainEqual =
|
||||
(a.mainSlot === undefined && b.mainSlot === undefined) ||
|
||||
(a.mainSlot !== undefined &&
|
||||
b.mainSlot !== undefined &&
|
||||
a.mainSlot.tier === b.mainSlot.tier &&
|
||||
a.mainSlot.price.amount === b.mainSlot.price.amount &&
|
||||
a.mainSlot.price.currency === b.mainSlot.price.currency &&
|
||||
a.mainSlot.available === b.mainSlot.available &&
|
||||
a.mainSlot.maxSlots === b.mainSlot.maxSlots &&
|
||||
a.mainSlot.benefits.length === b.mainSlot.benefits.length &&
|
||||
a.mainSlot.benefits.every((val, idx) => val === b.mainSlot!.benefits[idx]));
|
||||
|
||||
const secondaryEqual =
|
||||
(a.secondarySlots === undefined && b.secondarySlots === undefined) ||
|
||||
(a.secondarySlots !== undefined &&
|
||||
b.secondarySlots !== undefined &&
|
||||
a.secondarySlots.tier === b.secondarySlots.tier &&
|
||||
a.secondarySlots.price.amount === b.secondarySlots.price.amount &&
|
||||
a.secondarySlots.price.currency === b.secondarySlots.price.currency &&
|
||||
a.secondarySlots.available === b.secondarySlots.available &&
|
||||
a.secondarySlots.maxSlots === b.secondarySlots.maxSlots &&
|
||||
a.secondarySlots.benefits.length === b.secondarySlots.benefits.length &&
|
||||
a.secondarySlots.benefits.every(
|
||||
(val, idx) => val === b.secondarySlots!.benefits[idx],
|
||||
));
|
||||
|
||||
return (
|
||||
mainEqual &&
|
||||
secondaryEqual &&
|
||||
a.acceptingApplications === b.acceptingApplications &&
|
||||
a.customRequirements === b.customRequirements
|
||||
);
|
||||
}
|
||||
|
||||
static create(props: Partial<SponsorshipPricingProps> = {}): SponsorshipPricing {
|
||||
return new SponsorshipPricing({
|
||||
...(props.mainSlot !== undefined ? { mainSlot: props.mainSlot } : {}),
|
||||
...(props.secondarySlots !== undefined ? { secondarySlots: props.secondarySlots } : {}),
|
||||
acceptingApplications: props.acceptingApplications ?? true,
|
||||
...(props.customRequirements !== undefined ? { 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,
|
||||
};
|
||||
|
||||
const base = this.props;
|
||||
|
||||
return new SponsorshipPricing({
|
||||
...base,
|
||||
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,
|
||||
};
|
||||
|
||||
const base = this.props;
|
||||
|
||||
return new SponsorshipPricing({
|
||||
...base,
|
||||
secondarySlots: {
|
||||
...currentSecondary,
|
||||
...config,
|
||||
tier: 'secondary',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable accepting applications
|
||||
*/
|
||||
setAcceptingApplications(accepting: boolean): SponsorshipPricing {
|
||||
const base = this.props;
|
||||
|
||||
return new SponsorshipPricing({
|
||||
...base,
|
||||
acceptingApplications: accepting,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user