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:
parent
9ff55b36bd
commit
7010e8e7d9
4 changed files with 16 additions and 6 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
Loading…
Reference in a new issue