142 lines
4.9 KiB
TypeScript
142 lines
4.9 KiB
TypeScript
import type { ValueObject } from '@core/shared/domain/ValueObject';
|
|
import type { StewardingDecisionMode } from '../entities/League';
|
|
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
|
|
|
export interface SeasonStewardingConfigProps {
|
|
decisionMode: StewardingDecisionMode;
|
|
requiredVotes?: number | undefined;
|
|
requireDefense: boolean;
|
|
defenseTimeLimit: number;
|
|
voteTimeLimit: number;
|
|
protestDeadlineHours: number;
|
|
stewardingClosesHours: number;
|
|
notifyAccusedOnProtest: boolean;
|
|
notifyOnVoteRequired: boolean;
|
|
}
|
|
|
|
/**
|
|
* Value Object: SeasonStewardingConfig
|
|
*
|
|
* Encapsulates stewarding configuration owned by a Season.
|
|
* Shape intentionally mirrors LeagueStewardingFormDTO used by the wizard.
|
|
*/
|
|
export class SeasonStewardingConfig
|
|
implements ValueObject<SeasonStewardingConfigProps>
|
|
{
|
|
readonly decisionMode: StewardingDecisionMode;
|
|
readonly requiredVotes?: number;
|
|
readonly requireDefense: boolean;
|
|
readonly defenseTimeLimit: number;
|
|
readonly voteTimeLimit: number;
|
|
readonly protestDeadlineHours: number;
|
|
readonly stewardingClosesHours: number;
|
|
readonly notifyAccusedOnProtest: boolean;
|
|
readonly notifyOnVoteRequired: boolean;
|
|
|
|
constructor(props: SeasonStewardingConfigProps) {
|
|
if (!props.decisionMode) {
|
|
throw new RacingDomainValidationError(
|
|
'SeasonStewardingConfig.decisionMode is required',
|
|
);
|
|
}
|
|
|
|
if (
|
|
(props.decisionMode === 'steward_vote' ||
|
|
props.decisionMode === 'member_vote' ||
|
|
props.decisionMode === 'steward_veto' ||
|
|
props.decisionMode === 'member_veto') &&
|
|
(props.requiredVotes === undefined ||
|
|
!Number.isInteger(props.requiredVotes) ||
|
|
props.requiredVotes <= 0)
|
|
) {
|
|
throw new RacingDomainValidationError(
|
|
'SeasonStewardingConfig.requiredVotes must be a positive integer for voting/veto modes',
|
|
);
|
|
}
|
|
|
|
// For non-voting modes, requiredVotes should not be provided
|
|
if (props.decisionMode !== 'steward_vote' &&
|
|
props.decisionMode !== 'member_vote' &&
|
|
props.decisionMode !== 'steward_veto' &&
|
|
props.decisionMode !== 'member_veto' &&
|
|
props.requiredVotes !== undefined) {
|
|
throw new RacingDomainValidationError(
|
|
'SeasonStewardingConfig.requiredVotes should only be provided for voting/veto modes',
|
|
);
|
|
}
|
|
|
|
if (!Number.isInteger(props.defenseTimeLimit) || props.defenseTimeLimit < 0) {
|
|
throw new RacingDomainValidationError(
|
|
'SeasonStewardingConfig.defenseTimeLimit must be a non-negative integer (hours)',
|
|
);
|
|
}
|
|
|
|
if (!Number.isInteger(props.voteTimeLimit) || props.voteTimeLimit <= 0) {
|
|
throw new RacingDomainValidationError(
|
|
'SeasonStewardingConfig.voteTimeLimit must be a positive integer (hours)',
|
|
);
|
|
}
|
|
|
|
if (
|
|
!Number.isInteger(props.protestDeadlineHours) ||
|
|
props.protestDeadlineHours <= 0
|
|
) {
|
|
throw new RacingDomainValidationError(
|
|
'SeasonStewardingConfig.protestDeadlineHours must be a positive integer (hours)',
|
|
);
|
|
}
|
|
|
|
if (
|
|
!Number.isInteger(props.stewardingClosesHours) ||
|
|
props.stewardingClosesHours <= 0
|
|
) {
|
|
throw new RacingDomainValidationError(
|
|
'SeasonStewardingConfig.stewardingClosesHours must be a positive integer (hours)',
|
|
);
|
|
}
|
|
|
|
this.decisionMode = props.decisionMode;
|
|
if (props.requiredVotes !== undefined) {
|
|
this.requiredVotes = props.requiredVotes;
|
|
}
|
|
this.requireDefense = props.requireDefense;
|
|
this.defenseTimeLimit = props.defenseTimeLimit;
|
|
this.voteTimeLimit = props.voteTimeLimit;
|
|
this.protestDeadlineHours = props.protestDeadlineHours;
|
|
this.stewardingClosesHours = props.stewardingClosesHours;
|
|
this.notifyAccusedOnProtest = props.notifyAccusedOnProtest;
|
|
this.notifyOnVoteRequired = props.notifyOnVoteRequired;
|
|
}
|
|
|
|
get props(): SeasonStewardingConfigProps {
|
|
return {
|
|
decisionMode: this.decisionMode,
|
|
...(this.requiredVotes !== undefined
|
|
? { requiredVotes: this.requiredVotes }
|
|
: {}),
|
|
requireDefense: this.requireDefense,
|
|
defenseTimeLimit: this.defenseTimeLimit,
|
|
voteTimeLimit: this.voteTimeLimit,
|
|
protestDeadlineHours: this.protestDeadlineHours,
|
|
stewardingClosesHours: this.stewardingClosesHours,
|
|
notifyAccusedOnProtest: this.notifyAccusedOnProtest,
|
|
notifyOnVoteRequired: this.notifyOnVoteRequired,
|
|
};
|
|
}
|
|
|
|
equals(other: ValueObject<SeasonStewardingConfigProps>): boolean {
|
|
const a = this.props;
|
|
const b = other.props;
|
|
return (
|
|
a.decisionMode === b.decisionMode &&
|
|
a.requiredVotes === b.requiredVotes &&
|
|
a.requireDefense === b.requireDefense &&
|
|
a.defenseTimeLimit === b.defenseTimeLimit &&
|
|
a.voteTimeLimit === b.voteTimeLimit &&
|
|
a.protestDeadlineHours === b.protestDeadlineHours &&
|
|
a.stewardingClosesHours === b.stewardingClosesHours &&
|
|
a.notifyAccusedOnProtest === b.notifyAccusedOnProtest &&
|
|
a.notifyOnVoteRequired === b.notifyOnVoteRequired
|
|
);
|
|
}
|
|
} |