diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index ea46b9e..dfcfa75 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -3,7 +3,7 @@ import { ref, computed, onMounted, nextTick } from "vue"; import { apiGet } from "@/api/client"; import type { Note, NoteListResponse } from "@/types/note"; import type { Task, TaskListResponse } from "@/types/task"; -import type { Conversation, ToolCallRecord, Message } from "@/types/chat"; +import type { ToolCallRecord, Message } from "@/types/chat"; import NoteCard from "@/components/NoteCard.vue"; import TaskCard from "@/components/TaskCard.vue"; import ToolCallCard from "@/components/ToolCallCard.vue"; @@ -18,7 +18,7 @@ const dueTodayTasks = ref([]); const dueThisWeekTasks = ref([]); const highPriorityTasks = ref([]); const inProgressTasks = ref([]); -const recentChats = ref([]); +const otherTasks = ref([]); const loading = ref(true); const tasksStore = useTasksStore(); @@ -46,7 +46,6 @@ function sortByPriorityThenDate(tasks: Task[]): Task[] { const pa = PRIORITY_ORDER[a.priority ?? "none"] ?? 0; const pb = PRIORITY_ORDER[b.priority ?? "none"] ?? 0; if (pb !== pa) return pb - pa; - // Earlier due dates first; null due dates last if (a.due_date && b.due_date) return a.due_date.localeCompare(b.due_date); if (a.due_date) return -1; if (b.due_date) return 1; @@ -54,13 +53,27 @@ function sortByPriorityThenDate(tasks: Task[]): Task[] { }); } +function sortOtherTasks(tasks: Task[]): Task[] { + const withDate = tasks + .filter((t) => t.due_date) + .sort((a, b) => a.due_date!.localeCompare(b.due_date!)); + const withoutDate = tasks + .filter((t) => !t.due_date) + .sort((a, b) => { + const pa = PRIORITY_ORDER[a.priority ?? "none"] ?? 0; + const pb = PRIORITY_ORDER[b.priority ?? "none"] ?? 0; + return pb - pa; + }); + return [...withDate, ...withoutDate].slice(0, 10); +} + onMounted(async () => { const today = dateStr(0); const nextWeek = dateStr(7); - const [notesRes, overdueRes, dueSoonRes, highPriRes, inProgressRes, chatsRes] = + const [notesRes, overdueRes, dueSoonRes, highPriRes, inProgressRes, otherRes] = await Promise.allSettled([ - apiGet("/api/notes?sort=updated_at&order=desc&limit=5"), + apiGet("/api/notes?sort=updated_at&order=desc&limit=8"), apiGet( `/api/tasks?due_before=${today}&sort=due_date&order=asc&limit=20` ), @@ -73,22 +86,18 @@ onMounted(async () => { apiGet( `/api/tasks?status=in_progress&sort=updated_at&order=desc&limit=10` ), - apiGet<{ conversations: Conversation[]; total: number }>( - "/api/chat/conversations?limit=3&offset=0" + apiGet( + `/api/tasks?sort=updated_at&order=desc&limit=50` ), ]); if (notesRes.status === "fulfilled") { recentNotes.value = notesRes.value.notes; } - if (chatsRes.status === "fulfilled") { - recentChats.value = chatsRes.value.conversations; - } // Build seen-set incrementally so each section deduplicates against all above it const seen = new Set(); - // Overdue: past due, not done if (overdueRes.status === "fulfilled") { overdueTasks.value = sortByPriorityThenDate( overdueRes.value.tasks.filter((t) => t.status !== "done") @@ -96,7 +105,6 @@ onMounted(async () => { for (const t of overdueTasks.value) seen.add(t.id); } - // Due soon: split into "today" and "this week", exclude done if (dueSoonRes.status === "fulfilled") { const notDone = dueSoonRes.value.tasks.filter( (t) => t.status !== "done" && !seen.has(t.id) @@ -116,7 +124,6 @@ onMounted(async () => { for (const t of dueThisWeekTasks.value) seen.add(t.id); } - // High priority: not done, deduplicated if (highPriRes.status === "fulfilled") { highPriorityTasks.value = highPriRes.value.tasks.filter( (t) => t.status !== "done" && !seen.has(t.id) @@ -124,23 +131,41 @@ onMounted(async () => { for (const t of highPriorityTasks.value) seen.add(t.id); } - // In progress: deduplicated against all above if (inProgressRes.status === "fulfilled") { inProgressTasks.value = inProgressRes.value.tasks.filter( (t) => !seen.has(t.id) ); + for (const t of inProgressTasks.value) seen.add(t.id); + } + + if (otherRes.status === "fulfilled") { + const remaining = otherRes.value.tasks.filter( + (t) => t.status !== "done" && !seen.has(t.id) + ); + otherTasks.value = sortOtherTasks(remaining); } loading.value = false; nextTick(() => chatInputRef.value?.focus()); }); +const noTasks = computed( + () => + overdueTasks.value.length === 0 && + dueTodayTasks.value.length === 0 && + dueThisWeekTasks.value.length === 0 && + highPriorityTasks.value.length === 0 && + inProgressTasks.value.length === 0 && + otherTasks.value.length === 0 +); + const allTaskLists = [ () => overdueTasks, () => dueTodayTasks, () => dueThisWeekTasks, () => highPriorityTasks, () => inProgressTasks, + () => otherTasks, ]; function removeFromAllLists(id: number) { @@ -168,24 +193,20 @@ function onStatusToggle(id: number, status: TaskStatus) { } const chatInputRef = ref<{ focus: () => void } | null>(null); - const chatStore = useChatStore(); -// Warm default model after status fetch chatStore.fetchStatus().then(() => { if (chatStore.defaultModel) { chatStore.warmModel(chatStore.defaultModel); } }); -// Dashboard inline response state const dashboardConvId = ref(null); const dashboardDone = ref(false); const dashboardQuery = ref(""); const dashboardFinalContent = ref(""); const dashboardFinalToolCalls = ref([]); -// True when the response is pure text with no tool actions — suggest continuing in chat const isConversational = computed( () => dashboardDone.value && dashboardFinalToolCalls.value.length === 0 ); @@ -208,10 +229,8 @@ async function onChatSubmit(payload: { content: string; contextNoteId?: number } await chatStore.fetchConversation(conv.id); dashboardConvId.value = conv.id; - // Stream inline — sendMessage awaits until SSE is complete await chatStore.sendMessage(payload.content, payload.contextNoteId); - // Capture final response from the message pushed by the store's done handler const msgs = chatStore.currentConversation?.messages ?? []; const lastAssistant = [...msgs].reverse().find((m: Message) => m.role === "assistant"); dashboardFinalContent.value = lastAssistant?.content ?? ""; @@ -234,176 +253,165 @@ function clearDashboardResponse() {