mycode/myApp/LemonLimeTracker/IOS/ViewModels/DashboardViewModel.swift
2026-06-19 19:53:54 +09:00

85 lines
2.7 KiB
Swift

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<Goal>()
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<TaskItem>(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 }
}
}