Seed: project-management template (23 files)

This commit is contained in:
2026-07-23 22:14:37 +00:00
parent f8c401561c
commit 6545cd5e29
+181
View File
@@ -0,0 +1,181 @@
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import {
CalendarDays, ChevronLeft, ChevronRight, Diamond
} from "lucide-react";
import Sidebar from "@/components/Sidebar";
import Header from "@/components/Header";
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug"];
const timelineProjects = [
{
name: "Website Redesign",
startMonth: 0,
duration: 3,
color: "bg-violet-500",
lightColor: "bg-violet-100",
phase: "Complete",
milestones: [{ month: 2, label: "v1.0 Launch" }],
},
{
name: "Mobile App",
startMonth: 1,
duration: 5,
color: "bg-indigo-500",
lightColor: "bg-indigo-100",
phase: "In Progress",
milestones: [{ month: 3, label: "Beta Release" }, { month: 5, label: "GA Launch" }],
},
{
name: "Security Audit",
startMonth: 3,
duration: 3,
color: "bg-amber-500",
lightColor: "bg-amber-100",
phase: "In Progress",
milestones: [{ month: 5, label: "Audit Complete" }],
},
{
name: "Design System",
startMonth: 4,
duration: 4,
color: "bg-emerald-500",
lightColor: "bg-emerald-100",
phase: "Planning",
milestones: [{ month: 6, label: "Alpha Build" }],
},
];
const phaseColors: Record<string, string> = {
"Planning": "bg-blue-100 text-blue-700",
"In Progress": "bg-violet-100 text-violet-700",
"Review": "bg-amber-100 text-amber-700",
"Complete": "bg-emerald-100 text-emerald-700",
};
export default function TimelinePage() {
const totalMonths = months.length;
return (
<div className="flex min-h-screen bg-slate-50">
<Sidebar activePath="/timeline" />
<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">Timeline</h1>
<p className="text-slate-500 mt-1">Project roadmap and milestone overview</p>
</div>
<div className="flex items-center gap-2">
<Button variant="outline" size="sm"><ChevronLeft className="h-4 w-4" /></Button>
<span className="text-sm font-medium text-slate-700 px-2">2026</span>
<Button variant="outline" size="sm"><ChevronRight className="h-4 w-4" /></Button>
</div>
</div>
{/* ── Legend ── */}
<div className="flex flex-wrap items-center gap-4">
{Object.entries(phaseColors).map(([phase, color]) => (
<div key={phase} className="flex items-center gap-2">
<div className={`h-3 w-8 rounded-full \${color.split(" ")[0]}`} />
<span className="text-xs text-slate-600">{phase}</span>
</div>
))}
<div className="flex items-center gap-2">
<Diamond className="h-3 w-3 text-slate-700 fill-slate-700" />
<span className="text-xs text-slate-600">Milestone</span>
</div>
</div>
{/* ── Gantt Chart ── */}
<Card className="border-0 shadow-sm overflow-hidden">
<CardContent className="p-0">
<div className="overflow-x-auto">
<div className="min-w-[700px]">
{/* Month Headers */}
<div className="flex border-b border-slate-100">
<div className="w-48 shrink-0 p-4 font-medium text-sm text-slate-500 border-r border-slate-100">Project</div>
<div className="flex-1 flex">
{months.map((month, i) => (
<div key={month} className={`flex-1 p-3 text-center text-xs font-medium border-r border-slate-50 \${i === 3 ? "bg-violet-50/50 text-violet-700" : "text-slate-400"}`}>
{month} 2026
</div>
))}
</div>
</div>
{/* Project Rows */}
{timelineProjects.map((project, idx) => (
<div key={idx} className="flex border-b border-slate-50 hover:bg-slate-50/50 transition-colors">
<div className="w-48 shrink-0 p-4 border-r border-slate-100">
<p className="text-sm font-medium text-slate-900">{project.name}</p>
<Badge className={`\${phaseColors[project.phase]} text-[10px] font-medium border-0 mt-1`}>{project.phase}</Badge>
</div>
<div className="flex-1 flex items-center relative py-4">
{/* Background grid */}
{months.map((_, i) => (
<div key={i} className={`flex-1 border-r border-slate-50 h-full absolute top-0 \${i === 3 ? "bg-violet-50/30" : ""}`} style={{ left: `\${(i / totalMonths) * 100}%`, width: `\${100 / totalMonths}%` }} />
))}
{/* Bar */}
<div
className={`absolute h-8 rounded-full \${project.color} opacity-90 shadow-sm flex items-center justify-center animate-fade-up`}
style={{
left: `\${(project.startMonth / totalMonths) * 100}%`,
width: `\${(project.duration / totalMonths) * 100}%`,
}}
>
<span className="text-[10px] font-medium text-white truncate px-3">{project.name}</span>
</div>
{/* Milestones */}
{project.milestones.map((ms, mi) => (
<div key={mi} className="absolute top-1/2 -translate-y-1/2 z-10" style={{ left: `\${((ms.month + 0.5) / totalMonths) * 100}%` }}>
<div className="relative group">
<Diamond className="h-4 w-4 text-slate-700 fill-slate-700" />
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-1 hidden group-hover:block">
<div className="bg-slate-900 text-white text-[10px] px-2 py-1 rounded whitespace-nowrap">{ms.label}</div>
</div>
</div>
</div>
))}
</div>
</div>
))}
</div>
</div>
</CardContent>
</Card>
{/* ── Upcoming Milestones ── */}
<Card className="border-0 shadow-sm">
<CardHeader>
<CardTitle className="text-lg font-semibold text-slate-900">Upcoming Milestones</CardTitle>
<CardDescription>Key dates and deliverables across all projects</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
{timelineProjects.flatMap(p => p.milestones.map(ms => ({ project: p.name, ...ms, color: p.color }))).sort((a, b) => a.month - b.month).map((ms, i) => (
<div key={i} className="flex items-center gap-3 p-3 rounded-lg hover:bg-slate-50 transition-colors">
<div className={`h-8 w-8 rounded-lg \${ms.color} flex items-center justify-center`}>
<Diamond className="h-4 w-4 text-white fill-white" />
</div>
<div className="flex-1">
<p className="text-sm font-medium text-slate-900">{ms.label}</p>
<p className="text-xs text-slate-500">{ms.project}</p>
</div>
<Badge variant="outline" className="text-xs">
<CalendarDays className="mr-1 h-3 w-3" />
{months[ms.month]} 2026
</Badge>
</div>
))}
</CardContent>
</Card>
</main>
</div>
</div>
);
}