737 lines
29 KiB
TypeScript
737 lines
29 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import toast from "react-hot-toast";
|
|
import { Search, Plus, Eye, Calendar, Edit, Trash2, X } from "lucide-react";
|
|
import {
|
|
doctorService,
|
|
userService,
|
|
type Doctor,
|
|
type CrmUF,
|
|
} from "../../services";
|
|
import type { CreateDoctorInput } from "../../services/users/types";
|
|
|
|
interface DoctorFormData {
|
|
id?: string;
|
|
full_name: string;
|
|
cpf: string;
|
|
email: string;
|
|
phone_mobile: string;
|
|
crm: string;
|
|
crm_uf: string;
|
|
specialty: string;
|
|
birth_date?: string;
|
|
}
|
|
|
|
const UF_OPTIONS = [
|
|
"AC",
|
|
"AL",
|
|
"AP",
|
|
"AM",
|
|
"BA",
|
|
"CE",
|
|
"DF",
|
|
"ES",
|
|
"GO",
|
|
"MA",
|
|
"MT",
|
|
"MS",
|
|
"MG",
|
|
"PA",
|
|
"PB",
|
|
"PR",
|
|
"PE",
|
|
"PI",
|
|
"RJ",
|
|
"RN",
|
|
"RS",
|
|
"RO",
|
|
"RR",
|
|
"SC",
|
|
"SP",
|
|
"SE",
|
|
"TO",
|
|
];
|
|
|
|
// Helper para formatar nome do médico sem duplicar "Dr."
|
|
const formatDoctorName = (fullName: string): string => {
|
|
const name = fullName.trim();
|
|
// Verifica se já começa com Dr. ou Dr (case insensitive)
|
|
if (/^dr\.?\s/i.test(name)) {
|
|
return name;
|
|
}
|
|
return `Dr. ${name}`;
|
|
};
|
|
|
|
export function SecretaryDoctorList({
|
|
onOpenSchedule,
|
|
}: {
|
|
onOpenSchedule?: (doctorId: string) => void;
|
|
}) {
|
|
const [doctors, setDoctors] = useState<Doctor[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const [searchTerm, setSearchTerm] = useState("");
|
|
const [specialtyFilter, setSpecialtyFilter] = useState("Todas");
|
|
const [currentPage, setCurrentPage] = useState(1);
|
|
const [itemsPerPage] = useState(10);
|
|
|
|
// Modal states
|
|
const [showModal, setShowModal] = useState(false);
|
|
const [modalMode, setModalMode] = useState<"create" | "edit">("create");
|
|
const [formData, setFormData] = useState<DoctorFormData>({
|
|
full_name: "",
|
|
cpf: "",
|
|
email: "",
|
|
phone_mobile: "",
|
|
crm: "",
|
|
crm_uf: "",
|
|
specialty: "",
|
|
});
|
|
const [showViewModal, setShowViewModal] = useState(false);
|
|
const [selectedDoctor, setSelectedDoctor] = useState<Doctor | null>(null);
|
|
|
|
const loadDoctors = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const data = await doctorService.list();
|
|
setDoctors(Array.isArray(data) ? data : []);
|
|
} catch (error) {
|
|
console.error("Erro ao carregar médicos:", error);
|
|
toast.error("Erro ao carregar médicos");
|
|
setDoctors([]);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
loadDoctors();
|
|
}, []);
|
|
|
|
// Função de filtro
|
|
const filteredDoctors = doctors.filter((doctor) => {
|
|
// Filtro de busca por nome, CRM ou especialidade
|
|
const searchLower = searchTerm.toLowerCase();
|
|
const matchesSearch =
|
|
!searchTerm ||
|
|
doctor.full_name?.toLowerCase().includes(searchLower) ||
|
|
doctor.crm?.includes(searchTerm) ||
|
|
doctor.specialty?.toLowerCase().includes(searchLower);
|
|
|
|
// Filtro de especialidade
|
|
const matchesSpecialty =
|
|
specialtyFilter === "Todas" || doctor.specialty === specialtyFilter;
|
|
|
|
return matchesSearch && matchesSpecialty;
|
|
});
|
|
|
|
// Cálculos de paginação
|
|
const totalPages = Math.ceil(filteredDoctors.length / itemsPerPage);
|
|
const startIndex = (currentPage - 1) * itemsPerPage;
|
|
const endIndex = startIndex + itemsPerPage;
|
|
const paginatedDoctors = filteredDoctors.slice(startIndex, endIndex);
|
|
|
|
const handleSearch = () => {
|
|
loadDoctors();
|
|
};
|
|
|
|
const handleClear = () => {
|
|
setSearchTerm("");
|
|
setSpecialtyFilter("Todas");
|
|
setCurrentPage(1);
|
|
loadDoctors();
|
|
};
|
|
|
|
// Reset página quando filtros mudarem
|
|
useEffect(() => {
|
|
setCurrentPage(1);
|
|
}, [searchTerm, specialtyFilter]);
|
|
|
|
const handleNewDoctor = () => {
|
|
setModalMode("create");
|
|
setFormData({
|
|
full_name: "",
|
|
cpf: "",
|
|
email: "",
|
|
phone_mobile: "",
|
|
crm: "",
|
|
crm_uf: "",
|
|
specialty: "",
|
|
});
|
|
setShowModal(true);
|
|
};
|
|
|
|
const handleEditDoctor = (doctor: Doctor) => {
|
|
setModalMode("edit");
|
|
setFormData({
|
|
id: doctor.id,
|
|
full_name: doctor.full_name || "",
|
|
cpf: doctor.cpf || "",
|
|
email: doctor.email || "",
|
|
phone_mobile: doctor.phone_mobile || "",
|
|
crm: doctor.crm || "",
|
|
crm_uf: doctor.crm_uf || "",
|
|
specialty: doctor.specialty || "",
|
|
birth_date: doctor.birth_date || "",
|
|
});
|
|
setShowModal(true);
|
|
};
|
|
|
|
const handleFormSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
|
|
try {
|
|
if (modalMode === "edit" && formData.id) {
|
|
// Para edição, usa o endpoint antigo (PATCH /doctors/:id)
|
|
// Remove formatação de telefone e CPF
|
|
const cleanPhone = formData.phone_mobile
|
|
? formData.phone_mobile.replace(/\D/g, "")
|
|
: undefined;
|
|
const cleanCpf = formData.cpf.replace(/\D/g, "");
|
|
|
|
const doctorData = {
|
|
full_name: formData.full_name,
|
|
cpf: cleanCpf,
|
|
email: formData.email,
|
|
phone_mobile: cleanPhone,
|
|
crm: formData.crm,
|
|
crm_uf: formData.crm_uf as CrmUF,
|
|
specialty: formData.specialty,
|
|
birth_date: formData.birth_date || null,
|
|
};
|
|
await doctorService.update(formData.id, doctorData);
|
|
toast.success("Médico atualizado com sucesso!");
|
|
} else {
|
|
// Para criação, usa o novo endpoint create-doctor com validações completas
|
|
// Remove formatação de telefone e CPF
|
|
const cleanPhone = formData.phone_mobile
|
|
? formData.phone_mobile.replace(/\D/g, "")
|
|
: undefined;
|
|
const cleanCpf = formData.cpf.replace(/\D/g, "");
|
|
|
|
const createData: CreateDoctorInput = {
|
|
email: formData.email,
|
|
full_name: formData.full_name,
|
|
cpf: cleanCpf,
|
|
crm: formData.crm,
|
|
crm_uf: formData.crm_uf as CrmUF,
|
|
specialty: formData.specialty || undefined,
|
|
phone_mobile: cleanPhone,
|
|
};
|
|
|
|
await userService.createDoctor(createData);
|
|
toast.success("Médico cadastrado com sucesso!");
|
|
}
|
|
|
|
setShowModal(false);
|
|
loadDoctors();
|
|
} catch (error) {
|
|
console.error("Erro ao salvar médico:", error);
|
|
toast.error("Erro ao salvar médico");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const getInitials = (name: string) => {
|
|
return name
|
|
.split(" ")
|
|
.map((n) => n[0])
|
|
.join("")
|
|
.toUpperCase()
|
|
.slice(0, 2);
|
|
};
|
|
|
|
const getAvatarColor = (index: number) => {
|
|
const colors = [
|
|
"bg-red-500",
|
|
"bg-green-500",
|
|
"bg-blue-500",
|
|
"bg-yellow-500",
|
|
"bg-purple-500",
|
|
"bg-pink-500",
|
|
"bg-indigo-500",
|
|
"bg-teal-500",
|
|
];
|
|
return colors[index % colors.length];
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-3xl font-bold text-gray-900 dark:text-gray-100">Médicos</h1>
|
|
<p className="text-gray-600 dark:text-gray-400 mt-1">Gerencie os médicos cadastrados</p>
|
|
</div>
|
|
<button
|
|
onClick={handleNewDoctor}
|
|
className="flex items-center gap-2 px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors"
|
|
>
|
|
<Plus className="h-4 w-4" />
|
|
Novo Médico
|
|
</button>
|
|
</div>
|
|
|
|
{/* Search and Filters */}
|
|
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6 space-y-4">
|
|
<div className="flex gap-3">
|
|
<div className="flex-1 relative">
|
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400 dark:text-gray-500" />
|
|
<input
|
|
type="text"
|
|
placeholder="Buscar médicos por nome ou CRM..."
|
|
value={searchTerm}
|
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
className="w-full pl-10 pr-4 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400"
|
|
/>
|
|
</div>
|
|
<button
|
|
onClick={handleSearch}
|
|
className="px-6 py-2.5 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors"
|
|
>
|
|
Buscar
|
|
</button>
|
|
<button
|
|
onClick={handleClear}
|
|
className="px-6 py-2.5 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
|
>
|
|
Limpar
|
|
</button>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-sm text-gray-600 dark:text-gray-400">Especialidade:</span>
|
|
<select
|
|
value={specialtyFilter}
|
|
onChange={(e) => setSpecialtyFilter(e.target.value)}
|
|
className="px-3 py-1.5 border border-gray-300 dark:border-gray-600 rounded-lg text-sm focus:ring-2 focus:ring-green-500 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
|
|
>
|
|
<option>Todas</option>
|
|
<option>Cardiologia</option>
|
|
<option>Dermatologia</option>
|
|
<option>Ortopedia</option>
|
|
<option>Pediatria</option>
|
|
<option>Psiquiatria</option>
|
|
<option>Ginecologia</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Table */}
|
|
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden">
|
|
<table className="w-full">
|
|
<thead className="bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600">
|
|
<tr>
|
|
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
|
Médico
|
|
</th>
|
|
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
|
Especialidade
|
|
</th>
|
|
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
|
CRM
|
|
</th>
|
|
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
|
Próxima Disponível
|
|
</th>
|
|
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
|
Ações
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
|
|
{loading ? (
|
|
<tr>
|
|
<td
|
|
colSpan={5}
|
|
className="px-6 py-12 text-center text-gray-500 dark:text-gray-400"
|
|
>
|
|
Carregando médicos...
|
|
</td>
|
|
</tr>
|
|
) : filteredDoctors.length === 0 ? (
|
|
<tr>
|
|
<td
|
|
colSpan={5}
|
|
className="px-6 py-12 text-center text-gray-500 dark:text-gray-400"
|
|
>
|
|
{searchTerm || specialtyFilter !== "Todas"
|
|
? "Nenhum médico encontrado com esses filtros"
|
|
: "Nenhum médico encontrado"}
|
|
</td>
|
|
</tr>
|
|
) : (
|
|
paginatedDoctors.map((doctor, index) => (
|
|
<tr
|
|
key={doctor.id}
|
|
className="hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
|
>
|
|
<td className="px-6 py-4">
|
|
<div className="flex items-center gap-3">
|
|
<div
|
|
className={`w-10 h-10 rounded-full ${getAvatarColor(
|
|
index
|
|
)} flex items-center justify-center text-white font-semibold text-sm`}
|
|
>
|
|
{getInitials(doctor.full_name || "")}
|
|
</div>
|
|
<div>
|
|
<p className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
{formatDoctorName(doctor.full_name)}
|
|
</p>
|
|
<p className="text-sm text-gray-500 dark:text-gray-400">{doctor.email}</p>
|
|
<p className="text-sm text-gray-500 dark:text-gray-400">
|
|
{doctor.phone_mobile}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td className="px-6 py-4 text-sm text-gray-700 dark:text-gray-300">
|
|
{doctor.specialty || "—"}
|
|
</td>
|
|
<td className="px-6 py-4 text-sm text-gray-700 dark:text-gray-300">
|
|
{doctor.crm || "—"}
|
|
</td>
|
|
<td className="px-6 py-4 text-sm text-gray-700">
|
|
{/* TODO: Buscar próxima disponibilidade */}—
|
|
</td>
|
|
<td className="px-6 py-4">
|
|
<div className="flex items-center gap-2">
|
|
<button
|
|
onClick={() => {
|
|
setSelectedDoctor(doctor);
|
|
setShowViewModal(true);
|
|
}}
|
|
title="Visualizar"
|
|
className="p-2 text-blue-600 hover:bg-blue-50 rounded-lg transition-colors"
|
|
>
|
|
<Eye className="h-4 w-4" />
|
|
</button>
|
|
<button
|
|
onClick={() => {
|
|
// Prefer callback from parent to switch tab; fallback to sessionStorage
|
|
if (onOpenSchedule) {
|
|
onOpenSchedule(doctor.id);
|
|
} else {
|
|
sessionStorage.setItem("selectedDoctorForSchedule", doctor.id);
|
|
// dispatch a custom event to inform parent (optional)
|
|
window.dispatchEvent(new CustomEvent("open-doctor-schedule"));
|
|
}
|
|
}}
|
|
title="Gerenciar agenda"
|
|
className="p-2 text-green-600 hover:bg-green-50 rounded-lg transition-colors"
|
|
>
|
|
<Calendar className="h-4 w-4" />
|
|
</button>
|
|
<button
|
|
onClick={() => handleEditDoctor(doctor)}
|
|
title="Editar"
|
|
className="p-2 text-orange-600 hover:bg-orange-50 rounded-lg transition-colors"
|
|
>
|
|
<Edit className="h-4 w-4" />
|
|
</button>
|
|
<button
|
|
title="Deletar"
|
|
className="p-2 text-red-600 hover:bg-red-50 rounded-lg transition-colors"
|
|
>
|
|
<Trash2 className="h-4 w-4" />
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
))
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{/* Paginação */}
|
|
{filteredDoctors.length > 0 && (
|
|
<div className="flex items-center justify-between bg-white dark:bg-gray-800 px-6 py-4 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700">
|
|
<div className="text-sm text-gray-700 dark:text-gray-300">
|
|
Mostrando {startIndex + 1} até {Math.min(endIndex, filteredDoctors.length)} de {filteredDoctors.length} médicos
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<button
|
|
onClick={() => setCurrentPage((prev) => Math.max(prev - 1, 1))}
|
|
disabled={currentPage === 1}
|
|
className="px-4 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-gray-700 dark:text-gray-300"
|
|
>
|
|
Anterior
|
|
</button>
|
|
<div className="flex items-center gap-1">
|
|
{(() => {
|
|
const maxPagesToShow = 4;
|
|
let startPage = Math.max(1, currentPage - Math.floor(maxPagesToShow / 2));
|
|
let endPage = Math.min(totalPages, startPage + maxPagesToShow - 1);
|
|
|
|
if (endPage - startPage < maxPagesToShow - 1) {
|
|
startPage = Math.max(1, endPage - maxPagesToShow + 1);
|
|
}
|
|
|
|
const pages = [];
|
|
for (let i = startPage; i <= endPage; i++) {
|
|
pages.push(i);
|
|
}
|
|
|
|
return pages.map((page) => (
|
|
<button
|
|
key={page}
|
|
onClick={() => setCurrentPage(page)}
|
|
className={`px-3 py-2 text-sm rounded-lg transition-colors ${
|
|
currentPage === page
|
|
? "bg-green-600 text-white"
|
|
: "border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"
|
|
}`}
|
|
>
|
|
{page}
|
|
</button>
|
|
));
|
|
})()}
|
|
</div>
|
|
<button
|
|
onClick={() => setCurrentPage((prev) => Math.min(prev + 1, totalPages))}
|
|
disabled={currentPage === totalPages}
|
|
className="px-4 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-gray-700 dark:text-gray-300"
|
|
>
|
|
Próxima
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Modal de Formulário */}
|
|
{showModal && (
|
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
|
<div className="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-hidden flex flex-col">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between p-6 border-b border-gray-200">
|
|
<h2 className="text-xl font-semibold text-gray-900">
|
|
{modalMode === "create" ? "Novo Médico" : "Editar Médico"}
|
|
</h2>
|
|
<button
|
|
onClick={() => setShowModal(false)}
|
|
className="p-2 text-gray-400 hover:text-gray-600 rounded-lg transition-colors"
|
|
>
|
|
<X className="h-5 w-5" />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Form Content */}
|
|
<div className="flex-1 overflow-y-auto p-6">
|
|
<form onSubmit={handleFormSubmit} className="space-y-4">
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Nome Completo *
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={formData.full_name}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, full_name: e.target.value })
|
|
}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
|
required
|
|
placeholder="Dr. João Silva"
|
|
/>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
CPF *
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={formData.cpf}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, cpf: e.target.value })
|
|
}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
|
required
|
|
placeholder="000.000.000-00"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Data de Nascimento
|
|
</label>
|
|
<input
|
|
type="date"
|
|
value={formData.birth_date || ""}
|
|
onChange={(e) =>
|
|
setFormData({
|
|
...formData,
|
|
birth_date: e.target.value,
|
|
})
|
|
}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
CRM *
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={formData.crm}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, crm: e.target.value })
|
|
}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
|
required
|
|
placeholder="123456"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
UF do CRM *
|
|
</label>
|
|
<select
|
|
value={formData.crm_uf}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, crm_uf: e.target.value })
|
|
}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
|
required
|
|
>
|
|
<option value="">Selecione</option>
|
|
{UF_OPTIONS.map((uf) => (
|
|
<option key={uf} value={uf}>
|
|
{uf}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Especialidade
|
|
</label>
|
|
<select
|
|
value={formData.specialty}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, specialty: e.target.value })
|
|
}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
|
>
|
|
<option value="">Selecione</option>
|
|
<option value="Cardiologia">Cardiologia</option>
|
|
<option value="Dermatologia">Dermatologia</option>
|
|
<option value="Ortopedia">Ortopedia</option>
|
|
<option value="Pediatria">Pediatria</option>
|
|
<option value="Psiquiatria">Psiquiatria</option>
|
|
<option value="Ginecologia">Ginecologia</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Email *
|
|
</label>
|
|
<input
|
|
type="email"
|
|
value={formData.email}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, email: e.target.value })
|
|
}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
|
required
|
|
placeholder="medico@exemplo.com"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Telefone
|
|
</label>
|
|
<input
|
|
type="tel"
|
|
value={formData.phone_mobile}
|
|
onChange={(e) =>
|
|
setFormData({
|
|
...formData,
|
|
phone_mobile: e.target.value,
|
|
})
|
|
}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
|
placeholder="(11) 98888-8888"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex gap-3 pt-4">
|
|
<button
|
|
type="button"
|
|
onClick={() => setShowModal(false)}
|
|
className="flex-1 px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
|
>
|
|
Cancelar
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
className="flex-1 px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors disabled:opacity-50"
|
|
>
|
|
{loading ? "Salvando..." : "Salvar"}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Modal de Visualizar Médico */}
|
|
{showViewModal && selectedDoctor && (
|
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
|
<div className="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-hidden flex flex-col">
|
|
<div className="flex items-center justify-between p-6 border-b border-gray-200">
|
|
<h2 className="text-xl font-semibold text-gray-900">Visualizar Médico</h2>
|
|
<button
|
|
onClick={() => setShowViewModal(false)}
|
|
className="p-2 text-gray-400 hover:text-gray-600 rounded-lg transition-colors"
|
|
>
|
|
<X className="h-5 w-5" />
|
|
</button>
|
|
</div>
|
|
<div className="flex-1 overflow-y-auto p-6">
|
|
<div className="space-y-4">
|
|
<div>
|
|
<p className="text-sm text-gray-500">Nome</p>
|
|
<p className="text-gray-900 font-medium">{selectedDoctor.full_name}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Especialidade</p>
|
|
<p className="text-gray-900">{selectedDoctor.specialty || '—'}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">CRM</p>
|
|
<p className="text-gray-900">{selectedDoctor.crm || '—'}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Email</p>
|
|
<p className="text-gray-900">{selectedDoctor.email || '—'}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="p-6 border-t border-gray-200 flex justify-end gap-3">
|
|
<button
|
|
onClick={() => setShowViewModal(false)}
|
|
className="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
|
>
|
|
Fechar
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|