wip league admin tools

This commit is contained in:
2025-12-28 12:04:12 +01:00
parent 5dc8c2399c
commit 6edf12fda8
401 changed files with 15365 additions and 6047 deletions

View File

@@ -141,14 +141,6 @@ export class Race implements IEntity<string> {
strengthOfField = StrengthOfField.create(props.strengthOfField);
}
// Validate scheduled time is not in the past for new races
// Allow some flexibility for testing and bootstrap scenarios
const now = new Date();
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
if (status.isScheduled() && props.scheduledAt < oneHourAgo) {
throw new RacingDomainValidationError('Scheduled time cannot be more than 1 hour in the past');
}
return new Race({
id: props.id,
leagueId: props.leagueId,
@@ -219,6 +211,14 @@ export class Race implements IEntity<string> {
* Cancel the race
*/
cancel(): Race {
if (this.status.isCancelled()) {
throw new RacingDomainInvariantError('Race is already cancelled');
}
if (this.status.isCompleted()) {
throw new RacingDomainInvariantError('Cannot cancel completed race');
}
const transition = this.status.canTransitionTo('cancelled');
if (!transition.valid) {
throw new RacingDomainInvariantError(transition.error!);
@@ -234,9 +234,15 @@ export class Race implements IEntity<string> {
...(this.carId !== undefined ? { carId: this.carId } : {}),
sessionType: this.sessionType,
status: 'cancelled',
...(this.strengthOfField !== undefined ? { strengthOfField: this.strengthOfField.toNumber() } : {}),
...(this.registeredCount !== undefined ? { registeredCount: this.registeredCount.toNumber() } : {}),
...(this.maxParticipants !== undefined ? { maxParticipants: this.maxParticipants.toNumber() } : {}),
...(this.strengthOfField !== undefined
? { strengthOfField: this.strengthOfField.toNumber() }
: {}),
...(this.registeredCount !== undefined
? { registeredCount: this.registeredCount.toNumber() }
: {}),
...(this.maxParticipants !== undefined
? { maxParticipants: this.maxParticipants.toNumber() }
: {}),
});
}
@@ -244,6 +250,14 @@ export class Race implements IEntity<string> {
* Re-open a previously completed or cancelled race
*/
reopen(): Race {
if (this.status.isScheduled()) {
throw new RacingDomainInvariantError('Race is already scheduled');
}
if (this.status.isRunning()) {
throw new RacingDomainInvariantError('Cannot reopen running race');
}
const transition = this.status.canTransitionTo('scheduled');
if (!transition.valid) {
throw new RacingDomainInvariantError(transition.error!);
@@ -259,9 +273,15 @@ export class Race implements IEntity<string> {
...(this.carId !== undefined ? { carId: this.carId } : {}),
sessionType: this.sessionType,
status: 'scheduled',
...(this.strengthOfField !== undefined ? { strengthOfField: this.strengthOfField.toNumber() } : {}),
...(this.registeredCount !== undefined ? { registeredCount: this.registeredCount.toNumber() } : {}),
...(this.maxParticipants !== undefined ? { maxParticipants: this.maxParticipants.toNumber() } : {}),
...(this.strengthOfField !== undefined
? { strengthOfField: this.strengthOfField.toNumber() }
: {}),
...(this.registeredCount !== undefined
? { registeredCount: this.registeredCount.toNumber() }
: {}),
...(this.maxParticipants !== undefined
? { maxParticipants: this.maxParticipants.toNumber() }
: {}),
});
}