← TemplatesProductivity

Project Management

A powerful team collaboration tool with task boards, timelines, file sharing, and integrated team chat. Built for productivity.

Task BoardsTeam ChatFile UploadCalendarSettings

Overview

Keep Your Team in Sync

Everything your team needs to plan, track, and deliver projects on time. Visualize work with kanban boards, communicate in real-time, and never miss a deadline with smart calendar integration.

📋

Kanban Task Boards

Drag-and-drop task management with customizable workflows

💬

Team Communication

Built-in chat with channels, threads, and file sharing

📅

Calendar Integration

Timeline views, milestones, and deadline tracking

📁

File Management

Upload, organize, and share files with your team

Live Demo

Task Board

To Do

2
high

Design system updates

EJ
medium

API integration

MC

In Progress

2
high

User testing

SA
urgent

Bug fixes

DK

Review

1
low

Documentation

LW

Done

1
medium

Performance audit

EJ

Implementation Guide

1

Define Project Structure

Set up the data models for projects, tasks, and team members.

step-1.tsx
ReactTailwind
TSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// types/project.ts export interface Project { id: string; name: string; description?: string; color: string; members: TeamMember[]; createdAt: Date; } export interface Task { id: string; title: string; description?: string; status: "todo" | "in-progress" | "review" | "done"; priority: "low" | "medium" | "high" | "urgent"; assignee?: TeamMember; dueDate?: Date; tags: string[]; projectId: string; comments: Comment[]; attachments: Attachment[]; } export interface TeamMember { id: string; name: string; email: string; avatar?: string; role: "owner" | "admin" | "member" | "viewer"; } export interface Comment { id: string; content: string; author: TeamMember; createdAt: Date; } export interface Attachment { id: string; name: string; url: string; type: string; size: number; uploadedBy: TeamMember; uploadedAt: Date; }
2

Build the Task Board

Create an interactive kanban board with drag-and-drop functionality.

step-2.tsx
ReactTailwind
TSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
function TaskBoard({ projectId }: { projectId: string }) { const [tasks, setTasks] = useState<Task[]>([]); const [draggedTask, setDraggedTask] = useState<Task | null>(null); const columns = [ { id: "todo", title: "To Do", color: "bg-slate-500" }, { id: "in-progress", title: "In Progress", color: "bg-amber-500" }, { id: "review", title: "Review", color: "bg-purple-500" }, { id: "done", title: "Done", color: "bg-emerald-500" }, ]; const handleDragStart = (task: Task) => setDraggedTask(task); const handleDrop = (status: Task["status"]) => { if (draggedTask) { setTasks(prev => prev.map(t => t.id === draggedTask.id ? { ...t, status } : t) ); // Sync with backend updateTaskStatus(draggedTask.id, status); setDraggedTask(null); } }; return ( <div className="flex gap-4 overflow-x-auto pb-4 h-full"> {columns.map(column => { const columnTasks = tasks.filter(t => t.status === column.id); return ( <div key={column.id} onDragOver={e => e.preventDefault()} onDrop={() => handleDrop(column.id as Task["status"])} className="flex-shrink-0 w-80 flex flex-col" > <div className="flex items-center gap-2 mb-4"> <div className={`w-3 h-3 rounded-full ${column.color}`} /> <h3 className="font-semibold">{column.title}</h3> <span className="px-2 py-0.5 text-xs bg-slate-100 dark:bg-slate-800 rounded-full"> {columnTasks.length} </span> </div> <div className="space-y-3 flex-1"> {columnTasks.map(task => ( <TaskCard key={task.id} task={task} onDragStart={() => handleDragStart(task)} /> ))} </div> <button className="mt-4 w-full py-2 border-2 border-dashed border-slate-200 dark:border-slate-700 rounded-xl text-slate-500 hover:border-amber-500 hover:text-amber-500"> + Add Task </button> </div> ); })} </div> ); }
3

Implement Team Chat

Add real-time messaging with channels and direct messages.

step-3.tsx
ReactTailwind
TSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
function TeamChat({ projectId }: { projectId: string }) { const [messages, setMessages] = useState<Message[]>([]); const [newMessage, setNewMessage] = useState(""); const [activeChannel, setActiveChannel] = useState("general"); const messagesEndRef = useRef<HTMLDivElement>(null); // Subscribe to real-time messages useEffect(() => { const unsubscribe = subscribeToChannel(projectId, activeChannel, (message) => { setMessages(prev => [...prev, message]); }); return () => unsubscribe(); }, [projectId, activeChannel]); // Scroll to bottom on new messages useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, [messages]); const sendMessage = async () => { if (!newMessage.trim()) return; await createMessage({ content: newMessage, channelId: activeChannel, projectId, }); setNewMessage(""); }; return ( <div className="flex h-full"> {/* Channels sidebar */} <div className="w-64 border-r border-slate-200 dark:border-slate-800 p-4"> <h3 className="font-semibold mb-4">Channels</h3> <div className="space-y-1"> {["general", "design", "development", "random"].map(channel => ( <button key={channel} onClick={() => setActiveChannel(channel)} className={`w-full px-3 py-2 text-left rounded-lg ${ activeChannel === channel ? "bg-amber-100 text-amber-700" : "hover:bg-slate-100 dark:hover:bg-slate-800" }`} > # {channel} </button> ))} </div> </div> {/* Messages area */} <div className="flex-1 flex flex-col"> <div className="flex-1 overflow-y-auto p-4 space-y-4"> {messages.map(message => ( <MessageBubble key={message.id} message={message} /> ))} <div ref={messagesEndRef} /> </div> {/* Message input */} <div className="p-4 border-t border-slate-200 dark:border-slate-800"> <div className="flex gap-2"> <input type="text" value={newMessage} onChange={e => setNewMessage(e.target.value)} onKeyDown={e => e.key === "Enter" && sendMessage()} placeholder={`Message #${activeChannel}`} className="flex-1 px-4 py-2 bg-slate-100 dark:bg-slate-800 rounded-xl" /> <button onClick={sendMessage} className="px-4 py-2 bg-amber-500 text-white rounded-xl hover:bg-amber-600" > Send </button> </div> </div> </div> </div> ); }
4

Add Calendar View

Display tasks and deadlines on a visual calendar.

step-4.tsx
ReactTailwind
TSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
function ProjectCalendar({ projectId }: { projectId: string }) { const [currentMonth, setCurrentMonth] = useState(new Date()); const [tasks, setTasks] = useState<Task[]>([]); const daysInMonth = new Date( currentMonth.getFullYear(), currentMonth.getMonth() + 1, 0 ).getDate(); const firstDayOfMonth = new Date( currentMonth.getFullYear(), currentMonth.getMonth(), 1 ).getDay(); const getTasksForDay = (day: number) => { return tasks.filter(task => { if (!task.dueDate) return false; const dueDate = new Date(task.dueDate); return ( dueDate.getDate() === day && dueDate.getMonth() === currentMonth.getMonth() && dueDate.getFullYear() === currentMonth.getFullYear() ); }); }; return ( <div className="bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-800 p-6"> {/* Calendar header */} <div className="flex items-center justify-between mb-6"> <h2 className="text-xl font-semibold"> {currentMonth.toLocaleDateString("en-US", { month: "long", year: "numeric" })} </h2> <div className="flex gap-2"> <button onClick={() => setCurrentMonth(prev => new Date(prev.getFullYear(), prev.getMonth() - 1))} className="p-2 hover:bg-slate-100 dark:hover:bg-slate-800 rounded-lg" > </button> <button onClick={() => setCurrentMonth(new Date())} className="px-3 py-1 text-sm bg-slate-100 dark:bg-slate-800 rounded-lg" > Today </button> <button onClick={() => setCurrentMonth(prev => new Date(prev.getFullYear(), prev.getMonth() + 1))} className="p-2 hover:bg-slate-100 dark:hover:bg-slate-800 rounded-lg" > </button> </div> </div> {/* Days header */} <div className="grid grid-cols-7 mb-2"> {["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map(day => ( <div key={day} className="text-center text-sm font-medium text-slate-500 py-2"> {day} </div> ))} </div> {/* Calendar grid */} <div className="grid grid-cols-7 gap-1"> {/* Empty cells for days before month starts */} {Array.from({ length: firstDayOfMonth }).map((_, i) => ( <div key={`empty-${i}`} className="h-24" /> ))} {/* Day cells */} {Array.from({ length: daysInMonth }).map((_, i) => { const day = i + 1; const dayTasks = getTasksForDay(day); const isToday = new Date().toDateString() === new Date(currentMonth.getFullYear(), currentMonth.getMonth(), day).toDateString(); return ( <div key={day} className={`h-24 p-2 border border-slate-100 dark:border-slate-800 rounded-lg ${ isToday ? "bg-amber-50 dark:bg-amber-900/20" : "" }`} > <span className={`text-sm ${isToday ? "font-bold text-amber-600" : ""}`}> {day} </span> <div className="mt-1 space-y-1"> {dayTasks.slice(0, 2).map(task => ( <div key={task.id} className="px-1.5 py-0.5 text-xs bg-amber-100 text-amber-700 rounded truncate" > {task.title} </div> ))} {dayTasks.length > 2 && ( <span className="text-xs text-slate-500">+{dayTasks.length - 2} more</span> )} </div> </div> ); })} </div> </div> ); }

Full Source Code

project-management.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
"use client"; import { useState, useRef, useEffect } from "react"; // Types interface Task { id: string; title: string; description?: string; status: "todo" | "in-progress" | "review" | "done"; priority: "low" | "medium" | "high" | "urgent"; assignee?: string; dueDate?: Date; } interface Message { id: string; author: string; avatar: string; content: string; time: string; } // Main Project Management Dashboard export default function ProjectManagement() { const [activeView, setActiveView] = useState<"board" | "chat" | "calendar">("board"); return ( <div className="min-h-screen bg-slate-50 dark:bg-slate-950 flex"> <Sidebar activeView={activeView} onViewChange={setActiveView} /> <main className="flex-1 flex flex-col"> <Header view={activeView} /> <div className="flex-1 overflow-auto p-6"> {activeView === "board" && <TaskBoard />} {activeView === "chat" && <TeamChat />} {activeView === "calendar" && <ProjectCalendar />} </div> </main> </div> ); } // Task Board with Drag and Drop function TaskBoard() { const columns = [ { id: "todo", title: "To Do", color: "bg-slate-500" }, { id: "in-progress", title: "In Progress", color: "bg-amber-500" }, { id: "review", title: "Review", color: "bg-purple-500" }, { id: "done", title: "Done", color: "bg-emerald-500" }, ]; const [tasks, setTasks] = useState<Task[]>([ { id: "1", title: "Design system updates", status: "todo", priority: "high", assignee: "EJ" }, { id: "2", title: "API integration", status: "in-progress", priority: "medium", assignee: "MC" }, // More tasks... ]); const [draggedTask, setDraggedTask] = useState<Task | null>(null); const handleDragStart = (task: Task) => setDraggedTask(task); const handleDrop = (status: Task["status"]) => { if (draggedTask) { setTasks(prev => prev.map(t => t.id === draggedTask.id ? { ...t, status } : t) ); setDraggedTask(null); } }; return ( <div className="flex gap-4 overflow-x-auto pb-4 h-full"> {columns.map((column) => { const columnTasks = tasks.filter(t => t.status === column.id); return ( <div key={column.id} onDragOver={e => e.preventDefault()} onDrop={() => handleDrop(column.id as Task["status"])} className="flex-shrink-0 w-72 flex flex-col" > <div className="flex items-center gap-2 mb-4"> <div className={`w-3 h-3 rounded-full ${column.color}`} /> <h3 className="font-semibold">{column.title}</h3> <span className="px-2 py-0.5 text-xs bg-slate-100 rounded-full"> {columnTasks.length} </span> </div> <div className="space-y-3 flex-1"> {columnTasks.map(task => ( <div key={task.id} draggable onDragStart={() => handleDragStart(task)} className="p-4 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-800 cursor-grab active:cursor-grabbing" > <PriorityBadge priority={task.priority} /> <h4 className="font-medium mt-2">{task.title}</h4> {task.assignee && ( <div className="mt-3"> <Avatar initials={task.assignee} /> </div> )} </div> ))} </div> <button className="mt-4 w-full py-2 border-2 border-dashed border-slate-200 rounded-xl text-slate-500 hover:border-amber-500 hover:text-amber-500"> + Add Task </button> </div> ); })} </div> ); } // Team Chat Component function TeamChat() { const [messages, setMessages] = useState<Message[]>([ { id: "1", author: "Emily", avatar: "EJ", content: "Hey team!", time: "10:30 AM" }, ]); const [newMessage, setNewMessage] = useState(""); const messagesEndRef = useRef<HTMLDivElement>(null); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, [messages]); const sendMessage = () => { if (!newMessage.trim()) return; setMessages(prev => [...prev, { id: Date.now().toString(), author: "You", avatar: "YO", content: newMessage, time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), }]); setNewMessage(""); }; return ( <div className="flex h-full bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-800 overflow-hidden"> {/* Channels sidebar */} <div className="w-56 border-r border-slate-200 dark:border-slate-800 p-4"> <p className="text-xs font-semibold text-slate-400 uppercase mb-3">Channels</p> <div className="space-y-1"> {["general", "design", "development"].map((channel, i) => ( <button key={channel} className={`w-full px-3 py-2 text-left text-sm rounded-lg ${ i === 0 ? "bg-amber-100 text-amber-700" : "hover:bg-slate-100" }`} > # {channel} </button> ))} </div> </div> {/* Messages */} <div className="flex-1 flex flex-col"> <div className="flex-1 p-4 space-y-4 overflow-y-auto"> {messages.map(msg => ( <div key={msg.id} className="flex gap-3"> <Avatar initials={msg.avatar} /> <div> <div className="flex items-center gap-2 mb-1"> <span className="font-medium text-sm">{msg.author}</span> <span className="text-xs text-slate-500">{msg.time}</span> </div> <p className="text-sm text-slate-600 dark:text-slate-300">{msg.content}</p> </div> </div> ))} <div ref={messagesEndRef} /> </div> <div className="p-4 border-t border-slate-200 dark:border-slate-800"> <div className="flex gap-2"> <input type="text" value={newMessage} onChange={e => setNewMessage(e.target.value)} onKeyDown={e => e.key === "Enter" && sendMessage()} placeholder="Message #general" className="flex-1 px-4 py-2 bg-slate-100 dark:bg-slate-800 rounded-xl text-sm" /> <button onClick={sendMessage} className="px-4 py-2 bg-amber-600 text-white rounded-xl text-sm font-medium" > Send </button> </div> </div> </div> </div> ); } // Project Calendar function ProjectCalendar() { const [currentMonth] = useState(new Date()); return ( <div className="bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-800 p-6"> <div className="flex items-center justify-between mb-6"> <h2 className="text-xl font-semibold"> {currentMonth.toLocaleDateString("en-US", { month: "long", year: "numeric" })} </h2> <div className="flex gap-2"> <button className="p-2 hover:bg-slate-100 rounded-lg"></button> <button className="px-3 py-1 bg-slate-100 rounded-lg text-sm">Today</button> <button className="p-2 hover:bg-slate-100 rounded-lg"></button> </div> </div> <div className="grid grid-cols-7 gap-1"> {["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map(day => ( <div key={day} className="text-center text-sm font-medium text-slate-500 py-2"> {day} </div> ))} {/* Calendar days would be rendered here */} </div> </div> ); } // Helper Components function Avatar({ initials }: { initials: string }) { return ( <div className="w-8 h-8 rounded-full bg-gradient-to-br from-amber-500 to-orange-500 flex items-center justify-center text-white text-xs font-semibold"> {initials} </div> ); } function PriorityBadge({ priority }: { priority: Task["priority"] }) { const colors = { urgent: "bg-red-100 text-red-700", high: "bg-orange-100 text-orange-700", medium: "bg-amber-100 text-amber-700", low: "bg-slate-100 text-slate-600", }; return ( <span className={`px-2 py-0.5 text-xs font-medium rounded ${colors[priority]}`}> {priority} </span> ); } function Sidebar({ activeView, onViewChange }: { activeView: string; onViewChange: (view: "board" | "chat" | "calendar") => void; }) { return ( <aside className="w-64 bg-white dark:bg-slate-900 border-r border-slate-200 dark:border-slate-800"> {/* Sidebar content */} </aside> ); } function Header({ view }: { view: string }) { return ( <header className="h-16 bg-white dark:bg-slate-900 border-b border-slate-200 dark:border-slate-800 flex items-center justify-between px-6"> <h1 className="text-lg font-semibold capitalize">{view.replace("-", " ")}</h1> </header> ); }

Customization Guide

Custom Task Statuses

Configure your own workflow stages.

customization.tsx
ReactTailwind
TSX
const customStatuses = [
  { id: "backlog", title: "Backlog", color: "bg-slate-400" },
  { id: "ready", title: "Ready", color: "bg-blue-500" },
  { id: "in-progress", title: "In Progress", color: "bg-amber-500" },
  { id: "testing", title: "Testing", color: "bg-purple-500" },
  { id: "staging", title: "Staging", color: "bg-cyan-500" },
  { id: "deployed", title: "Deployed", color: "bg-emerald-500" },
];

Priority Levels

Define priority levels with colors and icons.

customization.tsx
ReactTailwind
TSX
const priorities = {
  urgent: {
    label: "Urgent",
    color: "bg-red-100 text-red-700",
    icon: "🔴",
  },
  high: {
    label: "High",
    color: "bg-orange-100 text-orange-700",
    icon: "🟠",
  },
  medium: {
    label: "Medium",
    color: "bg-amber-100 text-amber-700",
    icon: "🟡",
  },
  low: {
    label: "Low",
    color: "bg-slate-100 text-slate-700",
    icon: "⚪",
  },
};

Team Roles

Configure team member roles and permissions.

customization.tsx
ReactTailwind
TSX
const roles = {
  owner: {
    label: "Owner",
    permissions: ["all"],
  },
  admin: {
    label: "Admin",
    permissions: ["manage_members", "manage_settings", "manage_tasks"],
  },
  member: {
    label: "Member",
    permissions: ["create_tasks", "update_own_tasks", "comment"],
  },
  viewer: {
    label: "Viewer",
    permissions: ["view_tasks", "comment"],
  },
};

Notification Settings

Customize notification triggers and delivery.

customization.tsx
ReactTailwind
TSX
const notificationSettings = {
  task_assigned: { email: true, push: true, inApp: true },
  task_completed: { email: false, push: true, inApp: true },
  comment_added: { email: true, push: true, inApp: true },
  due_date_reminder: { email: true, push: true, inApp: true },
  mention: { email: true, push: true, inApp: true },
};

// Configure reminder timing
const reminderTiming = [
  { label: "1 day before", hours: 24 },
  { label: "3 hours before", hours: 3 },
  { label: "1 hour before", hours: 1 },
];

Ready to Boost Your Team's Productivity?

Copy this template and start managing projects effectively. Customize workflows to match your team.