summaryrefslogtreecommitdiffhomepage
path: root/packages/frontend/src/lib/components/TaskListPanel.svelte
blob: 1f84bb8ed4ee887ee1428b8ebb16e31dcec42968 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<script lang="ts">
import type { TaskItem } from "../types.js";

const { tasks }: { tasks: TaskItem[] } = $props();

type Status = TaskItem["status"];

const completedCount = $derived(tasks.filter((t) => t.status === "completed").length);
const inProgressCount = $derived(tasks.filter((t) => t.status === "in_progress").length);
const cancelledCount = $derived(tasks.filter((t) => t.status === "cancelled").length);
// "Active" total excludes cancelled items, so progress reads as work that still counts.
const activeTotal = $derived(tasks.length - cancelledCount);

function checkboxClass(status: Status): string {
	switch (status) {
		case "pending":
			return "checkbox checkbox-sm rounded-sm checkbox-secondary";
		case "in_progress":
			return "checkbox checkbox-sm rounded-sm checkbox-info";
		case "completed":
			return "checkbox checkbox-sm rounded-sm checkbox-success";
		case "cancelled":
			return "checkbox checkbox-sm rounded-sm checkbox-neutral";
	}
}

function isChecked(status: Status): boolean {
	return status === "completed";
}

function isIndeterminate(status: Status): boolean {
	return status === "in_progress";
}

function rowClass(status: Status): string {
	if (status === "completed") return "opacity-60";
	if (status === "cancelled") return "opacity-40";
	return "";
}

function textClass(status: Status): string {
	switch (status) {
		case "completed":
			return "line-through text-base-content/50";
		case "cancelled":
			return "line-through text-base-content/40";
		case "in_progress":
			return "font-semibold";
		default:
			return "";
	}
}
</script>

<div class="flex flex-col gap-2">
	{#if tasks.length === 0}
		<p class="text-xs text-base-content/50">No tasks yet.</p>
	{:else}
		<p class="text-xs text-base-content/60">
			{completedCount}/{activeTotal} completed{#if inProgressCount > 0}, {inProgressCount} in progress{/if}{#if cancelledCount > 0}, {cancelledCount} cancelled{/if}
		</p>
		<ul class="flex flex-col gap-0.5">
			{#each tasks as task (task.id)}
				<li class="flex items-start gap-2 rounded p-1.5 transition-colors {rowClass(task.status)}">
					<input
						type="checkbox"
						class={checkboxClass(task.status)}
						checked={isChecked(task.status)}
						indeterminate={isIndeterminate(task.status)}
						disabled
						tabindex="-1"
					/>
					<span class="text-xs leading-tight min-w-0 {textClass(task.status)}">
						{task.content}
					</span>
				</li>
			{/each}
		</ul>
	{/if}
</div>