refactor
This commit is contained in:
@@ -18,6 +18,9 @@ export * from './use-cases/GetLeagueStandingsUseCase';
|
||||
export * from './use-cases/GetLeagueDriverSeasonStatsUseCase';
|
||||
export * from './use-cases/GetAllLeaguesWithCapacityUseCase';
|
||||
export * from './use-cases/GetAllLeaguesWithCapacityAndScoringUseCase';
|
||||
export * from './use-cases/GetAllRacesUseCase';
|
||||
export * from './use-cases/GetTotalRacesUseCase';
|
||||
export * from './use-cases/ImportRaceResultsApiUseCase';
|
||||
export * from './use-cases/ListLeagueScoringPresetsUseCase';
|
||||
export * from './use-cases/GetLeagueScoringConfigUseCase';
|
||||
export * from './use-cases/RecalculateChampionshipStandingsUseCase';
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface SponsorDto {
|
||||
id: string;
|
||||
name: string;
|
||||
contactEmail: string;
|
||||
websiteUrl: string | undefined;
|
||||
logoUrl: string | undefined;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface CreateSponsorViewModel {
|
||||
sponsor: SponsorDto;
|
||||
}
|
||||
|
||||
export interface CreateSponsorResultDTO {
|
||||
sponsor: SponsorDto;
|
||||
}
|
||||
|
||||
export interface ICreateSponsorPresenter extends Presenter<CreateSponsorResultDTO, CreateSponsorViewModel> {}
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
import type { GetEntitySponsorshipPricingResultDTO } from '../use-cases/GetEntitySponsorshipPricingUseCase';
|
||||
|
||||
export interface IEntitySponsorshipPricingPresenter {
|
||||
present(data: GetEntitySponsorshipPricingResultDTO | null): void;
|
||||
}
|
||||
export interface IEntitySponsorshipPricingPresenter extends Presenter<GetEntitySponsorshipPricingResultDTO | null, GetEntitySponsorshipPricingResultDTO | null> {}
|
||||
20
core/racing/application/presenters/IGetAllRacesPresenter.ts
Normal file
20
core/racing/application/presenters/IGetAllRacesPresenter.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface RaceViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
date: string;
|
||||
leagueName?: string;
|
||||
}
|
||||
|
||||
export interface AllRacesPageViewModel {
|
||||
races: RaceViewModel[];
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
export interface GetAllRacesResultDTO {
|
||||
races: RaceViewModel[];
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
export interface IGetAllRacesPresenter extends Presenter<GetAllRacesResultDTO, AllRacesPageViewModel> {}
|
||||
20
core/racing/application/presenters/IGetSponsorsPresenter.ts
Normal file
20
core/racing/application/presenters/IGetSponsorsPresenter.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface SponsorDto {
|
||||
id: string;
|
||||
name: string;
|
||||
contactEmail: string;
|
||||
websiteUrl: string | undefined;
|
||||
logoUrl: string | undefined;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface GetSponsorsViewModel {
|
||||
sponsors: SponsorDto[];
|
||||
}
|
||||
|
||||
export interface GetSponsorsResultDTO {
|
||||
sponsors: SponsorDto[];
|
||||
}
|
||||
|
||||
export interface IGetSponsorsPresenter extends Presenter<GetSponsorsResultDTO, GetSponsorsViewModel> {}
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface SponsorshipPricingItemDto {
|
||||
id: string;
|
||||
level: string;
|
||||
price: number;
|
||||
currency: string;
|
||||
}
|
||||
|
||||
export interface GetSponsorshipPricingResultDTO {
|
||||
pricing: SponsorshipPricingItemDto[];
|
||||
}
|
||||
|
||||
export interface GetSponsorshipPricingViewModel {
|
||||
pricing: SponsorshipPricingItemDto[];
|
||||
}
|
||||
|
||||
export interface IGetSponsorshipPricingPresenter extends Presenter<GetSponsorshipPricingResultDTO, GetSponsorshipPricingViewModel> {}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface GetTotalRacesViewModel {
|
||||
totalRaces: number;
|
||||
}
|
||||
|
||||
export interface GetTotalRacesResultDTO {
|
||||
totalRaces: number;
|
||||
}
|
||||
|
||||
export interface IGetTotalRacesPresenter extends Presenter<GetTotalRacesResultDTO, GetTotalRacesViewModel> {}
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface ImportRaceResultsSummaryViewModel {
|
||||
success: boolean;
|
||||
raceId: string;
|
||||
driversProcessed: number;
|
||||
resultsRecorded: number;
|
||||
errors?: string[];
|
||||
}
|
||||
|
||||
export interface ImportRaceResultsApiResultDTO {
|
||||
success: boolean;
|
||||
raceId: string;
|
||||
driversProcessed: number;
|
||||
resultsRecorded: number;
|
||||
errors?: string[];
|
||||
}
|
||||
|
||||
export interface IImportRaceResultsApiPresenter extends Presenter<ImportRaceResultsApiResultDTO, ImportRaceResultsSummaryViewModel> {}
|
||||
61
core/racing/application/use-cases/CreateSponsorUseCase.ts
Normal file
61
core/racing/application/use-cases/CreateSponsorUseCase.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Application Use Case: CreateSponsorUseCase
|
||||
*
|
||||
* Creates a new sponsor.
|
||||
*/
|
||||
|
||||
import { Sponsor, type SponsorProps } from '../../domain/entities/Sponsor';
|
||||
import type { ISponsorRepository } from '../../domain/repositories/ISponsorRepository';
|
||||
import type {
|
||||
ICreateSponsorPresenter,
|
||||
CreateSponsorResultDTO,
|
||||
CreateSponsorViewModel,
|
||||
} from '../presenters/ICreateSponsorPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface CreateSponsorInput {
|
||||
name: string;
|
||||
contactEmail: string;
|
||||
websiteUrl?: string;
|
||||
logoUrl?: string;
|
||||
}
|
||||
|
||||
export class CreateSponsorUseCase
|
||||
implements UseCase<CreateSponsorInput, CreateSponsorResultDTO, CreateSponsorViewModel, ICreateSponsorPresenter>
|
||||
{
|
||||
constructor(
|
||||
private readonly sponsorRepository: ISponsorRepository,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
input: CreateSponsorInput,
|
||||
presenter: ICreateSponsorPresenter,
|
||||
): Promise<void> {
|
||||
presenter.reset();
|
||||
|
||||
const id = `sponsor-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
const sponsor = Sponsor.create({
|
||||
id,
|
||||
name: input.name,
|
||||
contactEmail: input.contactEmail,
|
||||
...(input.websiteUrl !== undefined ? { websiteUrl: input.websiteUrl } : {}),
|
||||
...(input.logoUrl !== undefined ? { logoUrl: input.logoUrl } : {}),
|
||||
} as any);
|
||||
|
||||
await this.sponsorRepository.create(sponsor);
|
||||
|
||||
const dto: CreateSponsorResultDTO = {
|
||||
sponsor: {
|
||||
id: sponsor.id,
|
||||
name: sponsor.name,
|
||||
contactEmail: sponsor.contactEmail,
|
||||
websiteUrl: sponsor.websiteUrl,
|
||||
logoUrl: sponsor.logoUrl,
|
||||
createdAt: sponsor.createdAt,
|
||||
},
|
||||
};
|
||||
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
34
core/racing/application/use-cases/GetAllRacesUseCase.ts
Normal file
34
core/racing/application/use-cases/GetAllRacesUseCase.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
|
||||
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
|
||||
import type { IGetAllRacesPresenter, GetAllRacesResultDTO, AllRacesPageViewModel } from '../presenters/IGetAllRacesPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface GetAllRacesUseCaseParams {}
|
||||
|
||||
export class GetAllRacesUseCase implements UseCase<GetAllRacesUseCaseParams, GetAllRacesResultDTO, AllRacesPageViewModel, IGetAllRacesPresenter> {
|
||||
constructor(
|
||||
private readonly raceRepository: IRaceRepository,
|
||||
private readonly leagueRepository: ILeagueRepository,
|
||||
) {}
|
||||
|
||||
async execute(params: GetAllRacesUseCaseParams, presenter: IGetAllRacesPresenter): Promise<void> {
|
||||
const races = await this.raceRepository.findAll();
|
||||
const leagues = await this.leagueRepository.findAll();
|
||||
const leagueMap = new Map(leagues.map(league => [league.id, league.name]));
|
||||
|
||||
const raceViewModels = races.map(race => ({
|
||||
id: race.id,
|
||||
name: `Race ${race.id}`, // Placeholder, adjust based on domain
|
||||
date: race.scheduledAt.toISOString(),
|
||||
leagueName: leagueMap.get(race.leagueId) || 'Unknown League',
|
||||
}));
|
||||
|
||||
const dto: GetAllRacesResultDTO = {
|
||||
races: raceViewModels,
|
||||
totalCount: races.length,
|
||||
};
|
||||
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,7 @@ import type { ISeasonSponsorshipRepository } from '../../domain/repositories/ISe
|
||||
import type { SponsorableEntityType } from '../../domain/entities/SponsorshipRequest';
|
||||
import type { SponsorshipTier } from '../../domain/entities/SeasonSponsorship';
|
||||
import type { IEntitySponsorshipPricingPresenter } from '../presenters/IEntitySponsorshipPricingPresenter';
|
||||
import type { AsyncUseCase } from '@gridpilot/shared/application';
|
||||
import type { Logger } from '../../../shared/src/logging/Logger';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface GetEntitySponsorshipPricingDTO {
|
||||
entityType: SponsorableEntityType;
|
||||
@@ -41,35 +40,28 @@ export interface GetEntitySponsorshipPricingResultDTO {
|
||||
}
|
||||
|
||||
export class GetEntitySponsorshipPricingUseCase
|
||||
implements AsyncUseCase<GetEntitySponsorshipPricingDTO, void> {
|
||||
implements UseCase<GetEntitySponsorshipPricingDTO, GetEntitySponsorshipPricingResultDTO | null, GetEntitySponsorshipPricingResultDTO | null, IEntitySponsorshipPricingPresenter>
|
||||
{
|
||||
constructor(
|
||||
private readonly sponsorshipPricingRepo: ISponsorshipPricingRepository,
|
||||
private readonly sponsorshipRequestRepo: ISponsorshipRequestRepository,
|
||||
private readonly seasonSponsorshipRepo: ISeasonSponsorshipRepository,
|
||||
private readonly presenter: IEntitySponsorshipPricingPresenter,
|
||||
private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(dto: GetEntitySponsorshipPricingDTO): Promise<void> {
|
||||
this.logger.debug(
|
||||
`Executing GetEntitySponsorshipPricingUseCase for entityType: ${dto.entityType}, entityId: ${dto.entityId}`,
|
||||
{ dto },
|
||||
);
|
||||
async execute(
|
||||
dto: GetEntitySponsorshipPricingDTO,
|
||||
presenter: IEntitySponsorshipPricingPresenter,
|
||||
): Promise<void> {
|
||||
presenter.reset();
|
||||
|
||||
try {
|
||||
const pricing = await this.sponsorshipPricingRepo.findByEntity(dto.entityType, dto.entityId);
|
||||
|
||||
if (!pricing) {
|
||||
this.logger.warn(
|
||||
`No pricing found for entityType: ${dto.entityType}, entityId: ${dto.entityId}. Presenting null.`,
|
||||
{ dto },
|
||||
);
|
||||
this.presenter.present(null);
|
||||
presenter.present(null);
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.debug(`Found pricing for entityType: ${dto.entityType}, entityId: ${dto.entityId}`, { pricing });
|
||||
|
||||
// Count pending requests by tier
|
||||
const pendingRequests = await this.sponsorshipRequestRepo.findPendingByEntity(
|
||||
dto.entityType,
|
||||
@@ -78,10 +70,6 @@ export class GetEntitySponsorshipPricingUseCase
|
||||
const pendingMainCount = pendingRequests.filter(r => r.tier === 'main').length;
|
||||
const pendingSecondaryCount = pendingRequests.filter(r => r.tier === 'secondary').length;
|
||||
|
||||
this.logger.debug(
|
||||
`Pending requests counts: main=${pendingMainCount}, secondary=${pendingSecondaryCount}`,
|
||||
);
|
||||
|
||||
// Count filled slots (for seasons, check SeasonSponsorship table)
|
||||
let filledMainSlots = 0;
|
||||
let filledSecondarySlots = 0;
|
||||
@@ -91,9 +79,6 @@ export class GetEntitySponsorshipPricingUseCase
|
||||
const activeSponsorships = sponsorships.filter(s => s.isActive());
|
||||
filledMainSlots = activeSponsorships.filter(s => s.tier === 'main').length;
|
||||
filledSecondarySlots = activeSponsorships.filter(s => s.tier === 'secondary').length;
|
||||
this.logger.debug(
|
||||
`Filled slots for season: main=${filledMainSlots}, secondary=${filledSecondarySlots}`,
|
||||
);
|
||||
}
|
||||
|
||||
const result: GetEntitySponsorshipPricingResultDTO = {
|
||||
@@ -118,7 +103,6 @@ export class GetEntitySponsorshipPricingUseCase
|
||||
filledSlots: filledMainSlots,
|
||||
pendingRequests: pendingMainCount,
|
||||
};
|
||||
this.logger.debug(`Main slot pricing information processed`, { mainSlot: result.mainSlot });
|
||||
}
|
||||
|
||||
if (pricing.secondarySlots) {
|
||||
@@ -135,26 +119,10 @@ export class GetEntitySponsorshipPricingUseCase
|
||||
filledSlots: filledSecondarySlots,
|
||||
pendingRequests: pendingSecondaryCount,
|
||||
};
|
||||
this.logger.debug(`Secondary slot pricing information processed`, {
|
||||
secondarySlot: result.secondarySlot,
|
||||
});
|
||||
}
|
||||
|
||||
this.logger.info(
|
||||
`Successfully retrieved and processed entity sponsorship pricing for entityType: ${dto.entityType}, entityId: ${dto.entityId}`,
|
||||
{ result },
|
||||
);
|
||||
this.presenter.present(result);
|
||||
presenter.present(result);
|
||||
} catch (error: unknown) {
|
||||
let errorMessage = 'An unknown error occurred';
|
||||
if (error instanceof Error) {
|
||||
errorMessage = error.message;
|
||||
}
|
||||
this.logger.error(
|
||||
`Failed to get entity sponsorship pricing for entityType: ${dto.entityType}, entityId: ${dto.entityId}. Error: ${errorMessage}`,
|
||||
{ error, dto },
|
||||
);
|
||||
// Re-throw the error or present an error state if the presenter supports it
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
43
core/racing/application/use-cases/GetSponsorsUseCase.ts
Normal file
43
core/racing/application/use-cases/GetSponsorsUseCase.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Application Use Case: GetSponsorsUseCase
|
||||
*
|
||||
* Retrieves all sponsors.
|
||||
*/
|
||||
|
||||
import type { ISponsorRepository } from '../../domain/repositories/ISponsorRepository';
|
||||
import type {
|
||||
IGetSponsorsPresenter,
|
||||
GetSponsorsResultDTO,
|
||||
GetSponsorsViewModel,
|
||||
} from '../presenters/IGetSponsorsPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export class GetSponsorsUseCase
|
||||
implements UseCase<void, GetSponsorsResultDTO, GetSponsorsViewModel, IGetSponsorsPresenter>
|
||||
{
|
||||
constructor(
|
||||
private readonly sponsorRepository: ISponsorRepository,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
_input: void,
|
||||
presenter: IGetSponsorsPresenter,
|
||||
): Promise<void> {
|
||||
presenter.reset();
|
||||
|
||||
const sponsors = await this.sponsorRepository.findAll();
|
||||
|
||||
const dto: GetSponsorsResultDTO = {
|
||||
sponsors: sponsors.map(sponsor => ({
|
||||
id: sponsor.id,
|
||||
name: sponsor.name,
|
||||
contactEmail: sponsor.contactEmail,
|
||||
websiteUrl: sponsor.websiteUrl,
|
||||
logoUrl: sponsor.logoUrl,
|
||||
createdAt: sponsor.createdAt,
|
||||
})),
|
||||
};
|
||||
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Application Use Case: GetSponsorshipPricingUseCase
|
||||
*
|
||||
* Retrieves general sponsorship pricing tiers.
|
||||
*/
|
||||
|
||||
import type {
|
||||
IGetSponsorshipPricingPresenter,
|
||||
GetSponsorshipPricingResultDTO,
|
||||
GetSponsorshipPricingViewModel,
|
||||
} from '../presenters/IGetSponsorshipPricingPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export class GetSponsorshipPricingUseCase
|
||||
implements UseCase<void, GetSponsorshipPricingResultDTO, GetSponsorshipPricingViewModel, IGetSponsorshipPricingPresenter>
|
||||
{
|
||||
constructor() {}
|
||||
|
||||
async execute(
|
||||
_input: void,
|
||||
presenter: IGetSponsorshipPricingPresenter,
|
||||
): Promise<void> {
|
||||
presenter.reset();
|
||||
|
||||
const dto: GetSponsorshipPricingResultDTO = {
|
||||
pricing: [
|
||||
{ id: 'tier-bronze', level: 'Bronze', price: 100, currency: 'USD' },
|
||||
{ id: 'tier-silver', level: 'Silver', price: 250, currency: 'USD' },
|
||||
{ id: 'tier-gold', level: 'Gold', price: 500, currency: 'USD' },
|
||||
],
|
||||
};
|
||||
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
20
core/racing/application/use-cases/GetTotalRacesUseCase.ts
Normal file
20
core/racing/application/use-cases/GetTotalRacesUseCase.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
|
||||
import type { IGetTotalRacesPresenter, GetTotalRacesResultDTO, GetTotalRacesViewModel } from '../presenters/IGetTotalRacesPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface GetTotalRacesUseCaseParams {}
|
||||
|
||||
export interface GetTotalRacesResultDTO {
|
||||
totalRaces: number;
|
||||
}
|
||||
|
||||
export class GetTotalRacesUseCase implements UseCase<GetTotalRacesUseCaseParams, GetTotalRacesResultDTO, GetTotalRacesViewModel, IGetTotalRacesPresenter> {
|
||||
constructor(private readonly raceRepository: IRaceRepository) {}
|
||||
|
||||
async execute(params: GetTotalRacesUseCaseParams, presenter: IGetTotalRacesPresenter): Promise<void> {
|
||||
const races = await this.raceRepository.findAll();
|
||||
const dto: GetTotalRacesResultDTO = { totalRaces: races.length };
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { IImportRaceResultsApiPresenter, ImportRaceResultsApiResultDTO, ImportRaceResultsSummaryViewModel } from '../presenters/IImportRaceResultsApiPresenter';
|
||||
import type { UseCase } from '@gridpilot/shared/application/UseCase';
|
||||
|
||||
export interface ImportRaceResultsApiParams {
|
||||
raceId: string;
|
||||
resultsFileContent: string;
|
||||
}
|
||||
|
||||
export class ImportRaceResultsApiUseCase implements UseCase<ImportRaceResultsApiParams, ImportRaceResultsApiResultDTO, ImportRaceResultsSummaryViewModel, IImportRaceResultsApiPresenter> {
|
||||
constructor() {} // No repositories for mock
|
||||
|
||||
async execute(params: ImportRaceResultsApiParams, presenter: IImportRaceResultsApiPresenter): Promise<void> {
|
||||
// Mock implementation
|
||||
const dto: ImportRaceResultsApiResultDTO = {
|
||||
success: true,
|
||||
raceId: params.raceId,
|
||||
driversProcessed: 10,
|
||||
resultsRecorded: 10,
|
||||
errors: [],
|
||||
};
|
||||
|
||||
presenter.reset();
|
||||
presenter.present(dto);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user