diff --git a/frontend/src/stores/notes.ts b/frontend/src/stores/notes.ts index 2a1cab6..1bf6a8a 100644 --- a/frontend/src/stores/notes.ts +++ b/frontend/src/stores/notes.ts @@ -80,6 +80,8 @@ export const useNotesStore = defineStore("notes", () => { tags?: string[]; project_id?: number | null; milestone_id?: number | null; + note_type?: string; + metadata?: Record | null; }): Promise { try { return await apiPost("/api/notes", data); @@ -91,7 +93,7 @@ export const useNotesStore = defineStore("notes", () => { async function updateNote( id: number, - data: Partial> + data: Partial> ): Promise { try { const note = await apiPut(`/api/notes/${id}`, data); diff --git a/frontend/src/types/note.ts b/frontend/src/types/note.ts index 55f182a..1e7c414 100644 --- a/frontend/src/types/note.ts +++ b/frontend/src/types/note.ts @@ -1,5 +1,6 @@ export type TaskStatus = "todo" | "in_progress" | "done" | "cancelled"; export type TaskPriority = "none" | "low" | "medium" | "high"; +export type NoteType = "note" | "person" | "place" | "list"; export interface Note { id: number; @@ -18,6 +19,8 @@ export interface Note { recurrence_rule: Record | null; recurrence_next_spawn_at: string | null; is_task: boolean; + note_type: NoteType; + metadata: Record; created_at: string; updated_at: string; } diff --git a/frontend/src/views/KnowledgeView.vue b/frontend/src/views/KnowledgeView.vue index ad87da6..c881a7e 100644 --- a/frontend/src/views/KnowledgeView.vue +++ b/frontend/src/views/KnowledgeView.vue @@ -50,6 +50,28 @@ const sortMode = ref<"modified" | "created" | "alpha" | "type">("modified"); const searchQuery = ref(""); let searchDebounce: ReturnType | null = null; +// ─── Type counts ────────────────────────────────────────────────────────────── + +interface KnowledgeCounts { note: number; person: number; place: number; list: number; total: number } +const typeCounts = ref({ note: 0, person: 0, place: 0, list: 0, total: 0 }); + +async function fetchCounts() { + try { + const p = new URLSearchParams(); + if (activeTag.value) p.set("tags", activeTag.value); + typeCounts.value = await apiGet(`/api/knowledge/counts?${p}`); + } catch { /* silent */ } +} + +// ─── New note dropdown ──────────────────────────────────────────────────────── + +const newNoteMenuOpen = ref(false); + +function createNew(type: string) { + newNoteMenuOpen.value = false; + router.push(type === "note" ? "/notes/new" : `/notes/new?type=${type}`); +} + // ─── Two-tier pagination ────────────────────────────────────────────────────── const ID_BATCH = 100; // IDs fetched per server round-trip @@ -156,7 +178,8 @@ function onSearchInput() { searchDebounce = setTimeout(() => resetAndReobserve(), 380); } -watch([activeType, activeTag, sortMode], () => resetAndReobserve()); +watch([activeType, sortMode], () => resetAndReobserve()); +watch(activeTag, () => { fetchCounts(); resetAndReobserve(); }); // ─── Today bar ──────────────────────────────────────────────────────────────── @@ -323,6 +346,7 @@ function setupObserver() { onMounted(async () => { await reset(); fetchTags(); + fetchCounts(); fetchTodayBar(); await nextTick(); setupObserver(); @@ -366,15 +390,38 @@ onUnmounted(() => {