import { getServiceSupabase } from "@/lib/supabase/client"; export interface Task { id: string; title: string; description?: string; type: "idea" | "task" | "bug" | "research" | "plan"; status: "open" | "todo" | "blocked" | "in-progress" | "review" | "validate" | "archived" | "canceled" | "done"; priority: "low" | "medium" | "high" | "urgent"; projectId: string; sprintId?: string; createdAt: string; updatedAt: string; createdById?: string; createdByName?: string; createdByAvatarUrl?: string; updatedById?: string; updatedByName?: string; updatedByAvatarUrl?: string; assigneeId?: string; assigneeName?: string; assigneeEmail?: string; assigneeAvatarUrl?: string; dueDate?: string; comments: unknown[]; tags: string[]; attachments: unknown[]; } const TASK_STATUSES: Task["status"][] = ["open", "todo", "blocked", "in-progress", "review", "validate", "archived", "canceled", "done"]; function isTaskStatus(value: unknown): value is Task["status"] { return typeof value === "string" && TASK_STATUSES.includes(value as Task["status"]); } function toNonEmptyString(value: unknown): string | undefined { return typeof value === "string" && value.trim().length > 0 ? value : undefined; } function mapTaskRow(row: Record): Task { const fallbackDate = new Date().toISOString(); return { id: String(row.id ?? ""), title: toNonEmptyString(row.title) ?? "", description: toNonEmptyString(row.description), type: (toNonEmptyString(row.type) as Task["type"]) ?? "task", status: isTaskStatus(row.status) ? row.status : "todo", priority: (toNonEmptyString(row.priority) as Task["priority"]) ?? "medium", projectId: String(row.project_id ?? ""), sprintId: toNonEmptyString(row.sprint_id), createdAt: toNonEmptyString(row.created_at) ?? fallbackDate, updatedAt: toNonEmptyString(row.updated_at) ?? fallbackDate, createdById: toNonEmptyString(row.created_by_id), createdByName: toNonEmptyString(row.created_by_name), createdByAvatarUrl: toNonEmptyString(row.created_by_avatar_url), updatedById: toNonEmptyString(row.updated_by_id), updatedByName: toNonEmptyString(row.updated_by_name), updatedByAvatarUrl: toNonEmptyString(row.updated_by_avatar_url), assigneeId: toNonEmptyString(row.assignee_id), assigneeName: toNonEmptyString(row.assignee_name), assigneeEmail: toNonEmptyString(row.assignee_email), assigneeAvatarUrl: toNonEmptyString(row.assignee_avatar_url), dueDate: toNonEmptyString(row.due_date), comments: Array.isArray(row.comments) ? row.comments : [], tags: Array.isArray(row.tags) ? row.tags.filter((tag): tag is string => typeof tag === "string") : [], attachments: Array.isArray(row.attachments) ? row.attachments : [], }; } /** * Fetch all tasks from Supabase */ export async function fetchAllTasks(): Promise { const supabase = getServiceSupabase(); const { data, error } = await supabase .from("tasks") .select("*") .order("created_at", { ascending: true }); if (error) { console.error("Error fetching tasks:", error); throw new Error(`Failed to fetch tasks: ${error.message}`); } return (data || []).map((row) => mapTaskRow(row as Record)); } /** * Fetch active tasks (status != 'done') */ export async function fetchActiveTasks(): Promise { const supabase = getServiceSupabase(); const { data, error } = await supabase .from("tasks") .select("*") .neq("status", "done") .order("created_at", { ascending: true }); if (error) { console.error("Error fetching active tasks:", error); throw new Error(`Failed to fetch active tasks: ${error.message}`); } return (data || []).map((row) => mapTaskRow(row as Record)); } /** * Count active tasks */ export async function countActiveTasks(): Promise { const supabase = getServiceSupabase(); const { count, error } = await supabase .from("tasks") .select("*", { count: "exact", head: true }) .neq("status", "done"); if (error) { console.error("Error counting active tasks:", error); return 0; } return count || 0; } /** * Count total tasks */ export async function countTotalTasks(): Promise { const supabase = getServiceSupabase(); const { count, error } = await supabase .from("tasks") .select("*", { count: "exact", head: true }); if (error) { console.error("Error counting total tasks:", error); return 0; } return count || 0; } /** * Get task counts by status */ export interface TaskStatusCounts { open: number; inProgress: number; review: number; done: number; total: number; } export async function getTaskStatusCounts(): Promise { const supabase = getServiceSupabase(); // Get all tasks and count by status const { data, error } = await supabase .from("tasks") .select("status"); if (error) { console.error("Error fetching task status counts:", error); return { open: 0, inProgress: 0, review: 0, done: 0, total: 0 }; } const counts = { open: 0, inProgress: 0, review: 0, done: 0, total: 0 }; for (const row of data || []) { counts.total++; const status = row.status; if (status === "open" || status === "todo" || status === "blocked") { counts.open++; } else if (status === "in-progress") { counts.inProgress++; } else if (status === "review" || status === "validate") { counts.review++; } else if (status === "done") { counts.done++; } } return counts; } /** * Count high priority tasks (high or urgent priority, not done) */ export async function countHighPriorityTasks(): Promise { const supabase = getServiceSupabase(); const { count, error } = await supabase .from("tasks") .select("*", { count: "exact", head: true }) .in("priority", ["high", "urgent"]) .neq("status", "done"); if (error) { console.error("Error counting high priority tasks:", error); return 0; } return count || 0; } /** * Count overdue tasks (due date in the past, not done) */ export async function countOverdueTasks(): Promise { const supabase = getServiceSupabase(); const today = new Date().toISOString().split("T")[0]; const { count, error } = await supabase .from("tasks") .select("*", { count: "exact", head: true }) .lt("due_date", today) .neq("status", "done") .not("due_date", "is", null); if (error) { console.error("Error counting overdue tasks:", error); return 0; } return count || 0; } /** * Fetch recently updated tasks (last 5) */ export async function fetchRecentlyUpdatedTasks(limit = 5): Promise { const supabase = getServiceSupabase(); const { data, error } = await supabase .from("tasks") .select("*") .order("updated_at", { ascending: false }) .limit(limit); if (error) { console.error("Error fetching recently updated tasks:", error); return []; } return (data || []).map((row) => mapTaskRow(row as Record)); } /** * Fetch recently completed tasks (last 5) */ export async function fetchRecentlyCompletedTasks(limit = 5): Promise { const supabase = getServiceSupabase(); const { data, error } = await supabase .from("tasks") .select("*") .eq("status", "done") .order("updated_at", { ascending: false }) .limit(limit); if (error) { console.error("Error fetching recently completed tasks:", error); return []; } return (data || []).map((row) => mapTaskRow(row as Record)); } /** * Fetch high priority open tasks (top 5) */ export async function fetchHighPriorityOpenTasks(limit = 5): Promise { const supabase = getServiceSupabase(); const { data, error } = await supabase .from("tasks") .select("*") .in("priority", ["high", "urgent"]) .neq("status", "done") .order("updated_at", { ascending: false }) .limit(limit); if (error) { console.error("Error fetching high priority open tasks:", error); return []; } return (data || []).map((row) => mapTaskRow(row as Record)); }