website cleanup
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
import { DashboardOverviewDto, DriverDto, RaceDto, LeagueStandingDto, FeedItemDto, FriendDto } from '../api/dashboard/DashboardApiClient';
|
||||
import type { DashboardOverviewDTO } from '@/lib/types/generated/DashboardOverviewDTO';
|
||||
import type { DashboardDriverSummaryDTO } from '@/lib/types/generated/DashboardDriverSummaryDTO';
|
||||
import type { DashboardRaceSummaryDTO } from '@/lib/types/generated/DashboardRaceSummaryDTO';
|
||||
import type { DashboardLeagueStandingSummaryDTO } from '@/lib/types/generated/DashboardLeagueStandingSummaryDTO';
|
||||
import type { DashboardFeedItemSummaryDTO } from '@/lib/types/generated/DashboardFeedItemSummaryDTO';
|
||||
import type { DashboardFriendSummaryDTO } from '@/lib/types/generated/DashboardFriendSummaryDTO';
|
||||
|
||||
export class DriverViewModel {
|
||||
constructor(private readonly dto: DriverDto) {}
|
||||
export class DashboardDriverSummaryViewModel {
|
||||
constructor(private readonly dto: DashboardDriverSummaryDTO) {}
|
||||
|
||||
get id(): string {
|
||||
return this.dto.id;
|
||||
@@ -32,25 +37,33 @@ export class DriverViewModel {
|
||||
}
|
||||
|
||||
get rating(): number {
|
||||
return this.dto.rating;
|
||||
return this.dto.rating ?? 0;
|
||||
}
|
||||
|
||||
get globalRank(): number {
|
||||
return this.dto.globalRank;
|
||||
return this.dto.globalRank ?? 0;
|
||||
}
|
||||
|
||||
get consistency(): number {
|
||||
return this.dto.consistency;
|
||||
return this.dto.consistency ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class RaceViewModel {
|
||||
constructor(private readonly dto: RaceDto) {}
|
||||
export class DashboardRaceSummaryViewModel {
|
||||
constructor(private readonly dto: DashboardRaceSummaryDTO) {}
|
||||
|
||||
get id(): string {
|
||||
return this.dto.id;
|
||||
}
|
||||
|
||||
get leagueId(): string {
|
||||
return this.dto.leagueId;
|
||||
}
|
||||
|
||||
get leagueName(): string {
|
||||
return this.dto.leagueName;
|
||||
}
|
||||
|
||||
get track(): string {
|
||||
return this.dto.track;
|
||||
}
|
||||
@@ -63,17 +76,17 @@ export class RaceViewModel {
|
||||
return new Date(this.dto.scheduledAt);
|
||||
}
|
||||
|
||||
get status(): string {
|
||||
return this.dto.status;
|
||||
}
|
||||
|
||||
get isMyLeague(): boolean {
|
||||
return this.dto.isMyLeague;
|
||||
}
|
||||
|
||||
get leagueName(): string | undefined {
|
||||
return this.dto.leagueName;
|
||||
}
|
||||
}
|
||||
|
||||
export class LeagueStandingViewModel {
|
||||
constructor(private readonly dto: LeagueStandingDto) {}
|
||||
export class DashboardLeagueStandingSummaryViewModel {
|
||||
constructor(private readonly dto: DashboardLeagueStandingSummaryDTO) {}
|
||||
|
||||
get leagueId(): string {
|
||||
return this.dto.leagueId;
|
||||
@@ -97,7 +110,7 @@ export class LeagueStandingViewModel {
|
||||
}
|
||||
|
||||
export class DashboardFeedItemSummaryViewModel {
|
||||
constructor(private readonly dto: FeedItemDto) {}
|
||||
constructor(private readonly dto: DashboardFeedItemSummaryDTO) {}
|
||||
|
||||
get id(): string {
|
||||
return this.dto.id;
|
||||
@@ -111,7 +124,7 @@ export class DashboardFeedItemSummaryViewModel {
|
||||
return this.dto.headline;
|
||||
}
|
||||
|
||||
get body(): string | null {
|
||||
get body(): string | undefined {
|
||||
return this.dto.body;
|
||||
}
|
||||
|
||||
@@ -128,8 +141,8 @@ export class DashboardFeedItemSummaryViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
export class FriendViewModel {
|
||||
constructor(private readonly dto: FriendDto) {}
|
||||
export class DashboardFriendSummaryViewModel {
|
||||
constructor(private readonly dto: DashboardFriendSummaryDTO) {}
|
||||
|
||||
get id(): string {
|
||||
return this.dto.id;
|
||||
@@ -149,33 +162,42 @@ export class FriendViewModel {
|
||||
}
|
||||
|
||||
export class DashboardOverviewViewModel {
|
||||
constructor(private readonly dto: DashboardOverviewDto) {}
|
||||
constructor(private readonly dto: DashboardOverviewDTO) {}
|
||||
|
||||
get currentDriver(): DriverViewModel {
|
||||
return new DriverViewModel(this.dto.currentDriver);
|
||||
get currentDriver(): DashboardDriverSummaryViewModel {
|
||||
// DTO uses optional property; enforce a consistent object for the UI
|
||||
return new DashboardDriverSummaryViewModel(this.dto.currentDriver ?? {
|
||||
id: '',
|
||||
name: '',
|
||||
country: '',
|
||||
avatarUrl: '',
|
||||
totalRaces: 0,
|
||||
wins: 0,
|
||||
podiums: 0,
|
||||
});
|
||||
}
|
||||
|
||||
get nextRace(): RaceViewModel | null {
|
||||
return this.dto.nextRace ? new RaceViewModel(this.dto.nextRace) : null;
|
||||
get nextRace(): DashboardRaceSummaryViewModel | null {
|
||||
return this.dto.nextRace ? new DashboardRaceSummaryViewModel(this.dto.nextRace) : null;
|
||||
}
|
||||
|
||||
get upcomingRaces(): RaceViewModel[] {
|
||||
return this.dto.upcomingRaces.map(dto => new RaceViewModel(dto));
|
||||
get upcomingRaces(): DashboardRaceSummaryViewModel[] {
|
||||
return this.dto.upcomingRaces.map((r) => new DashboardRaceSummaryViewModel(r));
|
||||
}
|
||||
|
||||
get leagueStandings(): LeagueStandingViewModel[] {
|
||||
return this.dto.leagueStandings.map(dto => new LeagueStandingViewModel(dto));
|
||||
get leagueStandings(): DashboardLeagueStandingSummaryViewModel[] {
|
||||
return this.dto.leagueStandingsSummaries.map((s) => new DashboardLeagueStandingSummaryViewModel(s));
|
||||
}
|
||||
|
||||
get feedItems(): DashboardFeedItemSummaryViewModel[] {
|
||||
return this.dto.feedItems.map(dto => new DashboardFeedItemSummaryViewModel(dto));
|
||||
return this.dto.feedSummary.items.map((i) => new DashboardFeedItemSummaryViewModel(i));
|
||||
}
|
||||
|
||||
get friends(): FriendViewModel[] {
|
||||
return this.dto.friends.map(dto => new FriendViewModel(dto));
|
||||
get friends(): DashboardFriendSummaryViewModel[] {
|
||||
return this.dto.friends.map((f) => new DashboardFriendSummaryViewModel(f));
|
||||
}
|
||||
|
||||
get activeLeaguesCount(): number {
|
||||
return this.dto.activeLeaguesCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DriverLeaderboardItemDTO } from '../types/generated/DriverLeaderboardItemDTO';
|
||||
import type { DriverLeaderboardItemDTO } from '../types/generated/DriverLeaderboardItemDTO';
|
||||
|
||||
export class DriverLeaderboardItemViewModel {
|
||||
id: string;
|
||||
@@ -16,7 +16,7 @@ export class DriverLeaderboardItemViewModel {
|
||||
position: number;
|
||||
private previousRating?: number;
|
||||
|
||||
constructor(dto: DriverLeaderboardItemDTO & { avatarUrl: string }, position: number, previousRating?: number) {
|
||||
constructor(dto: DriverLeaderboardItemDTO, position: number, previousRating?: number) {
|
||||
this.id = dto.id;
|
||||
this.name = dto.name;
|
||||
this.rating = dto.rating;
|
||||
@@ -27,7 +27,7 @@ export class DriverLeaderboardItemViewModel {
|
||||
this.podiums = dto.podiums;
|
||||
this.isActive = dto.isActive;
|
||||
this.rank = dto.rank;
|
||||
this.avatarUrl = dto.avatarUrl;
|
||||
this.avatarUrl = dto.avatarUrl ?? '';
|
||||
this.position = position;
|
||||
this.previousRating = previousRating;
|
||||
}
|
||||
@@ -84,4 +84,4 @@ export class DriverLeaderboardItemViewModel {
|
||||
get positionBadge(): string {
|
||||
return this.position.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ import { DriverLeaderboardItemViewModel } from './DriverLeaderboardItemViewModel
|
||||
export class DriverLeaderboardViewModel {
|
||||
drivers: DriverLeaderboardItemViewModel[];
|
||||
|
||||
constructor(dto: { drivers: (DriverLeaderboardItemDTO & { avatarUrl: string })[] }, previousDrivers?: (DriverLeaderboardItemDTO & { avatarUrl: string })[]) {
|
||||
constructor(
|
||||
dto: { drivers: DriverLeaderboardItemDTO[] },
|
||||
previousDrivers?: DriverLeaderboardItemDTO[],
|
||||
) {
|
||||
this.drivers = dto.drivers.map((driver, index) => {
|
||||
const previous = previousDrivers?.find(p => p.id === driver.id);
|
||||
return new DriverLeaderboardItemViewModel(driver, index + 1, previous?.rating);
|
||||
@@ -25,4 +28,4 @@ export class DriverLeaderboardViewModel {
|
||||
get activeCount(): number {
|
||||
return this.drivers.filter(driver => driver.isActive).length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import type { LeagueMemberDTO } from '../types/generated/LeagueMemberDTO';
|
||||
export class LeagueMembershipsViewModel {
|
||||
memberships: LeagueMemberViewModel[];
|
||||
|
||||
constructor(dto: { memberships: LeagueMemberDTO[] }, currentUserId: string) {
|
||||
this.memberships = dto.memberships.map(membership => new LeagueMemberViewModel(membership, currentUserId));
|
||||
constructor(dto: { members: LeagueMemberDTO[] }, currentUserId: string) {
|
||||
this.memberships = dto.members.map(membership => new LeagueMemberViewModel(membership, currentUserId));
|
||||
}
|
||||
|
||||
/** UI-specific: Number of members */
|
||||
@@ -22,4 +22,4 @@ export class LeagueMembershipsViewModel {
|
||||
get hasMembers(): boolean {
|
||||
return this.memberCount > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { LeagueScoringPresetsViewModel } from './LeagueScoringPresetsViewModel';
|
||||
import type { LeagueScoringPresetDTO } from '@core/racing/application/ports/LeagueScoringPresetProvider';
|
||||
import type { LeagueScoringPresetDTO } from '@/lib/types/generated/LeagueScoringPresetDTO';
|
||||
|
||||
const createPreset = (overrides: Partial<LeagueScoringPresetDTO> = {}): LeagueScoringPresetDTO => ({
|
||||
id: 'preset-1',
|
||||
name: 'Standard scoring',
|
||||
description: 'Top 15 get points',
|
||||
gameId: 'iracing',
|
||||
primaryChampionshipType: 'driver',
|
||||
sessionSummary: 'Sprint + Main',
|
||||
bonusSummary: 'None',
|
||||
dropPolicySummary: 'Best 6',
|
||||
...overrides,
|
||||
} as LeagueScoringPresetDTO);
|
||||
|
||||
|
||||
@@ -1,14 +1,49 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { LeagueSettingsViewModel } from './LeagueSettingsViewModel';
|
||||
import { DriverSummaryViewModel } from './DriverSummaryViewModel';
|
||||
import type { LeagueConfigFormModel } from '@core/racing/application';
|
||||
import type { LeagueScoringPresetDTO } from '@core/racing/application/ports/LeagueScoringPresetProvider';
|
||||
import type { LeagueConfigFormModel } from '@/lib/types/LeagueConfigFormModel';
|
||||
import type { LeagueScoringPresetDTO } from '@/lib/types/generated/LeagueScoringPresetDTO';
|
||||
|
||||
const createConfig = (overrides: Partial<LeagueConfigFormModel> = {}): LeagueConfigFormModel => ({
|
||||
name: 'Pro League',
|
||||
description: 'Top tier competition',
|
||||
maxDrivers: 40,
|
||||
maxTeams: 10,
|
||||
basics: {
|
||||
name: 'Pro League',
|
||||
description: 'Top tier competition',
|
||||
visibility: 'public',
|
||||
gameId: 'iracing',
|
||||
},
|
||||
structure: {
|
||||
mode: 'solo',
|
||||
maxDrivers: 40,
|
||||
},
|
||||
championships: {
|
||||
enableDriverChampionship: true,
|
||||
enableTeamChampionship: false,
|
||||
enableNationsChampionship: false,
|
||||
enableTrophyChampionship: false,
|
||||
},
|
||||
scoring: {
|
||||
patternId: 'sprint-main-driver',
|
||||
customScoringEnabled: false,
|
||||
},
|
||||
dropPolicy: {
|
||||
strategy: 'bestNResults',
|
||||
n: 6,
|
||||
},
|
||||
timings: {
|
||||
qualifyingMinutes: 30,
|
||||
mainRaceMinutes: 40,
|
||||
roundsPlanned: 8,
|
||||
},
|
||||
stewarding: {
|
||||
decisionMode: 'admin_vote',
|
||||
requireDefense: false,
|
||||
defenseTimeLimit: 24,
|
||||
voteTimeLimit: 48,
|
||||
protestDeadlineHours: 24,
|
||||
stewardingClosesHours: 168,
|
||||
notifyAccusedOnProtest: true,
|
||||
notifyOnVoteRequired: true,
|
||||
},
|
||||
...overrides,
|
||||
} as LeagueConfigFormModel);
|
||||
|
||||
@@ -16,7 +51,10 @@ const createPreset = (overrides: Partial<LeagueScoringPresetDTO> = {}): LeagueSc
|
||||
id: 'preset-1',
|
||||
name: 'Standard scoring',
|
||||
description: 'Top 15 get points',
|
||||
gameId: 'iracing',
|
||||
primaryChampionshipType: 'driver',
|
||||
sessionSummary: 'Sprint + Main',
|
||||
bonusSummary: 'None',
|
||||
dropPolicySummary: 'Best 6',
|
||||
...overrides,
|
||||
} as LeagueScoringPresetDTO);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// DTO matching the backend RacesPageDataRaceDTO
|
||||
interface RaceListItemDTO {
|
||||
export interface RaceListItemDTO {
|
||||
id: string;
|
||||
track: string;
|
||||
car: string;
|
||||
@@ -7,7 +7,7 @@ interface RaceListItemDTO {
|
||||
status: string;
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
strengthOfField: number | null;
|
||||
strengthOfField?: number | null;
|
||||
isUpcoming: boolean;
|
||||
isLive: boolean;
|
||||
isPast: boolean;
|
||||
@@ -34,7 +34,7 @@ export class RaceListItemViewModel {
|
||||
this.status = dto.status;
|
||||
this.leagueId = dto.leagueId;
|
||||
this.leagueName = dto.leagueName;
|
||||
this.strengthOfField = dto.strengthOfField;
|
||||
this.strengthOfField = dto.strengthOfField ?? null;
|
||||
this.isUpcoming = dto.isUpcoming;
|
||||
this.isLive = dto.isLive;
|
||||
this.isPast = dto.isPast;
|
||||
@@ -70,4 +70,4 @@ export class RaceListItemViewModel {
|
||||
const hours = Math.floor(minutes / 60);
|
||||
return `${hours}h ${minutes % 60}m`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
import { RaceListItemViewModel } from './RaceListItemViewModel';
|
||||
import type { RacesPageDataRaceDTO } from '../types/generated/RacesPageDataRaceDTO';
|
||||
import type { RaceListItemDTO } from './RaceListItemViewModel';
|
||||
|
||||
// DTO matching the backend RacesPageDataDTO
|
||||
interface RacesPageDTO {
|
||||
races: Array<{
|
||||
id: string;
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string;
|
||||
status: string;
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
strengthOfField: number | null;
|
||||
isUpcoming: boolean;
|
||||
isLive: boolean;
|
||||
isPast: boolean;
|
||||
}>;
|
||||
races: RacesPageDataRaceDTO[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -25,7 +15,17 @@ export class RacesPageViewModel {
|
||||
races: RaceListItemViewModel[];
|
||||
|
||||
constructor(dto: RacesPageDTO) {
|
||||
this.races = dto.races.map(r => new RaceListItemViewModel(r));
|
||||
this.races = dto.races.map((r) => {
|
||||
const normalized: RaceListItemDTO = {
|
||||
...r,
|
||||
strengthOfField: (r as any).strengthOfField ?? null,
|
||||
isUpcoming: r.isUpcoming,
|
||||
isLive: r.isLive,
|
||||
isPast: r.isPast,
|
||||
};
|
||||
|
||||
return new RaceListItemViewModel(normalized);
|
||||
});
|
||||
}
|
||||
|
||||
/** UI-specific: Total races */
|
||||
@@ -62,4 +62,4 @@ export class RacesPageViewModel {
|
||||
get completedRaces(): RaceListItemViewModel[] {
|
||||
return this.races.filter(r => r.status === 'completed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,24 @@ export class SessionViewModel {
|
||||
driverId?: string;
|
||||
isAuthenticated: boolean = true;
|
||||
|
||||
/**
|
||||
* Compatibility accessor.
|
||||
* Some legacy components expect `session.user.*`.
|
||||
*/
|
||||
get user(): {
|
||||
userId: string;
|
||||
email: string;
|
||||
displayName: string;
|
||||
primaryDriverId?: string | null;
|
||||
} {
|
||||
return {
|
||||
userId: this.userId,
|
||||
email: this.email,
|
||||
displayName: this.displayName,
|
||||
primaryDriverId: this.driverId ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
/** UI-specific: User greeting */
|
||||
get greeting(): string {
|
||||
return `Hello, ${this.displayName}!`;
|
||||
@@ -26,7 +44,7 @@ export class SessionViewModel {
|
||||
if (this.displayName) {
|
||||
return this.displayName.split(' ').map(n => n[0]).join('').toUpperCase();
|
||||
}
|
||||
return this.email[0].toUpperCase();
|
||||
return (this.email?.[0] ?? '?').toUpperCase();
|
||||
}
|
||||
|
||||
/** UI-specific: Whether has driver profile */
|
||||
@@ -38,4 +56,4 @@ export class SessionViewModel {
|
||||
get authStatusDisplay(): string {
|
||||
return this.isAuthenticated ? 'Logged In' : 'Logged Out';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PendingRequestDTO } from '@/components/sponsors/PendingSponsorshipRequests';
|
||||
import type { SponsorshipRequestDTO } from '@/lib/types/generated/SponsorshipRequestDTO';
|
||||
|
||||
export class SponsorshipRequestViewModel {
|
||||
id: string;
|
||||
@@ -14,17 +14,18 @@ export class SponsorshipRequestViewModel {
|
||||
platformFee: number;
|
||||
netAmount: number;
|
||||
|
||||
constructor(dto: PendingRequestDTO) {
|
||||
constructor(dto: SponsorshipRequestDTO) {
|
||||
this.id = dto.id;
|
||||
this.sponsorId = dto.sponsorId;
|
||||
this.sponsorName = dto.sponsorName;
|
||||
this.sponsorLogo = dto.sponsorLogo;
|
||||
this.tier = dto.tier;
|
||||
// Backend currently returns tier as string; normalize to our supported tiers.
|
||||
this.tier = dto.tier === 'main' ? 'main' : 'secondary';
|
||||
this.offeredAmount = dto.offeredAmount;
|
||||
this.currency = dto.currency;
|
||||
this.formattedAmount = dto.formattedAmount;
|
||||
this.message = dto.message;
|
||||
this.createdAt = dto.createdAt;
|
||||
this.createdAt = new Date(dto.createdAt);
|
||||
this.platformFee = dto.platformFee;
|
||||
this.netAmount = dto.netAmount;
|
||||
}
|
||||
@@ -51,4 +52,4 @@ export class SponsorshipRequestViewModel {
|
||||
get tierBadgeVariant(): 'primary' | 'secondary' {
|
||||
return this.tier === 'main' ? 'primary' : 'secondary';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { TeamListItemDTO } from '@/lib/types/generated/GetAllTeamsOutputDTO';
|
||||
import type { TeamListItemDTO } from '@/lib/types/generated/TeamListItemDTO';
|
||||
|
||||
interface TeamCardDTO {
|
||||
id: string;
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
// Note: No generated DTO available for TeamJoinRequest yet
|
||||
export interface TeamJoinRequestDTO {
|
||||
id: string;
|
||||
teamId: string;
|
||||
driverId: string;
|
||||
requestedAt: string;
|
||||
message?: string;
|
||||
}
|
||||
import type { TeamJoinRequestDTO } from '@/lib/types/generated/TeamJoinRequestDTO';
|
||||
|
||||
export class TeamJoinRequestViewModel {
|
||||
id: string;
|
||||
teamId: string;
|
||||
requestId: string;
|
||||
driverId: string;
|
||||
driverName: string;
|
||||
teamId: string;
|
||||
requestStatus: string;
|
||||
requestedAt: string;
|
||||
message?: string;
|
||||
avatarUrl: string;
|
||||
|
||||
private currentUserId: string;
|
||||
private isOwner: boolean;
|
||||
private readonly currentUserId: string;
|
||||
private readonly isOwner: boolean;
|
||||
|
||||
constructor(dto: TeamJoinRequestDTO, currentUserId: string, isOwner: boolean) {
|
||||
Object.assign(this, dto);
|
||||
this.requestId = dto.requestId;
|
||||
this.driverId = dto.driverId;
|
||||
this.driverName = dto.driverName;
|
||||
this.teamId = dto.teamId;
|
||||
this.requestStatus = dto.status;
|
||||
this.requestedAt = dto.requestedAt;
|
||||
this.avatarUrl = dto.avatarUrl;
|
||||
this.currentUserId = currentUserId;
|
||||
this.isOwner = isOwner;
|
||||
}
|
||||
@@ -33,13 +34,10 @@ export class TeamJoinRequestViewModel {
|
||||
return new Date(this.requestedAt).toLocaleString();
|
||||
}
|
||||
|
||||
/** UI-specific: Request status (pending) */
|
||||
get status(): string {
|
||||
return 'Pending';
|
||||
}
|
||||
|
||||
/** UI-specific: Status color */
|
||||
get statusColor(): string {
|
||||
if (this.requestStatus === 'approved') return 'green';
|
||||
if (this.requestStatus === 'rejected') return 'red';
|
||||
return 'yellow';
|
||||
}
|
||||
|
||||
@@ -52,4 +50,4 @@ export class TeamJoinRequestViewModel {
|
||||
get rejectButtonText(): string {
|
||||
return 'Reject';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import type { TeamMemberDTO } from '@/lib/types/generated/GetTeamMembersOutputDTO';
|
||||
import type { TeamMemberDTO } from '@/lib/types/generated/TeamMemberDTO';
|
||||
|
||||
type TeamMemberRole = 'owner' | 'manager' | 'member';
|
||||
|
||||
function normalizeTeamRole(role: string): TeamMemberRole {
|
||||
if (role === 'owner' || role === 'manager' || role === 'member') return role;
|
||||
// Backwards compatibility
|
||||
if (role === 'admin') return 'manager';
|
||||
return 'member';
|
||||
}
|
||||
|
||||
export class TeamMemberViewModel {
|
||||
driverId: string;
|
||||
driverName: string;
|
||||
role: 'owner' | 'admin' | 'member';
|
||||
role: TeamMemberRole;
|
||||
joinedAt: string;
|
||||
isActive: boolean;
|
||||
avatarUrl: string;
|
||||
@@ -14,7 +23,7 @@ export class TeamMemberViewModel {
|
||||
constructor(dto: TeamMemberDTO, currentUserId: string, teamOwnerId: string) {
|
||||
this.driverId = dto.driverId;
|
||||
this.driverName = dto.driverName;
|
||||
this.role = dto.role;
|
||||
this.role = normalizeTeamRole(dto.role);
|
||||
this.joinedAt = dto.joinedAt;
|
||||
this.isActive = dto.isActive;
|
||||
this.avatarUrl = dto.avatarUrl;
|
||||
@@ -26,7 +35,7 @@ export class TeamMemberViewModel {
|
||||
get roleBadgeVariant(): string {
|
||||
switch (this.role) {
|
||||
case 'owner': return 'primary';
|
||||
case 'admin': return 'secondary';
|
||||
case 'manager': return 'secondary';
|
||||
case 'member': return 'default';
|
||||
default: return 'default';
|
||||
}
|
||||
@@ -51,4 +60,4 @@ export class TeamMemberViewModel {
|
||||
get formattedJoinedAt(): string {
|
||||
return new Date(this.joinedAt).toLocaleDateString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { TeamListItemDTO } from '@/lib/types/generated/GetAllTeamsOutputDTO';
|
||||
import type { TeamListItemDTO } from '@/lib/types/generated/TeamListItemDTO';
|
||||
|
||||
export class TeamSummaryViewModel {
|
||||
id: string;
|
||||
@@ -54,4 +54,4 @@ export class TeamSummaryViewModel {
|
||||
get statusColor(): string {
|
||||
return this.isFull ? 'red' : 'green';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user