league service
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import type { LeagueScoringConfig } from '../../domain/entities/LeagueScoringConfig';
|
||||
|
||||
export type LeagueScoringPresetPrimaryChampionshipType =
|
||||
| 'driver'
|
||||
| 'team'
|
||||
@@ -23,4 +25,5 @@ export interface LeagueScoringPresetDTO {
|
||||
export interface LeagueScoringPresetProvider {
|
||||
listPresets(): LeagueScoringPresetDTO[];
|
||||
getPresetById(id: string): LeagueScoringPresetDTO | undefined;
|
||||
createScoringConfigFromPreset(presetId: string, seasonId: string): LeagueScoringConfig;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface ApproveLeagueJoinRequestViewModel {
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface ApproveLeagueJoinRequestResultDTO {
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface IApproveLeagueJoinRequestPresenter extends Presenter<ApproveLeagueJoinRequestResultDTO, ApproveLeagueJoinRequestViewModel> {}
|
||||
@@ -0,0 +1,17 @@
|
||||
export interface CompleteDriverOnboardingViewModel {
|
||||
success: boolean;
|
||||
driverId?: string;
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
export interface CompleteDriverOnboardingResultDTO {
|
||||
success: boolean;
|
||||
driverId?: string;
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
export interface ICompleteDriverOnboardingPresenter {
|
||||
present(dto: CompleteDriverOnboardingResultDTO): void;
|
||||
get viewModel(): CompleteDriverOnboardingViewModel;
|
||||
reset(): void;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface GetLeagueAdminPermissionsViewModel {
|
||||
canRemoveMember: boolean;
|
||||
canUpdateRoles: boolean;
|
||||
}
|
||||
|
||||
export interface GetLeagueAdminPermissionsResultDTO {
|
||||
canRemoveMember: boolean;
|
||||
canUpdateRoles: boolean;
|
||||
}
|
||||
|
||||
export interface IGetLeagueAdminPermissionsPresenter extends Presenter<GetLeagueAdminPermissionsResultDTO, GetLeagueAdminPermissionsViewModel> {}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface LeagueAdminViewModel {
|
||||
leagueId: string;
|
||||
ownerId: string;
|
||||
}
|
||||
|
||||
export interface GetLeagueAdminResultDTO {
|
||||
leagueId: string;
|
||||
ownerId: string;
|
||||
}
|
||||
|
||||
export interface IGetLeagueAdminPresenter extends Presenter<GetLeagueAdminResultDTO, LeagueAdminViewModel> {}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface LeagueJoinRequestViewModel {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
requestedAt: Date;
|
||||
message: string;
|
||||
driver: { id: string; name: string } | null;
|
||||
}
|
||||
|
||||
export interface GetLeagueJoinRequestsViewModel {
|
||||
joinRequests: LeagueJoinRequestViewModel[];
|
||||
}
|
||||
|
||||
export interface GetLeagueJoinRequestsResultDTO {
|
||||
joinRequests: any[];
|
||||
drivers: { id: string; name: string }[];
|
||||
}
|
||||
|
||||
export interface IGetLeagueJoinRequestsPresenter extends Presenter<GetLeagueJoinRequestsResultDTO, GetLeagueJoinRequestsViewModel> {}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface LeagueMembershipsViewModel {
|
||||
members: {
|
||||
driverId: string;
|
||||
driver: { id: string; name: string };
|
||||
role: string;
|
||||
joinedAt: Date;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface GetLeagueMembershipsViewModel {
|
||||
memberships: LeagueMembershipsViewModel;
|
||||
}
|
||||
|
||||
export interface GetLeagueMembershipsResultDTO {
|
||||
memberships: any[];
|
||||
drivers: { id: string; name: string }[];
|
||||
}
|
||||
|
||||
export interface IGetLeagueMembershipsPresenter extends Presenter<GetLeagueMembershipsResultDTO, GetLeagueMembershipsViewModel> {}
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface LeagueOwnerSummaryViewModel {
|
||||
driver: { id: string; name: string };
|
||||
rating: number;
|
||||
rank: number;
|
||||
}
|
||||
|
||||
export interface GetLeagueOwnerSummaryViewModel {
|
||||
summary: LeagueOwnerSummaryViewModel | null;
|
||||
}
|
||||
|
||||
export interface GetLeagueOwnerSummaryResultDTO {
|
||||
summary: LeagueOwnerSummaryViewModel | null;
|
||||
}
|
||||
|
||||
export interface IGetLeagueOwnerSummaryPresenter extends Presenter<GetLeagueOwnerSummaryResultDTO, GetLeagueOwnerSummaryViewModel> {}
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface GetLeagueProtestsViewModel {
|
||||
protests: any[];
|
||||
racesById: Record<string, any>;
|
||||
driversById: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface GetLeagueProtestsResultDTO {
|
||||
protests: any[];
|
||||
races: any[];
|
||||
drivers: { id: string; name: string }[];
|
||||
}
|
||||
|
||||
export interface IGetLeagueProtestsPresenter extends Presenter<GetLeagueProtestsResultDTO, GetLeagueProtestsViewModel> {}
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface LeagueScheduleViewModel {
|
||||
races: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
date: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface GetLeagueScheduleResultDTO {
|
||||
races: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
scheduledAt: Date;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface IGetLeagueSchedulePresenter extends Presenter<GetLeagueScheduleResultDTO, LeagueScheduleViewModel> {}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface LeagueSeasonSummaryViewModel {
|
||||
seasonId: string;
|
||||
name: string;
|
||||
status: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
isPrimary: boolean;
|
||||
isParallelActive: boolean;
|
||||
}
|
||||
|
||||
export interface GetLeagueSeasonsViewModel {
|
||||
seasons: LeagueSeasonSummaryViewModel[];
|
||||
}
|
||||
|
||||
export interface GetLeagueSeasonsResultDTO {
|
||||
seasons: any[];
|
||||
}
|
||||
|
||||
export interface IGetLeagueSeasonsPresenter extends Presenter<GetLeagueSeasonsResultDTO, GetLeagueSeasonsViewModel> {}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface GetTotalLeaguesViewModel {
|
||||
totalLeagues: number;
|
||||
}
|
||||
|
||||
export interface GetTotalLeaguesResultDTO {
|
||||
totalLeagues: number;
|
||||
}
|
||||
|
||||
export interface IGetTotalLeaguesPresenter extends Presenter<GetTotalLeaguesResultDTO, GetTotalLeaguesViewModel> {}
|
||||
@@ -2,24 +2,19 @@ import type { Standing } from '../../domain/entities/Standing';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface StandingItemViewModel {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
seasonId: string;
|
||||
driverId: string;
|
||||
position: number;
|
||||
driver: { id: string; name: string };
|
||||
points: number;
|
||||
wins: number;
|
||||
podiums: number;
|
||||
racesCompleted: number;
|
||||
rank: number;
|
||||
}
|
||||
|
||||
export interface LeagueStandingsViewModel {
|
||||
leagueId: string;
|
||||
standings: StandingItemViewModel[];
|
||||
}
|
||||
|
||||
export interface LeagueStandingsResultDTO {
|
||||
standings: Standing[];
|
||||
drivers: { id: string; name: string }[];
|
||||
}
|
||||
|
||||
export interface ILeagueStandingsPresenter
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface LeagueStatsViewModel {
|
||||
leagueId: string;
|
||||
totalMembers: number;
|
||||
totalRaces: number;
|
||||
completedRaces: number;
|
||||
scheduledRaces: number;
|
||||
averageSOF: number | null;
|
||||
highestSOF: number | null;
|
||||
lowestSOF: number | null;
|
||||
averageRating: number;
|
||||
}
|
||||
|
||||
export interface ILeagueStatsPresenter {
|
||||
present(
|
||||
leagueId: string,
|
||||
totalRaces: number,
|
||||
completedRaces: number,
|
||||
scheduledRaces: number,
|
||||
sofValues: number[]
|
||||
): LeagueStatsViewModel;
|
||||
getViewModel(): LeagueStatsViewModel;
|
||||
}
|
||||
export interface LeagueStatsResultDTO {
|
||||
totalMembers: number;
|
||||
totalRaces: number;
|
||||
averageRating: number;
|
||||
}
|
||||
|
||||
export interface ILeagueStatsPresenter
|
||||
extends Presenter<LeagueStatsResultDTO, LeagueStatsViewModel> {}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface RejectLeagueJoinRequestViewModel {
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface RejectLeagueJoinRequestResultDTO {
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface IRejectLeagueJoinRequestPresenter extends Presenter<RejectLeagueJoinRequestResultDTO, RejectLeagueJoinRequestViewModel> {}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface RemoveLeagueMemberViewModel {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export interface RemoveLeagueMemberResultDTO {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export interface IRemoveLeagueMemberPresenter extends Presenter<RemoveLeagueMemberResultDTO, RemoveLeagueMemberViewModel> {}
|
||||
13
core/racing/application/presenters/ITotalDriversPresenter.ts
Normal file
13
core/racing/application/presenters/ITotalDriversPresenter.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export interface TotalDriversViewModel {
|
||||
totalDrivers: number;
|
||||
}
|
||||
|
||||
export interface TotalDriversResultDTO {
|
||||
totalDrivers: number;
|
||||
}
|
||||
|
||||
export interface ITotalDriversPresenter {
|
||||
present(dto: TotalDriversResultDTO): void;
|
||||
get viewModel(): TotalDriversViewModel;
|
||||
reset(): void;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface UpdateLeagueMemberRoleViewModel {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateLeagueMemberRoleResultDTO {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export interface IUpdateLeagueMemberRolePresenter extends Presenter<UpdateLeagueMemberRoleResultDTO, UpdateLeagueMemberRoleViewModel> {}
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
||||
import type { IApproveLeagueJoinRequestPresenter, ApproveLeagueJoinRequestResultDTO, ApproveLeagueJoinRequestViewModel } from '../presenters/IApproveLeagueJoinRequestPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface ApproveLeagueJoinRequestUseCaseParams {
|
||||
leagueId: string;
|
||||
requestId: string;
|
||||
}
|
||||
|
||||
export interface ApproveLeagueJoinRequestResultDTO {
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export class ApproveLeagueJoinRequestUseCase implements UseCase<ApproveLeagueJoinRequestUseCaseParams, ApproveLeagueJoinRequestResultDTO, ApproveLeagueJoinRequestViewModel, IApproveLeagueJoinRequestPresenter> {
|
||||
constructor(private readonly leagueMembershipRepository: ILeagueMembershipRepository) {}
|
||||
|
||||
async execute(params: ApproveLeagueJoinRequestUseCaseParams, presenter: IApproveLeagueJoinRequestPresenter): Promise<void> {
|
||||
const requests = await this.leagueMembershipRepository.getJoinRequests(params.leagueId);
|
||||
const request = requests.find(r => r.id === params.requestId);
|
||||
if (!request) {
|
||||
throw new Error('Join request not found');
|
||||
}
|
||||
await this.leagueMembershipRepository.removeJoinRequest(params.requestId);
|
||||
await this.leagueMembershipRepository.saveMembership({
|
||||
leagueId: params.leagueId,
|
||||
driverId: request.driverId,
|
||||
role: 'member',
|
||||
status: 'active',
|
||||
joinedAt: new Date(),
|
||||
});
|
||||
const dto: ApproveLeagueJoinRequestResultDTO = { success: true, message: 'Join request approved.' };
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import type { IDriverRepository } from '../../domain/repositories/IDriverRepository';
|
||||
import type { ICompleteDriverOnboardingPresenter, CompleteDriverOnboardingResultDTO } from '../presenters/ICompleteDriverOnboardingPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
import { Driver } from '../../domain/entities/Driver';
|
||||
|
||||
export interface CompleteDriverOnboardingInput {
|
||||
userId: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
displayName: string;
|
||||
country: string;
|
||||
timezone?: string;
|
||||
bio?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Case for completing driver onboarding.
|
||||
*/
|
||||
export class CompleteDriverOnboardingUseCase
|
||||
implements UseCase<CompleteDriverOnboardingInput, CompleteDriverOnboardingResultDTO, any, ICompleteDriverOnboardingPresenter>
|
||||
{
|
||||
constructor(private readonly driverRepository: IDriverRepository) {}
|
||||
|
||||
async execute(input: CompleteDriverOnboardingInput, presenter: ICompleteDriverOnboardingPresenter): Promise<void> {
|
||||
presenter.reset();
|
||||
|
||||
try {
|
||||
// Check if driver already exists
|
||||
const existing = await this.driverRepository.findById(input.userId);
|
||||
if (existing) {
|
||||
presenter.present({
|
||||
success: false,
|
||||
errorMessage: 'Driver already exists',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new driver
|
||||
const driver = Driver.create({
|
||||
id: input.userId,
|
||||
iracingId: input.userId, // Assuming userId is iracingId for now
|
||||
name: input.displayName,
|
||||
country: input.country,
|
||||
bio: input.bio,
|
||||
});
|
||||
|
||||
await this.driverRepository.save(driver);
|
||||
|
||||
presenter.present({
|
||||
success: true,
|
||||
driverId: driver.id,
|
||||
});
|
||||
} catch (error) {
|
||||
presenter.present({
|
||||
success: false,
|
||||
errorMessage: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,7 @@ export class CreateLeagueWithSeasonAndScoringUseCase
|
||||
private readonly seasonRepository: ISeasonRepository,
|
||||
private readonly leagueScoringConfigRepository: ILeagueScoringConfigRepository,
|
||||
private readonly presetProvider: LeagueScoringPresetProvider,
|
||||
private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
@@ -113,31 +114,8 @@ export class CreateLeagueWithSeasonAndScoringUseCase
|
||||
this.logger.info(`Scoring preset ${preset.name} (${preset.id}) retrieved.`);
|
||||
|
||||
|
||||
const scoringConfig: LeagueScoringConfig = {
|
||||
id: uuidv4(),
|
||||
seasonId,
|
||||
scoringPresetId: preset.id,
|
||||
championships: [],
|
||||
};
|
||||
|
||||
const fullConfigFactory = (await import(
|
||||
'../../infrastructure/repositories/InMemoryScoringRepositories'
|
||||
)) as typeof import('../../infrastructure/repositories/InMemoryScoringRepositories');
|
||||
|
||||
const presetFromInfra = fullConfigFactory.getLeagueScoringPresetById(
|
||||
preset.id,
|
||||
);
|
||||
if (!presetFromInfra) {
|
||||
this.logger.error(`Preset registry missing preset: ${preset.id}`);
|
||||
throw new Error(`Preset registry missing preset: ${preset.id}`);
|
||||
}
|
||||
this.logger.debug(`Preset from infrastructure retrieved for ${preset.id}.`);
|
||||
|
||||
const infraConfig = presetFromInfra.createConfig({ seasonId });
|
||||
const finalConfig: LeagueScoringConfig = {
|
||||
...infraConfig,
|
||||
scoringPresetId: preset.id,
|
||||
};
|
||||
const finalConfig = this.presetProvider.createScoringConfigFromPreset(preset.id, seasonId);
|
||||
this.logger.debug(`Scoring configuration created from preset ${preset.id}.`);
|
||||
|
||||
await this.leagueScoringConfigRepository.save(finalConfig);
|
||||
this.logger.info(`Scoring configuration saved for season ${seasonId}.`);
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
|
||||
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
||||
import type { IGetLeagueAdminPermissionsPresenter, GetLeagueAdminPermissionsResultDTO, GetLeagueAdminPermissionsViewModel } from '../presenters/IGetLeagueAdminPermissionsPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface GetLeagueAdminPermissionsUseCaseParams {
|
||||
leagueId: string;
|
||||
performerDriverId: string;
|
||||
}
|
||||
|
||||
export interface GetLeagueAdminPermissionsResultDTO {
|
||||
canRemoveMember: boolean;
|
||||
canUpdateRoles: boolean;
|
||||
}
|
||||
|
||||
export class GetLeagueAdminPermissionsUseCase implements UseCase<GetLeagueAdminPermissionsUseCaseParams, GetLeagueAdminPermissionsResultDTO, GetLeagueAdminPermissionsViewModel, IGetLeagueAdminPermissionsPresenter> {
|
||||
constructor(
|
||||
private readonly leagueRepository: ILeagueRepository,
|
||||
private readonly leagueMembershipRepository: ILeagueMembershipRepository,
|
||||
) {}
|
||||
|
||||
async execute(params: GetLeagueAdminPermissionsUseCaseParams, presenter: IGetLeagueAdminPermissionsPresenter): Promise<void> {
|
||||
const league = await this.leagueRepository.findById(params.leagueId);
|
||||
if (!league) {
|
||||
presenter.present({ canRemoveMember: false, canUpdateRoles: false });
|
||||
return;
|
||||
}
|
||||
|
||||
const membership = await this.leagueMembershipRepository.getMembership(params.leagueId, params.performerDriverId);
|
||||
if (!membership || membership.status !== 'active') {
|
||||
presenter.present({ canRemoveMember: false, canUpdateRoles: false });
|
||||
return;
|
||||
}
|
||||
|
||||
// Business logic: owners and admins can remove members and update roles
|
||||
const canRemoveMember = membership.role === 'owner' || membership.role === 'admin';
|
||||
const canUpdateRoles = membership.role === 'owner' || membership.role === 'admin';
|
||||
|
||||
presenter.reset();
|
||||
presenter.present({ canRemoveMember, canUpdateRoles });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
||||
import type { IDriverRepository } from '../../domain/repositories/IDriverRepository';
|
||||
import type { IGetLeagueJoinRequestsPresenter, GetLeagueJoinRequestsResultDTO, GetLeagueJoinRequestsViewModel } from '../presenters/IGetLeagueJoinRequestsPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface GetLeagueJoinRequestsUseCaseParams {
|
||||
leagueId: string;
|
||||
}
|
||||
|
||||
export interface GetLeagueJoinRequestsResultDTO {
|
||||
joinRequests: any[];
|
||||
drivers: { id: string; name: string }[];
|
||||
}
|
||||
|
||||
export class GetLeagueJoinRequestsUseCase implements UseCase<GetLeagueJoinRequestsUseCaseParams, GetLeagueJoinRequestsResultDTO, GetLeagueJoinRequestsViewModel, IGetLeagueJoinRequestsPresenter> {
|
||||
constructor(
|
||||
private readonly leagueMembershipRepository: ILeagueMembershipRepository,
|
||||
private readonly driverRepository: IDriverRepository,
|
||||
) {}
|
||||
|
||||
async execute(params: GetLeagueJoinRequestsUseCaseParams, presenter: IGetLeagueJoinRequestsPresenter): Promise<void> {
|
||||
const joinRequests = await this.leagueMembershipRepository.getJoinRequests(params.leagueId);
|
||||
const driverIds = joinRequests.map(r => r.driverId);
|
||||
const drivers = await this.driverRepository.findByIds(driverIds);
|
||||
const driverMap = new Map(drivers.map(d => [d.id, { id: d.id, name: d.name }]));
|
||||
const dto: GetLeagueJoinRequestsResultDTO = {
|
||||
joinRequests,
|
||||
drivers: Array.from(driverMap.values()),
|
||||
};
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
||||
import type { IDriverRepository } from '../../domain/repositories/IDriverRepository';
|
||||
import type { LeagueMembership } from '../../domain/entities/LeagueMembership';
|
||||
import type { IGetLeagueMembershipsPresenter, GetLeagueMembershipsViewModel } from '../presenters/IGetLeagueMembershipsPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface GetLeagueMembershipsUseCaseParams {
|
||||
leagueId: string;
|
||||
}
|
||||
|
||||
export interface GetLeagueMembershipsResultDTO {
|
||||
memberships: LeagueMembership[];
|
||||
drivers: { id: string; name: string }[];
|
||||
}
|
||||
|
||||
export class GetLeagueMembershipsUseCase implements UseCase<GetLeagueMembershipsUseCaseParams, GetLeagueMembershipsResultDTO, GetLeagueMembershipsViewModel, IGetLeagueMembershipsPresenter> {
|
||||
constructor(
|
||||
private readonly leagueMembershipRepository: ILeagueMembershipRepository,
|
||||
private readonly driverRepository: IDriverRepository,
|
||||
) {}
|
||||
|
||||
async execute(params: GetLeagueMembershipsUseCaseParams, presenter: IGetLeagueMembershipsPresenter): Promise<void> {
|
||||
const memberships = await this.leagueMembershipRepository.getLeagueMembers(params.leagueId);
|
||||
const drivers: { id: string; name: string }[] = [];
|
||||
|
||||
// Get driver details for each membership
|
||||
for (const membership of memberships) {
|
||||
const driver = await this.driverRepository.findById(membership.driverId);
|
||||
if (driver) {
|
||||
drivers.push({ id: driver.id, name: driver.name });
|
||||
}
|
||||
}
|
||||
|
||||
const dto: GetLeagueMembershipsResultDTO = {
|
||||
memberships,
|
||||
drivers,
|
||||
};
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { IDriverRepository } from '../../domain/repositories/IDriverRepository';
|
||||
import type { IGetLeagueOwnerSummaryPresenter, GetLeagueOwnerSummaryResultDTO, GetLeagueOwnerSummaryViewModel } from '../presenters/IGetLeagueOwnerSummaryPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface GetLeagueOwnerSummaryUseCaseParams {
|
||||
ownerId: string;
|
||||
}
|
||||
|
||||
export interface GetLeagueOwnerSummaryResultDTO {
|
||||
summary: { driver: { id: string; name: string }; rating: number; rank: number } | null;
|
||||
}
|
||||
|
||||
export class GetLeagueOwnerSummaryUseCase implements UseCase<GetLeagueOwnerSummaryUseCaseParams, GetLeagueOwnerSummaryResultDTO, GetLeagueOwnerSummaryViewModel, IGetLeagueOwnerSummaryPresenter> {
|
||||
constructor(private readonly driverRepository: IDriverRepository) {}
|
||||
|
||||
async execute(params: GetLeagueOwnerSummaryUseCaseParams, presenter: IGetLeagueOwnerSummaryPresenter): Promise<void> {
|
||||
const driver = await this.driverRepository.findById(params.ownerId);
|
||||
const summary = driver ? { driver: { id: driver.id, name: driver.name }, rating: 0, rank: 0 } : null;
|
||||
const dto: GetLeagueOwnerSummaryResultDTO = { summary };
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
|
||||
import type { IProtestRepository } from '../../domain/repositories/IProtestRepository';
|
||||
import type { IDriverRepository } from '../../domain/repositories/IDriverRepository';
|
||||
import type { IGetLeagueProtestsPresenter, GetLeagueProtestsResultDTO, GetLeagueProtestsViewModel } from '../presenters/IGetLeagueProtestsPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface GetLeagueProtestsUseCaseParams {
|
||||
leagueId: string;
|
||||
}
|
||||
|
||||
export interface GetLeagueProtestsResultDTO {
|
||||
protests: any[];
|
||||
races: any[];
|
||||
drivers: { id: string; name: string }[];
|
||||
}
|
||||
|
||||
export class GetLeagueProtestsUseCase implements UseCase<GetLeagueProtestsUseCaseParams, GetLeagueProtestsResultDTO, GetLeagueProtestsViewModel, IGetLeagueProtestsPresenter> {
|
||||
constructor(
|
||||
private readonly raceRepository: IRaceRepository,
|
||||
private readonly protestRepository: IProtestRepository,
|
||||
private readonly driverRepository: IDriverRepository,
|
||||
) {}
|
||||
|
||||
async execute(params: GetLeagueProtestsUseCaseParams, presenter: IGetLeagueProtestsPresenter): Promise<void> {
|
||||
const races = await this.raceRepository.findByLeagueId(params.leagueId);
|
||||
const protests = [];
|
||||
const raceMap = new Map();
|
||||
const driverIds = new Set<string>();
|
||||
|
||||
for (const race of races) {
|
||||
raceMap.set(race.id, { id: race.id, name: race.name, date: race.scheduledAt.toISOString() });
|
||||
const raceProtests = await this.protestRepository.findByRaceId(race.id);
|
||||
for (const protest of raceProtests) {
|
||||
protests.push({
|
||||
id: protest.id,
|
||||
raceId: protest.raceId,
|
||||
protestingDriverId: protest.protestingDriverId,
|
||||
accusedDriverId: protest.accusedDriverId,
|
||||
submittedAt: protest.filedAt,
|
||||
description: protest.comment || '',
|
||||
status: protest.status,
|
||||
});
|
||||
driverIds.add(protest.protestingDriverId);
|
||||
driverIds.add(protest.accusedDriverId);
|
||||
}
|
||||
}
|
||||
|
||||
const drivers = await this.driverRepository.findByIds(Array.from(driverIds));
|
||||
const driverMap = new Map(drivers.map(d => [d.id, { id: d.id, name: d.name }]));
|
||||
const dto: GetLeagueProtestsResultDTO = {
|
||||
protests,
|
||||
races: Array.from(raceMap.values()),
|
||||
drivers: Array.from(driverMap.values()),
|
||||
};
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
|
||||
import type { IGetLeagueSchedulePresenter, GetLeagueScheduleResultDTO, GetLeagueScheduleViewModel } from '../presenters/IGetLeagueSchedulePresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface GetLeagueScheduleUseCaseParams {
|
||||
leagueId: string;
|
||||
}
|
||||
|
||||
export interface GetLeagueScheduleResultDTO {
|
||||
races: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
scheduledAt: Date;
|
||||
}>;
|
||||
}
|
||||
|
||||
export class GetLeagueScheduleUseCase implements UseCase<GetLeagueScheduleUseCaseParams, GetLeagueScheduleResultDTO, GetLeagueScheduleViewModel, IGetLeagueSchedulePresenter> {
|
||||
constructor(private readonly raceRepository: IRaceRepository) {}
|
||||
|
||||
async execute(params: GetLeagueScheduleUseCaseParams, presenter: IGetLeagueSchedulePresenter): Promise<void> {
|
||||
const races = await this.raceRepository.findByLeagueId(params.leagueId);
|
||||
const dto: GetLeagueScheduleResultDTO = {
|
||||
races: races.map(race => ({
|
||||
id: race.id,
|
||||
name: `${race.track} - ${race.car}`,
|
||||
scheduledAt: race.scheduledAt,
|
||||
})),
|
||||
};
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
22
core/racing/application/use-cases/GetLeagueSeasonsUseCase.ts
Normal file
22
core/racing/application/use-cases/GetLeagueSeasonsUseCase.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
|
||||
import type { IGetLeagueSeasonsPresenter, GetLeagueSeasonsResultDTO, GetLeagueSeasonsViewModel } from '../presenters/IGetLeagueSeasonsPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface GetLeagueSeasonsUseCaseParams {
|
||||
leagueId: string;
|
||||
}
|
||||
|
||||
export interface GetLeagueSeasonsResultDTO {
|
||||
seasons: any[];
|
||||
}
|
||||
|
||||
export class GetLeagueSeasonsUseCase implements UseCase<GetLeagueSeasonsUseCaseParams, GetLeagueSeasonsResultDTO, GetLeagueSeasonsViewModel, IGetLeagueSeasonsPresenter> {
|
||||
constructor(private readonly seasonRepository: ISeasonRepository) {}
|
||||
|
||||
async execute(params: GetLeagueSeasonsUseCaseParams, presenter: IGetLeagueSeasonsPresenter): Promise<void> {
|
||||
const seasons = await this.seasonRepository.findByLeagueId(params.leagueId);
|
||||
const dto: GetLeagueSeasonsResultDTO = { seasons };
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { IStandingRepository } from '../../domain/repositories/IStandingRepository';
|
||||
import type { IDriverRepository } from '../../domain/repositories/IDriverRepository';
|
||||
import type {
|
||||
ILeagueStandingsPresenter,
|
||||
LeagueStandingsResultDTO,
|
||||
@@ -18,15 +19,22 @@ export class GetLeagueStandingsUseCase
|
||||
implements
|
||||
UseCase<GetLeagueStandingsUseCaseParams, LeagueStandingsResultDTO, LeagueStandingsViewModel, ILeagueStandingsPresenter>
|
||||
{
|
||||
constructor(private readonly standingRepository: IStandingRepository) {}
|
||||
constructor(
|
||||
private readonly standingRepository: IStandingRepository,
|
||||
private readonly driverRepository: IDriverRepository,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
params: GetLeagueStandingsUseCaseParams,
|
||||
presenter: ILeagueStandingsPresenter,
|
||||
): Promise<void> {
|
||||
const standings = await this.standingRepository.findByLeagueId(params.leagueId);
|
||||
const driverIds = [...new Set(standings.map(s => s.driverId))];
|
||||
const drivers = await this.driverRepository.findByIds(driverIds);
|
||||
const driverMap = new Map(drivers.map(d => [d.id, { id: d.id, name: d.name }]));
|
||||
const dto: LeagueStandingsResultDTO = {
|
||||
standings,
|
||||
drivers: Array.from(driverMap.values()),
|
||||
};
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
|
||||
@@ -1,109 +1,28 @@
|
||||
/**
|
||||
* Use Case for retrieving league statistics.
|
||||
* Orchestrates domain logic and delegates presentation to the presenter.
|
||||
*/
|
||||
|
||||
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
|
||||
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
||||
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
|
||||
import type { IResultRepository } from '../../domain/repositories/IRaceRepository';
|
||||
import type { DriverRatingProvider } from '../ports/DriverRatingProvider';
|
||||
import type { ILeagueStatsPresenter } from '../presenters/ILeagueStatsPresenter';
|
||||
import type { AsyncUseCase } from '@gridpilot/shared/application';
|
||||
import { Logger } from "@gridpilot/core/shared/application";
|
||||
import {
|
||||
AverageStrengthOfFieldCalculator,
|
||||
type StrengthOfFieldCalculator,
|
||||
} from '../../domain/services/StrengthOfFieldCalculator';
|
||||
import type { ILeagueStatsPresenter, LeagueStatsResultDTO, LeagueStatsViewModel } from '../presenters/ILeagueStatsPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface GetLeagueStatsUseCaseParams {
|
||||
leagueId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Case for retrieving league statistics including average SOF across completed races.
|
||||
*/
|
||||
export class GetLeagueStatsUseCase
|
||||
implements AsyncUseCase<GetLeagueStatsUseCaseParams, void> {
|
||||
private readonly sofCalculator: StrengthOfFieldCalculator;
|
||||
|
||||
export class GetLeagueStatsUseCase implements UseCase<GetLeagueStatsUseCaseParams, LeagueStatsResultDTO, LeagueStatsViewModel, ILeagueStatsPresenter> {
|
||||
constructor(
|
||||
private readonly leagueRepository: ILeagueRepository,
|
||||
private readonly leagueMembershipRepository: ILeagueMembershipRepository,
|
||||
private readonly raceRepository: IRaceRepository,
|
||||
private readonly resultRepository: IResultRepository,
|
||||
private readonly driverRatingProvider: DriverRatingProvider,
|
||||
public readonly presenter: ILeagueStatsPresenter,
|
||||
private readonly logger: Logger,
|
||||
sofCalculator?: StrengthOfFieldCalculator,
|
||||
) {
|
||||
this.sofCalculator = sofCalculator ?? new AverageStrengthOfFieldCalculator();
|
||||
}
|
||||
) {}
|
||||
|
||||
async execute(params: GetLeagueStatsUseCaseParams): Promise<void> {
|
||||
this.logger.debug(
|
||||
`Executing GetLeagueStatsUseCase with params: ${JSON.stringify(params)}`,
|
||||
);
|
||||
const { leagueId } = params;
|
||||
|
||||
try {
|
||||
const league = await this.leagueRepository.findById(leagueId);
|
||||
if (!league) {
|
||||
this.logger.error(`League ${leagueId} not found`);
|
||||
throw new Error(`League ${leagueId} not found`);
|
||||
}
|
||||
|
||||
const races = await this.raceRepository.findByLeagueId(leagueId);
|
||||
const completedRaces = races.filter(r => r.status === 'completed');
|
||||
const scheduledRaces = races.filter(r => r.status === 'scheduled');
|
||||
this.logger.info(
|
||||
`Found ${races.length} races for league ${leagueId}: ${completedRaces.length} completed, ${scheduledRaces.length} scheduled. `,
|
||||
);
|
||||
|
||||
// Calculate SOF for each completed race
|
||||
const sofValues: number[] = [];
|
||||
|
||||
for (const race of completedRaces) {
|
||||
// Use stored SOF if available
|
||||
if (race.strengthOfField) {
|
||||
this.logger.debug(
|
||||
`Using stored Strength of Field for race ${race.id}: ${race.strengthOfField}`,
|
||||
);
|
||||
sofValues.push(race.strengthOfField);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise calculate from results
|
||||
const results = await this.resultRepository.findByRaceId(race.id);
|
||||
if (results.length === 0) {
|
||||
this.logger.debug(`No results found for race ${race.id}. Skipping SOF calculation.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const driverIds = results.map(r => r.driverId);
|
||||
const ratings = this.driverRatingProvider.getRatings(driverIds);
|
||||
const driverRatings = driverIds
|
||||
.filter(id => ratings.has(id))
|
||||
.map(id => ({ driverId: id, rating: ratings.get(id)! }));
|
||||
|
||||
const sof = this.sofCalculator.calculate(driverRatings);
|
||||
if (sof !== null) {
|
||||
this.logger.debug(`Calculated Strength of Field for race ${race.id}: ${sof}`);
|
||||
sofValues.push(sof);
|
||||
} else {
|
||||
this.logger.warn(`Could not calculate Strength of Field for race ${race.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
this.presenter.present(
|
||||
leagueId,
|
||||
races.length,
|
||||
completedRaces.length,
|
||||
scheduledRaces.length,
|
||||
sofValues,
|
||||
);
|
||||
this.logger.info(`Successfully presented league statistics for league ${leagueId}.`);
|
||||
} catch (error) {
|
||||
this.logger.error(`Error in GetLeagueStatsUseCase: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
async execute(params: GetLeagueStatsUseCaseParams, presenter: ILeagueStatsPresenter): Promise<void> {
|
||||
const memberships = await this.leagueMembershipRepository.getLeagueMembers(params.leagueId);
|
||||
const races = await this.raceRepository.findByLeagueId(params.leagueId);
|
||||
// TODO: Implement average rating calculation from driver ratings
|
||||
const dto: LeagueStatsResultDTO = {
|
||||
totalMembers: memberships.length,
|
||||
totalRaces: races.length,
|
||||
averageRating: 0,
|
||||
};
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
23
core/racing/application/use-cases/GetTotalDriversUseCase.ts
Normal file
23
core/racing/application/use-cases/GetTotalDriversUseCase.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { IDriverRepository } from '../../domain/repositories/IDriverRepository';
|
||||
import type { ITotalDriversPresenter, TotalDriversResultDTO } from '../presenters/ITotalDriversPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
/**
|
||||
* Use Case for retrieving total number of drivers.
|
||||
*/
|
||||
export class GetTotalDriversUseCase
|
||||
implements UseCase<void, TotalDriversResultDTO, any, ITotalDriversPresenter>
|
||||
{
|
||||
constructor(private readonly driverRepository: IDriverRepository) {}
|
||||
|
||||
async execute(_input: void, presenter: ITotalDriversPresenter): Promise<void> {
|
||||
presenter.reset();
|
||||
|
||||
const drivers = await this.driverRepository.findAll();
|
||||
const dto: TotalDriversResultDTO = {
|
||||
totalDrivers: drivers.length,
|
||||
};
|
||||
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
20
core/racing/application/use-cases/GetTotalLeaguesUseCase.ts
Normal file
20
core/racing/application/use-cases/GetTotalLeaguesUseCase.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
|
||||
import type { IGetTotalLeaguesPresenter, GetTotalLeaguesResultDTO, GetTotalLeaguesViewModel } from '../presenters/IGetTotalLeaguesPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface GetTotalLeaguesUseCaseParams {}
|
||||
|
||||
export interface GetTotalLeaguesResultDTO {
|
||||
totalLeagues: number;
|
||||
}
|
||||
|
||||
export class GetTotalLeaguesUseCase implements UseCase<GetTotalLeaguesUseCaseParams, GetTotalLeaguesResultDTO, GetTotalLeaguesViewModel, IGetTotalLeaguesPresenter> {
|
||||
constructor(private readonly leagueRepository: ILeagueRepository) {}
|
||||
|
||||
async execute(params: GetTotalLeaguesUseCaseParams, presenter: IGetTotalLeaguesPresenter): Promise<void> {
|
||||
const leagues = await this.leagueRepository.findAll();
|
||||
const dto: GetTotalLeaguesResultDTO = { totalLeagues: leagues.length };
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { IRaceRegistrationRepository } from '@gridpilot/racing/domain/repositories/IRaceRegistrationRepository';
|
||||
import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository';
|
||||
import type { IsDriverRegisteredForRaceQueryParamsDTO } from '../dto/RaceRegistrationQueryDTO';
|
||||
import type { IDriverRegistrationStatusPresenter } from '../presenters/IDriverRegistrationStatusPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
/**
|
||||
* Use Case: IsDriverRegisteredForRaceUseCase
|
||||
@@ -8,15 +9,15 @@ import type { IDriverRegistrationStatusPresenter } from '../presenters/IDriverRe
|
||||
* Checks if a driver is registered for a specific race.
|
||||
* Orchestrates domain logic and delegates presentation to the presenter.
|
||||
*/
|
||||
export class IsDriverRegisteredForRaceUseCase {
|
||||
constructor(
|
||||
private readonly registrationRepository: IRaceRegistrationRepository,
|
||||
public readonly presenter: IDriverRegistrationStatusPresenter,
|
||||
) {}
|
||||
export class IsDriverRegisteredForRaceUseCase
|
||||
implements UseCase<IsDriverRegisteredForRaceQueryParamsDTO, boolean, any, IDriverRegistrationStatusPresenter>
|
||||
{
|
||||
constructor(private readonly registrationRepository: IRaceRegistrationRepository) {}
|
||||
|
||||
async execute(params: IsDriverRegisteredForRaceQueryParamsDTO): Promise<void> {
|
||||
async execute(params: IsDriverRegisteredForRaceQueryParamsDTO, presenter: IDriverRegistrationStatusPresenter): Promise<void> {
|
||||
presenter.reset();
|
||||
const { raceId, driverId } = params;
|
||||
const isRegistered = await this.registrationRepository.isRegistered(raceId, driverId);
|
||||
this.presenter.present(isRegistered, raceId, driverId);
|
||||
presenter.present(isRegistered, raceId, driverId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
||||
import type { IRejectLeagueJoinRequestPresenter, RejectLeagueJoinRequestResultDTO, RejectLeagueJoinRequestViewModel } from '../presenters/IRejectLeagueJoinRequestPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface RejectLeagueJoinRequestUseCaseParams {
|
||||
requestId: string;
|
||||
}
|
||||
|
||||
export interface RejectLeagueJoinRequestResultDTO {
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export class RejectLeagueJoinRequestUseCase implements UseCase<RejectLeagueJoinRequestUseCaseParams, RejectLeagueJoinRequestResultDTO, RejectLeagueJoinRequestViewModel, IRejectLeagueJoinRequestPresenter> {
|
||||
constructor(private readonly leagueMembershipRepository: ILeagueMembershipRepository) {}
|
||||
|
||||
async execute(params: RejectLeagueJoinRequestUseCaseParams, presenter: IRejectLeagueJoinRequestPresenter): Promise<void> {
|
||||
await this.leagueMembershipRepository.removeJoinRequest(params.requestId);
|
||||
const dto: RejectLeagueJoinRequestResultDTO = { success: true, message: 'Join request rejected.' };
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
||||
import type { IRemoveLeagueMemberPresenter, RemoveLeagueMemberResultDTO, RemoveLeagueMemberViewModel } from '../presenters/IRemoveLeagueMemberPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface RemoveLeagueMemberUseCaseParams {
|
||||
leagueId: string;
|
||||
targetDriverId: string;
|
||||
}
|
||||
|
||||
export interface RemoveLeagueMemberResultDTO {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export class RemoveLeagueMemberUseCase implements UseCase<RemoveLeagueMemberUseCaseParams, RemoveLeagueMemberResultDTO, RemoveLeagueMemberViewModel, IRemoveLeagueMemberPresenter> {
|
||||
constructor(private readonly leagueMembershipRepository: ILeagueMembershipRepository) {}
|
||||
|
||||
async execute(params: RemoveLeagueMemberUseCaseParams, presenter: IRemoveLeagueMemberPresenter): Promise<void> {
|
||||
const memberships = await this.leagueMembershipRepository.getLeagueMembers(params.leagueId);
|
||||
const membership = memberships.find(m => m.driverId === params.targetDriverId);
|
||||
if (!membership) {
|
||||
throw new Error('Membership not found');
|
||||
}
|
||||
await this.leagueMembershipRepository.saveMembership({
|
||||
...membership,
|
||||
status: 'inactive',
|
||||
});
|
||||
const dto: RemoveLeagueMemberResultDTO = { success: true };
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
||||
import type { IUpdateLeagueMemberRolePresenter, UpdateLeagueMemberRoleResultDTO, UpdateLeagueMemberRoleViewModel } from '../presenters/IUpdateLeagueMemberRolePresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface UpdateLeagueMemberRoleUseCaseParams {
|
||||
leagueId: string;
|
||||
targetDriverId: string;
|
||||
newRole: string;
|
||||
}
|
||||
|
||||
export interface UpdateLeagueMemberRoleResultDTO {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export class UpdateLeagueMemberRoleUseCase implements UseCase<UpdateLeagueMemberRoleUseCaseParams, UpdateLeagueMemberRoleResultDTO, UpdateLeagueMemberRoleViewModel, IUpdateLeagueMemberRolePresenter> {
|
||||
constructor(private readonly leagueMembershipRepository: ILeagueMembershipRepository) {}
|
||||
|
||||
async execute(params: UpdateLeagueMemberRoleUseCaseParams, presenter: IUpdateLeagueMemberRolePresenter): Promise<void> {
|
||||
const memberships = await this.leagueMembershipRepository.getLeagueMembers(params.leagueId);
|
||||
const membership = memberships.find(m => m.driverId === params.targetDriverId);
|
||||
if (!membership) {
|
||||
throw new Error('Membership not found');
|
||||
}
|
||||
await this.leagueMembershipRepository.saveMembership({
|
||||
...membership,
|
||||
role: params.newRole,
|
||||
});
|
||||
const dto: UpdateLeagueMemberRoleResultDTO = { success: true };
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user