145 lines
4.2 KiB
TypeScript
145 lines
4.2 KiB
TypeScript
import { AdminVoteSessionRepository } from '../../domain/repositories/AdminVoteSessionRepository';
|
|
import { AdminVoteSession } from '../../domain/entities/AdminVoteSession';
|
|
import { OpenAdminVoteSessionInput, OpenAdminVoteSessionOutput } from '../dtos/AdminVoteSessionDto';
|
|
|
|
/**
|
|
* Use Case: OpenAdminVoteSessionUseCase
|
|
*
|
|
* Opens a new admin vote session for a league.
|
|
* Follows CQRS Light: command side operation.
|
|
*
|
|
* Per plans section 7.1.1
|
|
*/
|
|
export class OpenAdminVoteSessionUseCase {
|
|
constructor(
|
|
private readonly adminVoteSessionRepository: AdminVoteSessionRepository,
|
|
) {}
|
|
|
|
async execute(input: OpenAdminVoteSessionInput): Promise<OpenAdminVoteSessionOutput> {
|
|
try {
|
|
// Validate input
|
|
const errors = this.validateInput(input);
|
|
if (errors.length > 0) {
|
|
return {
|
|
success: false,
|
|
voteSessionId: input.voteSessionId,
|
|
errors,
|
|
};
|
|
}
|
|
|
|
// Check if session already exists
|
|
const existing = await this.adminVoteSessionRepository.findById(input.voteSessionId);
|
|
if (existing) {
|
|
return {
|
|
success: false,
|
|
voteSessionId: input.voteSessionId,
|
|
errors: ['Vote session with this ID already exists'],
|
|
};
|
|
}
|
|
|
|
// Check for overlapping active sessions for this admin in this league
|
|
const activeSessions = await this.adminVoteSessionRepository.findActiveForAdmin(
|
|
input.adminId,
|
|
input.leagueId
|
|
);
|
|
|
|
const startDate = new Date(input.startDate);
|
|
const endDate = new Date(input.endDate);
|
|
|
|
for (const session of activeSessions) {
|
|
// Check for overlap
|
|
if (
|
|
(startDate >= session.startDate && startDate <= session.endDate) ||
|
|
(endDate >= session.startDate && endDate <= session.endDate) ||
|
|
(startDate <= session.startDate && endDate >= session.endDate)
|
|
) {
|
|
return {
|
|
success: false,
|
|
voteSessionId: input.voteSessionId,
|
|
errors: ['Active vote session already exists for this admin in this league with overlapping dates'],
|
|
};
|
|
}
|
|
}
|
|
|
|
// Create the vote session
|
|
const session = AdminVoteSession.create({
|
|
voteSessionId: input.voteSessionId,
|
|
leagueId: input.leagueId,
|
|
adminId: input.adminId,
|
|
startDate,
|
|
endDate,
|
|
eligibleVoters: input.eligibleVoters,
|
|
});
|
|
|
|
// Save to repository
|
|
await this.adminVoteSessionRepository.save(session);
|
|
|
|
return {
|
|
success: true,
|
|
voteSessionId: input.voteSessionId,
|
|
};
|
|
|
|
} catch (error) {
|
|
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
return {
|
|
success: false,
|
|
voteSessionId: input.voteSessionId,
|
|
errors: [`Failed to open vote session: ${errorMsg}`],
|
|
};
|
|
}
|
|
}
|
|
|
|
private validateInput(input: OpenAdminVoteSessionInput): string[] {
|
|
const errors: string[] = [];
|
|
|
|
if (!input.voteSessionId || input.voteSessionId.trim().length === 0) {
|
|
errors.push('voteSessionId is required');
|
|
}
|
|
|
|
if (!input.leagueId || input.leagueId.trim().length === 0) {
|
|
errors.push('leagueId is required');
|
|
}
|
|
|
|
if (!input.adminId || input.adminId.trim().length === 0) {
|
|
errors.push('adminId is required');
|
|
}
|
|
|
|
if (!input.startDate) {
|
|
errors.push('startDate is required');
|
|
}
|
|
|
|
if (!input.endDate) {
|
|
errors.push('endDate is required');
|
|
}
|
|
|
|
if (input.startDate && input.endDate) {
|
|
const startDate = new Date(input.startDate);
|
|
const endDate = new Date(input.endDate);
|
|
|
|
if (isNaN(startDate.getTime())) {
|
|
errors.push('startDate must be a valid date');
|
|
}
|
|
|
|
if (isNaN(endDate.getTime())) {
|
|
errors.push('endDate must be a valid date');
|
|
}
|
|
|
|
if (startDate >= endDate) {
|
|
errors.push('startDate must be before endDate');
|
|
}
|
|
}
|
|
|
|
if (!input.eligibleVoters || input.eligibleVoters.length === 0) {
|
|
errors.push('At least one eligible voter is required');
|
|
} else {
|
|
// Check for duplicates
|
|
const uniqueVoters = new Set(input.eligibleVoters);
|
|
if (uniqueVoters.size !== input.eligibleVoters.length) {
|
|
errors.push('Duplicate eligible voters are not allowed');
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
}
|