feat: task management enhancements (cancelled status, recurrence, timestamps)

- Add 'cancelled' status to TaskStatus type, StatusBadge, TaskCard,
  TaskEditorView, TaskViewerView, TasksListView
- Add RecurrenceEditor component (none / interval / calendar rules)
- TaskEditorView: wire RecurrenceEditor, show started_at/completed_at
  timestamps read-only, include recurrence_rule in save payload
- TaskViewerView: show recurrence summary, timestamps in meta row
- tasks.ts: statusFilter/priorityFilter as arrays, add recurrence_rule
  to updateTask and createTask payloads

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-28 00:51:36 -04:00
parent 37e2420192
commit ac90548823
7 changed files with 337 additions and 33 deletions
+10 -10
View File
@@ -12,8 +12,8 @@ export const useTasksStore = defineStore("tasks", () => {
// Filter / pagination / sort state
const activeTagFilters = ref<string[]>([]);
const statusFilter = ref<TaskStatus | "">("");
const priorityFilter = ref<TaskPriority | "">("");
const statusFilter = ref<TaskStatus[]>([]);
const priorityFilter = ref<TaskPriority[]>([]);
const limit = ref(20);
const offset = ref(0);
const sortField = ref("updated_at");
@@ -28,9 +28,8 @@ export const useTasksStore = defineStore("tasks", () => {
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);
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));
@@ -72,6 +71,7 @@ export const useTasksStore = defineStore("tasks", () => {
project_id?: number | null;
milestone_id?: number | null;
parent_id?: number | null;
recurrence_rule?: Record<string, unknown> | null;
}): Promise<Task> {
try {
return await apiPost<Task>("/api/tasks", data);
@@ -84,7 +84,7 @@ export const useTasksStore = defineStore("tasks", () => {
async function updateTask(
id: number,
data: Partial<
Pick<Task, "title" | "body" | "tags" | "status" | "priority" | "due_date" | "project_id" | "milestone_id" | "parent_id">
Pick<Task, "title" | "body" | "tags" | "status" | "priority" | "due_date" | "project_id" | "milestone_id" | "parent_id" | "recurrence_rule">
>
): Promise<Task> {
try {
@@ -129,14 +129,14 @@ export const useTasksStore = defineStore("tasks", () => {
}
}
function setStatusFilter(status: TaskStatus | "") {
statusFilter.value = status;
function setStatusFilter(statuses: TaskStatus[]) {
statusFilter.value = statuses;
offset.value = 0;
refresh();
}
function setPriorityFilter(priority: TaskPriority | "") {
priorityFilter.value = priority;
function setPriorityFilter(priorities: TaskPriority[]) {
priorityFilter.value = priorities;
offset.value = 0;
refresh();
}