/** * Query: GetTeamRatingLedgerQuery * * Paginated/filtered query for team rating events (ledger). * Mirrors user slice 6 pattern but for teams. */ import { TeamLedgerEntryDto, TeamLedgerFilter, PaginatedTeamLedgerResult } from '../dtos/TeamLedgerEntryDto'; import { ITeamRatingEventRepository, PaginatedQueryOptions, TeamRatingEventFilter } from '../../domain/repositories/ITeamRatingEventRepository'; export interface GetTeamRatingLedgerQuery { teamId: string; limit?: number; offset?: number; filter?: TeamLedgerFilter; } export class GetTeamRatingLedgerQueryHandler { constructor( private readonly ratingEventRepo: ITeamRatingEventRepository ) {} async execute(query: GetTeamRatingLedgerQuery): Promise { const { teamId, limit = 20, offset = 0, filter } = query; // Build repo options const repoOptions: PaginatedQueryOptions = { limit, offset, }; // Add filter if provided if (filter) { const ratingEventFilter: TeamRatingEventFilter = {}; if (filter.dimensions) { ratingEventFilter.dimensions = filter.dimensions; } if (filter.sourceTypes) { ratingEventFilter.sourceTypes = filter.sourceTypes; } if (filter.from) { ratingEventFilter.from = new Date(filter.from); } if (filter.to) { ratingEventFilter.to = new Date(filter.to); } if (filter.reasonCodes) { ratingEventFilter.reasonCodes = filter.reasonCodes; } repoOptions.filter = ratingEventFilter; } // Query repository const result = await this.ratingEventRepo.findEventsPaginated(teamId, repoOptions); // Convert domain entities to DTOs const entries: TeamLedgerEntryDto[] = result.items.map(event => { const source: { type: 'race' | 'penalty' | 'vote' | 'adminAction' | 'manualAdjustment'; id?: string } = { type: event.source.type, }; if (event.source.id !== undefined) { source.id = event.source.id; } const reason: { code: string; description?: string } = { code: event.reason.code, }; if (event.reason.description !== undefined) { reason.description = event.reason.description; } const dto: TeamLedgerEntryDto = { id: event.id.value, teamId: event.teamId, dimension: event.dimension.value, delta: event.delta.value, occurredAt: event.occurredAt.toISOString(), createdAt: event.createdAt.toISOString(), source, reason, visibility: { public: event.visibility.public, }, }; if (event.weight !== undefined) { dto.weight = event.weight; } return dto; }); const nextOffset = result.nextOffset !== undefined ? result.nextOffset : null; return { entries, pagination: { total: result.total, limit: result.limit, offset: result.offset, hasMore: result.hasMore, nextOffset, }, }; } }