add-assignament-endpoints

This commit is contained in:
João Gustavo 2025-10-11 19:20:26 -03:00
parent 52c3e544f3
commit 8bf953a689
5 changed files with 115 additions and 14 deletions

View File

@ -317,6 +317,16 @@ export default function PacientesPage() {
</Dialog>
)}
{/* Assignment dialog */}
{assignDialogOpen && assignPatientId && (
<AssignmentForm
patientId={assignPatientId}
open={assignDialogOpen}
onClose={() => { setAssignDialogOpen(false); setAssignPatientId(null); }}
onSaved={() => { setAssignDialogOpen(false); setAssignPatientId(null); loadAll(); }}
/>
)}
<div className="text-sm text-muted-foreground">Mostrando {filtered.length} de {patients.length}</div>
</div>
);

View File

@ -737,23 +737,45 @@ const ProfissionalPage = () => {
);
}
// carregar laudos ao montar
// carregar laudos ao montar - somente dos pacientes atribuídos ao médico logado
useEffect(() => {
let mounted = true;
(async () => {
try {
await loadReports();
// obter assignments para o usuário logado
const assignments = await import('@/lib/assignment').then(m => m.listAssignmentsForUser(user?.id || ''));
const patientIds = Array.isArray(assignments) ? assignments.map(a => String(a.patient_id)).filter(Boolean) : [];
if (patientIds.length === 0) {
if (mounted) setLaudos([]);
return;
}
// carregar relatórios para cada paciente encontrado (useReports não tem batch by multiple ids, então carregamos por paciente)
const allReports: any[] = [];
for (const pid of patientIds) {
try {
const rels = await import('@/lib/reports').then(m => m.listarRelatoriosPorPaciente(pid));
if (Array.isArray(rels)) allReports.push(...rels);
} catch (err) {
console.warn('[LaudoManager] falha ao carregar relatórios para paciente', pid, err);
}
}
if (mounted) {
setLaudos(allReports);
}
} catch (e) {
// erro tratado no hook
console.warn('[LaudoManager] erro ao carregar laudos para pacientes atribuídos:', e);
if (mounted) setLaudos(reports || []);
}
if (mounted) setLaudos(reports || []);
})();
return () => { mounted = false; };
}, [loadReports]);
}, [user?.id]);
// sincroniza quando reports mudarem no hook
// sincroniza quando reports mudarem no hook (fallback)
useEffect(() => {
setLaudos(reports || []);
if (!laudos || laudos.length === 0) setLaudos(reports || []);
}, [reports]);
const [activeTab, setActiveTab] = useState("descobrir");

View File

@ -8,7 +8,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { Input } from "@/components/ui/input";
import { useToast } from "@/hooks/use-toast";
import { assignRoleToUser, listAssignmentsForPatient } from "@/lib/assignment";
import { assignRoleToUser, listAssignmentsForPatient, PatientAssignmentRole } from "@/lib/assignment";
import { listarProfissionais } from "@/lib/api";
type Props = {
@ -22,7 +22,8 @@ export default function AssignmentForm({ patientId, open, onClose, onSaved }: Pr
const { toast } = useToast();
const [professionals, setProfessionals] = useState<any[]>([]);
const [selectedProfessional, setSelectedProfessional] = useState<string | null>(null);
const [role, setRole] = useState<string>("doctor");
// default to Portuguese role values expected by the backend
const [role, setRole] = useState<PatientAssignmentRole>("medico");
const [loading, setLoading] = useState(false);
const [existing, setExisting] = useState<any[]>([]);
@ -48,11 +49,11 @@ export default function AssignmentForm({ patientId, open, onClose, onSaved }: Pr
}, [open, patientId]);
async function handleSave() {
if (!selectedProfessional) return toast({ title: 'Selecione um profissional', variant: 'warning' });
if (!selectedProfessional) return toast({ title: 'Selecione um profissional', variant: 'default' });
setLoading(true);
try {
await assignRoleToUser({ patient_id: patientId, user_id: selectedProfessional, role });
toast({ title: 'Atribuição criada', variant: 'success' });
toast({ title: 'Atribuição criada', variant: 'default' });
onSaved && onSaved();
onClose();
} catch (err: any) {
@ -87,8 +88,19 @@ export default function AssignmentForm({ patientId, open, onClose, onSaved }: Pr
<div>
<Label>Role</Label>
<Input value={role} onChange={(e) => setRole(e.target.value)} />
<div className="text-xs text-muted-foreground mt-1">Ex: doctor, nurse</div>
<Input
value={role}
onChange={(e) => {
const v = String(e.target.value || '').toLowerCase().trim();
// Map common english values to portuguese expected by backend
if (v === 'doctor') return setRole('medico');
if (v === 'nurse') return setRole('enfermeiro');
if (v === 'medico' || v === 'enfermeiro') return setRole(v as PatientAssignmentRole);
// fallback: keep current role (ignore unknown input)
return setRole(role);
}}
/>
<div className="text-xs text-muted-foreground mt-1">Ex: medico, enfermeiro (inglês: doctor medico)</div>
</div>
{existing && existing.length > 0 && (

View File

@ -838,6 +838,12 @@ export async function buscarMedicosPorIds(ids: Array<string | number>): Promise<
return unique;
}
// Alias/backwards-compat: listarProfissionais usado por components
export async function listarProfissionais(params?: { page?: number; limit?: number; q?: string; }): Promise<Medico[]> {
// Reuse listarMedicos implementation to avoid duplication
return await listarMedicos(params);
}
// Dentro de lib/api.ts
export async function criarMedico(input: MedicoInput): Promise<Medico> {
console.log("Enviando os dados para a API:", input); // Log para depuração

View File

@ -78,7 +78,9 @@ export async function assignRoleToUser(input: CreateAssignmentInput): Promise<Pa
statusText: response.statusText,
body: errorBody,
});
throw new Error(`Erro ao atribuir função: ${response.statusText} (${response.status})`);
// Include body (when available) to help debugging (e.g., constraint violations)
const bodySnippet = errorBody ? ` - body: ${errorBody}` : '';
throw new Error(`Erro ao atribuir função: ${response.statusText} (${response.status})${bodySnippet}`);
}
const createdAssignment = await response.json();
@ -131,3 +133,52 @@ export async function listAssignmentsForPatient(patientId: string): Promise<Pati
throw error;
}
}
/**
* Lista todas as atribuições para um dado usuário (médico/enfermeiro).
* Útil para obter os patient_id dos pacientes atribuídos ao usuário.
*/
export async function listAssignmentsForUser(userId: string): Promise<PatientAssignment[]> {
console.log(`🔍 [ASSIGNMENT] Listando atribuições para o usuário: ${userId}`);
const url = `${ASSIGNMENTS_URL}?user_id=eq.${userId}`;
try {
const headers = getHeaders();
console.debug('[ASSIGNMENT] GET', url, 'headers(masked)=', {
...headers,
Authorization: headers.Authorization ? '<<masked>>' : undefined,
});
const response = await fetch(url, {
method: 'GET',
headers,
});
// dump raw text for debugging when content-type isn't JSON or when empty
const contentType = response.headers.get('content-type') || '';
const txt = await response.clone().text().catch(() => '');
console.debug('[ASSIGNMENT] response status=', response.status, response.statusText, 'content-type=', contentType, 'bodyPreview=', txt ? (txt.length > 1000 ? txt.slice(0,1000) + '...[truncated]' : txt) : '<empty>');
if (!response.ok) {
const errorBody = txt || '';
console.error("❌ [ASSIGNMENT] Erro ao listar atribuições por usuário:", {
status: response.status,
statusText: response.statusText,
body: errorBody,
});
throw new Error(`Erro ao listar atribuições por usuário: ${response.status} ${response.statusText} - body: ${errorBody}`);
}
let assignments: any = [];
try {
assignments = await response.json();
} catch (e) {
console.warn('[ASSIGNMENT] não foi possível parsear JSON, usando texto cru como fallback');
assignments = txt ? JSON.parse(txt) : [];
}
console.log(`✅ [ASSIGNMENT] ${Array.isArray(assignments) ? assignments.length : 0} atribuições encontradas para o usuário.`);
return Array.isArray(assignments) ? assignments : [];
} catch (error) {
console.error("❌ [ASSIGNMENT] Erro inesperado ao listar atribuições por usuário:", error);
throw error;
}
}