diff --git a/frontend/src/components/TaskCard.vue b/frontend/src/components/TaskCard.vue index 28176ff..3a272db 100644 --- a/frontend/src/components/TaskCard.vue +++ b/frontend/src/components/TaskCard.vue @@ -122,19 +122,7 @@ function isOverdue(): boolean { display: flex; align-items: center; gap: 0.5rem; - padding: 0.4rem 0.75rem; - box-shadow: none; - border-radius: 0; - border-bottom: 1px solid var(--color-border); - transform: none !important; -} -.task-card.compact:first-child { - border-top: 1px solid var(--color-border); -} -.task-card.compact:hover { - box-shadow: none; - background: rgba(99, 102, 241, 0.04); - transform: none; + padding: 0.45rem 0.85rem; } /* Status dot */ diff --git a/frontend/src/views/TasksListView.vue b/frontend/src/views/TasksListView.vue index 762b0eb..21cd4aa 100644 --- a/frontend/src/views/TasksListView.vue +++ b/frontend/src/views/TasksListView.vue @@ -8,9 +8,8 @@ import { useListKeyboardNavigation } from "@/composables/useListKeyboardNavigati import SearchBar from "@/components/SearchBar.vue"; import TaskCard from "@/components/TaskCard.vue"; import TagPill from "@/components/TagPill.vue"; -import PaginationBar from "@/components/PaginationBar.vue"; -type ViewMode = "flat" | "grouped"; +type ViewMode = "smart" | "grouped"; const route = useRoute(); const router = useRouter(); @@ -22,29 +21,23 @@ function onFocusSearch() { searchBarRef.value?.focus(); } -const viewMode = ref( - (localStorage.getItem("fabled-tasks-view-mode") as ViewMode) ?? "flat" -); +const _storedMode = localStorage.getItem("fabled-tasks-view-mode"); +const viewMode = ref(_storedMode === "grouped" ? "grouped" : "smart"); function setViewMode(mode: ViewMode) { viewMode.value = mode; localStorage.setItem("fabled-tasks-view-mode", mode); - if (mode === "grouped") { - store.limit = 100; - store.offset = 0; - } else { - store.limit = 20; - store.offset = 0; - } + store.limit = mode === "grouped" ? 100 : 200; + store.offset = 0; store.refresh(); } -const isFlat = computed(() => viewMode.value === "flat"); -const { activeIndex } = useListKeyboardNavigation( +// Keyboard nav disabled — smart sections span multiple containers +useListKeyboardNavigation( computed(() => store.tasks), (task) => router.push(`/tasks/${task.id}`), ".kb-active-item", - isFlat, + computed(() => false), ); // Project map for group labels and task breadcrumbs @@ -66,6 +59,36 @@ interface TaskGroup { tasks: Task[]; } +interface TaskSection { + key: string; + label: string; + tasks: Task[]; +} + +const smartSections = computed(() => { + if (viewMode.value !== "smart") return []; + const today = new Date().toISOString().slice(0, 10); + const weekEnd = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10); + const overdue: Task[] = [], dueToday: Task[] = [], thisWeek: Task[] = [], + upcoming: Task[] = [], noDueDate: Task[] = [], done: Task[] = []; + for (const task of store.tasks) { + if (task.status === "done") { done.push(task); continue; } + if (!task.due_date) { noDueDate.push(task); continue; } + if (task.due_date < today) { overdue.push(task); continue; } + if (task.due_date === today) { dueToday.push(task); continue; } + if (task.due_date <= weekEnd) { thisWeek.push(task); continue; } + upcoming.push(task); + } + const sections: TaskSection[] = []; + if (overdue.length) sections.push({ key: "overdue", label: "Overdue", tasks: overdue }); + if (dueToday.length) sections.push({ key: "today", label: "Due Today", tasks: dueToday }); + if (thisWeek.length) sections.push({ key: "week", label: "This Week", tasks: thisWeek }); + if (upcoming.length) sections.push({ key: "upcoming", label: "Upcoming", tasks: upcoming }); + if (noDueDate.length) sections.push({ key: "no-date", label: "No Due Date", tasks: noDueDate }); + if (done.length) sections.push({ key: "done", label: "Completed", tasks: done }); + return sections; +}); + const groupedTasks = computed(() => { if (viewMode.value !== "grouped") return []; const map = new Map(); @@ -101,9 +124,8 @@ onMounted(async () => { if (route.query.status) { store.statusFilter = route.query.status as TaskStatus; } - if (viewMode.value === "grouped") { - store.limit = 100; - } + store.limit = viewMode.value === "grouped" ? 100 : 200; + collapsedGroups.value.add("done"); await Promise.all([store.refresh(), loadProjects()]); document.addEventListener("shortcut:focus-search", onFocusSearch); }); @@ -167,10 +189,6 @@ function onStatusToggle(id: number, status: TaskStatus) { store.patchStatus(id, status); } -function onOffsetUpdate(offset: number) { - store.setOffset(offset); -} - // Collapse state for grouped sections const collapsedGroups = ref>(new Set()); function toggleGroup(key: string) { @@ -222,9 +240,9 @@ function toggleGroup(key: string) {