diff --git a/src/pages/Agendamento.jsx b/src/pages/Agendamento.jsx index 3e927b6..57770f1 100644 --- a/src/pages/Agendamento.jsx +++ b/src/pages/Agendamento.jsx @@ -1,43 +1,47 @@ -import React, { useState, useMemo, useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; -import API_KEY from '../components/utils/apiKeys.js'; -import AgendamentoCadastroManager from './AgendamentoCadastroManager.jsx'; -import { GetPatientByID } from '../components/utils/Functions-Endpoints/Patient.js'; -import TabelaAgendamentoDia from '../components/AgendarConsulta/TabelaAgendamentoDia'; -import TabelaAgendamentoSemana from '../components/AgendarConsulta/TabelaAgendamentoSemana'; -import TabelaAgendamentoMes from '../components/AgendarConsulta/TabelaAgendamentoMes'; -import FormNovaConsulta from '../components/AgendarConsulta/FormNovaConsulta'; -import { GetAllDoctors, GetDoctorByID } from '../components/utils/Functions-Endpoints/Doctor.js'; -import { useAuth } from '../components/utils/AuthProvider.js'; -import dayjs from 'dayjs'; -import 'dayjs/locale/pt-br'; -import isBetween from 'dayjs/plugin/isBetween'; -import localeData from 'dayjs/plugin/localeData'; -import { ChevronLeft, ChevronRight, Edit, Trash2 } from 'lucide-react'; -import CalendarComponent from '../components/AgendarConsulta/CalendarComponent.jsx'; +import React, { useState, useMemo, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import API_KEY from "../components/utils/apiKeys.js"; +import AgendamentoCadastroManager from "./AgendamentoCadastroManager.jsx"; +import { GetPatientByID } from "../components/utils/Functions-Endpoints/Patient.js"; +import TabelaAgendamentoDia from "../components/AgendarConsulta/TabelaAgendamentoDia"; +import TabelaAgendamentoSemana from "../components/AgendarConsulta/TabelaAgendamentoSemana"; +import TabelaAgendamentoMes from "../components/AgendarConsulta/TabelaAgendamentoMes"; +import FormNovaConsulta from "../components/AgendarConsulta/FormNovaConsulta"; +import { + GetAllDoctors, + GetDoctorByID, +} from "../components/utils/Functions-Endpoints/Doctor.js"; +import { useAuth } from "../components/utils/AuthProvider.js"; +import dayjs from "dayjs"; +import "dayjs/locale/pt-br"; +import isBetween from "dayjs/plugin/isBetween"; +import localeData from "dayjs/plugin/localeData"; +import { ChevronLeft, ChevronRight, Edit, Trash2 } from "lucide-react"; +import CalendarComponent from "../components/AgendarConsulta/CalendarComponent.jsx"; import "./style/Agendamento.css"; -import './style/FilaEspera.css'; -import Spinner from '../components/Spinner.jsx'; +import "./style/FilaEspera.css"; +import Spinner from "../components/Spinner.jsx"; -dayjs.locale('pt-br'); +dayjs.locale("pt-br"); dayjs.extend(isBetween); dayjs.extend(localeData); const Agendamento = ({ setDictInfo }) => { - const navigate = useNavigate(); const [listaTodosAgendamentos, setListaTodosAgendamentos] = useState([]); - const [selectedID, setSelectedId] = useState('0'); + const [selectedID, setSelectedId] = useState("0"); const [filaEsperaData, setFilaEsperaData] = useState([]); const [FiladeEspera, setFiladeEspera] = useState(false); const [PageNovaConsulta, setPageConsulta] = useState(false); - const [searchTerm, setSearchTerm] = useState(''); + const [searchTerm, setSearchTerm] = useState(""); const { getAuthorizationHeader } = useAuth(); - const [DictAgendamentosOrganizados, setAgendamentosOrganizados] = useState({}); + const [DictAgendamentosOrganizados, setAgendamentosOrganizados] = useState( + {} + ); const [showDeleteModal, setShowDeleteModal] = useState(false); const [ListaDeMedicos, setListaDeMedicos] = useState([]); const [FiltredTodosMedicos, setFiltredTodosMedicos] = useState([]); - const [searchTermDoctor, setSearchTermDoctor] = useState(''); + const [searchTermDoctor, setSearchTermDoctor] = useState(""); const [doctorDropdownOpen, setDoctorDropdownOpen] = useState(false); const [MedicoFiltrado, setMedicoFiltrado] = useState({ id: "vazio" }); const [cacheAgendamentos, setCacheAgendamentos] = useState([]); @@ -45,9 +49,9 @@ const Agendamento = ({ setDictInfo }) => { const [showConfirmModal, setShowConfirmModal] = useState(false); const [motivoCancelamento, setMotivoCancelamento] = useState(""); const [showSpinner, setShowSpinner] = useState(true); - const [waitlistSearch, setWaitlistSearch] = useState(''); + const [waitlistSearch, setWaitlistSearch] = useState(""); const [waitSortKey, setWaitSortKey] = useState(null); - const [waitSortDir, setWaitSortDir] = useState('asc'); + const [waitSortDir, setWaitSortDir] = useState("asc"); const [waitPage, setWaitPage] = useState(1); const [waitPerPage, setWaitPerPage] = useState(10); const authHeader = getAuthorizationHeader(); @@ -59,22 +63,27 @@ const Agendamento = ({ setDictInfo }) => { const [quickJump, setQuickJump] = useState({ month: currentDate.month(), - year: currentDate.year() + year: currentDate.year(), }); const fetchAppointments = async () => { const myHeaders = new Headers(); myHeaders.append("Authorization", authHeader); myHeaders.append("apikey", API_KEY); - const requestOptions = { method: 'GET', headers: myHeaders, redirect: 'follow' }; + const requestOptions = { + method: "GET", + headers: myHeaders, + redirect: "follow", + }; try { - const res = await fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?select=*", + const res = await fetch( + "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?select=*", requestOptions ); const data = await res.json(); setListaTodosAgendamentos(data); } catch (err) { - console.error('Erro ao buscar agendamentos', err); + console.error("Erro ao buscar agendamentos", err); } }; @@ -84,7 +93,11 @@ const Agendamento = ({ setDictInfo }) => { myHeaders.append("apikey", API_KEY); myHeaders.append("Content-Type", "application/json"); myHeaders.append("Prefer", "return=representation"); - const requestOptions = { method: 'PATCH', headers: myHeaders, body: JSON.stringify(updates) }; + const requestOptions = { + method: "PATCH", + headers: myHeaders, + body: JSON.stringify(updates), + }; try { const response = await fetch( @@ -95,11 +108,11 @@ const Agendamento = ({ setDictInfo }) => { await fetchAppointments(); return true; } else { - console.error('Erro ao atualizar agendamento:', await response.text()); + console.error("Erro ao atualizar agendamento:", await response.text()); return false; } } catch (error) { - console.error('Erro de rede/servidor:', error); + console.error("Erro de rede/servidor:", error); return false; } }; @@ -109,13 +122,13 @@ const Agendamento = ({ setDictInfo }) => { const success = await updateAppointmentStatus(id, { status: "cancelled", cancellation_reason: motivoCancelamento, - updated_at: new Date().toISOString() + updated_at: new Date().toISOString(), }); setShowSpinner(false); if (success) { setShowDeleteModal(false); setMotivoCancelamento(""); - setSelectedId('0'); + setSelectedId("0"); } else { alert("Falha ao cancelar a consulta."); } @@ -126,25 +139,25 @@ const Agendamento = ({ setDictInfo }) => { const success = await updateAppointmentStatus(id, { status: "agendado", cancellation_reason: null, - updated_at: new Date().toISOString() + updated_at: new Date().toISOString(), }); setShowSpinner(false); if (success) { setShowConfirmModal(false); - setSelectedId('0'); + setSelectedId("0"); } else { alert("Falha ao reverter o cancelamento."); } }; const handleQuickJumpChange = (type, value) => { - setQuickJump(prev => ({ ...prev, [type]: Number(value) })); + setQuickJump((prev) => ({ ...prev, [type]: Number(value) })); }; useEffect(() => { setQuickJump({ month: currentDate.month(), - year: currentDate.year() + year: currentDate.year(), }); }, [currentDate]); @@ -155,7 +168,10 @@ const Agendamento = ({ setDictInfo }) => { }; useEffect(() => { - if (!listaTodosAgendamentos.length) { setShowSpinner(false); return; } + if (!listaTodosAgendamentos.length) { + setShowSpinner(false); + return; + } setShowSpinner(true); const fetchDados = async () => { const newDict = {}; @@ -163,12 +179,14 @@ const Agendamento = ({ setDictInfo }) => { for (const agendamento of listaTodosAgendamentos) { if (!agendamento.doctor_id || !agendamento.patient_id) continue; if (!cacheMedicos[agendamento.doctor_id]) { - cacheMedicos[agendamento.doctor_id] = - (await GetDoctorByID(agendamento.doctor_id, authHeader))[0]; + cacheMedicos[agendamento.doctor_id] = ( + await GetDoctorByID(agendamento.doctor_id, authHeader) + )[0]; } if (!cachePacientes[agendamento.patient_id]) { - cachePacientes[agendamento.patient_id] = - (await GetPatientByID(agendamento.patient_id, authHeader))[0]; + cachePacientes[agendamento.patient_id] = ( + await GetPatientByID(agendamento.patient_id, authHeader) + )[0]; } const medico = cacheMedicos[agendamento.doctor_id]; const paciente = cachePacientes[agendamento.patient_id]; @@ -177,13 +195,18 @@ const Agendamento = ({ setDictInfo }) => { ...agendamento, medico_nome: medico?.full_name, paciente_nome: paciente?.full_name, - paciente_cpf: paciente?.cpf + paciente_cpf: paciente?.cpf, }; if (agendamento.status === "requested") { - newFila.push({ agendamento: agendamentoMelhorado, Infos: agendamentoMelhorado }); + newFila.push({ + agendamento: agendamentoMelhorado, + Infos: agendamentoMelhorado, + }); } else { - const DiaAgendamento = dayjs(agendamento.scheduled_at).format("YYYY-MM-DD"); + const DiaAgendamento = dayjs(agendamento.scheduled_at).format( + "YYYY-MM-DD" + ); if (newDict[DiaAgendamento]) { newDict[DiaAgendamento].push(agendamentoMelhorado); } else { @@ -192,7 +215,9 @@ const Agendamento = ({ setDictInfo }) => { } } for (const key in newDict) { - newDict[key].sort((a, b) => a.scheduled_at.localeCompare(b.scheduled_at)); + newDict[key].sort((a, b) => + a.scheduled_at.localeCompare(b.scheduled_at) + ); } setAgendamentosOrganizados(newDict); setFilaEsperaData(newFila); @@ -203,48 +228,59 @@ const Agendamento = ({ setDictInfo }) => { useEffect(() => { fetchAppointments(); - GetAllDoctors(authHeader).then(docs => - setListaDeMedicos(docs.map(d => ({ nomeMedico: d.full_name, idMedico: d.id }))) + GetAllDoctors(authHeader).then((docs) => + setListaDeMedicos( + docs.map((d) => ({ nomeMedico: d.full_name, idMedico: d.id })) + ) ); }, [authHeader]); - const handleSearchMedicos = (term) => { }; + const handleSearchMedicos = (term) => {}; const filaEsperaFiltrada = useMemo(() => { if (!waitlistSearch.trim()) return filaEsperaData; const term = waitlistSearch.toLowerCase(); - return filaEsperaData.filter(item => - (item?.Infos?.paciente_nome?.toLowerCase() || '').includes(term) || - (item?.Infos?.paciente_cpf?.toLowerCase() || '').includes(term) || - (item?.Infos?.nome_medico?.toLowerCase() || '').includes(term) + return filaEsperaData.filter( + (item) => + (item?.Infos?.paciente_nome?.toLowerCase() || "").includes(term) || + (item?.Infos?.paciente_cpf?.toLowerCase() || "").includes(term) || + (item?.Infos?.nome_medico?.toLowerCase() || "").includes(term) ); }, [waitlistSearch, filaEsperaData]); const applySortingWaitlist = (arr) => { if (!Array.isArray(arr) || !waitSortKey) return arr; const copy = [...arr]; - if (waitSortKey === 'paciente') { + if (waitSortKey === "paciente") { copy.sort((a, b) => - (a?.Infos?.paciente_nome || '').localeCompare((b?.Infos?.paciente_nome || '')) + (a?.Infos?.paciente_nome || "").localeCompare( + b?.Infos?.paciente_nome || "" + ) ); - } else if (waitSortKey === 'medico') { + } else if (waitSortKey === "medico") { copy.sort((a, b) => - (a?.Infos?.nome_medico || '').localeCompare((b?.Infos?.nome_medico || '')) + (a?.Infos?.nome_medico || "").localeCompare(b?.Infos?.nome_medico || "") ); - } else if (waitSortKey === 'data') { - copy.sort((a, b) => - new Date(a?.agendamento?.scheduled_at || 0) - new Date(b?.agendamento?.scheduled_at || 0) + } else if (waitSortKey === "data") { + copy.sort( + (a, b) => + new Date(a?.agendamento?.scheduled_at || 0) - + new Date(b?.agendamento?.scheduled_at || 0) ); } - if (waitSortDir === 'desc') copy.reverse(); + if (waitSortDir === "desc") copy.reverse(); return copy; }; const filaEsperaOrdenada = applySortingWaitlist(filaEsperaFiltrada); - const waitTotalPages = Math.ceil(filaEsperaOrdenada.length / waitPerPage) || 1; + const waitTotalPages = + Math.ceil(filaEsperaOrdenada.length / waitPerPage) || 1; const waitIndiceInicial = (waitPage - 1) * waitPerPage; const waitIndiceFinal = waitIndiceInicial + waitPerPage; - const filaEsperaPaginada = filaEsperaOrdenada.slice(waitIndiceInicial, waitIndiceFinal); + const filaEsperaPaginada = filaEsperaOrdenada.slice( + waitIndiceInicial, + waitIndiceFinal + ); const gerarNumerosWaitPages = () => { const paginas = []; @@ -252,37 +288,47 @@ const Agendamento = ({ setDictInfo }) => { let inicio = Math.max(1, waitPage - Math.floor(paginasParaMostrar / 2)); let fim = Math.min(waitTotalPages, inicio + paginasParaMostrar - 1); inicio = Math.max(1, fim - paginasParaMostrar + 1); - for (let i = inicio; i <= fim; i++) { paginas.push(i); } + for (let i = inicio; i <= fim; i++) { + paginas.push(i); + } return paginas; }; - useEffect(() => { setWaitPage(1); }, [waitlistSearch, waitSortKey, waitSortDir]); + useEffect(() => { + setWaitPage(1); + }, [waitlistSearch, waitSortKey, waitSortDir]); const generateDateGrid = () => { const grid = []; - const startOfMonth = currentDate.startOf('month'); - let currentDay = startOfMonth.subtract(startOfMonth.day(), 'day'); + const startOfMonth = currentDate.startOf("month"); + let currentDay = startOfMonth.subtract(startOfMonth.day(), "day"); for (let i = 0; i < 42; i++) { grid.push(currentDay); - currentDay = currentDay.add(1, 'day'); + currentDay = currentDay.add(1, "day"); } return grid; }; const dateGrid = useMemo(() => generateDateGrid(), [currentDate]); - const weekDays = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb']; + const weekDays = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"]; const handleDateClick = (day) => setSelectedDay(day); const DeleteModal = () => ( -
-
+
Confirmação de Cancelamento
-
@@ -299,7 +345,10 @@ const Agendamento = ({ setDictInfo }) => { @@ -317,21 +366,29 @@ const Agendamento = ({ setDictInfo }) => { ); const ConfirmEditModal = () => ( -
-
+
Confirmação de edição
-

Tem certeza que deseja retirar o cancelamento?

- Isso reverterá o status do agendamento para Agendado. + + Isso reverterá o status do agendamento para Agendado. +
@@ -359,18 +421,28 @@ const Agendamento = ({ setDictInfo }) => {

Agendar nova consulta

{!PageNovaConsulta ? ( -
-
+
+
@@ -394,173 +466,279 @@ const Agendamento = ({ setDictInfo }) => { Gerenciar Exceções
-
+
{FiladeEspera === false ? (
- {selectedDay.format('MMM')} - {selectedDay.format('DD')} + {selectedDay.format("MMM")} + {selectedDay.format("DD")}
-

{selectedDay.format('dddd')}

-

{selectedDay.format('D [de] MMMM [de] YYYY')}

+

{selectedDay.format("dddd")}

+

{selectedDay.format("D [de] MMMM [de] YYYY")}

-
-
Realizado
-
Confirmado
-
Agendado
-
Cancelado
+
+
+ Realizado +
+
+ Confirmado +
+
+ Agendado +
+
+ Cancelado +

- Consultas para {selectedDay.format('DD/MM')} + Consultas para {selectedDay.format("DD/MM")}

{showSpinner ? ( - ) : (() => { - const allApps = - DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')] || []; - const termDoc = searchTermDoctor.toLowerCase(); -const filtered = allApps.filter(app => - !termDoc - ? true - : (app.medico_nome || '').toLowerCase().startsWith(termDoc) -); + ) : ( + (() => { + const allApps = + DictAgendamentosOrganizados[ + selectedDay.format("YYYY-MM-DD") + ] || []; + const termDoc = searchTermDoctor.toLowerCase(); + const filtered = allApps.filter((app) => + !termDoc + ? true + : (app.medico_nome || "") + .toLowerCase() + .startsWith(termDoc) + ); - return filtered.length > 0 ? ( - filtered.map(app => ( -
-
- {dayjs(app.scheduled_at).format('HH:mm')} -
-
- {app.paciente_nome} - Dr(a). {app.medico_nome} -
-
- {app.status === 'cancelled' ? ( - - ) : ( - <> + return filtered.length > 0 ? ( + filtered.map((app) => ( +
+
+ {dayjs(app.scheduled_at).format("HH:mm")} +
+
+ {app.paciente_nome} + Dr(a). {app.medico_nome} +
+
+ {app.status === "cancelled" ? ( - - - )} + ) : ( + <> + + + + )} +
+ )) + ) : ( +
+

Nenhuma consulta agendada.

- )) - ) : ( -
-

Nenhuma consulta agendada.

-
- ); - })()} + ); + })() + )} +
+
+
+
+
+
+
+ { + setSearchTermDoctor(e.target.value); + setDoctorDropdownOpen(true); + }} + onFocus={() => { + if (searchTermDoctor.trim()) + setDoctorDropdownOpen(true); + }} + onBlur={() => + setTimeout( + () => setDoctorDropdownOpen(false), + 200 + ) + } + /> + + {doctorDropdownOpen && searchTermDoctor.trim() && ( +
+ {ListaDeMedicos.filter((m) => { + const nome = (m.nomeMedico || "").toLowerCase(); + const term = searchTermDoctor.toLowerCase(); + return nome.startsWith(term); + }) + .slice(0, 10) + .map((m) => ( + + ))} +
+ )} +
+
+ +
+
+ + {currentDate.format("MMM")} + + + {currentDate.format("YYYY")} + +
+ +
+ + + +
+ +
+ + + +
+
+
-
-
- {/* SUBSTITUIR POR ESTE BLOCO 👇 */} -
-
- { - setSearchTermDoctor(e.target.value); - setDoctorDropdownOpen(true); - }} - onFocus={() => { - if (searchTermDoctor.trim()) setDoctorDropdownOpen(true); - }} - /> - {doctorDropdownOpen && searchTermDoctor.trim() && ( -
- {ListaDeMedicos - .filter((m) => { - const nome = (m.nomeMedico || "").toLowerCase(); - const term = searchTermDoctor.toLowerCase(); - return nome.startsWith(term); - }) - .slice(0, 20) - .map((m) => ( - - ))} -
- )} -
-
- -
-

{currentDate.format('MMMM [de] YYYY')}

-
{currentDate.format("MMMM [de] YYYY")} +
- - -
-
- {weekDays.map(day => ( -
{day}
- ))} - {dateGrid.map((day, index) => { - const dayKey = day.format('YYYY-MM-DD'); - const appointmentsOnDay = DictAgendamentosOrganizados[dayKey] || []; - const termDoc = searchTermDoctor.toLowerCase(); -const filteredAppointments = appointmentsOnDay.filter(app => - !termDoc - ? true - : (app.medico_nome || '').toLowerCase().startsWith(termDoc) -); - const cellClasses = `day-cell ${ - day.isSame(currentDate, 'month') ? 'current-month' : 'other-month' - } ${day.isSame(dayjs(), 'day') ? 'today' : ''} ${ - day.isSame(selectedDay, 'day') ? 'selected' : '' - }`; - return ( -
handleDateClick(day)} - > - {day.format('D')} - {filteredAppointments.length > 0 && ( -
- {filteredAppointments.length} +
+
+
+ {weekDays.map(day => ( +
{day}
+ ))} + {dateGrid.map((day, index) => { + const dayKey = day.format('YYYY-MM-DD'); + const appointmentsOnDay = DictAgendamentosOrganizados[dayKey] || []; + const termDoc = searchTermDoctor.toLowerCase(); + const filteredAppointments = appointmentsOnDay.filter(app => + !termDoc + ? true + : (app.medico_nome || '').toLowerCase().startsWith(termDoc) + ); + const cellClasses = `day-cell ${ + day.isSame(currentDate, 'month') ? 'current-month' : 'other-month' + } ${day.isSame(dayjs(), 'day') ? 'today' : ''} ${ + day.isSame(selectedDay, 'day') ? 'selected' : '' + }`; + return ( +
handleDateClick(day)} + > + {day.format('D')} + {filteredAppointments.length > 0 && ( +
+ {filteredAppointments.length} +
+ )}
- )} -
- ); - })} + ); + })} +
+
+
+ +
+ + Arraste para ver mais dias
@@ -661,7 +861,7 @@ const filteredAppointments = appointmentsOnDay.filter(app =>

Fila de Espera

- {/* ... resto da fila de espera igual ao seu código ... */} +
@@ -670,12 +870,12 @@ const filteredAppointments = appointmentsOnDay.filter(app => )}
- ) : ( + ) : ( { - fetchAppointments(); // recarrega os agendamentos do médico + fetchAppointments(); // recarrega os agendamentos do médico setPageConsulta(false); }} /> @@ -688,4 +888,3 @@ const filteredAppointments = appointmentsOnDay.filter(app => }; export default Agendamento; - diff --git a/src/pages/style/Agendamento.css b/src/pages/style/Agendamento.css index 734d404..a565db8 100644 --- a/src/pages/style/Agendamento.css +++ b/src/pages/style/Agendamento.css @@ -43,33 +43,6 @@ margin-left: 0; } -@media (max-width: 576px) { - .busca-atendimento { - flex-direction: column; - gap: 10px; - } - -.container-btns-agenda-fila_esepera { - flex-direction: column; - align-items: flex-start; - gap: 10px; - flex-wrap: wrap; /* Adicionado para permitir quebra de linha nos botões */ -} - -.btns-gerenciamento-e-consulta { - width: 100%; - justify-content: space-between; - flex-wrap: wrap; /* Adicionado para permitir quebra de linha nos botões */ -} - -.btn-adicionar-consulta { - padding: 8px 12px; /* Reduzido o padding */ - font-size: 0.8rem; /* Reduzido o tamanho da fonte */ - white-space: normal; /* Permite quebra de linha no texto do botão */ - text-align: center; -} -} - .container-btns-agenda-fila_esepera { margin-top: 20px; margin-left: 0; @@ -84,7 +57,7 @@ background-color: transparent; border: 0; border-bottom: 2px solid transparent; - padding: 10px 12px; /* Reduzido o padding */ + padding: 10px 12px; border-radius: 0; font-weight: 600; color: #718096; @@ -136,22 +109,8 @@ margin-top: 20px; } -@media (max-width: 768px) { - .calendar-wrapper { - flex-direction: column; - padding: 16px; - } -} .calendar-info-panel { flex: 0 0 300px; border-right: 1px solid #E2E8F0; padding-right: 24px; display: flex; flex-direction: column; } -@media (max-width: 768px) { - .calendar-info-panel { - border-right: none; - border-bottom: 1px solid #E2E8F0; - padding-right: 0; - padding-bottom: 16px; - } -} .info-date-display { background-color: #EDF2F7; border-radius: 8px; padding: 12px; text-align: center; margin-bottom: 16px; } .info-date-display span { font-weight: 600; color: #718096; text-transform: uppercase; font-size: 0.9rem; } .info-date-display strong { display: block; font-size: 2.5rem; font-weight: 700; color: #2D3748; } @@ -177,24 +136,105 @@ .nav-buttons { display: flex; gap: 8px; } .nav-buttons button { padding: 8px 12px; border-radius: 6px; border: 1px solid #CBD5E0; background-color: #fff; font-weight: 600; cursor: pointer; transition: all 0.2s; } .nav-buttons button:hover { background-color: #EDF2F7; } -.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; } -@media (max-width: 1200px) { - .calendar-grid { grid-template-columns: repeat(4, 1fr); } +.calendar-grid { + display: grid; + grid-template-columns: repeat(7, minmax(0, 1fr)); + gap: 4px; + width: 100%; + min-height: 0; + min-width: 0; } -@media (max-width: 768px) { - .calendar-grid { grid-template-columns: repeat(2, 1fr); } +.day-cell { + aspect-ratio: 1; + min-height: 40px; + max-height: 80px; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + padding: 4px; + border-radius: 8px; + cursor: pointer; + transition: all 0.2s ease; + overflow: hidden; + text-align: center; + word-break: break-word; + border: 1px solid #e2e8f0; +} + +.day-cell > span { + font-size: clamp(12px, 2.5vw, 16px); + font-weight: 600; + margin-bottom: 2px; + line-height: 1.2; + width: 100%; + text-align: center; + display: block; +} + +.day-header { + font-size: clamp(10px, 2vw, 14px); + font-weight: 600; + text-align: center; + padding: 8px 2px; + color: #64748b; + text-transform: uppercase; + letter-spacing: 0.5px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.appointments-indicator { + font-size: clamp(9px, 1.8vw, 12px); + min-width: 18px; + height: 18px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + background-color: #3b82f6; + color: white; + margin-top: 2px; + position: absolute; + bottom: 4px; + right: 4px; +} + + +.day-cell.current-month { + background-color: #ffffff; + color: #1e293b; +} + +.day-cell.other-month { + background-color: #f8fafc; + color: #94a3b8; + opacity: 0.7; +} + +.day-cell.today { + background-color: #eff6ff; + color: #1d4ed8; + font-weight: bold; + border: 2px solid #3b82f6; +} + +.day-cell.selected { + background-color: #3b82f6; + color: white; + box-shadow: 0 0 0 2px #93c5fd; + border: 1px solid #3b82f6; +} + +.day-cell:hover:not(.selected) { + background-color: #f1f5f9; + transform: translateY(-1px); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } -.day-header { font-weight: 600; color: #718096; text-align: center; padding: 8px 0; font-size: 0.875rem; } -.day-cell { min-height: 110px; border-radius: 8px; border: 1px solid #E2E8F0; padding: 8px; transition: background-color 0.2s, border-color 0.2s; cursor: pointer; position: relative; } -.day-cell span { font-weight: 600; color: #4A5568; } -.day-cell:hover { background-color: #EDF2F7; border-color: #BEE3F8; } -.day-cell.other-month { background-color: #F7FAFC; } -.day-cell.other-month span { color: #A0AEC0; } -.day-cell.today span { background-color: #4299E1; color: #fff; border-radius: 50%; padding: 2px 6px; display: inline-block; } -.day-cell.selected { background-color: #BEE3F8; border-color: #4299E1; } -.appointments-indicator { background-color: #4299E1; color: white; font-size: 0.7rem; font-weight: bold; border-radius: 50%; width: 18px; height: 18px; display: flex; justify-content: center; align-items: center; position: absolute; bottom: 8px; right: 8px; } .calendar-legend { display: flex; @@ -232,7 +272,6 @@ color: #C53030; } - .appointment-item { display: flex; align-items: center; @@ -275,22 +314,6 @@ background-color: #C53030; } -@media (max-width: 768px) { - .unidade-selecionarprofissional { flex-direction: column; align-items: stretch; gap: 12px; } - .calendar-wrapper { flex-direction: column; padding: 16px; } - .calendar-info-panel { flex: 0 0 auto; border-right: none; border-bottom: 1px solid #E2E8F0; padding-right: 0; padding-bottom: 16px; } - .calendar-grid { grid-template-columns: repeat(4, 1fr); } - .calendar-controls { flex-direction: column; align-items: flex-start; gap: 8px; } -} - -@media (max-width: 576px) { - .calendar-grid { grid-template-columns: 1fr; } /* 1 coluna em telas muito pequenas */ - .date-indicator h2 { font-size: 1.25rem; } - .legend-item { font-size: 0.75rem; padding: 4px 8px; } - .appointment-item { flex-direction: column; align-items: stretch; gap: 8px; } - .appointment-actions { width: 100%; } - .btn-action { width: 100%; } -} .btn-adicionar-consulta { background-color: #2a67e2; color: #fff; @@ -335,7 +358,7 @@ .btns-gerenciamento-e-consulta { display: flex; gap: 10px; - flex-wrap: wrap; /* Permite quebra de linha */ + flex-wrap: wrap; } .container-btns-agenda-fila_esepera { display: flex; @@ -344,18 +367,473 @@ margin: 0 0 8px; } - .calendario-ou-filaespera { margin-top: 0; padding-top: 0; } - .calendar-wrapper { margin-top: 0; } .calendar-legend { display: flex; gap: 8px; - margin-top: 12px; /* afasta dos filtros acima */ + margin-top: 12px; } + +.calendar-header-filter-container { + margin-bottom: 15px; +} + +.calendar-header-filter-responsive { + display: flex; + flex-direction: column; + gap: 10px; +} + +.filter-row { + width: 100%; +} + +.doctor-suggestions-dropdown { + position: absolute; + top: 100%; + left: 0; + right: 0; + z-index: 1000; + background-color: white; + border: 1px solid #dee2e6; + border-top: none; + border-radius: 0 0 4px 4px; + max-height: 200px; + overflow-y: auto; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +.doctor-suggestion-item { + width: 100%; + padding: 8px 12px; + border: none; + background: none; + text-align: left; + font-size: 0.9rem; + color: #333; + cursor: pointer; + transition: background-color 0.2s; +} + +.doctor-suggestion-item:hover { + background-color: #f8f9fa; +} + +.calendar-controls-compact { + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: center; + justify-content: space-between; + padding: 10px; + background-color: #f8f9fa; + border-radius: 6px; + border: 1px solid #e9ecef; +} + +.date-display-compact { + display: flex; + flex-direction: column; + align-items: center; + min-width: 60px; +} + +.month-display { + font-size: 1.2rem; + font-weight: 600; + color: #0d6efd; + text-transform: uppercase; +} + +.year-display { + font-size: 0.9rem; + color: #6c757d; +} + +.nav-buttons-compact { + display: flex; + gap: 5px; + align-items: center; +} + +.nav-btn-compact { + padding: 6px 10px; + border: 1px solid #dee2e6; + background-color: white; + border-radius: 4px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; +} + +.nav-btn-compact:hover { + background-color: #f8f9fa; + border-color: #0d6efd; +} + +.today-btn-compact { + min-width: 60px; + font-weight: 500; +} + +.quick-jump-compact { + display: flex; + gap: 5px; + align-items: center; + flex-wrap: wrap; +} + +.form-select-compact { + padding: 6px 10px; + border: 1px solid #dee2e6; + border-radius: 4px; + font-size: 0.85rem; + min-width: 100px; +} + +.jump-btn-compact { + padding: 6px 12px; + background-color: #0d6efd; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 0.85rem; + transition: background-color 0.2s; +} + +.jump-btn-compact:hover:not(:disabled) { + background-color: #0b5ed7; +} + +.jump-btn-compact:disabled { + background-color: #6c757d; + cursor: not-allowed; +} + +.calendar-grid-scroll-wrapper { + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + border-radius: 8px; + border: 1px solid #e9ecef; + background: white; + margin-top: 15px; + padding: 0; +} + +.calendar-grid-scroll-wrapper::-webkit-scrollbar { + height: 8px; +} + +.calendar-grid-scroll-wrapper::-webkit-scrollbar-track { + background: #f1f5f9; + border-radius: 4px; +} + +.calendar-grid-scroll-wrapper::-webkit-scrollbar-thumb { + background: #cbd5e1; + border-radius: 4px; +} + +.calendar-grid-scroll-wrapper::-webkit-scrollbar-thumb:hover { + background: #94a3b8; +} + +.calendar-grid-scrollable { + min-width: 800px; + padding: 10px; +} + +.scroll-hint { + text-align: center; + color: #64748b; + font-size: 0.85rem; + margin-top: 8px; + display: flex; + align-items: center; + justify-content: center; + gap: 5px; + opacity: 0.7; +} + +.scroll-hint i { + font-size: 1rem; + animation: bounce-horizontal 2s infinite; +} + +@keyframes bounce-horizontal { + 0%, 100% { + transform: translateX(0); + } + 50% { + transform: translateX(5px); + } +} + +@media (max-width: 768px) { + .busca-atendimento { + flex-direction: column; + gap: 10px; + } + + .container-btns-agenda-fila_esepera { + flex-direction: column; + align-items: flex-start; + gap: 10px; + flex-wrap: wrap; + } + + .btns-gerenciamento-e-consulta { + width: 100%; + justify-content: space-between; + flex-wrap: wrap; + } + + .btn-adicionar-consulta { + padding: 8px 12px; + font-size: 0.8rem; + white-space: normal; + text-align: center; + } + + .calendar-wrapper { + flex-direction: column; + padding: 16px; + } + + .calendar-info-panel { + border-right: none; + border-bottom: 1px solid #E2E8F0; + padding-right: 0; + padding-bottom: 16px; + } + + .unidade-selecionarprofissional { + flex-direction: column; + align-items: stretch; + gap: 12px; + } + + .calendar-controls-compact { + flex-direction: column; + align-items: stretch; + gap: 15px; + } + + .date-display-compact { + flex-direction: row; + justify-content: center; + gap: 8px; + } + + .month-display { + font-size: 1.1rem; + } + + .nav-buttons-compact { + justify-content: center; + } + + .quick-jump-compact { + justify-content: center; + } + + .form-select-compact { + flex: 1; + min-width: auto; + } + + .calendar-main { + padding: 0; + overflow: visible; + } + + .calendar-grid-scrollable { + min-width: 700px; + } + + .calendar-controls-compact { + margin-bottom: 15px; + flex-wrap: nowrap; + overflow-x: auto; + padding-bottom: 5px; + } + + .calendar-controls-compact::-webkit-scrollbar { + height: 4px; + } + + .calendar-controls { + flex-direction: column; + align-items: flex-start; + gap: 8px; + } +} + +@media (max-width: 576px) { + .calendar-grid-scrollable { + min-width: 650px; + } + + .day-cell { + min-height: 45px; + min-width: 45px; + } + + .day-header { + min-width: 45px; + } + + .calendar-grid { + grid-template-columns: repeat(7, minmax(30px, 1fr)); + } + + .date-indicator h2 { + font-size: 1.25rem; + } + + .legend-item { + font-size: 0.75rem; + padding: 4px 8px; + } + + .appointment-item { + flex-direction: column; + align-items: stretch; + gap: 8px; + } + + .appointment-actions { + width: 100%; + } + + .btn-action { + width: 100%; + } +} + +@media (max-width: 480px) { + .calendar-header-filter-responsive { + padding: 0 5px; + } + + .doctor-suggestion-item { + padding: 6px 10px; + font-size: 0.85rem; + } + + .nav-btn-compact { + padding: 5px 8px; + } + + .form-select-compact { + font-size: 0.8rem; + padding: 5px 8px; + } + + .jump-btn-compact { + padding: 5px 10px; + font-size: 0.8rem; + } + + .calendar-grid-scrollable { + min-width: 600px; + } + + .day-cell { + min-height: 40px; + min-width: 40px; + } + + .day-header { + min-width: 40px; + } + + .calendar-grid { + gap: 2px; + } + + .day-cell > span { + font-size: 13px; + } + + .appointments-indicator { + min-width: 16px; + height: 16px; + font-size: 10px; + bottom: 2px; + right: 2px; + } +} + +@media (max-width: 375px) { + .calendar-grid-scrollable { + min-width: 550px; + } + + .day-cell { + min-height: 38px; + min-width: 38px; + } + + .day-header { + min-width: 38px; + } +} + +@media (max-width: 320px) { + .calendar-grid { + grid-template-columns: repeat(7, minmax(28px, 1fr)); + } + + .day-header { + font-size: 9px; + padding: 4px 0; + } + + .day-cell > span { + font-size: 11px; + } + + .day-cell { + min-height: 32px; + } +} + +@media (max-width: 280px) { + .calendar-grid { + font-size: 10px; + } + + .day-cell { + min-height: 30px; + padding: 1px; + } + + .day-cell > span { + font-size: 10px; + } + + .appointments-indicator { + width: 12px; + height: 12px; + font-size: 8px; + bottom: 1px; + right: 1px; + } + + .day-header { + font-size: 8px; + padding: 3px 0; + } +} \ No newline at end of file