wip league admin tools
This commit is contained in:
@@ -27,8 +27,8 @@ export class GetLeagueMembershipsPresenter implements UseCaseOutputPort<GetLeagu
|
||||
joinedAt: driver!.joinedAt.toDate().toISOString(),
|
||||
...(driver!.bio ? { bio: driver!.bio.toString() } : {}),
|
||||
},
|
||||
role: membership.role.toString() as 'owner' | 'manager' | 'member',
|
||||
joinedAt: membership.joinedAt.toDate(),
|
||||
role: membership.role.toString() as 'owner' | 'admin' | 'steward' | 'member',
|
||||
joinedAt: membership.joinedAt.toDate().toISOString(),
|
||||
}));
|
||||
this.result = {
|
||||
memberships: {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { GetLeagueRosterMembersResult } from '@core/racing/application/use-cases/GetLeagueRosterMembersUseCase';
|
||||
import type { GetLeagueRosterJoinRequestsResult } from '@core/racing/application/use-cases/GetLeagueRosterJoinRequestsUseCase';
|
||||
import type { LeagueRosterMemberDTO } from '../dtos/LeagueRosterMemberDTO';
|
||||
import type { LeagueRosterJoinRequestDTO } from '../dtos/LeagueRosterJoinRequestDTO';
|
||||
import type { DriverDTO } from '../../driver/dtos/DriverDTO';
|
||||
|
||||
export class GetLeagueRosterMembersPresenter implements UseCaseOutputPort<GetLeagueRosterMembersResult> {
|
||||
private viewModel: LeagueRosterMemberDTO[] | null = null;
|
||||
|
||||
reset(): void {
|
||||
this.viewModel = null;
|
||||
}
|
||||
|
||||
present(result: GetLeagueRosterMembersResult): void {
|
||||
this.viewModel = result.members.map(({ membership, driver }) => ({
|
||||
driverId: membership.driverId.toString(),
|
||||
driver: this.mapDriver(driver),
|
||||
role: membership.role.toString() as 'owner' | 'admin' | 'steward' | 'member',
|
||||
joinedAt: membership.joinedAt.toDate().toISOString(),
|
||||
}));
|
||||
}
|
||||
|
||||
getViewModel(): LeagueRosterMemberDTO[] | null {
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
private mapDriver(driver: GetLeagueRosterMembersResult['members'][number]['driver']): DriverDTO {
|
||||
return {
|
||||
id: driver.id,
|
||||
iracingId: driver.iracingId.toString(),
|
||||
name: driver.name.toString(),
|
||||
country: driver.country.toString(),
|
||||
joinedAt: driver.joinedAt.toDate().toISOString(),
|
||||
...(driver.bio ? { bio: driver.bio.toString() } : {}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class GetLeagueRosterJoinRequestsPresenter implements UseCaseOutputPort<GetLeagueRosterJoinRequestsResult> {
|
||||
private viewModel: LeagueRosterJoinRequestDTO[] | null = null;
|
||||
|
||||
reset(): void {
|
||||
this.viewModel = null;
|
||||
}
|
||||
|
||||
present(result: GetLeagueRosterJoinRequestsResult): void {
|
||||
this.viewModel = result.joinRequests.map(req => ({
|
||||
id: req.id,
|
||||
leagueId: req.leagueId,
|
||||
driverId: req.driverId,
|
||||
requestedAt: req.requestedAt.toISOString(),
|
||||
...(req.message ? { message: req.message } : {}),
|
||||
driver: {
|
||||
id: req.driver.id,
|
||||
name: req.driver.name.toString(),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
getViewModel(): LeagueRosterJoinRequestDTO[] | null {
|
||||
return this.viewModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { LeagueSchedulePresenter } from './LeagueSchedulePresenter';
|
||||
import { Race } from '@core/racing/domain/entities/Race';
|
||||
|
||||
describe('LeagueSchedulePresenter', () => {
|
||||
it('includes seasonId on the schedule DTO and serializes dates to ISO strings', () => {
|
||||
const presenter = new LeagueSchedulePresenter();
|
||||
|
||||
const race = Race.create({
|
||||
id: 'race-1',
|
||||
leagueId: 'league-1',
|
||||
scheduledAt: new Date('2025-01-02T20:00:00Z'),
|
||||
track: 'Spa',
|
||||
car: 'GT3',
|
||||
});
|
||||
|
||||
presenter.present({
|
||||
league: { id: 'league-1' },
|
||||
seasonId: 'season-1',
|
||||
published: false,
|
||||
races: [{ race }],
|
||||
} as any);
|
||||
|
||||
const vm = presenter.getViewModel() as any;
|
||||
|
||||
expect(vm).not.toBeNull();
|
||||
expect(vm.seasonId).toBe('season-1');
|
||||
expect(vm.published).toBe(false);
|
||||
|
||||
expect(Array.isArray(vm.races)).toBe(true);
|
||||
expect(vm.races[0]).toMatchObject({
|
||||
id: 'race-1',
|
||||
name: 'Spa - GT3',
|
||||
date: '2025-01-02T20:00:00.000Z',
|
||||
});
|
||||
|
||||
// Guard: dates must be ISO strings (no Date objects)
|
||||
expect(typeof vm.races[0].date).toBe('string');
|
||||
expect(vm.races[0].date).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/);
|
||||
});
|
||||
});
|
||||
@@ -12,6 +12,8 @@ export class LeagueSchedulePresenter implements UseCaseOutputPort<GetLeagueSched
|
||||
|
||||
present(result: GetLeagueScheduleResult, leagueName?: string) {
|
||||
this.result = {
|
||||
seasonId: result.seasonId,
|
||||
published: result.published,
|
||||
races: result.races.map(race => ({
|
||||
id: race.race.id,
|
||||
name: `${race.race.track} - ${race.race.car}`,
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import type { UseCaseOutputPort } from '@core/shared/application';
|
||||
|
||||
import type {
|
||||
CreateLeagueScheduleRaceOutputDTO,
|
||||
LeagueScheduleRaceMutationSuccessDTO,
|
||||
} from '../dtos/LeagueScheduleRaceAdminDTO';
|
||||
import type { LeagueSeasonSchedulePublishOutputDTO } from '../dtos/LeagueSeasonSchedulePublishDTO';
|
||||
|
||||
import type { CreateLeagueSeasonScheduleRaceResult } from '@core/racing/application/use-cases/CreateLeagueSeasonScheduleRaceUseCase';
|
||||
import type { UpdateLeagueSeasonScheduleRaceResult } from '@core/racing/application/use-cases/UpdateLeagueSeasonScheduleRaceUseCase';
|
||||
import type { DeleteLeagueSeasonScheduleRaceResult } from '@core/racing/application/use-cases/DeleteLeagueSeasonScheduleRaceUseCase';
|
||||
import type { PublishLeagueSeasonScheduleResult } from '@core/racing/application/use-cases/PublishLeagueSeasonScheduleUseCase';
|
||||
import type { UnpublishLeagueSeasonScheduleResult } from '@core/racing/application/use-cases/UnpublishLeagueSeasonScheduleUseCase';
|
||||
|
||||
export class CreateLeagueSeasonScheduleRacePresenter
|
||||
implements UseCaseOutputPort<CreateLeagueSeasonScheduleRaceResult>
|
||||
{
|
||||
private responseModel: CreateLeagueScheduleRaceOutputDTO | null = null;
|
||||
|
||||
present(result: CreateLeagueSeasonScheduleRaceResult): void {
|
||||
this.responseModel = { raceId: result.raceId };
|
||||
}
|
||||
|
||||
getResponseModel(): CreateLeagueScheduleRaceOutputDTO | null {
|
||||
return this.responseModel;
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.responseModel = null;
|
||||
}
|
||||
}
|
||||
|
||||
export class UpdateLeagueSeasonScheduleRacePresenter
|
||||
implements UseCaseOutputPort<UpdateLeagueSeasonScheduleRaceResult>
|
||||
{
|
||||
private responseModel: LeagueScheduleRaceMutationSuccessDTO | null = null;
|
||||
|
||||
present(result: UpdateLeagueSeasonScheduleRaceResult): void {
|
||||
void result;
|
||||
this.responseModel = { success: true };
|
||||
}
|
||||
|
||||
getResponseModel(): LeagueScheduleRaceMutationSuccessDTO | null {
|
||||
return this.responseModel;
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.responseModel = null;
|
||||
}
|
||||
}
|
||||
|
||||
export class DeleteLeagueSeasonScheduleRacePresenter
|
||||
implements UseCaseOutputPort<DeleteLeagueSeasonScheduleRaceResult>
|
||||
{
|
||||
private responseModel: LeagueScheduleRaceMutationSuccessDTO | null = null;
|
||||
|
||||
present(result: DeleteLeagueSeasonScheduleRaceResult): void {
|
||||
void result;
|
||||
this.responseModel = { success: true };
|
||||
}
|
||||
|
||||
getResponseModel(): LeagueScheduleRaceMutationSuccessDTO | null {
|
||||
return this.responseModel;
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.responseModel = null;
|
||||
}
|
||||
}
|
||||
|
||||
export class PublishLeagueSeasonSchedulePresenter
|
||||
implements UseCaseOutputPort<PublishLeagueSeasonScheduleResult>
|
||||
{
|
||||
private responseModel: LeagueSeasonSchedulePublishOutputDTO | null = null;
|
||||
|
||||
present(result: PublishLeagueSeasonScheduleResult): void {
|
||||
this.responseModel = { success: true, published: result.published };
|
||||
}
|
||||
|
||||
getResponseModel(): LeagueSeasonSchedulePublishOutputDTO | null {
|
||||
return this.responseModel;
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.responseModel = null;
|
||||
}
|
||||
}
|
||||
|
||||
export class UnpublishLeagueSeasonSchedulePresenter
|
||||
implements UseCaseOutputPort<UnpublishLeagueSeasonScheduleResult>
|
||||
{
|
||||
private responseModel: LeagueSeasonSchedulePublishOutputDTO | null = null;
|
||||
|
||||
present(result: UnpublishLeagueSeasonScheduleResult): void {
|
||||
this.responseModel = { success: true, published: result.published };
|
||||
}
|
||||
|
||||
getResponseModel(): LeagueSeasonSchedulePublishOutputDTO | null {
|
||||
return this.responseModel;
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.responseModel = null;
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,9 @@ export class RejectLeagueJoinRequestPresenter implements UseCaseOutputPort<Rejec
|
||||
}
|
||||
|
||||
present(result: RejectLeagueJoinRequestResult): void {
|
||||
void result;
|
||||
|
||||
this.result = {
|
||||
success: true,
|
||||
message: 'Join request rejected successfully',
|
||||
success: result.success,
|
||||
message: result.message,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user