This commit is contained in:
2025-12-09 22:45:03 +01:00
parent 3adf2e5e94
commit 3659d25e52
20 changed files with 2537 additions and 85 deletions

View File

@@ -1,4 +1,5 @@
import type { LeagueVisibilityType } from '../../domain/value-objects/LeagueVisibility';
import type { StewardingDecisionMode } from '../../domain/entities/League';
export type LeagueStructureMode = 'solo' | 'fixedTeams';
@@ -57,6 +58,49 @@ export interface LeagueTimingsFormDTO {
monthlyWeekday?: import('../../domain/value-objects/Weekday').Weekday;
}
/**
* Stewarding configuration for protests and penalties.
*/
export interface LeagueStewardingFormDTO {
/**
* How protest decisions are made
*/
decisionMode: StewardingDecisionMode;
/**
* Number of votes required to uphold/reject a protest
* Used with steward_vote, member_vote, steward_veto, member_veto modes
*/
requiredVotes?: number;
/**
* Whether to require a defense from the accused before deciding
*/
requireDefense: boolean;
/**
* Time limit (hours) for accused to submit defense
*/
defenseTimeLimit: number;
/**
* Time limit (hours) for voting to complete
*/
voteTimeLimit: number;
/**
* Time limit (hours) after race ends when protests can be filed
*/
protestDeadlineHours: number;
/**
* Time limit (hours) after race ends when stewarding is closed
*/
stewardingClosesHours: number;
/**
* Whether to notify the accused when a protest is filed
*/
notifyAccusedOnProtest: boolean;
/**
* Whether to notify eligible voters when a vote is required
*/
notifyOnVoteRequired: boolean;
}
export interface LeagueConfigFormModel {
leagueId?: string; // present for admin, omitted for create
basics: {
@@ -80,6 +124,7 @@ export interface LeagueConfigFormModel {
scoring: LeagueScoringFormDTO;
dropPolicy: LeagueDropPolicyFormDTO;
timings: LeagueTimingsFormDTO;
stewarding: LeagueStewardingFormDTO;
}
/**

View File

@@ -77,4 +77,5 @@ export type {
LeagueDropPolicyFormDTO,
LeagueStructureMode,
LeagueTimingsFormDTO,
LeagueStewardingFormDTO,
} from './dto/LeagueConfigFormDTO';

View File

@@ -5,6 +5,56 @@
* Immutable entity with factory methods and domain validation.
*/
/**
* Stewarding decision mode for protests
*/
export type StewardingDecisionMode =
| 'admin_only' // Only admins can decide
| 'steward_vote' // X stewards must vote to uphold
| 'member_vote' // X members must vote to uphold
| 'steward_veto' // Upheld unless X stewards vote against
| 'member_veto'; // Upheld unless X members vote against
export interface StewardingSettings {
/**
* How protest decisions are made
*/
decisionMode: StewardingDecisionMode;
/**
* Number of votes required to uphold/reject a protest
* Used with steward_vote, member_vote, steward_veto, member_veto modes
*/
requiredVotes?: number;
/**
* Whether to require a defense from the accused before deciding
*/
requireDefense?: boolean;
/**
* Time limit (hours) for accused to submit defense
*/
defenseTimeLimit?: number;
/**
* Time limit (hours) for voting to complete
*/
voteTimeLimit?: number;
/**
* Time limit (hours) after race ends when protests can be filed
*/
protestDeadlineHours?: number;
/**
* Time limit (hours) after race ends when stewarding is closed (no more decisions)
*/
stewardingClosesHours?: number;
/**
* Whether to notify the accused when a protest is filed
*/
notifyAccusedOnProtest?: boolean;
/**
* Whether to notify eligible voters when a vote is required
*/
notifyOnVoteRequired?: boolean;
}
export interface LeagueSettings {
pointsSystem: 'f1-2024' | 'indycar' | 'custom';
sessionDuration?: number;
@@ -15,6 +65,10 @@ export interface LeagueSettings {
* Used for simple capacity display on the website.
*/
maxDrivers?: number;
/**
* Stewarding settings for protest handling
*/
stewarding?: StewardingSettings;
}
export interface LeagueSocialLinks {
@@ -64,11 +118,23 @@ export class League {
}): League {
this.validate(props);
const defaultStewardingSettings: StewardingSettings = {
decisionMode: 'admin_only',
requireDefense: false,
defenseTimeLimit: 48,
voteTimeLimit: 72,
protestDeadlineHours: 48,
stewardingClosesHours: 168, // 7 days
notifyAccusedOnProtest: true,
notifyOnVoteRequired: true,
};
const defaultSettings: LeagueSettings = {
pointsSystem: 'f1-2024',
sessionDuration: 60,
qualifyingFormat: 'open',
maxDrivers: 32,
stewarding: defaultStewardingSettings,
};
return new League({