import React, { useState, useMemo } from "react"; import { base44 } from "@/api/base44Client"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; import { DragDropContext, Draggable } from "@hello-pangea/dnd"; import { Link2, Plus, Users, Search, UserCircle, Filter, ArrowUpDown, EyeOff, Grid3x3, MoreVertical, Pin, Ruler, Palette } from "lucide-react"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator, DropdownMenuLabel, } from "@/components/ui/dropdown-menu"; import TaskCard from "@/components/tasks/TaskCard"; import TaskColumn from "@/components/tasks/TaskColumn"; import TaskDetailModal from "@/components/tasks/TaskDetailModal"; import { useToast } from "@/components/ui/use-toast"; export default function TaskBoard() { const { toast } = useToast(); const queryClient = useQueryClient(); const [createDialog, setCreateDialog] = useState(false); const [selectedTask, setSelectedTask] = useState(null); const [selectedStatus, setSelectedStatus] = useState("pending"); const [newTask, setNewTask] = useState({ task_name: "", description: "", priority: "normal", due_date: "", progress: 0, assigned_members: [] }); const [selectedMembers, setSelectedMembers] = useState([]); const [searchQuery, setSearchQuery] = useState(""); const [filterPerson, setFilterPerson] = useState("all"); const [filterPriority, setFilterPriority] = useState("all"); const [sortBy, setSortBy] = useState("due_date"); const [showCompleted, setShowCompleted] = useState(true); const [groupBy, setGroupBy] = useState("status"); const [pinnedColumns, setPinnedColumns] = useState([]); const [itemHeight, setItemHeight] = useState("normal"); const [conditionalColoring, setConditionalColoring] = useState(true); const { data: user } = useQuery({ queryKey: ['current-user-taskboard'], queryFn: () => base44.auth.me(), }); const { data: teams = [] } = useQuery({ queryKey: ['teams'], queryFn: () => base44.entities.Team.list(), initialData: [], }); const { data: teamMembers = [] } = useQuery({ queryKey: ['team-members'], queryFn: () => base44.entities.TeamMember.list(), initialData: [], }); const { data: tasks = [] } = useQuery({ queryKey: ['tasks'], queryFn: () => base44.entities.Task.list(), initialData: [], }); const userTeam = teams.find(t => t.owner_id === user?.id) || teams[0]; let teamTasks = tasks.filter(t => t.team_id === userTeam?.id); // Apply filters if (searchQuery) { teamTasks = teamTasks.filter(t => t.task_name?.toLowerCase().includes(searchQuery.toLowerCase()) || t.description?.toLowerCase().includes(searchQuery.toLowerCase()) ); } if (filterPerson !== "all") { teamTasks = teamTasks.filter(t => t.assigned_members?.some(m => m.member_id === filterPerson) ); } if (filterPriority !== "all") { teamTasks = teamTasks.filter(t => t.priority === filterPriority); } if (!showCompleted) { teamTasks = teamTasks.filter(t => t.status !== "completed"); } const currentTeamMembers = teamMembers.filter(m => m.team_id === userTeam?.id); const leadMembers = currentTeamMembers.filter(m => m.role === 'admin' || m.role === 'manager'); const regularMembers = currentTeamMembers.filter(m => m.role === 'member'); // Get unique departments from team members const departments = [...new Set(currentTeamMembers.map(m => m.department).filter(Boolean))]; const sortTasks = (tasks) => { return [...tasks].sort((a, b) => { switch (sortBy) { case "due_date": return new Date(a.due_date || '9999-12-31') - new Date(b.due_date || '9999-12-31'); case "priority": const priorityOrder = { high: 0, normal: 1, low: 2 }; return (priorityOrder[a.priority] || 1) - (priorityOrder[b.priority] || 1); case "created_date": return new Date(b.created_date || 0) - new Date(a.created_date || 0); case "task_name": return (a.task_name || '').localeCompare(b.task_name || ''); default: return (a.order_index || 0) - (b.order_index || 0); } }); }; const tasksByStatus = useMemo(() => ({ pending: sortTasks(teamTasks.filter(t => t.status === 'pending')), in_progress: sortTasks(teamTasks.filter(t => t.status === 'in_progress')), on_hold: sortTasks(teamTasks.filter(t => t.status === 'on_hold')), completed: sortTasks(teamTasks.filter(t => t.status === 'completed')), }), [teamTasks, sortBy]); const overallProgress = useMemo(() => { if (teamTasks.length === 0) return 0; const totalProgress = teamTasks.reduce((sum, t) => sum + (t.progress || 0), 0); return Math.round(totalProgress / teamTasks.length); }, [teamTasks]); const createTaskMutation = useMutation({ mutationFn: (taskData) => base44.entities.Task.create(taskData), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['tasks'] }); setCreateDialog(false); setNewTask({ task_name: "", description: "", priority: "normal", due_date: "", progress: 0, assigned_members: [] }); setSelectedMembers([]); toast({ title: "✅ Task Created", description: "New task added to the board", }); }, }); const updateTaskMutation = useMutation({ mutationFn: ({ id, data }) => base44.entities.Task.update(id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['tasks'] }); }, }); const handleDragEnd = (result) => { if (!result.destination) return; const { source, destination, draggableId } = result; if (source.droppableId === destination.droppableId && source.index === destination.index) { return; } const task = teamTasks.find(t => t.id === draggableId); if (!task) return; const newStatus = destination.droppableId; updateTaskMutation.mutate({ id: task.id, data: { ...task, status: newStatus, order_index: destination.index } }); }; const handleCreateTask = () => { if (!newTask.task_name.trim()) { toast({ title: "Task name required", variant: "destructive", }); return; } createTaskMutation.mutate({ ...newTask, team_id: userTeam?.id, status: selectedStatus, order_index: tasksByStatus[selectedStatus]?.length || 0, assigned_members: selectedMembers.map(m => ({ member_id: m.id, member_name: m.member_name, avatar_url: m.avatar_url })), assigned_department: selectedMembers.length > 0 && selectedMembers[0].department ? selectedMembers[0].department : null }); }; return (
{/* Header */}
{/* Toolbar */}
setSearchQuery(e.target.value)} className="pl-9 h-9" />
setFilterPerson("all")}> All People {currentTeamMembers.map((member) => ( setFilterPerson(member.id)} > {member.member_name} ))} Priority setFilterPriority("all")}>All setFilterPriority("high")}>High setFilterPriority("normal")}>Normal setFilterPriority("low")}>Low setSortBy("due_date")}>Due Date setSortBy("priority")}>Priority setSortBy("created_date")}>Created Date setSortBy("task_name")}>Name setGroupBy("status")}>Status setGroupBy("priority")}>Priority setGroupBy("assigned")}>Assigned To setPinnedColumns(pinnedColumns.length > 0 ? [] : ['pending'])}> Pin columns Item height setItemHeight("compact")}> Compact setItemHeight("normal")}> Normal setItemHeight("comfortable")}> Comfortable setConditionalColoring(!conditionalColoring)}> Conditional coloring

Task Board

Lead
{leadMembers.slice(0, 3).map((member, idx) => ( {member.member_name} ))} {leadMembers.length > 3 && (
+{leadMembers.length - 3}
)}
Team
{regularMembers.slice(0, 3).map((member, idx) => ( {member.member_name} ))} {regularMembers.length > 3 && (
+{regularMembers.length - 3}
)}
{/* Overall Progress */}
{overallProgress}%
{/* Kanban Board */}
{['pending', 'in_progress', 'on_hold', 'completed'].map((status) => ( { setSelectedStatus(status); setCreateDialog(true); }} > {tasksByStatus[status].map((task, index) => ( {(provided) => ( setSelectedTask(task)} itemHeight={itemHeight} conditionalColoring={conditionalColoring} /> )} ))} ))}
{teamTasks.length === 0 && (

No tasks yet

Create your first task to get started

)}
{/* Create Task Dialog */} Create New Task
setNewTask({ ...newTask, task_name: e.target.value })} placeholder="e.g., Website Design" className="mt-1" />