import { useMemo, useState } from "react"; import { Button } from "@/common/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/common/components/ui/dropdown-menu"; import { Input } from "@/common/components/ui/input"; import { useToast } from "@/common/components/ui/use-toast"; import DashboardLayout from "@/features/layouts/DashboardLayout"; import { DragDropContext, Draggable,type DraggableProvided,type DropResult } from "@hello-pangea/dnd"; import { useQueryClient } from "@tanstack/react-query"; import { ArrowUpDown, Calendar, Link2, MoreVertical, Palette, Ruler, Search, Users } from "lucide-react"; import { useListShifts, useUpdateShift, useListBusinesses } from "@/dataconnect-generated/react"; import { ShiftStatus } from "@/dataconnect-generated"; import { dataConnect } from "@/features/auth/firebase"; import TaskCard from "./TaskCard"; import TaskColumn from "./TaskColumn"; import { format } from "date-fns"; /** * TaskBoard Feature Component * Kanban board for managing shift assignments and their current status. * Optimized for real-time data using Firebase Data Connect. */ export default function TaskBoard() { const { toast } = useToast(); const queryClient = useQueryClient(); // UI State const [searchQuery, setSearchQuery] = useState(""); const [filterClient, setFilterClient] = useState("all"); const [filterDate, setFilterDate] = useState(""); const [sortBy, setSortBy] = useState("date"); const [itemHeight, setItemHeight] = useState<"compact" | "normal" | "comfortable">("normal"); const [conditionalColoring, setConditionalColoring] = useState(true); // Queries const { data: shiftsData, isLoading: shiftsLoading } = useListShifts(dataConnect); const { data: clientsData } = useListBusinesses(dataConnect); const shifts = useMemo(() => shiftsData?.shifts || [], [shiftsData]); const clients = useMemo(() => clientsData?.businesses || [], [clientsData]); // Filtering & Sorting Logic const filteredShifts = useMemo(() => { let result = [...shifts]; if (searchQuery) { result = result.filter(s => s.title?.toLowerCase().includes(searchQuery.toLowerCase()) || s.description?.toLowerCase().includes(searchQuery.toLowerCase()) || s.location?.toLowerCase().includes(searchQuery.toLowerCase()) ); } if (filterClient !== "all") { result = result.filter(s => s.order?.businessId === filterClient); } if (filterDate) { result = result.filter(s => { if (!s.date) return false; return format(new Date(s.date), 'yyyy-MM-dd') === filterDate; }); } // Sort return result.sort((a, b) => { switch (sortBy) { case "date": return new Date(a.date || '9999-12-31').getTime() - new Date(b.date || '9999-12-31').getTime(); case "title": return (a.title || '').localeCompare(b.title || ''); default: return 0; } }); }, [shifts, searchQuery, filterClient, filterDate, sortBy]); // Grouping Logic const shiftsByStatus = useMemo>>(() => ({ [ShiftStatus.OPEN]: filteredShifts.filter(s => s.status === ShiftStatus.OPEN), [ShiftStatus.PENDING]: filteredShifts.filter(s => s.status === ShiftStatus.PENDING), [ShiftStatus.CONFIRMED]: filteredShifts.filter(s => s.status === ShiftStatus.CONFIRMED), [ShiftStatus.IN_PROGRESS]: filteredShifts.filter(s => s.status === ShiftStatus.IN_PROGRESS), [ShiftStatus.COMPLETED]: filteredShifts.filter(s => s.status === ShiftStatus.COMPLETED), }), [filteredShifts]); const overallProgress = useMemo(() => { if (shifts.length === 0) return 0; const completedCount = shifts.filter(s => s.status === ShiftStatus.COMPLETED).length; return Math.round((completedCount / shifts.length) * 100); }, [shifts]); // Mutations const { mutate: updateShiftStatus } = useUpdateShift(dataConnect, { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['listShifts'] }); toast({ title: "Status Updated", description: "Shift status has been updated successfully." }); }, }); // Handlers const handleDragEnd = (result: DropResult) => { if (!result.destination) return; const { source, destination, draggableId } = result; if (source.droppableId === destination.droppableId && source.index === destination.index) return; updateShiftStatus({ id: draggableId, status: destination.droppableId as ShiftStatus }); }; return ( }> Share Board ) ]} > {/* Page Header */}
{/* Main Toolbar */}
setSearchQuery(e.target.value)} className="relative w-full md:w-96" leadingIcon={} />
setFilterDate(e.target.value)} className="w-44" /> Filter by Client setFilterClient("all")} className="rounded-xl font-medium py-3 px-4 hover:bg-primary/5 transition-colors">All Clients
{clients.map((client) => ( setFilterClient(client.id)} className="rounded-xl font-medium px-4 flex items-center gap-3 hover:bg-primary/5 transition-colors" > {client.businessName} ))}
Ordering Options setSortBy("date")} className="rounded-xl font-medium flex items-center justify-between">Date setSortBy("title")} className="rounded-xl font-medium flex items-center justify-between">Title
Display Settings setItemHeight("compact")} className="rounded-xl font-medium gap-3 hover:bg-primary/5"> Compact Cards setItemHeight("normal")} className="rounded-xl font-medium gap-3 hover:bg-primary/5"> Normal Layout setItemHeight("comfortable")} className="rounded-xl font-medium gap-3 hover:bg-primary/5"> Comfort View setConditionalColoring(!conditionalColoring)} className="rounded-xl font-medium gap-3 hover:bg-primary/5"> {conditionalColoring ? 'Minimalist Mode' : 'Enhanced Visuals'}
{/* Overall Progress */}
Global Completion {overallProgress}%
{/* Kanban Area */}
{[ { id: ShiftStatus.OPEN, title: "Unassigned" }, { id: ShiftStatus.PENDING, title: "Pending Acceptance" }, { id: ShiftStatus.CONFIRMED, title: "Confirmed" }, { id: ShiftStatus.IN_PROGRESS, title: "In Progress" }, { id: ShiftStatus.COMPLETED, title: "Completed" }, ].map((column) => ( {(shiftsByStatus[column.id] || []).map((shift, index) => ( {(provided: DraggableProvided) => ( {}} itemHeight={itemHeight} conditionalColoring={conditionalColoring} /> )} ))} ))}
{filteredShifts.length === 0 && (

No shifts found

No shifts matching your current filters. Adjust your search or filters to see more.

)}
); }