refactor core presenters
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export type SkillLevel = 'beginner' | 'intermediate' | 'advanced' | 'pro';
|
||||
|
||||
class TeamLeaderboardItemDTO {
|
||||
@ApiProperty()
|
||||
id: string;
|
||||
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
|
||||
@ApiProperty()
|
||||
memberCount: number;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
rating: number | null;
|
||||
|
||||
@ApiProperty()
|
||||
totalWins: number;
|
||||
|
||||
@ApiProperty()
|
||||
totalRaces: number;
|
||||
|
||||
@ApiProperty({ enum: ['beginner', 'intermediate', 'advanced', 'pro'] })
|
||||
performanceLevel: SkillLevel;
|
||||
|
||||
@ApiProperty()
|
||||
isRecruiting: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
createdAt: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
description?: string;
|
||||
|
||||
@ApiProperty({ enum: ['endurance', 'sprint', 'mixed'], required: false })
|
||||
specialization?: 'endurance' | 'sprint' | 'mixed';
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
region?: string;
|
||||
|
||||
@ApiProperty({ type: [String], required: false })
|
||||
languages?: string[];
|
||||
}
|
||||
|
||||
export class GetTeamsLeaderboardOutputDTO {
|
||||
@ApiProperty({ type: [TeamLeaderboardItemDTO] })
|
||||
teams: TeamLeaderboardItemDTO[];
|
||||
|
||||
@ApiProperty()
|
||||
recruitingCount: number;
|
||||
|
||||
@ApiProperty({ type: 'object', additionalProperties: { type: 'array', items: { $ref: '#/components/schemas/TeamLeaderboardItemDTO' } } })
|
||||
groupsBySkillLevel: Record<SkillLevel, TeamLeaderboardItemDTO[]>;
|
||||
|
||||
@ApiProperty({ type: [TeamLeaderboardItemDTO] })
|
||||
topTeams: TeamLeaderboardItemDTO[];
|
||||
}
|
||||
@@ -1,33 +1,33 @@
|
||||
import { IAllTeamsPresenter, AllTeamsResultDTO, AllTeamsViewModel, TeamListItemViewModel } from '@core/racing/application/presenters/IAllTeamsPresenter';
|
||||
import { GetAllTeamsOutputPort } from '@core/racing/application/ports/output/GetAllTeamsOutputPort';
|
||||
import { GetAllTeamsOutputDTO } from '../dtos/GetAllTeamsOutputDTO';
|
||||
|
||||
export class AllTeamsPresenter implements IAllTeamsPresenter {
|
||||
private result: AllTeamsViewModel | null = null;
|
||||
export class AllTeamsPresenter {
|
||||
private result: GetAllTeamsOutputDTO | null = null;
|
||||
|
||||
reset() {
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
present(dto: AllTeamsResultDTO) {
|
||||
const teams: TeamListItemViewModel[] = dto.teams.map(team => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
tag: team.tag,
|
||||
description: team.description,
|
||||
memberCount: team.memberCount,
|
||||
leagues: team.leagues || [],
|
||||
}));
|
||||
|
||||
async present(output: GetAllTeamsOutputPort) {
|
||||
this.result = {
|
||||
teams,
|
||||
totalCount: teams.length,
|
||||
teams: output.teams.map(team => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
tag: team.tag,
|
||||
description: team.description,
|
||||
memberCount: team.memberCount,
|
||||
leagues: team.leagues || [],
|
||||
// Note: specialization, region, languages not available in output port
|
||||
})),
|
||||
totalCount: output.totalCount || output.teams.length,
|
||||
};
|
||||
}
|
||||
|
||||
getViewModel(): AllTeamsViewModel | null {
|
||||
getViewModel(): GetAllTeamsOutputDTO | null {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
get viewModel(): AllTeamsViewModel {
|
||||
get viewModel(): GetAllTeamsOutputDTO {
|
||||
if (!this.result) throw new Error('Presenter not presented');
|
||||
return this.result;
|
||||
}
|
||||
|
||||
@@ -1,41 +1,38 @@
|
||||
import { IDriverTeamPresenter, DriverTeamResultDTO, DriverTeamViewModel } from '@core/racing/application/presenters/IDriverTeamPresenter';
|
||||
import { DriverTeamOutputPort } from '@core/racing/application/ports/output/DriverTeamOutputPort';
|
||||
import { GetDriverTeamOutputDTO } from '../dtos/GetDriverTeamOutputDTO';
|
||||
|
||||
export class DriverTeamPresenter implements IDriverTeamPresenter {
|
||||
private result: DriverTeamViewModel | null = null;
|
||||
export class DriverTeamPresenter {
|
||||
private result: GetDriverTeamOutputDTO | null = null;
|
||||
|
||||
reset() {
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
present(dto: DriverTeamResultDTO) {
|
||||
const isOwner = dto.team.ownerId === dto.driverId;
|
||||
const canManage = isOwner || dto.membership.role === 'owner' || dto.membership.role === 'manager';
|
||||
async present(output: DriverTeamOutputPort) {
|
||||
const isOwner = output.team.ownerId === output.driverId;
|
||||
const canManage = isOwner || output.membership.role === 'owner' || output.membership.role === 'manager';
|
||||
|
||||
this.result = {
|
||||
team: {
|
||||
id: dto.team.id,
|
||||
name: dto.team.name,
|
||||
tag: dto.team.tag,
|
||||
description: dto.team.description || '',
|
||||
ownerId: dto.team.ownerId,
|
||||
leagues: dto.team.leagues || [],
|
||||
id: output.team.id,
|
||||
name: output.team.name,
|
||||
tag: output.team.tag,
|
||||
description: output.team.description || '',
|
||||
ownerId: output.team.ownerId,
|
||||
leagues: output.team.leagues || [],
|
||||
createdAt: output.team.createdAt.toISOString(),
|
||||
},
|
||||
membership: {
|
||||
role: dto.membership.role as 'owner' | 'manager' | 'member',
|
||||
joinedAt: dto.membership.joinedAt.toISOString(),
|
||||
isActive: dto.membership.status === 'active',
|
||||
role: output.membership.role === 'driver' ? 'member' : output.membership.role,
|
||||
joinedAt: output.membership.joinedAt.toISOString(),
|
||||
isActive: output.membership.status === 'active',
|
||||
},
|
||||
isOwner,
|
||||
canManage,
|
||||
};
|
||||
}
|
||||
|
||||
getViewModel(): DriverTeamViewModel | null {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
get viewModel(): DriverTeamViewModel {
|
||||
if (!this.result) throw new Error('Presenter not presented');
|
||||
getViewModel(): GetDriverTeamOutputDTO | null {
|
||||
return this.result;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,36 @@
|
||||
import {
|
||||
ITeamDetailsPresenter,
|
||||
TeamDetailsResultDTO,
|
||||
TeamDetailsViewModel,
|
||||
} from '@core/racing/application/presenters/ITeamDetailsPresenter';
|
||||
import type { GetTeamDetailsOutputPort } from '@core/racing/application/ports/output/GetTeamDetailsOutputPort';
|
||||
import type { GetTeamDetailsOutputDTO } from '../dtos/GetTeamDetailsOutputDTO';
|
||||
|
||||
export class TeamDetailsPresenter implements ITeamDetailsPresenter {
|
||||
private result: TeamDetailsViewModel | null = null;
|
||||
export class TeamDetailsPresenter {
|
||||
private result: GetTeamDetailsOutputDTO | null = null;
|
||||
|
||||
reset() {
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
present(dto: TeamDetailsResultDTO) {
|
||||
const { team, membership } = dto;
|
||||
|
||||
const canManage =
|
||||
membership !== null &&
|
||||
(membership.role === 'owner' || membership.role === 'manager');
|
||||
|
||||
async present(outputPort: GetTeamDetailsOutputPort): Promise<void> {
|
||||
this.result = {
|
||||
team: {
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
tag: team.tag,
|
||||
description: team.description,
|
||||
ownerId: team.ownerId,
|
||||
leagues: team.leagues || [],
|
||||
createdAt: team.createdAt?.toISOString() || new Date().toISOString(),
|
||||
id: outputPort.team.id,
|
||||
name: outputPort.team.name,
|
||||
tag: outputPort.team.tag,
|
||||
description: outputPort.team.description,
|
||||
ownerId: outputPort.team.ownerId,
|
||||
leagues: outputPort.team.leagues,
|
||||
createdAt: outputPort.team.createdAt.toISOString(),
|
||||
},
|
||||
membership: membership
|
||||
membership: outputPort.membership
|
||||
? {
|
||||
role: membership.role as 'owner' | 'manager' | 'member',
|
||||
joinedAt: membership.joinedAt.toISOString(),
|
||||
isActive: membership.status === 'active',
|
||||
role: outputPort.membership.role,
|
||||
joinedAt: outputPort.membership.joinedAt.toISOString(),
|
||||
isActive: outputPort.membership.isActive,
|
||||
}
|
||||
: null,
|
||||
canManage,
|
||||
canManage: outputPort.canManage,
|
||||
};
|
||||
}
|
||||
|
||||
getViewModel(): TeamDetailsViewModel | null {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
get viewModel(): TeamDetailsViewModel {
|
||||
if (!this.result) throw new Error('Presenter not presented');
|
||||
getViewModel(): GetTeamDetailsOutputDTO | null {
|
||||
return this.result;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,30 @@
|
||||
import {
|
||||
ITeamJoinRequestsPresenter,
|
||||
TeamJoinRequestsResultDTO,
|
||||
TeamJoinRequestsViewModel,
|
||||
TeamJoinRequestViewModel,
|
||||
} from '@core/racing/application/presenters/ITeamJoinRequestsPresenter';
|
||||
import type { TeamJoinRequestsOutputPort } from '@core/racing/application/ports/output/TeamJoinRequestsOutputPort';
|
||||
import type { GetTeamJoinRequestsOutputDTO } from '../dtos/GetTeamJoinRequestsOutputDTO';
|
||||
|
||||
export class TeamJoinRequestsPresenter implements ITeamJoinRequestsPresenter {
|
||||
private result: TeamJoinRequestsViewModel | null = null;
|
||||
export class TeamJoinRequestsPresenter {
|
||||
private result: GetTeamJoinRequestsOutputDTO | null = null;
|
||||
|
||||
reset() {
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
present(dto: TeamJoinRequestsResultDTO) {
|
||||
const { requests, driverNames, avatarUrls } = dto;
|
||||
|
||||
const requestViewModels: TeamJoinRequestViewModel[] = requests.map((request) => ({
|
||||
requestId: request.id,
|
||||
driverId: request.driverId,
|
||||
driverName: driverNames[request.driverId] || 'Unknown',
|
||||
teamId: request.teamId,
|
||||
status: 'pending' as const,
|
||||
requestedAt: request.requestedAt.toISOString(),
|
||||
avatarUrl: avatarUrls[request.driverId] || '',
|
||||
}));
|
||||
|
||||
async present(outputPort: TeamJoinRequestsOutputPort): Promise<void> {
|
||||
this.result = {
|
||||
requests: requestViewModels,
|
||||
pendingCount: requestViewModels.length,
|
||||
totalCount: requestViewModels.length,
|
||||
requests: outputPort.requests.map(request => ({
|
||||
requestId: request.requestId,
|
||||
driverId: request.driverId,
|
||||
driverName: request.driverName,
|
||||
teamId: request.teamId,
|
||||
status: request.status,
|
||||
requestedAt: request.requestedAt.toISOString(),
|
||||
avatarUrl: request.avatarUrl,
|
||||
})),
|
||||
pendingCount: outputPort.pendingCount,
|
||||
totalCount: outputPort.totalCount,
|
||||
};
|
||||
}
|
||||
|
||||
getViewModel(): TeamJoinRequestsViewModel | null {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
get viewModel(): TeamJoinRequestsViewModel {
|
||||
if (!this.result) throw new Error('Presenter not presented');
|
||||
getViewModel(): GetTeamJoinRequestsOutputDTO | null {
|
||||
return this.result;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,31 @@
|
||||
import {
|
||||
ITeamMembersPresenter,
|
||||
TeamMembersResultDTO,
|
||||
TeamMembersViewModel,
|
||||
TeamMemberViewModel,
|
||||
} from '@core/racing/application/presenters/ITeamMembersPresenter';
|
||||
import type { TeamMembersOutputPort } from '@core/racing/application/ports/output/TeamMembersOutputPort';
|
||||
import type { GetTeamMembersOutputDTO } from '../dtos/GetTeamMembersOutputDTO';
|
||||
|
||||
export class TeamMembersPresenter implements ITeamMembersPresenter {
|
||||
private result: TeamMembersViewModel | null = null;
|
||||
export class TeamMembersPresenter {
|
||||
private result: GetTeamMembersOutputDTO | null = null;
|
||||
|
||||
reset() {
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
present(dto: TeamMembersResultDTO) {
|
||||
const { memberships, driverNames, avatarUrls } = dto;
|
||||
|
||||
const members: TeamMemberViewModel[] = memberships.map((membership) => ({
|
||||
driverId: membership.driverId,
|
||||
driverName: driverNames[membership.driverId] || 'Unknown',
|
||||
role: membership.role as 'owner' | 'manager' | 'member',
|
||||
joinedAt: membership.joinedAt.toISOString(),
|
||||
isActive: membership.status === 'active',
|
||||
avatarUrl: avatarUrls[membership.driverId] || '',
|
||||
}));
|
||||
|
||||
const ownerCount = members.filter((m) => m.role === 'owner').length;
|
||||
const managerCount = members.filter((m) => m.role === 'manager').length;
|
||||
const memberCount = members.filter((m) => m.role === 'member').length;
|
||||
|
||||
async present(outputPort: TeamMembersOutputPort): Promise<void> {
|
||||
this.result = {
|
||||
members,
|
||||
totalCount: members.length,
|
||||
ownerCount,
|
||||
managerCount,
|
||||
memberCount,
|
||||
members: outputPort.members.map(member => ({
|
||||
driverId: member.driverId,
|
||||
driverName: member.driverName,
|
||||
role: member.role,
|
||||
joinedAt: member.joinedAt.toISOString(),
|
||||
isActive: member.isActive,
|
||||
avatarUrl: member.avatarUrl,
|
||||
})),
|
||||
totalCount: outputPort.totalCount,
|
||||
ownerCount: outputPort.ownerCount,
|
||||
managerCount: outputPort.managerCount,
|
||||
memberCount: outputPort.memberCount,
|
||||
};
|
||||
}
|
||||
|
||||
getViewModel(): TeamMembersViewModel | null {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
get viewModel(): TeamMembersViewModel {
|
||||
if (!this.result) throw new Error('Presenter not presented');
|
||||
getViewModel(): GetTeamMembersOutputDTO | null {
|
||||
return this.result;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,112 @@
|
||||
import { ITeamsLeaderboardPresenter, TeamsLeaderboardResultDTO, TeamsLeaderboardViewModel, TeamLeaderboardItemViewModel } from '@core/racing/application/presenters/ITeamsLeaderboardPresenter';
|
||||
import type { TeamsLeaderboardOutputPort } from '@core/racing/application/ports/output/TeamsLeaderboardOutputPort';
|
||||
import type { GetTeamsLeaderboardOutputDTO } from '../dtos/GetTeamsLeaderboardOutputDTO';
|
||||
|
||||
export class TeamsLeaderboardPresenter implements ITeamsLeaderboardPresenter {
|
||||
private result: TeamsLeaderboardViewModel | null = null;
|
||||
export class TeamsLeaderboardPresenter {
|
||||
private result: GetTeamsLeaderboardOutputDTO | null = null;
|
||||
|
||||
reset() {
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
present(dto: TeamsLeaderboardResultDTO) {
|
||||
async present(outputPort: TeamsLeaderboardOutputPort): Promise<void> {
|
||||
this.result = {
|
||||
teams: dto.teams as TeamLeaderboardItemViewModel[],
|
||||
recruitingCount: dto.recruitingCount,
|
||||
teams: outputPort.teams.map(team => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
memberCount: team.memberCount,
|
||||
rating: team.rating,
|
||||
totalWins: team.totalWins,
|
||||
totalRaces: team.totalRaces,
|
||||
performanceLevel: team.performanceLevel,
|
||||
isRecruiting: team.isRecruiting,
|
||||
createdAt: team.createdAt.toISOString(),
|
||||
description: team.description,
|
||||
specialization: team.specialization,
|
||||
region: team.region,
|
||||
languages: team.languages,
|
||||
})),
|
||||
recruitingCount: outputPort.recruitingCount,
|
||||
groupsBySkillLevel: {
|
||||
beginner: [],
|
||||
intermediate: [],
|
||||
advanced: [],
|
||||
pro: [],
|
||||
beginner: outputPort.groupsBySkillLevel.beginner.map(team => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
memberCount: team.memberCount,
|
||||
rating: team.rating,
|
||||
totalWins: team.totalWins,
|
||||
totalRaces: team.totalRaces,
|
||||
performanceLevel: team.performanceLevel,
|
||||
isRecruiting: team.isRecruiting,
|
||||
createdAt: team.createdAt.toISOString(),
|
||||
description: team.description,
|
||||
specialization: team.specialization,
|
||||
region: team.region,
|
||||
languages: team.languages,
|
||||
})),
|
||||
intermediate: outputPort.groupsBySkillLevel.intermediate.map(team => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
memberCount: team.memberCount,
|
||||
rating: team.rating,
|
||||
totalWins: team.totalWins,
|
||||
totalRaces: team.totalRaces,
|
||||
performanceLevel: team.performanceLevel,
|
||||
isRecruiting: team.isRecruiting,
|
||||
createdAt: team.createdAt.toISOString(),
|
||||
description: team.description,
|
||||
specialization: team.specialization,
|
||||
region: team.region,
|
||||
languages: team.languages,
|
||||
})),
|
||||
advanced: outputPort.groupsBySkillLevel.advanced.map(team => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
memberCount: team.memberCount,
|
||||
rating: team.rating,
|
||||
totalWins: team.totalWins,
|
||||
totalRaces: team.totalRaces,
|
||||
performanceLevel: team.performanceLevel,
|
||||
isRecruiting: team.isRecruiting,
|
||||
createdAt: team.createdAt.toISOString(),
|
||||
description: team.description,
|
||||
specialization: team.specialization,
|
||||
region: team.region,
|
||||
languages: team.languages,
|
||||
})),
|
||||
pro: outputPort.groupsBySkillLevel.pro.map(team => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
memberCount: team.memberCount,
|
||||
rating: team.rating,
|
||||
totalWins: team.totalWins,
|
||||
totalRaces: team.totalRaces,
|
||||
performanceLevel: team.performanceLevel,
|
||||
isRecruiting: team.isRecruiting,
|
||||
createdAt: team.createdAt.toISOString(),
|
||||
description: team.description,
|
||||
specialization: team.specialization,
|
||||
region: team.region,
|
||||
languages: team.languages,
|
||||
})),
|
||||
},
|
||||
topTeams: (dto.teams as TeamLeaderboardItemViewModel[]).slice(0, 10),
|
||||
topTeams: outputPort.topTeams.map(team => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
memberCount: team.memberCount,
|
||||
rating: team.rating,
|
||||
totalWins: team.totalWins,
|
||||
totalRaces: team.totalRaces,
|
||||
performanceLevel: team.performanceLevel,
|
||||
isRecruiting: team.isRecruiting,
|
||||
createdAt: team.createdAt.toISOString(),
|
||||
description: team.description,
|
||||
specialization: team.specialization,
|
||||
region: team.region,
|
||||
languages: team.languages,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
getViewModel(): TeamsLeaderboardViewModel | null {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
get viewModel(): TeamsLeaderboardViewModel {
|
||||
if (!this.result) throw new Error('Presenter not presented');
|
||||
getViewModel(): GetTeamsLeaderboardOutputDTO | null {
|
||||
return this.result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user