"use client"; import { useEffect, useState } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { ScrollArea } from "@/components/ui/scroll-area"; import { format, isPast, isToday, isTomorrow, parseISO } from "date-fns"; import { AlertCircle, CheckCircle2, Clock, ExternalLink, Calendar, User, } from "lucide-react"; import Link from "next/link"; import { getGanttTaskUrl, getGanttTasksUrl } from "@/lib/config/sites"; interface TaskWithDueDate { id: string; title: string; status: string; priority: string; due_date: string | null; assignee_name: string | null; project_name: string | null; } interface TaskCalendarIntegrationProps { maxTasks?: number; } function getPriorityColor(priority: string): string { switch (priority) { case "urgent": return "bg-red-500/10 text-red-500 border-red-500/20"; case "high": return "bg-orange-500/10 text-orange-500 border-orange-500/20"; case "medium": return "bg-yellow-500/10 text-yellow-500 border-yellow-500/20"; default: return "bg-blue-500/10 text-blue-500 border-blue-500/20"; } } function getStatusIcon(status: string) { switch (status) { case "done": return ; case "in-progress": return ; default: return ; } } function formatDueDate(dateString: string): { text: string; isOverdue: boolean; isToday: boolean; isTomorrow: boolean } { const date = parseISO(dateString); const now = new Date(); return { text: format(date, "MMM d, yyyy"), isOverdue: isPast(date) && !isToday(date), isToday: isToday(date), isTomorrow: isTomorrow(date), }; } export function TaskCalendarIntegration({ maxTasks = 10 }: TaskCalendarIntegrationProps) { const [tasks, setTasks] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { async function fetchTasksWithDueDates() { try { // Fetch tasks from Supabase via our API const response = await fetch("/api/tasks/with-due-dates"); if (!response.ok) { throw new Error("Failed to fetch tasks"); } const data = await response.json(); setTasks(data.slice(0, maxTasks)); } catch (err) { setError(err instanceof Error ? err.message : "Failed to load tasks"); } finally { setIsLoading(false); } } fetchTasksWithDueDates(); }, [maxTasks]); if (isLoading) { return ( Tasks with Due Dates ); } if (error) { return ( Tasks with Due Dates {error} ); } const upcomingTasks = tasks.filter((t) => t.status !== "done"); const overdueTasks = upcomingTasks.filter((t) => t.due_date && formatDueDate(t.due_date).isOverdue); return ( Tasks with Due Dates {overdueTasks.length > 0 && ( {overdueTasks.length} overdue )} {tasks.length === 0 ? ( No tasks with due dates Tasks with due dates will appear here ) : ( <> {tasks.map((task) => { const dueInfo = task.due_date ? formatDueDate(task.due_date) : null; return ( {getStatusIcon(task.status)} {task.title} {dueInfo && ( {dueInfo.isToday ? "Today" : dueInfo.isTomorrow ? "Tomorrow" : dueInfo.text} )} {task.assignee_name && ( {task.assignee_name} )} {task.priority} ); })} View all tasks > )} ); }
{error}
No tasks with due dates
Tasks with due dates will appear here