import React, { useState, useEffect, useMemo } from "react"; import { useNavigate } from 'react-router-dom'; import { UserRole, EventData, EventStatus, EventType, Professional } from "../types"; import { EventTable } from "../components/EventTable"; import { EventFiltersBar, EventFilters } from "../components/EventFiltersBar"; import { EventForm } from "../components/EventForm"; import { Button } from "../components/Button"; import { PlusCircle, Search, CheckCircle, Clock, Edit, Users, Map, Building2, Calendar, MapPin, X, UserCheck, UserX, } from "lucide-react"; import { useAuth } from "../contexts/AuthContext"; import { useData } from "../contexts/DataContext"; import { STATUS_COLORS } from "../constants"; import { ProfessionalDetailsModal } from "../components/ProfessionalDetailsModal"; interface DashboardProps { initialView?: "list" | "create"; } export const Dashboard: React.FC = ({ initialView = "list", }) => { const { user } = useAuth(); const navigate = useNavigate(); // Extract updateEventDetails from useData const { events, getEventsByRole, addEvent, updateEventStatus, assignPhotographer, professionals, getInstitutionById, getActiveCoursesByInstitutionId, respondToAssignment, updateEventDetails, functions, isLoading, } = useData(); // ... (inside component) const handleSaveEvent = async (data: any) => { const isClient = user.role === UserRole.EVENT_OWNER; if (view === "edit" && selectedEvent) { if (updateEventDetails) { await updateEventDetails(selectedEvent.id, data); // Force reload of view to reflect changes (or rely on DataContext optimistic update) // But DataContext optimistic update only touched generic fields. // Address might still be old in 'selectedEvent' state if we don't update it. // Updating selectedEvent manually as well to be safe: const updatedEvent = { ...selectedEvent, ...data, date: data.date || data.data_evento?.split('T')[0] || selectedEvent.date }; setSelectedEvent(updatedEvent); setView("details"); // Optional: Reload page safely if critical fields changed that DataContext map didn't catch? // For now, trust DataContext + local state update. // Actually, DataContext refetch logic was "try import...", so it might be async. // Let's reload window to be 100% sure for the user as requested "mudei a data e não mudou". window.location.reload(); } else { console.error("Update function not available"); } } else { const initialStatus = isClient ? EventStatus.PENDING_APPROVAL : EventStatus.PLANNING; const newEvent: EventData = { ...data, id: Math.random().toString(36).substr(2, 9), status: initialStatus, checklist: [], ownerId: isClient ? user.id : "unknown", photographerIds: [], }; addEvent(newEvent); setView("list"); } }; const [view, setView] = useState<"list" | "create" | "edit" | "details">( initialView ); const [searchTerm, setSearchTerm] = useState(""); const [selectedEvent, setSelectedEvent] = useState(null); const [activeFilter, setActiveFilter] = useState("all"); const [advancedFilters, setAdvancedFilters] = useState({ date: "", fotId: "", type: "", }); const [isTeamModalOpen, setIsTeamModalOpen] = useState(false); const [viewingProfessional, setViewingProfessional] = useState(null); // Estados para filtros do modal de equipe const [teamSearchTerm, setTeamSearchTerm] = useState(""); const [teamRoleFilter, setTeamRoleFilter] = useState("all"); const [teamStatusFilter, setTeamStatusFilter] = useState("all"); const [teamAvailabilityFilter, setTeamAvailabilityFilter] = useState("all"); const [roleSelectionProf, setRoleSelectionProf] = useState(null); useEffect(() => { if (initialView) { setView(initialView); if (initialView === "create") setSelectedEvent(null); } }, [initialView]); const handleViewProfessional = (professional: Professional) => { setViewingProfessional(professional); }; // Função para calcular profissionais faltantes e status const calculateTeamStatus = (event: EventData) => { const assignments = event.assignments || []; // Contadores de profissionais aceitos por tipo // Helper to check if professional has a specific role const hasRole = (professional: Professional | undefined, roleSlug: string, assignedFuncaoId?: string) => { if (!professional) return false; const term = roleSlug.toLowerCase(); // 1. If assignment has explicit function, check it if (assignedFuncaoId) { const fn = functions.find(f => f.id === assignedFuncaoId); if (fn && fn.nome.toLowerCase().includes(term)) return true; // If term didn't match the assigned function, return false (exclusive assignment) // Unless we fallback? No, if assigned as Cine, shouldn't count as Fot. if (fn) return false; } // 2. Fallback to existing logic (capabilities) if no function assigned // Check functions array first (new multi-role system) if (professional.functions && professional.functions.length > 0) { return professional.functions.some(f => f.nome.toLowerCase().includes(term)); } // Fallback to legacy role field return (professional.role || "").toLowerCase().includes(term); }; // Contadores de profissionais aceitos por tipo const acceptedFotografos = assignments.filter(a => a.status === "ACEITO" && hasRole(professionals.find(p => p.id === a.professionalId), "fot", a.funcaoId) ).length; const pendingFotografos = assignments.filter(a => a.status === "PENDENTE" && hasRole(professionals.find(p => p.id === a.professionalId), "fot", a.funcaoId) ).length; const acceptedRecepcionistas = assignments.filter(a => a.status === "ACEITO" && hasRole(professionals.find(p => p.id === a.professionalId), "recep", a.funcaoId) ).length; const pendingRecepcionistas = assignments.filter(a => a.status === "PENDENTE" && hasRole(professionals.find(p => p.id === a.professionalId), "recep", a.funcaoId) ).length; const acceptedCinegrafistas = assignments.filter(a => a.status === "ACEITO" && hasRole(professionals.find(p => p.id === a.professionalId), "cine", a.funcaoId) ).length; const pendingCinegrafistas = assignments.filter(a => a.status === "PENDENTE" && hasRole(professionals.find(p => p.id === a.professionalId), "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, pendingFotografos, acceptedRecepcionistas, pendingRecepcionistas, acceptedCinegrafistas, pendingCinegrafistas, fotoFaltante, recepFaltante, cineFaltante, profissionaisOK }; }; // Função para fechar modal de equipe e limpar filtros const closeTeamModal = () => { setIsTeamModalOpen(false); setTeamSearchTerm(""); setTeamRoleFilter("all"); setTeamStatusFilter("all"); setTeamAvailabilityFilter("all"); }; // Função para filtrar profissionais no modal de equipe const getFilteredTeamProfessionals = () => { if (!selectedEvent) return professionals; return professionals.filter((professional) => { // Filter out professionals with unknown roles (matches Team.tsx logic) if (functions.length > 0) { const isValidRole = functions.some(f => f.id === professional.funcao_profissional_id); if (!isValidRole) return false; } // Filtro por busca (nome ou email) if (teamSearchTerm) { const searchLower = teamSearchTerm.toLowerCase(); const nameMatch = (professional.name || professional.nome || "").toLowerCase().includes(searchLower); const emailMatch = (professional.email || "").toLowerCase().includes(searchLower); if (!nameMatch && !emailMatch) return false; } // Filtro por função/role if (teamRoleFilter !== "all") { const professionalFunctions = professional.functions || []; // Check if professional has the selected function ID in their list const hasMatchingFunction = professionalFunctions.some(f => f.id === teamRoleFilter); // Also check if professional.funcao_profissional_id matches (primary role) const hasMatchingPrimaryRole = professional.funcao_profissional_id === teamRoleFilter; if (!hasMatchingFunction && !hasMatchingPrimaryRole) return false; } // Verificar status do assignment para este evento const assignment = (selectedEvent.assignments || []).find( (a) => a.professionalId === professional.id ); const status = assignment ? assignment.status : null; const isAssigned = !!status && status !== "REJEITADO"; // Verificar se está ocupado em outro evento na mesma data const isBusy = !isAssigned && events.some(e => e.id !== selectedEvent.id && e.date === selectedEvent.date && (e.assignments || []).some(a => a.professionalId === professional.id && a.status === 'ACEITO') ); // Filtro por status if (teamStatusFilter !== "all") { switch (teamStatusFilter) { case "assigned": if (!isAssigned) return false; break; case "available": if (isAssigned || isBusy) return false; break; case "busy": if (!isBusy) return false; break; case "rejected": if (!status || status !== "REJEITADO") return false; break; } } // Filtro por disponibilidade if (teamAvailabilityFilter !== "all") { switch (teamAvailabilityFilter) { case "available": if (isAssigned || isBusy) return false; break; case "unavailable": if (!isAssigned && !isBusy) return false; break; } } return true; }); }; // Guard Clause for basic security if (!user) return
Acesso Negado. Faça login.
; const myEvents = getEventsByRole(user.id, user.role); const currentProfessionalId = user.role === UserRole.PHOTOGRAPHER ? professionals.find((p) => p.usuarioId === user.id)?.id : undefined; // Extract unique values for filters const { availableTypes } = useMemo(() => { const types = [...new Set(myEvents.map((e) => e.type))].sort(); return { availableTypes: types, }; }, [myEvents]); // Filter Logic const filteredEvents = myEvents.filter((e) => { const matchesSearch = e.name .toLowerCase() .includes(searchTerm.toLowerCase()); const matchesStatus = activeFilter === "all" || (activeFilter === "pending" && e.status === EventStatus.PENDING_APPROVAL) || (activeFilter === "active" && e.status !== EventStatus.ARCHIVED && e.status !== EventStatus.PENDING_APPROVAL); // Advanced filters const matchesDate = !advancedFilters.date || e.date === advancedFilters.date; const matchesFot = !advancedFilters.fotId || String(e.fot || "").toLowerCase().includes(advancedFilters.fotId.toLowerCase()); const matchesType = !advancedFilters.type || e.type === advancedFilters.type; return ( matchesSearch && matchesStatus && matchesDate && matchesFot && matchesType ); }); // Keep selectedEvent in sync with global events state useEffect(() => { if (selectedEvent) { const updated = events.find((e) => e.id === selectedEvent.id); if (updated && updated !== selectedEvent) { setSelectedEvent(updated); } } }, [events, selectedEvent]); const handleApprove = (e: React.MouseEvent, eventId: string) => { e.stopPropagation(); updateEventStatus(eventId, EventStatus.CONFIRMED); }; const handleReject = (e: React.MouseEvent, eventId: string, reason?: string) => { e.stopPropagation(); updateEventStatus(eventId, EventStatus.ARCHIVED, reason); }; const handleAssignmentResponse = async ( e: React.MouseEvent, eventId: string, status: string, reason?: string ) => { e.stopPropagation(); // Validação de Lotação da Equipe (Apenas para Aceite) if (status === "ACEITO") { const targetEvent = events.find(evt => evt.id === eventId); const currentProfessional = professionals.find(p => p.usuarioId === user.id); if (targetEvent && currentProfessional) { // Reutilizando lógica de contagem (adaptada de calculateTeamStatus) // Precisamos recalcular pois calculateTeamStatus depende de selectedEvent que pode não ser o alvo aqui const assignments = targetEvent.assignments || []; const hasRole = (professional: Professional | undefined, roleSlug: string) => { if (!professional) return false; const term = roleSlug.toLowerCase(); if (professional.functions && professional.functions.length > 0) { return professional.functions.some(f => f.nome.toLowerCase().includes(term)); } return (professional.role || "").toLowerCase().includes(term); }; // Helper to check if assignment handles a specific role (using ID first, then fallback) const isAssignedToRole = (assignment: any, roleSlug: string) => { if (assignment.funcaoId && functions) { const func = functions.find(f => f.id === assignment.funcaoId); if (func) return func.nome.toLowerCase().includes(roleSlug.toLowerCase()); } // Fallback only if no function assigned if (!assignment.funcaoId) { const p = professionals.find(pr => pr.id === assignment.professionalId); return hasRole(p, roleSlug); } return false; }; // Contagens Atuais (Updated to match EventTable logic) const acceptedFot = assignments.filter(a => a.status === "ACEITO" && isAssignedToRole(a, "fot")).length; const acceptedRecep = assignments.filter(a => a.status === "ACEITO" && isAssignedToRole(a, "recep")).length; const acceptedCine = assignments.filter(a => a.status === "ACEITO" && isAssignedToRole(a, "cine")).length; // Limites const reqFot = targetEvent.qtdFotografos || 0; const reqRecep = targetEvent.qtdRecepcionistas || 0; const reqCine = targetEvent.qtdCinegrafistas || 0; // Find OUR assignment to know what we are accepting const myAssignment = assignments.find(a => a.professionalId === currentProfessional.id); const checkQuota = (roleSlugs: string[], acceptedCount: number, requiredCount: number, roleName: string) => { // Only check quota if I am assigned to this role if (myAssignment) { if (isAssignedToRole(myAssignment, roleSlugs[0])) { // Using first slug as proxy for role group if (acceptedCount >= requiredCount) { return `A equipe de ${roleName} já está completa (${acceptedCount}/${requiredCount}).`; } } } else { // Fallback if no assignment found (shouldn't happen for existing invite) const isProfessionalRole = roleSlugs.some(slug => hasRole(currentProfessional, slug)); if (isProfessionalRole && acceptedCount >= requiredCount) { return `A equipe de ${roleName} já está completa (${acceptedCount}/${requiredCount}).`; } } return null; }; const errors = []; const errFot = checkQuota(["fot"], acceptedFot, reqFot, "Fotografia"); if (errFot) errors.push(errFot); const errRecep = checkQuota(["recep"], acceptedRecep, reqRecep, "Recepção"); if (errRecep) errors.push(errRecep); const errCine = checkQuota(["cine"], acceptedCine, reqCine, "Cinegrafia"); if (errCine) errors.push(errCine); if (errors.length > 0) { alert(`Não foi possível aceitar o convite:\n\n${errors.join("\n")}\n\nEntre em contato com a administração.`); return; // Bloqueia a ação } } } await respondToAssignment(eventId, status, reason); }; const handleOpenMaps = () => { if (!selectedEvent) return; if (selectedEvent.address.mapLink) { window.open(selectedEvent.address.mapLink, "_blank"); return; } const { street, number, city, state } = selectedEvent.address; const query = encodeURIComponent( `${street}, ${number}, ${city} - ${state}` ); window.open( `https://www.google.com/maps/search/?api=1&query=${query}`, "_blank" ); }; const handleManageTeam = () => { setIsTeamModalOpen(true); }; const togglePhotographer = (photographerId: string) => { if (!selectedEvent) return; const prof = professionals.find(p => p.id === photographerId); const assignment = selectedEvent.assignments?.find(a => a.professionalId === photographerId); if (!assignment && prof && prof.functions && prof.functions.length > 1) { setRoleSelectionProf(prof); return; } assignPhotographer(selectedEvent.id, photographerId); }; const handleRoleSelect = (funcaoId: string) => { if (roleSelectionProf && selectedEvent) { assignPhotographer(selectedEvent.id, roleSelectionProf.id, funcaoId); setRoleSelectionProf(null); } }; // --- RENDERS PER ROLE --- const renderRoleSpecificHeader = () => { if (user.role === UserRole.EVENT_OWNER) { return (

Meus Eventos

Acompanhe seus eventos ou solicite novos orçamentos.

); } if (user.role === UserRole.PHOTOGRAPHER) { return (

Eventos Designados

Gerencie seus trabalhos e visualize detalhes.

); } return (

Gestão Geral

Controle total de eventos, aprovações e equipes.

); }; const renderRoleSpecificActions = () => { if (user.role === UserRole.PHOTOGRAPHER || user.role === UserRole.AGENDA_VIEWER || user.role === UserRole.RESEARCHER) return null; const label = user.role === UserRole.EVENT_OWNER ? "Solicitar Novo Evento" : "Novo Evento"; return ( ); }; // --- MAIN RENDER --- return (
{/* Header */} {view === "list" && (
{renderRoleSpecificHeader()} {renderRoleSpecificActions()}
)} {/* Content Switcher */} {view === "list" && (
{/* Search Bar */}
setSearchTerm(e.target.value)} />
{(user.role === UserRole.BUSINESS_OWNER || user.role === UserRole.SUPERADMIN) && (
)}
{/* Advanced Filters */} {/* Results Count */}
Exibindo{" "} {filteredEvents.length} {" "} de {myEvents.length} eventos
{/* Event Table */} { setSelectedEvent(event); setView("details"); }} onApprove={handleApprove} onReject={handleReject} userRole={user.role} currentProfessionalId={currentProfessionalId} onAssignmentResponse={handleAssignmentResponse} isManagingTeam={true} // Permitir aprovação na gestão geral professionals={professionals} // Adicionar lista de profissionais functions={functions} isLoading={isLoading} />
)} {(view === "create" || view === "edit") && ( setView(view === "edit" ? "details" : "list")} onSubmit={handleSaveEvent} initialData={view === "edit" ? selectedEvent : undefined} /> )} {view === "details" && selectedEvent && (
{/* Status Banner */} {selectedEvent.status === EventStatus.PENDING_APPROVAL && user.role === UserRole.EVENT_OWNER && (

Solicitação em Análise

Seu evento foi enviado e está aguardando aprovação da equipe Photum.

)}
{/* Header Section - Sem foto */}

{selectedEvent.name}

{new Date( selectedEvent.date + "T00:00:00" ).toLocaleDateString("pt-BR")}{" "} às {selectedEvent.time} {selectedEvent.address.city},{" "} {selectedEvent.address.state}
{selectedEvent.status}
{(user.role === UserRole.BUSINESS_OWNER || user.role === UserRole.SUPERADMIN) && ( <> )} {user.role === UserRole.EVENT_OWNER && selectedEvent.status !== EventStatus.ARCHIVED && ( )}
{/* Quick Info Cards */}

Tipo

{selectedEvent.type}

Data

{new Date( selectedEvent.date + "T00:00:00" ).toLocaleDateString("pt-BR")}

Horário

{selectedEvent.time}

{/* FOT Information Table */}

Informações FOT

{/* Seção de Gestão de Equipe */} {/* Helper to calculate pending counts */ (() => { const teamStatus = calculateTeamStatus(selectedEvent); const renderResourceRow = (label: string, confirmed: number, pending: number, required: number) => { if (!required && confirmed === 0 && pending === 0) { return ( ); } const isComplete = confirmed >= required; return ( ); }; return ( <> {renderResourceRow( "Fotógrafo", teamStatus.acceptedFotografos, teamStatus.pendingFotografos, selectedEvent.qtdFotografos || 0 )} {renderResourceRow( "Recepcionista", teamStatus.acceptedRecepcionistas, teamStatus.pendingRecepcionistas, selectedEvent.qtdRecepcionistas || 0 )} {renderResourceRow( "Cinegrafista", teamStatus.acceptedCinegrafistas, teamStatus.pendingCinegrafistas, selectedEvent.qtdCinegrafistas || 0 )} ); })()} {/* Status e Faltantes */} {(() => { const teamStatus = calculateTeamStatus(selectedEvent); return ( <> ); })()}
FOT {(selectedEvent as any).fot || "-"}
Data {new Date( selectedEvent.date + "T00:00:00" ).toLocaleDateString("pt-BR")}
Curso {selectedEvent.curso || "-"}
Instituição {selectedEvent.instituicao || "-"}
Ano Formatura {selectedEvent.anoFormatura || "-"}
Empresa {selectedEvent.empresa || "-"}
Tipo Evento {selectedEvent.type}
Observações {(selectedEvent as any).observacoes || "-"}
Local {(selectedEvent as any).local_evento || (selectedEvent as any).locationName || "-"}
Endereço {selectedEvent.address.street}, {selectedEvent.address.number} - {selectedEvent.address.city}/{selectedEvent.address.state} {selectedEvent.address.zip && ` | CEP: ${selectedEvent.address.zip}`}
Gestão de Equipe e Recursos
QTD Formandos {selectedEvent.qtdFormandos || (selectedEvent as any).qtd_formandos || selectedEvent.attendees || "-"}
{label} -
{label}
{confirmed} / {required || 0} {isComplete && }
{pending > 0 && ( ({pending} aguardando aceite) )}
Estúdio {selectedEvent.qtdEstudios || (selectedEvent as any).qtd_estudios || "-"}
Ponto de Foto {selectedEvent.qtdPontosFoto || (selectedEvent as any).qtd_ponto_foto || "-"}
Ponto Decorado {selectedEvent.qtdPontosDecorados || (selectedEvent as any).qtd_ponto_decorado || "-"}
Ponto LED {selectedEvent.qtdPontosLed || (selectedEvent as any).qtd_pontos_led || "-"}
Plataforma 360 {selectedEvent.qtdPlataforma360 || (selectedEvent as any).qtd_plataforma_360 || "-"}
Profissionais OK? {teamStatus.profissionaisOK ? ( <> Completo ) : ( <> Incompleto )}
Foto Faltante 0 ? "text-red-600" : "text-green-600"}`}> {teamStatus.fotoFaltante}
Recep. Faltante 0 ? "text-red-600" : "text-green-600"}`}> {teamStatus.recepFaltante}
Cine Faltante 0 ? "text-red-600" : "text-green-600"}`}> {teamStatus.cineFaltante}
Horário {selectedEvent.time}
{/* Institution Information */} {selectedEvent.institutionId && (() => { const institution = getInstitutionById( selectedEvent.institutionId ); if (institution) { return (

{institution.name}

{institution.type}

{/* Course Information */} {selectedEvent.courseId && (() => { const course = getActiveCoursesByInstitutionId( selectedEvent.institutionId ).find( (c) => c.id === selectedEvent.courseId ); if (course) { return (

Curso/Turma

{course.name} -{" "} {course.graduationType} ( {course.year}/{course.semester}º sem)

); } return null; })()}

Contato

{institution.phone}

{institution.email}

{institution.address && (

Endereço

{institution.address.street},{" "} {institution.address.number}

{institution.address.city} -{" "} {institution.address.state}

)}
{institution.description && (

{institution.description}

)}
); } return null; })()}

Sobre o Evento

{selectedEvent.briefing || "Sem briefing detalhado."}

{selectedEvent.contacts.length > 0 && (

Contatos & Responsáveis

{selectedEvent.contacts.map((c, i) => (

{c.name}

{c.role}

{c.phone}

))}
)}
{/* Localização Card */}

Localização

{selectedEvent.address.street},{" "} {selectedEvent.address.number}

{selectedEvent.address.city} -{" "} {selectedEvent.address.state}

{selectedEvent.address.zip && (

CEP: {selectedEvent.address.zip}

)}
{/* Equipe Designada */} {(selectedEvent.assignments && selectedEvent.assignments.filter(a => a.status !== "REJEITADO").length > 0) || ((user.role === UserRole.BUSINESS_OWNER || user.role === UserRole.SUPERADMIN) && selectedEvent.photographerIds.length > 0) ? (

Equipe ({(selectedEvent.assignments || []).filter(a => a.status !== "REJEITADO").length})

{(user.role === UserRole.BUSINESS_OWNER || user.role === UserRole.SUPERADMIN) && ( )}
{(selectedEvent.assignments || []) .filter(a => a.status !== "REJEITADO") .map((assignment) => { const photographer = professionals.find( (p) => p.id === assignment.professionalId ); return (
handleViewProfessional(photographer!)} >
{photographer?.name || "Fotógrafo"} {assignment.status === "PENDENTE" ? "Convite Pendente" : "Confirmado"} {assignment.funcaoId && functions && ( {functions.find(f => f.id === assignment.funcaoId)?.nome || ""} )}
{assignment.status === "PENDENTE" && ( )} {assignment.status === "ACEITO" && ( )}
); })} {(selectedEvent.assignments || []).filter(a => a.status !== "REJEITADO").length === 0 && (

Nenhum profissional na equipe.

)}
) : null }
)} {/* Modal de Gerenciamento de Equipe */} {isTeamModalOpen && selectedEvent && (
{/* Header */}

Gerenciar Equipe

{selectedEvent.name} -{" "} {new Date( selectedEvent.date + "T00:00:00" ).toLocaleDateString("pt-BR")}

{/* Body */}

Profissionais disponíveis para a data{" "} {new Date( selectedEvent.date + "T00:00:00" ).toLocaleDateString("pt-BR")} . Clique em "Adicionar" para atribuir ao evento.

{/* Filtros e Busca */}
{/* Barra de busca */}
setTeamSearchTerm(e.target.value)} className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-brand-purple focus:border-transparent" />
{/* Filtros */}
{/* Filtro por função */} {/* Filtro por status */} {/* Filtro por disponibilidade */} {/* Botão limpar filtros */} {(teamSearchTerm || teamRoleFilter !== "all" || teamStatusFilter !== "all" || teamAvailabilityFilter !== "all") && ( )}
{/* Tabela de Profissionais (Desktop) */}
{getFilteredTeamProfessionals().map((photographer) => { const assignment = (selectedEvent.assignments || []).find( (a) => a.professionalId === photographer.id ); const status = assignment ? assignment.status : null; const isAssigned = !!status && status !== "REJEITADO"; // Check if busy in other events on the same date const busyEvent = !isAssigned ? events.find(e => e.id !== selectedEvent.id && e.date === selectedEvent.date && (e.assignments || []).some(a => a.professionalId === photographer.id && a.status === 'ACEITO') ) : undefined; const isBusy = !!busyEvent; return ( {/* Profissional */} {/* Função */} {/* E-mail */} {/* Status */} {/* Ação */} ); })} {getFilteredTeamProfessionals().length === 0 && ( )}
Profissional Função E-mail Status Ação
handleViewProfessional(photographer)}>

{photographer.name}

ID: {photographer.id}

{photographer.functions && photographer.functions.length > 0 ? photographer.functions.map(f => f.nome).join(", ") : photographer.role} {photographer.email} {status === "ACEITO" && ( Confirmado )} {status === "PENDENTE" && ( Pendente )} {status === "REJEITADO" && ( Recusado )} {!status && ( {isBusy ? : } {isBusy ? "Em outro evento" : "Disponível"} )}

{professionals.length === 0 ? "Nenhum profissional disponível para esta data" : "Nenhum profissional encontrado com os filtros aplicados" }

{professionals.length === 0 ? "Tente selecionar outra data ou entre em contato com a equipe" : "Tente ajustar os filtros de busca" }

{/* Lista de Cards (Mobile) */}
{getFilteredTeamProfessionals().map((photographer) => { const assignment = (selectedEvent.assignments || []).find( (a) => a.professionalId === photographer.id ); const status = assignment ? assignment.status : null; // Check if busy in other events on the same date const isBusy = !status && events.some(e => e.id !== selectedEvent.id && e.date === selectedEvent.date && e.photographerIds.includes(photographer.id) ); return (
handleViewProfessional(photographer)}>

{photographer.name || photographer.nome}

ID: {photographer.id.substring(0, 8)}...

{photographer.functions && photographer.functions.length > 0 ? photographer.functions.map(f => f.nome).join(", ") : photographer.role} {status === "ACEITO" && ( Confirmado )} {status === "PENDENTE" && ( Pendente )} {status === "REJEITADO" && ( Recusado )} {!status && ( {isBusy ? : } {isBusy ? "Em outro evento" : "Disponível"} )}
); })} {professionals.length === 0 && (

Nenhum profissional disponível.

)}
{/* Footer */}
)} {viewingProfessional && ( setViewingProfessional(null)} /> )} {roleSelectionProf && (

Selecione a Função

Qual função {roleSelectionProf.nome} irá desempenhar neste evento?

{roleSelectionProf.functions?.map((fn) => ( ))}
)}
); };