- 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>
159 lines
4.2 KiB
Swift
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: [])
|
|
}
|
|
}
|