import React, { useState, useEffect } from "react"; import { Plus, Trash, User, Truck, MapPin } from "lucide-react"; import { useAuth } from "../contexts/AuthContext"; import { listEscalas, createEscala, deleteEscala, EscalaInput } from "../services/apiService"; import { useData } from "../contexts/DataContext"; import { UserRole } from "../types"; interface EventSchedulerProps { agendaId: string; dataEvento: string; // YYYY-MM-DD allowedProfessionals?: string[]; // IDs of professionals allowed to be scheduled onUpdateStats?: (stats: { studios: number }) => void; defaultTime?: string; } const timeSlots = [ "07:00", "08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00", "21:00", "22:00", "23:00", "00:00" ]; const EventScheduler: React.FC = ({ agendaId, dataEvento, allowedProfessionals, onUpdateStats, defaultTime }) => { const { token, user } = useAuth(); const { professionals, events } = useData(); const [escalas, setEscalas] = useState([]); const [loading, setLoading] = useState(false); // New entry state const [selectedProf, setSelectedProf] = useState(""); const [startTime, setStartTime] = useState(defaultTime || "08:00"); const [endTime, setEndTime] = useState("12:00"); // Could calculated based on start, but keep simple const [role, setRole] = useState(""); const isEditable = user?.role === UserRole.SUPERADMIN || user?.role === UserRole.BUSINESS_OWNER; // Helper to check availability const checkAvailability = (profId: string) => { // Parse current request time in minutes const [h, m] = startTime.split(':').map(Number); const reqStart = h * 60 + m; const reqEnd = reqStart + 240; // +4 hours // Check if professional is in any other event on the same day with overlap const conflict = events.some(e => { if (e.id === agendaId) return false; // Ignore current event (allow re-scheduling or moving within same event?) // Actually usually we don't want to double book in same event either unless intention is specific. // But 'escalas' check (Line 115) already handles "already in this scale". // If they are assigned to the *Event Team* (Logistics) but not Scale yet, it doesn't mean they are busy at THIS exact time? // Wait, 'events.photographerIds' means they are on the Team. // Being on the Team for specific time? // Does 'EventData' have time? Yes. 'e.time'. // If they are in another Event Team, we assume they are busy for that Event's duration. if (e.date === dataEvento && e.photographerIds.includes(profId)) { const [eh, em] = (e.time || "00:00").split(':').map(Number); const evtStart = eh * 60 + em; const evtEnd = evtStart + 240; // Assume 4h duration for other events too // Overlap check return (reqStart < evtEnd && evtStart < reqEnd); } return false; }); return conflict; }; useEffect(() => { if (agendaId && token) { fetchEscalas(); } }, [agendaId, token]); // Recalculate stats whenever scales change useEffect(() => { if (onUpdateStats && escalas.length >= 0) { let totalStudios = 0; escalas.forEach(item => { const prof = professionals.find(p => p.id === item.profissional_id); if (prof && prof.qtd_estudio) { totalStudios += prof.qtd_estudio; } }); onUpdateStats({ studios: totalStudios }); } }, [escalas, professionals, onUpdateStats]); const fetchEscalas = async () => { setLoading(true); const result = await listEscalas(agendaId, token!); if (result.data) { setEscalas(result.data); } setLoading(false); }; const handleAddEscala = async () => { if (!selectedProf || !startTime) return; // Create Date objects from Local Time const localStart = new Date(`${dataEvento}T${startTime}:00`); const localEnd = new Date(localStart); localEnd.setHours(localEnd.getHours() + 4); // Convert to UTC ISO String and format for backend (Space, no ms) // toISOString returns YYYY-MM-DDTHH:mm:ss.sssZ const startISO = localStart.toISOString().replace('T', ' ').replace(/\.\d{3}Z$/, 'Z'); const endISO = localEnd.toISOString().replace('T', ' ').replace(/\.\d{3}Z$/, 'Z'); const input: EscalaInput = { agenda_id: agendaId, profissional_id: selectedProf, data_hora_inicio: startISO, data_hora_fim: endISO, funcao_especifica: role }; const res = await createEscala(input, token!); if (res.data) { fetchEscalas(); setSelectedProf(""); setRole(""); } else { alert("Erro ao criar escala: " + res.error); } }; const handleDelete = async (id: string) => { if (confirm("Remover profissional da escala?")) { await deleteEscala(id, token!); fetchEscalas(); } }; // 1. Start with all professionals or just the allowed ones let availableProfs = professionals; if (allowedProfessionals && allowedProfessionals.length > 0) { availableProfs = availableProfs.filter(p => allowedProfessionals.includes(p.id)); } // 2. Filter out professionals already in schedule to prevent duplicates // But keep the currently selected one valid if it was just selected availableProfs = availableProfs.filter(p => !escalas.some(e => e.profissional_id === p.id)); const selectedProfessionalData = professionals.find(p => p.id === selectedProf); return (

Escala de Profissionais

{/* Warning if restricting and empty */} {isEditable && allowedProfessionals && allowedProfessionals.length === 0 && (
Nenhum profissional atribuído a este evento. Adicione membros à equipe antes de criar a escala.
)} {/* Add Form - Only for Admins */} {isEditable && (
setStartTime(e.target.value)} />
setRole(e.target.value)} />
{/* Equipment Info Preview */} {selectedProfessionalData && (selectedProfessionalData.equipamentos || selectedProfessionalData.qtd_estudio > 0) && (
Equipamentos: {selectedProfessionalData.equipamentos || "Nenhum cadastrado"} {selectedProfessionalData.qtd_estudio > 0 && ( • Possui {selectedProfessionalData.qtd_estudio} Estúdio(s) )}
)}
)} {/* Timeline / List */}
{loading ?

Carregando...

: escalas.length === 0 ? (

Nenhuma escala definida.

) : ( escalas.map(item => { // Find professional data again to show equipment in list if needed // Ideally backend should return it, but for now we look up in global list if available const profData = professionals.find(p => p.id === item.profissional_id); return (
{item.avatar_url ? ( ) : ( )}

{item.profissional_nome} {item.phone && ({item.phone})}

{new Date(item.start).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - {new Date(item.end).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} {item.role && {item.role}}

{isEditable && ( )}
{/* Show equipment if available */} {profData && profData.equipamentos && (
Equip: {profData.equipamentos}
)}
); }) )}
); }; export default EventScheduler;