responsividade agendamento
This commit is contained in:
parent
eff11fc075
commit
cfd4790bf1
@ -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 = () => (
|
||||
<div className="modal fade show"
|
||||
<div
|
||||
className="modal fade show"
|
||||
style={{ display: "block", backgroundColor: "rgba(0,0,0,0.5)" }}
|
||||
tabIndex="-1"
|
||||
>
|
||||
<div className="modal-dialog modal-dialog-centered">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header" style={{ backgroundColor: '#f8d7da', color: '#721c24' }}>
|
||||
<div
|
||||
className="modal-header"
|
||||
style={{ backgroundColor: "#f8d7da", color: "#721c24" }}
|
||||
>
|
||||
<h5 className="modal-title">Confirmação de Cancelamento</h5>
|
||||
<button type="button" className="btn-close"
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
onClick={() => setShowDeleteModal(false)}
|
||||
></button>
|
||||
</div>
|
||||
@ -299,7 +345,10 @@ const Agendamento = ({ setDictInfo }) => {
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
onClick={() => { setShowDeleteModal(false); setMotivoCancelamento(""); }}
|
||||
onClick={() => {
|
||||
setShowDeleteModal(false);
|
||||
setMotivoCancelamento("");
|
||||
}}
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
@ -317,21 +366,29 @@ const Agendamento = ({ setDictInfo }) => {
|
||||
);
|
||||
|
||||
const ConfirmEditModal = () => (
|
||||
<div className="modal fade show"
|
||||
<div
|
||||
className="modal fade show"
|
||||
style={{ display: "block", backgroundColor: "rgba(0,0,0,0.5)" }}
|
||||
tabIndex="-1"
|
||||
>
|
||||
<div className="modal-dialog modal-dialog-centered">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header" style={{ backgroundColor: '#d4edda', color: '#155724' }}>
|
||||
<div
|
||||
className="modal-header"
|
||||
style={{ backgroundColor: "#d4edda", color: "#155724" }}
|
||||
>
|
||||
<h5 className="modal-title">Confirmação de edição</h5>
|
||||
<button type="button" className="btn-close"
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
onClick={() => setShowConfirmModal(false)}
|
||||
></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<p>Tem certeza que deseja retirar o cancelamento?</p>
|
||||
<small className="text-muted">Isso reverterá o status do agendamento para Agendado.</small>
|
||||
<small className="text-muted">
|
||||
Isso reverterá o status do agendamento para Agendado.
|
||||
</small>
|
||||
</div>
|
||||
<div className="modal-footer justify-content-center">
|
||||
<button
|
||||
@ -346,7 +403,12 @@ const Agendamento = ({ setDictInfo }) => {
|
||||
className="btn btn-success"
|
||||
onClick={() => confirmConsulta(selectedID)}
|
||||
>
|
||||
<Trash2 size={16} className="me-1" style={{ transform: 'rotate(45deg)' }} /> Confirmar
|
||||
<Trash2
|
||||
size={16}
|
||||
className="me-1"
|
||||
style={{ transform: "rotate(45deg)" }}
|
||||
/>{" "}
|
||||
Confirmar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -359,18 +421,28 @@ const Agendamento = ({ setDictInfo }) => {
|
||||
<h1>Agendar nova consulta</h1>
|
||||
|
||||
{!PageNovaConsulta ? (
|
||||
<div className='atendimento-eprocura'>
|
||||
<div className='container-btns-agenda-fila_esepera'>
|
||||
<div className="atendimento-eprocura">
|
||||
<div className="container-btns-agenda-fila_esepera">
|
||||
<div className="tabs-agenda-fila">
|
||||
<button
|
||||
className={`btn-agenda ${!FiladeEspera ? "opc-agenda-ativo" : ""}`}
|
||||
onClick={() => { setFiladeEspera(false); setSearchTerm(''); }}
|
||||
className={`btn-agenda ${
|
||||
!FiladeEspera ? "opc-agenda-ativo" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setFiladeEspera(false);
|
||||
setSearchTerm("");
|
||||
}}
|
||||
>
|
||||
Agenda
|
||||
</button>
|
||||
<button
|
||||
className={`btn-fila-espera ${FiladeEspera ? "opc-filaespera-ativo" : ""}`}
|
||||
onClick={() => { setFiladeEspera(true); setSearchTerm(''); }}
|
||||
className={`btn-fila-espera ${
|
||||
FiladeEspera ? "opc-filaespera-ativo" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setFiladeEspera(true);
|
||||
setSearchTerm("");
|
||||
}}
|
||||
>
|
||||
Fila de espera
|
||||
</button>
|
||||
@ -394,164 +466,186 @@ const Agendamento = ({ setDictInfo }) => {
|
||||
<i className="bi bi-gear-fill me-1"></i> Gerenciar Exceções
|
||||
</button>
|
||||
<button
|
||||
className='manage-button btn'
|
||||
onClick={() => navigate('/secretaria/disponibilidade')}
|
||||
className="manage-button btn"
|
||||
onClick={() => navigate("/secretaria/disponibilidade")}
|
||||
>
|
||||
<i className="bi bi-gear-fill me-1"></i> Mudar Disponibilidade
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section className='calendario-ou-filaespera'>
|
||||
<section className="calendario-ou-filaespera">
|
||||
{FiladeEspera === false ? (
|
||||
<div className="calendar-wrapper">
|
||||
<div className="calendar-info-panel">
|
||||
<div className="info-date-display">
|
||||
<span>{selectedDay.format('MMM')}</span>
|
||||
<strong>{selectedDay.format('DD')}</strong>
|
||||
<span>{selectedDay.format("MMM")}</span>
|
||||
<strong>{selectedDay.format("DD")}</strong>
|
||||
</div>
|
||||
<div className="info-details">
|
||||
<h3>{selectedDay.format('dddd')}</h3>
|
||||
<p>{selectedDay.format('D [de] MMMM [de] YYYY')}</p>
|
||||
<h3>{selectedDay.format("dddd")}</h3>
|
||||
<p>{selectedDay.format("D [de] MMMM [de] YYYY")}</p>
|
||||
|
||||
<div className="calendar-legend">
|
||||
<div className="legend-item" data-status="completed">Realizado</div>
|
||||
<div className="legend-item" data-status="confirmed">Confirmado</div>
|
||||
<div className="legend-item" data-status="agendado">Agendado</div>
|
||||
<div className="legend-item" data-status="cancelled">Cancelado</div>
|
||||
<div className="calendar-legend">
|
||||
<div className="legend-item" data-status="completed">
|
||||
Realizado
|
||||
</div>
|
||||
<div className="legend-item" data-status="confirmed">
|
||||
Confirmado
|
||||
</div>
|
||||
<div className="legend-item" data-status="agendado">
|
||||
Agendado
|
||||
</div>
|
||||
<div className="legend-item" data-status="cancelled">
|
||||
Cancelado
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 className="subtitle-consultas">
|
||||
Consultas para {selectedDay.format('DD/MM')}
|
||||
Consultas para {selectedDay.format("DD/MM")}
|
||||
</h4>
|
||||
</div>
|
||||
<div className="appointments-list">
|
||||
{showSpinner ? (
|
||||
<Spinner />
|
||||
) : (() => {
|
||||
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 => (
|
||||
<div key={app.id} className="appointment-item" data-status={app.status}>
|
||||
<div className="item-time">
|
||||
{dayjs(app.scheduled_at).format('HH:mm')}
|
||||
</div>
|
||||
<div className="item-details">
|
||||
<span>{app.paciente_nome}</span>
|
||||
<small>Dr(a). {app.medico_nome}</small>
|
||||
</div>
|
||||
<div className="appointment-actions">
|
||||
{app.status === 'cancelled' ? (
|
||||
<button
|
||||
className="btn-action btn-edit"
|
||||
onClick={() => { setSelectedId(app.id); setShowConfirmModal(true); }}
|
||||
>
|
||||
<Edit size={16} />
|
||||
</button>
|
||||
) : (
|
||||
<>
|
||||
return filtered.length > 0 ? (
|
||||
filtered.map((app) => (
|
||||
<div
|
||||
key={app.id}
|
||||
className="appointment-item"
|
||||
data-status={app.status}
|
||||
>
|
||||
<div className="item-time">
|
||||
{dayjs(app.scheduled_at).format("HH:mm")}
|
||||
</div>
|
||||
<div className="item-details">
|
||||
<span>{app.paciente_nome}</span>
|
||||
<small>Dr(a). {app.medico_nome}</small>
|
||||
</div>
|
||||
<div className="appointment-actions">
|
||||
{app.status === "cancelled" ? (
|
||||
<button
|
||||
className="btn-action btn-edit"
|
||||
onClick={() => {
|
||||
setAppointmentToEdit(app);
|
||||
setEditingAppointmentId(app.id);
|
||||
setPageConsulta(true);
|
||||
setSelectedId(app.id);
|
||||
setShowConfirmModal(true);
|
||||
}}
|
||||
>
|
||||
<Edit size={16} />
|
||||
</button>
|
||||
<button
|
||||
className="btn-action btn-delete"
|
||||
onClick={() => { setSelectedId(app.id); setShowDeleteModal(true); }}
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
className="btn-action btn-edit"
|
||||
onClick={() => {
|
||||
setAppointmentToEdit(app);
|
||||
setEditingAppointmentId(app.id);
|
||||
setPageConsulta(true);
|
||||
}}
|
||||
>
|
||||
<Edit size={16} />
|
||||
</button>
|
||||
<button
|
||||
className="btn-action btn-delete"
|
||||
onClick={() => {
|
||||
setSelectedId(app.id);
|
||||
setShowDeleteModal(true);
|
||||
}}
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="no-appointments-info">
|
||||
<p>Nenhuma consulta agendada.</p>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="no-appointments-info">
|
||||
<p>Nenhuma consulta agendada.</p>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
);
|
||||
})()
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="calendar-main">
|
||||
<div className="calendar-header-filter">
|
||||
<div className="position-relative">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Filtrar atendimento do médico"
|
||||
value={searchTermDoctor}
|
||||
onChange={(e) => {
|
||||
setSearchTermDoctor(e.target.value);
|
||||
setDoctorDropdownOpen(true);
|
||||
}}
|
||||
onFocus={() => {
|
||||
if (searchTermDoctor.trim())
|
||||
setDoctorDropdownOpen(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
{doctorDropdownOpen && searchTermDoctor.trim() && (
|
||||
<div
|
||||
className="doctor-suggestions"
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "100%",
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 10,
|
||||
backgroundColor: "white",
|
||||
border: "1px solid #ced4da",
|
||||
borderTop: "none",
|
||||
maxHeight: "250px",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
{ListaDeMedicos.filter((m) => {
|
||||
const nome = (m.nomeMedico || "").toLowerCase();
|
||||
const term = searchTermDoctor.toLowerCase();
|
||||
return nome.startsWith(term);
|
||||
})
|
||||
.slice(0, 20)
|
||||
.map((m) => (
|
||||
<button
|
||||
key={m.idMedico}
|
||||
type="button"
|
||||
className="w-100 text-start px-3 py-1"
|
||||
style={{
|
||||
border: "none",
|
||||
background: "white",
|
||||
fontSize: "0.9rem",
|
||||
}}
|
||||
onClick={() => {
|
||||
setSearchTermDoctor(m.nomeMedico);
|
||||
setDoctorDropdownOpen(false);
|
||||
}}
|
||||
>
|
||||
{m.nomeMedico}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="calendar-main">
|
||||
{/* SUBSTITUIR POR ESTE BLOCO 👇 */}
|
||||
<div className="calendar-header-filter">
|
||||
<div className="position-relative">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Filtrar atendimento do médico"
|
||||
value={searchTermDoctor}
|
||||
onChange={(e) => {
|
||||
setSearchTermDoctor(e.target.value);
|
||||
setDoctorDropdownOpen(true);
|
||||
}}
|
||||
onFocus={() => {
|
||||
if (searchTermDoctor.trim()) setDoctorDropdownOpen(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
{doctorDropdownOpen && searchTermDoctor.trim() && (
|
||||
<div
|
||||
className="doctor-suggestions"
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "100%",
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 10,
|
||||
backgroundColor: "white",
|
||||
border: "1px solid #ced4da",
|
||||
borderTop: "none",
|
||||
maxHeight: "250px",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
{ListaDeMedicos
|
||||
.filter((m) => {
|
||||
const nome = (m.nomeMedico || "").toLowerCase();
|
||||
const term = searchTermDoctor.toLowerCase();
|
||||
return nome.startsWith(term);
|
||||
})
|
||||
.slice(0, 20)
|
||||
.map((m) => (
|
||||
<button
|
||||
key={m.idMedico}
|
||||
type="button"
|
||||
className="w-100 text-start px-3 py-1"
|
||||
style={{
|
||||
border: "none",
|
||||
background: "white",
|
||||
fontSize: "0.9rem",
|
||||
}}
|
||||
onClick={() => {
|
||||
setSearchTermDoctor(m.nomeMedico);
|
||||
setDoctorDropdownOpen(false);
|
||||
}}
|
||||
>
|
||||
{m.nomeMedico}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="calendar-controls">
|
||||
<div className="date-indicator">
|
||||
<h2>{currentDate.format('MMMM [de] YYYY')}</h2>
|
||||
@ -616,39 +710,48 @@ const filtered = allApps.filter(app =>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="calendar-grid">
|
||||
{weekDays.map(day => (
|
||||
<div key={day} className="day-header">{day}</div>
|
||||
))}
|
||||
{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 (
|
||||
<div
|
||||
key={index}
|
||||
className={cellClasses}
|
||||
onClick={() => handleDateClick(day)}
|
||||
>
|
||||
<span>{day.format('D')}</span>
|
||||
{filteredAppointments.length > 0 && (
|
||||
<div className="appointments-indicator">
|
||||
{filteredAppointments.length}
|
||||
<div className="calendar-grid-scroll-wrapper">
|
||||
<div className="calendar-grid-scrollable">
|
||||
<div className="calendar-grid">
|
||||
{weekDays.map(day => (
|
||||
<div key={day} className="day-header">{day}</div>
|
||||
))}
|
||||
{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 (
|
||||
<div
|
||||
key={index}
|
||||
className={cellClasses}
|
||||
onClick={() => handleDateClick(day)}
|
||||
>
|
||||
<span>{day.format('D')}</span>
|
||||
{filteredAppointments.length > 0 && (
|
||||
<div className="appointments-indicator">
|
||||
{filteredAppointments.length}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="d-md-none scroll-hint">
|
||||
<i className="bi bi-arrow-left-right"></i>
|
||||
<span>Arraste para ver mais dias</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -670,12 +773,12 @@ const filteredAppointments = appointmentsOnDay.filter(app =>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
) : (
|
||||
) : (
|
||||
<AgendamentoCadastroManager
|
||||
setPageConsulta={setPageConsulta}
|
||||
Dict={appointmentToEdit}
|
||||
onSaved={() => {
|
||||
fetchAppointments(); // recarrega os agendamentos do médico
|
||||
fetchAppointments(); // recarrega os agendamentos do médico
|
||||
setPageConsulta(false);
|
||||
}}
|
||||
/>
|
||||
@ -688,4 +791,3 @@ const filteredAppointments = appointmentsOnDay.filter(app =>
|
||||
};
|
||||
|
||||
export default Agendamento;
|
||||
|
||||
|
||||
@ -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,95 @@
|
||||
.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: 2px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.calendar-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
.day-cell {
|
||||
aspect-ratio: 1;
|
||||
min-height: 36px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 2px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.day-cell > span {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.day-header {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
padding: 6px 0;
|
||||
color: #64748b;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.appointments-indicator {
|
||||
font-size: 9px;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background-color: #3b82f6;
|
||||
color: white;
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
.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 +262,6 @@
|
||||
color: #C53030;
|
||||
}
|
||||
|
||||
|
||||
.appointment-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -275,22 +304,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;
|
||||
@ -324,7 +337,6 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 15px 0 20px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tabs-agenda-fila {
|
||||
@ -335,7 +347,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 +356,449 @@
|
||||
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: 700px;
|
||||
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: 650px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.calendar-grid {
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.day-cell {
|
||||
min-height: 32px;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.day-header {
|
||||
padding: 4px 0;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.calendar-grid-scrollable {
|
||||
min-width: 600px;
|
||||
}
|
||||
|
||||
.day-cell {
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
.day-header {
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
.calendar-grid {
|
||||
grid-template-columns: repeat(7, minmax(0, 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: 550px;
|
||||
}
|
||||
|
||||
.day-cell {
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.day-header {
|
||||
min-width: 28px;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.day-cell > span {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.appointments-indicator {
|
||||
min-width: 14px;
|
||||
height: 14px;
|
||||
font-size: 8px;
|
||||
bottom: 1px;
|
||||
right: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 375px) {
|
||||
.calendar-grid-scrollable {
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
.day-cell {
|
||||
min-height: 26px;
|
||||
}
|
||||
|
||||
.day-header {
|
||||
min-width: 26px;
|
||||
font-size: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 320px) {
|
||||
.calendar-grid-scrollable {
|
||||
min-width: 450px;
|
||||
}
|
||||
|
||||
.day-cell {
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
.day-header {
|
||||
min-width: 24px;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user