Files
FrenoCorp/Lendair/ViewModels/TrainingPlanViewModel.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

109 lines
3.4 KiB
Swift

import Foundation
import SwiftUI
@MainActor
class TrainingPlanViewModel: ObservableObject {
@Published var plans: [TrainingPlan] = []
@Published var selectedPlan: TrainingPlan?
@Published var isLoading: Bool = false
@Published var error: TrainingPlanError?
private let service: TrainingPlanServiceProtocol
init(service: TrainingPlanServiceProtocol = TrainingPlanService()) {
self.service = service
}
func fetchPlans(type: PlanType? = nil, difficulty: Difficulty? = nil) async {
isLoading = true
error = nil
defer { isLoading = false }
do {
plans = try await service.listPlans(type: type, difficulty: difficulty)
} catch let error as TrainingPlanError {
self.error = error
} catch {
print("Failed to fetch training plans: \(error)")
}
}
func selectPlan(id: String) async {
isLoading = true
error = nil
defer { isLoading = false }
do {
selectedPlan = try await service.getPlan(id: id)
} catch let error as TrainingPlanError {
self.error = error
} catch {
print("Failed to get plan: \(error)")
}
}
func generatePlan(request: GeneratePlanRequest) async -> TrainingPlan? {
isLoading = true
error = nil
defer { isLoading = false }
do {
let plan = try await service.generatePlan(request: request)
plans.insert(plan, at: 0)
return plan
} catch let error as TrainingPlanError {
self.error = error
return nil
} catch {
print("Failed to generate plan: \(error)")
return nil
}
}
func followPlan(id: String) async {
do {
try await service.followPlan(id: id)
if let index = plans.firstIndex(where: { $0.id == id }) {
plans[index].isFollowing = true
objectWillChange.send()
}
} catch {
print("Failed to follow plan: \(error)")
}
}
func unfollowPlan(id: String) async {
do {
try await service.unfollowPlan(id: id)
if let index = plans.firstIndex(where: { $0.id == id }) {
plans[index].isFollowing = false
objectWillChange.send()
}
} catch {
print("Failed to unfollow plan: \(error)")
}
}
func updateSessionStatus(sessionId: String, status: SessionStatus) async {
do {
try await service.updateSessionStatus(sessionId: sessionId, status: status)
if var plan = selectedPlan {
for weekIndex in plan.weeklyWorkouts.indices {
for sessionIndex in plan.weeklyWorkouts[weekIndex].dailySessions.indices {
if plan.weeklyWorkouts[weekIndex].dailySessions[sessionIndex].id == sessionId {
plan.weeklyWorkouts[weekIndex].dailySessions[sessionIndex].status = status
selectedPlan = plan
return
}
}
}
}
} catch {
print("Failed to update session status: \(error)")
}
}
var planTypes: [PlanType] { PlanType.allCases }
var difficulties: [Difficulty] { Difficulty.allCases }
}