Countdown + assignee names

X-Lovable-Edit-ID: edt-a7699745-29db-48d0-9843-3a673de2d4da
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-18 01:03:07 +00:00
co-authored by renee-png
+41 -9
View File
@@ -18,7 +18,7 @@ import {
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import { Plus, Search, Calendar as CalIcon, AlertCircle } from "lucide-react";
import { format, isBefore, startOfDay, isSameDay, addDays } from "date-fns";
import { format, isBefore, startOfDay, isSameDay, addDays, differenceInCalendarDays } from "date-fns";
import { TaskDetailPanel } from "@/components/tasks/task-detail-panel";
import { NewTaskDialog } from "@/components/tasks/new-task-dialog";
import { cn } from "@/lib/utils";
@@ -280,6 +280,22 @@ function TasksPage() {
{items.map((t) => {
const ids = assigneesByTask[t.id] || [];
const c = t.case_id ? casesById[t.case_id] : null;
const assigneeNames = ids
.map((uid) => profilesById[uid])
.filter(Boolean)
.map((p) => p.full_name || p.email);
let countdown: { label: string; tone: string } | null = null;
if (t.due_date && t.status !== "complete") {
const days = differenceInCalendarDays(startOfDay(new Date(t.due_date)), startOfDay(new Date()));
if (days < 0)
countdown = {
label: `${Math.abs(days)}d overdue`,
tone: "text-destructive",
};
else if (days === 0) countdown = { label: "Due today", tone: "text-orange-600" };
else if (days === 1) countdown = { label: "Due tomorrow", tone: "text-orange-600" };
else countdown = { label: `in ${days}d`, tone: "text-muted-foreground" };
}
return (
<div
key={t.id}
@@ -296,16 +312,32 @@ function TasksPage() {
<div className={cn("text-sm", t.status === "complete" && "line-through text-muted-foreground")}>
{t.title}
</div>
{c && (
<div className="text-[11px] text-muted-foreground truncate">
{c.case_number} — {c.title}
</div>
)}
<div className="flex flex-wrap gap-x-3 gap-y-0.5 text-[11px] text-muted-foreground">
{c && (
<span className="truncate">
{c.case_number} — {c.title}
</span>
)}
{assigneeNames.length > 0 && (
<span className="truncate">
Assigned: {assigneeNames.slice(0, 2).join(", ")}
{assigneeNames.length > 2 ? ` +${assigneeNames.length - 2}` : ""}
</span>
)}
{!assigneeNames.length && <span className="italic">Unassigned</span>}
</div>
</div>
{t.due_date && (
<div className="text-xs text-muted-foreground flex items-center gap-1">
<CalIcon className="h-3 w-3" />
{format(new Date(t.due_date), "MMM d")}
<div className="text-xs text-right shrink-0">
<div className="text-muted-foreground flex items-center gap-1 justify-end">
<CalIcon className="h-3 w-3" />
{format(new Date(t.due_date), "MMM d, yyyy")}
</div>
{countdown && (
<div className={cn("text-[11px] font-medium", countdown.tone)}>
{countdown.label}
</div>
)}
</div>
)}
<div className="flex -space-x-1.5">