fix: menu acessibilidade todos os modos funcionais
This commit is contained in:
parent
f2a9dc7b70
commit
60c8b5eaa9
@ -24,6 +24,8 @@ export function SecretaryAppointmentList() {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [statusFilter, setStatusFilter] = useState("Todos");
|
||||
const [typeFilter, setTypeFilter] = useState("Todos");
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [itemsPerPage] = useState(10);
|
||||
const [showCreateModal, setShowCreateModal] = useState(false);
|
||||
const [modalMode, setModalMode] = useState<"create" | "edit">("create");
|
||||
const [selectedAppointment, setSelectedAppointment] = useState<
|
||||
@ -152,6 +154,12 @@ export function SecretaryAppointmentList() {
|
||||
return matchesSearch && matchesStatus && matchesType;
|
||||
});
|
||||
|
||||
// Cálculos de paginação
|
||||
const totalPages = Math.ceil(filteredAppointments.length / itemsPerPage);
|
||||
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||
const endIndex = startIndex + itemsPerPage;
|
||||
const paginatedAppointments = filteredAppointments.slice(startIndex, endIndex);
|
||||
|
||||
const loadDoctorsAndPatients = async () => {
|
||||
try {
|
||||
const [patientsData, doctorsData] = await Promise.all([
|
||||
@ -237,9 +245,15 @@ export function SecretaryAppointmentList() {
|
||||
setSearchTerm("");
|
||||
setStatusFilter("Todos");
|
||||
setTypeFilter("Todos");
|
||||
setCurrentPage(1);
|
||||
loadAppointments();
|
||||
};
|
||||
|
||||
// Reset página quando filtros mudarem
|
||||
useEffect(() => {
|
||||
setCurrentPage(1);
|
||||
}, [searchTerm, statusFilter, typeFilter]);
|
||||
|
||||
const getStatusBadge = (status: string) => {
|
||||
const statusMap: Record<string, { label: string; className: string }> = {
|
||||
confirmada: {
|
||||
@ -293,8 +307,8 @@ export function SecretaryAppointmentList() {
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Consultas</h1>
|
||||
<p className="text-gray-600 mt-1">Gerencie as consultas agendadas</p>
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-gray-100">Consultas</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">Gerencie as consultas agendadas</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleOpenCreateModal}
|
||||
@ -306,16 +320,16 @@ export function SecretaryAppointmentList() {
|
||||
</div>
|
||||
|
||||
{/* Search and Filters */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 space-y-4">
|
||||
<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" />
|
||||
<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 consultas por paciente ou médico..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||
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
|
||||
@ -326,7 +340,7 @@ export function SecretaryAppointmentList() {
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClear}
|
||||
className="px-6 py-2.5 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
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>
|
||||
@ -334,11 +348,11 @@ export function SecretaryAppointmentList() {
|
||||
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-gray-600">Status:</span>
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">Status:</span>
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => setStatusFilter(e.target.value)}
|
||||
className="px-3 py-1.5 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||
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>Todos</option>
|
||||
<option>Confirmada</option>
|
||||
@ -348,11 +362,11 @@ export function SecretaryAppointmentList() {
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-gray-600">Tipo:</span>
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">Tipo:</span>
|
||||
<select
|
||||
value={typeFilter}
|
||||
onChange={(e) => setTypeFilter(e.target.value)}
|
||||
className="px-3 py-1.5 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||
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>Todos</option>
|
||||
<option>Presencial</option>
|
||||
@ -363,36 +377,36 @@ export function SecretaryAppointmentList() {
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
||||
<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 border-b border-gray-200">
|
||||
<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 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
||||
Paciente
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
||||
<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 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
||||
Data/Hora
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
||||
Tipo
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
||||
Status
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
||||
<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">
|
||||
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{loading ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={6}
|
||||
className="px-6 py-12 text-center text-gray-500"
|
||||
className="px-6 py-12 text-center text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
Carregando consultas...
|
||||
</td>
|
||||
@ -401,7 +415,7 @@ export function SecretaryAppointmentList() {
|
||||
<tr>
|
||||
<td
|
||||
colSpan={6}
|
||||
className="px-6 py-12 text-center text-gray-500"
|
||||
className="px-6 py-12 text-center text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{searchTerm ||
|
||||
statusFilter !== "Todos" ||
|
||||
@ -411,10 +425,10 @@ export function SecretaryAppointmentList() {
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
filteredAppointments.map((appointment) => (
|
||||
paginatedAppointments.map((appointment) => (
|
||||
<tr
|
||||
key={appointment.id}
|
||||
className="hover:bg-gray-50 transition-colors"
|
||||
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">
|
||||
@ -425,11 +439,11 @@ export function SecretaryAppointmentList() {
|
||||
color="blue"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{appointment.patient?.full_name ||
|
||||
"Paciente não encontrado"}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{appointment.patient?.email || "—"}
|
||||
</p>
|
||||
</div>
|
||||
@ -444,7 +458,7 @@ export function SecretaryAppointmentList() {
|
||||
color="green"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{appointment.doctor?.full_name ||
|
||||
"Médico não encontrado"}
|
||||
</p>
|
||||
@ -515,6 +529,62 @@ export function SecretaryAppointmentList() {
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Paginação */}
|
||||
{filteredAppointments.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, filteredAppointments.length)} de {filteredAppointments.length} consultas
|
||||
</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);
|
||||
|
||||
// Ajusta startPage se estivermos próximos do fim
|
||||
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 Criar Consulta */}
|
||||
{showCreateModal && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||
|
||||
@ -70,6 +70,8 @@ export function SecretaryDoctorList({
|
||||
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);
|
||||
@ -121,6 +123,12 @@ export function SecretaryDoctorList({
|
||||
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();
|
||||
};
|
||||
@ -128,9 +136,15 @@ export function SecretaryDoctorList({
|
||||
const handleClear = () => {
|
||||
setSearchTerm("");
|
||||
setSpecialtyFilter("Todas");
|
||||
setCurrentPage(1);
|
||||
loadDoctors();
|
||||
};
|
||||
|
||||
// Reset página quando filtros mudarem
|
||||
useEffect(() => {
|
||||
setCurrentPage(1);
|
||||
}, [searchTerm, specialtyFilter]);
|
||||
|
||||
const handleNewDoctor = () => {
|
||||
setModalMode("create");
|
||||
setFormData({
|
||||
@ -246,8 +260,8 @@ export function SecretaryDoctorList({
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Médicos</h1>
|
||||
<p className="text-gray-600 mt-1">Gerencie os médicos cadastrados</p>
|
||||
<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}
|
||||
@ -259,16 +273,16 @@ export function SecretaryDoctorList({
|
||||
</div>
|
||||
|
||||
{/* Search and Filters */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 space-y-4">
|
||||
<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" />
|
||||
<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 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||
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
|
||||
@ -279,18 +293,18 @@ export function SecretaryDoctorList({
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClear}
|
||||
className="px-6 py-2.5 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
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">Especialidade:</span>
|
||||
<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 rounded-lg text-sm focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||
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>
|
||||
@ -304,33 +318,33 @@ export function SecretaryDoctorList({
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
||||
<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 border-b border-gray-200">
|
||||
<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 uppercase tracking-wider">
|
||||
<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 uppercase tracking-wider">
|
||||
<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 uppercase tracking-wider">
|
||||
<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 uppercase tracking-wider">
|
||||
<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 uppercase tracking-wider">
|
||||
<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">
|
||||
<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"
|
||||
className="px-6 py-12 text-center text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
Carregando médicos...
|
||||
</td>
|
||||
@ -339,7 +353,7 @@ export function SecretaryDoctorList({
|
||||
<tr>
|
||||
<td
|
||||
colSpan={5}
|
||||
className="px-6 py-12 text-center text-gray-500"
|
||||
className="px-6 py-12 text-center text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{searchTerm || specialtyFilter !== "Todas"
|
||||
? "Nenhum médico encontrado com esses filtros"
|
||||
@ -347,10 +361,10 @@ export function SecretaryDoctorList({
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
filteredDoctors.map((doctor, index) => (
|
||||
paginatedDoctors.map((doctor, index) => (
|
||||
<tr
|
||||
key={doctor.id}
|
||||
className="hover:bg-gray-50 transition-colors"
|
||||
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">
|
||||
@ -362,20 +376,20 @@ export function SecretaryDoctorList({
|
||||
{getInitials(doctor.full_name || "")}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<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">{doctor.email}</p>
|
||||
<p className="text-sm text-gray-500">
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
@ -431,6 +445,61 @@ export function SecretaryDoctorList({
|
||||
</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">
|
||||
|
||||
@ -460,22 +460,22 @@ export function SecretaryDoctorSchedule() {
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Agenda Médica</h1>
|
||||
<p className="text-gray-600 mt-1">
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-gray-100">Agenda Médica</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
Gerencie disponibilidades e exceções
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Doctor Selector */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Selecione o Médico
|
||||
</label>
|
||||
<select
|
||||
value={selectedDoctorId}
|
||||
onChange={(e) => setSelectedDoctorId(e.target.value)}
|
||||
className="w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||
className="w-full px-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"
|
||||
>
|
||||
{doctors.map((doctor) => (
|
||||
<option key={doctor.id} value={doctor.id}>
|
||||
@ -486,27 +486,27 @@ export function SecretaryDoctorSchedule() {
|
||||
</div>
|
||||
|
||||
{/* Calendar */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-lg font-semibold text-gray-900 capitalize">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 capitalize">
|
||||
{formatMonthYear(currentDate)}
|
||||
</h2>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={goToToday}
|
||||
className="px-4 py-2 text-sm border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
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 text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Hoje
|
||||
</button>
|
||||
<button
|
||||
onClick={previousMonth}
|
||||
className="p-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
className="p-2 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={nextMonth}
|
||||
className="p-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
className="p-2 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</button>
|
||||
@ -517,15 +517,15 @@ export function SecretaryDoctorSchedule() {
|
||||
<div className="mb-4 flex flex-wrap gap-3 text-xs">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-3 h-3 rounded bg-yellow-100 border border-yellow-300"></div>
|
||||
<span className="text-gray-600">Solicitada</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">Solicitada</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-3 h-3 rounded bg-green-100 border border-green-300"></div>
|
||||
<span className="text-gray-600">Confirmada</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">Confirmada</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-3 h-3 rounded bg-blue-100 border border-blue-300"></div>
|
||||
<span className="text-gray-600">Concluída</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">Concluída</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-3 h-3 rounded bg-red-100 border border-red-300"></div>
|
||||
@ -537,11 +537,11 @@ export function SecretaryDoctorSchedule() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-7 gap-px bg-gray-200 border border-gray-200 rounded-lg overflow-hidden">
|
||||
<div className="grid grid-cols-7 gap-px bg-gray-200 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg overflow-hidden">
|
||||
{["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"].map((day) => (
|
||||
<div
|
||||
key={day}
|
||||
className="bg-gray-50 px-2 py-3 text-center text-sm font-semibold text-gray-700"
|
||||
className="bg-gray-50 dark:bg-gray-700 px-2 py-3 text-center text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{day}
|
||||
</div>
|
||||
@ -549,15 +549,15 @@ export function SecretaryDoctorSchedule() {
|
||||
{calendarDays.map((day, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`bg-white p-2 min-h-[100px] ${
|
||||
className={`bg-white dark:bg-gray-800 p-2 min-h-[100px] ${
|
||||
day.isCurrentMonth ? "" : "opacity-40"
|
||||
} ${
|
||||
day.date.toDateString() === new Date().toDateString()
|
||||
? "bg-blue-50"
|
||||
? "bg-blue-50 dark:bg-blue-900"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<div className="text-sm text-gray-700 mb-1 font-medium">
|
||||
<div className="text-sm text-gray-700 dark:text-gray-300 mb-1 font-medium">
|
||||
{day.date.getDate()}
|
||||
</div>
|
||||
|
||||
@ -575,8 +575,8 @@ export function SecretaryDoctorSchedule() {
|
||||
key={`exc-${i}`}
|
||||
className={`text-xs p-1 rounded mb-1 truncate ${
|
||||
exc.kind === "bloqueio"
|
||||
? "bg-red-100 text-red-800"
|
||||
: "bg-purple-100 text-purple-800"
|
||||
? "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200"
|
||||
: "bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200"
|
||||
}`}
|
||||
title={tooltipText}
|
||||
>
|
||||
@ -598,14 +598,14 @@ export function SecretaryDoctorSchedule() {
|
||||
key={`apt-${i}`}
|
||||
className={`text-xs p-1 rounded mb-1 truncate ${
|
||||
apt.status === "requested"
|
||||
? "bg-yellow-100 text-yellow-800"
|
||||
? "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200"
|
||||
: apt.status === "confirmed"
|
||||
? "bg-green-100 text-green-800"
|
||||
? "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200"
|
||||
: apt.status === "completed"
|
||||
? "bg-blue-100 text-blue-800"
|
||||
? "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200"
|
||||
: apt.status === "cancelled"
|
||||
? "bg-gray-100 text-gray-600"
|
||||
: "bg-orange-100 text-orange-800"
|
||||
? "bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300"
|
||||
: "bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200"
|
||||
}`}
|
||||
title={`${time} - ${apt.patient_id}`}
|
||||
>
|
||||
|
||||
@ -52,6 +52,8 @@ export function SecretaryPatientList({
|
||||
const [insuranceFilter, setInsuranceFilter] = useState("Todos");
|
||||
const [showBirthdays, setShowBirthdays] = useState(false);
|
||||
const [showVIP, setShowVIP] = useState(false);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [itemsPerPage] = useState(10);
|
||||
|
||||
// Modal states
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
@ -153,6 +155,12 @@ export function SecretaryPatientList({
|
||||
return matchesSearch && matchesBirthday && matchesInsurance && matchesVIP;
|
||||
});
|
||||
|
||||
// Cálculos de paginação
|
||||
const totalPages = Math.ceil(filteredPatients.length / itemsPerPage);
|
||||
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||
const endIndex = startIndex + itemsPerPage;
|
||||
const paginatedPatients = filteredPatients.slice(startIndex, endIndex);
|
||||
|
||||
const handleSearch = () => {
|
||||
loadPatients();
|
||||
};
|
||||
@ -162,9 +170,15 @@ export function SecretaryPatientList({
|
||||
setInsuranceFilter("Todos");
|
||||
setShowBirthdays(false);
|
||||
setShowVIP(false);
|
||||
setCurrentPage(1);
|
||||
loadPatients();
|
||||
};
|
||||
|
||||
// Reset página quando filtros mudarem
|
||||
useEffect(() => {
|
||||
setCurrentPage(1);
|
||||
}, [searchTerm, insuranceFilter, showBirthdays, showVIP]);
|
||||
|
||||
const handleNewPatient = () => {
|
||||
setModalMode("create");
|
||||
setFormData({
|
||||
@ -399,8 +413,8 @@ export function SecretaryPatientList({
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Pacientes</h1>
|
||||
<p className="text-gray-600 mt-1">
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-gray-100">Pacientes</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
Gerencie os pacientes cadastrados
|
||||
</p>
|
||||
</div>
|
||||
@ -414,16 +428,16 @@ export function SecretaryPatientList({
|
||||
</div>
|
||||
|
||||
{/* Search and Filters */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 space-y-4">
|
||||
<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" />
|
||||
<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 pacientes por nome ou email..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||
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
|
||||
@ -434,7 +448,7 @@ export function SecretaryPatientList({
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClear}
|
||||
className="px-6 py-2.5 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
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>
|
||||
@ -448,7 +462,7 @@ export function SecretaryPatientList({
|
||||
onChange={(e) => setShowBirthdays(e.target.checked)}
|
||||
className="h-4 w-4 text-green-600 border-gray-300 rounded focus:ring-green-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">
|
||||
<span className="text-sm text-gray-700 dark:text-gray-300">
|
||||
Aniversariantes do mês
|
||||
</span>
|
||||
</label>
|
||||
@ -459,14 +473,14 @@ export function SecretaryPatientList({
|
||||
onChange={(e) => setShowVIP(e.target.checked)}
|
||||
className="h-4 w-4 text-green-600 border-gray-300 rounded focus:ring-green-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">Somente VIP</span>
|
||||
<span className="text-sm text-gray-700 dark:text-gray-300">Somente VIP</span>
|
||||
</label>
|
||||
<div className="flex items-center gap-2 ml-auto">
|
||||
<span className="text-sm text-gray-600">Convênio:</span>
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">Convênio:</span>
|
||||
<select
|
||||
value={insuranceFilter}
|
||||
onChange={(e) => setInsuranceFilter(e.target.value)}
|
||||
className="px-3 py-1.5 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||
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>Todos</option>
|
||||
<option>Particular</option>
|
||||
@ -479,30 +493,30 @@ export function SecretaryPatientList({
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
||||
<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 border-b border-gray-200">
|
||||
<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 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
||||
Paciente
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
||||
Próximo Atendimento
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
||||
Convênio
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
||||
<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">
|
||||
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{loading ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={4}
|
||||
className="px-6 py-12 text-center text-gray-500"
|
||||
className="px-6 py-12 text-center text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
Carregando pacientes...
|
||||
</td>
|
||||
@ -511,7 +525,7 @@ export function SecretaryPatientList({
|
||||
<tr>
|
||||
<td
|
||||
colSpan={4}
|
||||
className="px-6 py-12 text-center text-gray-500"
|
||||
className="px-6 py-12 text-center text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{searchTerm
|
||||
? "Nenhum paciente encontrado com esse termo"
|
||||
@ -519,10 +533,10 @@ export function SecretaryPatientList({
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
filteredPatients.map((patient, index) => (
|
||||
paginatedPatients.map((patient, index) => (
|
||||
<tr
|
||||
key={patient.id}
|
||||
className="hover:bg-gray-50 transition-colors"
|
||||
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">
|
||||
@ -533,20 +547,20 @@ export function SecretaryPatientList({
|
||||
color={getPatientColor(index)}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{patient.full_name}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500">{patient.email}</p>
|
||||
<p className="text-sm text-gray-500">
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">{patient.email}</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{patient.phone_mobile}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-700">
|
||||
<td className="px-6 py-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
{/* TODO: Buscar próximo agendamento */}—
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-700">
|
||||
<td className="px-6 py-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
{(patient as any).convenio || "Particular"}
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
@ -588,6 +602,61 @@ export function SecretaryPatientList({
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Paginação */}
|
||||
{filteredPatients.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, filteredPatients.length)} de {filteredPatients.length} pacientes
|
||||
</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">
|
||||
|
||||
@ -335,8 +335,8 @@ export function SecretaryReportList() {
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Relatórios</h1>
|
||||
<p className="text-gray-600 mt-1">
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-gray-100">Relatórios</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
Visualize e baixe relatórios do sistema
|
||||
</p>
|
||||
</div>
|
||||
@ -350,16 +350,16 @@ export function SecretaryReportList() {
|
||||
</div>
|
||||
|
||||
{/* Search and Filters */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 space-y-4">
|
||||
<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" />
|
||||
<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 relatórios..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||
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
|
||||
@ -370,7 +370,7 @@ export function SecretaryReportList() {
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClear}
|
||||
className="px-6 py-2.5 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
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>
|
||||
@ -378,11 +378,11 @@ export function SecretaryReportList() {
|
||||
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-gray-600">Status:</span>
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">Status:</span>
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => setStatusFilter(e.target.value)}
|
||||
className="px-3 py-1.5 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-green-500 focus:border-transparent"
|
||||
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 value="">Todos</option>
|
||||
<option value="draft">Rascunho</option>
|
||||
@ -395,33 +395,33 @@ export function SecretaryReportList() {
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
||||
<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 border-b border-gray-200">
|
||||
<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 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
||||
Relatório
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
||||
Status
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
||||
Criado Em
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider">
|
||||
Solicitante
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
||||
<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">
|
||||
<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"
|
||||
className="px-6 py-12 text-center text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
Carregando relatórios...
|
||||
</td>
|
||||
@ -430,7 +430,7 @@ export function SecretaryReportList() {
|
||||
<tr>
|
||||
<td
|
||||
colSpan={5}
|
||||
className="px-6 py-12 text-center text-gray-500"
|
||||
className="px-6 py-12 text-center text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
Nenhum relatório encontrado
|
||||
</td>
|
||||
@ -439,18 +439,18 @@ export function SecretaryReportList() {
|
||||
reports.map((report) => (
|
||||
<tr
|
||||
key={report.id}
|
||||
className="hover:bg-gray-50 transition-colors"
|
||||
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="p-2 bg-blue-100 rounded-lg">
|
||||
<FileText className="h-5 w-5 text-blue-600" />
|
||||
<div className="p-2 bg-blue-100 dark:bg-blue-900 rounded-lg">
|
||||
<FileText className="h-5 w-5 text-blue-600 dark:text-blue-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{report.order_number}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{report.exam || "Sem exame"}
|
||||
</p>
|
||||
</div>
|
||||
@ -460,12 +460,12 @@ export function SecretaryReportList() {
|
||||
<span
|
||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||
report.status === "completed"
|
||||
? "bg-green-100 text-green-800"
|
||||
? "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200"
|
||||
: report.status === "pending"
|
||||
? "bg-yellow-100 text-yellow-800"
|
||||
? "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200"
|
||||
: report.status === "draft"
|
||||
? "bg-gray-100 text-gray-800"
|
||||
: "bg-red-100 text-red-800"
|
||||
? "bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200"
|
||||
: "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200"
|
||||
}`}
|
||||
>
|
||||
{report.status === "completed"
|
||||
@ -477,7 +477,7 @@ export function SecretaryReportList() {
|
||||
: "Cancelado"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-700">
|
||||
<td className="px-6 py-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
{formatDate(report.created_at)}
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-700">
|
||||
|
||||
262
src/index.css
262
src/index.css
@ -65,11 +65,32 @@ html.reduced-motion *::after {
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
|
||||
/* Filtro de luz azul (aplica matiz e tonalidade amarelada) */
|
||||
/* Filtro de luz azul (modo mais "padrão" com tom amarelado suave) */
|
||||
html.low-blue-light body {
|
||||
/* Mais quente: mais sepia e matiz mais próximo do laranja */
|
||||
filter: sepia(40%) hue-rotate(315deg) saturate(85%) brightness(98%);
|
||||
/* Filtro de luz azul (aplica overlay amarelada sem quebrar position: fixed) */
|
||||
html.low-blue-light {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
html.low-blue-light::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(255, 220, 150, 0.25),
|
||||
rgba(255, 200, 120, 0.25)
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 999999;
|
||||
mix-blend-mode: multiply;
|
||||
}
|
||||
|
||||
/* Garante que o menu de acessibilidade fique acima do filtro */
|
||||
html.low-blue-light button[aria-label="Menu de Acessibilidade"],
|
||||
html.low-blue-light [role="dialog"][aria-modal="true"] {
|
||||
z-index: 9999999 !important;
|
||||
}
|
||||
|
||||
/* Modo foco: destaque reforçado no elemento focado, sem quebrar layout */
|
||||
@ -137,50 +158,259 @@ html.focus-mode.dark *:focus-visible,
|
||||
}
|
||||
}
|
||||
|
||||
/* Estilos de Acessibilidade */
|
||||
/* Estilos de Acessibilidade - Alto Contraste */
|
||||
.high-contrast {
|
||||
--tw-bg-opacity: 1;
|
||||
}
|
||||
|
||||
.high-contrast body {
|
||||
background-color: #000 !important;
|
||||
color: #fff !important;
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
.high-contrast .bg-white {
|
||||
/* Backgrounds brancos/claros viram pretos */
|
||||
.high-contrast .bg-white,
|
||||
.high-contrast .bg-gray-50,
|
||||
.high-contrast .bg-gray-100 {
|
||||
background-color: #000 !important;
|
||||
color: #fff !important;
|
||||
border: 2px solid #fff !important;
|
||||
color: #ffff00 !important;
|
||||
border-color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Backgrounds escuros ficam pretos */
|
||||
.high-contrast .bg-gray-800,
|
||||
.high-contrast .bg-gray-900 {
|
||||
background-color: #000 !important;
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Textos cinzas ficam amarelos */
|
||||
.high-contrast .text-gray-400,
|
||||
.high-contrast .text-gray-500,
|
||||
.high-contrast .text-gray-600,
|
||||
.high-contrast .text-gray-700,
|
||||
.high-contrast .text-gray-800,
|
||||
.high-contrast .text-gray-900 {
|
||||
color: #fff !important;
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Textos brancos ficam amarelos */
|
||||
.high-contrast .text-white,
|
||||
.high-contrast .text-gray-100 {
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Botões primários (verde/azul) */
|
||||
.high-contrast .bg-blue-600,
|
||||
.high-contrast .bg-blue-500,
|
||||
.high-contrast .bg-green-600 {
|
||||
.high-contrast .bg-green-600,
|
||||
.high-contrast .bg-green-700 {
|
||||
background-color: #ffff00 !important;
|
||||
color: #000 !important;
|
||||
border: 2px solid #000 !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.high-contrast a,
|
||||
.high-contrast button:not(.bg-red-500) {
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
/* Botões com bordas */
|
||||
.high-contrast .border-gray-300,
|
||||
.high-contrast .border-gray-600,
|
||||
.high-contrast .border-gray-200,
|
||||
.high-contrast .border-gray-700 {
|
||||
border-color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Links e botões secundários */
|
||||
.high-contrast a {
|
||||
color: #ffff00 !important;
|
||||
text-decoration: underline !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.high-contrast button {
|
||||
border: 2px solid #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Inputs e selects */
|
||||
.high-contrast input,
|
||||
.high-contrast select,
|
||||
.high-contrast textarea {
|
||||
background-color: #fff !important;
|
||||
color: #000 !important;
|
||||
border: 3px solid #000 !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.high-contrast input::placeholder,
|
||||
.high-contrast textarea::placeholder {
|
||||
color: #666 !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
/* Badges e status */
|
||||
.high-contrast .bg-green-100,
|
||||
.high-contrast .bg-blue-100,
|
||||
.high-contrast .bg-yellow-100,
|
||||
.high-contrast .bg-red-100,
|
||||
.high-contrast .bg-purple-100,
|
||||
.high-contrast .bg-orange-100 {
|
||||
background-color: #ffff00 !important;
|
||||
color: #000 !important;
|
||||
border: 2px solid #000 !important;
|
||||
}
|
||||
|
||||
/* Tabelas */
|
||||
.high-contrast table {
|
||||
border: 2px solid #ffff00 !important;
|
||||
}
|
||||
|
||||
.high-contrast th,
|
||||
.high-contrast td {
|
||||
border: 1px solid #ffff00 !important;
|
||||
}
|
||||
|
||||
.high-contrast thead {
|
||||
background-color: #000 !important;
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Hover states */
|
||||
.high-contrast tr:hover,
|
||||
.high-contrast .hover\:bg-gray-50:hover,
|
||||
.high-contrast .hover\:bg-gray-100:hover {
|
||||
background-color: #1a1a1a !important;
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Icons devem ser visíveis */
|
||||
.high-contrast svg {
|
||||
color: #ffff00 !important;
|
||||
stroke: currentColor;
|
||||
}
|
||||
|
||||
/* Botões de ação com cores específicas */
|
||||
.high-contrast .text-blue-600,
|
||||
.high-contrast .text-green-600,
|
||||
.high-contrast .text-orange-600,
|
||||
.high-contrast .text-red-600,
|
||||
.high-contrast .text-purple-600 {
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
.high-contrast .hover\:bg-blue-50:hover,
|
||||
.high-contrast .hover\:bg-green-50:hover,
|
||||
.high-contrast .hover\:bg-orange-50:hover,
|
||||
.high-contrast .hover\:bg-red-50:hover {
|
||||
background-color: #333 !important;
|
||||
}
|
||||
|
||||
/* Divisores e bordas */
|
||||
.high-contrast .divide-y > * {
|
||||
border-color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Cards e containers */
|
||||
.high-contrast .rounded-xl,
|
||||
.high-contrast .rounded-lg {
|
||||
border: 2px solid #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Modals e dialogs */
|
||||
.high-contrast .shadow-xl,
|
||||
.high-contrast .shadow-sm {
|
||||
box-shadow: 0 0 0 3px #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Botões desabilitados */
|
||||
.high-contrast button:disabled {
|
||||
background-color: #333 !important;
|
||||
color: #666 !important;
|
||||
border-color: #666 !important;
|
||||
opacity: 0.5 !important;
|
||||
}
|
||||
|
||||
/* Paginação - página ativa */
|
||||
.high-contrast .bg-green-600.text-white {
|
||||
background-color: #ffff00 !important;
|
||||
color: #000 !important;
|
||||
border: 3px solid #000 !important;
|
||||
}
|
||||
|
||||
/* Calendário - células cinzas */
|
||||
.high-contrast .bg-gray-200 {
|
||||
background-color: #000 !important;
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Calendário - dias da semana e células */
|
||||
.high-contrast .bg-gray-50,
|
||||
.high-contrast .bg-gray-100 {
|
||||
background-color: #000 !important;
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Calendário - dia atual (azul claro) */
|
||||
.high-contrast .bg-blue-50 {
|
||||
background-color: #1a1a1a !important;
|
||||
color: #ffff00 !important;
|
||||
border: 3px solid #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Calendário - eventos/horários nas células */
|
||||
.high-contrast .bg-blue-100,
|
||||
.high-contrast .bg-green-100,
|
||||
.high-contrast .text-blue-800,
|
||||
.high-contrast .text-green-800,
|
||||
.high-contrast .text-yellow-800,
|
||||
.high-contrast .text-red-800,
|
||||
.high-contrast .text-purple-800 {
|
||||
background-color: #ffff00 !important;
|
||||
color: #000 !important;
|
||||
border: 2px solid #000 !important;
|
||||
}
|
||||
|
||||
/* Calendário - Grid com divisórias amarelas */
|
||||
.high-contrast .grid-cols-7 {
|
||||
background-color: #ffff00 !important;
|
||||
gap: 2px !important;
|
||||
padding: 2px !important;
|
||||
}
|
||||
|
||||
.high-contrast .grid-cols-7 > div {
|
||||
background-color: #000 !important;
|
||||
border: 2px solid #ffff00 !important;
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Calendário - Background do grid */
|
||||
.high-contrast .bg-gray-200.border.border-gray-200 {
|
||||
background-color: #ffff00 !important;
|
||||
border-color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Headers com fundo cinza */
|
||||
.high-contrast .bg-gray-700 {
|
||||
background-color: #000 !important;
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Texto em fundos coloridos */
|
||||
.high-contrast .text-blue-700,
|
||||
.high-contrast .text-green-700,
|
||||
.high-contrast .text-purple-700 {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
/* Garantir que backgrounds cinzas fiquem pretos */
|
||||
.high-contrast [class*="bg-gray"] {
|
||||
background-color: #000 !important;
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Garantir que textos cinzas fiquem amarelos */
|
||||
.high-contrast [class*="text-gray"] {
|
||||
color: #ffff00 !important;
|
||||
}
|
||||
|
||||
/* Modo Escuro Melhorado */
|
||||
.dark {
|
||||
color-scheme: dark;
|
||||
|
||||
@ -36,24 +36,24 @@ export default function PainelSecretaria() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
|
||||
{/* Header */}
|
||||
<header className="bg-white border-b border-gray-200 sticky top-0 z-10">
|
||||
<header className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 sticky top-0 z-10">
|
||||
<div className="max-w-[1400px] mx-auto px-6 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
||||
Painel da Secretaria
|
||||
</h1>
|
||||
{user && (
|
||||
<p className="text-sm text-gray-600 mt-1">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
Bem-vinda, {user.email}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="flex items-center gap-2 px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
className="flex items-center gap-2 px-4 py-2 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
Sair
|
||||
@ -63,7 +63,7 @@ export default function PainelSecretaria() {
|
||||
</header>
|
||||
|
||||
{/* Tabs Navigation */}
|
||||
<div className="bg-white border-b border-gray-200">
|
||||
<div className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="max-w-[1400px] mx-auto px-6">
|
||||
<nav className="flex gap-2">
|
||||
{tabs.map((tab) => {
|
||||
@ -75,8 +75,8 @@ export default function PainelSecretaria() {
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`flex items-center gap-2 px-4 py-3 border-b-2 transition-colors ${
|
||||
isActive
|
||||
? "border-green-600 text-green-600 font-medium"
|
||||
: "border-transparent text-gray-600 hover:text-gray-900 hover:border-gray-300"
|
||||
? "border-green-600 text-green-600 dark:text-green-400 font-medium"
|
||||
: "border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 hover:border-gray-300 dark:hover:border-gray-600"
|
||||
}`}
|
||||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user