Files
FrenoCorp/Lendair/Models/Race.swift
Michael Freno 57a460761a FRE-4665: Implement Phase 3 AI training plans and premium features
- Models: TrainingPlan, Race, FamilyPlan, BeginnerMode, CommunityEvent
- Services: 5 service layers with protocol-based architecture
- ViewModels: 5 view models with @MainActor ObservableObject pattern
- Views: 10 SwiftUI views for all Phase 3 features
- Updated README with full Phase 3 documentation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-03 15:21:01 -04:00

184 lines
4.4 KiB
Swift

import Foundation
import SwiftUI
// MARK: - Race
struct Race: Identifiable, Equatable, Codable {
let id: String
let name: String
let description: String
let location: String
let latitude: Double
let longitude: Double
let raceDate: Date
let distanceKm: Double
let raceType: RaceType
let organizerName: String
let registrationUrl: String?
let imageUrl: String?
let participantCount: Int?
let isRegistered: Bool
let isSaved: Bool
let elevationGain: Double
let terrainType: TerrainType
enum CodingKeys: String, CodingKey {
case id, name, description, location, latitude, longitude, raceDate, distanceKm, raceType, organizerName, registrationUrl, imageUrl, participantCount, isRegistered, isSaved, elevationGain, terrainType
}
init(
id: String,
name: String,
description: String,
location: String,
latitude: Double,
longitude: Double,
raceDate: Date,
distanceKm: Double,
raceType: RaceType,
organizerName: String,
registrationUrl: String?,
imageUrl: String?,
participantCount: Int?,
isRegistered: Bool,
isSaved: Bool,
elevationGain: Double,
terrainType: TerrainType
) {
self.id = id
self.name = name
self.description = description
self.location = location
self.latitude = latitude
self.longitude = longitude
self.raceDate = raceDate
self.distanceKm = distanceKm
self.raceType = raceType
self.organizerName = organizerName
self.registrationUrl = registrationUrl
self.imageUrl = imageUrl
self.participantCount = participantCount
self.isRegistered = isRegistered
self.isSaved = isSaved
self.elevationGain = elevationGain
self.terrainType = terrainType
}
static func == (lhs: Race, rhs: Race) -> Bool {
lhs.id == rhs.id && lhs.isRegistered == rhs.isRegistered && lhs.isSaved == rhs.isSaved
}
var daysUntilRace: Int {
let calendar = Calendar.current
return calendar.dateComponents([.day], from: Date(), to: raceDate).day ?? 0
}
var isUpcoming: Bool {
raceDate > Date()
}
}
// MARK: - Race Type
enum RaceType: String, CaseIterable, Codable {
case road
case trail
case track
case virtual
var displayName: String {
switch self {
case .road: return "Road"
case .trail: return "Trail"
case .track: return "Track"
case .virtual: return "Virtual"
}
}
var icon: String {
switch self {
case .road: return "car.fill"
case .trail: return "mountain.2.fill"
case .track: return "circle.fill"
case .virtual: return "globe"
}
}
}
// MARK: - Terrain Type
enum TerrainType: String, CaseIterable, Codable {
case flat
case rolling
case hilly
case mountainous
var displayName: String {
switch self {
case .flat: return "Flat"
case .rolling: return "Rolling"
case .hilly: return "Hilly"
case .mountainous: return "Mountainous"
}
}
}
// MARK: - Race Filter
struct RaceFilter: Encodable {
var distanceKm: Double?
var raceType: RaceType?
var terrainType: TerrainType?
var startDate: Date?
var endDate: Date?
var location: String?
var radiusKm: Double?
var limit: Int
var offset: Int
init(
distanceKm: Double? = nil,
raceType: RaceType? = nil,
terrainType: TerrainType? = nil,
startDate: Date? = nil,
endDate: Date? = nil,
location: String? = nil,
radiusKm: Double? = nil,
limit: Int = 20,
offset: Int = 0
) {
self.distanceKm = distanceKm
self.raceType = raceType
self.terrainType = terrainType
self.startDate = startDate
self.endDate = endDate
self.location = location
self.radiusKm = radiusKm
self.limit = limit
self.offset = offset
}
}
// MARK: - API Response Types
struct RaceListResponse: Decodable {
let races: [Race]
let hasMore: Bool
}
struct RaceDetailResponse: Decodable {
let race: Race
}
struct SaveRaceResponse: Decodable {
let success: Bool
let raceId: String
let isSaved: Bool
}
struct RegisterRaceResponse: Decodable {
let success: Bool
let raceId: String
let registrationUrl: String?
}