Add task completion animations and fix 7-day task count
Animation Testing: - Add AnimationTesting module with 14 animation types for task completion - Include 4 celebration animations: Implode, Firework, Starburst, Ripple - Card shrinks, shows checkmark with effect, then moves to next column - Extended timing (2.2s) for celebration animations Task Count Fix: - Fix "7 Days" and "30 Days" counts on residence cards and dashboard - Previously used API column membership (30-day "due soon" column) - Now calculates actual days until due from task's effectiveDueDate - Correctly counts tasks due within 7 days vs 8-30 days 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -280,6 +280,21 @@ final class WidgetDataManager {
|
||||
)
|
||||
}
|
||||
|
||||
/// Parse a date string (handles both "yyyy-MM-dd" and ISO datetime formats)
|
||||
/// Extracts date part if it includes time (e.g., "2025-12-26T00:00:00Z" -> "2025-12-26")
|
||||
private static let dateFormatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "yyyy-MM-dd"
|
||||
return formatter
|
||||
}()
|
||||
|
||||
static func parseDate(_ dateString: String?) -> Date? {
|
||||
guard let dateString = dateString, !dateString.isEmpty else { return nil }
|
||||
// Extract date part if it includes time
|
||||
let datePart = dateString.components(separatedBy: "T").first ?? dateString
|
||||
return dateFormatter.date(from: datePart)
|
||||
}
|
||||
|
||||
/// Get the shared App Group container URL
|
||||
private var sharedContainerURL: URL? {
|
||||
FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier)
|
||||
@@ -300,6 +315,9 @@ final class WidgetDataManager {
|
||||
// Columns to exclude from widget (these are "done" states)
|
||||
let excludedColumns = [Self.completedColumn, Self.cancelledColumn]
|
||||
|
||||
// Date calculation setup
|
||||
let today = Calendar.current.startOfDay(for: Date())
|
||||
|
||||
// Extract tasks from active columns only and convert to WidgetTask
|
||||
var allTasks: [WidgetTask] = []
|
||||
|
||||
@@ -309,12 +327,26 @@ final class WidgetDataManager {
|
||||
continue
|
||||
}
|
||||
|
||||
// Determine flags based on column name (using shared constants)
|
||||
// isOverdue is based on column (API correctly calculates this)
|
||||
let isOverdue = column.name == Self.overdueColumn
|
||||
let isDueWithin7Days = column.name == Self.dueWithin7DaysColumn
|
||||
let isDue8To30Days = column.name == Self.due8To30DaysColumn
|
||||
|
||||
for task in column.tasks {
|
||||
// Calculate isDueWithin7Days and isDue8To30Days from actual due date
|
||||
var isDueWithin7Days = false
|
||||
var isDue8To30Days = false
|
||||
|
||||
if let dueDate = Self.parseDate(task.effectiveDueDate) {
|
||||
let dueDay = Calendar.current.startOfDay(for: dueDate)
|
||||
let daysUntilDue = Calendar.current.dateComponents([.day], from: today, to: dueDay).day ?? 0
|
||||
|
||||
// Only count future tasks (not overdue)
|
||||
if daysUntilDue >= 0 && daysUntilDue <= 7 {
|
||||
isDueWithin7Days = true
|
||||
} else if daysUntilDue > 7 && daysUntilDue <= 30 {
|
||||
isDue8To30Days = true
|
||||
}
|
||||
}
|
||||
|
||||
let widgetTask = WidgetTask(
|
||||
id: Int(task.id),
|
||||
title: task.title,
|
||||
|
||||
Reference in New Issue
Block a user