Files
gridpilot.gg/core/racing/domain/value-objects/SponsorshipPricing.ts
2026-01-16 19:46:49 +01:00

185 lines
5.5 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 type { ValueObject } from '@core/shared/domain/ValueObject';
import { Money } from './Money';
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 ValueObject<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: ValueObject<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 } : {}),
});
}
/**
* 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,
});
}
}