feat(tasks): round-based grouping — FAB shows current round only

- New round starts when all tasks complete before next TaskCreate
- FAB counts only current round (e.g. 0/4 instead of 20/24)
- Bottom sheet: current round on top, collapsible "Previous tasks" history
- O(1) Set lookup for history filtering

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
kuannnn
2026-03-29 08:02:14 +08:00
parent a1ada37cba
commit b4d55c4de3
4 changed files with 99 additions and 9 deletions
+32 -2
View File
@@ -85,8 +85,28 @@ function TaskRow({ task, taskMap }: { task: AggregatedTask; taskMap: Map<string,
);
}
function HistorySection({ tasks, taskMap }: { tasks: AggregatedTask[]; taskMap: Map<string, AggregatedTask> }) {
const [expanded, setExpanded] = useState(false);
const completed = tasks.filter(t => t.status === 'completed').length;
return (
<div className="border-t border-border/30 mt-2 pt-2">
<button
onClick={() => setExpanded(!expanded)}
className="flex items-center justify-between w-full text-xs text-text-dim py-1"
>
<span>Previous tasks ({completed}/{tasks.length})</span>
{expanded ? <ChevronUp className="size-3" /> : <ChevronDown className="size-3" />}
</button>
{expanded && tasks.map(task => (
<TaskRow key={`${task.source}:${task.id}`} task={task} taskMap={taskMap} />
))}
</div>
);
}
export function TaskBottomSheet({ snapshot, open, onClose }: TaskBottomSheetProps) {
const { tasks, completed, total } = snapshot;
const { tasks, currentRound, completed, total, hasHistory } = snapshot;
const pct = total > 0 ? Math.round((completed / total) * 100) : 0;
const taskMap = useMemo(() => {
@@ -95,6 +115,12 @@ export function TaskBottomSheet({ snapshot, open, onClose }: TaskBottomSheetProp
return map;
}, [tasks]);
const historyTasks = useMemo(() => {
if (!hasHistory) return [];
const currentIds = new Set(currentRound.map(t => `${t.source}:${t.id}`));
return tasks.filter(t => !currentIds.has(`${t.source}:${t.id}`));
}, [tasks, currentRound, hasHistory]);
return (
<BottomSheet visible={open} onClose={onClose} className="max-h-[70vh] flex flex-col">
<div className="flex items-center justify-between px-4 pb-2">
@@ -107,13 +133,17 @@ export function TaskBottomSheet({ snapshot, open, onClose }: TaskBottomSheetProp
</div>
<div className="flex-1 overflow-y-auto px-4 pb-4">
{tasks.map(task => (
{currentRound.map(task => (
<TaskRow
key={`${task.source}:${task.id}`}
task={task}
taskMap={taskMap}
/>
))}
{historyTasks.length > 0 && (
<HistorySection tasks={historyTasks} taskMap={taskMap} />
)}
</div>
</BottomSheet>
);