fix(main-routes): security, layout, and form formatting
- Removed sensitive logs from the console - Added sidebar to the Schedule, Procedure, and Financial pages - Standardized spacing between labels and inputs in all forms - Added automatic formatting for ID, date of birth, and phone number in patient registration - Removed duplicate "Cell Phone" field in doctor registration - Adjusted page layout to follow standard
This commit is contained in:
parent
b478a1f8d3
commit
26d4077784
@ -99,16 +99,16 @@ export default function NovoAgendamentoPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-background">
|
||||
<div className="flex flex-col h-full bg-background">
|
||||
<HeaderAgenda />
|
||||
<main className="flex-1 mx-auto w-full max-w-7xl px-8 py-8">
|
||||
<CalendarRegistrationForm
|
||||
formData={formData as any}
|
||||
onFormChange={handleFormChange as any}
|
||||
createMode
|
||||
/>
|
||||
<main className="flex-1 mx-auto w-full max-w-7xl px-8 py-8 overflow-auto">
|
||||
<CalendarRegistrationForm
|
||||
formData={formData as any}
|
||||
onFormChange={handleFormChange as any}
|
||||
createMode
|
||||
/>
|
||||
</main>
|
||||
<FooterAgenda onSave={handleSave} onCancel={handleCancel} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,27 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Search, ChevronDown, Calculator, DollarSign } from "lucide-react";
|
||||
import { Plus } from "lucide-react";
|
||||
import { Calculator, DollarSign } from "lucide-react";
|
||||
import HeaderAgenda from "@/components/agenda/HeaderAgenda";
|
||||
import FooterAgenda from "@/components/agenda/FooterAgenda";
|
||||
|
||||
export default function FinanceiroPage() {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const [bloqueio, setBloqueio] = useState(false);
|
||||
const [formaTipo, setFormaTipo] = useState("");
|
||||
const [parcelas, setParcelas] = useState("1");
|
||||
|
||||
const isAg = pathname?.startsWith("/agendamento");
|
||||
const isPr = pathname?.startsWith("/procedimento");
|
||||
const isFi = pathname?.startsWith("/financeiro");
|
||||
|
||||
const handleSave = () => {
|
||||
// Lógica de salvar será implementada
|
||||
@ -33,12 +22,11 @@ export default function FinanceiroPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full min-h-screen flex flex-col bg-background">
|
||||
{/* HEADER */}
|
||||
<div className="flex flex-col h-full bg-background">
|
||||
<HeaderAgenda />
|
||||
|
||||
{/* CORPO */}
|
||||
<main className="mx-auto w-full max-w-7xl px-8 py-6 space-y-6 flex-grow">
|
||||
<main className="mx-auto w-full max-w-7xl px-8 py-6 space-y-6 flex-1 overflow-auto">
|
||||
{/* INFORMAÇÕES FINANCEIRAS */}
|
||||
<section className="space-y-6">
|
||||
{/* Selo Financeiro */}
|
||||
@ -58,7 +46,7 @@ export default function FinanceiroPage() {
|
||||
Valor do Atendimento
|
||||
</Label>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-muted-foreground">Valor Particular</Label>
|
||||
<div className="relative">
|
||||
<DollarSign className="pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
@ -68,7 +56,7 @@ export default function FinanceiroPage() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-muted-foreground">Valor Convênio</Label>
|
||||
<div className="relative">
|
||||
<DollarSign className="pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
@ -90,7 +78,7 @@ export default function FinanceiroPage() {
|
||||
Forma de Pagamento
|
||||
</Label>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-muted-foreground">Tipo</Label>
|
||||
<select value={formaTipo} onChange={(e) => setFormaTipo(e.target.value)} className="h-10 w-full rounded-md border border-gray-300 dark:border-input bg-background text-foreground pr-8 pl-3 text-[13px] appearance-none transition-colors hover:bg-muted/30 hover:border-gray-400">
|
||||
<option value="">Selecionar</option>
|
||||
@ -100,7 +88,7 @@ export default function FinanceiroPage() {
|
||||
<option value="convenio">Convênio</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-muted-foreground">Parcelas</Label>
|
||||
<select className="h-10 w-full rounded-md border border-gray-300 dark:border-input bg-background text-foreground pr-8 pl-3 text-[13px] appearance-none transition-colors hover:bg-muted/30 hover:border-gray-400">
|
||||
<option value="1">1x</option>
|
||||
@ -111,7 +99,7 @@ export default function FinanceiroPage() {
|
||||
<option value="6">6x</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-muted-foreground">Desconto</Label>
|
||||
<div className="relative">
|
||||
<Calculator className="pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
@ -156,4 +144,4 @@ export default function FinanceiroPage() {
|
||||
<FooterAgenda onSave={handleSave} onCancel={handleCancel} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,25 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Search, ChevronDown, RotateCcw } from "lucide-react";
|
||||
import { Search, ChevronDown } from "lucide-react";
|
||||
import { Plus } from "lucide-react";
|
||||
import HeaderAgenda from "@/components/agenda/HeaderAgenda";
|
||||
import FooterAgenda from "@/components/agenda/FooterAgenda";
|
||||
|
||||
export default function ProcedimentoPage() {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const [bloqueio, setBloqueio] = useState(false);
|
||||
|
||||
const isAg = pathname?.startsWith("/agendamento");
|
||||
const isPr = pathname?.startsWith("/procedimento");
|
||||
const isFi = pathname?.startsWith("/financeiro");
|
||||
|
||||
const handleSave = () => {
|
||||
// Lógica de salvar será implementada
|
||||
@ -30,20 +22,12 @@ export default function ProcedimentoPage() {
|
||||
router.push("/calendar");
|
||||
};
|
||||
|
||||
const tab = (active: boolean, extra = "") =>
|
||||
`px-4 py-1.5 text-[13px] border ${
|
||||
active
|
||||
? "border-sky-500 bg-sky-50 dark:bg-sky-900/30 text-sky-700 dark:text-sky-300 font-medium"
|
||||
: "text-muted-foreground hover:bg-muted border-border"
|
||||
} ${extra}`;
|
||||
|
||||
return (
|
||||
<div className="w-full min-h-screen flex flex-col bg-background">
|
||||
{/* HEADER */}
|
||||
<div className="flex flex-col h-full bg-background">
|
||||
<HeaderAgenda />
|
||||
|
||||
{/* CORPO */}
|
||||
<main className="mx-auto w-full max-w-7xl px-8 py-6 space-y-6 flex-grow">
|
||||
<main className="mx-auto w-full max-w-7xl px-8 py-6 space-y-6 flex-1 overflow-auto">
|
||||
{/* ATENDIMENTOS */}
|
||||
<section className="space-y-6">
|
||||
{/* Selo Atendimento com + dentro da bolinha */}
|
||||
@ -1080,8 +1080,8 @@ export function CalendarRegistrationForm({ formData, onFormChange, createMode =
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-3 mt-3">
|
||||
<div>
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[13px]">Status</Label>
|
||||
<select name="status" className="h-11 w-full rounded-md border border-gray-300 dark:border-input bg-background text-foreground pr-3 text-[13px]" value={formData.status || ''} onChange={handleChange}>
|
||||
<option value="">Selecione</option>
|
||||
@ -1094,50 +1094,50 @@ export function CalendarRegistrationForm({ formData, onFormChange, createMode =
|
||||
<option value="no_show">Não compareceu</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[13px]">Duração (min)</Label>
|
||||
<Input name="duration_minutes" type="number" min={1} className="h-11 w-full rounded-md" value={formData.duration_minutes ?? ''} onChange={handleChange} readOnly={lockedDurationFromSlot} disabled={lockedDurationFromSlot} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[13px]">Convênio</Label>
|
||||
<Input name="insurance_provider" placeholder="Operadora" className="h-11 w-full rounded-md" value={formData.insurance_provider || ''} onChange={handleChange} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2 mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-[13px]">Observações</Label>
|
||||
|
||||
</div>
|
||||
<Textarea name="notes" rows={4} className="text-[13px] min-h-[80px] resize-none rounded-md transition-colors hover:bg-muted/30" value={formData.notes || ''} onChange={handleChange} />
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-3">
|
||||
<div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[13px]">Queixa principal</Label>
|
||||
<Textarea name="chief_complaint" rows={3} className="text-[13px] rounded-md" value={formData.chief_complaint || ''} onChange={handleChange} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[13px]">Notas do paciente</Label>
|
||||
<Textarea name="patient_notes" rows={3} className="text-[13px] rounded-md" value={formData.patient_notes || ''} onChange={handleChange} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-3 mt-3">
|
||||
<div>
|
||||
<div className="grid grid-cols-3 gap-3 mt-4">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[13px]">Horário de check-in</Label>
|
||||
<Input name="checked_in_at" type="datetime-local" className="h-11 w-full rounded-md" value={isoToDatetimeLocal(formData.checked_in_at as any)} onChange={handleChange} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[13px]">Concluído em</Label>
|
||||
<Input name="completed_at" type="datetime-local" className="h-11 w-full rounded-md" value={isoToDatetimeLocal(formData.completed_at as any)} onChange={handleChange} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[13px]">Cancelado em</Label>
|
||||
<Input name="cancelled_at" type="datetime-local" className="h-11 w-full rounded-md" value={isoToDatetimeLocal(formData.cancelled_at as any)} onChange={handleChange} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<div className="mt-4 space-y-1">
|
||||
<Label className="text-[13px]">Motivo do cancelamento</Label>
|
||||
<Input name="cancellation_reason" className="h-11 w-full rounded-md" value={formData.cancellation_reason || ''} onChange={handleChange} />
|
||||
</div>
|
||||
|
||||
@ -935,14 +935,6 @@ async function handleSubmit(ev: React.FormEvent) {
|
||||
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Celular</Label>
|
||||
<Input
|
||||
value={form.celular}
|
||||
onChange={(e) => setField("celular", formatPhone(e.target.value))}
|
||||
placeholder="(XX) XXXXX-XXXX"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Contato de Emergência</Label>
|
||||
<Input
|
||||
|
||||
@ -98,6 +98,30 @@ export function PatientRegistrationForm({
|
||||
const [form, setForm] = useState<FormData>(initial);
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [expanded, setExpanded] = useState({ dados: true, contato: false, endereco: false, obs: false });
|
||||
|
||||
// Funções de formatação
|
||||
const formatRG = (value: string) => {
|
||||
const cleaned = value.replace(/\D/g, '');
|
||||
if (cleaned.length <= 9) {
|
||||
return cleaned.replace(/(\d{2})(\d{3})(\d{3})(\d{1})/, '$1.$2.$3-$4');
|
||||
}
|
||||
return cleaned.slice(0, 9);
|
||||
};
|
||||
|
||||
const formatTelefone = (value: string) => {
|
||||
const cleaned = value.replace(/\D/g, '');
|
||||
if (cleaned.length <= 10) {
|
||||
return cleaned.replace(/(\d{2})(\d{4})(\d{4})/, '($1) $2-$3');
|
||||
}
|
||||
return cleaned.replace(/(\d{2})(\d{5})(\d{4})/, '($1) $2-$3');
|
||||
};
|
||||
|
||||
const formatDataNascimento = (value: string) => {
|
||||
const cleaned = value.replace(/\D/g, '');
|
||||
if (cleaned.length <= 2) return cleaned;
|
||||
if (cleaned.length <= 4) return `${cleaned.slice(0, 2)}/${cleaned.slice(2)}`;
|
||||
return `${cleaned.slice(0, 2)}/${cleaned.slice(2, 4)}/${cleaned.slice(4, 8)}`;
|
||||
};
|
||||
const [isSubmitting, setSubmitting] = useState(false);
|
||||
const [isUploadingPhoto, setUploadingPhoto] = useState(false);
|
||||
const [isSearchingCEP, setSearchingCEP] = useState(false);
|
||||
@ -362,7 +386,7 @@ export function PatientRegistrationForm({
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2"><Label>CPF *</Label><Input value={form.cpf} onChange={(e) => handleCPFChange(e.target.value)} placeholder="000.000.000-00" maxLength={14} className={errors.cpf ? "border-destructive" : ""} />{errors.cpf && <p className="text-sm text-destructive">{errors.cpf}</p>}</div>
|
||||
<div className="space-y-2"><Label>RG</Label><Input value={form.rg} onChange={(e) => setField("rg", e.target.value)} /></div>
|
||||
<div className="space-y-2"><Label>RG</Label><Input value={form.rg} onChange={(e) => setField("rg", formatRG(e.target.value))} placeholder="00.000.000-0" maxLength={12} /></div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
@ -372,7 +396,7 @@ export function PatientRegistrationForm({
|
||||
<SelectContent><SelectItem value="masculino">Masculino</SelectItem><SelectItem value="feminino">Feminino</SelectItem><SelectItem value="outro">Outro</SelectItem></SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2"><Label>Data de Nascimento</Label><Input placeholder="dd/mm/aaaa" value={form.birth_date} onChange={(e) => { const v = e.target.value.replace(/[^0-9\/]*/g, "").slice(0, 10); setField("birth_date", v); }} onBlur={() => { const raw = form.birth_date; const parts = raw.split(/\D+/).filter(Boolean); if (parts.length === 3) { const d = `${parts[0].padStart(2,'0')}/${parts[1].padStart(2,'0')}/${parts[2].padStart(4,'0')}`; setField("birth_date", d); } }} /></div>
|
||||
<div className="space-y-2"><Label>Data de Nascimento</Label><Input placeholder="dd/mm/aaaa" value={form.birth_date} onChange={(e) => setField("birth_date", formatDataNascimento(e.target.value))} maxLength={10} /></div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</CollapsibleContent>
|
||||
@ -388,7 +412,7 @@ export function PatientRegistrationForm({
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2"><Label>E-mail</Label><Input value={form.email} onChange={(e) => setField("email", e.target.value)} />{errors.email && <p className="text-sm text-destructive">{errors.email}</p>}</div>
|
||||
<div className="space-y-2"><Label>Telefone</Label><Input value={form.telefone} onChange={(e) => setField("telefone", e.target.value)} />{errors.telefone && <p className="text-sm text-destructive">{errors.telefone}</p>}</div>
|
||||
<div className="space-y-2"><Label>Telefone</Label><Input value={form.telefone} onChange={(e) => setField("telefone", formatTelefone(e.target.value))} placeholder="(00) 00000-0000" maxLength={15} />{errors.telefone && <p className="text-sm text-destructive">{errors.telefone}</p>}</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</CollapsibleContent>
|
||||
|
||||
@ -652,28 +652,26 @@ function withPrefer(h: Record<string, string>, prefer: string) {
|
||||
// Helper: fetch seguro que tenta urls alternativas caso a requisição primária falhe
|
||||
async function fetchWithFallback<T = any>(url: string, headers: Record<string, string>, altUrls?: string[]): Promise<T | null> {
|
||||
try {
|
||||
console.debug('[fetchWithFallback] tentando URL:', url);
|
||||
// Log removido por segurança
|
||||
const res = await fetch(url, { method: 'GET', headers });
|
||||
if (res.ok) {
|
||||
return await parse<T>(res);
|
||||
}
|
||||
const raw = await res.clone().text().catch(() => '');
|
||||
console.warn('[fetchWithFallback] falha na URL primária:', url, 'status:', res.status, 'raw:', raw);
|
||||
// Log removido por segurança
|
||||
if (!altUrls || !altUrls.length) return null;
|
||||
for (const alt of altUrls) {
|
||||
try {
|
||||
console.debug('[fetchWithFallback] tentando fallback URL:', alt);
|
||||
// Log removido por segurança
|
||||
const r2 = await fetch(alt, { method: 'GET', headers });
|
||||
if (r2.ok) return await parse<T>(r2);
|
||||
const raw2 = await r2.clone().text().catch(() => '');
|
||||
console.warn('[fetchWithFallback] fallback falhou:', alt, 'status:', r2.status, 'raw:', raw2);
|
||||
// Log removido por segurança
|
||||
} catch (e) {
|
||||
console.warn('[fetchWithFallback] erro no fallback:', alt, e);
|
||||
// Log removido por segurança
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
console.warn('[fetchWithFallback] erro fetch primario:', url, e);
|
||||
// Log removido por segurança
|
||||
if (!altUrls || !altUrls.length) return null;
|
||||
for (const alt of altUrls) {
|
||||
try {
|
||||
@ -724,22 +722,17 @@ async function parse<T>(res: Response): Promise<T> {
|
||||
|
||||
// Special-case authentication/authorization errors to reduce noisy logs
|
||||
if (res.status === 401) {
|
||||
// If the server returned an empty body, avoid dumping raw text to console.error
|
||||
if (!rawText && !json) {
|
||||
console.warn('[API AUTH] 401 Unauthorized for', res.url, '- no auth token or token expired.');
|
||||
} else {
|
||||
console.warn('[API AUTH] 401 Unauthorized for', res.url, 'response:', json ?? rawText);
|
||||
}
|
||||
// Log removido por segurança - não expor URL da Supabase
|
||||
throw new Error('Você não está autenticado. Faça login novamente.');
|
||||
}
|
||||
|
||||
if (res.status === 403) {
|
||||
console.warn('[API AUTH] 403 Forbidden for', res.url, (json ?? rawText) ? 'response: ' + (json ?? rawText) : '');
|
||||
// Log removido por segurança - não expor URL da Supabase
|
||||
throw new Error('Você não tem permissão para executar esta ação.');
|
||||
}
|
||||
|
||||
// For other errors, log a concise error and try to produce a friendly message
|
||||
console.error('[API ERROR]', res.url, res.status, json ? json : 'no-json', rawText ? 'raw body present' : 'no raw body');
|
||||
console.error('[API ERROR] Status:', res.status, json ? 'JSON response' : 'no-json', rawText ? 'raw body present' : 'no raw body');
|
||||
|
||||
// Mensagens amigáveis para erros comuns
|
||||
let friendlyMessage = msg;
|
||||
@ -877,9 +870,7 @@ export async function buscarPacientes(termo: string): Promise<Paciente[]> {
|
||||
params.set('limit', '10');
|
||||
const url = `${REST}/patients?${params.toString()}`;
|
||||
const headers = baseHeaders();
|
||||
const masked = (headers['Authorization'] as string | undefined) ? `${String(headers['Authorization']).slice(0,6)}...${String(headers['Authorization']).slice(-6)}` : null;
|
||||
console.debug('[buscarPacientes] URL:', url);
|
||||
console.debug('[buscarPacientes] Headers (masked):', { ...headers, Authorization: masked ? '<<masked>>' : undefined });
|
||||
// Logs removidos por segurança
|
||||
const res = await fetch(url, { method: "GET", headers });
|
||||
const arr = await parse<Paciente[]>(res);
|
||||
|
||||
@ -908,7 +899,7 @@ export async function buscarPacientePorUserId(userId?: string | null): Promise<P
|
||||
try {
|
||||
const url = `${REST}/patients?user_id=eq.${encodeURIComponent(String(userId))}&limit=1`;
|
||||
const headers = baseHeaders();
|
||||
console.debug('[buscarPacientePorUserId] URL:', url);
|
||||
// Log removido por segurança
|
||||
const arr = await fetchWithFallback<Paciente[]>(url, headers).catch(() => []);
|
||||
if (arr && arr.length) return arr[0];
|
||||
return null;
|
||||
@ -925,7 +916,7 @@ export async function buscarPacientePorId(id: string | number): Promise<Paciente
|
||||
// Tenta buscar por id (UUID ou string) primeiro
|
||||
try {
|
||||
const url = `${ENV_CONFIG.SUPABASE_URL}/rest/v1/patients?id=eq.${encodeURIComponent(idParam)}`;
|
||||
console.debug('[buscarPacientePorId] tentando por id URL:', url);
|
||||
// Log removido por segurança
|
||||
const arr = await fetchWithFallback<Paciente[]>(url, headers);
|
||||
if (arr && arr.length) return arr[0];
|
||||
} catch (e) {
|
||||
@ -943,7 +934,7 @@ export async function buscarPacientePorId(id: string | number): Promise<Paciente
|
||||
altParams.set('social_name', `ilike.*${String(id)}*`);
|
||||
altParams.set('limit', '5');
|
||||
const alt = `${REST}/patients?${altParams.toString()}`;
|
||||
console.debug('[buscarPacientePorId] tentando por nome URL:', url);
|
||||
// Log removido por segurança
|
||||
const arr2 = await fetchWithFallback<Paciente[]>(url, headers, [alt]);
|
||||
if (arr2 && arr2.length) return arr2[0];
|
||||
}
|
||||
@ -1348,31 +1339,31 @@ export async function buscarRelatorioPorId(id: string | number): Promise<Report>
|
||||
// 1) tenta por id (UUID ou campo id)
|
||||
try {
|
||||
const urlById = `${REST}/reports?id=eq.${encodeURIComponent(sId)}`;
|
||||
console.debug('[buscarRelatorioPorId] tentando por id URL:', urlById);
|
||||
// Log removido por segurança
|
||||
const arr = await fetchWithFallback<Report[]>(urlById, headers);
|
||||
if (arr && arr.length) return arr[0];
|
||||
} catch (e) {
|
||||
console.warn('[buscarRelatorioPorId] falha ao buscar por id:', e);
|
||||
// Falha silenciosa - tenta próxima estratégia
|
||||
}
|
||||
|
||||
// 2) tenta por order_number (caso o usuário cole um código legível)
|
||||
try {
|
||||
const urlByOrder = `${REST}/reports?order_number=eq.${encodeURIComponent(sId)}`;
|
||||
console.debug('[buscarRelatorioPorId] tentando por order_number URL:', urlByOrder);
|
||||
// Log removido por segurança
|
||||
const arr2 = await fetchWithFallback<Report[]>(urlByOrder, headers);
|
||||
if (arr2 && arr2.length) return arr2[0];
|
||||
} catch (e) {
|
||||
console.warn('[buscarRelatorioPorId] falha ao buscar por order_number:', e);
|
||||
// Falha silenciosa - tenta próxima estratégia
|
||||
}
|
||||
|
||||
// 3) tenta por patient_id (caso o usuário passe um patient_id em vez do report id)
|
||||
try {
|
||||
const urlByPatient = `${REST}/reports?patient_id=eq.${encodeURIComponent(sId)}`;
|
||||
console.debug('[buscarRelatorioPorId] tentando por patient_id URL:', urlByPatient);
|
||||
// Log removido por segurança
|
||||
const arr3 = await fetchWithFallback<Report[]>(urlByPatient, headers);
|
||||
if (arr3 && arr3.length) return arr3[0];
|
||||
} catch (e) {
|
||||
console.warn('[buscarRelatorioPorId] falha ao buscar por patient_id:', e);
|
||||
// Falha silenciosa - não encontrado
|
||||
}
|
||||
|
||||
// Não encontrado
|
||||
@ -1424,7 +1415,7 @@ export async function buscarPacientesPorIds(ids: Array<string | number>): Promis
|
||||
altParams.set('limit', '100');
|
||||
const alt = `${REST}/patients?${altParams.toString()}`;
|
||||
const headers = baseHeaders();
|
||||
console.debug('[buscarPacientesPorIds] URL (patient by name):', url);
|
||||
// Log removido por segurança
|
||||
const arr = await fetchWithFallback<Paciente[]>(url, headers, [alt]);
|
||||
if (arr && arr.length) results.push(...arr);
|
||||
} catch (e) {
|
||||
@ -1567,7 +1558,7 @@ export async function criarPaciente(input: PacienteInput): Promise<Paciente> {
|
||||
const a = maskedHeaders.Authorization as string;
|
||||
maskedHeaders.Authorization = `${a.slice(0,6)}...${a.slice(-6)}`;
|
||||
}
|
||||
console.debug('[criarPaciente] POST', u, 'headers(masked):', maskedHeaders, 'payloadKeys:', Object.keys(payload));
|
||||
// Log removido por segurança
|
||||
const res = await fetch(u, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
@ -1766,8 +1757,7 @@ export async function buscarMedicos(termo: string): Promise<Medico[]> {
|
||||
queries.push(`specialty=ilike.*${q}*`);
|
||||
}
|
||||
|
||||
// debug: mostrar queries construídas
|
||||
console.debug('[buscarMedicos] queries construídas:', queries);
|
||||
// Debug removido por segurança
|
||||
|
||||
const results: Medico[] = [];
|
||||
const seenIds = new Set<string>();
|
||||
@ -1783,10 +1773,7 @@ export async function buscarMedicos(termo: string): Promise<Medico[]> {
|
||||
params.set('limit', '10');
|
||||
const url = `${REST}/doctors?${params.toString()}`;
|
||||
const headers = baseHeaders();
|
||||
const masked = (headers['Authorization'] as string | undefined) ? `${String(headers['Authorization']).slice(0,6)}...${String(headers['Authorization']).slice(-6)}` : null;
|
||||
console.debug('[buscarMedicos] URL params:', params.toString());
|
||||
console.debug('[buscarMedicos] URL:', url);
|
||||
console.debug('[buscarMedicos] Headers (masked):', { ...headers, Authorization: masked ? '<<masked>>' : undefined });
|
||||
// Logs removidos por segurança
|
||||
const res = await fetch(url, { method: 'GET', headers });
|
||||
const arr = await parse<Medico[]>(res);
|
||||
|
||||
@ -1819,7 +1806,7 @@ export async function buscarMedicoPorId(id: string | number): Promise<Medico | n
|
||||
// 1) Se parece UUID, busca por id direto
|
||||
if (isString && uuidRegex.test(sId)) {
|
||||
const url = `${REST}/doctors?id=eq.${encodeURIComponent(sId)}`;
|
||||
console.debug('[buscarMedicoPorId] tentando por id URL:', url);
|
||||
// Log removido por segurança
|
||||
const arr = await fetchWithFallback<Medico[]>(url, baseHeaders());
|
||||
if (arr && arr.length > 0) return arr[0];
|
||||
}
|
||||
@ -1868,18 +1855,16 @@ export async function buscarMedicoPorId(id: string | number): Promise<Medico | n
|
||||
// Se não encontrar no Supabase, tenta o mock API
|
||||
try {
|
||||
const mockUrl = `https://yuanqog.com/m1/1053378-0-default/rest/v1/doctors/${encodeURIComponent(String(id))}`;
|
||||
console.debug('[buscarMedicoPorId] tentando mock API URL:', mockUrl);
|
||||
// Log removido por segurança
|
||||
try {
|
||||
const medico = await fetchWithFallback<any>(mockUrl, { Accept: 'application/json' });
|
||||
if (medico) {
|
||||
console.log('✅ Médico encontrado no Mock API:', medico);
|
||||
return medico as Medico;
|
||||
}
|
||||
// fetchWithFallback returned null -> not found
|
||||
console.warn('[buscarMedicoPorId] mock API returned no result for id:', id);
|
||||
return null;
|
||||
} catch (fetchErr) {
|
||||
console.warn('[buscarMedicoPorId] mock API fetch failed or returned no result:', fetchErr);
|
||||
// Falha silenciosa
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
@ -1928,7 +1913,7 @@ export async function buscarMedicosPorIds(ids: Array<string | number>): Promise<
|
||||
altParams.set('limit', '200');
|
||||
const alt = `${REST}/doctors?${altParams.toString()}`;
|
||||
const headers = baseHeaders();
|
||||
console.debug('[buscarMedicosPorIds] URL (doctor by name):', url);
|
||||
// Log removido por segurança
|
||||
const socialAltParams = new URLSearchParams();
|
||||
socialAltParams.set('social_name', `ilike.*${name}*`);
|
||||
socialAltParams.set('limit', '200');
|
||||
@ -2047,7 +2032,7 @@ export async function criarMedico(input: MedicoInput): Promise<Medico> {
|
||||
const a = maskedHeaders.Authorization as string;
|
||||
maskedHeaders.Authorization = `${a.slice(0,6)}...${a.slice(-6)}`;
|
||||
}
|
||||
console.debug('[criarMedico] POST', u, 'headers(masked):', maskedHeaders, 'payloadKeys:', Object.keys(payload));
|
||||
// Log removido por segurança
|
||||
|
||||
const res = await fetch(u, {
|
||||
method: 'POST',
|
||||
@ -2106,12 +2091,7 @@ export async function criarMedico(input: MedicoInput): Promise<Medico> {
|
||||
|
||||
const url = `${API_BASE}/functions/v1/create-doctor`;
|
||||
const headers = { ...baseHeaders(), 'Content-Type': 'application/json' } as Record<string, string>;
|
||||
const maskedHeaders = { ...headers } as Record<string, string>;
|
||||
if (maskedHeaders.Authorization) {
|
||||
const a = maskedHeaders.Authorization as string;
|
||||
maskedHeaders.Authorization = `${a.slice(0,6)}...${a.slice(-6)}`;
|
||||
}
|
||||
console.debug('[criarMedico fallback] POST', url, 'headers(masked):', maskedHeaders, 'body:', JSON.stringify(fallbackPayload));
|
||||
// Logs removidos por segurança
|
||||
|
||||
const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify(fallbackPayload) });
|
||||
const parsed = await parse<any>(res as Response);
|
||||
@ -2198,19 +2178,14 @@ export async function vincularUserIdPaciente(pacienteId: string | number, userId
|
||||
const uuidRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
||||
const looksLikeUuid = uuidRegex.test(idStr);
|
||||
// Allow non-UUID ids (legacy) but log a debug warning when it's not UUID
|
||||
if (!looksLikeUuid) console.warn('[vincularUserIdPaciente] pacienteId does not look like a UUID:', idStr);
|
||||
// Log removido por segurança
|
||||
|
||||
const url = `${REST}/patients?id=eq.${encodeURIComponent(idStr)}`;
|
||||
const payload = { user_id: String(userId) };
|
||||
|
||||
// Debug-friendly masked headers
|
||||
const headers = withPrefer({ ...baseHeaders(), 'Content-Type': 'application/json' }, 'return=representation');
|
||||
const maskedHeaders = { ...headers } as Record<string, string>;
|
||||
if (maskedHeaders.Authorization) {
|
||||
const a = maskedHeaders.Authorization as string;
|
||||
maskedHeaders.Authorization = a.slice(0,6) + '...' + a.slice(-6);
|
||||
}
|
||||
console.debug('[vincularUserIdPaciente] PATCH', url, 'payload:', { ...payload }, 'headers(masked):', maskedHeaders);
|
||||
// Logs removidos por segurança
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: 'PATCH',
|
||||
@ -2223,7 +2198,7 @@ export async function vincularUserIdPaciente(pacienteId: string | number, userId
|
||||
const arr = await parse<Paciente[] | Paciente>(res);
|
||||
return Array.isArray(arr) ? arr[0] : (arr as Paciente);
|
||||
} catch (err) {
|
||||
console.error('[vincularUserIdPaciente] erro ao vincular:', { pacienteId: idStr, userId, url });
|
||||
console.error('[vincularUserIdPaciente] erro ao vincular - falha na requisição');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@ -2232,8 +2207,7 @@ export async function vincularUserIdPaciente(pacienteId: string | number, userId
|
||||
|
||||
|
||||
export async function atualizarMedico(id: string | number, input: MedicoInput): Promise<Medico> {
|
||||
console.log(`Tentando atualizar médico ID: ${id}`);
|
||||
console.log(`Payload original:`, input);
|
||||
// Logs removidos por segurança
|
||||
|
||||
// Criar um payload limpo apenas com campos básicos que sabemos que existem
|
||||
const cleanPayload = {
|
||||
@ -2280,7 +2254,7 @@ export async function atualizarMedico(id: string | number, input: MedicoInput):
|
||||
// Atualizar apenas no Supabase (dados reais)
|
||||
try {
|
||||
const url = `${REST}/doctors?id=eq.${id}`;
|
||||
console.log(`URL de atualização: ${url}`);
|
||||
// Log removido por segurança
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "PATCH",
|
||||
@ -2288,12 +2262,12 @@ export async function atualizarMedico(id: string | number, input: MedicoInput):
|
||||
body: JSON.stringify(cleanPayload),
|
||||
});
|
||||
|
||||
console.log(`Resposta do servidor: ${res.status} ${res.statusText}`);
|
||||
// Log removido por segurança
|
||||
|
||||
if (res.ok) {
|
||||
const arr = await parse<Medico[] | Medico>(res);
|
||||
const result = Array.isArray(arr) ? arr[0] : (arr as Medico);
|
||||
console.log('Médico atualizado no Supabase:', result);
|
||||
// Log removido por segurança
|
||||
return result;
|
||||
} else {
|
||||
// Vamos tentar ver o erro detalhado
|
||||
|
||||
@ -139,15 +139,12 @@ export async function listAssignmentsForPatient(patientId: string): Promise<Pati
|
||||
* Ú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}`);
|
||||
// Log removido por segurança
|
||||
const url = `${ASSIGNMENTS_URL}?user_id=eq.${encodeURIComponent(userId)}`;
|
||||
|
||||
try {
|
||||
const headers = getHeaders();
|
||||
console.debug('[ASSIGNMENT] GET', url, 'headers(masked)=', {
|
||||
...headers,
|
||||
Authorization: headers.Authorization ? '<<masked>>' : undefined,
|
||||
});
|
||||
// Logs removidos por segurança
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers,
|
||||
@ -156,7 +153,7 @@ export async function listAssignmentsForUser(userId: string): Promise<PatientAss
|
||||
// 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>');
|
||||
// Log removido por segurança
|
||||
|
||||
if (!response.ok) {
|
||||
const errorBody = txt || '';
|
||||
|
||||
@ -140,22 +140,14 @@ export async function listarRelatorios(filtros?: { patient_id?: string; status?:
|
||||
cabecalhos['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
// Logs de depuração (mask token)
|
||||
const masked = token ? `${token.slice(0, 6)}...${token.slice(-6)}` : null;
|
||||
console.log('[listarRelatorios] URL:', url);
|
||||
console.log('[listarRelatorios] Authorization (masked):', masked);
|
||||
console.log('[listarRelatorios] Headers (masked):', {
|
||||
...cabecalhos,
|
||||
Authorization: cabecalhos['Authorization'] ? '<<masked>>' : undefined,
|
||||
});
|
||||
// Logs removidos por segurança
|
||||
|
||||
const resposta = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: cabecalhos,
|
||||
});
|
||||
console.log('[listarRelatorios] Status:', resposta.status, resposta.statusText);
|
||||
// Logs removidos por segurança
|
||||
const dados = await resposta.json().catch(() => null);
|
||||
console.log('[listarRelatorios] Payload:', dados);
|
||||
if (!resposta.ok) throw new Error('Erro ao buscar relatórios');
|
||||
if (Array.isArray(dados)) return dados;
|
||||
if (dados && Array.isArray(dados.data)) return dados.data;
|
||||
@ -170,14 +162,14 @@ export async function listarRelatorios(filtros?: { patient_id?: string; status?:
|
||||
*/
|
||||
export async function buscarRelatorioPorId(id: string): Promise<Report> {
|
||||
try {
|
||||
console.log('🔍 [API RELATÓRIOS] Buscando relatório ID:', id);
|
||||
// Log removido por segurança
|
||||
const resposta = await fetch(`${BASE_API_RELATORIOS}?id=eq.${id}`, {
|
||||
method: 'GET',
|
||||
headers: obterCabecalhos(),
|
||||
});
|
||||
const resultado = await tratarRespostaApi<Report[]>(resposta);
|
||||
const relatorio = Array.isArray(resultado) && resultado.length > 0 ? resultado[0] : null;
|
||||
console.log('✅ [API RELATÓRIOS] Relatório encontrado:', relatorio);
|
||||
// Log removido por segurança
|
||||
if (!relatorio) throw new Error('Relatório não encontrado');
|
||||
return relatorio;
|
||||
} catch (erro) {
|
||||
@ -191,16 +183,14 @@ export async function buscarRelatorioPorId(id: string): Promise<Report> {
|
||||
*/
|
||||
export async function criarRelatorio(dadosRelatorio: CreateReportData, token?: string): Promise<Report> {
|
||||
const headers = obterCabecalhos(token);
|
||||
const masked = (headers as any)['Authorization'] ? String((headers as any)['Authorization']).replace(/Bearer\s+(.+)/, 'Bearer <token_masked>') : null;
|
||||
console.log('[criarRelatorio] POST', BASE_API_RELATORIOS);
|
||||
console.log('[criarRelatorio] Headers (masked):', { ...headers, Authorization: masked });
|
||||
// Logs removidos por segurança
|
||||
|
||||
const resposta = await fetch(BASE_API_RELATORIOS, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(dadosRelatorio),
|
||||
});
|
||||
console.log('[criarRelatorio] Status:', resposta.status, resposta.statusText);
|
||||
// Log removido por segurança
|
||||
if (!resposta.ok) {
|
||||
let mensagemErro = `HTTP ${resposta.status}: ${resposta.statusText}`;
|
||||
try {
|
||||
@ -229,8 +219,7 @@ export async function criarRelatorio(dadosRelatorio: CreateReportData, token?: s
|
||||
*/
|
||||
export async function atualizarRelatorio(id: string, dadosRelatorio: UpdateReportData): Promise<Report> {
|
||||
try {
|
||||
console.log('📝 [API RELATÓRIOS] Atualizando relatório ID:', id);
|
||||
console.log('📤 [API RELATÓRIOS] Dados:', dadosRelatorio);
|
||||
// Logs removidos por segurança
|
||||
const resposta = await fetch(`${BASE_API_RELATORIOS}?id=eq.${id}`, {
|
||||
method: 'PATCH',
|
||||
headers: obterCabecalhos(),
|
||||
@ -238,7 +227,7 @@ export async function atualizarRelatorio(id: string, dadosRelatorio: UpdateRepor
|
||||
});
|
||||
const resultado = await tratarRespostaApi<Report[]>(resposta);
|
||||
const relatorio = Array.isArray(resultado) && resultado.length > 0 ? resultado[0] : null;
|
||||
console.log('✅ [API RELATÓRIOS] Relatório atualizado:', relatorio);
|
||||
// Log removido por segurança
|
||||
if (!relatorio) throw new Error('Relatório não encontrado');
|
||||
return relatorio;
|
||||
} catch (erro) {
|
||||
@ -252,13 +241,13 @@ export async function atualizarRelatorio(id: string, dadosRelatorio: UpdateRepor
|
||||
*/
|
||||
export async function deletarRelatorio(id: string): Promise<void> {
|
||||
try {
|
||||
console.log('🗑️ [API RELATÓRIOS] Deletando relatório ID:', id);
|
||||
// Log removido por segurança
|
||||
const resposta = await fetch(`${BASE_API_RELATORIOS}/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: obterCabecalhos(),
|
||||
});
|
||||
await tratarRespostaApi<void>(resposta);
|
||||
console.log('✅ [API RELATÓRIOS] Relatório deletado com sucesso');
|
||||
// Log removido por segurança
|
||||
} catch (erro) {
|
||||
console.error('❌ [API RELATÓRIOS] Erro ao deletar relatório:', erro);
|
||||
throw erro;
|
||||
@ -270,20 +259,19 @@ export async function deletarRelatorio(id: string): Promise<void> {
|
||||
*/
|
||||
export async function listarRelatoriosPorPaciente(idPaciente: string): Promise<Report[]> {
|
||||
try {
|
||||
console.log('👤 [API RELATÓRIOS] Buscando relatórios do paciente:', idPaciente);
|
||||
// Logs removidos por segurança
|
||||
// Try a strict eq lookup first (encode the id)
|
||||
const encodedId = encodeURIComponent(String(idPaciente));
|
||||
let url = `${BASE_API_RELATORIOS}?patient_id=eq.${encodedId}`;
|
||||
const headers = obterCabecalhos();
|
||||
const masked = (headers as any)['Authorization'] ? `${String((headers as any)['Authorization']).slice(0,6)}...${String((headers as any)['Authorization']).slice(-6)}` : null;
|
||||
console.debug('[listarRelatoriosPorPaciente] URL:', url);
|
||||
console.debug('[listarRelatoriosPorPaciente] Headers (masked):', { ...headers, Authorization: masked ? '<<masked>>' : undefined });
|
||||
// Logs removidos por segurança
|
||||
const resposta = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers,
|
||||
});
|
||||
const resultado = await tratarRespostaApi<Report[]>(resposta);
|
||||
console.log('✅ [API RELATÓRIOS] Relatórios do paciente encontrados (eq):', resultado.length);
|
||||
// Log removido por segurança
|
||||
// If eq returned results, return them. Otherwise retry using `in.(id)` which some setups prefer.
|
||||
if (Array.isArray(resultado) && resultado.length) return resultado;
|
||||
|
||||
@ -291,13 +279,13 @@ export async function listarRelatoriosPorPaciente(idPaciente: string): Promise<R
|
||||
try {
|
||||
const inClause = encodeURIComponent(`(${String(idPaciente)})`);
|
||||
const urlIn = `${BASE_API_RELATORIOS}?patient_id=in.${inClause}`;
|
||||
console.debug('[listarRelatoriosPorPaciente] retrying with IN clause URL:', urlIn);
|
||||
// Log removido por segurança
|
||||
const resp2 = await fetch(urlIn, { method: 'GET', headers });
|
||||
const res2 = await tratarRespostaApi<Report[]>(resp2);
|
||||
console.log('✅ [API RELATÓRIOS] Relatórios do paciente encontrados (in):', Array.isArray(res2) ? res2.length : 0);
|
||||
// Log removido por segurança
|
||||
return Array.isArray(res2) ? res2 : [];
|
||||
} catch (e) {
|
||||
console.warn('[listarRelatoriosPorPaciente] fallback in.() failed', e);
|
||||
// Log removido por segurança
|
||||
}
|
||||
|
||||
return [];
|
||||
@ -315,15 +303,13 @@ export async function listarRelatoriosPorMedico(idMedico: string): Promise<Repor
|
||||
console.log('👨⚕️ [API RELATÓRIOS] Buscando relatórios do médico:', idMedico);
|
||||
const url = `${BASE_API_RELATORIOS}?requested_by=eq.${idMedico}`;
|
||||
const headers = obterCabecalhos();
|
||||
const masked = (headers as any)['Authorization'] ? `${String((headers as any)['Authorization']).slice(0,6)}...${String((headers as any)['Authorization']).slice(-6)}` : null;
|
||||
console.debug('[listarRelatoriosPorMedico] URL:', url);
|
||||
console.debug('[listarRelatoriosPorMedico] Headers (masked):', { ...headers, Authorization: masked ? '<<masked>>' : undefined });
|
||||
// Logs removidos por segurança
|
||||
const resposta = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: obterCabecalhos(),
|
||||
});
|
||||
const resultado = await tratarRespostaApi<Report[]>(resposta);
|
||||
console.log('✅ [API RELATÓRIOS] Relatórios do médico encontrados:', resultado.length);
|
||||
// Log removido por segurança
|
||||
return resultado;
|
||||
} catch (erro) {
|
||||
console.error('❌ [API RELATÓRIOS] Erro ao buscar relatórios do médico:', erro);
|
||||
@ -346,13 +332,11 @@ export async function listarRelatoriosPorPacientes(ids: string[]): Promise<Repor
|
||||
const inClause = cleaned.join(',');
|
||||
const url = `${BASE_API_RELATORIOS}?patient_id=in.(${inClause})`;
|
||||
const headers = obterCabecalhos();
|
||||
const masked = (headers as any)['Authorization'] ? '<<masked>>' : undefined;
|
||||
console.debug('[listarRelatoriosPorPacientes] URL:', url);
|
||||
console.debug('[listarRelatoriosPorPacientes] Headers (masked):', { ...headers, Authorization: masked ? '<<masked>>' : undefined });
|
||||
// Logs removidos por segurança
|
||||
|
||||
const resposta = await fetch(url, { method: 'GET', headers });
|
||||
const resultado = await tratarRespostaApi<Report[]>(resposta);
|
||||
console.log('✅ [API RELATÓRIOS] Relatórios encontrados para pacientes:', resultado.length);
|
||||
// Log removido por segurança
|
||||
return resultado;
|
||||
} catch (erro) {
|
||||
console.error('❌ [API RELATÓRIOS] Erro ao buscar relatórios para vários pacientes:', erro);
|
||||
@ -368,26 +352,26 @@ export async function listarRelatoriosPorPacientes(ids: string[]): Promise<Repor
|
||||
export async function listarRelatoriosParaMedicoAtribuido(userId?: string): Promise<Report[]> {
|
||||
try {
|
||||
if (!userId) {
|
||||
console.warn('[listarRelatoriosParaMedicoAtribuido] userId ausente, retornando array vazio');
|
||||
// Log removido por segurança
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log('[listarRelatoriosParaMedicoAtribuido] buscando assignments para user:', userId);
|
||||
// Log removido por segurança
|
||||
// importe dinamicamente para evitar possíveis ciclos
|
||||
const assignmentMod = await import('./assignment');
|
||||
const assigns = await assignmentMod.listAssignmentsForUser(String(userId));
|
||||
if (!assigns || !Array.isArray(assigns) || assigns.length === 0) {
|
||||
console.log('[listarRelatoriosParaMedicoAtribuido] nenhum paciente atribuído encontrado para user:', userId);
|
||||
// Log removido por segurança
|
||||
return [];
|
||||
}
|
||||
|
||||
const patientIds = Array.from(new Set(assigns.map((a: any) => String(a.patient_id)).filter(Boolean)));
|
||||
if (!patientIds.length) {
|
||||
console.log('[listarRelatoriosParaMedicoAtribuido] nenhuma patient_id válida encontrada nas atribuições');
|
||||
// Log removido por segurança
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log('[listarRelatoriosParaMedicoAtribuido] carregando relatórios para pacientes:', patientIds);
|
||||
// Log removido por segurança
|
||||
const rels = await listarRelatoriosPorPacientes(patientIds);
|
||||
return rels || [];
|
||||
} catch (err) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user