Files
PlantGuide/PlantGuide/Presentation/Scenes/PlantDetail/Components/AllTasksView.swift
Trey t 681476a499 WIP: Various UI and feature improvements
- Add AllTasksView and PlantEditView components
- Update CoreDataStack CloudKit container ID
- Improve CameraView and IdentificationViewModel
- Update MainTabView, RoomsListView, UpcomingTasksSection
- Minor fixes to PlantGuideApp and SettingsViewModel

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 22:50:04 -06:00

159 lines
4.2 KiB
Swift

//
// AllTasksView.swift
// PlantGuide
//
// Created on 2026-01-31.
//
import SwiftUI
// MARK: - AllTasksView
/// Displays all pending care tasks for a plant
struct AllTasksView: View {
// MARK: - Properties
let plantName: String
let tasks: [CareTask]
var onTaskComplete: ((CareTask) -> Void)?
// MARK: - Body
var body: some View {
Group {
if tasks.isEmpty {
emptyStateView
} else {
taskListView
}
}
.navigationTitle("All Tasks")
.navigationBarTitleDisplayMode(.inline)
.background(Color(.systemGroupedBackground))
}
// MARK: - Task List View
private var taskListView: some View {
ScrollView {
VStack(spacing: 16) {
// Overdue tasks section
let overdueTasks = tasks.filter { $0.isOverdue }
if !overdueTasks.isEmpty {
taskSection(title: "Overdue", tasks: overdueTasks, tintColor: .red)
}
// Today's tasks
let todayTasks = tasks.filter { !$0.isOverdue && Calendar.current.isDateInToday($0.scheduledDate) }
if !todayTasks.isEmpty {
taskSection(title: "Today", tasks: todayTasks, tintColor: .blue)
}
// Upcoming tasks
let upcomingTasks = tasks.filter { !$0.isOverdue && !Calendar.current.isDateInToday($0.scheduledDate) }
if !upcomingTasks.isEmpty {
taskSection(title: "Upcoming", tasks: upcomingTasks, tintColor: .secondary)
}
}
.padding()
}
}
// MARK: - Task Section
private func taskSection(title: String, tasks: [CareTask], tintColor: Color) -> some View {
VStack(alignment: .leading, spacing: 12) {
HStack {
Text(title)
.font(.headline)
.foregroundStyle(tintColor)
Spacer()
Text("\(tasks.count)")
.font(.subheadline)
.foregroundStyle(.secondary)
}
VStack(spacing: 8) {
ForEach(tasks) { task in
TaskRow(task: task, onComplete: {
onTaskComplete?(task)
})
}
}
}
.padding()
.background(Color(.systemGray6))
.cornerRadius(12)
}
// MARK: - Empty State View
private var emptyStateView: some View {
VStack(spacing: 16) {
Spacer()
Image(systemName: "checkmark.circle")
.font(.system(size: 60))
.foregroundStyle(.green)
Text("All Caught Up!")
.font(.title2)
.fontWeight(.semibold)
Text("No pending care tasks for \(plantName)")
.font(.subheadline)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
Spacer()
}
.padding()
}
}
// MARK: - Previews
#Preview("All Tasks - With Tasks") {
let samplePlantID = UUID()
let sampleTasks = [
CareTask(
plantID: samplePlantID,
type: .watering,
scheduledDate: Date().addingTimeInterval(-86400), // 1 day ago (overdue)
notes: "Check soil moisture"
),
CareTask(
plantID: samplePlantID,
type: .fertilizing,
scheduledDate: Date(), // Today
notes: ""
),
CareTask(
plantID: samplePlantID,
type: .pruning,
scheduledDate: Date().addingTimeInterval(86400 * 3), // 3 days from now
notes: "Remove dead leaves"
),
CareTask(
plantID: samplePlantID,
type: .watering,
scheduledDate: Date().addingTimeInterval(86400 * 7), // 7 days from now
notes: ""
)
]
return NavigationStack {
AllTasksView(plantName: "Monstera", tasks: sampleTasks) { task in
print("Completed task: \(task.type)")
}
}
}
#Preview("All Tasks - Empty") {
NavigationStack {
AllTasksView(plantName: "Monstera", tasks: [])
}
}