website refactor
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { GetAllTeamsOutputDTO } from '@/lib/types/generated/GetAllTeamsOutputDTO';
|
||||
import type { GetTeamsLeaderboardOutputDTO } from '@/lib/types/generated/GetTeamsLeaderboardOutputDTO';
|
||||
import type { GetTeamDetailsOutputDTO } from '@/lib/types/generated/GetTeamDetailsOutputDTO';
|
||||
import type { GetTeamMembersOutputDTO } from '@/lib/types/generated/GetTeamMembersOutputDTO';
|
||||
import type { GetTeamJoinRequestsOutputDTO } from '@/lib/types/generated/GetTeamJoinRequestsOutputDTO';
|
||||
@@ -21,6 +22,11 @@ export class TeamsApiClient extends BaseApiClient {
|
||||
return this.get<GetAllTeamsOutputDTO>('/teams/all');
|
||||
}
|
||||
|
||||
/** Get teams leaderboard */
|
||||
getLeaderboard(): Promise<GetTeamsLeaderboardOutputDTO> {
|
||||
return this.get<GetTeamsLeaderboardOutputDTO>('/teams/leaderboard');
|
||||
}
|
||||
|
||||
/** Get team details */
|
||||
getDetails(teamId: string): Promise<GetTeamDetailsOutputDTO | null> {
|
||||
return this.get<GetTeamDetailsOutputDTO | null>(`/teams/${teamId}`);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { DriverLeaderboardItemDTO } from '@/lib/types/generated/DriverLeaderboardItemDTO';
|
||||
import type { TeamListItemDTO } from '@/lib/types/generated/TeamListItemDTO';
|
||||
import type { GetTeamsLeaderboardOutputDTO } from '@/lib/types/generated/GetTeamsLeaderboardOutputDTO';
|
||||
import type { LeaderboardsViewData } from '@/lib/view-data/LeaderboardsViewData';
|
||||
|
||||
export class LeaderboardsViewDataBuilder {
|
||||
static build(
|
||||
apiDto: { drivers: { drivers: DriverLeaderboardItemDTO[] }; teams: { teams: TeamListItemDTO[] } }
|
||||
apiDto: { drivers: { drivers: DriverLeaderboardItemDTO[] }; teams: GetTeamsLeaderboardOutputDTO }
|
||||
): LeaderboardsViewData {
|
||||
return {
|
||||
drivers: apiDto.drivers.drivers.slice(0, 10).map(driver => ({
|
||||
@@ -18,19 +18,19 @@ export class LeaderboardsViewDataBuilder {
|
||||
avatarUrl: driver.avatarUrl || '',
|
||||
position: driver.rank,
|
||||
})),
|
||||
teams: apiDto.teams.teams.slice(0, 10).map((team, index) => ({
|
||||
teams: apiDto.teams.topTeams.map((team, index) => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
tag: team.tag,
|
||||
memberCount: team.memberCount,
|
||||
category: team.category,
|
||||
category: undefined,
|
||||
totalWins: team.totalWins || 0,
|
||||
logoUrl: team.logoUrl || '',
|
||||
position: index + 1,
|
||||
isRecruiting: team.isRecruiting,
|
||||
performanceLevel: team.performanceLevel || 'N/A',
|
||||
rating: team.rating,
|
||||
rating: team.rating || 0,
|
||||
})),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { GetTeamsLeaderboardOutputDTO } from '@/lib/types/generated/GetTeamsLeaderboardOutputDTO';
|
||||
import type { TeamRankingsViewData } from '@/lib/view-data/TeamRankingsViewData';
|
||||
|
||||
export class TeamRankingsViewDataBuilder {
|
||||
static build(apiDto: GetTeamsLeaderboardOutputDTO): TeamRankingsViewData {
|
||||
const allTeams = apiDto.teams.map((team, index) => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
tag: team.tag,
|
||||
memberCount: team.memberCount,
|
||||
category: undefined,
|
||||
totalWins: team.totalWins || 0,
|
||||
logoUrl: team.logoUrl || '',
|
||||
position: index + 1,
|
||||
isRecruiting: team.isRecruiting,
|
||||
performanceLevel: team.performanceLevel || 'N/A',
|
||||
rating: team.rating || 0,
|
||||
totalRaces: team.totalRaces || 0,
|
||||
}));
|
||||
|
||||
return {
|
||||
teams: allTeams,
|
||||
podium: allTeams.slice(0, 3),
|
||||
recruitingCount: apiDto.recruitingCount,
|
||||
};
|
||||
}
|
||||
}
|
||||
31
apps/website/lib/page-queries/TeamRankingsPageQuery.ts
Normal file
31
apps/website/lib/page-queries/TeamRankingsPageQuery.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { PageQuery } from '@/lib/contracts/page-queries/PageQuery';
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
import { TeamRankingsViewDataBuilder } from '@/lib/builders/view-data/TeamRankingsViewDataBuilder';
|
||||
import type { TeamRankingsViewData } from '@/lib/view-data/TeamRankingsViewData';
|
||||
import { TeamRankingsService } from '@/lib/services/leaderboards/TeamRankingsService';
|
||||
import { mapToPresentationError, type PresentationError } from '@/lib/contracts/page-queries/PresentationError';
|
||||
|
||||
/**
|
||||
* Team Rankings page query
|
||||
* Returns Result<TeamRankingsViewData, PresentationError>
|
||||
*/
|
||||
export class TeamRankingsPageQuery implements PageQuery<TeamRankingsViewData, void, PresentationError> {
|
||||
async execute(): Promise<Result<TeamRankingsViewData, PresentationError>> {
|
||||
const service = new TeamRankingsService();
|
||||
|
||||
const serviceResult = await service.getTeamRankings();
|
||||
|
||||
if (serviceResult.isErr()) {
|
||||
return Result.err(mapToPresentationError(serviceResult.getError()));
|
||||
}
|
||||
|
||||
const apiDto = serviceResult.unwrap();
|
||||
const viewData = TeamRankingsViewDataBuilder.build(apiDto);
|
||||
return Result.ok(viewData);
|
||||
}
|
||||
|
||||
static async execute(): Promise<Result<TeamRankingsViewData, PresentationError>> {
|
||||
const query = new TeamRankingsPageQuery();
|
||||
return query.execute();
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,7 @@ export interface RouteGroup {
|
||||
leaderboards: {
|
||||
root: string;
|
||||
drivers: string;
|
||||
teams: string;
|
||||
};
|
||||
error: {
|
||||
notFound: string;
|
||||
@@ -119,7 +120,7 @@ export interface RouteGroup {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export const routes: RouteGroup & { leaderboards: { root: string; drivers: string } } = {
|
||||
export const routes: RouteGroup & { leaderboards: { root: string; drivers: string; teams: string } } = {
|
||||
auth: {
|
||||
login: '/auth/login',
|
||||
signup: '/auth/signup',
|
||||
@@ -192,6 +193,7 @@ export const routes: RouteGroup & { leaderboards: { root: string; drivers: strin
|
||||
leaderboards: {
|
||||
root: '/leaderboards',
|
||||
drivers: '/leaderboards/drivers',
|
||||
teams: '/leaderboards/teams',
|
||||
},
|
||||
error: {
|
||||
notFound: '/404',
|
||||
|
||||
@@ -20,7 +20,7 @@ export class LeaderboardsService implements Service {
|
||||
|
||||
const [driverResult, teamResult] = await Promise.all([
|
||||
driversApiClient.getLeaderboard(),
|
||||
teamsApiClient.getAll()
|
||||
teamsApiClient.getLeaderboard()
|
||||
]);
|
||||
|
||||
if (!driverResult) {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { TeamsApiClient } from '@/lib/api/teams/TeamsApiClient';
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
import { Service, DomainError } from '@/lib/contracts/services/Service';
|
||||
import { ConsoleErrorReporter } from '@/lib/infrastructure/logging/ConsoleErrorReporter';
|
||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||
import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import type { GetTeamsLeaderboardOutputDTO } from '@/lib/types/generated/GetTeamsLeaderboardOutputDTO';
|
||||
|
||||
export class TeamRankingsService implements Service {
|
||||
async getTeamRankings(): Promise<Result<GetTeamsLeaderboardOutputDTO, DomainError>> {
|
||||
try {
|
||||
const baseUrl = getWebsiteApiBaseUrl();
|
||||
const errorReporter = new ConsoleErrorReporter();
|
||||
const logger = new ConsoleLogger();
|
||||
|
||||
const teamsApiClient = new TeamsApiClient(baseUrl, errorReporter, logger);
|
||||
|
||||
const teamResult = await teamsApiClient.getLeaderboard();
|
||||
|
||||
if (!teamResult) {
|
||||
return Result.err({ type: 'notFound', message: 'No team leaderboard data available' });
|
||||
}
|
||||
|
||||
return Result.ok(teamResult);
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) {
|
||||
switch (error.type) {
|
||||
case 'NOT_FOUND':
|
||||
return Result.err({ type: 'notFound', message: error.message });
|
||||
case 'AUTH_ERROR':
|
||||
return Result.err({ type: 'unauthorized', message: error.message });
|
||||
case 'SERVER_ERROR':
|
||||
return Result.err({ type: 'serverError', message: error.message });
|
||||
case 'NETWORK_ERROR':
|
||||
case 'TIMEOUT_ERROR':
|
||||
return Result.err({ type: 'networkError', message: error.message });
|
||||
default:
|
||||
return Result.err({ type: 'unknown', message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
return Result.err({ type: 'unknown', message: error.message });
|
||||
}
|
||||
|
||||
return Result.err({ type: 'unknown', message: 'Team rankings fetch failed' });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { DriverLeaderboardItemDTO } from '@/lib/types/generated/DriverLeaderboardItemDTO';
|
||||
import type { TeamListItemDTO } from '@/lib/types/generated/TeamListItemDTO';
|
||||
import type { GetTeamsLeaderboardOutputDTO } from '@/lib/types/generated/GetTeamsLeaderboardOutputDTO';
|
||||
|
||||
export interface LeaderboardsData {
|
||||
drivers: { drivers: DriverLeaderboardItemDTO[] };
|
||||
teams: { teams: TeamListItemDTO[] };
|
||||
}
|
||||
teams: GetTeamsLeaderboardOutputDTO;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
export interface TeamLeaderboardItemDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
logoUrl?: string;
|
||||
memberCount: number;
|
||||
rating?: number;
|
||||
totalWins: number;
|
||||
|
||||
@@ -5,9 +5,10 @@ export interface LeaderboardTeamItem {
|
||||
memberCount: number;
|
||||
category?: string;
|
||||
totalWins: number;
|
||||
totalRaces?: number;
|
||||
logoUrl: string;
|
||||
position: number;
|
||||
isRecruiting: boolean;
|
||||
performanceLevel: string;
|
||||
rating?: number;
|
||||
}
|
||||
}
|
||||
|
||||
7
apps/website/lib/view-data/TeamRankingsViewData.ts
Normal file
7
apps/website/lib/view-data/TeamRankingsViewData.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { LeaderboardTeamItem } from './LeaderboardTeamItem';
|
||||
|
||||
export interface TeamRankingsViewData {
|
||||
teams: LeaderboardTeamItem[];
|
||||
podium: LeaderboardTeamItem[];
|
||||
recruitingCount: number;
|
||||
}
|
||||
Reference in New Issue
Block a user