fix: menu acessibilidade todos os modos funcionais

This commit is contained in:
Pedro Araujo da Silveira 2025-11-04 16:20:04 -03:00
parent f2a9dc7b70
commit 60c8b5eaa9
7 changed files with 592 additions and 154 deletions

View File

@ -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">

View File

@ -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">

View File

@ -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}`}
>

View File

@ -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">

View File

@ -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">

View File

@ -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;

View File

@ -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" />