import React, { useState, useEffect } from "react"; import { Users, Camera, Mail, Phone, MapPin, Star, Plus, Search, Filter, User, Upload, X, Video, UserCheck, Car, Building, CreditCard, Trash2, Edit2, AlertTriangle, Check, DollarSign, } from "lucide-react"; import { Button } from "../components/Button"; import { getFunctions, createProfessional, getProfessionals, updateProfessional, deleteProfessional, getUploadURL, uploadFileToSignedUrl, } from "../services/apiService"; import { useAuth } from "../contexts/AuthContext"; import { Professional, CreateProfessionalDTO } from "../types"; export const TeamPage: React.FC = () => { const { user, token: contextToken } = useAuth(); const token = contextToken || ""; // Lists const [professionals, setProfessionals] = useState([]); const [roles, setRoles] = useState<{ id: string; nome: string }[]>([]); // Loading States const [isLoading, setIsLoading] = useState(true); const [isBackendDown, setIsBackendDown] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const [isLoadingCep, setIsLoadingCep] = useState(false); // Filters const [searchTerm, setSearchTerm] = useState(""); const [roleFilter, setRoleFilter] = useState("all"); const [statusFilter, setStatusFilter] = useState("all"); // Selection & Modals const [selectedProfessional, setSelectedProfessional] = useState(null); const [showAddModal, setShowAddModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); const [professionalToDelete, setProfessionalToDelete] = useState(null); const [viewProfessional, setViewProfessional] = useState(null); // Form State const initialFormState: CreateProfessionalDTO = { nome: "", funcao_profissional_id: "", email: "", whatsapp: "", cpf_cnpj_titular: "", endereco: "", cidade: "", uf: "", banco: "", agencia: "", conta_pix: "", tipo_cartao: "", carro_disponivel: false, tem_estudio: false, qtd_estudio: 0, observacao: "", qual_tec: 0, educacao_simpatia: 0, desempenho_evento: 0, disp_horario: 0, media: 0, tabela_free: "", extra_por_equipamento: false, equipamentos: "", avatar_url: "", }; const [formData, setFormData] = useState(initialFormState); const [avatarFile, setAvatarFile] = useState(null); const [avatarPreview, setAvatarPreview] = useState(""); // Fetch Data useEffect(() => { fetchData(); }, [token]); const fetchData = async () => { setIsLoading(true); try { const [rolesData, prosData] = await Promise.all([ getFunctions(), getProfessionals(token), ]); if (rolesData.data) setRoles(rolesData.data); if (prosData.data) { setProfessionals(prosData.data); setIsBackendDown(false); } else if (prosData.error) { console.error("Error fetching professionals:", prosData.error); if (prosData.isBackendDown) setIsBackendDown(true); } } catch (error) { console.error("Error fetching data:", error); } finally { setIsLoading(false); } }; // Helpers const GenericAvatar = "https://ui-avatars.com/api/?background=random"; const ufs = [ "AC", "AL", "AP", "AM", "BA", "CE", "DF", "ES", "GO", "MA", "MT", "MS", "MG", "PA", "PB", "PR", "PE", "PI", "RJ", "RN", "RS", "RO", "RR", "SC", "SP", "SE", "TO" ]; const maskPhone = (value: string) => { return value .replace(/\D/g, "") .replace(/^(\d{2})(\d)/g, "($1) $2") .replace(/(\d)(\d{4})$/, "$1-$2") .slice(0, 15); }; const maskCpfCnpj = (value: string) => { const clean = value.replace(/\D/g, ""); if (clean.length <= 11) { return clean .replace(/(\d{3})(\d)/, "$1.$2") .replace(/(\d{3})(\d)/, "$1.$2") .replace(/(\d{3})(\d{1,2})/, "$1-$2") .replace(/(-\d{2})\d+?$/, "$1"); // Captures 11 digits } else { return clean .replace(/^(\d{2})(\d)/, "$1.$2") .replace(/^(\d{2})\.(\d{3})(\d)/, "$1.$2.$3") .replace(/\.(\d{3})(\d)/, ".$1/$2") .replace(/(\d{4})(\d)/, "$1-$2") .replace(/(-\d{2})\d+?$/, "$1") // Captures 14 digits .slice(0, 18); } }; const calculateMedia = (ratings: { qual_tec: number; educacao_simpatia: number; desempenho_evento: number; disp_horario: number; }) => { const weightedScore = ratings.qual_tec * 2 + ratings.educacao_simpatia + ratings.desempenho_evento + ratings.disp_horario; return weightedScore / 5; }; const handleCepBlur = async () => { const cep = formData.cep?.replace(/\D/g, "") || ""; if (cep.length !== 8) return; setIsLoadingCep(true); try { const response = await fetch( `https://cep.awesomeapi.com.br/json/${cep}` ); if (!response.ok) throw new Error("CEP não encontrado"); const data = await response.json(); setFormData((prev) => ({ ...prev, endereco: `${data.address || ""} ${data.district ? `- ${data.district}` : ""}`.trim() || prev.endereco, cidade: data.city || prev.cidade, uf: data.state || prev.uf, })); } catch (error) { console.error("Erro ao buscar CEP:", error); } finally { setIsLoadingCep(false); } }; // Handlers const handleAvatarChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { setAvatarFile(file); const reader = new FileReader(); reader.onloadend = () => { setAvatarPreview(reader.result as string); }; reader.readAsDataURL(file); } }; const removeAvatar = () => { setAvatarFile(null); setAvatarPreview(""); setFormData((prev) => ({ ...prev, avatar_url: "" })); }; const resetForm = () => { setFormData(initialFormState); setAvatarFile(null); setAvatarPreview(""); setSelectedProfessional(null); }; const handleEditClick = (professional: Professional) => { setFormData({ nome: professional.nome, funcao_profissional_id: professional.funcao_profissional_id, email: professional.email || "", whatsapp: professional.whatsapp || "", cpf_cnpj_titular: professional.cpf_cnpj_titular || "", endereco: professional.endereco || "", cidade: professional.cidade || "", uf: professional.uf || "", cep: professional.cep || "", banco: professional.banco || "", agencia: professional.agencia || "", conta_pix: professional.conta_pix || "", tipo_cartao: professional.tipo_cartao || "", carro_disponivel: professional.carro_disponivel || false, tem_estudio: professional.tem_estudio || false, qtd_estudio: professional.qtd_estudio || 0, observacao: professional.observacao || "", qual_tec: professional.qual_tec || 0, educacao_simpatia: professional.educacao_simpatia || 0, desempenho_evento: professional.desempenho_evento || 0, disp_horario: professional.disp_horario || 0, tabela_free: professional.tabela_free || "", extra_por_equipamento: professional.extra_por_equipamento || false, equipamentos: professional.equipamentos || "", avatar_url: professional.avatar_url || "", media: professional.media || 0, }); setAvatarPreview(professional.avatar_url || (professional.avatar ?? GenericAvatar)); setAvatarFile(null); setSelectedProfessional(professional); // Storing the professional being edited here setShowEditModal(true); }; const handleViewClick = (professional: Professional) => { setViewProfessional(professional); }; // Update Media when ratings change useEffect(() => { const newMedia = calculateMedia({ qual_tec: formData.qual_tec || 0, educacao_simpatia: formData.educacao_simpatia || 0, desempenho_evento: formData.desempenho_evento || 0, disp_horario: formData.disp_horario || 0, }); // Only update if it's different to avoid loops/excessive renders, though optional in this simple case setFormData((prev) => { if (prev.media === newMedia) return prev; return { ...prev, media: newMedia }; }); }, [ formData.qual_tec, formData.educacao_simpatia, formData.desempenho_evento, formData.disp_horario, ]); const handleSubmit = async (e: React.FormEvent, isEdit: boolean) => { e.preventDefault(); setIsSubmitting(true); try { let finalAvatarUrl = formData.avatar_url; // Handle Avatar Upload if new file selected if (avatarFile) { const uploadRes = await getUploadURL(avatarFile.name, avatarFile.type); if (uploadRes.data) { await uploadFileToSignedUrl(uploadRes.data.upload_url, avatarFile); finalAvatarUrl = uploadRes.data.public_url; } } const payload = { ...formData, avatar_url: finalAvatarUrl }; if (isEdit && selectedProfessional) { await updateProfessional(selectedProfessional.id, payload, token); alert("Profissional atualizado com sucesso!"); } else { await createProfessional(payload, token); alert("Profissional criado com sucesso!"); } setShowAddModal(false); setShowEditModal(false); fetchData(); // Reset form // Reset form resetForm(); } catch (error) { console.error("Error submitting form:", error); alert("Erro ao salvar profissional. Verifique o console."); } finally { setIsSubmitting(false); } }; const handleDelete = async () => { if (!professionalToDelete) return; try { await deleteProfessional(professionalToDelete.id, token); setShowDeleteModal(false); setProfessionalToDelete(null); fetchData(); } catch (error) { console.error("Error deleting professional:", error); alert("Erro ao excluir profissional."); } }; // Helper renderers const getRoleName = (id: string) => { return roles.find((r) => r.id === id)?.nome || "Desconhecido"; }; const getRoleIcon = (roleName: string) => { const lower = roleName.toLowerCase(); if (lower.includes("foto")) return Camera; if (lower.includes("video") || lower.includes("cine")) return Video; return UserCheck; }; // Filter Logic const filteredProfessionals = professionals.filter((p) => { const matchesSearch = p.nome.toLowerCase().includes(searchTerm.toLowerCase()) || (p.email && p.email.toLowerCase().includes(searchTerm.toLowerCase())); // Adjusted role logic since we have ID in database but names in roles array const roleName = getRoleName(p.funcao_profissional_id); const matchesRole = roleFilter === "all" || roleName === roleFilter; // Hide users with unknown roles if (roleName === "Desconhecido") return false; return matchesSearch && matchesRole; }); const stats = { total: professionals.length, photographers: professionals.filter(p => getRoleName(p.funcao_profissional_id).toLowerCase().includes("fot") || getRoleName(p.funcao_profissional_id).toLowerCase().includes("foto")).length, cine: professionals.filter(p => getRoleName(p.funcao_profissional_id).toLowerCase().includes("cine") || getRoleName(p.funcao_profissional_id).toLowerCase().includes("video")).length, recep: professionals.filter(p => getRoleName(p.funcao_profissional_id).toLowerCase().includes("recep")).length, }; return (
{/* Header */}

Equipe

Gerencie sua equipe de profissionais

{/* Stats */}
{roles.map(role => { const count = professionals.filter(p => p.funcao_profissional_id === role.id).length; const RoleIcon = getRoleIcon(role.nome); // Optional: Customize colors based on role or just cycle/default // For simplicity using existing logic or default let iconColorClass = "text-brand-black"; if (role.nome.toLowerCase().includes("foto")) iconColorClass = "text-brand-gold"; else if (role.nome.toLowerCase().includes("video") || role.nome.toLowerCase().includes("cine")) iconColorClass = "text-blue-600"; else if (role.nome.toLowerCase().includes("recep")) iconColorClass = "text-purple-600"; return (

Total de {role.nome}s

{count}

); })}
{/* Filters and Search */}
setSearchTerm(e.target.value)} className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold" />
{/* List */} {isLoading ? (
Carregando...
) : (
{filteredProfessionals.map((p) => { const roleName = getRoleName(p.funcao_profissional_id); const RoleIcon = getRoleIcon(roleName); return ( handleViewClick(p)}> ); })}
Profissional Função Contato Ações
{p.nome}
{p.nome}
{p.media ? p.media.toFixed(1) : "N/A"}
{roleName}
{p.whatsapp}
{p.email}
)}
{/* Add/Edit Modal */} {(showAddModal || showEditModal) && (

{showEditModal ? "Editar Profissional" : "Novo Profissional"}

handleSubmit(e, showEditModal)} className="space-y-6"> {/* Photo */}
{avatarPreview ? ( Preview ) : ( )}
{avatarPreview && ( )}
{/* Basic Info */}
setFormData({ ...formData, nome: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand-gold focus:ring focus:ring-brand-gold focus:ring-opacity-50 p-2 border" />
setFormData({ ...formData, email: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand-gold focus:ring focus:ring-brand-gold focus:ring-opacity-50 p-2 border" />
setFormData({ ...formData, whatsapp: maskPhone(e.target.value) })} maxLength={15} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand-gold focus:ring focus:ring-brand-gold focus:ring-opacity-50 p-2 border" />
setFormData({ ...formData, cpf_cnpj_titular: maskCpfCnpj(e.target.value) })} maxLength={18} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand-gold focus:ring focus:ring-brand-gold focus:ring-opacity-50 p-2 border" />

Endereço

{ const val = e.target.value.replace(/\D/g, "").replace(/^(\d{5})(\d)/, "$1-$2"); setFormData({ ...formData, cep: val }); }} onBlur={handleCepBlur} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand-gold focus:ring focus:ring-brand-gold focus:ring-opacity-50 p-2 border" placeholder="00000-000" /> {isLoadingCep && Buscando...}
setFormData({ ...formData, endereco: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand-gold focus:ring focus:ring-brand-gold focus:ring-opacity-50 p-2 border" />
setFormData({ ...formData, cidade: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand-gold focus:ring focus:ring-brand-gold focus:ring-opacity-50 p-2 border" />
{/* Banking */}

Dados Bancários

setFormData({ ...formData, banco: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand-gold focus:ring focus:ring-brand-gold focus:ring-opacity-50 p-2 border" />
setFormData({ ...formData, agencia: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand-gold focus:ring focus:ring-brand-gold focus:ring-opacity-50 p-2 border" />
setFormData({ ...formData, conta_pix: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand-gold focus:ring focus:ring-brand-gold focus:ring-opacity-50 p-2 border" />
setFormData({ ...formData, tipo_cartao: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand-gold focus:ring focus:ring-brand-gold focus:ring-opacity-50 p-2 border" placeholder="SD, XQD..." />
{/* Resources */}

Recursos

{formData.tem_estudio && (
setFormData({ ...formData, qtd_estudio: Math.max(0, parseInt(e.target.value)) })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm p-2 border" />
)}
{/* Ratings */}

Avaliações e Valores

setFormData({ ...formData, qual_tec: parseInt(e.target.value) || 0 })} className="block w-full rounded-md border-gray-300 shadow-sm p-2 border" />
setFormData({ ...formData, educacao_simpatia: parseInt(e.target.value) || 0 })} className="block w-full rounded-md border-gray-300 shadow-sm p-2 border" />
setFormData({ ...formData, desempenho_evento: parseInt(e.target.value) || 0 })} className="block w-full rounded-md border-gray-300 shadow-sm p-2 border" />
setFormData({ ...formData, disp_horario: parseInt(e.target.value) || 0 })} className="block w-full rounded-md border-gray-300 shadow-sm p-2 border" />
Média {formData.media ? (typeof formData.media === 'number' ? formData.media.toFixed(1) : parseFloat(formData.media).toFixed(1)) : "0.0"}
setFormData({ ...formData, tabela_free: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm p-2 border" />
)} {/* Delete Confirmation Modal */} {showDeleteModal && (

Confirmar Exclusão

Tem certeza que deseja excluir {professionalToDelete?.nome}? Esta ação não pode ser desfeita.

)} {/* View Modal */} {viewProfessional && (
{/* Header / Avatar Section */}
{viewProfessional.nome}

{viewProfessional.nome}

{(() => { const RoleIcon = getRoleIcon(getRoleName(viewProfessional.funcao_profissional_id)); return ; })()} {getRoleName(viewProfessional.funcao_profissional_id)} {viewProfessional.media !== undefined && viewProfessional.media !== null && ( {typeof viewProfessional.media === 'number' ? viewProfessional.media.toFixed(1) : parseFloat(viewProfessional.media).toFixed(1)} )}
{/* Personal Data */}

Dados Pessoais

{viewProfessional.email && (
{viewProfessional.email}
)} {viewProfessional.whatsapp && (
{viewProfessional.whatsapp}
)} {(viewProfessional.cidade || viewProfessional.uf) && (
{viewProfessional.cidade}{viewProfessional.cidade && viewProfessional.uf ? ", " : ""}{viewProfessional.uf}
)} {viewProfessional.endereco && (
{viewProfessional.endereco}
)}
{/* Equipment & Skills */}

Equipamentos

{viewProfessional.equipamentos || "Nenhum equipamento listado."}
{viewProfessional.carro_disponivel && ( Carro Próprio )} {viewProfessional.tem_estudio && ( Estúdio ({viewProfessional.qtd_estudio}) )} {viewProfessional.extra_por_equipamento && ( Extra p/ Equip. )}
{/* Performance / Observations */} {/* Financial Data */}

Dados Financeiros

CPF/CNPJ Titular {viewProfessional.cpf_cnpj_titular || "-"}
Chave Pix {viewProfessional.conta_pix || "-"}
Banco / Agência {viewProfessional.banco || "-"}{viewProfessional.agencia ? ` / ${viewProfessional.agencia}` : ""}
Tabela Free R$ {viewProfessional.tabela_free || "0,00"}
Tipo de Cartão {viewProfessional.tipo_cartao || "-"}
{/* Performance / Observations */}

Performance & Avaliação

Técnica
{viewProfessional.qual_tec || 0}
Simpatia
{viewProfessional.educacao_simpatia || 0}
Desempenho
{viewProfessional.desempenho_evento || 0}
Horário
{viewProfessional.disp_horario || 0}

Média Geral: {viewProfessional.media ? (typeof viewProfessional.media === 'number' ? viewProfessional.media.toFixed(1) : parseFloat(viewProfessional.media).toFixed(1)) : "N/A"}

{viewProfessional.observacao && (
"{viewProfessional.observacao}"
)}
{/* Footer */}
)}
); };