fix: correções de email, avatar e filtro de equipe no agendador

Backend:
- Corrigido mapeamento de email na listagem e busca por ID de profissionais. Agora o sistema utiliza o email do usuário vinculado (`usuario_email`) como fallback caso o email do perfil profissional esteja vazio.

Frontend:
- EventScheduler: Implementado filtro estrito para exibir apenas profissionais que foram explicitamente adicionados à equipe do evento ("Gerenciar Equipe"), prevenindo escalações indevidas.
- EventScheduler: Adicionada validação para ocultar cargos administrativos sem função operacional definida.
- ProfessionalDetailsModal: Corrigida a lógica de exibição do avatar para suportar a propriedade `avatar_url` (padrão atual do backend), resolvendo o problema de imagens quebradas ou ícones genéricos.
This commit is contained in:
NANDO9322 2026-01-12 12:20:08 -03:00
parent 9ff55b36bd
commit 7010e8e7d9
4 changed files with 16 additions and 6 deletions

View file

@ -89,6 +89,10 @@ func toResponse(p interface{}) ProfissionalResponse {
AvatarURL: fromPgText(v.AvatarUrl),
}
case generated.ListProfissionaisRow:
email := fromPgText(v.Email)
if email == nil {
email = fromPgText(v.UsuarioEmail)
}
return ProfissionalResponse{
ID: uuid.UUID(v.ID.Bytes).String(),
UsuarioID: uuid.UUID(v.UsuarioID.Bytes).String(),
@ -116,7 +120,7 @@ func toResponse(p interface{}) ProfissionalResponse {
TabelaFree: fromPgText(v.TabelaFree),
ExtraPorEquipamento: fromPgBool(v.ExtraPorEquipamento),
Equipamentos: fromPgText(v.Equipamentos),
Email: fromPgText(v.Email),
Email: email,
AvatarURL: fromPgText(v.AvatarUrl),
}
case generated.GetProfissionalByIDRow:
@ -147,6 +151,7 @@ func toResponse(p interface{}) ProfissionalResponse {
TabelaFree: fromPgText(v.TabelaFree),
ExtraPorEquipamento: fromPgBool(v.ExtraPorEquipamento),
Equipamentos: fromPgText(v.Equipamentos),
Email: fromPgText(v.Email),
AvatarURL: fromPgText(v.AvatarUrl),
}
default:

View file

@ -1,7 +1,7 @@
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 { listEscalas, createEscala, deleteEscala, EscalaInput, getFunctions } from "../services/apiService";
import { useData } from "../contexts/DataContext";
import { UserRole } from "../types";
@ -23,6 +23,7 @@ const EventScheduler: React.FC<EventSchedulerProps> = ({ agendaId, dataEvento, a
const { token, user } = useAuth();
const { professionals, events } = useData();
const [escalas, setEscalas] = useState<any[]>([]);
const [roles, setRoles] = useState<{ id: string; nome: string }[]>([]);
const [loading, setLoading] = useState(false);
// New entry state
@ -78,6 +79,9 @@ const EventScheduler: React.FC<EventSchedulerProps> = ({ agendaId, dataEvento, a
useEffect(() => {
if (agendaId && token) {
fetchEscalas();
getFunctions().then(res => {
if (res.data) setRoles(res.data);
});
}
}, [agendaId, token]);
@ -143,10 +147,11 @@ const EventScheduler: React.FC<EventSchedulerProps> = ({ agendaId, dataEvento, a
};
// 1. Start with all professionals or just the allowed ones
let availableProfs = professionals;
// FILTER: Only show professionals with a valid role (Function), matching "Equipe" page logic.
let availableProfs = professionals.filter(p => roles.some(r => r.id === p.funcao_profissional_id));
const allowedMap = new Map<string, string>(); // ID -> Status
if (allowedProfessionals && allowedProfessionals.length > 0) {
if (allowedProfessionals) {
// Normalize allowed list
const ids: string[] = [];
allowedProfessionals.forEach((p: any) => {

View file

@ -80,7 +80,7 @@ export const ProfessionalDetailsModal: React.FC<ProfessionalDetailsModalProps> =
<div
className="w-32 h-32 rounded-full border-4 border-white bg-white shadow-lg mb-4 bg-gray-200"
style={{
backgroundImage: `url(${professional.avatar || `https://ui-avatars.com/api/?name=${encodeURIComponent(professional.name || professional.nome)}&background=random`})`,
backgroundImage: `url(${professional.avatar_url || professional.avatar || `https://ui-avatars.com/api/?name=${encodeURIComponent(professional.name || professional.nome)}&background=random`})`,
backgroundSize: 'cover',
backgroundPosition: 'center'
}}

View file

@ -611,7 +611,7 @@ export const ProfessionalForm: React.FC<ProfessionalFormProps> = ({
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Nome do Titular da Conta ou Observação
Observações
</label>
<textarea
value={formData.observacao}