Seed: project-management template (23 files)
This commit is contained in:
@@ -0,0 +1,194 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
|
import {
|
||||||
|
Plus, Calendar, Filter, GripVertical, MoreHorizontal
|
||||||
|
} from "lucide-react";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import Sidebar from "@/components/Sidebar";
|
||||||
|
import Header from "@/components/Header";
|
||||||
|
|
||||||
|
interface Task {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
priority: string;
|
||||||
|
priorityColor: string;
|
||||||
|
assignee: string;
|
||||||
|
assigneeColor: string;
|
||||||
|
due: string;
|
||||||
|
tags: { label: string; color: string }[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Column {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
color: string;
|
||||||
|
dotColor: string;
|
||||||
|
tasks: Task[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialColumns: Column[] = [
|
||||||
|
{
|
||||||
|
id: "todo",
|
||||||
|
title: "To Do",
|
||||||
|
color: "bg-slate-100 text-slate-700",
|
||||||
|
dotColor: "bg-slate-400",
|
||||||
|
tasks: [
|
||||||
|
{ id: 1, title: "Create user onboarding wizard", priority: "High", priorityColor: "bg-red-100 text-red-700", assignee: "AK", assigneeColor: "bg-violet-500", due: "Apr 14", tags: [{ label: "Frontend", color: "bg-violet-100 text-violet-700" }] },
|
||||||
|
{ id: 2, title: "Write API documentation", priority: "Medium", priorityColor: "bg-amber-100 text-amber-700", assignee: "MR", assigneeColor: "bg-indigo-500", due: "Apr 16", tags: [{ label: "Docs", color: "bg-sky-100 text-sky-700" }] },
|
||||||
|
{ id: 3, title: "Set up error monitoring", priority: "Low", priorityColor: "bg-slate-100 text-slate-600", assignee: "JL", assigneeColor: "bg-emerald-500", due: "Apr 18", tags: [{ label: "DevOps", color: "bg-orange-100 text-orange-700" }] },
|
||||||
|
{ id: 4, title: "Design email templates", priority: "Medium", priorityColor: "bg-amber-100 text-amber-700", assignee: "SC", assigneeColor: "bg-amber-500", due: "Apr 20", tags: [{ label: "Design", color: "bg-pink-100 text-pink-700" }] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "in-progress",
|
||||||
|
title: "In Progress",
|
||||||
|
color: "bg-violet-100 text-violet-700",
|
||||||
|
dotColor: "bg-violet-500",
|
||||||
|
tasks: [
|
||||||
|
{ id: 5, title: "Implement auth flow with OAuth", priority: "High", priorityColor: "bg-red-100 text-red-700", assignee: "MR", assigneeColor: "bg-indigo-500", due: "Apr 13", tags: [{ label: "Backend", color: "bg-emerald-100 text-emerald-700" }] },
|
||||||
|
{ id: 6, title: "Build notification system", priority: "High", priorityColor: "bg-red-100 text-red-700", assignee: "AK", assigneeColor: "bg-violet-500", due: "Apr 15", tags: [{ label: "Frontend", color: "bg-violet-100 text-violet-700" }, { label: "Backend", color: "bg-emerald-100 text-emerald-700" }] },
|
||||||
|
{ id: 7, title: "Optimize database queries", priority: "Medium", priorityColor: "bg-amber-100 text-amber-700", assignee: "TP", assigneeColor: "bg-sky-500", due: "Apr 17", tags: [{ label: "Backend", color: "bg-emerald-100 text-emerald-700" }] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "done",
|
||||||
|
title: "Done",
|
||||||
|
color: "bg-emerald-100 text-emerald-700",
|
||||||
|
dotColor: "bg-emerald-500",
|
||||||
|
tasks: [
|
||||||
|
{ id: 8, title: "Set up CI/CD pipeline", priority: "High", priorityColor: "bg-red-100 text-red-700", assignee: "JL", assigneeColor: "bg-emerald-500", due: "Apr 10", tags: [{ label: "DevOps", color: "bg-orange-100 text-orange-700" }] },
|
||||||
|
{ id: 9, title: "Design landing page mockups", priority: "Medium", priorityColor: "bg-amber-100 text-amber-700", assignee: "SC", assigneeColor: "bg-amber-500", due: "Apr 9", tags: [{ label: "Design", color: "bg-pink-100 text-pink-700" }] },
|
||||||
|
{ id: 10, title: "Configure analytics tracking", priority: "Low", priorityColor: "bg-slate-100 text-slate-600", assignee: "TP", assigneeColor: "bg-sky-500", due: "Apr 8", tags: [{ label: "Frontend", color: "bg-violet-100 text-violet-700" }] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function TasksPage() {
|
||||||
|
const [columns, setColumns] = useState(initialColumns);
|
||||||
|
const [priorityFilter, setPriorityFilter] = useState("all");
|
||||||
|
const [assigneeFilter, setAssigneeFilter] = useState("all");
|
||||||
|
|
||||||
|
const handleAddTask = (columnId: string) => {
|
||||||
|
toast("Task creation dialog would open here");
|
||||||
|
};
|
||||||
|
|
||||||
|
const filteredColumns = columns.map(col => ({
|
||||||
|
...col,
|
||||||
|
tasks: col.tasks.filter(t => {
|
||||||
|
if (priorityFilter !== "all" && t.priority !== priorityFilter) return false;
|
||||||
|
if (assigneeFilter !== "all" && t.assignee !== assigneeFilter) return false;
|
||||||
|
return true;
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen bg-slate-50">
|
||||||
|
<Sidebar activePath="/tasks" />
|
||||||
|
<div className="flex-1 lg:ml-64">
|
||||||
|
<Header />
|
||||||
|
<main className="p-6 space-y-6">
|
||||||
|
{/* ── Header ── */}
|
||||||
|
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl font-bold text-slate-900 animate-fade-up">Tasks</h1>
|
||||||
|
<p className="text-slate-500 mt-1">Kanban board for your current sprint</p>
|
||||||
|
</div>
|
||||||
|
<Button className="bg-violet-600 hover:bg-violet-700 text-white" onClick={() => toast("Task added!")}>
|
||||||
|
<Plus className="mr-2 h-4 w-4" /> New Task
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ── Filters ── */}
|
||||||
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
|
<div className="flex items-center gap-2 text-sm text-slate-500">
|
||||||
|
<Filter className="h-4 w-4" />
|
||||||
|
<span>Filter by:</span>
|
||||||
|
</div>
|
||||||
|
<Select value={priorityFilter} onValueChange={setPriorityFilter}>
|
||||||
|
<SelectTrigger className="w-[140px] h-9">
|
||||||
|
<SelectValue placeholder="Priority" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="all">All Priorities</SelectItem>
|
||||||
|
<SelectItem value="High">High</SelectItem>
|
||||||
|
<SelectItem value="Medium">Medium</SelectItem>
|
||||||
|
<SelectItem value="Low">Low</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<Select value={assigneeFilter} onValueChange={setAssigneeFilter}>
|
||||||
|
<SelectTrigger className="w-[140px] h-9">
|
||||||
|
<SelectValue placeholder="Assignee" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="all">All Members</SelectItem>
|
||||||
|
<SelectItem value="AK">Alex Kim</SelectItem>
|
||||||
|
<SelectItem value="MR">Maria Rodriguez</SelectItem>
|
||||||
|
<SelectItem value="JL">Jordan Lee</SelectItem>
|
||||||
|
<SelectItem value="SC">Sam Chen</SelectItem>
|
||||||
|
<SelectItem value="TP">Taylor Park</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ── Kanban Board ── */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 stagger">
|
||||||
|
{filteredColumns.map((column) => (
|
||||||
|
<div key={column.id} className="space-y-3 animate-fade-up">
|
||||||
|
{/* Column Header */}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className={`h-2.5 w-2.5 rounded-full \${column.dotColor}`} />
|
||||||
|
<h2 className="font-semibold text-slate-900">{column.title}</h2>
|
||||||
|
<Badge variant="secondary" className="text-xs">{column.tasks.length}</Badge>
|
||||||
|
</div>
|
||||||
|
<Button variant="ghost" size="sm" className="h-8 w-8 p-0 text-slate-400 hover:text-violet-600" onClick={() => handleAddTask(column.id)}>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Task Cards */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
{column.tasks.map((task) => (
|
||||||
|
<Card key={task.id} className="border-0 shadow-sm backdrop-blur-sm bg-card/80 border-border/50 hover:-translate-y-1 hover:shadow-lg transition-all duration-300 cursor-pointer group">
|
||||||
|
<CardContent className="p-4">
|
||||||
|
<div className="flex items-start justify-between mb-2">
|
||||||
|
<h3 className="text-sm font-medium text-slate-900 group-hover:text-violet-600 transition-colors leading-snug">{task.title}</h3>
|
||||||
|
<Button variant="ghost" size="sm" className="h-6 w-6 p-0 text-slate-300 opacity-0 group-hover:opacity-100 transition-opacity shrink-0 ml-2">
|
||||||
|
<MoreHorizontal className="h-3.5 w-3.5" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-1.5 mb-3">
|
||||||
|
{task.tags.map((tag, i) => (
|
||||||
|
<Badge key={i} className={`\${tag.color} text-[10px] font-medium border-0 px-1.5 py-0`}>{tag.label}</Badge>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Badge className={`\${task.priorityColor} text-[10px] font-medium border-0`}>{task.priority}</Badge>
|
||||||
|
<span className="text-[11px] text-slate-400 flex items-center gap-1">
|
||||||
|
<Calendar className="h-3 w-3" />{task.due}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Avatar className="h-6 w-6">
|
||||||
|
<AvatarFallback className={`text-[10px] text-white \${task.assigneeColor}`}>{task.assignee}</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
<Button variant="ghost" className="w-full border-2 border-dashed border-slate-200 text-slate-400 hover:text-violet-600 hover:border-violet-300 transition-colors" onClick={() => handleAddTask(column.id)}>
|
||||||
|
<Plus className="mr-1 h-4 w-4" /> Add Task
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user