Matrix: classic Eisenhower — you set importance, deadline sets urgency

Quadrant is now computed (importance x urgency) instead of a fixed
assignment. Importance comes from the task's assigned row (Q1/Q2 vs
Q3/Q4); urgency is derived from the (next active) due date being within
urgentWindowDays (7). So important tasks slide Q2->Q1 as they near,
unimportant slide Q4->Q3, and recurring tasks roll forward (a completed
occurrence is replaced by the next, never piling up in a quadrant).

- VM: displayQuadrant/isUrgent/effectiveDue; matrixTasks groups by the
  computed quadrant and uses each recurring task's single next occurrence.
- MatrixView grid + drill-down (overdue/later/completed) group by it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
kutesir
2026-06-10 10:00:29 +03:00
parent b4e36db763
commit 2f1bc5d1c3
2 changed files with 51 additions and 48 deletions

View File

@@ -56,7 +56,7 @@ struct MatrixView: View {
quadrant: .urgent,
roman: "I", label: "Urgent & Important",
badgeBg: AppColors.accentSoft, badgeColor: AppColors.accent,
tasks: taskVM.tasks(for: .urgent).filter { !hideCompleted || !$0.isComplete },
tasks: taskVM.matrixTasks(for: .urgent).filter { !hideCompleted || !$0.isComplete },
isDropTarget: dropTarget == .urgent,
onToggle: { taskVM.toggle($0) },
onDrop: { id in withAnimation(KisaniSpring.snappy) { taskVM.moveToQuadrant(taskID: id, quadrant: .urgent); dropTarget = nil } },
@@ -74,7 +74,7 @@ struct MatrixView: View {
quadrant: .schedule,
roman: "II", label: "Not Urgent & Important",
badgeBg: AppColors.yellowSoft, badgeColor: AppColors.yellow,
tasks: taskVM.tasks(for: .schedule).filter { !hideCompleted || !$0.isComplete },
tasks: taskVM.matrixTasks(for: .schedule).filter { !hideCompleted || !$0.isComplete },
isDropTarget: dropTarget == .schedule,
onToggle: { taskVM.toggle($0) },
onDrop: { id in withAnimation(KisaniSpring.snappy) { taskVM.moveToQuadrant(taskID: id, quadrant: .schedule); dropTarget = nil } },
@@ -96,7 +96,7 @@ struct MatrixView: View {
quadrant: .delegate_,
roman: "III", label: "Urgent & Unimportant",
badgeBg: AppColors.blueSoft, badgeColor: AppColors.blue,
tasks: taskVM.tasks(for: .delegate_).filter { !hideCompleted || !$0.isComplete },
tasks: taskVM.matrixTasks(for: .delegate_).filter { !hideCompleted || !$0.isComplete },
isDropTarget: dropTarget == .delegate_,
onToggle: { taskVM.toggle($0) },
onDrop: { id in withAnimation(KisaniSpring.snappy) { taskVM.moveToQuadrant(taskID: id, quadrant: .delegate_); dropTarget = nil } },
@@ -114,7 +114,7 @@ struct MatrixView: View {
quadrant: .eliminate,
roman: "IV", label: "Not Urgent & Unimportant",
badgeBg: AppColors.greenSoft, badgeColor: AppColors.green,
tasks: taskVM.tasks(for: .eliminate).filter { !hideCompleted || !$0.isComplete },
tasks: taskVM.matrixTasks(for: .eliminate).filter { !hideCompleted || !$0.isComplete },
isDropTarget: dropTarget == .eliminate,
onToggle: { taskVM.toggle($0) },
onDrop: { id in withAnimation(KisaniSpring.snappy) { taskVM.moveToQuadrant(taskID: id, quadrant: .eliminate); dropTarget = nil } },
@@ -408,21 +408,23 @@ struct QuadrantDetailView: View {
@State private var showAllCompleted = false
private let completedLimit = 5
// Grouped by the *computed* quadrant (importance × deadline urgency); recurring
// tasks contribute their single next-active occurrence.
private var overdueTasks: [TaskItem] {
let startOfDay = Calendar.current.startOfDay(for: Date())
return taskVM.tasks
.filter { $0.quadrant == quadrant && !$0.isComplete && ($0.dueDate ?? .distantFuture) < startOfDay }
return taskVM.matrixTasks(for: quadrant)
.filter { !$0.isComplete && ($0.dueDate ?? .distantFuture) < startOfDay }
.sorted { ($0.dueDate ?? .distantFuture) < ($1.dueDate ?? .distantFuture) }
}
private var laterTasks: [TaskItem] {
let startOfDay = Calendar.current.startOfDay(for: Date())
return taskVM.tasks
.filter { $0.quadrant == quadrant && !$0.isComplete && ($0.dueDate ?? .distantFuture) >= startOfDay }
return taskVM.matrixTasks(for: quadrant)
.filter { !$0.isComplete && ($0.dueDate ?? .distantFuture) >= startOfDay }
.sorted { ($0.dueDate ?? .distantFuture) < ($1.dueDate ?? .distantFuture) }
}
private var completedTasks: [TaskItem] {
taskVM.tasks
.filter { $0.quadrant == quadrant && $0.isComplete }
taskVM.matrixTasks(for: quadrant)
.filter { $0.isComplete }
.sorted { ($0.completedAt ?? .distantPast) > ($1.completedAt ?? .distantPast) }
}
private var allEmpty: Bool { overdueTasks.isEmpty && laterTasks.isEmpty && completedTasks.isEmpty }