develop #83

Merged
M-Gabrielly merged 426 commits from develop into main 2025-12-04 04:13:15 +00:00
6 changed files with 956 additions and 91 deletions
Showing only changes of commit b2ee5987c6 - Show all commits

View File

@ -10,34 +10,29 @@ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, Di
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { MoreHorizontal, Plus, Search, Eye, Edit, Trash2, ArrowLeft } from "lucide-react"; import { MoreHorizontal, Plus, Search, Eye, Edit, Trash2, ArrowLeft } from "lucide-react";
import { Paciente, Endereco, listarPacientes, buscarPacientePorId, excluirPaciente } from "@/lib/api"; import { Paciente, Endereco, listarPacientes, buscarPacientes, buscarPacientePorId, excluirPaciente } from "@/lib/api";
import { PatientRegistrationForm } from "@/components/forms/patient-registration-form"; import { PatientRegistrationForm } from "@/components/forms/patient-registration-form";
function normalizePaciente(p: any): Paciente { function normalizePaciente(p: any): Paciente {
const endereco: Endereco = {
cep: p.endereco?.cep ?? p.cep ?? "",
logradouro: p.endereco?.logradouro ?? p.street ?? "",
numero: p.endereco?.numero ?? p.number ?? "",
complemento: p.endereco?.complemento ?? p.complement ?? "",
bairro: p.endereco?.bairro ?? p.neighborhood ?? "",
cidade: p.endereco?.cidade ?? p.city ?? "",
estado: p.endereco?.estado ?? p.state ?? "",
};
return { return {
id: String(p.id ?? p.uuid ?? p.paciente_id ?? ""), id: String(p.id ?? p.uuid ?? p.paciente_id ?? ""),
nome: p.full_name ?? "", // 👈 troca nome → full_name full_name: p.full_name ?? p.name ?? p.nome ?? "",
nome_social: p.social_name ?? null, // 👈 Supabase usa social_name social_name: p.social_name ?? p.nome_social ?? null,
cpf: p.cpf ?? "", cpf: p.cpf ?? "",
rg: p.rg ?? p.document_number ?? null, // 👈 às vezes vem como document_number rg: p.rg ?? p.document_number ?? null,
sexo: p.sexo ?? p.sex ?? null, // 👈 Supabase usa sex sex: p.sex ?? p.sexo ?? null,
data_nascimento: p.data_nascimento ?? p.birth_date ?? null, birth_date: p.birth_date ?? p.data_nascimento ?? null,
telefone: p.telefone ?? p.phone_mobile ?? "", phone_mobile: p.phone_mobile ?? p.telefone ?? "",
email: p.email ?? "", email: p.email ?? "",
endereco, cep: p.cep ?? "",
observacoes: p.observacoes ?? p.notes ?? null, street: p.street ?? p.logradouro ?? "",
foto_url: p.foto_url ?? null, number: p.number ?? p.numero ?? "",
complement: p.complement ?? p.complemento ?? "",
neighborhood: p.neighborhood ?? p.bairro ?? "",
city: p.city ?? p.cidade ?? "",
state: p.state ?? p.estado ?? "",
notes: p.notes ?? p.observacoes ?? null,
}; };
} }
@ -56,7 +51,12 @@ export default function PacientesPage() {
try { try {
setLoading(true); setLoading(true);
const data = await listarPacientes({ page: 1, limit: 20 }); const data = await listarPacientes({ page: 1, limit: 20 });
setPatients((data ?? []).map(normalizePaciente));
if (Array.isArray(data)) {
setPatients(data.map(normalizePaciente));
} else {
setPatients([]);
}
setError(null); setError(null);
} catch (e: any) { } catch (e: any) {
setPatients([]); setPatients([]);
@ -72,13 +72,23 @@ export default function PacientesPage() {
const filtered = useMemo(() => { const filtered = useMemo(() => {
if (!search.trim()) return patients; if (!search.trim()) return patients;
const q = search.toLowerCase(); const q = search.toLowerCase().trim();
const qDigits = q.replace(/\D/g, ""); const qDigits = q.replace(/\D/g, "");
return patients.filter((p) => { return patients.filter((p) => {
const byName = (p.nome || "").toLowerCase().includes(q); // Busca por nome
const byCPF = (p.cpf || "").replace(/\D/g, "").includes(qDigits); const byName = (p.full_name || "").toLowerCase().includes(q);
const byId = String(p.id || "").includes(qDigits);
return byName || byCPF || byId; // Busca por CPF (remove formatação)
const byCPF = qDigits.length >= 3 && (p.cpf || "").replace(/\D/g, "").includes(qDigits);
// Busca por ID (UUID completo ou parcial)
const byId = (p.id || "").toLowerCase().includes(q);
// Busca por email
const byEmail = (p.email || "").toLowerCase().includes(q);
return byName || byCPF || byId || byEmail;
}); });
}, [patients, search]); }, [patients, search]);
@ -122,25 +132,33 @@ export default function PacientesPage() {
const q = search.trim(); const q = search.trim();
if (!q) return loadAll(); if (!q) return loadAll();
try {
setLoading(true);
setError(null);
if (/^\d+$/.test(q)) { // Se parece com ID (UUID), busca diretamente
try { if (q.includes('-') && q.length > 10) {
setLoading(true);
const one = await buscarPacientePorId(q); const one = await buscarPacientePorId(q);
setPatients(one ? [normalizePaciente(one)] : []); setPatients(one ? [normalizePaciente(one)] : []);
setError(one ? null : "Paciente não encontrado."); setError(one ? null : "Paciente não encontrado.");
} catch (e: any) { // Limpa o campo de busca para que o filtro não interfira
setPatients([]); setSearch("");
setError(e?.message || "Paciente não encontrado."); return;
} finally {
setLoading(false);
} }
return;
// Para outros termos, usa busca avançada
const results = await buscarPacientes(q);
setPatients(results.map(normalizePaciente));
setError(results.length === 0 ? "Nenhum paciente encontrado." : null);
// Limpa o campo de busca para que o filtro não interfira
setSearch("");
} catch (e: any) {
setPatients([]);
setError(e?.message || "Erro na busca.");
} finally {
setLoading(false);
} }
await loadAll();
setTimeout(() => setSearch(q), 0);
} }
if (loading) return <p>Carregando pacientes...</p>; if (loading) return <p>Carregando pacientes...</p>;
@ -210,11 +228,11 @@ export default function PacientesPage() {
{filtered.length > 0 ? ( {filtered.length > 0 ? (
filtered.map((p) => ( filtered.map((p) => (
<TableRow key={p.id}> <TableRow key={p.id}>
<TableCell className="font-medium">{p.nome || "(sem nome)"}</TableCell> <TableCell className="font-medium">{p.full_name || "(sem nome)"}</TableCell>
<TableCell>{p.cpf || "-"}</TableCell> <TableCell>{p.cpf || "-"}</TableCell>
<TableCell>{p.telefone || "-"}</TableCell> <TableCell>{p.phone_mobile || "-"}</TableCell>
<TableCell>{p.endereco?.cidade || "-"}</TableCell> <TableCell>{p.city || "-"}</TableCell>
<TableCell>{p.endereco?.estado || "-"}</TableCell> <TableCell>{p.state || "-"}</TableCell>
<TableCell> <TableCell>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
@ -258,13 +276,13 @@ export default function PacientesPage() {
<DialogHeader> <DialogHeader>
<DialogTitle>Detalhes do Paciente</DialogTitle> <DialogTitle>Detalhes do Paciente</DialogTitle>
<DialogDescription> <DialogDescription>
Informações detalhadas de {viewingPatient.nome}. Informações detalhadas de {viewingPatient.full_name}.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="grid gap-4 py-4"> <div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4"> <div className="grid grid-cols-4 items-center gap-4">
<Label className="text-right">Nome</Label> <Label className="text-right">Nome</Label>
<span className="col-span-3 font-medium">{viewingPatient.nome}</span> <span className="col-span-3 font-medium">{viewingPatient.full_name}</span>
</div> </div>
<div className="grid grid-cols-4 items-center gap-4"> <div className="grid grid-cols-4 items-center gap-4">
<Label className="text-right">CPF</Label> <Label className="text-right">CPF</Label>
@ -272,17 +290,17 @@ export default function PacientesPage() {
</div> </div>
<div className="grid grid-cols-4 items-center gap-4"> <div className="grid grid-cols-4 items-center gap-4">
<Label className="text-right">Telefone</Label> <Label className="text-right">Telefone</Label>
<span className="col-span-3">{viewingPatient.telefone}</span> <span className="col-span-3">{viewingPatient.phone_mobile}</span>
</div> </div>
<div className="grid grid-cols-4 items-center gap-4"> <div className="grid grid-cols-4 items-center gap-4">
<Label className="text-right">Endereço</Label> <Label className="text-right">Endereço</Label>
<span className="col-span-3"> <span className="col-span-3">
{`${viewingPatient.endereco?.logradouro || ''}, ${viewingPatient.endereco?.numero || ''} - ${viewingPatient.endereco?.bairro || ''}, ${viewingPatient.endereco?.cidade || ''} - ${viewingPatient.endereco?.estado || ''}`} {`${viewingPatient.street || ''}, ${viewingPatient.number || ''} - ${viewingPatient.neighborhood || ''}, ${viewingPatient.city || ''} - ${viewingPatient.state || ''}`}
</span> </span>
</div> </div>
<div className="grid grid-cols-4 items-center gap-4"> <div className="grid grid-cols-4 items-center gap-4">
<Label className="text-right">Observações</Label> <Label className="text-right">Observações</Label>
<span className="col-span-3">{viewingPatient.observacoes || "Nenhuma"}</span> <span className="col-span-3">{viewingPatient.notes || "Nenhuma"}</span>
</div> </div>
</div> </div>
<DialogFooter> <DialogFooter>

View File

@ -7,6 +7,8 @@ import "react-quill/dist/quill.snow.css";
import Link from "next/link"; import Link from "next/link";
import ProtectedRoute from "@/components/ProtectedRoute"; import ProtectedRoute from "@/components/ProtectedRoute";
import { useAuth } from "@/hooks/useAuth"; import { useAuth } from "@/hooks/useAuth";
import { buscarPacientes } from "@/lib/api";
import { ApiTest } from "@/components/debug/ApiTest";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
@ -566,53 +568,189 @@ const ProfissionalPage = () => {
}; };
const renderPacientesSection = () => ( const renderPacientesSection = () => {
<div className="bg-white shadow-md rounded-lg p-6"> // Estados para busca de pacientes
<h2 className="text-2xl font-bold mb-4">Gerenciamento de Pacientes</h2> const [buscaPaciente, setBuscaPaciente] = useState("");
<Table> const [pacientesBusca, setPacientesBusca] = useState<any[]>([]);
<TableHeader> const [carregandoBusca, setCarregandoBusca] = useState(false);
<TableRow> const [erroBusca, setErroBusca] = useState<string | null>(null);
<TableHead>Paciente</TableHead>
<TableHead>CPF</TableHead> // Função para buscar pacientes
<TableHead>Idade</TableHead> const handleBuscarPaciente = async () => {
<TableHead>Status do laudo</TableHead> if (!buscaPaciente.trim()) {
<TableHead>Ações</TableHead> setPacientesBusca([]);
</TableRow> setErroBusca(null);
</TableHeader> return;
<TableBody> }
{pacientes.map((paciente) => (
<TableRow key={paciente.cpf}> setCarregandoBusca(true);
<TableCell className="font-medium">{paciente.nome}</TableCell> setErroBusca(null);
<TableCell>{paciente.cpf}</TableCell>
<TableCell>{paciente.idade}</TableCell> try {
<TableCell>{paciente.statusLaudo}</TableCell> // Importa a função de busca
<TableCell> const { buscarPacientes } = await import("@/lib/api");
<div className="flex items-center gap-2"> const resultados = await buscarPacientes(buscaPaciente.trim());
<div className="relative group">
if (resultados.length === 0) {
setErroBusca("Nenhum paciente encontrado com os critérios informados.");
setPacientesBusca([]);
} else {
// Transforma os dados da API para o formato usado no componente
const pacientesFormatados = resultados.map(p => ({
nome: p.full_name || "Nome não informado",
cpf: p.cpf || "CPF não informado",
idade: p.birth_date ? new Date().getFullYear() - new Date(p.birth_date).getFullYear() : "N/A",
statusLaudo: "Pendente", // Status padrão
id: p.id
}));
setPacientesBusca(pacientesFormatados);
setErroBusca(null);
}
} catch (error: any) {
console.error("Erro ao buscar pacientes:", error);
setErroBusca(error.message || "Erro ao buscar pacientes. Tente novamente.");
setPacientesBusca([]);
} finally {
setCarregandoBusca(false);
}
};
const handleLimparBusca = () => {
setBuscaPaciente("");
setPacientesBusca([]);
setErroBusca(null);
};
return (
<div className="bg-white shadow-md rounded-lg p-6">
<h2 className="text-2xl font-bold mb-4">Gerenciamento de Pacientes</h2>
{/* Campo de busca */}
<div className="mb-6 p-4 bg-gray-50 rounded-lg">
<h3 className="text-lg font-semibold mb-3">Buscar Paciente</h3>
<div className="flex gap-2">
<div className="flex-1">
<Input
placeholder="Digite ID, CPF, nome ou email do paciente..."
value={buscaPaciente}
onChange={(e) => setBuscaPaciente(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleBuscarPaciente()}
className="w-full"
/>
</div>
<Button
onClick={handleBuscarPaciente}
disabled={carregandoBusca}
className="flex items-center gap-2"
>
{carregandoBusca ? (
<>
<div className="animate-spin h-4 w-4 border-2 border-white border-t-transparent rounded-full"></div>
Buscando...
</>
) : (
<>
<User className="h-4 w-4" />
Buscar
</>
)}
</Button>
{(buscaPaciente || pacientesBusca.length > 0 || erroBusca) && (
<Button
variant="outline"
onClick={handleLimparBusca}
className="flex items-center gap-2"
>
<X className="h-4 w-4" />
Limpar
</Button>
)}
</div>
{/* Resultados da busca */}
{erroBusca && (
<div className="mt-3 p-3 bg-red-50 border border-red-200 rounded-md">
<p className="text-red-700 text-sm">{erroBusca}</p>
</div>
)}
{pacientesBusca.length > 0 && (
<div className="mt-4">
<h4 className="text-md font-medium mb-2">Resultados da busca ({pacientesBusca.length}):</h4>
<div className="space-y-2">
{pacientesBusca.map((paciente, index) => (
<div key={index} className="flex items-center justify-between p-3 bg-white border rounded-lg hover:shadow-sm">
<div>
<p className="font-medium">{paciente.nome}</p>
<p className="text-sm text-gray-600">CPF: {paciente.cpf} Idade: {paciente.idade} anos</p>
</div>
<Button <Button
variant="outline"
size="sm" size="sm"
className="border-primary text-primary hover:bg-primary hover:text-white cursor-pointer"
onClick={() => { onClick={() => {
handleAbrirProntuario(paciente); handleAbrirProntuario(paciente);
setActiveSection('prontuario'); setActiveSection('prontuario');
}} }}
className="flex items-center gap-2"
> >
<FolderOpen className="h-4 w-4" /> <FolderOpen className="h-4 w-4" />
Abrir Prontuário
</Button> </Button>
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-3 py-1 bg-gray-900 text-white text-xs rounded-md opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none whitespace-nowrap z-50">
Ver informações do paciente
<div className="absolute top-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-l-4 border-r-4 border-t-4 border-transparent border-t-gray-900"></div>
</div>
</div> </div>
</div> ))}
</TableCell> </div>
</TableRow> </div>
))} )}
</TableBody> </div>
</Table>
</div> {/* Tabela de pacientes padrão */}
); <div>
<h3 className="text-lg font-semibold mb-3">Pacientes Recentes</h3>
<Table>
<TableHeader>
<TableRow>
<TableHead>Paciente</TableHead>
<TableHead>CPF</TableHead>
<TableHead>Idade</TableHead>
<TableHead>Status do laudo</TableHead>
<TableHead>Ações</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{pacientes.map((paciente) => (
<TableRow key={paciente.cpf}>
<TableCell className="font-medium">{paciente.nome}</TableCell>
<TableCell>{paciente.cpf}</TableCell>
<TableCell>{paciente.idade}</TableCell>
<TableCell>{paciente.statusLaudo}</TableCell>
<TableCell>
<div className="flex items-center gap-2">
<div className="relative group">
<Button
variant="outline"
size="sm"
className="border-primary text-primary hover:bg-primary hover:text-white cursor-pointer"
onClick={() => {
handleAbrirProntuario(paciente);
setActiveSection('prontuario');
}}
>
<FolderOpen className="h-4 w-4" />
</Button>
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-3 py-1 bg-gray-900 text-white text-xs rounded-md opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none whitespace-nowrap z-50">
Ver informações do paciente
<div className="absolute top-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-l-4 border-r-4 border-t-4 border-transparent border-t-gray-900"></div>
</div>
</div>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
);
};
const renderProntuarioSection = () => ( const renderProntuarioSection = () => (
@ -2286,6 +2424,18 @@ function LaudoEditor() {
); );
const renderDebugSection = () => (
<div className="space-y-6">
<div>
<h2 className="text-2xl font-bold mb-4">Debug da API</h2>
<p className="text-gray-600 mb-6">
Use esta seção para testar a conectividade com a API e debugar problemas de busca de pacientes.
</p>
</div>
<ApiTest />
</div>
);
const renderActiveSection = () => { const renderActiveSection = () => {
switch (activeSection) { switch (activeSection) {
case 'calendario': case 'calendario':
@ -2302,6 +2452,8 @@ function LaudoEditor() {
return renderRelatoriosMedicosSection(); return renderRelatoriosMedicosSection();
case 'perfil': case 'perfil':
return renderPerfilSection(); return renderPerfilSection();
case 'debug':
return renderDebugSection();
default: default:
return renderCalendarioSection(); return renderCalendarioSection();
} }
@ -2396,6 +2548,14 @@ function LaudoEditor() {
<Settings className="mr-2 h-4 w-4" /> <Settings className="mr-2 h-4 w-4" />
Meu Perfil Meu Perfil
</Button> </Button>
<Button
variant={activeSection === 'debug' ? 'default' : 'ghost'}
className="w-full justify-start cursor-pointer hover:bg-primary hover:text-primary-foreground cursor-pointer"
onClick={() => setActiveSection('debug')}
>
<Settings className="mr-2 h-4 w-4" />
Debug API
</Button>
</nav> </nav>
</aside> </aside>

View File

@ -0,0 +1,191 @@
"use client";
import React, { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { buscarPacientePorId, buscarPacientes, listarPacientes } from "@/lib/api";
export const ApiTest = () => {
const [testId, setTestId] = useState("7ddbd1e2-1aee-4f7a-94f9-ee4c735ca276");
const [resultado, setResultado] = useState<any>(null);
const [erro, setErro] = useState<string | null>(null);
const [carregando, setCarregando] = useState(false);
const testarConexao = async () => {
setCarregando(true);
setErro(null);
setResultado(null);
try {
console.log("Testando conexão com a API...");
// Primeiro teste básico
const pacientes = await listarPacientes({ limit: 5 });
console.log("Pacientes encontrados:", pacientes);
// Teste direto da API para ver estrutura
const REST = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1";
const headers: Record<string, string> = {
apikey: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ",
Accept: "application/json",
};
// Token do localStorage se disponível
const token = localStorage.getItem("auth_token") || localStorage.getItem("token");
if (token) {
headers.Authorization = `Bearer ${token}`;
}
console.log("Headers sendo usados:", headers);
const directRes = await fetch(`${REST}/patients?limit=3&select=id,full_name,cpf,email`, {
method: "GET",
headers
});
console.log("Status da resposta direta:", directRes.status);
if (directRes.ok) {
const directData = await directRes.json();
console.log("Dados diretos da API:", directData);
setResultado({
tipo: "Conexão + Estrutura",
data: {
pacientes: pacientes,
estruturaDireta: directData,
statusCode: directRes.status,
headers: Object.fromEntries(directRes.headers.entries())
}
});
} else {
const errorText = await directRes.text();
console.error("Erro na resposta direta:", errorText);
setErro(`Erro ${directRes.status}: ${errorText}`);
}
} catch (error: any) {
console.error("Erro na conexão:", error);
setErro(error.message);
} finally {
setCarregando(false);
}
};
const testarBuscaPorId = async () => {
if (!testId.trim()) {
setErro("Digite um ID para buscar");
return;
}
setCarregando(true);
setErro(null);
setResultado(null);
try {
console.log("Buscando paciente por ID:", testId);
const paciente = await buscarPacientePorId(testId);
setResultado({ tipo: "Busca por ID", data: paciente });
} catch (error: any) {
console.error("Erro na busca por ID:", error);
setErro(error.message);
} finally {
setCarregando(false);
}
};
const testarBuscaGeral = async () => {
if (!testId.trim()) {
setErro("Digite um termo para buscar");
return;
}
setCarregando(true);
setErro(null);
setResultado(null);
try {
console.log("Buscando pacientes:", testId);
const pacientes = await buscarPacientes(testId);
setResultado({ tipo: "Busca geral", data: pacientes });
} catch (error: any) {
console.error("Erro na busca geral:", error);
setErro(error.message);
} finally {
setCarregando(false);
}
};
return (
<Card className="max-w-2xl mx-auto">
<CardHeader>
<CardTitle>Teste da API - Pacientes</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex gap-2">
<Input
placeholder="Digite ID, CPF ou nome para buscar..."
value={testId}
onChange={(e) => setTestId(e.target.value)}
className="flex-1"
/>
</div>
<div className="flex gap-2 flex-wrap">
<Button
onClick={testarConexao}
disabled={carregando}
variant="outline"
>
{carregando ? "Testando..." : "Testar Conexão"}
</Button>
<Button
onClick={testarBuscaPorId}
disabled={carregando || !testId.trim()}
>
{carregando ? "Buscando..." : "Buscar por ID"}
</Button>
<Button
onClick={testarBuscaGeral}
disabled={carregando || !testId.trim()}
variant="secondary"
>
{carregando ? "Buscando..." : "Busca Geral"}
</Button>
</div>
{erro && (
<div className="p-3 bg-red-50 border border-red-200 rounded-md">
<p className="text-red-700 font-medium">Erro:</p>
<p className="text-red-600 text-sm">{erro}</p>
</div>
)}
{resultado && (
<div className="p-3 bg-green-50 border border-green-200 rounded-md">
<p className="text-green-700 font-medium">Resultado ({resultado.tipo}):</p>
<pre className="text-sm text-green-600 mt-2 overflow-auto">
{JSON.stringify(resultado.data, null, 2)}
</pre>
</div>
)}
<div className="text-xs text-gray-500 mt-4">
<p> <strong>Testar Conexão:</strong> Lista os primeiros 5 pacientes e verifica a estrutura da API</p>
<p> <strong>Buscar por ID:</strong> Busca um paciente específico por ID, CPF ou nome</p>
<p> <strong>Busca Geral:</strong> Busca avançada em múltiplos campos</p>
<p className="mt-2 font-medium">Abra o console do navegador (F12) para ver logs detalhados da investigação.</p>
</div>
</CardContent>
</Card>
);
};

View File

@ -0,0 +1,56 @@
"use client";
import React, { useState } from "react";
import { Button } from "@/components/ui/button";
import { listarPacientes, buscarPacientePorId } from "@/lib/api";
export const ApiTestSimple = () => {
const [resultado, setResultado] = useState<string>("");
const [carregando, setCarregando] = useState(false);
const testarListarPacientes = async () => {
setCarregando(true);
try {
const pacientes = await listarPacientes();
setResultado(`✅ Sucesso! Encontrados ${pacientes.length} pacientes:\n${JSON.stringify(pacientes, null, 2)}`);
} catch (error: any) {
setResultado(`❌ Erro: ${error.message}`);
} finally {
setCarregando(false);
}
};
const testarBuscarPorId = async () => {
setCarregando(true);
const id = "7ddbd1e2-1aee-4f7a-94f9-ee4c735ca276";
try {
const paciente = await buscarPacientePorId(id);
setResultado(`✅ Paciente encontrado:\n${JSON.stringify(paciente, null, 2)}`);
} catch (error: any) {
setResultado(`❌ Erro ao buscar ID ${id}: ${error.message}`);
} finally {
setCarregando(false);
}
};
return (
<div className="p-4 border rounded-lg max-w-4xl mx-auto">
<h3 className="text-lg font-bold mb-4">Teste da API Mock</h3>
<div className="flex gap-2 mb-4">
<Button onClick={testarListarPacientes} disabled={carregando}>
{carregando ? "Testando..." : "Listar Pacientes"}
</Button>
<Button onClick={testarBuscarPorId} disabled={carregando}>
{carregando ? "Testando..." : "Buscar ID Específico"}
</Button>
</div>
{resultado && (
<div className="bg-gray-100 p-4 rounded-lg">
<pre className="text-sm overflow-auto">{resultado}</pre>
</div>
)}
</div>
);
};

View File

@ -196,17 +196,16 @@ function withPrefer(h: Record<string, string>, prefer: string) {
} }
// Parse genérico // Parse genérico
// Dentro de lib/api.ts
async function parse<T>(res: Response): Promise<T> { async function parse<T>(res: Response): Promise<T> {
let json: any = null; let json: any = null;
try { try {
json = await res.json(); json = await res.json();
} catch (err) { } catch (err) {
console.error("Erro ao parsear a resposta:", err); // Coloque esse log aqui console.error("Erro ao parsear a resposta:", err);
} }
if (!res.ok) { if (!res.ok) {
console.error("[API ERROR]", res.url, res.status, json); // Coloque esse log aqui console.error("[API ERROR]", res.url, res.status, json);
const code = (json && (json.error?.code || json.code)) ?? res.status; const code = (json && (json.error?.code || json.code)) ?? res.status;
const msg = (json && (json.error?.message || json.message)) ?? res.statusText; const msg = (json && (json.error?.message || json.message)) ?? res.statusText;
throw new Error(`${code}: ${msg}`); throw new Error(`${code}: ${msg}`);
@ -244,6 +243,68 @@ export async function listarPacientes(params?: {
return await parse<Paciente[]>(res); return await parse<Paciente[]>(res);
} }
// Nova função para busca avançada de pacientes
export async function buscarPacientes(termo: string): Promise<Paciente[]> {
if (!termo || termo.trim().length < 2) {
return [];
}
const searchTerm = termo.toLowerCase().trim();
const digitsOnly = searchTerm.replace(/\D/g, '');
// Monta queries para buscar em múltiplos campos
const queries = [];
// Busca por ID se parece com UUID
if (searchTerm.includes('-') && searchTerm.length > 10) {
queries.push(`id=eq.${searchTerm}`);
}
// Busca por CPF (com e sem formatação)
if (digitsOnly.length >= 11) {
queries.push(`cpf=eq.${digitsOnly}`);
} else if (digitsOnly.length >= 3) {
queries.push(`cpf=ilike.*${digitsOnly}*`);
}
// Busca por nome (usando ilike para busca case-insensitive)
if (searchTerm.length >= 2) {
queries.push(`full_name=ilike.*${searchTerm}*`);
queries.push(`social_name=ilike.*${searchTerm}*`);
}
// Busca por email se contém @
if (searchTerm.includes('@')) {
queries.push(`email=ilike.*${searchTerm}*`);
}
const results: Paciente[] = [];
const seenIds = new Set<string>();
// Executa as buscas e combina resultados únicos
for (const query of queries) {
try {
const url = `${REST}/patients?${query}&limit=10`;
const res = await fetch(url, { method: "GET", headers: baseHeaders() });
const arr = await parse<Paciente[]>(res);
if (arr?.length > 0) {
for (const paciente of arr) {
if (!seenIds.has(paciente.id)) {
seenIds.add(paciente.id);
results.push(paciente);
}
}
}
} catch (error) {
console.warn(`Erro na busca com query: ${query}`, error);
}
}
return results.slice(0, 20); // Limita a 20 resultados
}
export async function buscarPacientePorId(id: string | number): Promise<Paciente> { export async function buscarPacientePorId(id: string | number): Promise<Paciente> {
const url = `${REST}/patients?id=eq.${id}`; const url = `${REST}/patients?id=eq.${id}`;
const res = await fetch(url, { method: "GET", headers: baseHeaders() }); const res = await fetch(url, { method: "GET", headers: baseHeaders() });

View File

@ -2365,6 +2365,53 @@
"node": ">=10.16.0" "node": ">=10.16.0"
} }
}, },
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.0",
"es-define-property": "^1.0.0",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/call-bound": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"get-intrinsic": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001739", "version": "1.0.30001739",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz",
@ -2659,6 +2706,40 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/define-properties": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
"license": "MIT",
"dependencies": {
"define-data-property": "^1.0.1",
"has-property-descriptors": "^1.0.0",
"object-keys": "^1.1.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/detect-libc": { "node_modules/detect-libc": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
@ -2685,6 +2766,20 @@
"@types/trusted-types": "^2.0.7" "@types/trusted-types": "^2.0.7"
} }
}, },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.213", "version": "1.5.213",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.213.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.213.tgz",
@ -2733,6 +2828,36 @@
"node": ">=10.13.0" "node": ">=10.13.0"
} }
}, },
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-toolkit": { "node_modules/es-toolkit": {
"version": "1.39.10", "version": "1.39.10",
"resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.10.tgz", "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.10.tgz",
@ -2800,6 +2925,24 @@
"url": "https://github.com/sponsors/rawify" "url": "https://github.com/sponsors/rawify"
} }
}, },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/functions-have-names": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/geist": { "node_modules/geist": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/geist/-/geist-1.4.2.tgz", "resolved": "https://registry.npmjs.org/geist/-/geist-1.4.2.tgz",
@ -2809,6 +2952,30 @@
"next": ">=13.2.0" "next": ">=13.2.0"
} }
}, },
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-nonce": { "node_modules/get-nonce": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
@ -2818,12 +2985,88 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/graceful-fs": { "node_modules/graceful-fs": {
"version": "4.2.11", "version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/html2canvas": { "node_modules/html2canvas": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
@ -2873,6 +3116,56 @@
"integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==", "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/is-arguments": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
"integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"has-tostringtag": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-date-object": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
"integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"has-tostringtag": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-regex": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
"integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"gopd": "^1.2.0",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/jiti": { "node_modules/jiti": {
"version": "2.5.1", "version": "2.5.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz",
@ -3182,6 +3475,15 @@
"@jridgewell/sourcemap-codec": "^1.5.5" "@jridgewell/sourcemap-codec": "^1.5.5"
} }
}, },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/minipass": { "node_modules/minipass": {
"version": "7.1.2", "version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
@ -3352,6 +3654,31 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/object-is": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
"integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.7",
"define-properties": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/pako": { "node_modules/pako": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
@ -3749,6 +4076,26 @@
"license": "MIT", "license": "MIT",
"optional": true "optional": true
}, },
"node_modules/regexp.prototype.flags": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
"integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.8",
"define-properties": "^1.2.1",
"es-errors": "^1.3.0",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"set-function-name": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/reselect": { "node_modules/reselect": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
@ -3780,6 +4127,38 @@
"integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==", "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"license": "MIT",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/set-function-name": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
"integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
"license": "MIT",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"functions-have-names": "^1.2.3",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/signature_pad": { "node_modules/signature_pad": {
"version": "2.3.2", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/signature_pad/-/signature_pad-2.3.2.tgz", "resolved": "https://registry.npmjs.org/signature_pad/-/signature_pad-2.3.2.tgz",