import React, { useState, useMemo } from "react"; import { EventData, EventStatus, UserRole } from "../types"; import { CheckCircle, ArrowUpDown, ArrowUp, ArrowDown, AlertCircle } from "lucide-react"; import { STATUS_COLORS } from "../constants"; interface EventTableProps { events: EventData[]; onEventClick: (event: EventData) => void; onApprove?: (e: React.MouseEvent, eventId: string) => void; onReject?: (e: React.MouseEvent, eventId: string, reason?: string) => void; userRole: UserRole; currentProfessionalId?: string; onAssignmentResponse?: (e: React.MouseEvent, eventId: string, status: string, reason?: string) => void; isManagingTeam?: boolean; // Nova prop para determinar se está na tela de gerenciar equipe professionals?: any[]; // Lista de profissionais para cálculos de gestão de equipe functions?: any[]; // Lista de funções disponíveis isLoading?: boolean; onFinalizeClass?: (e: React.MouseEvent, event: EventData) => void; } type SortField = | "fot" | "date" | "curso" | "instituicao" | "anoFormatura" | "empresa" | "type" | "status"; type SortOrder = "asc" | "desc" | null; export const EventTable: React.FC = ({ events, onEventClick, onApprove, onReject, userRole, currentProfessionalId, onAssignmentResponse, isManagingTeam = false, professionals = [], functions = [], isLoading = false, onFinalizeClass, }) => { const canApprove = isManagingTeam && (userRole === UserRole.BUSINESS_OWNER || userRole === UserRole.SUPERADMIN); const canReject = userRole === UserRole.BUSINESS_OWNER || userRole === UserRole.SUPERADMIN; const isPhotographer = userRole === UserRole.PHOTOGRAPHER; // Função para calcular status da equipe const calculateTeamStatus = (event: EventData) => { const assignments = event.assignments || []; // Helper to check if assignment handles a specific role const isAssignedToRole = (assignment: any, roleSlug: string) => { // If assignment has a specific function ID, check against that function's name if (assignment.funcaoId && professionals && professionals.length > 0) { // Find the function definition in the professionals list or a separate functions list? // The plan said pass `functions` list. // But checking `assignment.funcaoId` against `functions` list is cleaner. // Let's assume we receive `functions` prop. if (functions) { const func = functions.find((f:any) => f.id === assignment.funcaoId); if (func) { return func.nome.toLowerCase().includes(roleSlug.toLowerCase()); } } } // Fallback for legacy data or if assignment.funcaoId is missing (backward compatibility) // Check the professional's capability (OLD BEHAVIOR - CAUSES DOUBLE COUNT if multi-role) // ONLY fallback if funcaoId is missing. if (!assignment.funcaoId) { const professional = professionals.find(p => p.id === assignment.professionalId); if (!professional) return false; // Check functions array first if (professional.functions && professional.functions.length > 0) { return professional.functions.some((f: any) => f.nome.toLowerCase().includes(roleSlug.toLowerCase())); } return (professional.role || "").toLowerCase().includes(roleSlug.toLowerCase()); } return false; }; // Contadores de profissionais aceitos por tipo const acceptedFotografos = assignments.filter(a => a.status === "ACEITO" && isAssignedToRole(a, "fot") ).length; const acceptedRecepcionistas = assignments.filter(a => a.status === "ACEITO" && isAssignedToRole(a, "recep") ).length; const acceptedCinegrafistas = assignments.filter(a => a.status === "ACEITO" && isAssignedToRole(a, "cine") ).length; // Quantidades necessárias const qtdFotografos = event.qtdFotografos || 0; const qtdRecepcionistas = event.qtdRecepcionistas || 0; const qtdCinegrafistas = event.qtdCinegrafistas || 0; // Calcular faltantes const fotoFaltante = Math.max(0, qtdFotografos - acceptedFotografos); const recepFaltante = Math.max(0, qtdRecepcionistas - acceptedRecepcionistas); const cineFaltante = Math.max(0, qtdCinegrafistas - acceptedCinegrafistas); // Verificar se todos os profissionais estão OK const profissionaisOK = fotoFaltante === 0 && recepFaltante === 0 && cineFaltante === 0; return { acceptedFotografos, acceptedRecepcionistas, acceptedCinegrafistas, fotoFaltante, recepFaltante, cineFaltante, profissionaisOK }; }; const [sortField, setSortField] = useState(null); const [sortOrder, setSortOrder] = useState(null); const handleSort = (field: SortField) => { if (sortField === field) { if (sortOrder === "asc") { setSortOrder("desc"); } else if (sortOrder === "desc") { setSortOrder(null); setSortField(null); } } else { setSortField(field); setSortOrder("asc"); } }; const sortedEvents = useMemo(() => { if (!sortField || !sortOrder) { return events; } const sorted = [...events].sort((a, b) => { let aValue: any; let bValue: any; switch (sortField) { case "fot": aValue = (a as any).fotId || ""; bValue = (b as any).fotId || ""; break; case "date": // Parse date robustly const dateA = a.date ? new Date(a.date.includes('T') ? a.date : a.date + "T00:00:00") : new Date(0); const dateB = b.date ? new Date(b.date.includes('T') ? b.date : b.date + "T00:00:00") : new Date(0); aValue = !isNaN(dateA.getTime()) ? dateA.getTime() : 0; bValue = !isNaN(dateB.getTime()) ? dateB.getTime() : 0; break; case "curso": aValue = ((a as any).curso || "").toLowerCase(); bValue = ((b as any).curso || "").toLowerCase(); break; case "instituicao": aValue = ((a as any).instituicao || "").toLowerCase(); bValue = ((b as any).instituicao || "").toLowerCase(); break; case "anoFormatura": aValue = (a as any).anoFormatura || 0; bValue = (b as any).anoFormatura || 0; break; case "empresa": aValue = ((a as any).empresa || "").toLowerCase(); bValue = ((b as any).empresa || "").toLowerCase(); break; case "type": aValue = a.type.toLowerCase(); bValue = b.type.toLowerCase(); break; case "status": aValue = a.status.toLowerCase(); bValue = b.status.toLowerCase(); break; default: return 0; } if (aValue < bValue) return sortOrder === "asc" ? -1 : 1; if (aValue > bValue) return sortOrder === "asc" ? 1 : -1; return 0; }); return sorted; }, [events, sortField, sortOrder]); // Pagination State const [currentPage, setCurrentPage] = useState(1); const itemsPerPage = 50; const totalPages = Math.ceil(sortedEvents.length / itemsPerPage); const paginatedEvents = useMemo(() => { const start = (currentPage - 1) * itemsPerPage; return sortedEvents.slice(start, start + itemsPerPage); }, [sortedEvents, currentPage]); // Reset page when filters/sort change (implicitly when sortedEvents length changes drastically, though here sortedEvents updates) React.useEffect(() => { setCurrentPage(1); }, [events.length, sortField, sortOrder]); // Reset if data source changes significantly const getSortIcon = (field: SortField) => { if (sortField !== field) { return ( ); } if (sortOrder === "asc") { return ; } return ; }; const formatDate = (date: string) => { const eventDate = new Date(date + "T00:00:00"); return eventDate.toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric", }); }; const getStatusDisplay = (status: EventStatus) => { const statusLabels: Record = { [EventStatus.PENDING_APPROVAL]: "Pendente", [EventStatus.CONFIRMED]: "Confirmado", [EventStatus.PLANNING]: "Planejamento", [EventStatus.IN_PROGRESS]: "Em Andamento", [EventStatus.DELIVERED]: "Entregue", [EventStatus.ARCHIVED]: "Arquivado", }; return statusLabels[status] || status; }; // Scroll Sync Logic const tableContainerRef = React.useRef(null); const topScrollRef = React.useRef(null); const [tableScrollWidth, setTableScrollWidth] = useState(0); // Sync scroll width React.useEffect(() => { if (tableContainerRef.current) { setTableScrollWidth(tableContainerRef.current.scrollWidth); } }, [events, sortedEvents, isManagingTeam]); // Dependencies that change table width const handleTopScroll = () => { if (topScrollRef.current && tableContainerRef.current) { tableContainerRef.current.scrollLeft = topScrollRef.current.scrollLeft; } }; const handleTableScroll = () => { if (topScrollRef.current && tableContainerRef.current) { topScrollRef.current.scrollLeft = tableContainerRef.current.scrollLeft; } }; return (
{/* Mobile Card View */}
{sortedEvents.map((event) => { let photographerAssignment = null; if (isPhotographer && currentProfessionalId && event.assignments) { photographerAssignment = event.assignments.find(a => a.professionalId === currentProfessionalId); } return (
onEventClick(event)} >
FOT {event.fot || "-"} {event.type}
{formatDate(event.date)}

{event.curso || "Sem curso defined"}

{event.instituicao || "-"}

{event.empresa || "-"} {event.anoFormatura || "-"}
{getStatusDisplay(event.status)} {(canApprove || canReject || isPhotographer) && (
e.stopPropagation()} className="flex items-center gap-2"> {canApprove && event.status === EventStatus.PENDING_APPROVAL && ( )} {canReject && !canApprove && event.status === EventStatus.PENDING_APPROVAL && ( )} {isPhotographer && photographerAssignment && ( <> {photographerAssignment.status === "PENDENTE" && (
)} {photographerAssignment.status === "ACEITO" && ( Aceito )} {photographerAssignment.status === "REJEITADO" && ( Rejeitado )} )}
)}
); })}
0 ? tableScrollWidth : '100%', height: '1px' }}>
{/* Novas colunas de gestão de equipe */} {(userRole === UserRole.BUSINESS_OWNER || userRole === UserRole.SUPERADMIN) && ( <> )} {(canApprove || isPhotographer) && ( )} {isLoading ? ( ) : ( paginatedEvents.map((event) => { // Logic to find photographer assignment status let photographerAssignment = null; if (isPhotographer && currentProfessionalId && event.assignments) { photographerAssignment = event.assignments.find(a => a.professionalId === currentProfessionalId); } return ( onEventClick(event)} className={`cursor-pointer transition-colors ${event.fot_finalizada ? "bg-red-50 hover:bg-red-100 border-l-4 border-l-red-500" : "hover:bg-gray-50"}`} > {/* Novas células de gestão de equipe */} {(userRole === UserRole.BUSINESS_OWNER || userRole === UserRole.SUPERADMIN) && (() => { const teamStatus = calculateTeamStatus(event); return ( <> ); })()} {(canApprove || canReject || isPhotographer) && ( )} ); }) )}
handleSort("fot")} >
FOT {getSortIcon("fot")}
handleSort("date")} >
Data {getSortIcon("date")}
handleSort("curso")} >
Curso {getSortIcon("curso")}
handleSort("instituicao")} >
Instituição {getSortIcon("instituicao")}
handleSort("anoFormatura")} >
Ano Formatura {getSortIcon("anoFormatura")}
handleSort("empresa")} >
Empresa {getSortIcon("empresa")}
handleSort("type")} >
Tipo Evento {getSortIcon("type")}
handleSort("status")} >
Status {getSortIcon("status")}
QTD Form. Fotóg. Recep. Cine. Estúd. Pts. Foto Pts. Dec. Pts. LED Prof. OK? Fot. Falt. Rec. Falt. Cin. Falt. Ações

Carregando dados...

{event.fot || "-"} {formatDate(event.date)} {event.curso || "-"} {event.instituicao || "-"} {event.anoFormatura || "-"} {event.empresa || "-"} {event.type} {getStatusDisplay(event.status)} {event.qtdFormandos || event.attendees || "-"} {event.qtdFotografos || "-"} {event.qtdRecepcionistas || "-"} {event.qtdCinegrafistas || "-"} {event.qtdEstudios || "-"} {event.qtdPontosFoto || "-"} {event.qtdPontosDecorados || "-"} {event.qtdPontosLed || "-"} {teamStatus.profissionaisOK ? "✓" : "✗"} 0 ? "text-red-600" : "text-green-600" }`}> {teamStatus.fotoFaltante} 0 ? "text-red-600" : "text-green-600" }`}> {teamStatus.recepFaltante} 0 ? "text-red-600" : "text-green-600" }`}> {teamStatus.cineFaltante} e.stopPropagation()} >
{canApprove && event.status === EventStatus.PENDING_APPROVAL && (
)} {canReject && !canApprove && event.status === EventStatus.PENDING_APPROVAL && ( )} {isPhotographer && photographerAssignment && ( <> {photographerAssignment.status === "PENDENTE" && ( <> )} {photographerAssignment.status === "ACEITO" && ( Aceito )} {photographerAssignment.status === "REJEITADO" && (
Rejeitado {photographerAssignment.reason && ( )}
)} )}
{(userRole === UserRole.BUSINESS_OWNER || userRole === UserRole.SUPERADMIN) && onFinalizeClass && ( )}
{/* Pagination Controls */} {sortedEvents.length === 0 && (

Nenhum evento encontrado.

)}
); }; interface PaginationControlsProps { currentPage: number; totalPages: number; onPageChange: (page: number) => void; } const PaginationControls: React.FC = ({ currentPage, totalPages, onPageChange }) => { if (totalPages <= 1) return null; return (

Mostrando página {currentPage} de{" "} {totalPages}

); };