Organize and present data efficiently with Tailwind CSS dashboard tables. These layouts feature sortable columns, clean typography, and responsive design to make complex information easy to understand. Perfect for admin panels, analytics dashboards, and SaaS platforms seeking structured and user-friendly data presentation.
"use client";
import { CheckCircle, FileTextIcon, Loader2, PauseIcon, PlayIcon, Trash2Icon } from "lucide-react";
import { useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow
} from "@/components/ui/table";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
interface Task {
id: string;
title: string;
assignee: string;
status: "pending" | "in-progress" | "completed" | "blocked";
priority: "low" | "medium" | "high" | "urgent";
dueDate: string;
notes: string;
}
type TaskActionType = "start" | "pause" | "complete" | "delete" | "view";
const tasks: Task[] = [
{
id: "TASK-001",
title: "Implement User Authentication",
assignee: "Sarah Chen",
status: "in-progress",
priority: "high",
dueDate: "2024-03-25",
notes: "OAuth 2.0 integration with Google and GitHub providers"
},
{
id: "TASK-002",
title: "Design Dashboard UI",
assignee: "Michael Torres",
status: "completed",
priority: "medium",
dueDate: "2024-03-20",
notes: "Finalize dashboard layout with responsive grid system"
},
{
id: "TASK-003",
title: "API Performance Optimization",
assignee: "Emma Rodriguez",
status: "pending",
priority: "urgent",
dueDate: "2024-03-22",
notes: "Reduce API response time by implementing caching strategy"
},
{
id: "TASK-004",
title: "Write Unit Tests",
assignee: "James Wilson",
status: "in-progress",
priority: "medium",
dueDate: "2024-03-28",
notes: "Achieve 80% code coverage for authentication module"
},
{
id: "TASK-005",
title: "Database Migration",
assignee: "Olivia Martinez",
status: "blocked",
priority: "high",
dueDate: "2024-03-24",
notes: "Waiting for infrastructure team approval before proceeding"
},
{
id: "TASK-006",
title: "Update Documentation",
assignee: "Lucas Anderson",
status: "pending",
priority: "low",
dueDate: "2024-03-30",
notes: "Document new API endpoints and authentication flow"
},
{
id: "TASK-007",
title: "Security Audit",
assignee: "Sophia Taylor",
status: "completed",
priority: "urgent",
dueDate: "2024-03-19",
notes: "Conducted comprehensive security review and vulnerability assessment"
}
];
function getStatusBadge(status: Task["status"]) {
switch (status) {
case "pending":
return (
<Badge
variant="outline"
className="border-0 bg-amber-500/15 text-amber-700 hover:bg-amber-500/25 dark:bg-amber-500/10 dark:text-amber-300 dark:hover:bg-amber-500/20">
Pending
</Badge>
);
case "in-progress":
return (
<Badge
variant="outline"
className="border-0 bg-blue-500/15 text-blue-700 hover:bg-blue-500/25 dark:bg-blue-500/10 dark:text-blue-400 dark:hover:bg-blue-500/20">
In Progress
</Badge>
);
case "completed":
return (
<Badge
variant="outline"
className="border-0 bg-green-500/15 text-green-700 hover:bg-green-500/25 dark:bg-green-500/10 dark:text-green-400 dark:hover:bg-green-500/20">
Completed
</Badge>
);
case "blocked":
return (
<Badge
variant="outline"
className="border-0 bg-rose-500/15 text-rose-700 hover:bg-rose-500/25 dark:bg-rose-500/10 dark:text-rose-400 dark:hover:bg-rose-500/20">
Blocked
</Badge>
);
default:
return <Badge variant="secondary">{status}</Badge>;
}
}
export default function Table02() {
const [pendingAction, setPendingAction] = useState<{
id: string;
type: TaskActionType;
} | null>(null);
const isTaskActionPending = (action: TaskActionType, taskId: string) =>
pendingAction?.id === taskId && pendingAction.type === action;
const isTaskBusy = (taskId: string) => pendingAction?.id === taskId;
const handleAction = (task: Task, actionType: TaskActionType) => {
setPendingAction({ id: task.id, type: actionType });
setTimeout(() => {
setPendingAction(null);
console.log(`Action "${actionType}" completed for task:`, task.title);
}, 1000);
};
const renderTaskRow = (task: Task) => {
const busy = isTaskBusy(task.id);
const startPending = isTaskActionPending("start", task.id);
const pausePending = isTaskActionPending("pause", task.id);
const completePending = isTaskActionPending("complete", task.id);
const deletePending = isTaskActionPending("delete", task.id);
return (
<TableRow key={task.id} className="hover:bg-muted/50">
<TableCell className="h-16 px-4 font-medium">{task.title}</TableCell>
<TableCell className="text-muted-foreground h-16 px-4 text-sm">{task.assignee}</TableCell>
<TableCell className="h-16 px-4">{getStatusBadge(task.status)}</TableCell>
<TableCell className="text-muted-foreground h-16 px-4 text-sm">{task.dueDate}</TableCell>
<TableCell className="text-muted-foreground h-16 max-w-[300px] px-4 text-sm">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<span className="block cursor-help truncate">{task.notes}</span>
</TooltipTrigger>
<TooltipContent className="max-w-md">{task.notes}</TooltipContent>
</Tooltip>
</TooltipProvider>
</TableCell>
<TableCell className="h-16 px-4">
<TooltipProvider>
<div className="flex items-center gap-1">
{(task.status === "pending" || task.status === "blocked") && (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
className="h-8 w-8"
onClick={() => handleAction(task, "start")}
disabled={busy}>
{startPending ? (
<Loader2 className="size-4 animate-spin" />
) : (
<PlayIcon className="size-4" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>Start</TooltipContent>
</Tooltip>
)}
{task.status === "in-progress" && (
<>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
className="h-8 w-8"
onClick={() => handleAction(task, "pause")}
disabled={busy}>
{pausePending ? (
<Loader2 className="size-4 animate-spin" />
) : (
<PauseIcon className="size-4" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>Pause</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
className="h-8 w-8"
onClick={() => handleAction(task, "complete")}
disabled={busy}>
{completePending ? (
<Loader2 className="size-4 animate-spin" />
) : (
<CheckCircle className="size-4" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>Complete</TooltipContent>
</Tooltip>
</>
)}
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
className="text-destructive hover:bg-destructive h-8 w-8 hover:text-white"
onClick={() => handleAction(task, "delete")}
disabled={busy}>
{deletePending ? (
<Loader2 className="size-4 animate-spin" />
) : (
<Trash2Icon className="size-4" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>Delete</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
className="h-8 w-8"
onClick={() => handleAction(task, "view")}
disabled={busy}>
<FileTextIcon className="size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>View Details</TooltipContent>
</Tooltip>
</div>
</TooltipProvider>
</TableCell>
</TableRow>
);
};
return (
<div className="container mx-auto px-4 py-6 lg:py-10">
<div className="rounded-lg border">
<Table>
<TableHeader>
<TableRow className="border-b hover:bg-transparent">
<TableHead className="h-12 px-4 font-medium">Title</TableHead>
<TableHead className="h-12 px-4 font-medium">Assignee</TableHead>
<TableHead className="h-12 w-[120px] px-4 font-medium">Status</TableHead>
<TableHead className="h-12 px-4 font-medium">Due Date</TableHead>
<TableHead className="h-12 px-4 font-medium">Notes</TableHead>
<TableHead className="h-12 w-[180px] px-4 font-medium">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>{tasks.map(renderTaskRow)}</TableBody>
</Table>
</div>
</div>
);
}
npx shadcn@latest add @bundui/tablesimport { Tables } from "@/components/tables"<Tables />