Unify task stats calculation and update UI labels
- Create shared TaskStatsCalculator for consistent date bucket logic - Fix widget stats to use exclusive buckets (overdue | 7 days | 30 days) - Update labels: "This Week" → "Next 7 Days" / "7 Days" - Large widget now shows Overdue, 7 Days, 30 Days (removed Total) - Rename "Pros" tab to "Contractors" - Remove red pulsing ring from residence card icons 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -203,50 +203,26 @@ struct SimpleEntry: TimelineEntry {
|
||||
upcomingTasks.first
|
||||
}
|
||||
|
||||
/// Tasks due within the next 7 days
|
||||
var dueThisWeekCount: Int {
|
||||
let calendar = Calendar.current
|
||||
let today = calendar.startOfDay(for: Date())
|
||||
let weekFromNow = calendar.date(byAdding: .day, value: 7, to: today)!
|
||||
|
||||
return upcomingTasks.filter { task in
|
||||
guard let dueDateString = task.dueDate else { return false }
|
||||
guard let dueDate = parseDate(dueDateString) else { return false }
|
||||
let dueDay = calendar.startOfDay(for: dueDate)
|
||||
return dueDay <= weekFromNow
|
||||
}.count
|
||||
/// Computed task stats using shared TaskStatsCalculator
|
||||
/// Uses exclusive buckets: overdue | next 7 days | next 30 days (8-30)
|
||||
private var calculatedStats: TaskStats {
|
||||
let dueDates = upcomingTasks.map { $0.dueDate }
|
||||
return TaskStatsCalculator.calculate(from: dueDates)
|
||||
}
|
||||
|
||||
/// Tasks due within the next 30 days
|
||||
/// Overdue tasks count
|
||||
var overdueCount: Int {
|
||||
calculatedStats.overdueCount
|
||||
}
|
||||
|
||||
/// Tasks due within the next 7 days (exclusive of overdue)
|
||||
var dueNext7DaysCount: Int {
|
||||
calculatedStats.next7DaysCount
|
||||
}
|
||||
|
||||
/// Tasks due within next 30 days (days 8-30, exclusive of next 7 days)
|
||||
var dueNext30DaysCount: Int {
|
||||
let calendar = Calendar.current
|
||||
let today = calendar.startOfDay(for: Date())
|
||||
let thirtyDaysFromNow = calendar.date(byAdding: .day, value: 30, to: today)!
|
||||
|
||||
return upcomingTasks.filter { task in
|
||||
guard let dueDateString = task.dueDate else { return false }
|
||||
guard let dueDate = parseDate(dueDateString) else { return false }
|
||||
let dueDay = calendar.startOfDay(for: dueDate)
|
||||
return dueDay <= thirtyDaysFromNow
|
||||
}.count
|
||||
}
|
||||
|
||||
/// Parse date string to Date
|
||||
private func parseDate(_ dateString: String) -> Date? {
|
||||
let dateOnlyFormatter = DateFormatter()
|
||||
dateOnlyFormatter.dateFormat = "yyyy-MM-dd"
|
||||
if let date = dateOnlyFormatter.date(from: dateString) {
|
||||
return date
|
||||
}
|
||||
|
||||
let isoFormatter = ISO8601DateFormatter()
|
||||
isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
||||
if let date = isoFormatter.date(from: dateString) {
|
||||
return date
|
||||
}
|
||||
|
||||
isoFormatter.formatOptions = [.withInternetDateTime]
|
||||
return isoFormatter.date(from: dateString)
|
||||
calculatedStats.next30DaysCount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,30 +537,30 @@ struct LargeWidgetStatsView: View {
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 0) {
|
||||
// Total Tasks
|
||||
// Overdue
|
||||
StatItem(
|
||||
value: entry.taskCount,
|
||||
label: "Total",
|
||||
color: .blue
|
||||
value: entry.overdueCount,
|
||||
label: "Overdue",
|
||||
color: entry.overdueCount > 0 ? .red : .secondary
|
||||
)
|
||||
|
||||
Divider()
|
||||
.frame(height: 30)
|
||||
|
||||
// Due This Week
|
||||
// Next 7 Days (exclusive of overdue)
|
||||
StatItem(
|
||||
value: entry.dueThisWeekCount,
|
||||
label: "This Week",
|
||||
value: entry.dueNext7DaysCount,
|
||||
label: "7 Days",
|
||||
color: .orange
|
||||
)
|
||||
|
||||
Divider()
|
||||
.frame(height: 30)
|
||||
|
||||
// Due Next 30 Days
|
||||
// Next 30 Days (days 8-30)
|
||||
StatItem(
|
||||
value: entry.dueNext30DaysCount,
|
||||
label: "Next 30 Days",
|
||||
label: "30 Days",
|
||||
color: .green
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user