import Foundation import Observation import SwiftData @MainActor @Observable final class DashboardViewModel { private(set) var dailyGoalProgress: [GoalProgress] = [] private(set) var remainingTasks: [TaskItem] = [] private(set) var completedTaskCount: Int = 0 private(set) var totalTaskCount: Int = 0 private var context: ModelContext? var overallProgress: Double { if !dailyGoalProgress.isEmpty { let sum = dailyGoalProgress.reduce(0.0) { $0 + $1.ratio } return sum / Double(dailyGoalProgress.count) } guard totalTaskCount > 0 else { return 0 } return Double(completedTaskCount) / Double(totalTaskCount) } var topGoalProgress: [GoalProgress] { Array(dailyGoalProgress.sorted { $0.ratio < $1.ratio }.prefix(3)) } func setup(context: ModelContext) { self.context = context refresh() } func refresh() { guard let context else { return } let goalDescriptor = FetchDescriptor() let goals = (try? context.fetch(goalDescriptor)) ?? [] let dailyGoals = goals.filter { $0.frequency == .daily } dailyGoalProgress = dailyGoals.map { goal in let current = todayValue(for: goal) return GoalProgress(goal: goal, current: current, target: goal.conditions) } let taskDescriptor = FetchDescriptor(sortBy: [SortDescriptor(\.name)]) let tasks = (try? context.fetch(taskDescriptor)) ?? [] totalTaskCount = tasks.count remainingTasks = tasks.filter { !isCompletedToday($0) } completedTaskCount = totalTaskCount - remainingTasks.count } func addCount(to task: TaskItem) { guard let context else { return } let log = TaskLog(date: .now, duration: 0, count: 1) log.task = task context.insert(log) try? context.save() refresh() } private func isCompletedToday(_ task: TaskItem) -> Bool { let todayLogs = task.logs.filter { Calendar.current.isDateInToday($0.date) } switch task.type { case .count: return todayLogs.reduce(0) { $0 + $1.count } > 0 case .time: return !todayLogs.isEmpty } } private func todayValue(for goal: Goal) -> Double { let logs: [TaskLog] if let task = goal.task { logs = task.logs } else if let category = goal.category { logs = category.tasks.flatMap { $0.logs } } else { logs = [] } let todayLogs = logs.filter { Calendar.current.isDateInToday($0.date) } return goal.targetType == "count" ? Double(todayLogs.reduce(0) { $0 + $1.count }) : todayLogs.reduce(0.0) { $0 + $1.duration } } }