feat(todo): Today, Upcoming, Inbox, projects, quick add and the task sheet (ADR 0033, D8, D9)

The tabs hold the person's tasks: Today what is overdue (Reschedule
moving it all to a day, times kept) and what is due today; Upcoming the
overdue ones and the next four weeks day by day; Inbox and each
project's screen from Browse the tasks above and in their sections, done
ones on asking; Browse the projects with their open counts and the
labels. A task's row shows its check in its priority's colour, its date
in the colour of how near it is, its deadline, its sub-tasks done and
its labels; quick add takes its title, date, priority, project, labels
and description and stays for the next; its sheet edits all of it, adds
sub-tasks and deletes it with them after asking. Colours are the
palette's, cerise for overdue and P1 against the strawberry wash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-01 10:25:34 +01:00
commit 030c62ddde
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -0,0 +1,109 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:halkyon_todo_flutter/src/feature/task/model/task.dart';
import 'package:halkyon_todo_flutter/src/feature/task/model/task_views.dart';
/// What Todo's lists show of the person's tasks (ADR 0033, D8).
void main() {
const today = Day(2026, 11, 1);
final done = DateTime(2026, 11, 1, 8);
OpenTask task(
String id, {
String project = 'inbox',
String? section,
String? parent,
double position = 0,
Day? day,
int? time,
Priority priority = Priority.none,
bool completed = false,
}) => (
id: id,
content: TaskContent(
title: id,
projectId: project,
sectionId: section,
parentId: parent,
position: position,
date: day == null ? null : TaskDate(day, time: time),
priority: priority,
completedAt: completed ? done : null,
),
);
test("a project's list: its own tasks above its sections or in one, no "
'sub-tasks, done ones only when asked, in their order', () {
final tasks = [
task('b', position: 2),
task('a', position: 1),
task('sub', parent: 'a'),
task('in section', section: 's'),
task('done', completed: true),
task('elsewhere', project: 'home'),
];
expect(tasksIn(tasks, 'inbox').map((t) => t.id), ['a', 'b']);
expect(
tasksIn(tasks, 'inbox', completed: true).map((t) => t.id),
['done', 'a', 'b'],
);
expect(tasksIn(tasks, 'inbox', sectionId: 's').map((t) => t.id), [
'in section',
]);
expect(subTasksOf(tasks, 'a').map((t) => t.id), ['sub']);
expect(openCountIn(tasks, 'inbox'), 4);
});
test("Today: what is overdue, oldest first, never done ones; then today's, "
'timed first by their time, then by priority', () {
final tasks = [
task('late', day: today.plus(-2)),
task('later', day: today.plus(-1)),
task('late but done', day: today.plus(-3), completed: true),
task('any time', day: today),
task('urgent', day: today, priority: Priority.p1),
task('at 9', day: today, time: 9 * 60),
task('at 7', day: today, time: 7 * 60),
task('done today', day: today, completed: true),
task('tomorrow', day: today.plus(1)),
task('no date'),
];
final (:overdue, :due) = todayOf(tasks, today);
expect(overdue.map((t) => t.id), ['late', 'later']);
expect(due.map((t) => t.id), ['at 7', 'at 9', 'urgent', 'any time']);
expect(
todayOf(tasks, today, completed: true).due.map((t) => t.id),
contains('done today'),
);
});
test('Upcoming: the overdue ones, then every day from today on, empty '
'days kept', () {
final tasks = [
task('late', day: today.plus(-1)),
task('today', day: today),
task('in three days', day: today.plus(3)),
task('too far', day: today.plus(30)),
];
final (:overdue, :days) = upcomingOf(tasks, today, days: 5);
expect(overdue.map((t) => t.id), ['late']);
expect(
[for (final (day, _) in days) day],
[
for (var i = 0; i < 5; i++) today.plus(i),
],
);
expect(
[for (final (_, list) in days) list.map((t) => t.id).toList()],
[
['today'],
<String>[],
<String>[],
['in three days'],
<String>[],
],
);
});
}