chore: update components config
This commit is contained in:
parent
a5d89b3fff
commit
a7c9c90ebb
@ -1,10 +1,10 @@
|
||||
// app/agendamento/page.tsx
|
||||
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
// Importação dinâmica para evitar erros de SSR
|
||||
|
||||
const AgendaCalendar = dynamic(() => import('@/components/agendamento/AgendaCalendar'), {
|
||||
ssr: false,
|
||||
loading: () => (
|
||||
@ -24,7 +24,7 @@ const AgendaCalendar = dynamic(() => import('@/components/agendamento/AgendaCale
|
||||
const AppointmentModal = dynamic(() => import('@/components/agendamento/AppointmentModal'), { ssr: false });
|
||||
const ListaEspera = dynamic(() => import('@/components/agendamento/ListaEspera'), { ssr: false });
|
||||
|
||||
// Dados mockados
|
||||
|
||||
const mockAppointments = [
|
||||
{ id: '1', patient: 'Ana Costa', time: '2025-09-10T09:00', duration: 30, type: 'consulta' as const, status: 'confirmed' as const, professional: '1', notes: '' },
|
||||
{ id: '2', patient: 'Pedro Alves', time: '2025-09-10T10:30', duration: 45, type: 'retorno' as const, status: 'pending' as const, professional: '2', notes: '' },
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
/* src/app/dashboard/pacientes/page.tsx */
|
||||
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
@ -11,7 +11,7 @@ import { MoreHorizontal, Plus, Search, Eye, Edit, Trash2, ArrowLeft } from "luci
|
||||
import { Paciente, Endereco, listarPacientes, buscarPacientePorId, excluirPaciente } from "@/lib/api";
|
||||
import { PatientRegistrationForm } from "@/components/forms/patient-registration-form";
|
||||
|
||||
// Converte qualquer formato que vier do mock para o shape do nosso tipo Paciente
|
||||
|
||||
function normalizePaciente(p: any): Paciente {
|
||||
const endereco: Endereco = {
|
||||
cep: p.endereco?.cep ?? p.cep ?? "",
|
||||
@ -114,7 +114,7 @@ export default function PacientesPage() {
|
||||
const q = search.trim();
|
||||
if (!q) return loadAll();
|
||||
|
||||
// Se for apenas números, tentamos como ID no servidor
|
||||
|
||||
if (/^\d+$/.test(q)) {
|
||||
try {
|
||||
setLoading(true);
|
||||
@ -130,7 +130,7 @@ export default function PacientesPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Senão, recarrega e filtra local (o mock nem sempre filtra por nome/CPF)
|
||||
|
||||
await loadAll();
|
||||
setTimeout(() => setSearch(q), 0);
|
||||
}
|
||||
@ -161,7 +161,7 @@ export default function PacientesPage() {
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Cabeçalho + Busca (um único input no padrão do print) */}
|
||||
{}
|
||||
<div className="flex items-center justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">Pacientes</h1>
|
||||
|
||||
@ -31,7 +31,7 @@ import {
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
|
||||
// Importações do FullCalendar
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import dayGridPlugin from "@fullcalendar/daygrid";
|
||||
import timeGridPlugin from "@fullcalendar/timegrid";
|
||||
@ -54,7 +54,7 @@ const medico = {
|
||||
fotoUrl: "",
|
||||
}
|
||||
|
||||
// Tipos de consulta com cores
|
||||
|
||||
const colorsByType = {
|
||||
Rotina: "#4dabf7",
|
||||
Cardiologia: "#f76c6c",
|
||||
@ -116,7 +116,7 @@ const ProfissionalPage = () => {
|
||||
setPacienteSelecionado(null);
|
||||
};
|
||||
|
||||
// Clicar em um dia -> abrir popup 3 etapas
|
||||
|
||||
const handleDateClick = (arg: any) => {
|
||||
setSelectedDate(arg.dateStr);
|
||||
setNewEvent({ title: "", type: "", time: "", pacienteId: "" });
|
||||
@ -125,7 +125,7 @@ const ProfissionalPage = () => {
|
||||
setShowPopup(true);
|
||||
};
|
||||
|
||||
// Adicionar nova consulta
|
||||
|
||||
const handleAddEvent = () => {
|
||||
const paciente = pacientes.find(p => p.nome === newEvent.title);
|
||||
const eventToAdd = {
|
||||
@ -141,7 +141,7 @@ const ProfissionalPage = () => {
|
||||
setShowPopup(false);
|
||||
};
|
||||
|
||||
// Editar consulta existente
|
||||
|
||||
const handleEditEvent = () => {
|
||||
setEvents((prevEvents) =>
|
||||
prevEvents.map((ev) =>
|
||||
@ -161,19 +161,19 @@ const ProfissionalPage = () => {
|
||||
setShowActionModal(false);
|
||||
};
|
||||
|
||||
// Próxima etapa no popup
|
||||
|
||||
const handleNextStep = () => {
|
||||
if (step < 3) setStep(step + 1);
|
||||
else editingEvent ? handleEditEvent() : handleAddEvent();
|
||||
};
|
||||
|
||||
// Clicar em uma consulta -> abre modal de ação (Editar/Apagar)
|
||||
|
||||
const handleEventClick = (clickInfo: any) => {
|
||||
setSelectedEvent(clickInfo.event);
|
||||
setShowActionModal(true);
|
||||
};
|
||||
|
||||
// Apagar consulta
|
||||
|
||||
const handleDeleteEvent = () => {
|
||||
if (!selectedEvent) return;
|
||||
setEvents((prevEvents) =>
|
||||
@ -182,7 +182,7 @@ const ProfissionalPage = () => {
|
||||
setShowActionModal(false);
|
||||
};
|
||||
|
||||
// Começar a editar
|
||||
|
||||
const handleStartEdit = () => {
|
||||
if (!selectedEvent) return;
|
||||
setEditingEvent(selectedEvent);
|
||||
@ -197,7 +197,7 @@ const ProfissionalPage = () => {
|
||||
setShowPopup(true);
|
||||
};
|
||||
|
||||
// Aparência da consulta dentro do calendário
|
||||
|
||||
const renderEventContent = (eventInfo: any) => {
|
||||
const bg = eventInfo.event.backgroundColor || eventInfo.event.extendedProps?.color || "#4dabf7";
|
||||
|
||||
@ -241,7 +241,7 @@ const ProfissionalPage = () => {
|
||||
</header>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-[220px_1fr] gap-6">
|
||||
{/* Sidebar */}
|
||||
{}
|
||||
<aside className="md:sticky md:top-8 h-fit">
|
||||
<nav className="bg-white shadow-md rounded-lg p-3 space-y-1">
|
||||
<Button asChild variant="ghost" className="w-full justify-start hover:bg-primary hover:text-primary-foreground">
|
||||
@ -602,7 +602,7 @@ const ProfissionalPage = () => {
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{/* POPUP 3 etapas (Adicionar/Editar) */}
|
||||
{}
|
||||
{showPopup && (
|
||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex justify-center items-center z-50">
|
||||
|
||||
@ -713,7 +713,7 @@ const ProfissionalPage = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* MODAL de ação ao clicar em consulta */}
|
||||
{}
|
||||
{showActionModal && selectedEvent && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center z-50">
|
||||
<div className="bg-white p-6 rounded-lg w-96">
|
||||
|
||||
@ -8,9 +8,9 @@ export function AboutSection() {
|
||||
<section className="py-16 lg:py-24 bg-muted/30">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||||
{/* Left Content */}
|
||||
{}
|
||||
<div className="space-y-8">
|
||||
{/* Professional Image */}
|
||||
{}
|
||||
<div className="relative">
|
||||
<img
|
||||
src="/Screenshot 2025-09-11 121911.png"
|
||||
@ -19,7 +19,7 @@ export function AboutSection() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Objective Card */}
|
||||
{}
|
||||
<Card className="bg-primary text-primary-foreground p-8 rounded-2xl">
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="flex-shrink-0 w-12 h-12 bg-primary-foreground/20 rounded-full flex items-center justify-center">
|
||||
@ -36,7 +36,7 @@ export function AboutSection() {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Right Content */}
|
||||
{}
|
||||
<div className="space-y-8">
|
||||
<div className="space-y-4">
|
||||
<div className="inline-block px-4 py-2 bg-primary/10 text-primary rounded-full text-sm font-medium uppercase tracking-wide">
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// app/agenda/page.tsx
|
||||
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { AgendaCalendar, AppointmentModal, ListaEspera } from '@/components/agendamento';
|
||||
|
||||
// Dados mockados - substitua pelos seus dados reais
|
||||
|
||||
const mockAppointments = [
|
||||
{ id: '1', patient: 'Ana Costa', time: '2025-09-10T09:00', duration: 30, type: 'consulta' as const, status: 'confirmed' as const, professional: '1', notes: '' },
|
||||
{ id: '2', patient: 'Pedro Alves', time: '2025-09-10T10:30', duration: 45, type: 'retorno' as const, status: 'pending' as const, professional: '2', notes: '' },
|
||||
@ -32,10 +32,10 @@ export default function AgendaPage() {
|
||||
|
||||
const handleSaveAppointment = (appointment: any) => {
|
||||
if (appointment.id) {
|
||||
// Editar agendamento existente
|
||||
|
||||
setAppointments(prev => prev.map(a => a.id === appointment.id ? appointment : a));
|
||||
} else {
|
||||
// Novo agendamento
|
||||
|
||||
const newAppointment = {
|
||||
...appointment,
|
||||
id: Date.now().toString(),
|
||||
@ -60,7 +60,7 @@ export default function AgendaPage() {
|
||||
};
|
||||
|
||||
const handleNotifyPatient = (patientId: string) => {
|
||||
// Lógica para notificar paciente
|
||||
|
||||
console.log(`Notificando paciente ${patientId}`);
|
||||
};
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// components/agendamento/AgendaCalendar.tsx (atualizado)
|
||||
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
@ -86,7 +86,7 @@ export default function AgendaCalendar({
|
||||
setCurrentDate(new Date());
|
||||
};
|
||||
|
||||
// Filtra os agendamentos por profissional selecionado
|
||||
|
||||
const filteredAppointments = selectedProfessional === 'all'
|
||||
? appointments
|
||||
: appointments.filter(app => app.professional === selectedProfessional);
|
||||
@ -187,7 +187,7 @@ export default function AgendaCalendar({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Visualização de Dia/Semana (calendário) */}
|
||||
{}
|
||||
{view !== 'month' && (
|
||||
<div className="overflow-auto">
|
||||
<div className="min-w-full">
|
||||
@ -256,7 +256,7 @@ export default function AgendaCalendar({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Visualização de Mês (lista) */}
|
||||
{}
|
||||
{view === 'month' && (
|
||||
<div className="p-4">
|
||||
<div className="space-y-4">
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// components/agendamento/ListaEspera.tsx
|
||||
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
@ -12,10 +12,10 @@ export function Footer() {
|
||||
<footer className="bg-background border-t border-border">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div className="flex flex-col md:flex-row items-center justify-between space-y-4 md:space-y-0">
|
||||
{/* Copyright */}
|
||||
{}
|
||||
<div className="text-muted-foreground text-sm">© 2025 SUS Conecta</div>
|
||||
|
||||
{/* Footer Links */}
|
||||
{}
|
||||
<nav className="flex items-center space-x-8">
|
||||
<a href="#" className="text-muted-foreground hover:text-primary transition-colors text-sm">
|
||||
Termos
|
||||
@ -28,7 +28,7 @@ export function Footer() {
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
{/* Back to Top Button */}
|
||||
{}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
/* src/components/forms/patient-registration-form.tsx */
|
||||
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
@ -101,7 +101,7 @@ export function PatientRegistrationForm({
|
||||
|
||||
const title = useMemo(() => (mode === "create" ? "Cadastro de Paciente" : "Editar Paciente"), [mode]);
|
||||
|
||||
// Edição
|
||||
|
||||
useEffect(() => {
|
||||
async function load() {
|
||||
if (mode !== "edit" || patientId == null) return;
|
||||
@ -129,7 +129,7 @@ export function PatientRegistrationForm({
|
||||
const ax = await listarAnexos(String(patientId)).catch(() => []);
|
||||
setServerAnexos(Array.isArray(ax) ? ax : []);
|
||||
} catch {
|
||||
// ignora
|
||||
|
||||
}
|
||||
}
|
||||
load();
|
||||
@ -208,7 +208,7 @@ export function PatientRegistrationForm({
|
||||
ev.preventDefault();
|
||||
if (!validateLocal()) return;
|
||||
|
||||
// validação externa do CPF (mock → pode falhar, tratamos erro legível)
|
||||
|
||||
try {
|
||||
const { valido, existe } = await validarCPF(form.cpf);
|
||||
if (!valido) {
|
||||
@ -220,7 +220,7 @@ export function PatientRegistrationForm({
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// se o mock der 404/timeout, seguimos sem bloquear
|
||||
|
||||
}
|
||||
|
||||
setSubmitting(true);
|
||||
@ -318,7 +318,7 @@ export function PatientRegistrationForm({
|
||||
)}
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{/* DADOS PESSOAIS */}
|
||||
{}
|
||||
<Collapsible open={expanded.dados} onOpenChange={() => setExpanded((s) => ({ ...s, dados: !s.dados }))}>
|
||||
<Card>
|
||||
<CollapsibleTrigger asChild>
|
||||
@ -337,7 +337,7 @@ export function PatientRegistrationForm({
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-24 h-24 border-2 border-dashed border-muted-foreground rounded-lg flex items-center justify-center overflow-hidden">
|
||||
{photoPreview ? (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
|
||||
<img src={photoPreview} alt="Preview" className="w-full h-full object-cover" />
|
||||
) : (
|
||||
<FileImage className="h-8 w-8 text-muted-foreground" />
|
||||
@ -420,7 +420,7 @@ export function PatientRegistrationForm({
|
||||
</Card>
|
||||
</Collapsible>
|
||||
|
||||
{/* CONTATO */}
|
||||
{}
|
||||
<Collapsible open={expanded.contato} onOpenChange={() => setExpanded((s) => ({ ...s, contato: !s.contato }))}>
|
||||
<Card>
|
||||
<CollapsibleTrigger asChild>
|
||||
@ -448,7 +448,7 @@ export function PatientRegistrationForm({
|
||||
</Card>
|
||||
</Collapsible>
|
||||
|
||||
{/* ENDEREÇO */}
|
||||
{}
|
||||
<Collapsible open={expanded.endereco} onOpenChange={() => setExpanded((s) => ({ ...s, endereco: !s.endereco }))}>
|
||||
<Card>
|
||||
<CollapsibleTrigger asChild>
|
||||
@ -517,7 +517,7 @@ export function PatientRegistrationForm({
|
||||
</Card>
|
||||
</Collapsible>
|
||||
|
||||
{/* OBSERVAÇÕES & ANEXOS */}
|
||||
{}
|
||||
<Collapsible open={expanded.obs} onOpenChange={() => setExpanded((s) => ({ ...s, obs: !s.obs }))}>
|
||||
<Card>
|
||||
<CollapsibleTrigger asChild>
|
||||
@ -584,7 +584,7 @@ export function PatientRegistrationForm({
|
||||
</Card>
|
||||
</Collapsible>
|
||||
|
||||
{/* AÇÕES */}
|
||||
{}
|
||||
<div className="flex justify-end gap-4 pt-6 border-t">
|
||||
<Button type="button" variant="outline" onClick={() => (inline ? onClose?.() : onOpenChange?.(false))} disabled={isSubmitting}>
|
||||
<XCircle className="mr-2 h-4 w-4" />
|
||||
|
||||
@ -19,7 +19,7 @@ export function Header() {
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
{}
|
||||
<nav className="hidden md:flex items-center space-x-8">
|
||||
<Link
|
||||
href="/"
|
||||
@ -32,7 +32,7 @@ export function Header() {
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
{/* Desktop Action Buttons */}
|
||||
{}
|
||||
<div className="hidden md:flex items-center space-x-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
@ -53,13 +53,13 @@ export function Header() {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
{}
|
||||
<button className="md:hidden p-2" onClick={() => setIsMenuOpen(!isMenuOpen)} aria-label="Toggle menu">
|
||||
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
{}
|
||||
{isMenuOpen && (
|
||||
<div className="md:hidden py-4 border-t border-border">
|
||||
<nav className="flex flex-col space-y-4">
|
||||
|
||||
@ -7,7 +7,7 @@ export function HeroSection() {
|
||||
<section className="py-16 lg:py-24 bg-background">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||||
{/* Content */}
|
||||
{}
|
||||
<div className="space-y-8">
|
||||
<div className="space-y-4">
|
||||
<div className="inline-block px-4 py-2 bg-accent/10 text-accent rounded-full text-sm font-medium">
|
||||
@ -23,7 +23,7 @@ export function HeroSection() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
{}
|
||||
<div className="flex flex-col sm:flex-row gap-4">
|
||||
<Button size="lg" className="bg-primary hover:bg-primary/90 text-primary-foreground">
|
||||
Sou Paciente
|
||||
@ -38,7 +38,7 @@ export function HeroSection() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Hero Image */}
|
||||
{}
|
||||
<div className="relative">
|
||||
<div className="relative rounded-2xl overflow-hidden bg-gradient-to-br from-accent/20 to-primary/20 p-8">
|
||||
<img
|
||||
@ -50,7 +50,7 @@ export function HeroSection() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Features */}
|
||||
{}
|
||||
<div className="mt-16 grid md:grid-cols-3 gap-8">
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className="flex-shrink-0 w-8 h-8 bg-primary/10 rounded-full flex items-center justify-center">
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// hooks/useAgenda.ts
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
export interface Appointment {
|
||||
@ -42,10 +42,10 @@ export const useAgenda = () => {
|
||||
|
||||
const handleSaveAppointment = (appointment: Appointment) => {
|
||||
if (appointment.id) {
|
||||
// Editar agendamento existente
|
||||
|
||||
setAppointments(prev => prev.map(a => a.id === appointment.id ? appointment : a));
|
||||
} else {
|
||||
// Novo agendamento
|
||||
|
||||
const newAppointment = {
|
||||
...appointment,
|
||||
id: Date.now().toString(),
|
||||
@ -70,7 +70,7 @@ export const useAgenda = () => {
|
||||
};
|
||||
|
||||
const handleNotifyPatient = (patientId: string) => {
|
||||
// Lógica para notificar paciente
|
||||
|
||||
console.log(`Notificando paciente ${patientId}`);
|
||||
};
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
// Inspired by react-hot-toast library
|
||||
|
||||
import * as React from "react"
|
||||
|
||||
import type {
|
||||
@ -93,8 +93,7 @@ export const reducer = (state: State, action: Action): State => {
|
||||
case "DISMISS_TOAST": {
|
||||
const { toastId } = action
|
||||
|
||||
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
||||
// but I'll keep it here for simplicity
|
||||
|
||||
if (toastId) {
|
||||
addToRemoveQueue(toastId)
|
||||
} else {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
/* src/lib/api.ts */
|
||||
|
||||
|
||||
export type ApiOk<T = any> = {
|
||||
success: boolean;
|
||||
@ -58,9 +58,7 @@ export type PacienteInput = {
|
||||
observacoes?: string | null;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Config & helpers
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
const API_BASE = process.env.NEXT_PUBLIC_API_BASE ?? "https://mock.apidog.com/m1/1053378-0-default";
|
||||
|
||||
@ -97,21 +95,20 @@ async function parse<T>(res: Response): Promise<T> {
|
||||
try {
|
||||
json = await res.json();
|
||||
} catch {
|
||||
// ignore
|
||||
|
||||
}
|
||||
if (!res.ok) {
|
||||
const code = json?.apidogError?.code ?? res.status;
|
||||
const msg = json?.apidogError?.message ?? res.statusText;
|
||||
throw new Error(`${code}: ${msg}`);
|
||||
}
|
||||
// muitos endpoints do mock respondem { success, data }
|
||||
|
||||
return (json?.data ?? json) as T;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Pacientes (CRUD)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
export async function listarPacientes(params?: { page?: number; limit?: number; q?: string }): Promise<Paciente[]> {
|
||||
const query = new URLSearchParams();
|
||||
if (params?.page) query.set("page", String(params.page));
|
||||
@ -156,9 +153,9 @@ export async function excluirPaciente(id: string | number): Promise<void> {
|
||||
logAPI("excluirPaciente", { url, result: { ok: true } });
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Foto
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
export async function uploadFotoPaciente(id: string | number, file: File): Promise<{ foto_url?: string; thumbnail_url?: string }> {
|
||||
const url = `${API_BASE}${PATHS.foto(id)}`;
|
||||
@ -178,9 +175,9 @@ export async function removerFotoPaciente(id: string | number): Promise<void> {
|
||||
logAPI("removerFotoPaciente", { url, result: { ok: true } });
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Anexos
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
export async function listarAnexos(id: string | number): Promise<any[]> {
|
||||
const url = `${API_BASE}${PATHS.anexos(id)}`;
|
||||
@ -193,7 +190,7 @@ export async function listarAnexos(id: string | number): Promise<any[]> {
|
||||
export async function adicionarAnexo(id: string | number, file: File): Promise<any> {
|
||||
const url = `${API_BASE}${PATHS.anexos(id)}`;
|
||||
const fd = new FormData();
|
||||
// alguns mocks usam "arquivo" e outros "file"; tentamos ambos
|
||||
|
||||
fd.append("arquivo", file);
|
||||
const res = await fetch(url, { method: "POST", body: fd, headers: headers("form") });
|
||||
const data = await parse<ApiOk<any>>(res);
|
||||
@ -208,9 +205,9 @@ export async function removerAnexo(id: string | number, anexoId: string | number
|
||||
logAPI("removerAnexo", { url, result: { ok: true } });
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Validações
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
export async function validarCPF(cpf: string): Promise<{ valido: boolean; existe: boolean; paciente_id: string | null }> {
|
||||
const url = `${API_BASE}${PATHS.validarCPF}`;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
@import 'tailwindcss';
|
||||
@import 'tw-animate-css';
|
||||
/* Removed invalid @custom-variant at-rule */
|
||||
|
||||
|
||||
:root {
|
||||
--background: var(--primary)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user