import { ref } from "vue"; import { defineStore } from "pinia"; import { apiGet, apiPost, apiPut, apiPatch, apiDelete } from "@/api/client"; import { useToastStore } from "@/stores/toast"; import type { Task, TaskListResponse, TaskStatus, TaskPriority } from "@/types/task"; export const useTasksStore = defineStore("tasks", () => { const tasks = ref([]); const currentTask = ref(null); const total = ref(0); const loading = ref(false); // Filter / pagination / sort state const activeTagFilters = ref([]); const statusFilter = ref([]); const priorityFilter = ref([]); 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); } for (const s of statusFilter.value) searchParams.append("status", s); for (const p of priorityFilter.value) searchParams.append("priority", p); 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( `/api/tasks${qs ? `?${qs}` : ""}` ); tasks.value = data.tasks; total.value = data.total; } catch (e) { useToastStore().show("Failed to load tasks", "error"); throw e; } finally { loading.value = false; } } async function fetchTask(id: number) { loading.value = true; try { currentTask.value = await apiGet(`/api/tasks/${id}`); } catch (e) { useToastStore().show("Failed to load task", "error"); throw e; } finally { loading.value = false; } } async function createTask(data: { title: string; body: string; tags?: string[]; status?: TaskStatus; priority?: TaskPriority; due_date?: string | null; project_id?: number | null; milestone_id?: number | null; parent_id?: number | null; recurrence_rule?: Record | null; }): Promise { try { return await apiPost("/api/tasks", data); } catch (e) { useToastStore().show("Failed to create task", "error"); throw e; } } async function updateTask( id: number, data: Partial< Pick > ): Promise { try { const task = await apiPut(`/api/tasks/${id}`, data); if (currentTask.value?.id === id) { currentTask.value = task; } return task; } catch (e) { useToastStore().show("Failed to update task", "error"); throw e; } } async function patchStatus(id: number, status: TaskStatus): Promise { try { const task = await apiPatch(`/api/tasks/${id}/status`, { status }); 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; } catch (e) { useToastStore().show("Failed to update task status", "error"); throw e; } } async function deleteTask(id: number) { try { await apiDelete(`/api/tasks/${id}`); tasks.value = tasks.value.filter((t) => t.id !== id); if (currentTask.value?.id === id) { currentTask.value = null; } } catch (e) { useToastStore().show("Failed to delete task", "error"); throw e; } } function setStatusFilter(statuses: TaskStatus[]) { statusFilter.value = statuses; offset.value = 0; refresh(); } function setPriorityFilter(priorities: TaskPriority[]) { priorityFilter.value = priorities; 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, }; });