This commit is contained in:
2025-12-11 21:06:25 +01:00
parent c49ea2598d
commit ec3ddc3a5c
227 changed files with 3496 additions and 2083 deletions

View File

@@ -76,9 +76,9 @@ export class Car implements IEntity<string> {
carClass: props.carClass ?? 'gt',
license: props.license ?? 'D',
year: props.year ?? new Date().getFullYear(),
horsepower: props.horsepower,
weight: props.weight,
imageUrl: props.imageUrl,
...(props.horsepower !== undefined ? { horsepower: props.horsepower } : {}),
...(props.weight !== undefined ? { weight: props.weight } : {}),
...(props.imageUrl !== undefined ? { imageUrl: props.imageUrl } : {}),
gameId: props.gameId,
});
}

View File

@@ -46,7 +46,11 @@ export class Driver implements IEntity<string> {
this.validate(props);
return new Driver({
...props,
id: props.id,
iracingId: props.iracingId,
name: props.name,
country: props.country,
...(props.bio !== undefined ? { bio: props.bio } : {}),
joinedAt: props.joinedAt ?? new Date(),
});
}

View File

@@ -1,13 +1,11 @@
/**
* Domain Entity: DriverLivery
*/
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
import type { IEntity } from '@gridpilot/shared/domain';
*
*
* Represents a driver's custom livery for a specific car.
* Includes user-placed decals and league-specific overrides.
*/
import { RacingDomainValidationError, RacingDomainInvariantError, RacingDomainError } from '../errors/RacingDomainError';
import type { IEntity } from '@gridpilot/shared/domain';
import type { LiveryDecal } from '../value-objects/LiveryDecal';

View File

@@ -1,13 +1,11 @@
/**
* Domain Entity: LiveryTemplate
*/
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
import type { IEntity } from '@gridpilot/shared/domain';
*
*
* Represents an admin-defined livery template for a specific car.
* Contains base image and sponsor decal placements.
*/
import { RacingDomainValidationError, RacingDomainInvariantError, RacingDomainError } from '../errors/RacingDomainError';
import type { IEntity } from '@gridpilot/shared/domain';
import type { LiveryDecal } from '../value-objects/LiveryDecal';

View File

@@ -77,14 +77,14 @@ export class Race implements IEntity<string> {
leagueId: props.leagueId,
scheduledAt: props.scheduledAt,
track: props.track,
trackId: props.trackId,
...(props.trackId !== undefined ? { trackId: props.trackId } : {}),
car: props.car,
carId: props.carId,
...(props.carId !== undefined ? { carId: props.carId } : {}),
sessionType: props.sessionType ?? 'race',
status: props.status ?? 'scheduled',
strengthOfField: props.strengthOfField,
registeredCount: props.registeredCount,
maxParticipants: props.maxParticipants,
...(props.strengthOfField !== undefined ? { strengthOfField: props.strengthOfField } : {}),
...(props.registeredCount !== undefined ? { registeredCount: props.registeredCount } : {}),
...(props.maxParticipants !== undefined ? { maxParticipants: props.maxParticipants } : {}),
});
}

View File

@@ -70,11 +70,11 @@ export class Season implements IEntity<string> {
leagueId: props.leagueId,
gameId: props.gameId,
name: props.name,
year: props.year,
order: props.order,
...(props.year !== undefined ? { year: props.year } : {}),
...(props.order !== undefined ? { order: props.order } : {}),
status,
startDate: props.startDate,
endDate: props.endDate,
...(props.startDate !== undefined ? { startDate: props.startDate } : {}),
...(props.endDate !== undefined ? { endDate: props.endDate } : {}),
});
}

View File

@@ -48,16 +48,22 @@ export class SeasonSponsorship implements IEntity<string> {
this.description = props.description;
}
static create(props: Omit<SeasonSponsorshipProps, 'createdAt' | 'status'> & {
static create(props: Omit<SeasonSponsorshipProps, 'createdAt' | 'status'> & {
createdAt?: Date;
status?: SponsorshipStatus;
}): SeasonSponsorship {
this.validate(props);
return new SeasonSponsorship({
...props,
createdAt: props.createdAt ?? new Date(),
id: props.id,
seasonId: props.seasonId,
sponsorId: props.sponsorId,
tier: props.tier,
pricing: props.pricing,
status: props.status ?? 'pending',
createdAt: props.createdAt ?? new Date(),
...(props.activatedAt !== undefined ? { activatedAt: props.activatedAt } : {}),
...(props.description !== undefined ? { description: props.description } : {}),
});
}

View File

@@ -5,7 +5,7 @@
* Immutable entity with factory methods and domain validation.
*/
import { RacingDomainValidationError } from '../errors/RacingDomainError';
import { RacingDomainValidationError, RacingDomainError } from '../errors/RacingDomainError';
import type { IEntity } from '@gridpilot/shared/domain';
export class Standing implements IEntity<string> {

View File

@@ -1,7 +1,7 @@
import { SeasonSchedule } from '../value-objects/SeasonSchedule';
import { ScheduledRaceSlot } from '../value-objects/ScheduledRaceSlot';
import type { RecurrenceStrategy } from '../value-objects/RecurrenceStrategy';
import { RacingDomainValidationError } from '../errors/RacingDomainError';
import { RacingDomainValidationError, RacingDomainError } from '../errors/RacingDomainError';
import { RaceTimeOfDay } from '../value-objects/RaceTimeOfDay';
import type { Weekday } from '../types/Weekday';
import { weekdayToIndex } from '../types/Weekday';

View File

@@ -1,4 +1,4 @@
import type { Weekday } from './Weekday';
import type { Weekday } from '../types/Weekday';
import type { IValueObject } from '@gridpilot/shared/domain';
export interface MonthlyRecurrencePatternProps {

View File

@@ -36,12 +36,59 @@ export class SponsorshipPricing implements IValueObject<SponsorshipPricingProps>
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({
mainSlot: props.mainSlot,
secondarySlots: props.secondarySlots,
...(props.mainSlot !== undefined ? { mainSlot: props.mainSlot } : {}),
...(props.secondarySlots !== undefined ? { secondarySlots: props.secondarySlots } : {}),
acceptingApplications: props.acceptingApplications ?? true,
customRequirements: props.customRequirements,
...(props.customRequirements !== undefined ? { customRequirements: props.customRequirements } : {}),
});
}