Task Progress
Task Progress is a React and Tailwind CSS feedback component from HextaUI, licensed MIT. Copy it and paste it into your project.
HextaUI
MIT
feedback
What it is
Task Progress is a React and Tailwind CSS feedback component from HextaUI, licensed MIT. Copy it and paste it into your project.
How to use it
Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.
Five free copies a month. No card.
No packages beyond the project baseline
Licence
This component comes from HextaUI and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.
Task Progress
"use client";
import { CheckCircle2, Target, TrendingUp } from "lucide-react";
import { useMemo } from "react";
import { cn } from "@/lib/utils";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/registry/new-york/ui/card";
import { Progress } from "@/registry/new-york/ui/progress";
import type { Task } from "./task-list";
export interface TaskProgressProps {
tasks?: Task[];
goal?: number;
period?: "day" | "week" | "month" | "all";
className?: string;
showBreakdown?: boolean;
showTrend?: boolean;
}
export default function TaskProgress({
tasks = [],
goal,
period = "all",
className,
showBreakdown = true,
showTrend = true,
}: TaskProgressProps) {
const stats = useMemo(() => {
const total = tasks.length;
const completed = tasks.filter((t) => t.status === "done").length;
const inProgress = tasks.filter((t) => t.status === "in_progress").length;
const todo = tasks.filter((t) => t.status === "todo").length;
const cancelled = tasks.filter((t) => t.status === "cancelled").length;
const completionRate = total > 0 ? (completed / total) * 100 : 0;
return {
total,
completed,
inProgress,
todo,
cancelled,
completionRate,
};
}, [tasks]);
const progressPercentage = goal
? Math.min((stats.completed / goal) * 100, 100)
: stats.completionRate;
return (
<Card className={cn("w-full shadow-xs", className)}>
<CardHeader>
<div className="flex flex-col gap-1">
<CardTitle>Progress Overview</CardTitle>
<CardDescription>
{stats.completed} of {stats.total} tasks completed
{goal && ` (Goal: ${goal})`}
</CardDescription>
</div>
</CardHeader>
<CardContent>
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-2">
<div className="flex items-center justify-between">
<span className="text-muted-foreground text-sm">
Overall Progress
</span>
<span className="font-semibold text-sm">
{Math.round(progressPercentage)}%
</span>
</div>
<Progress className="h-2" value={progressPercentage} />
{goal && (
<div className="flex items-center justify-between text-xs">
<span className="text-muted-foreground">
{stats.completed} / {goal} completed
</span>
{stats.completed >= goal && (
<div className="flex items-center gap-1 text-green-600">
<Target className="size-3" />
Goal achieved!
</div>
)}
</div>
)}
</div>
{showBreakdown && (
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
<div className="flex flex-col gap-2 rounded-lg border bg-card p-3">
<div className="flex items-center gap-2">
<CheckCircle2 className="size-4 text-green-600" />
<span className="text-muted-foreground text-xs">
Completed
</span>
</div>
<div className="flex items-baseline gap-1">
<span className="font-semibold text-2xl">
{stats.completed}
</span>
<span className="text-muted-foreground text-xs">
({Math.round((stats.completed / stats.total) * 100) || 0}%)
</span>
</div>
</div>
<div className="flex flex-col gap-2 rounded-lg border bg-card p-3">
<div className="flex items-center gap-2">
<TrendingUp className="size-4 text-blue-600" />
<span className="text-muted-foreground text-xs">
In Progress
</span>
</div>
<div className="flex items-baseline gap-1">
<span className="font-semibold text-2xl">
{stats.inProgress}
</span>
<span className="text-muted-foreground text-xs">
({Math.round((stats.inProgress / stats.total) * 100) || 0}%)
</span>
</div>
</div>
<div className="flex flex-col gap-2 rounded-lg border bg-card p-3">
<div className="flex items-center gap-2">
<Target className="size-4 text-muted-foreground" />
<span className="text-muted-foreground text-xs">To Do</span>
</div>
<div className="flex items-baseline gap-1">
<span className="font-semibold text-2xl">{stats.todo}</span>
<span className="text-muted-foreground text-xs">
({Math.round((stats.todo / stats.total) * 100) || 0}%)
</span>
</div>
</div>
<div className="flex flex-col gap-2 rounded-lg border bg-card p-3">
<div className="flex items-center gap-2">
<Target className="size-4 text-muted-foreground" />
<span className="text-muted-foreground text-xs">Total</span>
</div>
<div className="flex items-baseline gap-1">
<span className="font-semibold text-2xl">{stats.total}</span>
<span className="text-muted-foreground text-xs">tasks</span>
</div>
</div>
</div>
)}
</div>
</CardContent>
</Card>
);
}