(tasks: VaultTask[], today: Date)
| 281 | * the user's timezone is respected. Waiting overrides everything except Done. |
| 282 | * Tasks without a due date land in Today. */ |
| 283 | export function groupTasks(tasks: VaultTask[], today: Date): VaultTaskGroups { |
| 284 | const todayIso = toIsoDate(today) |
| 285 | const today_: VaultTask[] = [] |
| 286 | const upcoming: VaultTask[] = [] |
| 287 | const waiting: VaultTask[] = [] |
| 288 | const done: VaultTask[] = [] |
| 289 | let overdueCount = 0 |
| 290 | |
| 291 | for (const task of tasks) { |
| 292 | if (task.checked) { |
| 293 | done.push(task) |
| 294 | continue |
| 295 | } |
| 296 | if (task.waiting) { |
| 297 | waiting.push(task) |
| 298 | continue |
| 299 | } |
| 300 | if (!task.due) { |
| 301 | today_.push(task) |
| 302 | continue |
| 303 | } |
| 304 | if (task.due < todayIso) { |
| 305 | today_.push(task) |
| 306 | overdueCount += 1 |
| 307 | continue |
| 308 | } |
| 309 | if (task.due === todayIso) { |
| 310 | today_.push(task) |
| 311 | continue |
| 312 | } |
| 313 | upcoming.push(task) |
| 314 | } |
| 315 | |
| 316 | // Sort each bucket for stable, useful ordering. |
| 317 | const byDueThenPath = (a: VaultTask, b: VaultTask): number => { |
| 318 | const ad = a.due ?? '9999-99-99' |
| 319 | const bd = b.due ?? '9999-99-99' |
| 320 | if (ad !== bd) return ad < bd ? -1 : 1 |
| 321 | if (a.sourcePath !== b.sourcePath) return a.sourcePath < b.sourcePath ? -1 : 1 |
| 322 | return a.taskIndex - b.taskIndex |
| 323 | } |
| 324 | const priorityRank: Record<TaskPriority, number> = { high: 0, med: 1, low: 2 } |
| 325 | const byPriorityThenDue = (a: VaultTask, b: VaultTask): number => { |
| 326 | const ap = a.priority ? priorityRank[a.priority] : 3 |
| 327 | const bp = b.priority ? priorityRank[b.priority] : 3 |
| 328 | if (ap !== bp) return ap - bp |
| 329 | return byDueThenPath(a, b) |
| 330 | } |
| 331 | |
| 332 | today_.sort(byPriorityThenDue) |
| 333 | upcoming.sort(byDueThenPath) |
| 334 | waiting.sort(byPriorityThenDue) |
| 335 | done.sort((a, b) => { |
| 336 | if (a.sourcePath !== b.sourcePath) return a.sourcePath < b.sourcePath ? -1 : 1 |
| 337 | return a.taskIndex - b.taskIndex |
| 338 | }) |
| 339 | |
| 340 | return { today: today_, upcoming, waiting, done, overdueCount } |
no test coverage detected