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