rename to core
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import type { League } from '../../domain/entities/League';
|
||||
import type { Season } from '../../domain/entities/Season';
|
||||
import type { LeagueScoringConfig } from '../../domain/entities/LeagueScoringConfig';
|
||||
import type { Game } from '../../domain/entities/Game';
|
||||
import type { LeagueScoringPresetDTO } from '../ports/LeagueScoringPresetProvider';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface LeagueSummaryViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
createdAt: string;
|
||||
maxDrivers: number;
|
||||
usedDriverSlots: number;
|
||||
maxTeams?: number;
|
||||
usedTeamSlots?: number;
|
||||
structureSummary: string;
|
||||
scoringPatternSummary?: string;
|
||||
timingSummary: string;
|
||||
scoring?: {
|
||||
gameId: string;
|
||||
gameName: string;
|
||||
primaryChampionshipType: 'driver' | 'team' | 'nations' | 'trophy';
|
||||
scoringPresetId: string;
|
||||
scoringPresetName: string;
|
||||
dropPolicySummary: string;
|
||||
scoringPatternSummary: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AllLeaguesWithCapacityAndScoringViewModel {
|
||||
leagues: LeagueSummaryViewModel[];
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
export interface LeagueEnrichedData {
|
||||
league: League;
|
||||
usedDriverSlots: number;
|
||||
season?: Season;
|
||||
scoringConfig?: LeagueScoringConfig;
|
||||
game?: Game;
|
||||
preset?: LeagueScoringPresetDTO;
|
||||
}
|
||||
|
||||
export interface IAllLeaguesWithCapacityAndScoringPresenter
|
||||
extends Presenter<LeagueEnrichedData[], AllLeaguesWithCapacityAndScoringViewModel> {}
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { League } from '../../domain/entities/League';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface LeagueWithCapacityViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
settings: {
|
||||
maxDrivers: number;
|
||||
sessionDuration?: number;
|
||||
visibility?: string;
|
||||
};
|
||||
createdAt: string;
|
||||
socialLinks?: {
|
||||
discordUrl?: string;
|
||||
youtubeUrl?: string;
|
||||
websiteUrl?: string;
|
||||
};
|
||||
usedSlots: number;
|
||||
}
|
||||
|
||||
export interface AllLeaguesWithCapacityViewModel {
|
||||
leagues: LeagueWithCapacityViewModel[];
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
export interface AllLeaguesWithCapacityResultDTO {
|
||||
leagues: League[];
|
||||
memberCounts: Map<string, number>;
|
||||
}
|
||||
|
||||
export interface IAllLeaguesWithCapacityPresenter
|
||||
extends Presenter<AllLeaguesWithCapacityResultDTO, AllLeaguesWithCapacityViewModel> {}
|
||||
29
core/racing/application/presenters/IAllRacesPagePresenter.ts
Normal file
29
core/racing/application/presenters/IAllRacesPagePresenter.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export type AllRacesStatus = 'scheduled' | 'running' | 'completed' | 'cancelled' | 'all';
|
||||
|
||||
export interface AllRacesListItemViewModel {
|
||||
id: string;
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string;
|
||||
status: 'scheduled' | 'running' | 'completed' | 'cancelled';
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
strengthOfField: number | null;
|
||||
}
|
||||
|
||||
export interface AllRacesFilterOptionsViewModel {
|
||||
statuses: { value: AllRacesStatus; label: string }[];
|
||||
leagues: { id: string; name: string }[];
|
||||
}
|
||||
|
||||
export interface AllRacesPageViewModel {
|
||||
races: AllRacesListItemViewModel[];
|
||||
filters: AllRacesFilterOptionsViewModel;
|
||||
}
|
||||
|
||||
export type AllRacesPageResultDTO = AllRacesPageViewModel;
|
||||
|
||||
export interface IAllRacesPagePresenter
|
||||
extends Presenter<AllRacesPageResultDTO, AllRacesPageViewModel> {}
|
||||
34
core/racing/application/presenters/IAllTeamsPresenter.ts
Normal file
34
core/racing/application/presenters/IAllTeamsPresenter.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface TeamListItemViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
memberCount: number;
|
||||
leagues: string[];
|
||||
specialization?: 'endurance' | 'sprint' | 'mixed';
|
||||
region?: string;
|
||||
languages?: string[];
|
||||
}
|
||||
|
||||
export interface AllTeamsViewModel {
|
||||
teams: TeamListItemViewModel[];
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
export interface AllTeamsResultDTO {
|
||||
teams: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
createdAt: Date;
|
||||
memberCount: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface IAllTeamsPresenter
|
||||
extends Presenter<AllTeamsResultDTO, AllTeamsViewModel> {}
|
||||
@@ -0,0 +1,90 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface DashboardDriverSummaryViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
country: string;
|
||||
avatarUrl: string;
|
||||
rating: number | null;
|
||||
globalRank: number | null;
|
||||
totalRaces: number;
|
||||
wins: number;
|
||||
podiums: number;
|
||||
consistency: number | null;
|
||||
}
|
||||
|
||||
export interface DashboardRaceSummaryViewModel {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string;
|
||||
status: 'scheduled' | 'running' | 'completed' | 'cancelled';
|
||||
isMyLeague: boolean;
|
||||
}
|
||||
|
||||
export interface DashboardRecentResultViewModel {
|
||||
raceId: string;
|
||||
raceName: string;
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
finishedAt: string;
|
||||
position: number;
|
||||
incidents: number;
|
||||
}
|
||||
|
||||
export interface DashboardLeagueStandingSummaryViewModel {
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
position: number;
|
||||
totalDrivers: number;
|
||||
points: number;
|
||||
}
|
||||
|
||||
export interface DashboardFeedItemSummaryViewModel {
|
||||
id: string;
|
||||
type: string;
|
||||
headline: string;
|
||||
body?: string;
|
||||
timestamp: string;
|
||||
ctaLabel?: string;
|
||||
ctaHref?: string;
|
||||
}
|
||||
|
||||
export interface DashboardFeedSummaryViewModel {
|
||||
notificationCount: number;
|
||||
items: DashboardFeedItemSummaryViewModel[];
|
||||
}
|
||||
|
||||
export interface DashboardFriendSummaryViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
country: string;
|
||||
avatarUrl: string;
|
||||
}
|
||||
|
||||
export interface DashboardOverviewViewModel {
|
||||
currentDriver: DashboardDriverSummaryViewModel | null;
|
||||
myUpcomingRaces: DashboardRaceSummaryViewModel[];
|
||||
otherUpcomingRaces: DashboardRaceSummaryViewModel[];
|
||||
/**
|
||||
* All upcoming races for the driver, already sorted by scheduledAt ascending.
|
||||
*/
|
||||
upcomingRaces: DashboardRaceSummaryViewModel[];
|
||||
/**
|
||||
* Count of distinct leagues that are currently "active" for the driver,
|
||||
* based on upcoming races and league standings.
|
||||
*/
|
||||
activeLeaguesCount: number;
|
||||
nextRace: DashboardRaceSummaryViewModel | null;
|
||||
recentResults: DashboardRecentResultViewModel[];
|
||||
leagueStandingsSummaries: DashboardLeagueStandingSummaryViewModel[];
|
||||
feedSummary: DashboardFeedSummaryViewModel;
|
||||
friends: DashboardFriendSummaryViewModel[];
|
||||
}
|
||||
|
||||
export type DashboardOverviewResultDTO = DashboardOverviewViewModel;
|
||||
|
||||
export interface IDashboardOverviewPresenter
|
||||
extends Presenter<DashboardOverviewResultDTO, DashboardOverviewViewModel> {}
|
||||
@@ -0,0 +1,14 @@
|
||||
export interface DriverRegistrationStatusViewModel {
|
||||
isRegistered: boolean;
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
}
|
||||
|
||||
export interface IDriverRegistrationStatusPresenter {
|
||||
present(
|
||||
isRegistered: boolean,
|
||||
raceId: string,
|
||||
driverId: string
|
||||
): DriverRegistrationStatusViewModel;
|
||||
getViewModel(): DriverRegistrationStatusViewModel;
|
||||
}
|
||||
33
core/racing/application/presenters/IDriverTeamPresenter.ts
Normal file
33
core/racing/application/presenters/IDriverTeamPresenter.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { Team } from '../../domain/entities/Team';
|
||||
import type { TeamMembership } from '../../domain/types/TeamMembership';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface DriverTeamViewModel {
|
||||
team: {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
specialization?: 'endurance' | 'sprint' | 'mixed';
|
||||
region?: string;
|
||||
languages?: string[];
|
||||
};
|
||||
membership: {
|
||||
role: 'owner' | 'manager' | 'member';
|
||||
joinedAt: string;
|
||||
isActive: boolean;
|
||||
};
|
||||
isOwner: boolean;
|
||||
canManage: boolean;
|
||||
}
|
||||
|
||||
export interface DriverTeamResultDTO {
|
||||
team: Team;
|
||||
membership: TeamMembership;
|
||||
driverId: string;
|
||||
}
|
||||
|
||||
export interface IDriverTeamPresenter
|
||||
extends Presenter<DriverTeamResultDTO, DriverTeamViewModel> {}
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { Driver } from '../../domain/entities/Driver';
|
||||
import type { SkillLevel } from '../../domain/services/SkillLevelService';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export type { SkillLevel };
|
||||
|
||||
export interface DriverLeaderboardItemViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
rating: number;
|
||||
skillLevel: SkillLevel;
|
||||
nationality: string;
|
||||
racesCompleted: number;
|
||||
wins: number;
|
||||
podiums: number;
|
||||
isActive: boolean;
|
||||
rank: number;
|
||||
avatarUrl: string;
|
||||
}
|
||||
|
||||
export interface DriversLeaderboardViewModel {
|
||||
drivers: DriverLeaderboardItemViewModel[];
|
||||
totalRaces: number;
|
||||
totalWins: number;
|
||||
activeCount: number;
|
||||
}
|
||||
|
||||
export interface DriversLeaderboardResultDTO {
|
||||
drivers: Driver[];
|
||||
rankings: Array<{ driverId: string; rating: number; overallRank: number | null }>;
|
||||
stats: Record<
|
||||
string,
|
||||
{
|
||||
rating: number;
|
||||
wins: number;
|
||||
podiums: number;
|
||||
totalRaces: number;
|
||||
overallRank: number | null;
|
||||
}
|
||||
>;
|
||||
avatarUrls: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface IDriversLeaderboardPresenter
|
||||
extends Presenter<DriversLeaderboardResultDTO, DriversLeaderboardViewModel> {}
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { GetEntitySponsorshipPricingResultDTO } from '../use-cases/GetEntitySponsorshipPricingUseCase';
|
||||
|
||||
export interface IEntitySponsorshipPricingPresenter {
|
||||
present(data: GetEntitySponsorshipPricingResultDTO | null): void;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export interface ImportRaceResultsSummaryViewModel {
|
||||
importedCount: number;
|
||||
standingsRecalculated: boolean;
|
||||
}
|
||||
|
||||
export interface IImportRaceResultsPresenter {
|
||||
present(viewModel: ImportRaceResultsSummaryViewModel): ImportRaceResultsSummaryViewModel;
|
||||
getViewModel(): ImportRaceResultsSummaryViewModel | null;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface LeagueDriverSeasonStatsItemViewModel {
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
position: number;
|
||||
driverName: string;
|
||||
teamId?: string;
|
||||
teamName?: string;
|
||||
totalPoints: number;
|
||||
basePoints: number;
|
||||
penaltyPoints: number;
|
||||
bonusPoints: number;
|
||||
pointsPerRace: number;
|
||||
racesStarted: number;
|
||||
racesFinished: number;
|
||||
dnfs: number;
|
||||
noShows: number;
|
||||
avgFinish: number | null;
|
||||
rating: number | null;
|
||||
ratingChange: number | null;
|
||||
}
|
||||
|
||||
export interface LeagueDriverSeasonStatsViewModel {
|
||||
leagueId: string;
|
||||
stats: LeagueDriverSeasonStatsItemViewModel[];
|
||||
}
|
||||
|
||||
export interface LeagueDriverSeasonStatsResultDTO {
|
||||
leagueId: string;
|
||||
standings: Array<{
|
||||
driverId: string;
|
||||
position: number;
|
||||
points: number;
|
||||
racesCompleted: number;
|
||||
}>;
|
||||
penalties: Map<string, { baseDelta: number; bonusDelta: number }>;
|
||||
driverResults: Map<string, Array<{ position: number }>>;
|
||||
driverRatings: Map<string, { rating: number | null; ratingChange: number | null }>;
|
||||
}
|
||||
|
||||
export interface ILeagueDriverSeasonStatsPresenter
|
||||
extends Presenter<LeagueDriverSeasonStatsResultDTO, LeagueDriverSeasonStatsViewModel> {}
|
||||
@@ -0,0 +1,65 @@
|
||||
import type { League } from '../../domain/entities/League';
|
||||
import type { Season } from '../../domain/entities/Season';
|
||||
import type { LeagueScoringConfig } from '../../domain/entities/LeagueScoringConfig';
|
||||
import type { Game } from '../../domain/entities/Game';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface LeagueConfigFormViewModel {
|
||||
leagueId: string;
|
||||
basics: {
|
||||
name: string;
|
||||
description: string;
|
||||
visibility: string;
|
||||
gameId: string;
|
||||
};
|
||||
structure: {
|
||||
mode: string;
|
||||
maxDrivers: number;
|
||||
maxTeams?: number;
|
||||
driversPerTeam?: number;
|
||||
multiClassEnabled: boolean;
|
||||
};
|
||||
championships: {
|
||||
enableDriverChampionship: boolean;
|
||||
enableTeamChampionship: boolean;
|
||||
enableNationsChampionship: boolean;
|
||||
enableTrophyChampionship: boolean;
|
||||
};
|
||||
scoring: {
|
||||
patternId?: string;
|
||||
customScoringEnabled: boolean;
|
||||
};
|
||||
dropPolicy: {
|
||||
strategy: string;
|
||||
n?: number;
|
||||
};
|
||||
timings: {
|
||||
practiceMinutes: number;
|
||||
qualifyingMinutes: number;
|
||||
sprintRaceMinutes?: number;
|
||||
mainRaceMinutes: number;
|
||||
sessionCount: number;
|
||||
roundsPlanned: number;
|
||||
};
|
||||
stewarding: {
|
||||
decisionMode: string;
|
||||
requireDefense: boolean;
|
||||
defenseTimeLimit: number;
|
||||
voteTimeLimit: number;
|
||||
protestDeadlineHours: number;
|
||||
stewardingClosesHours: number;
|
||||
notifyAccusedOnProtest: boolean;
|
||||
notifyOnVoteRequired: boolean;
|
||||
requiredVotes?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LeagueFullConfigData {
|
||||
league: League;
|
||||
activeSeason?: Season;
|
||||
scoringConfig?: LeagueScoringConfig;
|
||||
game?: Game;
|
||||
}
|
||||
|
||||
export interface ILeagueFullConfigPresenter
|
||||
extends Presenter<LeagueFullConfigData, LeagueConfigFormViewModel> {}
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { LeagueSchedulePreviewDTO } from '../dto/LeagueScheduleDTO';
|
||||
|
||||
export interface ILeagueSchedulePreviewPresenter {
|
||||
present(data: LeagueSchedulePreviewDTO): void;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { ChampionshipConfig } from '../../domain/types/ChampionshipConfig';
|
||||
import type { LeagueScoringPresetDTO } from '../ports/LeagueScoringPresetProvider';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface LeagueScoringChampionshipViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
sessionTypes: string[];
|
||||
pointsPreview: Array<{ sessionType: string; position: number; points: number }>;
|
||||
bonusSummary: string[];
|
||||
dropPolicyDescription: string;
|
||||
}
|
||||
|
||||
export interface LeagueScoringConfigViewModel {
|
||||
leagueId: string;
|
||||
seasonId: string;
|
||||
gameId: string;
|
||||
gameName: string;
|
||||
scoringPresetId?: string;
|
||||
scoringPresetName?: string;
|
||||
dropPolicySummary: string;
|
||||
championships: LeagueScoringChampionshipViewModel[];
|
||||
}
|
||||
|
||||
export interface LeagueScoringConfigData {
|
||||
leagueId: string;
|
||||
seasonId: string;
|
||||
gameId: string;
|
||||
gameName: string;
|
||||
scoringPresetId?: string;
|
||||
preset?: LeagueScoringPresetDTO;
|
||||
championships: ChampionshipConfig[];
|
||||
}
|
||||
|
||||
export interface ILeagueScoringConfigPresenter
|
||||
extends Presenter<LeagueScoringConfigData, LeagueScoringConfigViewModel> {}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { LeagueScoringPresetDTO } from '../ports/LeagueScoringPresetProvider';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface LeagueScoringPresetsViewModel {
|
||||
presets: LeagueScoringPresetDTO[];
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
export interface LeagueScoringPresetsResultDTO {
|
||||
presets: LeagueScoringPresetDTO[];
|
||||
}
|
||||
|
||||
export interface ILeagueScoringPresetsPresenter
|
||||
extends Presenter<LeagueScoringPresetsResultDTO, LeagueScoringPresetsViewModel> {}
|
||||
@@ -0,0 +1,26 @@
|
||||
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;
|
||||
points: number;
|
||||
wins: number;
|
||||
podiums: number;
|
||||
racesCompleted: number;
|
||||
}
|
||||
|
||||
export interface LeagueStandingsViewModel {
|
||||
leagueId: string;
|
||||
standings: StandingItemViewModel[];
|
||||
}
|
||||
|
||||
export interface LeagueStandingsResultDTO {
|
||||
standings: Standing[];
|
||||
}
|
||||
|
||||
export interface ILeagueStandingsPresenter
|
||||
extends Presenter<LeagueStandingsResultDTO, LeagueStandingsViewModel> {}
|
||||
20
core/racing/application/presenters/ILeagueStatsPresenter.ts
Normal file
20
core/racing/application/presenters/ILeagueStatsPresenter.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export interface LeagueStatsViewModel {
|
||||
leagueId: string;
|
||||
totalRaces: number;
|
||||
completedRaces: number;
|
||||
scheduledRaces: number;
|
||||
averageSOF: number | null;
|
||||
highestSOF: number | null;
|
||||
lowestSOF: number | null;
|
||||
}
|
||||
|
||||
export interface ILeagueStatsPresenter {
|
||||
present(
|
||||
leagueId: string,
|
||||
totalRaces: number,
|
||||
completedRaces: number,
|
||||
scheduledRaces: number,
|
||||
sofValues: number[]
|
||||
): LeagueStatsViewModel;
|
||||
getViewModel(): LeagueStatsViewModel;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
import type { GetPendingSponsorshipRequestsResultDTO } from '../use-cases/GetPendingSponsorshipRequestsUseCase';
|
||||
|
||||
export type PendingSponsorshipRequestsViewModel = GetPendingSponsorshipRequestsResultDTO;
|
||||
|
||||
export interface IPendingSponsorshipRequestsPresenter
|
||||
extends Presenter<GetPendingSponsorshipRequestsResultDTO, PendingSponsorshipRequestsViewModel> {}
|
||||
105
core/racing/application/presenters/IProfileOverviewPresenter.ts
Normal file
105
core/racing/application/presenters/IProfileOverviewPresenter.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
export interface ProfileOverviewDriverSummaryViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
country: string;
|
||||
avatarUrl: string;
|
||||
iracingId: string | null;
|
||||
joinedAt: string;
|
||||
rating: number | null;
|
||||
globalRank: number | null;
|
||||
consistency: number | null;
|
||||
bio: string | null;
|
||||
totalDrivers: number | null;
|
||||
}
|
||||
|
||||
export interface ProfileOverviewStatsViewModel {
|
||||
totalRaces: number;
|
||||
wins: number;
|
||||
podiums: number;
|
||||
dnfs: number;
|
||||
avgFinish: number | null;
|
||||
bestFinish: number | null;
|
||||
worstFinish: number | null;
|
||||
finishRate: number | null;
|
||||
winRate: number | null;
|
||||
podiumRate: number | null;
|
||||
percentile: number | null;
|
||||
rating: number | null;
|
||||
consistency: number | null;
|
||||
overallRank: number | null;
|
||||
}
|
||||
|
||||
export interface ProfileOverviewFinishDistributionViewModel {
|
||||
totalRaces: number;
|
||||
wins: number;
|
||||
podiums: number;
|
||||
topTen: number;
|
||||
dnfs: number;
|
||||
other: number;
|
||||
}
|
||||
|
||||
export interface ProfileOverviewTeamMembershipViewModel {
|
||||
teamId: string;
|
||||
teamName: string;
|
||||
teamTag: string | null;
|
||||
role: string;
|
||||
joinedAt: string;
|
||||
isCurrent: boolean;
|
||||
}
|
||||
|
||||
export interface ProfileOverviewSocialFriendSummaryViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
country: string;
|
||||
avatarUrl: string;
|
||||
}
|
||||
|
||||
export interface ProfileOverviewSocialSummaryViewModel {
|
||||
friendsCount: number;
|
||||
friends: ProfileOverviewSocialFriendSummaryViewModel[];
|
||||
}
|
||||
|
||||
export type ProfileOverviewSocialPlatform = 'twitter' | 'youtube' | 'twitch' | 'discord';
|
||||
|
||||
export type ProfileOverviewAchievementRarity = 'common' | 'rare' | 'epic' | 'legendary';
|
||||
|
||||
export interface ProfileOverviewAchievementViewModel {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
icon: 'trophy' | 'medal' | 'star' | 'crown' | 'target' | 'zap';
|
||||
rarity: ProfileOverviewAchievementRarity;
|
||||
earnedAt: string;
|
||||
}
|
||||
|
||||
export interface ProfileOverviewSocialHandleViewModel {
|
||||
platform: ProfileOverviewSocialPlatform;
|
||||
handle: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface ProfileOverviewExtendedProfileViewModel {
|
||||
socialHandles: ProfileOverviewSocialHandleViewModel[];
|
||||
achievements: ProfileOverviewAchievementViewModel[];
|
||||
racingStyle: string;
|
||||
favoriteTrack: string;
|
||||
favoriteCar: string;
|
||||
timezone: string;
|
||||
availableHours: string;
|
||||
lookingForTeam: boolean;
|
||||
openToRequests: boolean;
|
||||
}
|
||||
|
||||
export interface ProfileOverviewViewModel {
|
||||
currentDriver: ProfileOverviewDriverSummaryViewModel | null;
|
||||
stats: ProfileOverviewStatsViewModel | null;
|
||||
finishDistribution: ProfileOverviewFinishDistributionViewModel | null;
|
||||
teamMemberships: ProfileOverviewTeamMembershipViewModel[];
|
||||
socialSummary: ProfileOverviewSocialSummaryViewModel;
|
||||
extendedProfile: ProfileOverviewExtendedProfileViewModel | null;
|
||||
}
|
||||
|
||||
export interface IProfileOverviewPresenter {
|
||||
present(viewModel: ProfileOverviewViewModel): void;
|
||||
getViewModel(): ProfileOverviewViewModel | null;
|
||||
}
|
||||
60
core/racing/application/presenters/IRaceDetailPresenter.ts
Normal file
60
core/racing/application/presenters/IRaceDetailPresenter.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { SessionType, RaceStatus } from '../../domain/entities/Race';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface RaceDetailEntryViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
country: string;
|
||||
avatarUrl: string;
|
||||
rating: number | null;
|
||||
isCurrentUser: boolean;
|
||||
}
|
||||
|
||||
export interface RaceDetailUserResultViewModel {
|
||||
position: number;
|
||||
startPosition: number;
|
||||
incidents: number;
|
||||
fastestLap: number;
|
||||
positionChange: number;
|
||||
isPodium: boolean;
|
||||
isClean: boolean;
|
||||
ratingChange: number | null;
|
||||
}
|
||||
|
||||
export interface RaceDetailRaceViewModel {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string;
|
||||
sessionType: SessionType;
|
||||
status: RaceStatus;
|
||||
strengthOfField: number | null;
|
||||
registeredCount?: number;
|
||||
maxParticipants?: number;
|
||||
}
|
||||
|
||||
export interface RaceDetailLeagueViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
settings: {
|
||||
maxDrivers?: number;
|
||||
qualifyingFormat?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RaceDetailViewModel {
|
||||
race: RaceDetailRaceViewModel | null;
|
||||
league: RaceDetailLeagueViewModel | null;
|
||||
entryList: RaceDetailEntryViewModel[];
|
||||
registration: {
|
||||
isUserRegistered: boolean;
|
||||
canRegister: boolean;
|
||||
};
|
||||
userResult: RaceDetailUserResultViewModel | null;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface IRaceDetailPresenter
|
||||
extends Presenter<RaceDetailViewModel, RaceDetailViewModel> {}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { Penalty, PenaltyType, PenaltyStatus } from '../../domain/entities/Penalty';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface RacePenaltyViewModel {
|
||||
id: string;
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
driverName: string;
|
||||
type: PenaltyType;
|
||||
value?: number;
|
||||
reason: string;
|
||||
protestId?: string;
|
||||
issuedBy: string;
|
||||
issuedByName: string;
|
||||
status: PenaltyStatus;
|
||||
description: string;
|
||||
issuedAt: string;
|
||||
appliedAt?: string;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface RacePenaltiesViewModel {
|
||||
penalties: RacePenaltyViewModel[];
|
||||
}
|
||||
|
||||
export interface RacePenaltiesResultDTO {
|
||||
penalties: Penalty[];
|
||||
driverMap: Map<string, string>;
|
||||
}
|
||||
|
||||
export interface IRacePenaltiesPresenter
|
||||
extends Presenter<RacePenaltiesResultDTO, RacePenaltiesViewModel> {}
|
||||
32
core/racing/application/presenters/IRaceProtestsPresenter.ts
Normal file
32
core/racing/application/presenters/IRaceProtestsPresenter.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import type { Protest, ProtestStatus, ProtestIncident } from '../../domain/entities/Protest';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation/Presenter';
|
||||
|
||||
export interface RaceProtestViewModel {
|
||||
id: string;
|
||||
raceId: string;
|
||||
protestingDriverId: string;
|
||||
protestingDriverName: string;
|
||||
accusedDriverId: string;
|
||||
accusedDriverName: string;
|
||||
incident: ProtestIncident;
|
||||
comment?: string;
|
||||
proofVideoUrl?: string;
|
||||
status: ProtestStatus;
|
||||
reviewedBy?: string;
|
||||
reviewedByName?: string;
|
||||
decisionNotes?: string;
|
||||
filedAt: string;
|
||||
reviewedAt?: string;
|
||||
}
|
||||
|
||||
export interface RaceProtestsViewModel {
|
||||
protests: RaceProtestViewModel[];
|
||||
}
|
||||
|
||||
export interface RaceProtestsResultDTO {
|
||||
protests: Protest[];
|
||||
driverMap: Map<string, string>;
|
||||
}
|
||||
|
||||
export interface IRaceProtestsPresenter
|
||||
extends Presenter<RaceProtestsResultDTO, RaceProtestsViewModel> {}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface RaceRegistrationsViewModel {
|
||||
registeredDriverIds: string[];
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface RaceRegistrationsResultDTO {
|
||||
registeredDriverIds: string[];
|
||||
}
|
||||
|
||||
export interface IRaceRegistrationsPresenter
|
||||
extends Presenter<RaceRegistrationsResultDTO, RaceRegistrationsViewModel> {}
|
||||
@@ -0,0 +1,39 @@
|
||||
import type { RaceStatus } from '../../domain/entities/Race';
|
||||
import type { Result } from '../../domain/entities/Result';
|
||||
import type { Driver } from '../../domain/entities/Driver';
|
||||
import type { PenaltyType } from '../../domain/entities/Penalty';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface RaceResultsHeaderViewModel {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
track: string;
|
||||
scheduledAt: Date;
|
||||
status: RaceStatus;
|
||||
}
|
||||
|
||||
export interface RaceResultsLeagueViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface RaceResultsPenaltySummaryViewModel {
|
||||
driverId: string;
|
||||
type: PenaltyType;
|
||||
value?: number;
|
||||
}
|
||||
|
||||
export interface RaceResultsDetailViewModel {
|
||||
race: RaceResultsHeaderViewModel | null;
|
||||
league: RaceResultsLeagueViewModel | null;
|
||||
results: Result[];
|
||||
drivers: Driver[];
|
||||
penalties: RaceResultsPenaltySummaryViewModel[];
|
||||
pointsSystem?: Record<number, number>;
|
||||
fastestLapTime?: number;
|
||||
currentDriverId?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface IRaceResultsDetailPresenter
|
||||
extends Presenter<RaceResultsDetailViewModel, RaceResultsDetailViewModel> {}
|
||||
36
core/racing/application/presenters/IRaceWithSOFPresenter.ts
Normal file
36
core/racing/application/presenters/IRaceWithSOFPresenter.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface RaceWithSOFViewModel {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
scheduledAt: string;
|
||||
track: string;
|
||||
trackId: string;
|
||||
car: string;
|
||||
carId: string;
|
||||
sessionType: string;
|
||||
status: string;
|
||||
strengthOfField: number | null;
|
||||
registeredCount: number;
|
||||
maxParticipants: number;
|
||||
participantCount: number;
|
||||
}
|
||||
|
||||
export interface RaceWithSOFResultDTO {
|
||||
raceId: string;
|
||||
leagueId: string;
|
||||
scheduledAt: Date;
|
||||
track: string;
|
||||
trackId: string;
|
||||
car: string;
|
||||
carId: string;
|
||||
sessionType: string;
|
||||
status: string;
|
||||
strengthOfField: number | null;
|
||||
registeredCount: number;
|
||||
maxParticipants: number;
|
||||
participantCount: number;
|
||||
}
|
||||
|
||||
export interface IRaceWithSOFPresenter
|
||||
extends Presenter<RaceWithSOFResultDTO, RaceWithSOFViewModel> {}
|
||||
35
core/racing/application/presenters/IRacesPagePresenter.ts
Normal file
35
core/racing/application/presenters/IRacesPagePresenter.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface RaceListItemViewModel {
|
||||
id: string;
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string;
|
||||
status: 'scheduled' | 'running' | 'completed' | 'cancelled';
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
strengthOfField: number | null;
|
||||
isUpcoming: boolean;
|
||||
isLive: boolean;
|
||||
isPast: boolean;
|
||||
}
|
||||
|
||||
export interface RacesPageViewModel {
|
||||
races: RaceListItemViewModel[];
|
||||
stats: {
|
||||
total: number;
|
||||
scheduled: number;
|
||||
running: number;
|
||||
completed: number;
|
||||
};
|
||||
liveRaces: RaceListItemViewModel[];
|
||||
upcomingThisWeek: RaceListItemViewModel[];
|
||||
recentResults: RaceListItemViewModel[];
|
||||
}
|
||||
|
||||
export interface RacesPageResultDTO {
|
||||
races: any[];
|
||||
}
|
||||
|
||||
export interface IRacesPagePresenter
|
||||
extends Presenter<RacesPageResultDTO, RacesPageViewModel> {}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { SponsorDashboardDTO } from '../use-cases/GetSponsorDashboardUseCase';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export type SponsorDashboardViewModel = SponsorDashboardDTO | null;
|
||||
|
||||
export interface ISponsorDashboardPresenter
|
||||
extends Presenter<SponsorDashboardDTO | null, SponsorDashboardViewModel> {}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { SponsorSponsorshipsDTO } from '../use-cases/GetSponsorSponsorshipsUseCase';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export type SponsorSponsorshipsViewModel = SponsorSponsorshipsDTO | null;
|
||||
|
||||
export interface ISponsorSponsorshipsPresenter
|
||||
extends Presenter<SponsorSponsorshipsDTO | null, SponsorSponsorshipsViewModel> {}
|
||||
30
core/racing/application/presenters/ITeamDetailsPresenter.ts
Normal file
30
core/racing/application/presenters/ITeamDetailsPresenter.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import type { Team } from '../../domain/entities/Team';
|
||||
import type { TeamMembership } from '../../domain/types/TeamMembership';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface TeamDetailsViewModel {
|
||||
team: {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
createdAt: string;
|
||||
};
|
||||
membership: {
|
||||
role: 'owner' | 'manager' | 'member';
|
||||
joinedAt: string;
|
||||
isActive: boolean;
|
||||
} | null;
|
||||
canManage: boolean;
|
||||
}
|
||||
|
||||
export interface TeamDetailsResultDTO {
|
||||
team: Team;
|
||||
membership: TeamMembership | null;
|
||||
driverId: string;
|
||||
}
|
||||
|
||||
export interface ITeamDetailsPresenter
|
||||
extends Presenter<TeamDetailsResultDTO, TeamDetailsViewModel> {}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { TeamJoinRequest } from '../../domain/types/TeamMembership';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface TeamJoinRequestViewModel {
|
||||
requestId: string;
|
||||
driverId: string;
|
||||
driverName: string;
|
||||
teamId: string;
|
||||
status: 'pending' | 'approved' | 'rejected';
|
||||
requestedAt: string;
|
||||
avatarUrl: string;
|
||||
}
|
||||
|
||||
export interface TeamJoinRequestsViewModel {
|
||||
requests: TeamJoinRequestViewModel[];
|
||||
pendingCount: number;
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
export interface TeamJoinRequestsResultDTO {
|
||||
requests: TeamJoinRequest[];
|
||||
driverNames: Record<string, string>;
|
||||
avatarUrls: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface ITeamJoinRequestsPresenter
|
||||
extends Presenter<TeamJoinRequestsResultDTO, TeamJoinRequestsViewModel> {}
|
||||
28
core/racing/application/presenters/ITeamMembersPresenter.ts
Normal file
28
core/racing/application/presenters/ITeamMembersPresenter.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { TeamMembership } from '../../domain/types/TeamMembership';
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export interface TeamMemberViewModel {
|
||||
driverId: string;
|
||||
driverName: string;
|
||||
role: 'owner' | 'manager' | 'member';
|
||||
joinedAt: string;
|
||||
isActive: boolean;
|
||||
avatarUrl: string;
|
||||
}
|
||||
|
||||
export interface TeamMembersViewModel {
|
||||
members: TeamMemberViewModel[];
|
||||
totalCount: number;
|
||||
ownerCount: number;
|
||||
managerCount: number;
|
||||
memberCount: number;
|
||||
}
|
||||
|
||||
export interface TeamMembersResultDTO {
|
||||
memberships: TeamMembership[];
|
||||
driverNames: Record<string, string>;
|
||||
avatarUrls: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface ITeamMembersPresenter
|
||||
extends Presenter<TeamMembersResultDTO, TeamMembersViewModel> {}
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { Presenter } from '@gridpilot/shared/presentation';
|
||||
|
||||
export type SkillLevel = 'beginner' | 'intermediate' | 'advanced' | 'pro';
|
||||
|
||||
export interface TeamLeaderboardItemViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
memberCount: number;
|
||||
rating: number | null;
|
||||
totalWins: number;
|
||||
totalRaces: number;
|
||||
performanceLevel: SkillLevel;
|
||||
isRecruiting: boolean;
|
||||
createdAt: Date;
|
||||
description?: string;
|
||||
specialization?: 'endurance' | 'sprint' | 'mixed';
|
||||
region?: string;
|
||||
languages?: string[];
|
||||
}
|
||||
|
||||
export interface TeamsLeaderboardViewModel {
|
||||
teams: TeamLeaderboardItemViewModel[];
|
||||
recruitingCount: number;
|
||||
/**
|
||||
* Teams grouped by their skill level for UI display.
|
||||
*/
|
||||
groupsBySkillLevel: Record<SkillLevel, TeamLeaderboardItemViewModel[]>;
|
||||
/**
|
||||
* Precomputed top teams ordered for leaderboard preview.
|
||||
*/
|
||||
topTeams: TeamLeaderboardItemViewModel[];
|
||||
}
|
||||
|
||||
export interface TeamsLeaderboardResultDTO {
|
||||
teams: unknown[];
|
||||
recruitingCount: number;
|
||||
}
|
||||
|
||||
export interface ITeamsLeaderboardPresenter
|
||||
extends Presenter<TeamsLeaderboardResultDTO, TeamsLeaderboardViewModel> {}
|
||||
Reference in New Issue
Block a user