Files
FabledScribe/frontend/src/stores/tasks.ts
T
bvandeusen 22a3a3c1d1 Initial commit: note-taking/task-tracking app with LLM integration scaffold
Vue 3 + TypeScript frontend with Pinia stores, markdown rendering (marked + DOMPurify),
wikilink/tag linkification, and autocomplete. Quart async backend with SQLAlchemy 2.0,
PostgreSQL ARRAY columns, task-note companion linking, backlinks, and note-to-task
conversion. Docker Compose setup with PostgreSQL 16 and Ollama.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 23:35:44 -05:00

181 lines
4.5 KiB
TypeScript

import { ref } from "vue";
import { defineStore } from "pinia";
import { apiGet, apiPost, apiPut, apiPatch, apiDelete } from "@/api/client";
import type { Task, TaskListResponse, TaskStatus, TaskPriority } from "@/types/task";
export const useTasksStore = defineStore("tasks", () => {
const tasks = ref<Task[]>([]);
const currentTask = ref<Task | null>(null);
const total = ref(0);
const loading = ref(false);
// Filter / pagination / sort state
const activeTagFilters = ref<string[]>([]);
const statusFilter = ref<TaskStatus | "">("");
const priorityFilter = ref<TaskPriority | "">("");
const limit = ref(20);
const offset = ref(0);
const sortField = ref("updated_at");
const sortOrder = ref<"asc" | "desc">("desc");
const searchQuery = ref("");
async function refresh() {
loading.value = true;
try {
const searchParams = new URLSearchParams();
if (searchQuery.value) searchParams.set("q", searchQuery.value);
for (const t of activeTagFilters.value) {
searchParams.append("tag", t);
}
if (statusFilter.value) searchParams.set("status", statusFilter.value);
if (priorityFilter.value)
searchParams.set("priority", priorityFilter.value);
searchParams.set("sort", sortField.value);
searchParams.set("order", sortOrder.value);
searchParams.set("limit", String(limit.value));
searchParams.set("offset", String(offset.value));
const qs = searchParams.toString();
const data = await apiGet<TaskListResponse>(
`/api/tasks${qs ? `?${qs}` : ""}`
);
tasks.value = data.tasks;
total.value = data.total;
} finally {
loading.value = false;
}
}
async function fetchTask(id: number) {
loading.value = true;
try {
currentTask.value = await apiGet<Task>(`/api/tasks/${id}`);
} finally {
loading.value = false;
}
}
async function createTask(data: {
title: string;
description: string;
status?: TaskStatus;
priority?: TaskPriority;
due_date?: string | null;
}): Promise<Task> {
return await apiPost<Task>("/api/tasks", data);
}
async function updateTask(
id: number,
data: Partial<
Pick<Task, "title" | "description" | "status" | "priority" | "due_date">
>
): Promise<Task> {
const task = await apiPut<Task>(`/api/tasks/${id}`, data);
if (currentTask.value?.id === id) {
currentTask.value = task;
}
return task;
}
async function patchStatus(id: number, status: TaskStatus): Promise<Task> {
const task = await apiPatch<Task>(`/api/tasks/${id}/status`, { status });
// Update in list if present
const idx = tasks.value.findIndex((t) => t.id === id);
if (idx !== -1) {
tasks.value[idx] = task;
}
if (currentTask.value?.id === id) {
currentTask.value = task;
}
return task;
}
async function deleteTask(id: number) {
await apiDelete(`/api/tasks/${id}`);
tasks.value = tasks.value.filter((t) => t.id !== id);
if (currentTask.value?.id === id) {
currentTask.value = null;
}
}
function setStatusFilter(status: TaskStatus | "") {
statusFilter.value = status;
offset.value = 0;
refresh();
}
function setPriorityFilter(priority: TaskPriority | "") {
priorityFilter.value = priority;
offset.value = 0;
refresh();
}
function addTagFilter(tag: string) {
if (!activeTagFilters.value.includes(tag)) {
activeTagFilters.value.push(tag);
offset.value = 0;
refresh();
}
}
function removeTagFilter(tag: string) {
activeTagFilters.value = activeTagFilters.value.filter((t) => t !== tag);
offset.value = 0;
refresh();
}
function clearTagFilters() {
activeTagFilters.value = [];
offset.value = 0;
refresh();
}
function setSort(field: string, order: "asc" | "desc") {
sortField.value = field;
sortOrder.value = order;
offset.value = 0;
refresh();
}
function setOffset(newOffset: number) {
offset.value = newOffset;
refresh();
}
function setSearch(q: string) {
searchQuery.value = q;
offset.value = 0;
refresh();
}
return {
tasks,
currentTask,
total,
loading,
activeTagFilters,
statusFilter,
priorityFilter,
limit,
offset,
sortField,
sortOrder,
searchQuery,
refresh,
fetchTask,
createTask,
updateTask,
patchStatus,
deleteTask,
setStatusFilter,
setPriorityFilter,
addTagFilter,
removeTagFilter,
clearTagFilters,
setSort,
setOffset,
setSearch,
};
});