[{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/ActivityItemViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/AdminUserViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/AnalyticsDashboardViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/AnalyticsMetricsViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/AvailableLeaguesViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/AvatarGenerationViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/AvatarViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/BillingStatsViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/BillingViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/CompleteOnboardingViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/builders/view-data/CompleteOnboardingViewData","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":103}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { CompleteOnboardingViewData } from '@/lib/builders/view-data/CompleteOnboardingViewData';\nimport { OnboardingStatusDisplay } from '../display-objects/OnboardingStatusDisplay';\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * Complete onboarding view model\n * UI representation of onboarding completion result\n *\n * Composes Display Objects and transforms ViewData for UI consumption.\n */\nexport class CompleteOnboardingViewModel extends ViewModel {\n private readonly data: CompleteOnboardingViewData;\n\n constructor(data: CompleteOnboardingViewData) {\n super();\n this.data = data;\n }\n\n get success(): boolean { return this.data.success; }\n get driverId(): string | undefined { return this.data.driverId; }\n get errorMessage(): string | undefined { return this.data.errorMessage; }\n\n /** UI-specific: Status label using Display Object */\n get statusLabel(): string {\n return OnboardingStatusDisplay.statusLabel(this.success);\n }\n\n /** UI-specific: Status variant using Display Object */\n get statusVariant(): string {\n return OnboardingStatusDisplay.statusVariant(this.success);\n }\n\n /** UI-specific: Status icon using Display Object */\n get statusIcon(): string {\n return OnboardingStatusDisplay.statusIcon(this.success);\n }\n\n /** UI-specific: Status message using Display Object */\n get statusMessage(): string {\n return OnboardingStatusDisplay.statusMessage(this.success, this.errorMessage);\n }\n\n /** UI-specific: Whether onboarding was successful */\n get isSuccessful(): boolean {\n return this.success;\n }\n\n /** UI-specific: Whether there was an error */\n get hasError(): boolean {\n return !!this.errorMessage;\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/CreateLeagueViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/CreateTeamViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/DashboardStatsViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/DeleteMediaViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/builders/view-data/DeleteMediaViewData","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":89}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { DeleteMediaViewData } from '@/lib/builders/view-data/DeleteMediaViewData';\nimport { ViewModel } from '../contracts/view-models/ViewModel';\n\n/**\n * Delete Media View Model\n *\n * Represents the result of a media deletion operation\n * Composes ViewData for UI consumption.\n */\nexport class DeleteMediaViewModel extends ViewModel {\n private readonly data: DeleteMediaViewData;\n\n constructor(data: DeleteMediaViewData) {\n super();\n this.data = data;\n }\n\n get success(): boolean { return this.data.success; }\n get error(): string | undefined { return this.data.error; }\n\n /** UI-specific: Whether the deletion was successful */\n get isSuccessful(): boolean {\n return this.success;\n }\n\n /** UI-specific: Whether there was an error */\n get hasError(): boolean {\n return !!this.error;\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/DriverLeaderboardItemViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/DriverLeaderboardViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/DriverProfileDriverSummaryViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/DriverProfileViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/DriverRegistrationStatusViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/DriverSummaryViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/DriverTeamViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/DriverViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":14,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":23,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Driver view model\n * UI representation of a driver\n *\n * Note: client-only ViewModel created from ViewData (never DTO).\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\nimport { RatingDisplay } from \"../display-objects/RatingDisplay\";\n\n/**\n * ViewData for Driver\n * This is the JSON-serializable input for the Template.\n */\nexport interface DriverViewData {\n id: string;\n name: string;\n avatarUrl: string | null;\n iracingId?: string;\n rating?: number;\n country?: string;\n bio?: string;\n joinedAt?: string;\n}\n\nexport class DriverViewModel extends ViewModel {\n private readonly data: DriverViewData;\n\n constructor(data: DriverViewData) {\n super();\n this.data = data;\n }\n\n get id(): string { return this.data.id; }\n get name(): string { return this.data.name; }\n get avatarUrl(): string { return this.data.avatarUrl || ''; }\n get iracingId(): string | undefined { return this.data.iracingId; }\n get rating(): number | undefined { return this.data.rating; }\n get country(): string | undefined { return this.data.country; }\n get bio(): string | undefined { return this.data.bio; }\n get joinedAt(): string | undefined { return this.data.joinedAt; }\n\n /** UI-specific: Whether driver has an iRacing ID */\n get hasIracingId(): boolean {\n return !!this.iracingId;\n }\n\n /** UI-specific: Formatted rating */\n get formattedRating(): string {\n return this.rating !== undefined ? RatingDisplay.format(this.rating) : \"Unrated\";\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/EmailSignupViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/HomeDiscoveryViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/ImportRaceResultsSummaryViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/InvoiceViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueAdminRosterJoinRequestViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":7,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":14,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueAdminRosterJoinRequest\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueAdminRosterJoinRequestViewData {\n id: string;\n leagueId: string;\n driverId: string;\n driverName: string;\n requestedAtIso: string;\n message?: string;\n}\n\nexport class LeagueAdminRosterJoinRequestViewModel extends ViewModel {\n private readonly data: LeagueAdminRosterJoinRequestViewData;\n\n constructor(data: LeagueAdminRosterJoinRequestViewData) {\n super();\n this.data = data;\n }\n\n get id(): string { return this.data.id; }\n get leagueId(): string { return this.data.leagueId; }\n get driverId(): string { return this.data.driverId; }\n get driverName(): string { return this.data.driverName; }\n get requestedAtIso(): string { return this.data.requestedAtIso; }\n get message(): string | undefined { return this.data.message; }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueAdminRosterMemberViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/MembershipRole","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":66},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":9,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":14,"endColumn":2}],"suppressedMessages":[],"errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { MembershipRole } from '@/lib/types/MembershipRole';\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueAdminRosterMember\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueAdminRosterMemberViewData {\n driverId: string;\n driverName: string;\n role: MembershipRole;\n joinedAtIso: string;\n}\n\nexport class LeagueAdminRosterMemberViewModel extends ViewModel {\n private readonly data: LeagueAdminRosterMemberViewData;\n\n constructor(data: LeagueAdminRosterMemberViewData) {\n super();\n this.data = data;\n }\n\n get driverId(): string { return this.data.driverId; }\n get driverName(): string { return this.data.driverName; }\n get role(): MembershipRole { return this.data.role; }\n get joinedAtIso(): string { return this.data.joinedAtIso; }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueAdminScheduleViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":9,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":13,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { LeagueScheduleRaceViewModel } from './LeagueScheduleViewModel';\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueAdminSchedule\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueAdminScheduleViewData {\n seasonId: string;\n published: boolean;\n races: any[];\n}\n\nexport class LeagueAdminScheduleViewModel extends ViewModel {\n private readonly data: LeagueAdminScheduleViewData;\n\n constructor(data: LeagueAdminScheduleViewData) {\n super();\n this.data = data;\n }\n\n get seasonId(): string { return this.data.seasonId; }\n get published(): boolean { return this.data.published; }\n get races(): any[] { return this.data.races; }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueAdminViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":12,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { LeagueMemberViewModel, LeagueMemberViewData } from './LeagueMemberViewModel';\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueAdmin\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueAdminViewData {\n config: unknown;\n members: LeagueMemberViewData[];\n joinRequests: any[];\n}\n\nexport class LeagueAdminViewModel extends ViewModel {\n private readonly data: LeagueAdminViewData;\n readonly members: LeagueMemberViewModel[];\n\n constructor(data: LeagueAdminViewData) {\n super();\n this.data = data;\n this.members = data.members.map(m => new LeagueMemberViewModel(m));\n }\n\n get config(): unknown { return this.data.config; }\n get joinRequests(): any[] { return this.data.joinRequests; }\n\n /** UI-specific: Total pending requests count */\n get pendingRequestsCount(): number {\n return this.joinRequests.length;\n }\n\n /** UI-specific: Whether there are any pending requests */\n get hasPendingRequests(): boolean {\n return this.joinRequests.length > 0;\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueCardViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":7,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":11,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueCard\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueCardViewData {\n id: string;\n name: string;\n description?: string;\n}\n\nexport class LeagueCardViewModel extends ViewModel {\n private readonly data: LeagueCardViewData;\n\n constructor(data: LeagueCardViewData) {\n super();\n this.data = data;\n }\n\n get id(): string { return this.data.id; }\n get name(): string { return this.data.name; }\n get description(): string { return this.data.description ?? 'Competitive iRacing league'; }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueDetailPageViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":1,"endColumn":105},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/LeagueWithCapacityAndScoringDTO","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":105},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":41},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":41},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":2,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":2,"endColumn":71},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/LeagueStatsDTO","line":2,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":2,"endColumn":71},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":2,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":2,"endColumn":24},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":2,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":2,"endColumn":24},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":3,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":3,"endColumn":83},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/LeagueMembershipsDTO","line":3,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":3,"endColumn":83},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":3,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":3,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":3,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":3,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":4,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":4,"endColumn":79},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/GetDriverOutputDTO","line":4,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":4,"endColumn":79},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":4,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":4,"endColumn":28},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":4,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":4,"endColumn":28},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":5,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":5,"endColumn":87},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/LeagueScoringConfigDTO","line":5,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":5,"endColumn":87},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":5,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":5,"endColumn":32},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":5,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":5,"endColumn":32},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":76,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":76,"endColumn":28},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":79,"column":18,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":79,"endColumn":40},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":82,"column":12,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":82,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":118,"column":13,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":118,"endColumn":44},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":119,"column":12,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":119,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":120,"column":20,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":120,"endColumn":42},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":121,"column":14,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":121,"endColumn":32},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":122,"column":18,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":122,"endColumn":38},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":124,"column":18,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":124,"endColumn":32},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":219,"column":11,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":219,"endColumn":20},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":220,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":220,"endColumn":19},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":223,"column":23,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":223,"endColumn":32},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":227,"column":11,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":227,"endColumn":20},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":228,"column":13,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":228,"endColumn":22},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":230,"column":18,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":230,"endColumn":27}],"suppressedMessages":[],"errorCount":35,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { LeagueWithCapacityAndScoringDTO } from '@/lib/types/generated/LeagueWithCapacityAndScoringDTO';\nimport { LeagueStatsDTO } from '@/lib/types/generated/LeagueStatsDTO';\nimport { LeagueMembershipsDTO } from '@/lib/types/generated/LeagueMembershipsDTO';\nimport { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO';\nimport { LeagueScoringConfigDTO } from '@/lib/types/generated/LeagueScoringConfigDTO';\nimport { RaceViewModel } from './RaceViewModel';\nimport { DriverViewModel } from './DriverViewModel';\n\n// Sponsor info type\nexport interface SponsorInfo {\n id: string;\n name: string;\n logoUrl?: string;\n websiteUrl?: string;\n tier: 'main' | 'secondary';\n tagline?: string;\n}\n\n// Driver summary for management section\nexport interface DriverSummary {\n driver: DriverViewModel;\n rating: number | null;\n rank: number | null;\n}\n\n// League membership with role\nexport interface LeagueMembershipWithRole {\n driverId: string;\n role: 'owner' | 'admin' | 'steward' | 'member';\n status: 'active' | 'inactive';\n joinedAt: string;\n}\n\n// Helper interfaces for type narrowing\ninterface LeagueSettings {\n maxDrivers?: number;\n}\n\ninterface SocialLinks {\n discordUrl?: string;\n youtubeUrl?: string;\n websiteUrl?: string;\n}\n\ninterface LeagueStatsExtended {\n averageSOF?: number;\n averageRating?: number;\n completedRaces?: number;\n totalRaces?: number;\n}\n\ninterface MembershipsContainer {\n members?: Array<{ driverId: string; role: string; status?: 'active' | 'inactive'; joinedAt: string }>;\n memberships?: Array<{ driverId: string; role: string; status?: 'active' | 'inactive'; joinedAt: string }>;\n}\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport class LeagueDetailPageViewModel extends ViewModel {\n // League basic info\n id: string;\n name: string;\n description?: string;\n ownerId: string;\n createdAt: string;\n settings: {\n maxDrivers?: number;\n };\n socialLinks: {\n discordUrl?: string;\n youtubeUrl?: string;\n websiteUrl?: string;\n } | undefined;\n\n // Owner info\n owner: GetDriverOutputDTO | null;\n\n // Scoring configuration\n scoringConfig: LeagueScoringConfigDTO | null;\n\n // Drivers and memberships\n drivers: GetDriverOutputDTO[];\n memberships: LeagueMembershipWithRole[];\n\n // Races\n allRaces: RaceViewModel[];\n runningRaces: RaceViewModel[];\n\n // Stats\n averageSOF: number | null;\n completedRacesCount: number;\n\n // Sponsors\n sponsors: SponsorInfo[];\n\n // Sponsor insights data\n sponsorInsights: {\n avgViewsPerRace: number;\n totalImpressions: number;\n engagementRate: string;\n estimatedReach: number;\n mainSponsorAvailable: boolean;\n secondarySlotsAvailable: number;\n mainSponsorPrice: number;\n secondaryPrice: number;\n tier: 'premium' | 'standard' | 'starter';\n trustScore: number;\n discordMembers: number;\n monthlyActivity: number;\n };\n\n // Driver summaries for management\n ownerSummary: DriverSummary | null;\n adminSummaries: DriverSummary[];\n stewardSummaries: DriverSummary[];\n\n constructor(\n league: LeagueWithCapacityAndScoringDTO,\n owner: GetDriverOutputDTO | null,\n scoringConfig: LeagueScoringConfigDTO | null,\n drivers: GetDriverOutputDTO[],\n memberships: LeagueMembershipsDTO,\n allRaces: RaceViewModel[],\n leagueStats: LeagueStatsDTO,\n sponsors: SponsorInfo[]\n ) {\n this.id = league.id;\n this.name = league.name;\n this.description = league.description ?? '';\n this.ownerId = league.ownerId;\n this.createdAt = league.createdAt;\n \n // Handle settings with proper type narrowing\n const settings = league.settings as LeagueSettings | undefined;\n const maxDrivers = settings?.maxDrivers;\n this.settings = {\n maxDrivers: maxDrivers,\n };\n \n // Handle social links with proper type narrowing\n const socialLinks = league.socialLinks as SocialLinks | undefined;\n const discordUrl = socialLinks?.discordUrl;\n const youtubeUrl = socialLinks?.youtubeUrl;\n const websiteUrl = socialLinks?.websiteUrl;\n \n this.socialLinks = {\n discordUrl,\n youtubeUrl,\n websiteUrl,\n };\n\n this.owner = owner;\n this.scoringConfig = scoringConfig;\n this.drivers = drivers;\n\n // Handle memberships with proper type narrowing\n const membershipsContainer = memberships as MembershipsContainer;\n const membershipDtos = membershipsContainer.members ?? \n membershipsContainer.memberships ?? \n [];\n\n this.memberships = membershipDtos.map((m) => ({\n driverId: m.driverId,\n role: m.role as 'owner' | 'admin' | 'steward' | 'member',\n status: m.status ?? 'active',\n joinedAt: m.joinedAt,\n }));\n\n this.allRaces = allRaces;\n this.runningRaces = allRaces.filter(r => r.status === 'running');\n\n // Calculate SOF from available data with proper type narrowing\n const statsExtended = leagueStats as LeagueStatsExtended;\n const averageSOF = statsExtended.averageSOF ?? \n statsExtended.averageRating ?? undefined;\n const completedRaces = statsExtended.completedRaces ?? \n statsExtended.totalRaces ?? undefined;\n\n this.averageSOF = typeof averageSOF === 'number' ? averageSOF : null;\n this.completedRacesCount = typeof completedRaces === 'number' ? completedRaces : 0;\n\n this.sponsors = sponsors;\n\n // Calculate sponsor insights\n const memberCount = this.memberships.length;\n const mainSponsorTaken = this.sponsors.some(s => s.tier === 'main');\n const secondaryTaken = this.sponsors.filter(s => s.tier === 'secondary').length;\n\n this.sponsorInsights = {\n avgViewsPerRace: 5400 + memberCount * 50,\n totalImpressions: 45000 + memberCount * 500,\n engagementRate: (3.5 + (memberCount / 50)).toFixed(1),\n estimatedReach: memberCount * 150,\n mainSponsorAvailable: !mainSponsorTaken,\n secondarySlotsAvailable: Math.max(0, 2 - secondaryTaken),\n mainSponsorPrice: 800 + Math.floor(memberCount * 10),\n secondaryPrice: 250 + Math.floor(memberCount * 3),\n tier: (this.averageSOF && this.averageSOF > 3000 ? 'premium' : this.averageSOF && this.averageSOF > 2000 ? 'standard' : 'starter') as 'premium' | 'standard' | 'starter',\n trustScore: Math.min(100, 60 + memberCount + this.completedRacesCount),\n discordMembers: memberCount * 3,\n monthlyActivity: Math.min(100, 40 + this.completedRacesCount * 2),\n };\n\n // Build driver summaries\n this.ownerSummary = this.buildDriverSummary(this.ownerId);\n this.adminSummaries = this.memberships\n .filter(m => m.role === 'admin')\n .slice(0, 3)\n .map(m => this.buildDriverSummary(m.driverId))\n .filter((s): s is DriverSummary => s !== null);\n this.stewardSummaries = this.memberships\n .filter(m => m.role === 'steward')\n .slice(0, 3)\n .map(m => this.buildDriverSummary(m.driverId))\n .filter((s): s is DriverSummary => s !== null);\n }\n\n private buildDriverSummary(driverId: string): DriverSummary | null {\n const driverDto = this.drivers.find(d => d.id === driverId);\n if (!driverDto) return null;\n\n // Handle avatarUrl with proper type checking\n const driverAny = driverDto as { avatarUrl?: unknown };\n const avatarUrl = typeof driverAny.avatarUrl === 'string' ? driverAny.avatarUrl : null;\n\n const driver = new DriverViewModel({\n id: driverDto.id,\n name: driverDto.name,\n avatarUrl: avatarUrl,\n iracingId: driverDto.iracingId,\n });\n\n // Detailed rating and rank data are not wired from the analytics services yet;\n // expose the driver identity only so the UI can still render role assignments.\n return {\n driver,\n rating: null,\n rank: null,\n };\n }\n\n // UI helper methods\n get isSponsorMode(): boolean {\n // League detail pages are rendered in organizer mode in this build; sponsor-specific\n // mode switches will be introduced once sponsor dashboards share this view model.\n return false;\n }\n\n get currentUserMembership(): LeagueMembershipWithRole | null {\n // Current user identity is not available in this view model context yet; callers must\n // pass an explicit membership if they need per-user permissions.\n return null;\n }\n\n get canEndRaces(): boolean {\n return this.currentUserMembership?.role === 'admin' || this.currentUserMembership?.role === 'owner';\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueDetailViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":11,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":15,"endColumn":2},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":68,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":89,"endColumn":2}],"suppressedMessages":[],"errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { DriverViewModel, DriverViewData } from \"./DriverViewModel\";\nimport { RaceViewModel, RaceViewData } from \"./RaceViewModel\";\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\nimport { CurrencyDisplay } from \"../display-objects/CurrencyDisplay\";\nimport { LeagueTierDisplay } from \"../display-objects/LeagueTierDisplay\";\n\n/**\n * ViewData for LeagueDetail\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueDetailViewData {\n league: LeagueViewData;\n drivers: (DriverViewData & { impressions: number })[];\n races: (RaceViewData & { views: number })[];\n}\n\nexport class LeagueDetailViewModel extends ViewModel {\n private readonly data: LeagueDetailViewData;\n readonly league: LeagueViewModel;\n readonly drivers: LeagueDetailDriverViewModel[];\n readonly races: LeagueDetailRaceViewModel[];\n\n constructor(data: LeagueDetailViewData) {\n super();\n this.data = data;\n this.league = new LeagueViewModel(data.league);\n this.drivers = data.drivers.map(driver => new LeagueDetailDriverViewModel(driver));\n this.races = data.races.map(race => new LeagueDetailRaceViewModel(race));\n }\n}\n\nexport class LeagueDetailDriverViewModel extends DriverViewModel {\n private readonly detailData: DriverViewData & { impressions: number };\n\n constructor(data: DriverViewData & { impressions: number }) {\n super(data);\n this.detailData = data;\n }\n\n get impressions(): number {\n return this.detailData.impressions;\n }\n\n get formattedImpressions(): string {\n // Client-only formatting\n return this.impressions.toLocaleString();\n }\n}\n\nexport class LeagueDetailRaceViewModel extends RaceViewModel {\n private readonly detailData: RaceViewData & { views: number };\n\n constructor(data: RaceViewData & { views: number }) {\n super(data);\n this.detailData = data;\n }\n\n get views(): number {\n return this.detailData.views;\n }\n\n get formattedViews(): string {\n // Client-only formatting\n return this.views.toLocaleString();\n }\n}\n\nexport interface LeagueViewData {\n id: string;\n name: string;\n game: string;\n tier: 'premium' | 'standard' | 'starter';\n season: string;\n description: string;\n drivers: number;\n races: number;\n completedRaces: number;\n totalImpressions: number;\n avgViewsPerRace: number;\n engagement: number;\n rating: number;\n seasonStatus: 'active' | 'upcoming' | 'completed';\n seasonDates: { start: string; end: string };\n nextRace?: { name: string; date: string };\n sponsorSlots: {\n main: { available: boolean; price: number; benefits: string[] };\n secondary: { available: number; total: number; price: number; benefits: string[] };\n };\n}\n\nexport class LeagueViewModel extends ViewModel {\n private readonly data: LeagueViewData;\n\n constructor(data: LeagueViewData) {\n super();\n this.data = data;\n }\n\n get id(): string { return this.data.id; }\n get name(): string { return this.data.name; }\n get game(): string { return this.data.game; }\n get tier(): 'premium' | 'standard' | 'starter' { return this.data.tier; }\n get season(): string { return this.data.season; }\n get description(): string { return this.data.description; }\n get drivers(): number { return this.data.drivers; }\n get races(): number { return this.data.races; }\n get completedRaces(): number { return this.data.completedRaces; }\n get totalImpressions(): number { return this.data.totalImpressions; }\n get avgViewsPerRace(): number { return this.data.avgViewsPerRace; }\n get engagement(): number { return this.data.engagement; }\n get rating(): number { return this.data.rating; }\n get seasonStatus(): 'active' | 'upcoming' | 'completed' { return this.data.seasonStatus; }\n get seasonDates() { return this.data.seasonDates; }\n get nextRace() { return this.data.nextRace; }\n get sponsorSlots() { return this.data.sponsorSlots; }\n\n get formattedTotalImpressions(): string {\n // Client-only formatting\n return this.totalImpressions.toLocaleString();\n }\n\n get formattedAvgViewsPerRace(): string {\n // Client-only formatting\n return this.avgViewsPerRace.toLocaleString();\n }\n\n get projectedTotalViews(): number {\n return Math.round(this.avgViewsPerRace * this.races);\n }\n\n get formattedProjectedTotal(): string {\n // Client-only formatting\n return this.projectedTotalViews.toLocaleString();\n }\n\n get mainSponsorCpm(): number {\n return Math.round((this.sponsorSlots.main.price / this.projectedTotalViews) * 1000);\n }\n\n get formattedMainSponsorCpm(): string {\n return CurrencyDisplay.format(this.mainSponsorCpm);\n }\n\n get racesLeft(): number {\n return this.races - this.completedRaces;\n }\n\n get tierConfig() {\n return LeagueTierDisplay.getDisplay(this.tier);\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueJoinRequestViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":1,"endColumn":88},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/LeagueJoinRequestDTO","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":88},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":35},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":35},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":13,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":19,"endColumn":2}],"suppressedMessages":[],"errorCount":5,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { LeagueJoinRequestDTO } from '@/lib/types/generated/LeagueJoinRequestDTO';\n\n/**\n * League join request view model\n * Transform from DTO to ViewModel with UI fields\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueJoinRequest\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueJoinRequestViewData {\n id: string;\n leagueId: string;\n driverId: string;\n requestedAt: string;\n isAdmin: boolean;\n}\n\nexport class LeagueJoinRequestViewModel extends ViewModel {\n private readonly data: LeagueJoinRequestViewData;\n\n constructor(data: LeagueJoinRequestViewData) {\n super();\n this.data = data;\n }\n\n get id(): string { return this.data.id; }\n get leagueId(): string { return this.data.leagueId; }\n get driverId(): string { return this.data.driverId; }\n get requestedAt(): string { return this.data.requestedAt; }\n\n /** UI-specific: Formatted request date */\n get formattedRequestedAt(): string {\n return new Date(this.requestedAt).toLocaleString();\n }\n\n /** UI-specific: Whether the request can be approved by current user */\n get canApprove(): boolean {\n return this.data.isAdmin;\n }\n\n /** UI-specific: Whether the request can be rejected by current user */\n get canReject(): boolean {\n return this.data.isAdmin;\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueMemberViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":14,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\nimport { LeagueRoleDisplay, LeagueRole } from \"../display-objects/LeagueRoleDisplay\";\n\n/**\n * ViewData for LeagueMember\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueMemberViewData {\n driverId: string;\n currentUserId: string;\n driver?: any;\n role: string;\n joinedAt: string;\n}\n\nexport class LeagueMemberViewModel extends ViewModel {\n private readonly data: LeagueMemberViewData;\n\n constructor(data: LeagueMemberViewData) {\n super();\n this.data = data;\n }\n\n get driverId(): string { return this.data.driverId; }\n get driver(): any { return this.data.driver; }\n get role(): string { return this.data.role; }\n get joinedAt(): string { return this.data.joinedAt; }\n\n /** UI-specific: Formatted join date */\n get formattedJoinedAt(): string {\n // Client-only formatting\n return new Date(this.joinedAt).toLocaleDateString();\n }\n\n /** UI-specific: Badge classes for role */\n get roleBadgeClasses(): string {\n return LeagueRoleDisplay.getLeagueRoleDisplay(this.role as LeagueRole)?.badgeClasses || '';\n }\n\n /** UI-specific: Whether this member is the owner */\n get isOwner(): boolean {\n return this.role === 'owner';\n }\n\n /** UI-specific: Whether this is the current user */\n get isCurrentUser(): boolean {\n return this.driverId === this.data.currentUserId;\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueMembershipsViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":2,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":2,"endColumn":78},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/LeagueMemberDTO","line":2,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":2,"endColumn":78},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":2,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":2,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":2,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":2,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":15,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":17,"endColumn":2}],"suppressedMessages":[],"errorCount":5,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { LeagueMemberViewModel } from './LeagueMemberViewModel';\nimport type { LeagueMemberDTO } from '@/lib/types/generated/LeagueMemberDTO';\n\n/**\n * View Model for League Memberships\n *\n * Represents the league's memberships in a UI-ready format.\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueMemberships\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueMembershipsViewData {\n memberships: any[];\n}\n\nexport class LeagueMembershipsViewModel extends ViewModel {\n private readonly data: LeagueMembershipsViewData;\n readonly memberships: LeagueMemberViewModel[];\n\n constructor(data: LeagueMembershipsViewData) {\n super();\n this.data = data;\n this.memberships = data.memberships.map((m) => new LeagueMemberViewModel(m));\n }\n\n /** UI-specific: Number of members */\n get memberCount(): number {\n return this.memberships.length;\n }\n\n /** UI-specific: Whether the league has members */\n get hasMembers(): boolean {\n return this.memberCount > 0;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeaguePageDetailViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":16,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * League Page Detail View Model\n *\n * View model for league page details.\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface LeaguePageDetailViewData {\n id: string;\n name: string;\n description: string;\n ownerId: string;\n ownerName: string;\n isAdmin: boolean;\n mainSponsor: { name: string; logoUrl: string; websiteUrl: string } | null;\n}\n\nexport class LeaguePageDetailViewModel extends ViewModel {\n id: string;\n name: string;\n description: string;\n ownerId: string;\n ownerName: string;\n isAdmin: boolean;\n mainSponsor: { name: string; logoUrl: string; websiteUrl: string } | null;\n\n constructor(data: LeaguePageDetailViewData) {\n super();\n this.id = data.id;\n this.name = data.name;\n this.description = data.description;\n this.ownerId = data.ownerId;\n this.ownerName = data.ownerName;\n this.isAdmin = data.isAdmin;\n this.mainSponsor = data.mainSponsor;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueScheduleViewModel.ts","messages":[{"ruleId":"import/no-duplicates","severity":1,"message":"'/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/contracts/view-models/ViewModel.ts' imported multiple times.","line":6,"column":27,"nodeType":"Literal","endLine":6,"endColumn":63},{"ruleId":"import/no-duplicates","severity":1,"message":"'/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/contracts/view-models/ViewModel.ts' imported multiple times.","line":23,"column":27,"nodeType":"Literal","endLine":23,"endColumn":63},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":29,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":31,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * View Model for League Schedule\n *\n * Service layer maps DTOs into these shapes; UI consumes ViewModels only.\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface LeagueScheduleRaceViewModel extends ViewModel {\n id: string;\n name: string;\n scheduledAt: Date;\n formattedDate: string;\n formattedTime: string;\n isPast: boolean;\n isUpcoming: boolean;\n status: string;\n track?: string;\n car?: string;\n sessionType?: string;\n isRegistered?: boolean;\n}\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueSchedule\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueScheduleViewData {\n races: any[];\n}\n\nexport class LeagueScheduleViewModel extends ViewModel {\n private readonly data: LeagueScheduleViewData;\n readonly races: LeagueScheduleRaceViewModel[];\n\n constructor(data: LeagueScheduleViewData) {\n super();\n this.data = data;\n this.races = data.races;\n }\n\n get raceCount(): number {\n return this.races.length;\n }\n\n get hasRaces(): boolean {\n return this.raceCount > 0;\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueScoringChampionshipViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueScoringConfigViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":7,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":12,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueScoringConfig\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueScoringConfigViewData {\n gameName: string;\n scoringPresetName?: string;\n dropPolicySummary?: string;\n championships?: any[];\n}\n\nexport class LeagueScoringConfigViewModel extends ViewModel {\n private readonly data: LeagueScoringConfigViewData;\n\n constructor(data: LeagueScoringConfigViewData) {\n super();\n this.data = data;\n }\n\n get gameName(): string { return this.data.gameName; }\n get scoringPresetName(): string | undefined { return this.data.scoringPresetName; }\n get dropPolicySummary(): string | undefined { return this.data.dropPolicySummary; }\n get championships(): any[] | undefined { return this.data.championships; }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueScoringPresetViewModel.ts","messages":[{"ruleId":"import/no-duplicates","severity":1,"message":"'/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/contracts/view-models/ViewModel.ts' imported multiple times.","line":1,"column":27,"nodeType":"Literal","endLine":1,"endColumn":63},{"ruleId":"import/no-duplicates","severity":1,"message":"'/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/contracts/view-models/ViewModel.ts' imported multiple times.","line":24,"column":27,"nodeType":"Literal","endLine":24,"endColumn":63}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport type LeagueScoringPresetTimingDefaultsViewModel = ViewModel & {\n practiceMinutes: number;\n qualifyingMinutes: number;\n sprintRaceMinutes: number;\n mainRaceMinutes: number;\n sessionCount: number;\n};\n\nexport type LeagueScoringPresetViewModelInput = {\n id: string;\n name: string;\n sessionSummary: string;\n bonusSummary?: string;\n defaultTimings: LeagueScoringPresetTimingDefaultsViewModel;\n};\n\n/**\n * LeagueScoringPresetViewModel\n *\n * View model for league scoring preset configuration\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport class LeagueScoringPresetViewModel extends ViewModel {\n readonly id: string;\n readonly name: string;\n readonly sessionSummary: string;\n readonly bonusSummary?: string;\n readonly defaultTimings: LeagueScoringPresetTimingDefaultsViewModel;\n\n constructor(input: LeagueScoringPresetViewModelInput) {\n this.id = input.id;\n this.name = input.name;\n this.sessionSummary = input.sessionSummary;\n this.bonusSummary = input.bonusSummary;\n this.defaultTimings = input.defaultTimings;\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueScoringPresetsViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":6,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":9,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * View Model for league scoring presets\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface LeagueScoringPresetsViewData {\n presets: any[];\n totalCount?: number;\n}\n\nexport class LeagueScoringPresetsViewModel extends ViewModel {\n presets: any[];\n totalCount: number;\n\n constructor(data: LeagueScoringPresetsViewData) {\n super();\n this.presets = data.presets;\n this.totalCount = data.totalCount ?? data.presets.length;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueScoringSectionViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/LeagueConfigFormModel","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":80}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { LeagueConfigFormModel } from '@/lib/types/LeagueConfigFormModel';\nimport type { LeagueScoringPresetViewModel } from '@/lib/view-models/LeagueScoringPresetViewModel';\nimport type { CustomPointsConfig } from '@/lib/view-models/ScoringConfigurationViewModel';\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * LeagueScoringSectionViewModel\n *\n * View model for the league scoring section UI state and operations\n */\nexport class LeagueScoringSectionViewModel extends ViewModel {\n readonly form: LeagueConfigFormModel;\n readonly presets: LeagueScoringPresetViewModel[];\n readonly readOnly: boolean;\n readonly patternOnly: boolean;\n readonly championshipsOnly: boolean;\n readonly disabled: boolean;\n readonly currentPreset: LeagueScoringPresetViewModel | null;\n readonly isCustom: boolean;\n\n constructor(\n form: LeagueConfigFormModel,\n presets: LeagueScoringPresetViewModel[],\n options: {\n readOnly?: boolean;\n patternOnly?: boolean;\n championshipsOnly?: boolean;\n } = {}\n ) {\n super();\n this.form = form;\n this.presets = presets;\n this.readOnly = options.readOnly || false;\n this.patternOnly = options.patternOnly || false;\n this.championshipsOnly = options.championshipsOnly || false;\n this.disabled = this.readOnly;\n this.currentPreset = form.scoring.patternId \n ? presets.find(p => p.id === form.scoring.patternId) || null\n : null;\n this.isCustom = form.scoring.customScoringEnabled || false;\n }\n\n /**\n * Get default custom points configuration\n */\n static getDefaultCustomPoints(): CustomPointsConfig {\n return {\n racePoints: [25, 18, 15, 12, 10, 8, 6, 4, 2, 1],\n poleBonusPoints: 1,\n fastestLapPoints: 1,\n leaderLapPoints: 0,\n };\n }\n\n /**\n * Check if form can be modified\n */\n canModify(): boolean {\n return !this.readOnly;\n }\n\n /**\n * Get available presets for display\n */\n getAvailablePresets(): LeagueScoringPresetViewModel[] {\n return this.presets;\n }\n\n /**\n * Get championships configuration for display\n */\n getChampionshipsConfig() {\n const isTeamsMode = this.form.structure.mode === 'fixedTeams';\n \n return [\n {\n key: 'enableDriverChampionship' as const,\n label: 'Driver Standings',\n description: 'Track individual driver points',\n enabled: this.form.championships.enableDriverChampionship,\n available: true,\n },\n {\n key: 'enableTeamChampionship' as const,\n label: 'Team Standings',\n description: 'Combined team points',\n enabled: this.form.championships.enableTeamChampionship,\n available: isTeamsMode,\n unavailableHint: 'Teams mode only',\n },\n {\n key: 'enableNationsChampionship' as const,\n label: 'Nations Cup',\n description: 'By nationality',\n enabled: this.form.championships.enableNationsChampionship,\n available: true,\n },\n {\n key: 'enableTrophyChampionship' as const,\n label: 'Trophy Cup',\n description: 'Special category',\n enabled: this.form.championships.enableTrophyChampionship,\n available: true,\n },\n ];\n }\n\n /**\n * Get panel visibility based on flags\n */\n shouldShowPatternPanel(): boolean {\n return !this.championshipsOnly;\n }\n\n shouldShowChampionshipsPanel(): boolean {\n return !this.patternOnly;\n }\n\n /**\n * Get the active custom points configuration\n */\n getActiveCustomPoints(): CustomPointsConfig {\n // This would be stored separately in the form model\n // For now, return defaults\n return LeagueScoringSectionViewModel.getDefaultCustomPoints();\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueSeasonSummaryViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueSettingsViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/LeagueConfigFormModel","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":80},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":2,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":2,"endColumn":92},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/LeagueScoringPresetDTO","line":2,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":2,"endColumn":92},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":2,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":2,"endColumn":37},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":2,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":2,"endColumn":37},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":15,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":26,"endColumn":2},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":23,"column":12,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":23,"endColumn":34}],"suppressedMessages":[],"errorCount":7,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { LeagueConfigFormModel } from '@/lib/types/LeagueConfigFormModel';\nimport type { LeagueScoringPresetDTO } from '@/lib/types/generated/LeagueScoringPresetDTO';\nimport { DriverSummaryViewModel } from './DriverSummaryViewModel';\n\n/**\n * View Model for league settings page\n * Combines league config, presets, owner, and members\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueSettings\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueSettingsViewData {\n league: {\n id: string;\n name: string;\n ownerId: string;\n createdAt: string;\n };\n config: LeagueConfigFormModel;\n presets: LeagueScoringPresetDTO[];\n owner: any | null;\n members: any[];\n}\n\nexport class LeagueSettingsViewModel extends ViewModel {\n private readonly data: LeagueSettingsViewData;\n readonly owner: DriverSummaryViewModel | null;\n readonly members: DriverSummaryViewModel[];\n\n constructor(data: LeagueSettingsViewData) {\n super();\n this.data = data;\n this.owner = data.owner ? new DriverSummaryViewModel(data.owner) : null;\n this.members = data.members.map(m => new DriverSummaryViewModel(m));\n }\n\n get league() { return this.data.league; }\n get config() { return this.data.config; }\n get presets() { return this.data.presets; }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueStandingsViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":12,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { StandingEntryViewModel, StandingEntryViewData } from './StandingEntryViewModel';\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueStandings\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueStandingsViewData {\n standings: StandingEntryViewData[];\n drivers: any[];\n memberships: any[];\n}\n\nexport class LeagueStandingsViewModel extends ViewModel {\n private readonly data: LeagueStandingsViewData;\n readonly standings: StandingEntryViewModel[];\n\n constructor(data: LeagueStandingsViewData) {\n super();\n this.data = data;\n this.standings = data.standings.map(s => new StandingEntryViewModel(s));\n }\n\n get drivers(): any[] { return this.data.drivers; }\n get memberships(): any[] { return this.data.memberships; }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueStatsViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":12,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":14,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * View Model for League Statistics\n *\n * Represents the total number of leagues in a UI-ready format.\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueStats\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueStatsViewData {\n totalLeagues: number;\n}\n\nexport class LeagueStatsViewModel extends ViewModel {\n private readonly data: LeagueStatsViewData;\n\n constructor(data: LeagueStatsViewData) {\n super();\n this.data = data;\n }\n\n get totalLeagues(): number { return this.data.totalLeagues; }\n\n /** UI-specific: Formatted total leagues display */\n get formattedTotalLeagues(): string {\n // Client-only formatting\n return this.totalLeagues.toLocaleString();\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueStewardingViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":11,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":14,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * League Stewarding View Model\n * Represents all data needed for league stewarding across all races\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueStewarding\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueStewardingViewData {\n racesWithData: RaceWithProtests[];\n driverMap: Record;\n}\n\nexport class LeagueStewardingViewModel extends ViewModel {\n private readonly data: LeagueStewardingViewData;\n\n constructor(data: LeagueStewardingViewData) {\n super();\n this.data = data;\n }\n\n get racesWithData(): RaceWithProtests[] { return this.data.racesWithData; }\n get driverMap() { return this.data.driverMap; }\n\n /** UI-specific: Total pending protests count */\n get totalPending(): number {\n return this.racesWithData.reduce((sum, r) => sum + r.pendingProtests.length, 0);\n }\n\n /** UI-specific: Total resolved protests count */\n get totalResolved(): number {\n return this.racesWithData.reduce((sum, r) => sum + r.resolvedProtests.length, 0);\n }\n\n /** UI-specific: Total penalties count */\n get totalPenalties(): number {\n return this.racesWithData.reduce((sum, r) => sum + r.penalties.length, 0);\n }\n\n /** UI-specific: Filtered races for pending tab */\n get pendingRaces(): RaceWithProtests[] {\n return this.racesWithData.filter(r => r.pendingProtests.length > 0);\n }\n\n /** UI-specific: Filtered races for history tab */\n get historyRaces(): RaceWithProtests[] {\n return this.racesWithData.filter(r => r.resolvedProtests.length > 0 || r.penalties.length > 0);\n }\n\n /** UI-specific: All drivers for quick penalty modal */\n get allDrivers(): Array<{ id: string; name: string; avatarUrl?: string; iracingId?: string; rating?: number }> {\n return Object.values(this.driverMap);\n }\n}\n\nexport interface RaceWithProtests {\n race: {\n id: string;\n track: string;\n scheduledAt: Date;\n };\n pendingProtests: Protest[];\n resolvedProtests: Protest[];\n penalties: Penalty[];\n}\n\nexport interface Protest {\n id: string;\n protestingDriverId: string;\n accusedDriverId: string;\n incident: {\n lap: number;\n description: string;\n };\n filedAt: string;\n status: string;\n decisionNotes?: string;\n proofVideoUrl?: string;\n}\n\nexport interface Penalty {\n id: string;\n driverId: string;\n type: string;\n value: number;\n reason: string;\n notes?: string;\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueSummaryViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":7,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":33,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for LeagueSummary\n * This is the JSON-serializable input for the Template.\n */\nexport interface LeagueSummaryViewData {\n id: string;\n name: string;\n description: string | null;\n logoUrl: string | null;\n ownerId: string;\n createdAt: string;\n maxDrivers: number;\n usedDriverSlots: number;\n activeDriversCount?: number;\n nextRaceAt?: string;\n maxTeams?: number;\n usedTeamSlots?: number;\n structureSummary: string;\n scoringPatternSummary?: string;\n timingSummary: string;\n category?: string | null;\n scoring?: {\n gameId: string;\n gameName: string;\n primaryChampionshipType: 'driver' | 'team' | 'nations' | 'trophy';\n scoringPresetId: string;\n scoringPresetName: string;\n dropPolicySummary: string;\n scoringPatternSummary: string;\n };\n}\n\nexport class LeagueSummaryViewModel extends ViewModel {\n private readonly data: LeagueSummaryViewData;\n\n constructor(data: LeagueSummaryViewData) {\n super();\n this.data = data;\n }\n\n get id(): string { return this.data.id; }\n get name(): string { return this.data.name; }\n get description(): string | null { return this.data.description; }\n get logoUrl(): string | null { return this.data.logoUrl; }\n get ownerId(): string { return this.data.ownerId; }\n get createdAt(): string { return this.data.createdAt; }\n get maxDrivers(): number { return this.data.maxDrivers; }\n get usedDriverSlots(): number { return this.data.usedDriverSlots; }\n get activeDriversCount(): number | undefined { return this.data.activeDriversCount; }\n get nextRaceAt(): string | undefined { return this.data.nextRaceAt; }\n get maxTeams(): number | undefined { return this.data.maxTeams; }\n get usedTeamSlots(): number | undefined { return this.data.usedTeamSlots; }\n get structureSummary(): string { return this.data.structureSummary; }\n get scoringPatternSummary(): string | undefined { return this.data.scoringPatternSummary; }\n get timingSummary(): string { return this.data.timingSummary; }\n get category(): string | null | undefined { return this.data.category; }\n get scoring() { return this.data.scoring; }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/LeagueWalletViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":16,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":26,"endColumn":4},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":28,"column":20,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":28,"endColumn":23},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":29,"column":21,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":29,"endColumn":24},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":30,"column":25,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":30,"endColumn":28},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":31,"column":22,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":31,"endColumn":25},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":32,"column":29,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":32,"endColumn":32},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":33,"column":27,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":33,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":34,"column":25,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":34,"endColumn":28},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":35,"column":24,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":35,"endColumn":27},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":36,"column":34,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":36,"endColumn":37}],"suppressedMessages":[],"errorCount":10,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { WalletTransactionViewModel } from './WalletTransactionViewModel';\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport class LeagueWalletViewModel extends ViewModel {\n balance: number;\n currency: string;\n totalRevenue: number;\n totalFees: number;\n totalWithdrawals: number;\n pendingPayouts: number;\n transactions: WalletTransactionViewModel[];\n canWithdraw: boolean;\n withdrawalBlockReason?: string;\n\n constructor(dto: {\n balance: number;\n currency: string;\n totalRevenue: number;\n totalFees: number;\n totalWithdrawals: number;\n pendingPayouts: number;\n transactions: WalletTransactionViewModel[];\n canWithdraw: boolean;\n withdrawalBlockReason?: string;\n }) {\n super();\n this.balance = dto.balance;\n this.currency = dto.currency;\n this.totalRevenue = dto.totalRevenue;\n this.totalFees = dto.totalFees;\n this.totalWithdrawals = dto.totalWithdrawals;\n this.pendingPayouts = dto.pendingPayouts;\n this.transactions = dto.transactions;\n this.canWithdraw = dto.canWithdraw;\n this.withdrawalBlockReason = dto.withdrawalBlockReason;\n }\n\n /** UI-specific: Formatted balance */\n get formattedBalance(): string {\n return `$${this.balance.toFixed(2)}`;\n }\n\n /** UI-specific: Formatted total revenue */\n get formattedTotalRevenue(): string {\n return `$${this.totalRevenue.toFixed(2)}`;\n }\n\n /** UI-specific: Formatted total fees */\n get formattedTotalFees(): string {\n return `$${this.totalFees.toFixed(2)}`;\n }\n\n /** UI-specific: Formatted pending payouts */\n get formattedPendingPayouts(): string {\n return `$${this.pendingPayouts.toFixed(2)}`;\n }\n\n /** UI-specific: Filtered transactions by type */\n getFilteredTransactions(type: 'all' | 'sponsorship' | 'membership' | 'withdrawal' | 'prize'): WalletTransactionViewModel[] {\n return type === 'all' ? this.transactions : this.transactions.filter(t => t.type === type);\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/MediaViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":5,"column":1,"nodeType":"TSTypeAliasDeclaration","messageId":"noViewDataDefinition","endLine":5,"endColumn":59}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { MediaViewData } from '@/lib/view-data/MediaViewData';\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\ntype MediaAssetViewData = MediaViewData['assets'][number];\n\n/**\n * Media View Model\n *\n * Client-only ViewModel created from ViewData (never DTO).\n * Represents a single media asset card in the UI.\n */\nexport class MediaViewModel extends ViewModel {\n private readonly data: MediaAssetViewData;\n\n constructor(data: MediaAssetViewData) {\n super();\n this.data = data;\n }\n\n get id(): string { return this.data.id; }\n get src(): string { return this.data.src; }\n get title(): string { return this.data.title; }\n get category(): string { return this.data.category; }\n get date(): string | undefined { return this.data.date; }\n get dimensions(): string | undefined { return this.data.dimensions; }\n\n /** UI-specific: Combined subtitle used by MediaCard */\n get subtitle(): string {\n return `${this.category}${this.dimensions ? ` • ${this.dimensions}` : ''}`;\n }\n\n /** UI-specific: Whether any metadata is present */\n get hasMetadata(): boolean {\n return !!this.date || !!this.dimensions;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/MembershipFeeViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":17,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\nimport { CurrencyDisplay } from \"../display-objects/CurrencyDisplay\";\n\n/**\n * ViewData for MembershipFee\n * This is the JSON-serializable input for the Template.\n */\nexport interface MembershipFeeViewData {\n id: string;\n leagueId: string;\n seasonId?: string;\n type: string;\n amount: number;\n enabled: boolean;\n createdAt: string;\n updatedAt: string;\n}\n\nexport class MembershipFeeViewModel extends ViewModel {\n private readonly data: MembershipFeeViewData;\n\n constructor(data: MembershipFeeViewData) {\n super();\n this.data = data;\n }\n\n get id(): string { return this.data.id; }\n get leagueId(): string { return this.data.leagueId; }\n get seasonId(): string | undefined { return this.data.seasonId; }\n get type(): string { return this.data.type; }\n get amount(): number { return this.data.amount; }\n get enabled(): boolean { return this.data.enabled; }\n get createdAt(): string { return this.data.createdAt; }\n get updatedAt(): string { return this.data.updatedAt; }\n\n /** UI-specific: Formatted amount */\n get formattedAmount(): string {\n return CurrencyDisplay.format(this.amount, 'EUR');\n }\n\n /** UI-specific: Type display */\n get typeDisplay(): string {\n switch (this.type) {\n case 'season': return 'Per Season';\n case 'monthly': return 'Monthly';\n case 'per_race': return 'Per Race';\n default: return this.type;\n }\n }\n\n /** UI-specific: Status display */\n get statusDisplay(): string {\n return this.enabled ? 'Enabled' : 'Disabled';\n }\n\n /** UI-specific: Status color */\n get statusColor(): string {\n return this.enabled ? 'green' : 'red';\n }\n\n /** UI-specific: Formatted created date */\n get formattedCreatedAt(): string {\n return this.createdAt.toLocaleString();\n }\n\n /** UI-specific: Formatted updated date */\n get formattedUpdatedAt(): string {\n return this.updatedAt.toLocaleString();\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/NotificationSettingsViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":3,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":10,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface NotificationSettingsViewData {\n emailNewSponsorships: boolean;\n emailWeeklyReport: boolean;\n emailRaceAlerts: boolean;\n emailPaymentAlerts: boolean;\n emailNewOpportunities: boolean;\n emailContractExpiry: boolean;\n}\n\nexport class NotificationSettingsViewModel extends ViewModel {\n emailNewSponsorships: boolean;\n emailWeeklyReport: boolean;\n emailRaceAlerts: boolean;\n emailPaymentAlerts: boolean;\n emailNewOpportunities: boolean;\n emailContractExpiry: boolean;\n\n constructor(data: NotificationSettingsViewData) {\n super();\n this.emailNewSponsorships = data.emailNewSponsorships;\n this.emailWeeklyReport = data.emailWeeklyReport;\n this.emailRaceAlerts = data.emailRaceAlerts;\n this.emailPaymentAlerts = data.emailPaymentAlerts;\n this.emailNewOpportunities = data.emailNewOpportunities;\n this.emailContractExpiry = data.emailContractExpiry;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/OnboardingViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":7,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":9,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for Onboarding\n * This is the JSON-serializable input for the Template.\n */\nexport interface OnboardingViewData {\n isAlreadyOnboarded: boolean;\n}\n\nexport class OnboardingViewModel extends ViewModel {\n private readonly data: OnboardingViewData;\n\n constructor(data: OnboardingViewData) {\n super();\n this.data = data;\n }\n\n get isAlreadyOnboarded(): boolean {\n return this.data.isAlreadyOnboarded;\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/PaymentMethodViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/PaymentViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":21,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\nimport { CurrencyDisplay } from \"../display-objects/CurrencyDisplay\";\n\n/**\n * ViewData for Payment\n * This is the JSON-serializable input for the Template.\n */\nexport interface PaymentViewData {\n id: string;\n type: string;\n amount: number;\n platformFee: number;\n netAmount: number;\n payerId: string;\n payerType: string;\n leagueId: string;\n seasonId?: string;\n status: string;\n createdAt: string;\n completedAt?: string;\n}\n\nexport class PaymentViewModel extends ViewModel {\n private readonly data: PaymentViewData;\n\n constructor(data: PaymentViewData) {\n super();\n this.data = data;\n }\n\n get id(): string { return this.data.id; }\n get type(): string { return this.data.type; }\n get amount(): number { return this.data.amount; }\n get platformFee(): number { return this.data.platformFee; }\n get netAmount(): number { return this.data.netAmount; }\n get payerId(): string { return this.data.payerId; }\n get payerType(): string { return this.data.payerType; }\n get leagueId(): string { return this.data.leagueId; }\n get seasonId(): string | undefined { return this.data.seasonId; }\n get status(): string { return this.data.status; }\n get createdAt(): string { return this.data.createdAt; }\n get completedAt(): string | undefined { return this.data.completedAt; }\n\n /** UI-specific: Formatted amount */\n get formattedAmount(): string {\n return CurrencyDisplay.format(this.amount, 'EUR');\n }\n\n /** UI-specific: Formatted net amount */\n get formattedNetAmount(): string {\n return CurrencyDisplay.format(this.netAmount, 'EUR');\n }\n\n /** UI-specific: Status color */\n get statusColor(): string {\n switch (this.status) {\n case 'completed': return 'green';\n case 'pending': return 'yellow';\n case 'failed': return 'red';\n case 'refunded': return 'orange';\n default: return 'gray';\n }\n }\n\n /** UI-specific: Formatted created date */\n get formattedCreatedAt(): string {\n return this.createdAt.toLocaleString();\n }\n\n /** UI-specific: Formatted completed date */\n get formattedCompletedAt(): string {\n return this.completedAt ? this.completedAt.toLocaleString() : 'Not completed';\n }\n\n /** UI-specific: Status display */\n get statusDisplay(): string {\n return this.status.charAt(0).toUpperCase() + this.status.slice(1);\n }\n\n /** UI-specific: Type display */\n get typeDisplay(): string {\n return this.type.replace('_', ' ').replace(/\\b\\w/g, l => l.toUpperCase());\n }\n\n /** UI-specific: Payer type display */\n get payerTypeDisplay(): string {\n return this.payerType.charAt(0).toUpperCase() + this.payerType.slice(1);\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/PrivacySettingsViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":3,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":8,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface PrivacySettingsViewData {\n publicProfile: boolean;\n showStats: boolean;\n showActiveSponsorships: boolean;\n allowDirectContact: boolean;\n}\n\nexport class PrivacySettingsViewModel extends ViewModel {\n publicProfile: boolean;\n showStats: boolean;\n showActiveSponsorships: boolean;\n allowDirectContact: boolean;\n\n constructor(data: PrivacySettingsViewData) {\n super();\n this.publicProfile = data.publicProfile;\n this.showStats = data.showStats;\n this.showActiveSponsorships = data.showActiveSponsorships;\n this.allowDirectContact = data.allowDirectContact;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/PrizeViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":9,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":22,"endColumn":2},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":19,"column":3,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":19,"endColumn":12},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":41,"column":7,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":41,"endColumn":16},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":41,"column":58,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":41,"endColumn":67}],"suppressedMessages":[],"errorCount":4,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\nimport { CurrencyDisplay } from \"../display-objects/CurrencyDisplay\";\nimport { FinishDisplay } from \"../display-objects/FinishDisplay\";\n\n/**\n * ViewData for Prize\n * This is the JSON-serializable input for the Template.\n */\nexport interface PrizeViewData {\n id: string;\n leagueId: string;\n seasonId: string;\n position: number;\n name: string;\n amount: number;\n type: string;\n description?: string;\n awarded: boolean;\n awardedTo?: string;\n awardedAt?: string;\n createdAt: string;\n}\n\nexport class PrizeViewModel extends ViewModel {\n private readonly data: PrizeViewData;\n\n constructor(data: PrizeViewData) {\n super();\n this.data = data;\n }\n\n get id(): string { return this.data.id; }\n get leagueId(): string { return this.data.leagueId; }\n get seasonId(): string { return this.data.seasonId; }\n get position(): number { return this.data.position; }\n get name(): string { return this.data.name; }\n get amount(): number { return this.data.amount; }\n get type(): string { return this.data.type; }\n get description(): string | undefined { return this.data.description; }\n get awarded(): boolean { return this.data.awarded; }\n get awardedTo(): string | undefined { return this.data.awardedTo; }\n get awardedAt(): string | undefined { return this.data.awardedAt; }\n get createdAt(): string { return this.data.createdAt; }\n\n /** UI-specific: Formatted amount */\n get formattedAmount(): string {\n return CurrencyDisplay.format(this.amount, 'EUR');\n }\n\n /** UI-specific: Position display */\n get positionDisplay(): string {\n return FinishDisplay.format(this.position);\n }\n\n /** UI-specific: Type display */\n get typeDisplay(): string {\n switch (this.type) {\n case 'cash': return 'Cash Prize';\n case 'merchandise': return 'Merchandise';\n case 'other': return 'Other';\n default: return this.type;\n }\n }\n\n /** UI-specific: Status display */\n get statusDisplay(): string {\n return this.awarded ? 'Awarded' : 'Available';\n }\n\n /** UI-specific: Status color */\n get statusColor(): string {\n return this.awarded ? 'green' : 'blue';\n }\n\n /** UI-specific: Prize description */\n get prizeDescription(): string {\n return `${this.name} - ${this.formattedAmount}`;\n }\n\n /** UI-specific: Formatted awarded date */\n get formattedAwardedAt(): string {\n return this.awardedAt ? this.awardedAt.toLocaleString() : 'Not awarded';\n }\n\n /** UI-specific: Formatted created date */\n get formattedCreatedAt(): string {\n return this.createdAt.toLocaleString();\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/ProfileOverviewViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":99,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":106,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface ProfileOverviewDriverSummaryViewModel extends ViewModel {\n id: string;\n name: string;\n country: string;\n avatarUrl: string;\n iracingId: string | null;\n joinedAt: string;\n rating: number | null;\n globalRank: number | null;\n consistency: number | null;\n bio: string | null;\n totalDrivers: number | null;\n}\n\nexport interface ProfileOverviewStatsViewModel extends ViewModel {\n totalRaces: number;\n wins: number;\n podiums: number;\n dnfs: number;\n avgFinish: number | null;\n bestFinish: number | null;\n worstFinish: number | null;\n finishRate: number | null;\n winRate: number | null;\n podiumRate: number | null;\n percentile: number | null;\n rating: number | null;\n consistency: number | null;\n overallRank: number | null;\n}\n\nexport interface ProfileOverviewFinishDistributionViewModel extends ViewModel {\n totalRaces: number;\n wins: number;\n podiums: number;\n topTen: number;\n dnfs: number;\n other: number;\n}\n\nexport interface ProfileOverviewTeamMembershipViewModel extends ViewModel {\n teamId: string;\n teamName: string;\n teamTag: string | null;\n role: string;\n joinedAt: string;\n isCurrent: boolean;\n}\n\nexport interface ProfileOverviewSocialFriendSummaryViewModel extends ViewModel {\n id: string;\n name: string;\n country: string;\n avatarUrl: string;\n}\n\nexport interface ProfileOverviewSocialSummaryViewModel extends ViewModel {\n friendsCount: number;\n friends: ProfileOverviewSocialFriendSummaryViewModel[];\n}\n\nexport type ProfileOverviewSocialPlatform = 'twitter' | 'youtube' | 'twitch' | 'discord';\n\nexport type ProfileOverviewAchievementRarity = 'common' | 'rare' | 'epic' | 'legendary';\n\nexport interface ProfileOverviewAchievementViewModel extends ViewModel {\n id: string;\n title: string;\n description: string;\n icon: 'trophy' | 'medal' | 'star' | 'crown' | 'target' | 'zap';\n rarity: ProfileOverviewAchievementRarity;\n earnedAt: string;\n}\n\nexport interface ProfileOverviewSocialHandleViewModel extends ViewModel {\n platform: ProfileOverviewSocialPlatform;\n handle: string;\n url: string;\n}\n\nexport interface ProfileOverviewExtendedProfileViewModel extends ViewModel {\n socialHandles: ProfileOverviewSocialHandleViewModel[];\n achievements: ProfileOverviewAchievementViewModel[];\n racingStyle: string;\n favoriteTrack: string;\n favoriteCar: string;\n timezone: string;\n availableHours: string;\n lookingForTeam: boolean;\n openToRequests: boolean;\n}\n\n/**\n * ViewData for ProfileOverview\n * This is the JSON-serializable input for the Template.\n */\nexport interface ProfileOverviewViewData {\n currentDriver: any | null;\n stats: any | null;\n finishDistribution: any | null;\n teamMemberships: any[];\n socialSummary: any;\n extendedProfile: any | null;\n}\n\nexport class ProfileOverviewViewModel extends ViewModel {\n private readonly data: ProfileOverviewViewData;\n\n constructor(data: ProfileOverviewViewData) {\n super();\n this.data = data;\n }\n\n get currentDriver(): ProfileOverviewDriverSummaryViewModel | null { return this.data.currentDriver; }\n get stats(): ProfileOverviewStatsViewModel | null { return this.data.stats; }\n get finishDistribution(): ProfileOverviewFinishDistributionViewModel | null { return this.data.finishDistribution; }\n get teamMemberships(): ProfileOverviewTeamMembershipViewModel[] { return this.data.teamMemberships; }\n get socialSummary(): ProfileOverviewSocialSummaryViewModel { return this.data.socialSummary; }\n get extendedProfile(): ProfileOverviewExtendedProfileViewModel | null { return this.data.extendedProfile; }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/ProtestDetailViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/ProtestDriverViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":3,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":6,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface ProtestDriverViewData {\n id: string;\n name: string;\n}\n\nexport class ProtestDriverViewModel extends ViewModel {\n constructor(private readonly data: ProtestDriverViewData) {\n super();\n }\n\n get id(): string {\n return this.data.id;\n }\n\n get name(): string {\n return this.data.name;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/ProtestViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RaceDetailEntryViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":1,"endColumn":79},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/RaceDetailEntryDTO","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":79},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":28},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":28},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":9,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":16,"endColumn":2}],"suppressedMessages":[],"errorCount":5,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { RaceDetailEntryDTO } from '@/lib/types/generated/RaceDetailEntryDTO';\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for RaceDetailEntry\n * This is the JSON-serializable input for the Template.\n */\nexport interface RaceDetailEntryViewData {\n id: string;\n name: string;\n country: string;\n avatarUrl: string;\n isCurrentUser: boolean;\n rating: number | null;\n}\n\nexport class RaceDetailEntryViewModel extends ViewModel {\n private readonly data: RaceDetailEntryViewData;\n\n constructor(data: RaceDetailEntryViewData) {\n super();\n this.data = data;\n }\n\n get id(): string { return this.data.id; }\n get name(): string { return this.data.name; }\n get country(): string { return this.data.country; }\n get avatarUrl(): string { return this.data.avatarUrl; }\n get isCurrentUser(): boolean { return this.data.isCurrentUser; }\n get rating(): number | null { return this.data.rating; }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RaceDetailUserResultViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":17,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\nimport { DurationDisplay } from \"../display-objects/DurationDisplay\";\n\n/**\n * ViewData for RaceDetailUserResult\n * This is the JSON-serializable input for the Template.\n */\nexport interface RaceDetailUserResultViewData {\n position: number;\n startPosition: number;\n incidents: number;\n fastestLap: number;\n positionChange: number;\n isPodium: boolean;\n isClean: boolean;\n ratingChange: number;\n}\n\nexport class RaceDetailUserResultViewModel extends ViewModel {\n private readonly data: RaceDetailUserResultViewData;\n\n constructor(data: RaceDetailUserResultViewData) {\n super();\n this.data = data;\n }\n\n get position(): number { return this.data.position; }\n get startPosition(): number { return this.data.startPosition; }\n get incidents(): number { return this.data.incidents; }\n get fastestLap(): number { return this.data.fastestLap; }\n get positionChange(): number { return this.data.positionChange; }\n get isPodium(): boolean { return this.data.isPodium; }\n get isClean(): boolean { return this.data.isClean; }\n get ratingChange(): number { return this.data.ratingChange; }\n\n /** UI-specific: Display for position change */\n get positionChangeDisplay(): string {\n if (this.positionChange > 0) return `+${this.positionChange}`;\n if (this.positionChange < 0) return `${this.positionChange}`;\n return '0';\n }\n\n /** UI-specific: Color for position change */\n get positionChangeColor(): string {\n if (this.positionChange > 0) return 'green';\n if (this.positionChange < 0) return 'red';\n return 'gray';\n }\n\n /** UI-specific: Whether this is the winner */\n get isWinner(): boolean {\n return this.position === 1;\n }\n\n /** UI-specific: Rating change display */\n get ratingChangeDisplay(): string {\n if (this.ratingChange > 0) return `+${this.ratingChange}`;\n return `${this.ratingChange}`;\n }\n\n /** UI-specific: Rating change color */\n get ratingChangeColor(): string {\n if (this.ratingChange > 0) return 'green';\n if (this.ratingChange < 0) return 'red';\n return 'gray';\n }\n\n /** UI-specific: Formatted lap time */\n get lapTimeFormatted(): string {\n if (this.fastestLap <= 0) return '--:--.---';\n const minutes = Math.floor(this.fastestLap / 60);\n const seconds = Math.floor(this.fastestLap % 60);\n const milliseconds = Math.floor((this.fastestLap % 1) * 1000);\n return `${minutes}:${seconds.toString().padStart(2, '0')}.${milliseconds.toString().padStart(3, '0')}`;\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RaceDetailsViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":5,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":12,"endColumn":2},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":25,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":30,"endColumn":2},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":41,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":44,"endColumn":2},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":57,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":65,"endColumn":2}],"suppressedMessages":[],"errorCount":4,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { RaceDetailEntryViewModel, RaceDetailEntryViewData } from './RaceDetailEntryViewModel';\nimport { RaceDetailUserResultViewModel, RaceDetailUserResultViewData } from './RaceDetailUserResultViewModel';\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface RaceDetailsRaceViewData {\n id: string;\n track: string;\n car: string;\n scheduledAt: string;\n status: string;\n sessionType: string;\n}\n\nexport class RaceDetailsRaceViewModel extends ViewModel {\n private readonly data: RaceDetailsRaceViewData;\n constructor(data: RaceDetailsRaceViewData) { super(); this.data = data; }\n get id(): string { return this.data.id; }\n get track(): string { return this.data.track; }\n get car(): string { return this.data.car; }\n get scheduledAt(): string { return this.data.scheduledAt; }\n get status(): string { return this.data.status; }\n get sessionType(): string { return this.data.sessionType; }\n}\n\nexport interface RaceDetailsLeagueViewData {\n id: string;\n name: string;\n description?: string | null;\n settings?: unknown;\n}\n\nexport class RaceDetailsLeagueViewModel extends ViewModel {\n private readonly data: RaceDetailsLeagueViewData;\n constructor(data: RaceDetailsLeagueViewData) { super(); this.data = data; }\n get id(): string { return this.data.id; }\n get name(): string { return this.data.name; }\n get description(): string | null | undefined { return this.data.description; }\n get settings(): unknown { return this.data.settings; }\n}\n\nexport interface RaceDetailsRegistrationViewData {\n canRegister: boolean;\n isUserRegistered: boolean;\n}\n\nexport class RaceDetailsRegistrationViewModel extends ViewModel {\n private readonly data: RaceDetailsRegistrationViewData;\n constructor(data: RaceDetailsRegistrationViewData) { super(); this.data = data; }\n get canRegister(): boolean { return this.data.canRegister; }\n get isUserRegistered(): boolean { return this.data.isUserRegistered; }\n}\n\n/**\n * ViewData for RaceDetails\n * This is the JSON-serializable input for the Template.\n */\nexport interface RaceDetailsViewData {\n race: RaceDetailsRaceViewData | null;\n league: RaceDetailsLeagueViewData | null;\n entryList: RaceDetailEntryViewData[];\n registration: RaceDetailsRegistrationViewData;\n userResult: RaceDetailUserResultViewData | null;\n canReopenRace: boolean;\n error?: string;\n}\n\nexport class RaceDetailsViewModel extends ViewModel {\n private readonly data: RaceDetailsViewData;\n readonly race: RaceDetailsRaceViewModel | null;\n readonly league: RaceDetailsLeagueViewModel | null;\n readonly entryList: RaceDetailEntryViewModel[];\n readonly registration: RaceDetailsRegistrationViewModel;\n readonly userResult: RaceDetailUserResultViewModel | null;\n\n constructor(data: RaceDetailsViewData) {\n super();\n this.data = data;\n this.race = data.race ? new RaceDetailsRaceViewModel(data.race) : null;\n this.league = data.league ? new RaceDetailsLeagueViewModel(data.league) : null;\n this.entryList = data.entryList.map(e => new RaceDetailEntryViewModel(e));\n this.registration = new RaceDetailsRegistrationViewModel(data.registration);\n this.userResult = data.userResult ? new RaceDetailUserResultViewModel(data.userResult) : null;\n }\n\n get canReopenRace(): boolean { return this.data.canReopenRace; }\n get error(): string | undefined { return this.data.error; }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RaceListItemViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":20,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\nimport { RaceStatusDisplay } from \"../display-objects/RaceStatusDisplay\";\n\n/**\n * ViewData for RaceListItem\n * This is the JSON-serializable input for the Template.\n */\nexport interface RaceListItemViewData {\n id: string;\n track: string;\n car: string;\n scheduledAt: string;\n status: string;\n leagueId: string;\n leagueName: string;\n strengthOfField: number | null;\n isUpcoming: boolean;\n isLive: boolean;\n isPast: boolean;\n}\n\nexport class RaceListItemViewModel extends ViewModel {\n private readonly data: RaceListItemViewData;\n\n constructor(data: RaceListItemViewData) {\n super();\n this.data = data;\n }\n\n get id(): string { return this.data.id; }\n get track(): string { return this.data.track; }\n get car(): string { return this.data.car; }\n get scheduledAt(): string { return this.data.scheduledAt; }\n get status(): string { return this.data.status; }\n get leagueId(): string { return this.data.leagueId; }\n get leagueName(): string { return this.data.leagueName; }\n get strengthOfField(): number | null { return this.data.strengthOfField; }\n get isUpcoming(): boolean { return this.data.isUpcoming; }\n get isLive(): boolean { return this.data.isLive; }\n get isPast(): boolean { return this.data.isPast; }\n\n get title(): string {\n return `${this.track} - ${this.car}`;\n }\n\n /** UI-specific: Formatted scheduled time */\n get formattedScheduledTime(): string {\n // Client-only formatting using browser locale\n return new Date(this.scheduledAt).toLocaleString();\n }\n\n /** UI-specific: Badge variant for status */\n get statusBadgeVariant(): string {\n return RaceStatusDisplay.getVariant(this.status);\n }\n\n /** UI-specific: Time until start in minutes */\n get timeUntilStart(): number {\n const now = new Date();\n const scheduled = new Date(this.scheduledAt);\n return Math.max(0, Math.floor((scheduled.getTime() - now.getTime()) / (1000 * 60)));\n }\n\n /** UI-specific: Display for time until start */\n get timeUntilStartDisplay(): string {\n const minutes = this.timeUntilStart;\n if (minutes < 60) return `${minutes}m`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h ${minutes % 60}m`;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RaceResultViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":9,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":22,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { FinishDisplay } from '../display-objects/FinishDisplay';\nimport { DurationDisplay } from '../display-objects/DurationDisplay';\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for RaceResult\n * This is the JSON-serializable input for the Template.\n */\nexport interface RaceResultViewData {\n driverId: string;\n driverName: string;\n avatarUrl: string;\n position: number;\n startPosition: number;\n incidents: number;\n fastestLap: number;\n positionChange: number;\n isPodium: boolean;\n isClean: boolean;\n id: string;\n raceId: string;\n}\n\nexport class RaceResultViewModel extends ViewModel {\n private readonly data: RaceResultViewData;\n\n constructor(data: RaceResultViewData) {\n super();\n this.data = data;\n }\n\n get driverId(): string { return this.data.driverId; }\n get driverName(): string { return this.data.driverName; }\n get avatarUrl(): string { return this.data.avatarUrl; }\n get position(): number { return this.data.position; }\n get startPosition(): number { return this.data.startPosition; }\n get incidents(): number { return this.data.incidents; }\n get fastestLap(): number { return this.data.fastestLap; }\n get positionChange(): number { return this.data.positionChange; }\n get isPodium(): boolean { return this.data.isPodium; }\n get isClean(): boolean { return this.data.isClean; }\n get id(): string { return this.data.id; }\n get raceId(): string { return this.data.raceId; }\n\n /** UI-specific: Display for position change */\n get positionChangeDisplay(): string {\n if (this.positionChange > 0) return `+${this.positionChange}`;\n if (this.positionChange < 0) return `${this.positionChange}`;\n return '0';\n }\n\n /** UI-specific: Color for position change */\n get positionChangeColor(): string {\n if (this.positionChange > 0) return 'green';\n if (this.positionChange < 0) return 'red';\n return 'gray';\n }\n\n /** UI-specific: Whether this is the winner */\n get isWinner(): boolean {\n return this.position === 1;\n }\n\n /** UI-specific: Whether has fastest lap */\n get hasFastestLap(): boolean {\n return this.fastestLap > 0;\n }\n\n /** UI-specific: Badge for position */\n get positionBadge(): string {\n return FinishDisplay.format(this.position);\n }\n\n /** UI-specific: Color for incidents badge */\n get incidentsBadgeColor(): string {\n if (this.incidents === 0) return 'green';\n if (this.incidents <= 2) return 'yellow';\n return 'red';\n }\n\n /** UI-specific: Formatted lap time */\n get lapTimeFormatted(): string {\n if (this.fastestLap <= 0) return '--:--.---';\n return DurationDisplay.formatSeconds(this.fastestLap);\n }\n\n /** Required by ResultsTable */\n getPositionChange(): number {\n return this.positionChange;\n }\n\n get formattedPosition(): string {\n return FinishDisplay.format(this.position);\n }\n\n get formattedStartPosition(): string {\n return FinishDisplay.format(this.startPosition);\n }\n\n get formattedIncidents(): string {\n return `${this.incidents}x incidents`;\n }\n\n get formattedPositionsGained(): string | undefined {\n if (this.position < this.startPosition) {\n return `+${this.startPosition - this.position} positions`;\n }\n return undefined;\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RaceResultsDetailViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":1,"endColumn":69},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/RaceResultDTO","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":69},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":23},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":23},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":2,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":2,"endColumn":83},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/RaceResultsDetailDTO","line":2,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":2,"endColumn":83},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":2,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":2,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":2,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":2,"endColumn":30},{"ruleId":"import/no-duplicates","severity":1,"message":"'/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RaceResultViewModel.ts' imported multiple times.","line":3,"column":37,"nodeType":"Literal","endLine":3,"endColumn":60,"fix":{"range":[181,339],"text":", RaceResultViewData } from './RaceResultViewModel';\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n"}},{"ruleId":"import/no-duplicates","severity":1,"message":"'/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RaceResultViewModel.ts' imported multiple times.","line":7,"column":36,"nodeType":"Literal","endLine":7,"endColumn":59},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":13,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":25,"endColumn":2}],"suppressedMessages":[],"errorCount":9,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":1,"source":"import { RaceResultDTO } from '@/lib/types/generated/RaceResultDTO';\nimport { RaceResultsDetailDTO } from '@/lib/types/generated/RaceResultsDetailDTO';\nimport { RaceResultViewModel } from './RaceResultViewModel';\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nimport { RaceResultViewData } from './RaceResultViewModel';\n\n/**\n * ViewData for RaceResultsDetail\n * This is the JSON-serializable input for the Template.\n */\nexport interface RaceResultsDetailViewData {\n raceId: string;\n track: string;\n currentUserId: string;\n results: RaceResultViewData[];\n league?: { id: string; name: string };\n race?: { id: string; track: string; scheduledAt: string };\n drivers: { id: string; name: string }[];\n pointsSystem: Record;\n fastestLapTime: number;\n penalties: { driverId: string; type: string; value?: number }[];\n currentDriverId: string;\n}\n\nexport class RaceResultsDetailViewModel extends ViewModel {\n private readonly data: RaceResultsDetailViewData;\n readonly results: RaceResultViewModel[];\n\n constructor(data: RaceResultsDetailViewData) {\n super();\n this.data = data;\n this.results = data.results.map(r => new RaceResultViewModel(r));\n }\n\n get raceId(): string { return this.data.raceId; }\n get track(): string { return this.data.track; }\n get currentUserId(): string { return this.data.currentUserId; }\n get league() { return this.data.league; }\n get race() { return this.data.race; }\n get drivers() { return this.data.drivers; }\n get pointsSystem() { return this.data.pointsSystem; }\n get fastestLapTime(): number { return this.data.fastestLapTime; }\n get penalties() { return this.data.penalties; }\n get currentDriverId(): string { return this.data.currentDriverId; }\n\n /** UI-specific: Results sorted by position */\n get resultsByPosition(): RaceResultViewModel[] {\n return [...this.results].sort((a, b) => a.position - b.position);\n }\n\n /** UI-specific: Results sorted by fastest lap */\n get resultsByFastestLap(): RaceResultViewModel[] {\n return [...this.results].sort((a, b) => a.fastestLap - b.fastestLap);\n }\n\n /** UI-specific: Clean drivers only */\n get cleanDrivers(): RaceResultViewModel[] {\n return this.results.filter(r => r.isClean);\n }\n\n /** UI-specific: Current user's result */\n get currentUserResult(): RaceResultViewModel | undefined {\n return this.results.find(r => r.driverId === this.currentUserId);\n }\n\n /** UI-specific: Race stats */\n get stats(): { totalDrivers: number; cleanRate: number; averageIncidents: number } {\n const total = this.results.length;\n const clean = this.cleanDrivers.length;\n const totalIncidents = this.results.reduce((sum, r) => sum + r.incidents, 0);\n return {\n totalDrivers: total,\n cleanRate: total > 0 ? (clean / total) * 100 : 0,\n averageIncidents: total > 0 ? totalIncidents / total : 0\n };\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RaceStatsViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":1,"endColumn":72},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/RaceStatsDTO","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":72},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":27},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":27},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":13,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":15,"endColumn":2}],"suppressedMessages":[],"errorCount":5,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { RaceStatsDTO } from '@/lib/types/generated/RaceStatsDTO';\n\n/**\n * Race stats view model\n * Represents race statistics for display\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for RaceStats\n * This is the JSON-serializable input for the Template.\n */\nexport interface RaceStatsViewData {\n totalRaces: number;\n}\n\nexport class RaceStatsViewModel extends ViewModel {\n private readonly data: RaceStatsViewData;\n\n constructor(data: RaceStatsViewData) {\n super();\n this.data = data;\n }\n\n get totalRaces(): number { return this.data.totalRaces; }\n\n /** UI-specific: Formatted total races */\n get formattedTotalRaces(): string {\n // Client-only formatting\n return this.totalRaces.toLocaleString();\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RaceStewardingViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":7,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":40,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for RaceStewarding\n * This is the JSON-serializable input for the Template.\n */\nexport interface RaceStewardingViewData {\n race: {\n id: string;\n track: string;\n scheduledAt: string;\n status: string;\n } | null;\n league: {\n id: string;\n name: string;\n } | null;\n protests: Array<{\n id: string;\n protestingDriverId: string;\n accusedDriverId: string;\n incident: {\n lap: number;\n description: string;\n };\n filedAt: string;\n status: string;\n decisionNotes?: string;\n proofVideoUrl?: string;\n }>;\n penalties: Array<{\n id: string;\n driverId: string;\n type: string;\n value: number;\n reason: string;\n notes?: string;\n }>;\n driverMap: Record;\n}\n\nexport class RaceStewardingViewModel extends ViewModel {\n private readonly data: RaceStewardingViewData;\n\n constructor(data: RaceStewardingViewData) {\n super();\n this.data = data;\n }\n\n get race() { return this.data.race; }\n get league() { return this.data.league; }\n get protests() { return this.data.protests; }\n get penalties() { return this.data.penalties; }\n get driverMap() { return this.data.driverMap; }\n\n /** UI-specific: Pending protests */\n get pendingProtests() {\n return this.protests.filter(p => p.status === 'pending' || p.status === 'under_review');\n }\n\n /** UI-specific: Resolved protests */\n get resolvedProtests() {\n return this.protests.filter(p =>\n p.status === 'upheld' ||\n p.status === 'dismissed' ||\n p.status === 'withdrawn'\n );\n }\n\n /** UI-specific: Total pending protests count */\n get pendingCount(): number {\n return this.pendingProtests.length;\n }\n\n /** UI-specific: Total resolved protests count */\n get resolvedCount(): number {\n return this.resolvedProtests.length;\n }\n\n /** UI-specific: Total penalties count */\n get penaltiesCount(): number {\n return this.penalties.length;\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RaceViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RaceWithSOFViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":1,"endColumn":71},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/RaceWithSOFDTO","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":71},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":24},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":24},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":9,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":13,"endColumn":2}],"suppressedMessages":[],"errorCount":5,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { RaceWithSOFDTO } from '@/lib/types/generated/RaceWithSOFDTO';\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * ViewData for RaceWithSOF\n * This is the JSON-serializable input for the Template.\n */\nexport interface RaceWithSOFViewData {\n id: string;\n track: string;\n strengthOfField: number | null;\n}\n\nexport class RaceWithSOFViewModel extends ViewModel {\n private readonly data: RaceWithSOFViewData;\n\n constructor(data: RaceWithSOFViewData) {\n super();\n this.data = data;\n }\n\n get id(): string { return this.data.id; }\n get track(): string { return this.data.track; }\n get strengthOfField(): number | null { return this.data.strengthOfField; }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RacesPageViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RecordEngagementInputViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":7,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":11,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Record engagement input view model\n * Represents input data for recording an engagement event\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface RecordEngagementInputViewData {\n eventType: string;\n userId?: string;\n metadata?: Record;\n}\n\nexport class RecordEngagementInputViewModel extends ViewModel {\n eventType: string;\n userId?: string;\n metadata?: Record;\n\n constructor(data: RecordEngagementInputViewData) {\n super();\n this.eventType = data.eventType;\n this.userId = data.userId;\n this.metadata = data.metadata;\n }\n\n /** UI-specific: Formatted event type for display */\n get displayEventType(): string {\n return this.eventType.replace(/_/g, ' ').replace(/\\b\\w/g, l => l.toUpperCase());\n }\n\n /** UI-specific: Has metadata */\n get hasMetadata(): boolean {\n return this.metadata !== undefined && Object.keys(this.metadata).length > 0;\n }\n\n /** UI-specific: Metadata keys count */\n get metadataKeysCount(): number {\n return this.metadata ? Object.keys(this.metadata).length : 0;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RecordEngagementOutputViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RecordPageViewInputViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":7,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":10,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Record page view input view model\n * Represents input data for recording a page view\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface RecordPageViewInputViewData {\n path: string;\n userId?: string;\n}\n\nexport class RecordPageViewInputViewModel extends ViewModel {\n path: string;\n userId?: string;\n\n constructor(data: RecordPageViewInputViewData) {\n super();\n this.path = data.path;\n this.userId = data.userId;\n }\n\n /** UI-specific: Formatted path for display */\n get displayPath(): string {\n return this.path.startsWith('/') ? this.path : `/${this.path}`;\n }\n\n /** UI-specific: Has user context */\n get hasUserContext(): boolean {\n return this.userId !== undefined && this.userId !== '';\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RecordPageViewOutputViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":3,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":5,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface RecordPageViewOutputViewData {\n pageViewId: string;\n}\n\nexport class RecordPageViewOutputViewModel extends ViewModel {\n pageViewId: string;\n\n constructor(data: RecordPageViewOutputViewData) {\n super();\n this.pageViewId = data.pageViewId;\n }\n\n /** UI-specific: Formatted page view ID for display */\n get displayPageViewId(): string {\n return `Page View: ${this.pageViewId}`;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RemoveMemberViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":10,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * View Model for Remove Member Result\n *\n * Represents the result of removing a member from a league in a UI-ready format.\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface RemoveMemberViewData {\n success: boolean;\n}\n\nexport class RemoveMemberViewModel extends ViewModel {\n success: boolean;\n\n constructor(data: RemoveMemberViewData) {\n super();\n this.success = data.success;\n }\n\n /** UI-specific: Success message */\n get successMessage(): string {\n return this.success ? 'Member removed successfully!' : 'Failed to remove member.';\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RenewalAlertViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":14,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Renewal Alert View Model\n *\n * View model for upcoming renewal alerts.\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface RenewalAlertViewData {\n id: string;\n name: string;\n type: 'league' | 'team' | 'driver' | 'race' | 'platform';\n renewDate: string;\n price: number;\n}\n\nexport class RenewalAlertViewModel extends ViewModel {\n id: string;\n name: string;\n type: 'league' | 'team' | 'driver' | 'race' | 'platform';\n renewDate: Date;\n price: number;\n\n constructor(data: RenewalAlertViewData) {\n super();\n this.id = data.id;\n this.name = data.name;\n this.type = data.type;\n this.renewDate = new Date(data.renewDate);\n this.price = data.price;\n }\n\n get formattedPrice(): string {\n return `$${this.price}`;\n }\n\n get formattedRenewDate(): string {\n return this.renewDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });\n }\n\n get typeIcon() {\n const icons = {\n league: 'Trophy',\n team: 'Users',\n driver: 'Car',\n race: 'Flag',\n platform: 'Megaphone',\n };\n return icons[this.type] || 'Trophy';\n }\n\n get daysUntilRenewal(): number {\n const now = new Date();\n const diffTime = this.renewDate.getTime() - now.getTime();\n return Math.ceil(diffTime / (1000 * 60 * 60 * 24));\n }\n\n get isUrgent(): boolean {\n return this.daysUntilRenewal <= 30;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/RequestAvatarGenerationViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":1,"endColumn":107},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/RequestAvatarGenerationOutputDTO","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":107},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":42},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":42},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":17,"column":5,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":26,"endColumn":10},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":18,"column":9,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":18,"endColumn":41},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":29,"column":20,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":29,"endColumn":23},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":31,"column":24,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":31,"endColumn":27},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":31,"column":31,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":31,"endColumn":34},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":31,"column":77,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":31,"endColumn":80},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":33,"column":25,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":33,"endColumn":28},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":33,"column":32,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":33,"endColumn":35},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":34,"column":25,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":34,"endColumn":28},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":35,"column":31,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":35,"endColumn":34},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":35,"column":38,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":35,"endColumn":41},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":36,"column":26,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":36,"endColumn":29},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":39,"column":27,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":39,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":39,"column":34,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":39,"endColumn":37},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":40,"column":27,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":40,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":41,"column":27,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":41,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":41,"column":34,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":41,"endColumn":37},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":42,"column":27,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":42,"endColumn":30}],"suppressedMessages":[],"errorCount":22,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { RequestAvatarGenerationOutputDTO } from '@/lib/types/generated/RequestAvatarGenerationOutputDTO';\n\n/**\n * Request Avatar Generation View Model\n *\n * Represents the result of an avatar generation request\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport class RequestAvatarGenerationViewModel extends ViewModel {\n success: boolean;\n requestId?: string;\n avatarUrls?: string[];\n errorMessage?: string;\n\n constructor(\n dto:\n | RequestAvatarGenerationOutputDTO\n | {\n success: boolean;\n requestId?: string;\n avatarUrls?: string[];\n errorMessage?: string;\n avatarUrl?: string;\n error?: string;\n },\n ) {\n super();\n this.success = dto.success;\n\n if ('requestId' in dto && dto.requestId !== undefined) this.requestId = dto.requestId;\n\n if ('avatarUrls' in dto && dto.avatarUrls !== undefined) {\n this.avatarUrls = dto.avatarUrls;\n } else if ('avatarUrl' in dto && dto.avatarUrl !== undefined) {\n this.avatarUrls = [dto.avatarUrl];\n }\n\n if ('errorMessage' in dto && dto.errorMessage !== undefined) {\n this.errorMessage = dto.errorMessage;\n } else if ('error' in dto && dto.error !== undefined) {\n this.errorMessage = dto.error;\n }\n }\n\n /** UI-specific: Whether generation was successful */\n get isSuccessful(): boolean {\n return this.success;\n }\n\n /** UI-specific: Whether there was an error */\n get hasError(): boolean {\n return !!this.errorMessage;\n }\n\n /** UI-specific: Get first avatar URL */\n get firstAvatarUrl(): string | undefined {\n return this.avatarUrls?.[0];\n }\n\n get avatarUrl(): string | undefined {\n return this.firstAvatarUrl;\n }\n\n get error(): string | undefined {\n return this.errorMessage;\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/ScoringConfigurationViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/LeagueConfigFormModel","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":80}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { LeagueConfigFormModel } from '@/lib/types/LeagueConfigFormModel';\nimport type { LeagueScoringPresetViewModel } from '@/lib/view-models/LeagueScoringPresetViewModel';\n\nexport interface CustomPointsConfig {\n racePoints: number[];\n poleBonusPoints: number;\n fastestLapPoints: number;\n leaderLapPoints: number;\n}\n\n/**\n * ScoringConfigurationViewModel\n *\n * View model for scoring configuration including presets and custom points\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport class ScoringConfigurationViewModel extends ViewModel {\n readonly patternId?: string;\n readonly customScoringEnabled: boolean;\n readonly customPoints?: CustomPointsConfig;\n readonly currentPreset?: LeagueScoringPresetViewModel;\n\n constructor(\n config: LeagueConfigFormModel['scoring'],\n presets: LeagueScoringPresetViewModel[],\n customPoints?: CustomPointsConfig\n ) {\n this.patternId = config.patternId;\n this.customScoringEnabled = config.customScoringEnabled || false;\n this.customPoints = customPoints;\n this.currentPreset = config.patternId \n ? presets.find(p => p.id === config.patternId)\n : undefined;\n }\n\n /**\n * Get the active points configuration\n */\n getActivePointsConfig(): CustomPointsConfig {\n if (this.customScoringEnabled && this.customPoints) {\n return this.customPoints;\n }\n // Return default points if no custom config\n return {\n racePoints: [25, 18, 15, 12, 10, 8, 6, 4, 2, 1],\n poleBonusPoints: 1,\n fastestLapPoints: 1,\n leaderLapPoints: 0,\n };\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/SessionViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":1,"endColumn":83},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/AuthenticatedUserDTO","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":83},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":10,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":14,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":14,"endColumn":40},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":14,"column":20,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":14,"endColumn":40},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":16,"column":19,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":16,"endColumn":22},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":17,"column":18,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":17,"endColumn":21},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":18,"column":24,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":18,"endColumn":27},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":21,"column":9,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":21,"endColumn":12},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":22,"column":23,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":22,"endColumn":26},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":25,"column":9,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":25,"endColumn":12},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":26,"column":24,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":26,"endColumn":27},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":29,"column":9,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":29,"endColumn":12},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":30,"column":19,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":30,"endColumn":22}],"suppressedMessages":[],"errorCount":15,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { AuthenticatedUserDTO } from '@/lib/types/generated/AuthenticatedUserDTO';\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport class SessionViewModel extends ViewModel {\n userId: string;\n email: string;\n displayName: string;\n avatarUrl?: string | null;\n role?: string;\n driverId?: string;\n isAuthenticated: boolean = true;\n\n constructor(dto: AuthenticatedUserDTO) {\n super();\n this.userId = dto.userId;\n this.email = dto.email;\n this.displayName = dto.displayName;\n\n // Use the optional fields from the DTO\n if (dto.primaryDriverId) {\n this.driverId = dto.primaryDriverId;\n }\n \n if (dto.avatarUrl !== undefined) {\n this.avatarUrl = dto.avatarUrl;\n }\n \n if (dto.role) {\n this.role = dto.role;\n }\n }\n\n /**\n * Compatibility accessor.\n * Some legacy components expect `session.user.*`.\n */\n get user(): {\n userId: string;\n email: string;\n displayName: string;\n primaryDriverId?: string | null;\n avatarUrl?: string | null;\n } {\n return {\n userId: this.userId,\n email: this.email,\n displayName: this.displayName,\n primaryDriverId: this.driverId ?? null,\n avatarUrl: this.avatarUrl,\n };\n }\n\n /** UI-specific: User greeting */\n get greeting(): string {\n return `Hello, ${this.displayName}!`;\n }\n\n /** UI-specific: Avatar initials */\n get avatarInitials(): string {\n if (this.displayName) {\n return this.displayName.split(' ').map(n => n[0]).join('').toUpperCase();\n }\n return (this.email?.[0] ?? '?').toUpperCase();\n }\n\n /** UI-specific: Whether has driver profile */\n get hasDriverProfile(): boolean {\n return !!this.driverId;\n }\n\n /** UI-specific: Authentication status display */\n get authStatusDisplay(): string {\n return this.isAuthenticated ? 'Logged In' : 'Logged Out';\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/SponsorDashboardViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":11,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Sponsor Dashboard View Model\n *\n * Represents dashboard data for a sponsor with UI-specific transformations.\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface SponsorDashboardViewData {\n sponsorId: string;\n sponsorName: string;\n}\n\nexport class SponsorDashboardViewModel extends ViewModel {\n sponsorId: string;\n sponsorName: string;\n\n constructor(data: SponsorDashboardViewData) {\n super();\n this.sponsorId = data.sponsorId;\n this.sponsorName = data.sponsorName;\n }\n\n /** UI-specific: Welcome message */\n get welcomeMessage(): string {\n return `Welcome back, ${this.sponsorName}!`;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/SponsorProfileViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":3,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":24,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface SponsorProfileViewData {\n companyName: string;\n contactName: string;\n contactEmail: string;\n contactPhone: string;\n website: string;\n description: string;\n logoUrl: string | null;\n industry: string;\n address: {\n street: string;\n city: string;\n country: string;\n postalCode: string;\n };\n taxId: string;\n socialLinks: {\n twitter: string;\n linkedin: string;\n instagram: string;\n };\n}\n\nexport class SponsorProfileViewModel extends ViewModel {\n companyName: string;\n contactName: string;\n contactEmail: string;\n contactPhone: string;\n website: string;\n description: string;\n logoUrl: string | null;\n industry: string;\n address: {\n street: string;\n city: string;\n country: string;\n postalCode: string;\n };\n taxId: string;\n socialLinks: {\n twitter: string;\n linkedin: string;\n instagram: string;\n };\n\n constructor(data: SponsorProfileViewData) {\n super();\n this.companyName = data.companyName;\n this.contactName = data.contactName;\n this.contactEmail = data.contactEmail;\n this.contactPhone = data.contactPhone;\n this.website = data.website;\n this.description = data.description;\n this.logoUrl = data.logoUrl;\n this.industry = data.industry;\n this.address = data.address;\n this.taxId = data.taxId;\n this.socialLinks = data.socialLinks;\n }\n\n get fullAddress(): string {\n return `${this.address.street}, ${this.address.city}, ${this.address.postalCode}, ${this.address.country}`;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/SponsorSettingsViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":11,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":15,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Sponsor Settings View Model\n *\n * View model for sponsor settings data.\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\nimport { SponsorProfileViewModel, SponsorProfileViewData } from \"./SponsorProfileViewModel\";\nimport { NotificationSettingsViewModel, NotificationSettingsViewData } from \"./NotificationSettingsViewModel\";\nimport { PrivacySettingsViewModel, PrivacySettingsViewData } from \"./PrivacySettingsViewModel\";\n\nexport interface SponsorSettingsViewData {\n profile: SponsorProfileViewData;\n notifications: NotificationSettingsViewData;\n privacy: PrivacySettingsViewData;\n}\n\nexport class SponsorSettingsViewModel extends ViewModel {\n profile: SponsorProfileViewModel;\n notifications: NotificationSettingsViewModel;\n privacy: PrivacySettingsViewModel;\n\n constructor(data: SponsorSettingsViewData) {\n super();\n this.profile = new SponsorProfileViewModel(data.profile);\n this.notifications = new NotificationSettingsViewModel(data.notifications);\n this.privacy = new PrivacySettingsViewModel(data.privacy);\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/SponsorSponsorshipsViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":9,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":13,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\nimport { SponsorshipDetailViewModel } from './SponsorshipDetailViewModel';\n\n/**\n * Sponsor Sponsorships View Model\n *\n * View model for sponsor sponsorships data with UI-specific transformations.\n */\nexport interface SponsorSponsorshipsViewData {\n sponsorId: string;\n sponsorName: string;\n sponsorships: any[]; // Will be mapped to SponsorshipDetailViewModel\n}\n\nexport class SponsorSponsorshipsViewModel extends ViewModel {\n sponsorId: string;\n sponsorName: string;\n sponsorships: SponsorshipDetailViewModel[];\n\n constructor(data: SponsorSponsorshipsViewData) {\n super();\n this.sponsorId = data.sponsorId;\n this.sponsorName = data.sponsorName;\n this.sponsorships = (data.sponsorships || []).map(s => new SponsorshipDetailViewModel(s));\n }\n\n /** UI-specific: Total sponsorships count */\n get totalCount(): number {\n return this.sponsorships.length;\n }\n\n /** UI-specific: Active sponsorships */\n get activeSponsorships(): SponsorshipDetailViewModel[] {\n return this.sponsorships.filter(s => s.status === 'active');\n }\n\n /** UI-specific: Active count */\n get activeCount(): number {\n return this.activeSponsorships.length;\n }\n\n /** UI-specific: Has sponsorships */\n get hasSponsorships(): boolean {\n return this.sponsorships.length > 0;\n }\n\n /** UI-specific: Total investment */\n get totalInvestment(): number {\n return this.sponsorships.reduce((sum, s) => sum + s.amount, 0);\n }\n\n /** UI-specific: Formatted total investment */\n get formattedTotalInvestment(): string {\n const firstCurrency = this.sponsorships[0]?.currency || 'USD';\n return `${firstCurrency} ${this.totalInvestment.toLocaleString()}`;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/SponsorViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":2,"column":11,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":2,"endColumn":21},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":17,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":17,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":17,"column":20,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":17,"endColumn":30},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":18,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":18,"endColumn":18},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":19,"column":17,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":19,"endColumn":20},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":20,"column":9,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":20,"endColumn":12},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":20,"column":51,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":20,"endColumn":54},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":21,"column":9,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":21,"endColumn":12},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":21,"column":57,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":21,"endColumn":60}],"suppressedMessages":[],"errorCount":9,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"// Note: No generated DTO available for Sponsor yet\ninterface SponsorDTO {\n id: string;\n name: string;\n logoUrl?: string;\n websiteUrl?: string;\n}\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport class SponsorViewModel extends ViewModel {\n id: string;\n name: string;\n declare logoUrl?: string;\n declare websiteUrl?: string;\n\n constructor(dto: SponsorDTO) {\n this.id = dto.id;\n this.name = dto.name;\n if (dto.logoUrl !== undefined) this.logoUrl = dto.logoUrl;\n if (dto.websiteUrl !== undefined) this.websiteUrl = dto.websiteUrl;\n }\n\n /** UI-specific: Display name */\n get displayName(): string {\n return this.name;\n }\n\n /** UI-specific: Whether has website */\n get hasWebsite(): boolean {\n return !!this.websiteUrl;\n }\n\n /** UI-specific: Website link text */\n get websiteLinkText(): string {\n return 'Visit Website';\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/SponsorshipDetailViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":3,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":17,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface SponsorshipDetailViewData {\n id: string;\n leagueId: string;\n leagueName: string;\n seasonId: string;\n seasonName: string;\n tier: 'main' | 'secondary';\n status: string;\n amount: number;\n currency: string;\n type: string;\n entityName: string;\n price: number;\n impressions: number;\n}\n\nexport class SponsorshipDetailViewModel extends ViewModel {\n id: string;\n leagueId: string;\n leagueName: string;\n seasonId: string;\n seasonName: string;\n tier: 'main' | 'secondary';\n status: string;\n amount: number;\n currency: string;\n type: string;\n entityName: string;\n price: number;\n impressions: number;\n\n constructor(data: SponsorshipDetailViewData) {\n super();\n this.id = data.id;\n this.leagueId = data.leagueId;\n this.leagueName = data.leagueName;\n this.seasonId = data.seasonId;\n this.seasonName = data.seasonName;\n this.tier = data.tier;\n this.status = data.status;\n this.amount = data.amount;\n this.currency = data.currency;\n this.type = data.type;\n this.entityName = data.entityName;\n this.price = data.price;\n this.impressions = data.impressions;\n }\n\n /** UI-specific: Formatted amount */\n get formattedAmount(): string {\n return `${this.currency} ${this.amount.toLocaleString()}`;\n }\n\n /** UI-specific: Tier badge variant */\n get tierBadgeVariant(): string {\n return this.tier === 'main' ? 'primary' : 'secondary';\n }\n\n /** UI-specific: Status color */\n get statusColor(): string {\n switch (this.status) {\n case 'active': return 'green';\n case 'pending': return 'yellow';\n case 'expired': return 'red';\n default: return 'gray';\n }\n }\n\n /** UI-specific: Status display */\n get statusDisplay(): string {\n return this.status.charAt(0).toUpperCase() + this.status.slice(1);\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/SponsorshipPricingViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":12,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Sponsorship Pricing View Model\n *\n * View model for sponsorship pricing data with UI-specific transformations.\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface SponsorshipPricingViewData {\n mainSlotPrice: number;\n secondarySlotPrice: number;\n currency: string;\n}\n\nexport class SponsorshipPricingViewModel extends ViewModel {\n mainSlotPrice: number;\n secondarySlotPrice: number;\n currency: string;\n\n constructor(data: SponsorshipPricingViewData) {\n super();\n this.mainSlotPrice = data.mainSlotPrice;\n this.secondarySlotPrice = data.secondarySlotPrice;\n this.currency = data.currency;\n }\n\n /** UI-specific: Formatted main slot price */\n get formattedMainSlotPrice(): string {\n return `${this.currency} ${this.mainSlotPrice.toLocaleString()}`;\n }\n\n /** UI-specific: Formatted secondary slot price */\n get formattedSecondarySlotPrice(): string {\n return `${this.currency} ${this.secondarySlotPrice.toLocaleString()}`;\n }\n\n /** UI-specific: Price difference */\n get priceDifference(): number {\n return this.mainSlotPrice - this.secondarySlotPrice;\n }\n\n /** UI-specific: Formatted price difference */\n get formattedPriceDifference(): string {\n return `${this.currency} ${this.priceDifference.toLocaleString()}`;\n }\n\n /** UI-specific: Discount percentage for secondary slot */\n get secondaryDiscountPercentage(): number {\n if (this.mainSlotPrice === 0) return 0;\n return Math.round((1 - this.secondarySlotPrice / this.mainSlotPrice) * 100);\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/SponsorshipRequestViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":3,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":16,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface SponsorshipRequestViewData {\n id: string;\n sponsorId: string;\n sponsorName: string;\n sponsorLogo?: string;\n tier: 'main' | 'secondary';\n offeredAmount: number;\n currency: string;\n formattedAmount: string;\n message?: string;\n createdAt: string;\n platformFee: number;\n netAmount: number;\n}\n\nexport class SponsorshipRequestViewModel extends ViewModel {\n id: string;\n sponsorId: string;\n sponsorName: string;\n sponsorLogo?: string;\n tier: 'main' | 'secondary';\n offeredAmount: number;\n currency: string;\n formattedAmount: string;\n message?: string;\n createdAt: Date;\n platformFee: number;\n netAmount: number;\n\n constructor(data: SponsorshipRequestViewData) {\n super();\n this.id = data.id;\n this.sponsorId = data.sponsorId;\n this.sponsorName = data.sponsorName;\n this.sponsorLogo = data.sponsorLogo;\n this.tier = data.tier;\n this.offeredAmount = data.offeredAmount;\n this.currency = data.currency;\n this.formattedAmount = data.formattedAmount;\n this.message = data.message;\n this.createdAt = new Date(data.createdAt);\n this.platformFee = data.platformFee;\n this.netAmount = data.netAmount;\n }\n\n /** UI-specific: Formatted date */\n get formattedDate(): string {\n return this.createdAt.toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n });\n }\n\n /** UI-specific: Net amount in dollars */\n get netAmountDollars(): string {\n return `$${(this.netAmount / 100).toFixed(2)}`;\n }\n\n /** UI-specific: Tier display */\n get tierDisplay(): string {\n return this.tier === 'main' ? 'Main Sponsor' : 'Secondary';\n }\n\n /** UI-specific: Tier badge variant */\n get tierBadgeVariant(): 'primary' | 'secondary' {\n return this.tier === 'main' ? 'primary' : 'secondary';\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/SponsorshipViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":9,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":28,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { CurrencyDisplay } from '@/lib/display-objects/CurrencyDisplay';\nimport { DateDisplay } from '@/lib/display-objects/DateDisplay';\nimport { NumberDisplay } from '@/lib/display-objects/NumberDisplay';\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\n/**\n * Interface for sponsorship data input\n */\nexport interface SponsorshipViewData {\n id: string;\n type: 'leagues' | 'teams' | 'drivers' | 'races' | 'platform';\n entityId: string;\n entityName: string;\n tier?: 'main' | 'secondary';\n status: 'active' | 'pending_approval' | 'approved' | 'rejected' | 'expired';\n applicationDate?: string | Date;\n approvalDate?: string | Date;\n rejectionReason?: string;\n startDate: string | Date;\n endDate: string | Date;\n price: number;\n impressions: number;\n impressionsChange?: number;\n engagement?: number;\n details?: string;\n entityOwner?: string;\n applicationMessage?: string;\n}\n\n/**\n * Sponsorship View Model\n *\n * View model for individual sponsorship data.\n */\nexport class SponsorshipViewModel extends ViewModel {\n id: string;\n type: 'leagues' | 'teams' | 'drivers' | 'races' | 'platform';\n entityId: string;\n entityName: string;\n tier?: 'main' | 'secondary';\n status: 'active' | 'pending_approval' | 'approved' | 'rejected' | 'expired';\n applicationDate?: Date;\n approvalDate?: Date;\n rejectionReason?: string;\n startDate: Date;\n endDate: Date;\n price: number;\n impressions: number;\n impressionsChange?: number;\n engagement?: number;\n details?: string;\n entityOwner?: string;\n applicationMessage?: string;\n\n constructor(data: SponsorshipViewData) {\n super();\n this.id = data.id;\n this.type = data.type;\n this.entityId = data.entityId;\n this.entityName = data.entityName;\n this.tier = data.tier;\n this.status = data.status;\n this.applicationDate = data.applicationDate ? new Date(data.applicationDate) : undefined;\n this.approvalDate = data.approvalDate ? new Date(data.approvalDate) : undefined;\n this.rejectionReason = data.rejectionReason;\n this.startDate = new Date(data.startDate);\n this.endDate = new Date(data.endDate);\n this.price = data.price;\n this.impressions = data.impressions;\n this.impressionsChange = data.impressionsChange;\n this.engagement = data.engagement;\n this.details = data.details;\n this.entityOwner = data.entityOwner;\n this.applicationMessage = data.applicationMessage;\n }\n\n get formattedImpressions(): string {\n return NumberDisplay.format(this.impressions);\n }\n\n get formattedPrice(): string {\n return CurrencyDisplay.format(this.price);\n }\n\n get daysRemaining(): number {\n const now = new Date();\n const diffTime = this.endDate.getTime() - now.getTime();\n return Math.ceil(diffTime / (1000 * 60 * 60 * 24));\n }\n\n get isExpiringSoon(): boolean {\n return this.daysRemaining > 0 && this.daysRemaining <= 30;\n }\n\n get statusLabel(): string {\n const labels = {\n active: 'Active',\n pending_approval: 'Awaiting Approval',\n approved: 'Approved',\n rejected: 'Declined',\n expired: 'Expired',\n };\n return labels[this.status] || this.status;\n }\n\n get typeLabel(): string {\n const labels = {\n leagues: 'League',\n teams: 'Team',\n drivers: 'Driver',\n races: 'Race',\n platform: 'Platform',\n };\n return labels[this.type] || this.type;\n }\n\n get periodDisplay(): string {\n const start = DateDisplay.formatMonthYear(this.startDate);\n const end = DateDisplay.formatMonthYear(this.endDate);\n return `${start} - ${end}`;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/StandingEntryViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":20,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../contracts/view-models/ViewModel\";\nimport { FinishDisplay } from \"../display-objects/FinishDisplay\";\n\n/**\n * ViewData for StandingEntry\n * This is the JSON-serializable input for the Template.\n */\nexport interface StandingEntryViewData {\n driverId: string;\n position: number;\n points: number;\n wins: number;\n podiums: number;\n races: number;\n leaderPoints: number;\n nextPoints: number;\n currentUserId: string;\n previousPosition?: number;\n driver?: any;\n}\n\nexport class StandingEntryViewModel extends ViewModel {\n private readonly data: StandingEntryViewData;\n\n constructor(data: StandingEntryViewData) {\n super();\n this.data = data;\n }\n\n get driverId(): string { return this.data.driverId; }\n get position(): number { return this.data.position; }\n get points(): number { return this.data.points; }\n get wins(): number { return this.data.wins; }\n get podiums(): number { return this.data.podiums; }\n get races(): number { return this.data.races; }\n get driver(): any { return this.data.driver; }\n\n /** UI-specific: Badge for position display */\n get positionBadge(): string {\n return FinishDisplay.format(this.position);\n }\n\n /** UI-specific: Points difference to leader */\n get pointsGapToLeader(): number {\n return this.points - this.data.leaderPoints;\n }\n\n /** UI-specific: Points difference to next position */\n get pointsGapToNext(): number {\n return this.points - this.data.nextPoints;\n }\n\n /** UI-specific: Whether this entry is the current user */\n get isCurrentUser(): boolean {\n return this.driverId === this.data.currentUserId;\n }\n\n /** UI-specific: Trend compared to previous */\n get trend(): 'up' | 'down' | 'same' {\n if (!this.data.previousPosition) return 'same';\n if (this.position < this.data.previousPosition) return 'up';\n if (this.position > this.data.previousPosition) return 'down';\n return 'same';\n }\n\n /** UI-specific: Arrow for trend */\n get trendArrow(): string {\n switch (this.trend) {\n case 'up': return '↑';\n case 'down': return '↓';\n default: return '-';\n }\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/TeamCardViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/TeamDetailsViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":1,"endColumn":94},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/GetTeamDetailsOutputDTO","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":94},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":38},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":38},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":21,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":21,"endColumn":43},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":21,"column":20,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":21,"endColumn":43},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":22,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":22,"endColumn":18},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":23,"column":17,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":23,"endColumn":20},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":24,"column":16,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":24,"endColumn":19},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":25,"column":24,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":25,"endColumn":27},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":26,"column":20,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":26,"endColumn":23},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":27,"column":20,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":27,"endColumn":23},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":28,"column":22,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":28,"endColumn":25},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":30,"column":24,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":30,"endColumn":27},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":30,"column":43,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":30,"endColumn":46},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":41,"column":23,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":41,"endColumn":26},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":42,"column":13,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":42,"endColumn":16},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":43,"column":17,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":43,"endColumn":20},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":44,"column":17,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":44,"endColumn":20},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":46,"column":23,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":46,"endColumn":26}],"suppressedMessages":[],"errorCount":20,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { GetTeamDetailsOutputDTO } from '@/lib/types/generated/GetTeamDetailsOutputDTO';\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport class TeamDetailsViewModel extends ViewModel {\n id!: string;\n name!: string;\n tag!: string;\n description?: string;\n ownerId!: string;\n leagues!: string[];\n createdAt: string | undefined;\n specialization: string | undefined;\n region: string | undefined;\n languages: string[] | undefined;\n category: string | undefined;\n membership: { role: string; joinedAt: string; isActive: boolean } | null;\n private _canManage: boolean;\n private currentUserId: string;\n\n constructor(dto: GetTeamDetailsOutputDTO, currentUserId: string) {\n this.id = dto.team.id;\n this.name = dto.team.name;\n this.tag = dto.team.tag;\n this.description = dto.team.description;\n this.ownerId = dto.team.ownerId;\n this.leagues = dto.team.leagues;\n this.createdAt = dto.team.createdAt;\n\n const teamExtras = dto.team as typeof dto.team & {\n specialization?: string;\n region?: string;\n languages?: string[];\n category?: string;\n };\n\n this.specialization = teamExtras.specialization ?? undefined;\n this.region = teamExtras.region ?? undefined;\n this.languages = teamExtras.languages ?? undefined;\n this.category = teamExtras.category ?? undefined;\n this.membership = dto.membership ? {\n role: dto.membership.role,\n joinedAt: dto.membership.joinedAt,\n isActive: dto.membership.isActive\n } : null;\n this._canManage = dto.canManage;\n this.currentUserId = currentUserId;\n }\n\n /** UI-specific: Whether current user is owner */\n get isOwner(): boolean {\n return this.membership?.role === 'owner';\n }\n\n /** UI-specific: Whether can manage team */\n get canManage(): boolean {\n return this._canManage;\n }\n\n /** UI-specific: Whether current user is member */\n get isMember(): boolean {\n return this.membership !== null;\n }\n\n /** UI-specific: Current user's role */\n get userRole(): string {\n return this.membership?.role || 'none';\n }\n}","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/TeamJoinRequestViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":1,"endColumn":84},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/TeamJoinRequestDTO","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":84},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":33},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":33},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":17,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":17,"endColumn":38},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":17,"column":20,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":17,"endColumn":38},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":18,"column":22,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":18,"endColumn":25},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":19,"column":21,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":19,"endColumn":24},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":20,"column":23,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":20,"endColumn":26},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":21,"column":19,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":21,"endColumn":22},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":22,"column":26,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":22,"endColumn":29},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":23,"column":24,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":23,"endColumn":27},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":24,"column":22,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":24,"endColumn":25}],"suppressedMessages":[],"errorCount":13,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { TeamJoinRequestDTO } from '@/lib/types/generated/TeamJoinRequestDTO';\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport class TeamJoinRequestViewModel extends ViewModel {\n requestId: string;\n driverId: string;\n driverName: string;\n teamId: string;\n requestStatus: string;\n requestedAt: string;\n avatarUrl: string;\n\n private readonly currentUserId: string;\n private readonly isOwner: boolean;\n\n constructor(dto: TeamJoinRequestDTO, currentUserId: string, isOwner: boolean) {\n this.requestId = dto.requestId;\n this.driverId = dto.driverId;\n this.driverName = dto.driverName;\n this.teamId = dto.teamId;\n this.requestStatus = dto.status;\n this.requestedAt = dto.requestedAt;\n this.avatarUrl = dto.avatarUrl || '';\n this.currentUserId = currentUserId;\n this.isOwner = isOwner;\n }\n\n get id(): string {\n return this.requestId;\n }\n\n get status(): string {\n if (this.requestStatus === 'pending') return 'Pending';\n if (this.requestStatus === 'approved') return 'Approved';\n if (this.requestStatus === 'rejected') return 'Rejected';\n return this.requestStatus;\n }\n\n /** UI-specific: Whether current user can approve */\n get canApprove(): boolean {\n return this.isOwner;\n }\n\n /** UI-specific: Formatted requested date */\n get formattedRequestedAt(): string {\n return new Date(this.requestedAt).toLocaleString();\n }\n\n /** UI-specific: Status color */\n get statusColor(): string {\n if (this.requestStatus === 'approved') return 'green';\n if (this.requestStatus === 'rejected') return 'red';\n return 'yellow';\n }\n\n /** UI-specific: Approve button text */\n get approveButtonText(): string {\n return 'Approve';\n }\n\n /** UI-specific: Reject button text */\n get rejectButtonText(): string {\n return 'Reject';\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/TeamMemberViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not import from DTO paths. DTOs belong to lib/types/generated/. Import from lib/view-data/ or use plain properties instead.","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"noDtoImport","endLine":1,"endColumn":74},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: @/lib/types/generated/TeamMemberDTO","line":1,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":1,"endColumn":74},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":28},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":1,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":1,"endColumn":28},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":25,"column":15,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":25,"endColumn":33},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":25,"column":20,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":25,"endColumn":33},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":26,"column":21,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":26,"endColumn":24},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":27,"column":23,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":27,"endColumn":26},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":28,"column":35,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":28,"endColumn":38},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":29,"column":21,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":29,"endColumn":24},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":30,"column":21,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":30,"endColumn":24},{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels must not use the word \"DTO\". DTOs belong to the API/Service layer. Use plain logic-rich properties or ViewData types.","line":31,"column":22,"nodeType":"Identifier","messageId":"noDtoInViewModel","endLine":31,"endColumn":25}],"suppressedMessages":[],"errorCount":12,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { TeamMemberDTO } from '@/lib/types/generated/TeamMemberDTO';\n\ntype TeamMemberRole = 'owner' | 'manager' | 'member';\n\nfunction normalizeTeamRole(role: string): TeamMemberRole {\n if (role === 'owner' || role === 'manager' || role === 'member') return role;\n // Backwards compatibility\n if (role === 'admin') return 'manager';\n return 'member';\n}\n\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport class TeamMemberViewModel extends ViewModel {\n driverId: string;\n driverName: string;\n role: TeamMemberRole;\n joinedAt: string;\n isActive: boolean;\n avatarUrl: string;\n\n private currentUserId: string;\n private teamOwnerId: string;\n\n constructor(dto: TeamMemberDTO, currentUserId: string, teamOwnerId: string) {\n this.driverId = dto.driverId;\n this.driverName = dto.driverName;\n this.role = normalizeTeamRole(dto.role);\n this.joinedAt = dto.joinedAt;\n this.isActive = dto.isActive;\n this.avatarUrl = dto.avatarUrl || '';\n this.currentUserId = currentUserId;\n this.teamOwnerId = teamOwnerId;\n }\n\n /** UI-specific: Role badge variant */\n get roleBadgeVariant(): string {\n switch (this.role) {\n case 'owner': return 'primary';\n case 'manager': return 'secondary';\n case 'member': return 'default';\n default: return 'default';\n }\n }\n\n /** UI-specific: Whether this member is the owner */\n get isOwner(): boolean {\n return this.driverId === this.teamOwnerId;\n }\n\n /** UI-specific: Whether current user can manage this member */\n get canManage(): boolean {\n return this.currentUserId === this.teamOwnerId && this.driverId !== this.currentUserId;\n }\n\n /** UI-specific: Whether this is the current user */\n get isCurrentUser(): boolean {\n return this.driverId === this.currentUserId;\n }\n\n /** UI-specific: Formatted joined date */\n get formattedJoinedAt(): string {\n return new Date(this.joinedAt).toLocaleDateString();\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/TeamSummaryViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/UpcomingRaceCardViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/UpdateAvatarViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":11,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Update Avatar View Model\n *\n * Represents the result of an avatar update operation\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface UpdateAvatarViewData {\n success: boolean;\n error?: string;\n}\n\nexport class UpdateAvatarViewModel extends ViewModel {\n success: boolean;\n error?: string;\n\n constructor(data: UpdateAvatarViewData) {\n super();\n this.success = data.success;\n this.error = data.error;\n }\n\n /** UI-specific: Whether update was successful */\n get isSuccessful(): boolean {\n return this.success;\n }\n\n /** UI-specific: Whether there was an error */\n get hasError(): boolean {\n return !!this.error;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/UpdateTeamViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":10,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * View Model for Update Team Result\n *\n * Represents the result of updating a team in a UI-ready format.\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface UpdateTeamViewData {\n success: boolean;\n}\n\nexport class UpdateTeamViewModel extends ViewModel {\n success: boolean;\n\n constructor(data: UpdateTeamViewData) {\n super();\n this.success = data.success;\n }\n\n /** UI-specific: Success message */\n get successMessage(): string {\n return this.success ? 'Team updated successfully!' : 'Failed to update team.';\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/UploadMediaViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewData must not be defined within ViewModel files. Import them from lib/view-data/ instead.","line":8,"column":8,"nodeType":"TSInterfaceDeclaration","messageId":"noViewDataDefinition","endLine":13,"endColumn":2}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Upload Media View Model\n *\n * Represents the result of a media upload operation\n */\nimport { ViewModel } from \"../contracts/view-models/ViewModel\";\n\nexport interface UploadMediaViewData {\n success: boolean;\n mediaId?: string;\n url?: string;\n error?: string;\n}\n\nexport class UploadMediaViewModel extends ViewModel {\n success: boolean;\n mediaId?: string;\n url?: string;\n error?: string;\n\n constructor(data: UploadMediaViewData) {\n super();\n this.success = data.success;\n this.mediaId = data.mediaId;\n this.url = data.url;\n this.error = data.error;\n }\n\n /** UI-specific: Whether upload was successful */\n get isSuccessful(): boolean {\n return this.success;\n }\n\n /** UI-specific: Whether there was an error */\n get hasError(): boolean {\n return !!this.error;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/UserListViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/UserProfileViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/WalletTransactionViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/WalletViewModel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/auth/ForgotPasswordInterfaces.ts","messages":[{"ruleId":"gridpilot-rules/view-model-implements","severity":2,"message":"ViewModel files must be classes named *ViewModel","line":1,"column":1,"nodeType":"Program","messageId":"notAClass","endLine":17,"endColumn":1}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"export interface ForgotPasswordFormField {\n value: string;\n error?: string;\n touched: boolean;\n validating: boolean;\n}\n\nexport interface ForgotPasswordFormState {\n fields: {\n email: ForgotPasswordFormField;\n };\n isValid: boolean;\n isSubmitting: boolean;\n submitError?: string;\n submitCount: number;\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/auth/ForgotPasswordViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: ./ForgotPasswordInterfaces","line":2,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":2,"endColumn":75}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../../contracts/view-models/ViewModel\";\nimport type { ForgotPasswordFormState } from \"./ForgotPasswordInterfaces\";\n\n/**\n * Forgot Password ViewModel\n * \n * Client-side state management for forgot password flow.\n * Immutable, class-based, contains only UI state.\n */\nexport class ForgotPasswordViewModel extends ViewModel {\n constructor(\n public readonly returnTo: string,\n public readonly formState: ForgotPasswordFormState,\n public readonly showSuccess: boolean = false,\n public readonly successMessage: string | null = null,\n public readonly magicLink: string | null = null,\n public readonly mutationPending: boolean = false,\n public readonly mutationError: string | null = null\n ) {\n super();\n }\n\n withFormState(formState: ForgotPasswordFormState): ForgotPasswordViewModel {\n return new ForgotPasswordViewModel(\n this.returnTo,\n formState,\n this.showSuccess,\n this.successMessage,\n this.magicLink,\n this.mutationPending,\n this.mutationError\n );\n }\n\n withSuccess(successMessage: string, magicLink: string | null = null): ForgotPasswordViewModel {\n return new ForgotPasswordViewModel(\n this.returnTo,\n this.formState,\n true,\n successMessage,\n magicLink,\n false,\n null\n );\n }\n\n withMutationState(pending: boolean, error: string | null): ForgotPasswordViewModel {\n return new ForgotPasswordViewModel(\n this.returnTo,\n this.formState,\n this.showSuccess,\n this.successMessage,\n this.magicLink,\n pending,\n error\n );\n }\n\n get isSubmitting(): boolean {\n return this.formState.isSubmitting || this.mutationPending;\n }\n\n get submitError(): string | undefined {\n return this.formState.submitError || this.mutationError || undefined;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/auth/LoginInterfaces.ts","messages":[{"ruleId":"gridpilot-rules/view-model-implements","severity":2,"message":"ViewModel files must be classes named *ViewModel","line":1,"column":1,"nodeType":"Program","messageId":"notAClass","endLine":24,"endColumn":1}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"export interface LoginFormField {\n value: string | boolean;\n error?: string;\n touched: boolean;\n validating: boolean;\n}\n\nexport interface LoginFormState {\n fields: {\n email: LoginFormField;\n password: LoginFormField;\n rememberMe: LoginFormField;\n };\n isValid: boolean;\n isSubmitting: boolean;\n submitError?: string;\n submitCount: number;\n}\n\nexport interface LoginUIState {\n showPassword: boolean;\n showErrorDetails: boolean;\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/auth/LoginViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: ./LoginInterfaces","line":2,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":2,"endColumn":71}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../../contracts/view-models/ViewModel\";\nimport type { LoginFormState, LoginUIState } from \"./LoginInterfaces\";\n\n/**\n * Login ViewModel\n * \n * Client-side state management for login flow.\n * Immutable, class-based, contains only UI state.\n */\nexport class LoginViewModel extends ViewModel {\n constructor(\n public readonly returnTo: string,\n public readonly hasInsufficientPermissions: boolean,\n public readonly formState: LoginFormState,\n public readonly uiState: LoginUIState,\n public readonly mutationPending: boolean = false,\n public readonly mutationError: string | null = null\n ) {\n super();\n }\n\n // Immutable updates\n withFormState(formState: LoginFormState): LoginViewModel {\n return new LoginViewModel(\n this.returnTo,\n this.hasInsufficientPermissions,\n formState,\n this.uiState,\n this.mutationPending,\n this.mutationError\n );\n }\n\n withUIState(uiState: LoginUIState): LoginViewModel {\n return new LoginViewModel(\n this.returnTo,\n this.hasInsufficientPermissions,\n this.formState,\n uiState,\n this.mutationPending,\n this.mutationError\n );\n }\n\n withMutationState(pending: boolean, error: string | null): LoginViewModel {\n return new LoginViewModel(\n this.returnTo,\n this.hasInsufficientPermissions,\n this.formState,\n this.uiState,\n pending,\n error\n );\n }\n\n // Getters for template consumption\n get showPassword(): boolean {\n return this.uiState.showPassword;\n }\n\n get showErrorDetails(): boolean {\n return this.uiState.showErrorDetails;\n }\n\n get isSubmitting(): boolean {\n return this.formState.isSubmitting || this.mutationPending;\n }\n\n get submitError(): string | undefined {\n return this.formState.submitError || this.mutationError || undefined;\n }\n\n get formFields() {\n return this.formState.fields;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/auth/ResetPasswordInterfaces.ts","messages":[{"ruleId":"gridpilot-rules/view-model-implements","severity":2,"message":"ViewModel files must be classes named *ViewModel","line":1,"column":1,"nodeType":"Program","messageId":"notAClass","endLine":23,"endColumn":1}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"export interface ResetPasswordFormField {\n value: string;\n error?: string;\n touched: boolean;\n validating: boolean;\n}\n\nexport interface ResetPasswordFormState {\n fields: {\n newPassword: ResetPasswordFormField;\n confirmPassword: ResetPasswordFormField;\n };\n isValid: boolean;\n isSubmitting: boolean;\n submitError?: string;\n submitCount: number;\n}\n\nexport interface ResetPasswordUIState {\n showPassword: boolean;\n showConfirmPassword: boolean;\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/auth/ResetPasswordViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: ./ResetPasswordInterfaces","line":2,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":2,"endColumn":95}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../../contracts/view-models/ViewModel\";\nimport type { ResetPasswordFormState, ResetPasswordUIState } from \"./ResetPasswordInterfaces\";\n\n/**\n * Reset Password ViewModel\n * \n * Client-side state management for reset password flow.\n * Immutable, class-based, contains only UI state.\n */\nexport class ResetPasswordViewModel extends ViewModel {\n constructor(\n public readonly token: string,\n public readonly returnTo: string,\n public readonly formState: ResetPasswordFormState,\n public readonly uiState: ResetPasswordUIState,\n public readonly showSuccess: boolean = false,\n public readonly successMessage: string | null = null,\n public readonly mutationPending: boolean = false,\n public readonly mutationError: string | null = null\n ) {\n super();\n }\n\n withFormState(formState: ResetPasswordFormState): ResetPasswordViewModel {\n return new ResetPasswordViewModel(\n this.token,\n this.returnTo,\n formState,\n this.uiState,\n this.showSuccess,\n this.successMessage,\n this.mutationPending,\n this.mutationError\n );\n }\n\n withUIState(uiState: ResetPasswordUIState): ResetPasswordViewModel {\n return new ResetPasswordViewModel(\n this.token,\n this.returnTo,\n this.formState,\n uiState,\n this.showSuccess,\n this.successMessage,\n this.mutationPending,\n this.mutationError\n );\n }\n\n withSuccess(successMessage: string): ResetPasswordViewModel {\n return new ResetPasswordViewModel(\n this.token,\n this.returnTo,\n this.formState,\n this.uiState,\n true,\n successMessage,\n false,\n null\n );\n }\n\n withMutationState(pending: boolean, error: string | null): ResetPasswordViewModel {\n return new ResetPasswordViewModel(\n this.token,\n this.returnTo,\n this.formState,\n this.uiState,\n this.showSuccess,\n this.successMessage,\n pending,\n error\n );\n }\n\n get isSubmitting(): boolean {\n return this.formState.isSubmitting || this.mutationPending;\n }\n\n get submitError(): string | undefined {\n return this.formState.submitError || this.mutationError || undefined;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/auth/SignupInterfaces.ts","messages":[{"ruleId":"gridpilot-rules/view-model-implements","severity":2,"message":"ViewModel files must be classes named *ViewModel","line":1,"column":1,"nodeType":"Program","messageId":"notAClass","endLine":26,"endColumn":1}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"export interface SignupFormField {\n value: string;\n error?: string;\n touched: boolean;\n validating: boolean;\n}\n\nexport interface SignupFormState {\n fields: {\n firstName: SignupFormField;\n lastName: SignupFormField;\n email: SignupFormField;\n password: SignupFormField;\n confirmPassword: SignupFormField;\n };\n isValid: boolean;\n isSubmitting: boolean;\n submitError?: string;\n submitCount: number;\n}\n\nexport interface SignupUIState {\n showPassword: boolean;\n showConfirmPassword: boolean;\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/auth/SignupViewModel.ts","messages":[{"ruleId":"gridpilot-rules/view-model-taxonomy","severity":2,"message":"ViewModels can only import from lib/contracts/, lib/view-models/, lib/view-data/, or lib/display-objects/. External imports are allowed. Found: ./SignupInterfaces","line":2,"column":1,"nodeType":"ImportDeclaration","messageId":"strictImport","endLine":2,"endColumn":74}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ViewModel } from \"../../contracts/view-models/ViewModel\";\nimport type { SignupFormState, SignupUIState } from \"./SignupInterfaces\";\n\n/**\n * Signup ViewModel\n * \n * Client-side state management for signup flow.\n * Immutable, class-based, contains only UI state.\n */\nexport class SignupViewModel extends ViewModel {\n constructor(\n public readonly returnTo: string,\n public readonly formState: SignupFormState,\n public readonly uiState: SignupUIState,\n public readonly mutationPending: boolean = false,\n public readonly mutationError: string | null = null\n ) {\n super();\n }\n\n withFormState(formState: SignupFormState): SignupViewModel {\n return new SignupViewModel(\n this.returnTo,\n formState,\n this.uiState,\n this.mutationPending,\n this.mutationError\n );\n }\n\n withUIState(uiState: SignupUIState): SignupViewModel {\n return new SignupViewModel(\n this.returnTo,\n this.formState,\n uiState,\n this.mutationPending,\n this.mutationError\n );\n }\n\n withMutationState(pending: boolean, error: string | null): SignupViewModel {\n return new SignupViewModel(\n this.returnTo,\n this.formState,\n this.uiState,\n pending,\n error\n );\n }\n\n get isSubmitting(): boolean {\n return this.formState.isSubmitting || this.mutationPending;\n }\n\n get submitError(): string | undefined {\n return this.formState.submitError || this.mutationError || undefined;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/marcmintel/Projects/gridpilot-3/apps/website/lib/view-models/index.ts","messages":[{"ruleId":"gridpilot-rules/view-model-implements","severity":2,"message":"ViewModel files must be classes named *ViewModel","line":1,"column":1,"nodeType":"Program","messageId":"notAClass","endLine":97,"endColumn":1}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"export * from \"./ActivityItemViewModel\";\nexport * from \"./AdminUserViewModel\";\nexport * from \"./AnalyticsDashboardViewModel\";\nexport * from \"./AnalyticsMetricsViewModel\";\nexport * from \"./AvailableLeaguesViewModel\";\nexport * from \"./AvatarGenerationViewModel\";\nexport * from \"./AvatarViewModel\";\nexport * from \"./BillingViewModel\";\nexport * from \"./CompleteOnboardingViewModel\";\nexport * from \"./CreateLeagueViewModel\";\nexport * from \"./CreateTeamViewModel\";\nexport * from \"./DeleteMediaViewModel\";\nexport * from \"./DriverLeaderboardItemViewModel\";\nexport * from \"./DriverLeaderboardViewModel\";\nexport * from \"./DriverProfileViewModel\";\nexport * from \"./DriverRegistrationStatusViewModel\";\nexport * from \"./DriverSummaryViewModel\";\nexport * from \"./DriverTeamViewModel\";\nexport * from \"./DriverViewModel\";\nexport * from \"./EmailSignupViewModel\";\nexport * from \"./HomeDiscoveryViewModel\";\nexport * from \"./ImportRaceResultsSummaryViewModel\";\nexport * from \"./LeagueAdminRosterJoinRequestViewModel\";\nexport * from \"./LeagueAdminRosterMemberViewModel\";\nexport * from \"./LeagueAdminScheduleViewModel\";\nexport * from \"./LeagueAdminViewModel\";\nexport * from \"./LeagueCardViewModel\";\nexport * from \"./LeagueDetailPageViewModel\";\nexport * from \"./LeagueDetailViewModel\";\nexport * from \"./LeagueJoinRequestViewModel\";\nexport * from \"./LeagueMembershipsViewModel\";\nexport * from \"./LeagueMemberViewModel\";\nexport * from \"./LeaguePageDetailViewModel\";\nexport * from \"./LeagueScheduleViewModel\";\nexport * from \"./LeagueScoringChampionshipViewModel\";\nexport * from \"./LeagueScoringConfigViewModel\";\nexport * from \"./LeagueScoringPresetsViewModel\";\nexport * from \"./LeagueScoringPresetViewModel\";\nexport * from \"./LeagueScoringSectionViewModel\";\nexport * from \"./LeagueSeasonSummaryViewModel\";\nexport * from \"./LeagueSettingsViewModel\";\nexport * from \"./LeagueStandingsViewModel\";\nexport * from \"./LeagueStatsViewModel\";\nexport * from \"./LeagueStewardingViewModel\";\nexport * from \"./LeagueSummaryViewModel\";\nexport * from \"./LeagueWalletViewModel\";\nexport * from \"./MediaViewModel\";\nexport * from \"./MembershipFeeViewModel\";\nexport * from \"./OnboardingViewModel\";\nexport * from \"./PaymentViewModel\";\nexport * from \"./PrizeViewModel\";\nexport * from \"./ProfileOverviewViewModel\";\nexport * from \"./ProtestDetailViewModel\";\nexport * from \"./ProtestDriverViewModel\";\nexport * from \"./ProtestViewModel\";\nexport * from \"./RaceDetailEntryViewModel\";\nexport * from \"./RaceDetailsViewModel\";\nexport * from \"./RaceDetailUserResultViewModel\";\nexport * from \"./RaceListItemViewModel\";\nexport * from \"./RaceResultsDetailViewModel\";\nexport * from \"./RaceResultViewModel\";\nexport * from \"./RacesPageViewModel\";\nexport * from \"./RaceStatsViewModel\";\nexport * from \"./RaceStewardingViewModel\";\nexport * from \"./RaceViewModel\";\nexport * from \"./RaceWithSOFViewModel\";\nexport * from \"./RecordEngagementInputViewModel\";\nexport * from \"./RecordEngagementOutputViewModel\";\nexport * from \"./RecordPageViewInputViewModel\";\nexport * from \"./RecordPageViewOutputViewModel\";\nexport * from \"./RemoveMemberViewModel\";\nexport * from \"./RenewalAlertViewModel\";\nexport * from \"./RequestAvatarGenerationViewModel\";\nexport * from \"./ScoringConfigurationViewModel\";\nexport * from \"./SessionViewModel\";\nexport * from \"./SponsorDashboardViewModel\";\nexport * from \"./SponsorSettingsViewModel\";\nexport * from \"./SponsorshipDetailViewModel\";\nexport * from \"./SponsorshipPricingViewModel\";\nexport * from \"./SponsorshipRequestViewModel\";\nexport * from \"./SponsorshipViewModel\";\nexport * from \"./SponsorSponsorshipsViewModel\";\nexport * from \"./SponsorViewModel\";\nexport * from \"./StandingEntryViewModel\";\nexport * from \"./TeamCardViewModel\";\nexport * from \"./TeamDetailsViewModel\";\nexport * from \"./TeamJoinRequestViewModel\";\nexport * from \"./TeamMemberViewModel\";\nexport * from \"./TeamSummaryViewModel\";\nexport * from \"./UpcomingRaceCardViewModel\";\nexport * from \"./UpdateAvatarViewModel\";\nexport * from \"./UpdateTeamViewModel\";\nexport * from \"./UploadMediaViewModel\";\nexport * from \"./UserProfileViewModel\";\nexport * from \"./WalletTransactionViewModel\";\nexport * from \"./WalletViewModel\";\n","usedDeprecatedRules":[]}]