forked from RiseUP/riseup-squad23
Modificacoes-da-Tabela
This commit is contained in:
parent
e007c167e7
commit
cfdfd61040
7993
package-lock.json
generated
7993
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ckeditor/ckeditor5-build-classic": "^41.4.2",
|
"@ckeditor/ckeditor5-build-classic": "^41.4.2",
|
||||||
"@ckeditor/ckeditor5-react": "^11.0.0",
|
"@ckeditor/ckeditor5-react": "^11.0.0",
|
||||||
|
"@jitsi/react-sdk": "^1.4.0",
|
||||||
"@sweetalert2/theme-dark": "^5.0.27",
|
"@sweetalert2/theme-dark": "^5.0.27",
|
||||||
"@testing-library/dom": "^10.4.1",
|
"@testing-library/dom": "^10.4.1",
|
||||||
"@testing-library/jest-dom": "^6.8.0",
|
"@testing-library/jest-dom": "^6.8.0",
|
||||||
@ -78,5 +79,8 @@
|
|||||||
"sass": "^1.91.0",
|
"sass": "^1.91.0",
|
||||||
"sass-loader": "^16.0.5",
|
"sass-loader": "^16.0.5",
|
||||||
"tailwindcss": "^4.1.13"
|
"tailwindcss": "^4.1.13"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"react": "$react"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
90
src/PagesMedico/DoctorAgendamentoEditPage.jsx
Normal file
90
src/PagesMedico/DoctorAgendamentoEditPage.jsx
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
import FormNovaConsulta from '../components/AgendarConsulta/FormNovaConsulta'
|
||||||
|
import API_KEY from '../components/utils/apiKeys'
|
||||||
|
import { useAuth } from '../components/utils/AuthProvider'
|
||||||
|
import { UserInfos } from '../components/utils/Functions-Endpoints/General'
|
||||||
|
|
||||||
|
const DoctorAgendamentoEditPage = ({DictInfo, setDictInfo}) => {
|
||||||
|
|
||||||
|
const {getAuthorizationHeader} = useAuth();
|
||||||
|
|
||||||
|
const [consultaToPut, setConsultaToPUT] = useState({})
|
||||||
|
const [idUsuario, setIdUsuario] = useState("")
|
||||||
|
|
||||||
|
|
||||||
|
const authHeader = getAuthorizationHeader()
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
//console.log(DictInfo.scheduled_at.split("T")[0])
|
||||||
|
setDictInfo({...DictInfo, dataAtendimento:DictInfo?.scheduled_at?.split("T")[0]})
|
||||||
|
|
||||||
|
const fetchUserInfo = async () => {
|
||||||
|
const InfosUser = await UserInfos(authHeader)
|
||||||
|
console.log("Informações", InfosUser)
|
||||||
|
setIdUsuario(InfosUser.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchUserInfo()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleSave = (DictParaPatch) => {
|
||||||
|
var myHeaders = new Headers();
|
||||||
|
myHeaders.append("Content-Type", "application/json");
|
||||||
|
myHeaders.append('apikey', API_KEY)
|
||||||
|
myHeaders.append("authorization", authHeader)
|
||||||
|
|
||||||
|
console.log(DictParaPatch)
|
||||||
|
|
||||||
|
var raw = JSON.stringify({"patient_id": DictParaPatch.patient_id,
|
||||||
|
"doctor_id": DictParaPatch.doctor_id,
|
||||||
|
|
||||||
|
"duration_minutes": 30,
|
||||||
|
|
||||||
|
"chief_complaint": "Dor de cabeça há 3 ",
|
||||||
|
|
||||||
|
"created_by": idUsuario,
|
||||||
|
|
||||||
|
"scheduled_at": `${DictParaPatch.dataAtendimento}T${DictParaPatch.horarioInicio}:00.000Z`,
|
||||||
|
|
||||||
|
"appointment_type": DictParaPatch.tipo_consulta,
|
||||||
|
|
||||||
|
"patient_notes": "Prefiro horário pela manhã",
|
||||||
|
"insurance_provider": DictParaPatch.convenio,
|
||||||
|
"status": DictParaPatch.status,
|
||||||
|
"created_by": idUsuario
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// console.log(DictParaPatch)
|
||||||
|
//console.log(id)
|
||||||
|
|
||||||
|
var requestOptions = {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: myHeaders,
|
||||||
|
body: raw,
|
||||||
|
redirect: 'follow'
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${DictInfo.id}`, requestOptions)
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(result => console.log(result))
|
||||||
|
.catch(error => console.log('error', error));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FormNovaConsulta agendamento={DictInfo} setAgendamento={setDictInfo} onSave={handleSave}/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DoctorAgendamentoEditPage
|
||||||
@ -13,7 +13,7 @@ import { useAuth } from '../components/utils/AuthProvider.js';
|
|||||||
// ✨ NOVO: Caminho de importação corrigido com base na sua estrutura de pastas
|
// ✨ NOVO: Caminho de importação corrigido com base na sua estrutura de pastas
|
||||||
import AgendamentosMes from '../components/AgendarConsulta/DadosConsultasMock.js';
|
import AgendamentosMes from '../components/AgendarConsulta/DadosConsultasMock.js';
|
||||||
|
|
||||||
|
import { UserInfos } from '../components/utils/Functions-Endpoints/General.js';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import "../pages/style/Agendamento.css";
|
import "../pages/style/Agendamento.css";
|
||||||
import '../pages/style/FilaEspera.css';
|
import '../pages/style/FilaEspera.css';
|
||||||
@ -35,11 +35,15 @@ const Agendamento = ({setDictInfo}) => {
|
|||||||
const [DictAgendamentosOrganizados, setAgendamentosOrganizados ] = useState({})
|
const [DictAgendamentosOrganizados, setAgendamentosOrganizados ] = useState({})
|
||||||
|
|
||||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||||
const [AgendamentoFiltrado, setAgendamentoFiltrado] = useState()
|
const [showConfirmModal, setShowConfirmModal] = useState(false)
|
||||||
|
|
||||||
const [ListaDeMedicos, setListaDeMedicos] = useState([])
|
const [coresConsultas, setCoresConsultas] = useState([])
|
||||||
const [FiltredTodosMedicos, setFiltredTodosMedicos] = useState([])
|
|
||||||
const [searchTermDoctor, setSearchTermDoctor] = useState('');
|
const [listaConsultasID, setListaConsultaID] = useState([])
|
||||||
|
|
||||||
|
const [motivoCancelamento, setMotivoCancelamento] = useState("")
|
||||||
|
|
||||||
|
const [user, setUser] = useState({})
|
||||||
|
|
||||||
|
|
||||||
let authHeader = getAuthorizationHeader()
|
let authHeader = getAuthorizationHeader()
|
||||||
@ -51,14 +55,14 @@ const Agendamento = ({setDictInfo}) => {
|
|||||||
let paciente = await GetPatientByID(patient_id, authHeader);
|
let paciente = await GetPatientByID(patient_id, authHeader);
|
||||||
|
|
||||||
let dicionario = {
|
let dicionario = {
|
||||||
agendamento: agendamento,
|
...agendamento,
|
||||||
Infos: {
|
|
||||||
nome_nedico: medico.full_name,
|
nome_medico: medico[0].full_name,
|
||||||
doctor_id: medico.id,
|
doctor_id: medico.id,
|
||||||
patient_id: paciente[0].id,
|
patient_id: paciente[0].id,
|
||||||
paciente_nome: paciente[0].full_name,
|
paciente_nome: paciente[0].full_name,
|
||||||
paciente_cpf: paciente[0].cpf
|
paciente_cpf: paciente[0].cpf
|
||||||
}
|
|
||||||
};
|
};
|
||||||
return dicionario;
|
return dicionario;
|
||||||
};
|
};
|
||||||
@ -69,17 +73,19 @@ const Agendamento = ({setDictInfo}) => {
|
|||||||
// 1. Agrupamento (igual ao seu código original)
|
// 1. Agrupamento (igual ao seu código original)
|
||||||
for (const agendamento of listaTodosAgendamentos) {
|
for (const agendamento of listaTodosAgendamentos) {
|
||||||
if (agendamento.status === 'requested') {
|
if (agendamento.status === 'requested') {
|
||||||
// Recomenda-se usar Promise.all para melhorar a performance
|
|
||||||
// mas, para manter a estrutura, mantemos o await no loop.
|
|
||||||
let v = await ConfigurarFiladeEspera(agendamento.patient_id, agendamento.doctor_id, agendamento);
|
let v = await ConfigurarFiladeEspera(agendamento.patient_id, agendamento.doctor_id, agendamento);
|
||||||
ListaFilaDeEspera.push(v);
|
ListaFilaDeEspera.push(v);
|
||||||
} else {
|
} else {
|
||||||
const DiaAgendamento = agendamento.scheduled_at.split("T")[0];
|
|
||||||
|
const DiaAgendamento = agendamento.scheduled_at?.split("T")[0];
|
||||||
|
|
||||||
|
let novoAgendamento = await ConfigurarFiladeEspera(agendamento.patient_id, agendamento.doctor_id, agendamento);
|
||||||
|
|
||||||
if (DiaAgendamento in DictAgendamentosOrganizados) {
|
if (DiaAgendamento in DictAgendamentosOrganizados) {
|
||||||
DictAgendamentosOrganizados[DiaAgendamento].push(agendamento);
|
DictAgendamentosOrganizados[DiaAgendamento].push(novoAgendamento);
|
||||||
} else {
|
} else {
|
||||||
DictAgendamentosOrganizados[DiaAgendamento] = [agendamento];
|
DictAgendamentosOrganizados[DiaAgendamento] = [novoAgendamento];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,8 +123,23 @@ const Agendamento = ({setDictInfo}) => {
|
|||||||
setfilaEsperaData(ListaFilaDeEspera);
|
setfilaEsperaData(ListaFilaDeEspera);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
console.log(user, "usuario")
|
||||||
|
|
||||||
|
}, [user])
|
||||||
|
|
||||||
// Requisição inicial para mostrar os agendamentos do banco de dados
|
// Requisição inicial para mostrar os agendamentos do banco de dados
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
|
async function fetchDadosUser (){
|
||||||
|
let dado = await UserInfos(authHeader)
|
||||||
|
setUser(dado)
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchDadosUser()
|
||||||
|
|
||||||
|
|
||||||
var myHeaders = new Headers();
|
var myHeaders = new Headers();
|
||||||
myHeaders.append("Authorization", authHeader);
|
myHeaders.append("Authorization", authHeader);
|
||||||
myHeaders.append("apikey", API_KEY)
|
myHeaders.append("apikey", API_KEY)
|
||||||
@ -129,85 +150,42 @@ const Agendamento = ({setDictInfo}) => {
|
|||||||
redirect: 'follow'
|
redirect: 'follow'
|
||||||
};
|
};
|
||||||
|
|
||||||
fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?select&doctor_id&patient_id&status&scheduled_at&order&limit&offset", requestOptions)
|
fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?doctor_id=eq.${"078d2a67-b4c1-43c8-ae32-c1e75bb5b3df"}`, requestOptions)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(result => {FiltrarAgendamentos(result);})
|
.then(result => {FiltrarAgendamentos(result); console.log(result, "RESULTRADO DA API")})
|
||||||
.catch(error => console.log('error', error));
|
.catch(error => console.log('error', error));
|
||||||
|
|
||||||
const PegarTodosOsMedicos = async () => {
|
|
||||||
let lista = []
|
|
||||||
const TodosOsMedicos = await GetAllDoctors(authHeader)
|
|
||||||
|
|
||||||
for(let d = 0; TodosOsMedicos.length > d; d++){
|
|
||||||
lista.push({nomeMedico: TodosOsMedicos[d].full_name, idMedico: TodosOsMedicos[d].id })}
|
|
||||||
setListaDeMedicos(lista)
|
|
||||||
}
|
|
||||||
PegarTodosOsMedicos()
|
|
||||||
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log("mudou FiltredTodosMedicos:", FiltredTodosMedicos);
|
|
||||||
if (FiltredTodosMedicos.length === 1) {
|
|
||||||
const unicoMedico = FiltredTodosMedicos[0];
|
|
||||||
console.log(unicoMedico)
|
|
||||||
const idMedicoFiltrado = unicoMedico.idMedico;
|
|
||||||
console.log(`Médico único encontrado: ${unicoMedico.nomeMedico}. ID: ${idMedicoFiltrado}`);
|
|
||||||
|
|
||||||
const agendamentosDoMedico = filtrarAgendamentosPorMedico(
|
|
||||||
DictAgendamentosOrganizados,
|
|
||||||
idMedicoFiltrado
|
|
||||||
);
|
|
||||||
console.log(`Total de agendamentos filtrados para este médico: ${agendamentosDoMedico.length}`);
|
|
||||||
console.log("Lista completa de Agendamentos do Médico:", agendamentosDoMedico);
|
|
||||||
FiltrarAgendamentos(agendamentosDoMedico)
|
|
||||||
|
|
||||||
}
|
|
||||||
}, [FiltredTodosMedicos]);
|
|
||||||
|
|
||||||
const deleteConsulta = (selectedPatientId) => {
|
const deleteConsulta = (selectedPatientId) => {
|
||||||
console.log("tentando apagar")
|
var myHeaders = new Headers();
|
||||||
var myHeaders = new Headers();
|
myHeaders.append("Content-Type", "application/json");
|
||||||
myHeaders.append("Authorization", authHeader);
|
myHeaders.append('apikey', API_KEY)
|
||||||
myHeaders.append("apikey", API_KEY)
|
myHeaders.append("authorization", authHeader)
|
||||||
|
|
||||||
var requestOptions = {
|
|
||||||
method: 'DELETE',
|
|
||||||
redirect: 'follow',
|
|
||||||
headers: myHeaders
|
|
||||||
};
|
|
||||||
|
|
||||||
fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${selectedPatientId}`, requestOptions)
|
var raw = JSON.stringify({ "status":"cancelled",
|
||||||
.then(response => response.json())
|
"cancellation_reason": motivoCancelamento
|
||||||
.then(result => console.log(result))
|
});
|
||||||
.catch(error => console.log('error', error));
|
|
||||||
|
|
||||||
|
|
||||||
|
var requestOptions = {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: myHeaders,
|
||||||
|
body: raw,
|
||||||
|
redirect: 'follow'
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${selectedPatientId}`, requestOptions)
|
||||||
|
.then(response => {if(response.status !== 200)(console.log(response))})
|
||||||
|
.then(result => console.log(result))
|
||||||
|
.catch(error => console.log('error', error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filtra todos os agendamentos em um objeto aninhado (data -> [agendamentos])
|
|
||||||
* com base no ID do médico.
|
|
||||||
*
|
|
||||||
* @param {Object} dictAgendamentos - O dicionário de agendamentos.
|
|
||||||
* @param {string} idMedicoFiltrado - O ID do médico (doctor_id) para ser usado como filtro.
|
|
||||||
* @returns {Array} Um array contendo todos os agendamentos que correspondem ao idMedicoFiltrado.
|
|
||||||
*/
|
|
||||||
const filtrarAgendamentosPorMedico = (dictAgendamentos, idMedicoFiltrado) => {
|
|
||||||
|
|
||||||
// O corpo da função deve usar esses nomes de variáveis:
|
|
||||||
const todasAsListasDeAgendamentos = Object.values(dictAgendamentos);
|
|
||||||
|
|
||||||
const todosOsAgendamentos = todasAsListasDeAgendamentos.flat();
|
|
||||||
|
|
||||||
const agendamentosFiltrados = todosOsAgendamentos.filter(agendamento =>
|
|
||||||
agendamento.doctor_id === idMedicoFiltrado
|
|
||||||
);
|
|
||||||
|
|
||||||
return agendamentosFiltrados;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Lógica para filtrar os dados da AGENDA (AgendamentosMes)
|
// Lógica para filtrar os dados da AGENDA (AgendamentosMes)
|
||||||
const filteredAgendamentos = useMemo(() => {
|
const filteredAgendamentos = useMemo(() => {
|
||||||
@ -249,26 +227,30 @@ const filtrarAgendamentosPorMedico = (dictAgendamentos, idMedicoFiltrado) => {
|
|||||||
return ListaDiasDatas
|
return ListaDiasDatas
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClickAgendamento = (agendamento) => {
|
|
||||||
if (agendamento.status !== 'vazio') return
|
const confirmConsulta = (selectedPatientId) => {
|
||||||
else setPageConsulta(true)
|
var myHeaders = new Headers();
|
||||||
};
|
myHeaders.append("Content-Type", "application/json");
|
||||||
|
myHeaders.append('apikey', API_KEY)
|
||||||
|
myHeaders.append("authorization", authHeader)
|
||||||
|
|
||||||
|
|
||||||
const handleSearchMedicos = (term) => {
|
var raw = JSON.stringify({ "status":"confirmed"
|
||||||
setSearchTermDoctor(term);
|
});
|
||||||
if (term.trim() === '') {
|
|
||||||
setFiltredTodosMedicos([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lógica simples de filtragem:
|
|
||||||
const filtered = ListaDeMedicos.filter(medico =>
|
|
||||||
medico.nomeMedico.toLowerCase().includes(term.toLowerCase())
|
|
||||||
);
|
|
||||||
setFiltredTodosMedicos(filtered);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
var requestOptions = {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: myHeaders,
|
||||||
|
body: raw,
|
||||||
|
redirect: 'follow'
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${selectedPatientId}`, requestOptions)
|
||||||
|
.then(response => {if(response.status !== 200)(console.log(response))})
|
||||||
|
.then(result => console.log(result))
|
||||||
|
.catch(error => console.log('error', error));
|
||||||
|
}
|
||||||
|
|
||||||
const handleClickCancel = () => setPageConsulta(false)
|
const handleClickCancel = () => setPageConsulta(false)
|
||||||
|
|
||||||
@ -289,78 +271,10 @@ const handleSearchMedicos = (term) => {
|
|||||||
<div className='atendimento-eprocura'>
|
<div className='atendimento-eprocura'>
|
||||||
|
|
||||||
<div className='busca-atendimento-container'>
|
<div className='busca-atendimento-container'>
|
||||||
|
|
||||||
<div className='input-e-dropdown-wrapper'>
|
|
||||||
|
|
||||||
<div className='busca-atendimento'>
|
|
||||||
<div>
|
|
||||||
<i className="fa-solid fa-calendar-day"></i>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Filtrar atendimento por médico..."
|
|
||||||
value={searchTermDoctor}
|
|
||||||
onChange={(e) => handleSearchMedicos(e.target.value)} // Chama a nova função de filtro
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* DROPDOWN (RENDERIZAÇÃO CONDICIONAL) */}
|
|
||||||
{searchTermDoctor && FiltredTodosMedicos.length > 0 && (
|
|
||||||
<div className='dropdown-medicos'>
|
|
||||||
{FiltredTodosMedicos.map((medico) => (
|
|
||||||
<div
|
|
||||||
key={medico.id}
|
|
||||||
className='dropdown-item'
|
|
||||||
onClick={() => {
|
|
||||||
// Ação ao selecionar o médico
|
|
||||||
setSearchTermDoctor(medico.nomeMedico); // Preenche o input
|
|
||||||
//setFiltredTodosMedicos([]); // Fecha o dropdown
|
|
||||||
// Lógica adicional, como selecionar o ID do médico...
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p>{medico.nomeMedico} </p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div className='unidade-selecionarprofissional'>
|
|
||||||
<select>
|
|
||||||
<option value="" disabled selected >Unidade</option>
|
|
||||||
<option value="">Unidade Central</option>
|
|
||||||
<option value="">Unidade Zona Norte</option>
|
|
||||||
<option value="">Unidade Zona Oeste</option>
|
|
||||||
</select>
|
|
||||||
<input type="text" placeholder='Selecionar profissional' />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='container-btns-agenda-fila_esepera'>
|
|
||||||
<button
|
|
||||||
className={`btn-agenda ${FiladeEspera === false ? "opc-agenda-ativo" : ""}`}
|
|
||||||
onClick={() => {
|
|
||||||
setFiladeEspera(false);
|
|
||||||
setSearchTerm('');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Agenda
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className={`btn-fila-espera ${FiladeEspera === true ? "opc-filaespera-ativo" : ""}`}
|
|
||||||
onClick={() => {
|
|
||||||
setFiladeEspera(true);
|
|
||||||
setSearchTerm('');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Fila de espera
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section className='calendario-ou-filaespera'>
|
<section className='calendario-ou-filaespera'>
|
||||||
{FiladeEspera === false ?
|
|
||||||
(
|
|
||||||
<div className='calendario'>
|
<div className='calendario'>
|
||||||
<div>
|
<div>
|
||||||
<section className='btns-e-legenda-container'>
|
<section className='btns-e-legenda-container'>
|
||||||
@ -383,80 +297,34 @@ const handleSearchMedicos = (term) => {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{tabela === "diario" && <TabelaAgendamentoDia handleClickAgendamento={handleClickAgendamento} agendamentos={DictAgendamentosOrganizados} setShowDeleteModal={setShowDeleteModal} setSelectedId={setSelectedId} setDictInfo={setDictInfo} />}
|
{tabela === "diario" && <TabelaAgendamentoDia agendamentos={DictAgendamentosOrganizados}
|
||||||
{tabela === 'semanal' && <TabelaAgendamentoSemana agendamentos={DictAgendamentosOrganizados} ListarDiasdoMes={ListarDiasdoMes} setShowDeleteModal={setShowDeleteModal} setSelectedId={setSelectedId} setDictInfo={setDictInfo}/>}
|
setShowDeleteModal={setShowDeleteModal} setSelectedId={setSelectedId} setDictInfo={setDictInfo}
|
||||||
{tabela === 'mensal' && <TabelaAgendamentoMes ListarDiasdoMes={ListarDiasdoMes} aplicarCores={true} agendamentos={DictAgendamentosOrganizados} setShowDeleteModal={setShowDeleteModal} setSelectedId={setSelectedId} setDictInfo={setDictInfo} />}
|
listaConsultasID={listaConsultasID}
|
||||||
|
setListaConsultaID={setListaConsultaID} coresConsultas={coresConsultas} setShowConfirmModal={setShowConfirmModal}
|
||||||
|
|
||||||
|
/>}
|
||||||
|
|
||||||
|
|
||||||
|
{tabela === 'semanal' && <TabelaAgendamentoSemana agendamentos={DictAgendamentosOrganizados} ListarDiasdoMes={ListarDiasdoMes}
|
||||||
|
setShowDeleteModal={setShowDeleteModal} setSelectedId={setSelectedId} setDictInfo={setDictInfo}
|
||||||
|
listaConsultasID={listaConsultasID} setListaConsultaID={setListaConsultaID} coresConsultas={coresConsultas} setShowConfirmModal={setShowConfirmModal} />}
|
||||||
|
|
||||||
|
|
||||||
|
{tabela === 'mensal' && <TabelaAgendamentoMes ListarDiasdoMes={ListarDiasdoMes} aplicarCores={true} agendamentos={DictAgendamentosOrganizados}
|
||||||
|
setShowDeleteModal={setShowDeleteModal} setSelectedId={setSelectedId} setDictInfo={setDictInfo} listaConsultasID={listaConsultasID}
|
||||||
|
setListaConsultaID={setListaConsultaID} coresConsultas={coresConsultas} setShowConfirmModal={setShowConfirmModal} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
|
||||||
:
|
|
||||||
(
|
|
||||||
<div className="fila-container">
|
|
||||||
<div className="fila-header">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Pesquisar na fila de espera..."
|
|
||||||
className="busca-fila-espera"
|
|
||||||
value={searchTerm}
|
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
|
||||||
/>
|
|
||||||
<h2 className="fila-titulo">Fila de Espera</h2>
|
|
||||||
</div>
|
|
||||||
<table className="fila-tabela">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Nome</th>
|
|
||||||
<th>Telefone</th>
|
|
||||||
|
|
||||||
<th>Telefone</th>
|
</section>
|
||||||
<th>Entrou na fila de espera</th>
|
|
||||||
<th>Ações</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{filaEsperaData.map((item, index) => (
|
|
||||||
<tr key={index}>
|
|
||||||
<td> <p>{item.Infos?.paciente_nome} </p> </td>
|
|
||||||
<td><p>{} </p></td>
|
|
||||||
<td>{}</td>
|
|
||||||
<td>{}</td>
|
|
||||||
<td> <div className="d-flex gap-2">
|
|
||||||
|
|
||||||
<button className="btn btn-sm btn-edit"
|
|
||||||
onClick={() => {
|
|
||||||
console.log(item, 'item')
|
|
||||||
navigate(`${2}/edit`)
|
|
||||||
setDictInfo(item)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i className="bi bi-pencil me-1"></i> Editar
|
|
||||||
</button>
|
|
||||||
|
|
||||||
|
|
||||||
<button
|
|
||||||
className="btn btn-sm btn-delete"
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedId(item.agendamento.id)
|
|
||||||
setShowDeleteModal(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i className="bi bi-trash me-1"></i> Excluir
|
|
||||||
</button>
|
|
||||||
</div></td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<AgendamentoCadastroManager setPageConsulta={setPageConsulta} />
|
<AgendamentoCadastroManager setPageConsulta={setPageConsulta} Dict={{nome_medico:user?.profile?.full_name}}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showDeleteModal && (
|
|
||||||
|
{showConfirmModal &&(
|
||||||
<div
|
<div
|
||||||
className="modal fade show"
|
className="modal fade show"
|
||||||
style={{
|
style={{
|
||||||
@ -469,11 +337,74 @@ const handleSearchMedicos = (term) => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="modal-dialog modal-dialog-centered">
|
<div className="modal-dialog modal-dialog-centered">
|
||||||
|
<div className="modal-content">
|
||||||
|
|
||||||
|
<div className="modal-header bg-success">
|
||||||
|
<h5 className="modal-title">
|
||||||
|
Confirmação de edição
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="modal-body">
|
||||||
|
<p className="mb-0 fs-5">
|
||||||
|
Tem certeza que deseja retirar o cancelamento ?
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="modal-footer">
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary"
|
||||||
|
onClick={() => {setShowConfirmModal(false); setSelectedId("")}}
|
||||||
|
>
|
||||||
|
Cancelar
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-success"
|
||||||
|
onClick={() => {confirmConsulta(selectedID);setShowConfirmModal(false)
|
||||||
|
let lista_cores = coresConsultas
|
||||||
|
|
||||||
|
let lista = listaConsultasID
|
||||||
|
|
||||||
|
lista.push(selectedID)
|
||||||
|
lista_cores.push("confirmed")
|
||||||
|
|
||||||
|
setCoresConsultas(lista_cores)
|
||||||
|
|
||||||
|
setListaConsultaID(lista)
|
||||||
|
}}
|
||||||
|
|
||||||
|
>
|
||||||
|
<i className="bi bi-trash me-1"></i> Confirmar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>)}
|
||||||
|
|
||||||
|
{showDeleteModal && (
|
||||||
|
<div
|
||||||
|
className="modal fade show"
|
||||||
|
style={{
|
||||||
|
display: "block",
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||||
|
}}
|
||||||
|
tabIndex="-1"
|
||||||
|
onClick={(e) =>
|
||||||
|
e.target.classList.contains("modal") && setShowDeleteModal(false)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="modal-dialog modal-dialog-centered">
|
||||||
<div className="modal-content">
|
<div className="modal-content">
|
||||||
|
|
||||||
<div className="modal-header bg-danger bg-opacity-25">
|
<div className="modal-header bg-danger bg-opacity-25">
|
||||||
<h5 className="modal-title text-danger">
|
<h5 className="modal-title text-danger">
|
||||||
Confirmação de Exclusão
|
Confirmação de Cancelamento
|
||||||
</h5>
|
</h5>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -484,8 +415,12 @@ const handleSearchMedicos = (term) => {
|
|||||||
|
|
||||||
<div className="modal-body">
|
<div className="modal-body">
|
||||||
<p className="mb-0 fs-5">
|
<p className="mb-0 fs-5">
|
||||||
Tem certeza que deseja excluir este agendamento?
|
Qual o motivo do cancelamento?
|
||||||
</p>
|
</p>
|
||||||
|
<div className='campo-de-input'>
|
||||||
|
|
||||||
|
<textarea className='input-modal' value={motivoCancelamento} onChange={(e) => setMotivoCancelamento(e.target.value)} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="modal-footer">
|
<div className="modal-footer">
|
||||||
@ -493,7 +428,10 @@ const handleSearchMedicos = (term) => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
onClick={() => setShowDeleteModal(false)}
|
onClick={() => {
|
||||||
|
setShowDeleteModal(false);
|
||||||
|
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Cancelar
|
Cancelar
|
||||||
</button>
|
</button>
|
||||||
@ -502,7 +440,24 @@ const handleSearchMedicos = (term) => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-danger"
|
className="btn btn-danger"
|
||||||
onClick={() => {deleteConsulta(selectedID);setShowDeleteModal(false)}}
|
onClick={() => {
|
||||||
|
|
||||||
|
deleteConsulta(selectedID)
|
||||||
|
setShowDeleteModal(false)
|
||||||
|
let lista_cores = coresConsultas
|
||||||
|
|
||||||
|
let lista = listaConsultasID
|
||||||
|
|
||||||
|
lista.push(selectedID)
|
||||||
|
lista_cores.push("cancelled")
|
||||||
|
|
||||||
|
setCoresConsultas(lista_cores)
|
||||||
|
|
||||||
|
setListaConsultaID(lista)
|
||||||
|
|
||||||
|
console.log("lista", lista)
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
>
|
>
|
||||||
<i className="bi bi-trash me-1"></i> Excluir
|
<i className="bi bi-trash me-1"></i> Excluir
|
||||||
@ -513,6 +468,8 @@ const handleSearchMedicos = (term) => {
|
|||||||
</div>)}
|
</div>)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,6 @@ const CardConsultaPaciente = ({consulta, setConsulta, setSelectedId, setShowDel
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const ids = useMemo(() => {
|
const ids = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
doctor_id: consulta?.doctor_id,
|
doctor_id: consulta?.doctor_id,
|
||||||
@ -45,9 +44,6 @@ const CardConsultaPaciente = ({consulta, setConsulta, setSelectedId, setShowDel
|
|||||||
}, [ids, authHeader]);
|
}, [ids, authHeader]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
console.log(consulta, "dento do card")
|
|
||||||
|
|
||||||
let horario = consulta.scheduled_at.split("T")[1]
|
let horario = consulta.scheduled_at.split("T")[1]
|
||||||
let Data = consulta.scheduled_at.split("T")[0]
|
let Data = consulta.scheduled_at.split("T")[0]
|
||||||
|
|
||||||
|
|||||||
@ -30,14 +30,16 @@ const ConsultaCadastroManager = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleSave = (Dict) => {
|
const handleSave = (Dict) => {
|
||||||
|
|
||||||
let DataAtual = dayjs()
|
let DataAtual = dayjs()
|
||||||
var myHeaders = new Headers();
|
var myHeaders = new Headers();
|
||||||
myHeaders.append("apikey", API_KEY);
|
myHeaders.append("apikey", API_KEY);
|
||||||
myHeaders.append("Authorization", authHeader);
|
myHeaders.append("Authorization", authHeader);
|
||||||
myHeaders.append("Content-Type", "application/json");
|
myHeaders.append("Content-Type", "application/json");
|
||||||
|
|
||||||
|
|
||||||
var raw = JSON.stringify({
|
var raw = JSON.stringify({
|
||||||
"patient_id": Dict.patient_id,
|
"patient_id": "6e7f8829-0574-42df-9290-8dbb70f75ada",
|
||||||
"doctor_id": Dict.doctor_id,
|
"doctor_id": Dict.doctor_id,
|
||||||
"scheduled_at": `${Dict.dataAtendimento}T${Dict.horarioInicio}:00.000Z`,
|
"scheduled_at": `${Dict.dataAtendimento}T${Dict.horarioInicio}:00.000Z`,
|
||||||
"duration_minutes": 30,
|
"duration_minutes": 30,
|
||||||
@ -45,7 +47,7 @@ const ConsultaCadastroManager = () => {
|
|||||||
|
|
||||||
"patient_notes": "Prefiro horário pela manhã",
|
"patient_notes": "Prefiro horário pela manhã",
|
||||||
"insurance_provider": Dict.convenio,
|
"insurance_provider": Dict.convenio,
|
||||||
"status": Dict.status,
|
"status": "confirmed",
|
||||||
"created_by": idUsuario
|
"created_by": idUsuario
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { GetPatientByID } from '../components/utils/Functions-Endpoints/Patient'
|
|||||||
// 1. Importe o useNavigate
|
// 1. Importe o useNavigate
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
const ConsultaEditPage = ({ dadosConsulta }) => {
|
const ConsultaEditPage = ({ DictInfo }) => {
|
||||||
// 2. Crie a instância do navigate
|
// 2. Crie a instância do navigate
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
@ -17,20 +17,23 @@ const ConsultaEditPage = ({ dadosConsulta }) => {
|
|||||||
const authHeader = getAuthorizationHeader();
|
const authHeader = getAuthorizationHeader();
|
||||||
|
|
||||||
const [idUsuario, setIDusuario] = useState(null);
|
const [idUsuario, setIDusuario] = useState(null);
|
||||||
const [DictInfo, setDict] = useState({});
|
const [Dict, setDict] = useState({});
|
||||||
const [Medico, setMedico] = useState(null);
|
const [Medico, setMedico] = useState(null);
|
||||||
const [Paciente, setPaciente] = useState(null);
|
const [Paciente, setPaciente] = useState(null);
|
||||||
|
|
||||||
|
console.log("dentro do edit", DictInfo)
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDict({ ...dadosConsulta });
|
setDict({ ...DictInfo });
|
||||||
|
|
||||||
const fetchInitialData = async () => {
|
const fetchInitialData = async () => {
|
||||||
if (dadosConsulta.doctor_id) {
|
if (DictInfo.doctor_id) {
|
||||||
const medicoData = await GetDoctorByID(dadosConsulta.doctor_id, authHeader);
|
const medicoData = await GetDoctorByID(DictInfo.doctor_id, authHeader);
|
||||||
setMedico(medicoData[0]);
|
setMedico(medicoData[0]);
|
||||||
}
|
}
|
||||||
if (dadosConsulta.patient_id) {
|
if (DictInfo.patient_id) {
|
||||||
const pacienteData = await GetPatientByID(dadosConsulta.patient_id, authHeader);
|
const pacienteData = await GetPatientByID(DictInfo.patient_id, authHeader);
|
||||||
setPaciente(pacienteData[0]);
|
setPaciente(pacienteData[0]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -43,17 +46,17 @@ const ConsultaEditPage = ({ dadosConsulta }) => {
|
|||||||
fetchUserInfo();
|
fetchUserInfo();
|
||||||
fetchInitialData();
|
fetchInitialData();
|
||||||
|
|
||||||
}, [dadosConsulta, authHeader]);
|
}, [DictInfo, authHeader]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Medico) {
|
if (Medico) {
|
||||||
setDict(prevDict => ({
|
setDict(prevDict => ({
|
||||||
...prevDict,
|
...prevDict,
|
||||||
medico_nome: Medico?.full_name,
|
medico_nome: Medico?.full_name,
|
||||||
dataAtendimento: dadosConsulta.scheduled_at?.split("T")[0]
|
dataAtendimento: DictInfo.scheduled_at?.split("T")[0]
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [Medico, dadosConsulta.scheduled_at]);
|
}, [Medico, DictInfo.scheduled_at]);
|
||||||
|
|
||||||
const handleSave = async (DictParaPatch) => {
|
const handleSave = async (DictParaPatch) => {
|
||||||
try {
|
try {
|
||||||
@ -112,7 +115,7 @@ const ConsultaEditPage = ({ dadosConsulta }) => {
|
|||||||
<div>
|
<div>
|
||||||
{}
|
{}
|
||||||
<FormConsultaPaciente
|
<FormConsultaPaciente
|
||||||
agendamento={DictInfo}
|
agendamento={Dict}
|
||||||
setAgendamento={setDict}
|
setAgendamento={setDict}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
|
|||||||
@ -1,36 +1,115 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import "./style.css"
|
import "./style.css"
|
||||||
|
import "../pages/style/TablePaciente.css"
|
||||||
import CardConsultaPaciente from './CardConsultaPaciente'
|
import CardConsultaPaciente from './CardConsultaPaciente'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState, useMemo } from 'react'
|
||||||
import API_KEY from '../components/utils/apiKeys'
|
import API_KEY from '../components/utils/apiKeys'
|
||||||
import { useAuth } from '../components/utils/AuthProvider'
|
import { useAuth } from '../components/utils/AuthProvider'
|
||||||
|
import { GetPatientByID } from '../components/utils/Functions-Endpoints/Patient'
|
||||||
|
import { GetDoctorByID } from '../components/utils/Functions-Endpoints/Doctor'
|
||||||
|
|
||||||
const ConsultasPaciente = ({ setConsulta }) => {
|
import { UserInfos } from '../components/utils/Functions-Endpoints/General'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import TabelaAgendamentoDia from "../components/AgendarConsulta/TabelaAgendamentoDia"
|
||||||
|
|
||||||
|
const ConsultasPaciente = ({ setDictInfo }) => {
|
||||||
const { getAuthorizationHeader } = useAuth()
|
const { getAuthorizationHeader } = useAuth()
|
||||||
|
const [agendamentosOrganizados, setAgendamentosOrganizados] = useState({})
|
||||||
|
const [listaTodasConsultas, setListaTodasConsultas] = useState([])
|
||||||
|
const [patientID, setPatientID] = useState("")
|
||||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||||
const [selectedID, setSelectedId] = useState("")
|
const [selectedID, setSelectedId] = useState("")
|
||||||
let authHeader = getAuthorizationHeader()
|
let authHeader = getAuthorizationHeader()
|
||||||
|
|
||||||
|
const [motivoCancelamento, setMotivoCancelamento] = useState("")
|
||||||
|
|
||||||
const [consultas, setConsultas] = useState([])
|
const [consultas, setConsultas] = useState([])
|
||||||
|
|
||||||
const FiltrarAgendamentos = (agendamentos, id) => {
|
const [consultasOrganizadas, setConsultasOrganizadas] = useState({})
|
||||||
if (!agendamentos || !Array.isArray(agendamentos)) {
|
const [filaDeEspera, setFilaDeEspera] = useState([])
|
||||||
console.error("A lista de agendamentos é inválida.");
|
const [viewFila, setViewFila] = useState(false)
|
||||||
setConsultas([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const consultasFiltradas = agendamentos.filter(agendamento => {
|
const [listaConsultasID, setListaConsultaID] = useState([])
|
||||||
return agendamento.patient_id && agendamento.patient_id.toString() === id.toString();
|
const [coresConsultas,setCoresConsultas] = useState([])
|
||||||
});
|
|
||||||
|
const [showConfirmModal, setShowConfirmModal] = useState(false)
|
||||||
|
|
||||||
|
// Estados para a tabela da fila de espera
|
||||||
|
const [searchTermFila, setSearchTermFila] = useState('');
|
||||||
|
const [filtroMedicoFila, setFiltroMedicoFila] = useState('Todos');
|
||||||
|
const [paginaAtualFila, setPaginaAtualFila] = useState(1);
|
||||||
|
const [itensPorPaginaFila, setItensPorPaginaFila] = useState(10);
|
||||||
|
|
||||||
console.log(consultasFiltradas)
|
|
||||||
setConsultas(consultasFiltradas);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
|
console.log(listaConsultasID, coresConsultas, "ojwhdofigewfey7few0fr74r")
|
||||||
|
|
||||||
|
}, [coresConsultas, listaConsultasID])
|
||||||
|
|
||||||
|
useMemo(() => {
|
||||||
|
let conjuntoConsultas = {}
|
||||||
|
let filaEspera = []
|
||||||
|
|
||||||
|
const fetchInfosConsultas = async (consulta) => {
|
||||||
|
//console.log(doctor, "PACIENTE TRAZIDO PELO ")
|
||||||
|
|
||||||
|
//let consultaMelhorada = {...consulta, paciente_nome:paciente[0].full_name, medico_nome:doctor[0].full_name }
|
||||||
|
|
||||||
|
//console.log(consultaMelhorada,"ID DO MEDICO")
|
||||||
|
|
||||||
|
for(let i = 0; listaTodasConsultas.length > i; i++){
|
||||||
|
|
||||||
|
let consulta = listaTodasConsultas[i]
|
||||||
|
|
||||||
|
let doctor = await GetDoctorByID(consulta.doctor_id, authHeader)
|
||||||
|
let paciente = await GetPatientByID(consulta.patient_id, authHeader)
|
||||||
|
|
||||||
|
consulta = {...consulta, medico_nome:doctor[0]?.full_name, paciente_nome:paciente[0]?.full_name}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(consulta.status === "requested"){
|
||||||
|
|
||||||
|
filaEspera.push(consulta)
|
||||||
|
|
||||||
|
}else{
|
||||||
|
|
||||||
|
let data = consulta.scheduled_at.split("T")[0]
|
||||||
|
let chavesConsultas = Object.keys(conjuntoConsultas)
|
||||||
|
|
||||||
|
if(chavesConsultas.includes(data)){
|
||||||
|
let lista = conjuntoConsultas[data]
|
||||||
|
|
||||||
|
lista.push(consulta)
|
||||||
|
|
||||||
|
conjuntoConsultas = {...conjuntoConsultas, [data]:lista}
|
||||||
|
}else{
|
||||||
|
conjuntoConsultas = {...conjuntoConsultas, [data]:[consulta] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setConsultasOrganizadas(conjuntoConsultas)
|
||||||
|
setFilaDeEspera(filaEspera)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("so muda")
|
||||||
|
if(!listaTodasConsultas.length) return
|
||||||
|
|
||||||
|
console.log(filaEspera, "fila de espera")
|
||||||
|
fetchInfosConsultas();
|
||||||
|
|
||||||
|
}, [listaTodasConsultas])
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let userInfos = UserInfos(authHeader)
|
||||||
|
|
||||||
const fetchConsultas = async () => {
|
const fetchConsultas = async () => {
|
||||||
try {
|
try {
|
||||||
const myHeaders = new Headers();
|
const myHeaders = new Headers();
|
||||||
@ -43,18 +122,45 @@ const ConsultasPaciente = ({ setConsulta }) => {
|
|||||||
redirect: 'follow'
|
redirect: 'follow'
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?select=*", requestOptions);
|
const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?patient_id=eq.${"6e7f8829-0574-42df-9290-8dbb70f75ada"}`, requestOptions);
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
FiltrarAgendamentos(result, "6e7f8829-0574-42df-9290-8dbb70f75ada");
|
setListaTodasConsultas(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('error', error);
|
console.log('error', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchConsultas();
|
fetchConsultas();
|
||||||
}, [authHeader]);
|
}, []);
|
||||||
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
|
||||||
|
const confirmConsulta = (selectedPatientId) => {
|
||||||
|
var myHeaders = new Headers();
|
||||||
|
myHeaders.append("Content-Type", "application/json");
|
||||||
|
myHeaders.append('apikey', API_KEY)
|
||||||
|
myHeaders.append("authorization", authHeader)
|
||||||
|
|
||||||
|
|
||||||
|
var raw = JSON.stringify({ "status":"confirmed"
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var requestOptions = {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: myHeaders,
|
||||||
|
body: raw,
|
||||||
|
redirect: 'follow'
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${selectedPatientId}`, requestOptions)
|
||||||
|
.then(response => {if(response.status !== 200)(console.log(response))})
|
||||||
|
.then(result => console.log(result))
|
||||||
|
.catch(error => console.log('error', error));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
const deleteConsulta = async (ID) => {
|
const deleteConsulta = async (ID) => {
|
||||||
try {
|
try {
|
||||||
const myHeaders = new Headers();
|
const myHeaders = new Headers();
|
||||||
@ -62,7 +168,7 @@ const ConsultasPaciente = ({ setConsulta }) => {
|
|||||||
myHeaders.append('apikey', API_KEY);
|
myHeaders.append('apikey', API_KEY);
|
||||||
myHeaders.append("authorization", authHeader);
|
myHeaders.append("authorization", authHeader);
|
||||||
|
|
||||||
const raw = JSON.stringify({ "status": "cancelled" });
|
const raw = JSON.stringify({ "status": "cancelled", "cancellation_reason":motivoCancelamento });
|
||||||
|
|
||||||
const requestOptions = {
|
const requestOptions = {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
@ -92,54 +198,463 @@ const ConsultasPaciente = ({ setConsulta }) => {
|
|||||||
<div>
|
<div>
|
||||||
<h1> Gerencie suas consultas</h1>
|
<h1> Gerencie suas consultas</h1>
|
||||||
|
|
||||||
|
{/*Adicionei esse className para poder ter o fundo branco presente no style, mesmo não sendo para um form */}
|
||||||
<div className='form-container'>
|
<div className='form-container'>
|
||||||
|
|
||||||
|
<div className='btns-container'>
|
||||||
<button className="btn btn-primary" onClick={() => { navigate("criar") }}>
|
<button className="btn btn-primary" onClick={() => { navigate("criar") }}>
|
||||||
<i className="bi bi-plus-circle"></i> Adicionar Consulta
|
<i className="bi bi-plus-circle"></i> Adicionar Consulta
|
||||||
</button>
|
</button>
|
||||||
|
{!viewFila ?
|
||||||
|
<button onClick={() => setViewFila(true)} className="btn btn-primary">Ver fila de espera</button>
|
||||||
|
:
|
||||||
|
<button onClick={() => setViewFila(false)} className="btn btn-primary">Ver consultas </button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>Seus próximos atendimentos</h2>
|
|
||||||
|
|
||||||
{consultas.map((consulta) => (
|
|
||||||
<CardConsultaPaciente key={consulta.id} consulta={consulta} setConsulta={setConsulta} setShowDeleteModal={setShowDeleteModal} setSelectedId={setSelectedId} />
|
|
||||||
))}
|
|
||||||
|
|
||||||
{showDeleteModal &&
|
{viewFila ?
|
||||||
<div className="modal-dialog modal-dialog-centered">
|
<div className="page-content table-paciente-container">
|
||||||
<div className="modal-content">
|
<section className="row">
|
||||||
<div className="modal-header bg-danger bg-opacity-25">
|
<div className="col-12">
|
||||||
<h5 className="modal-title text-danger">
|
<div className="card table-paciente-card">
|
||||||
Confirmação de Exclusão
|
<div className="card-header">
|
||||||
</h5>
|
<h4 className="card-title mb-0">Fila de Espera</h4>
|
||||||
<button
|
</div>
|
||||||
type="button"
|
|
||||||
className="btn-close"
|
<div className="card-body">
|
||||||
onClick={() => setShowDeleteModal(false)}
|
{/* Filtros */}
|
||||||
></button>
|
<div className="card p-3 mb-3 table-paciente-filters">
|
||||||
</div>
|
<h5 className="mb-3">
|
||||||
<div className="modal-body">
|
<i className="bi bi-funnel-fill me-2 text-primary"></i> Filtros
|
||||||
<p className="mb-0 fs-5">
|
</h5>
|
||||||
Tem certeza que deseja excluir este agendamento?
|
|
||||||
</p>
|
<div className="mb-3">
|
||||||
</div>
|
<input
|
||||||
<div className="modal-footer">
|
type="text"
|
||||||
<button
|
className="form-control"
|
||||||
type="button"
|
placeholder="Buscar por nome do paciente ou CPF..."
|
||||||
className="btn btn-primary"
|
value={searchTermFila}
|
||||||
onClick={() => setShowDeleteModal(false)}
|
onChange={(e) => setSearchTermFila(e.target.value)}
|
||||||
>
|
/>
|
||||||
Cancelar
|
<small className="text-muted">
|
||||||
</button>
|
Digite o nome completo ou número do CPF
|
||||||
<button
|
</small>
|
||||||
type="button"
|
</div>
|
||||||
className="btn btn-danger"
|
|
||||||
onClick={() => { deleteConsulta(selectedID); setShowDeleteModal(false) }}
|
<div className="mb-3">
|
||||||
>
|
<input
|
||||||
<i className="bi bi-trash me-1"></i> Excluir
|
type="text"
|
||||||
</button>
|
className="form-control"
|
||||||
</div>
|
placeholder="Filtrar por nome do médico..."
|
||||||
|
value={filtroMedicoFila === 'Todos' ? '' : filtroMedicoFila}
|
||||||
|
onChange={(e) => setFiltroMedicoFila(e.target.value || 'Todos')}
|
||||||
|
/>
|
||||||
|
<small className="text-muted">
|
||||||
|
Digite o nome do médico para filtrar
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="d-flex justify-content-end">
|
||||||
|
<button
|
||||||
|
className="btn btn-outline-secondary btn-sm"
|
||||||
|
onClick={() => {
|
||||||
|
setSearchTermFila('');
|
||||||
|
setFiltroMedicoFila('Todos');
|
||||||
|
setPaginaAtualFila(1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="bi bi-arrow-clockwise me-1"></i> Limpar Filtros
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-3">
|
||||||
|
<div className="contador-pacientes">
|
||||||
|
{(() => {
|
||||||
|
const filtrados = filaDeEspera.filter((item) => {
|
||||||
|
const buscaNome = item?.paciente_nome?.toLowerCase().includes(searchTermFila.toLowerCase());
|
||||||
|
const buscaCPF = item?.paciente_cpf?.toLowerCase().includes(searchTermFila.toLowerCase());
|
||||||
|
const passaBusca = searchTermFila === "" || buscaNome || buscaCPF;
|
||||||
|
const passaMedico = filtroMedicoFila === "Todos" || item?.medico_nome?.toLowerCase().includes(filtroMedicoFila.toLowerCase());
|
||||||
|
return passaBusca && passaMedico;
|
||||||
|
});
|
||||||
|
return filtrados.length;
|
||||||
|
})()} DE {filaDeEspera.length} SOLICITAÇÕES ENCONTRADAS
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Filtros Ativos */}
|
||||||
|
{(searchTermFila || filtroMedicoFila !== "Todos") && (
|
||||||
|
<div className="alert alert-info mb-3 filters-active">
|
||||||
|
<strong>Filtros ativos:</strong>
|
||||||
|
<div className="mt-1">
|
||||||
|
{searchTermFila && <span className="badge bg-primary me-2">Busca: "{searchTermFila}"</span>}
|
||||||
|
{filtroMedicoFila !== "Todos" && <span className="badge bg-primary me-2">Médico: {filtroMedicoFila}</span>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Tabela */}
|
||||||
|
<div className="table-responsive">
|
||||||
|
<table className="table table-striped table-hover table-paciente-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Nome do Paciente</th>
|
||||||
|
<th>CPF</th>
|
||||||
|
<th>Médico Solicitado</th>
|
||||||
|
<th>Data da Solicitação</th>
|
||||||
|
<th>Ações</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{(() => {
|
||||||
|
// Filtrar dados
|
||||||
|
const filaFiltrada = filaDeEspera.filter((item) => {
|
||||||
|
const buscaNome = item?.paciente_nome?.toLowerCase().includes(searchTermFila.toLowerCase());
|
||||||
|
const buscaCPF = item?.paciente_cpf?.toLowerCase().includes(searchTermFila.toLowerCase());
|
||||||
|
const passaBusca = searchTermFila === "" || buscaNome || buscaCPF;
|
||||||
|
const passaMedico = filtroMedicoFila === "Todos" || item?.medico_nome?.toLowerCase().includes(filtroMedicoFila.toLowerCase());
|
||||||
|
return passaBusca && passaMedico;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Paginação
|
||||||
|
const totalPaginasFila = Math.ceil(filaFiltrada.length / itensPorPaginaFila);
|
||||||
|
const indiceInicial = (paginaAtualFila - 1) * itensPorPaginaFila;
|
||||||
|
const indiceFinal = indiceInicial + itensPorPaginaFila;
|
||||||
|
const filaPaginada = filaFiltrada.slice(indiceInicial, indiceFinal);
|
||||||
|
|
||||||
|
if (filaPaginada.length > 0) {
|
||||||
|
return filaPaginada.map((item, index) => (
|
||||||
|
<tr key={index}>
|
||||||
|
<td>
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
|
{item?.paciente_nome}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>{item?.paciente_cpf}</td>
|
||||||
|
<td>
|
||||||
|
<span className="badge insurance-badge">
|
||||||
|
{item?.medico_nome || 'Não informado'}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>{dayjs(item?.created_at).format('DD/MM/YYYY HH:mm')}</td>
|
||||||
|
<td>
|
||||||
|
<div className="d-flex gap-2">
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-delete"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedId(item.id);
|
||||||
|
setShowDeleteModal(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="bi bi-trash me-1"></i> Excluir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<td colSpan="5" className="text-center py-4">
|
||||||
|
<div className="text-muted">
|
||||||
|
<i className="bi bi-search display-4"></i>
|
||||||
|
<p className="mt-2">Nenhuma solicitação encontrada com os filtros aplicados.</p>
|
||||||
|
{(searchTermFila || filtroMedicoFila !== "Todos") && (
|
||||||
|
<button
|
||||||
|
className="btn btn-outline-primary btn-sm mt-2"
|
||||||
|
onClick={() => {
|
||||||
|
setSearchTermFila('');
|
||||||
|
setFiltroMedicoFila('Todos');
|
||||||
|
setPaginaAtualFila(1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Limpar filtros
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{/* Paginação */}
|
||||||
|
{(() => {
|
||||||
|
const filaFiltrada = filaDeEspera.filter((item) => {
|
||||||
|
const buscaNome = item?.paciente_nome?.toLowerCase().includes(searchTermFila.toLowerCase());
|
||||||
|
const buscaCPF = item?.paciente_cpf?.toLowerCase().includes(searchTermFila.toLowerCase());
|
||||||
|
const passaBusca = searchTermFila === "" || buscaNome || buscaCPF;
|
||||||
|
const passaMedico = filtroMedicoFila === "Todos" || item?.medico_nome?.toLowerCase().includes(filtroMedicoFila.toLowerCase());
|
||||||
|
return passaBusca && passaMedico;
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalPaginasFila = Math.ceil(filaFiltrada.length / itensPorPaginaFila);
|
||||||
|
const indiceInicial = (paginaAtualFila - 1) * itensPorPaginaFila;
|
||||||
|
const indiceFinal = indiceInicial + itensPorPaginaFila;
|
||||||
|
|
||||||
|
if (filaFiltrada.length > 0) {
|
||||||
|
return (
|
||||||
|
<div className="d-flex justify-content-between align-items-center mt-3">
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
|
<span className="me-2 text-muted">Itens por página:</span>
|
||||||
|
<select
|
||||||
|
className="form-select form-select-sm"
|
||||||
|
style={{ width: '80px' }}
|
||||||
|
value={itensPorPaginaFila}
|
||||||
|
onChange={(e) => {
|
||||||
|
setItensPorPaginaFila(Number(e.target.value));
|
||||||
|
setPaginaAtualFila(1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value={5}>5</option>
|
||||||
|
<option value={10}>10</option>
|
||||||
|
<option value={20}>20</option>
|
||||||
|
<option value={50}>50</option>
|
||||||
|
</select>
|
||||||
|
<span className="ms-3 text-muted">
|
||||||
|
Mostrando {indiceInicial + 1}-{Math.min(indiceFinal, filaFiltrada.length)} de {filaFiltrada.length}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul className="pagination pagination-sm mb-0">
|
||||||
|
<li className={`page-item ${paginaAtualFila === 1 ? 'disabled' : ''}`}>
|
||||||
|
<button
|
||||||
|
className="page-link"
|
||||||
|
onClick={() => setPaginaAtualFila(1)}
|
||||||
|
disabled={paginaAtualFila === 1}
|
||||||
|
>
|
||||||
|
Primeira
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li className={`page-item ${paginaAtualFila === 1 ? 'disabled' : ''}`}>
|
||||||
|
<button
|
||||||
|
className="page-link"
|
||||||
|
onClick={() => setPaginaAtualFila(paginaAtualFila - 1)}
|
||||||
|
disabled={paginaAtualFila === 1}
|
||||||
|
>
|
||||||
|
Anterior
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{[...Array(totalPaginasFila)].map((_, i) => {
|
||||||
|
const pageNum = i + 1;
|
||||||
|
if (
|
||||||
|
pageNum === 1 ||
|
||||||
|
pageNum === totalPaginasFila ||
|
||||||
|
(pageNum >= paginaAtualFila - 1 && pageNum <= paginaAtualFila + 1)
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<li key={pageNum} className={`page-item ${paginaAtualFila === pageNum ? 'active' : ''}`}>
|
||||||
|
<button
|
||||||
|
className="page-link"
|
||||||
|
onClick={() => setPaginaAtualFila(pageNum)}
|
||||||
|
>
|
||||||
|
{pageNum}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
} else if (pageNum === paginaAtualFila - 2 || pageNum === paginaAtualFila + 2) {
|
||||||
|
return <li key={pageNum} className="page-item disabled"><span className="page-link">...</span></li>;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
|
||||||
|
<li className={`page-item ${paginaAtualFila === totalPaginasFila ? 'disabled' : ''}`}>
|
||||||
|
<button
|
||||||
|
className="page-link"
|
||||||
|
onClick={() => setPaginaAtualFila(paginaAtualFila + 1)}
|
||||||
|
disabled={paginaAtualFila === totalPaginasFila}
|
||||||
|
>
|
||||||
|
Próxima
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li className={`page-item ${paginaAtualFila === totalPaginasFila ? 'disabled' : ''}`}>
|
||||||
|
<button
|
||||||
|
className="page-link"
|
||||||
|
onClick={() => setPaginaAtualFila(totalPaginasFila)}
|
||||||
|
disabled={paginaAtualFila === totalPaginasFila}
|
||||||
|
>
|
||||||
|
Última
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div>
|
||||||
|
<h2 className='fila-titulo'>Suas proximas consultas</h2>
|
||||||
|
|
||||||
|
<TabelaAgendamentoDia agendamentos={consultasOrganizadas} setDictInfo={setDictInfo}
|
||||||
|
selectedID={selectedID} setSelectedId={setSelectedId} setShowDeleteModal={setShowDeleteModal}
|
||||||
|
coresConsultas={coresConsultas} setListaConsultaID={setListaConsultaID}
|
||||||
|
listaConsultasID={listaConsultasID} setShowConfirmModal={setShowConfirmModal}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{showDeleteModal && (
|
||||||
|
<div
|
||||||
|
className="modal fade show"
|
||||||
|
style={{
|
||||||
|
display: "block",
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||||
|
}}
|
||||||
|
tabIndex="-1"
|
||||||
|
onClick={(e) =>
|
||||||
|
e.target.classList.contains("modal") && setShowDeleteModal(false)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="modal-dialog modal-dialog-centered">
|
||||||
|
<div className="modal-content">
|
||||||
|
|
||||||
|
<div className="modal-header bg-danger bg-opacity-25">
|
||||||
|
<h5 className="modal-title text-danger">
|
||||||
|
Confirmação de Cancelamento
|
||||||
|
</h5>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn-close"
|
||||||
|
onClick={() => setShowDeleteModal(false)}
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="modal-body">
|
||||||
|
<p className="mb-0 fs-5">
|
||||||
|
Qual o motivo do cancelamento?
|
||||||
|
</p>
|
||||||
|
<div className='campo-de-input'>
|
||||||
|
|
||||||
|
<textarea className='input-modal' value={motivoCancelamento} onChange={(e) => setMotivoCancelamento(e.target.value)} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="modal-footer">
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary"
|
||||||
|
onClick={() => {setShowDeleteModal(false);
|
||||||
|
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancelar
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-danger"
|
||||||
|
onClick={() => {
|
||||||
|
|
||||||
|
deleteConsulta(selectedID)
|
||||||
|
setShowDeleteModal(false)
|
||||||
|
let lista_cores = coresConsultas
|
||||||
|
|
||||||
|
let lista = listaConsultasID
|
||||||
|
|
||||||
|
lista.push(selectedID)
|
||||||
|
lista_cores.push("cancelled")
|
||||||
|
|
||||||
|
setCoresConsultas(lista_cores)
|
||||||
|
|
||||||
|
setListaConsultaID(lista)
|
||||||
|
|
||||||
|
console.log("lista", lista)
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
>
|
||||||
|
<i className="bi bi-trash me-1"></i> Excluir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>)}
|
||||||
|
|
||||||
|
{showConfirmModal &&(
|
||||||
|
<div
|
||||||
|
className="modal fade show"
|
||||||
|
style={{
|
||||||
|
display: "block",
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||||
|
}}
|
||||||
|
tabIndex="-1"
|
||||||
|
onClick={(e) =>
|
||||||
|
e.target.classList.contains("modal") && setShowDeleteModal(false)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="modal-dialog modal-dialog-centered">
|
||||||
|
<div className="modal-content">
|
||||||
|
|
||||||
|
<div className="modal-header bg-success">
|
||||||
|
<h5 className="modal-title">
|
||||||
|
Confirmação de edição
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="modal-body">
|
||||||
|
<p className="mb-0 fs-5">
|
||||||
|
Tem certeza que deseja retirar o cancelamento ?
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="modal-footer">
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary"
|
||||||
|
onClick={() => {setShowConfirmModal(false); setSelectedId("")}}
|
||||||
|
>
|
||||||
|
Cancelar
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-success"
|
||||||
|
onClick={() => {confirmConsulta(selectedID);setShowConfirmModal(false)
|
||||||
|
let lista_cores = coresConsultas
|
||||||
|
|
||||||
|
let lista = listaConsultasID
|
||||||
|
|
||||||
|
lista.push(selectedID)
|
||||||
|
lista_cores.push("confirmed")
|
||||||
|
|
||||||
|
setCoresConsultas(lista_cores)
|
||||||
|
|
||||||
|
setListaConsultaID(lista)
|
||||||
|
}}
|
||||||
|
|
||||||
|
>
|
||||||
|
<i className="bi bi-trash me-1"></i> Confirmar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -169,7 +169,7 @@ const formatarHora = (datetimeString) => {
|
|||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
alert("Agendamento salvo!");
|
alert("Agendamento salvo!");
|
||||||
navigate("/paciente/agendamento")
|
|
||||||
onSave({...agendamento, horarioInicio:horarioInicio})
|
onSave({...agendamento, horarioInicio:horarioInicio})
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -178,11 +178,7 @@ const handleSubmit = (e) => {
|
|||||||
|
|
||||||
|
|
||||||
<form className="form-agendamento" onSubmit={handleSubmit}>
|
<form className="form-agendamento" onSubmit={handleSubmit}>
|
||||||
1
|
|
||||||
|
|
||||||
<h2 className="section-title">Informações do atendimento</h2>
|
<h2 className="section-title">Informações do atendimento</h2>
|
||||||
|
|
||||||
|
|
||||||
<div className="campo-informacoes-atendimento">
|
<div className="campo-informacoes-atendimento">
|
||||||
|
|
||||||
<div className="campo-de-input-container"> {/* NOVO CONTAINER PAI */}
|
<div className="campo-de-input-container"> {/* NOVO CONTAINER PAI */}
|
||||||
|
|||||||
@ -104,3 +104,12 @@
|
|||||||
background-color: #c82333; /* Um vermelho um pouco mais escuro para o hover */
|
background-color: #c82333; /* Um vermelho um pouco mais escuro para o hover */
|
||||||
filter: brightness(90%); /* Alternativa: escurecer um pouco mais */
|
filter: brightness(90%); /* Alternativa: escurecer um pouco mais */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btns-container{
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h2-proximos-agendamentos{
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
@ -8,7 +8,6 @@ const CardConsulta = ( {DadosConsulta, TabelaAgendamento, setShowDeleteModal, se
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const {getAuthorizationHeader} = useAuth()
|
const {getAuthorizationHeader} = useAuth()
|
||||||
const authHeader = getAuthorizationHeader()
|
const authHeader = getAuthorizationHeader()
|
||||||
const [Paciente, setPaciente] = useState()
|
const [Paciente, setPaciente] = useState()
|
||||||
@ -16,54 +15,15 @@ const CardConsulta = ( {DadosConsulta, TabelaAgendamento, setShowDeleteModal, se
|
|||||||
|
|
||||||
const [decidirBotton, setDecidirBotton] = useState("")
|
const [decidirBotton, setDecidirBotton] = useState("")
|
||||||
|
|
||||||
|
let nameArrayPaciente = DadosConsulta?.paciente_nome?.split(' ')
|
||||||
|
let nameArrayMedico = DadosConsulta?.medico_nome?.split(' ')
|
||||||
const ids = useMemo(() => {
|
|
||||||
return {
|
|
||||||
doctor_id: DadosConsulta?.doctor_id,
|
|
||||||
patient_id: DadosConsulta?.patient_id,
|
|
||||||
status: DadosConsulta?.status
|
|
||||||
};
|
|
||||||
}, [DadosConsulta]);
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const BuscarMedicoEPaciente = async () => {
|
|
||||||
if (!ids.doctor_id || !ids.patient_id || ids.status === 'nada') return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const [Doctor, Patient] = await Promise.all([
|
|
||||||
GetDoctorByID(ids.doctor_id, authHeader),
|
|
||||||
GetPatientByID(ids.patient_id, authHeader)
|
|
||||||
]);
|
|
||||||
|
|
||||||
setMedico(Doctor?.[0] || null);
|
|
||||||
setPaciente(Patient?.[0] || null);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Erro ao buscar médico/paciente:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
BuscarMedicoEPaciente();
|
|
||||||
}, [ids, authHeader]);
|
|
||||||
|
|
||||||
let nameArrayPaciente = Paciente?.full_name.split(' ')
|
|
||||||
|
|
||||||
let nameArrayMedico = Medico?.full_name.split(' ')
|
|
||||||
|
|
||||||
|
|
||||||
let indice_cor = listaConsultasID.indexOf(DadosConsulta.id)
|
let indice_cor = listaConsultasID.indexOf(DadosConsulta.id)
|
||||||
|
|
||||||
// console.log(coresConsultas)
|
|
||||||
//console.log(indice_cor,"indice no cores")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`container-cardconsulta container-cardconsulta-${TabelaAgendamento} ` }>
|
<div className={`container-cardconsulta container-cardconsulta-${TabelaAgendamento} ` }>
|
||||||
|
|
||||||
|
|
||||||
{DadosConsulta.id?
|
{DadosConsulta.id?
|
||||||
|
|
||||||
<div className={`cardconsulta`} id={indice_cor !== -1 ? `status-card-consulta-${coresConsultas[indice_cor]}` : `status-card-consulta-${DadosConsulta.status}`}>
|
<div className={`cardconsulta`} id={indice_cor !== -1 ? `status-card-consulta-${coresConsultas[indice_cor]}` : `status-card-consulta-${DadosConsulta.status}`}>
|
||||||
@ -84,45 +44,81 @@ const CardConsulta = ( {DadosConsulta, TabelaAgendamento, setShowDeleteModal, se
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='actions-container'>
|
<div className='actions-container'>
|
||||||
<button className="btn btn-sm btn-edit-custom"
|
<button className="btn btn-sm btn-edit-custom"
|
||||||
|
onClick={() => {
|
||||||
|
navigate(`edit`);
|
||||||
|
console.log(DadosConsulta);
|
||||||
|
setDictInfo({
|
||||||
|
...DadosConsulta,
|
||||||
|
paciente_cpf: DadosConsulta?.paciente_cpf,
|
||||||
|
paciente_nome: DadosConsulta?.paciente_nome,
|
||||||
|
nome_medico: DadosConsulta?.medico_nome
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="bi bi-pencil me-1"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
onClick={() => {navigate(`2/edit`)
|
{indice_cor !== -1 ? (
|
||||||
setDictInfo({...DadosConsulta,paciente_cpf:Paciente.cpf, paciente_nome:Paciente.full_name, nome_medico:Medico.full_name})
|
// ✅ Caso o ID esteja na lista
|
||||||
}}
|
<>
|
||||||
|
{coresConsultas[indice_cor] === "cancelled" ?
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-confirm-style"
|
||||||
|
onClick={() => {
|
||||||
|
console.log(DadosConsulta.id);
|
||||||
|
setShowConfirmModal(true);
|
||||||
|
setSelectedId(DadosConsulta.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="bi bi-check-lg"></i>
|
||||||
|
</button>
|
||||||
|
:
|
||||||
|
|
||||||
>
|
<button
|
||||||
<i className="bi bi-pencil me-1"></i>
|
className="btn btn-sm btn-delete-custom-style"
|
||||||
</button>
|
onClick={() => {
|
||||||
{DadosConsulta.status === "cancelled" ?
|
console.log(DadosConsulta.id);
|
||||||
<button
|
setSelectedId(DadosConsulta.id);
|
||||||
className="btn btn-sm btn-confirm-style"
|
setShowDeleteModal(true);
|
||||||
onClick={() => {
|
}}
|
||||||
console.log(DadosConsulta.id)
|
>
|
||||||
setShowConfirmModal(true)
|
<i className="bi bi-trash me-1"></i>
|
||||||
setSelectedId(DadosConsulta.id);
|
</button>
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i class="bi bi-check-lg"></i>
|
|
||||||
</button>
|
|
||||||
:
|
|
||||||
<button
|
|
||||||
className="btn btn-sm btn-delete-custom-style "
|
|
||||||
onClick={() => {
|
|
||||||
console.log(DadosConsulta.id)
|
|
||||||
setSelectedId(DadosConsulta.id);
|
|
||||||
|
|
||||||
|
|
||||||
setShowDeleteModal(true);
|
|
||||||
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i className="bi bi-trash me-1"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
// 🧩 Caso normal — segue a lógica do status
|
||||||
|
<>
|
||||||
|
{DadosConsulta.status === "cancelled" ? (
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-confirm-style"
|
||||||
|
onClick={() => {
|
||||||
|
console.log(DadosConsulta.id);
|
||||||
|
setShowConfirmModal(true);
|
||||||
|
setSelectedId(DadosConsulta.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="bi bi-check-lg"></i>
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-delete-custom-style"
|
||||||
|
onClick={() => {
|
||||||
|
console.log(DadosConsulta.id);
|
||||||
|
setSelectedId(DadosConsulta.id);
|
||||||
|
setShowDeleteModal(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="bi bi-trash me-1"></i>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
|
|||||||
@ -19,7 +19,9 @@ const FormNovaConsulta = ({ onCancel, onSave, setAgendamento, agendamento }) =>
|
|||||||
const [horarioTermino, setHorarioTermino] = useState('');
|
const [horarioTermino, setHorarioTermino] = useState('');
|
||||||
const [horariosDisponiveis, sethorariosDisponiveis] = useState([]);
|
const [horariosDisponiveis, sethorariosDisponiveis] = useState([]);
|
||||||
|
|
||||||
const authHeader = getAuthorizationHeader();
|
const [status, setStatus] = useState("confirmed")
|
||||||
|
|
||||||
|
let authHeader = getAuthorizationHeader()
|
||||||
|
|
||||||
const FormatCPF = (valor) => {
|
const FormatCPF = (valor) => {
|
||||||
const digits = String(valor).replace(/\D/g, '').slice(0, 11);
|
const digits = String(valor).replace(/\D/g, '').slice(0, 11);
|
||||||
@ -69,6 +71,14 @@ const FormNovaConsulta = ({ onCancel, onSave, setAgendamento, agendamento }) =>
|
|||||||
}, [authHeader]);
|
}, [authHeader]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
|
console.log("Horario","tessssste" )
|
||||||
|
if (agendamento?.scheduled_at) {
|
||||||
|
setHorarioInicio(formatarHora(agendamento.scheduled_at));
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
ChamarMedicos();
|
ChamarMedicos();
|
||||||
}, [ChamarMedicos]);
|
}, [ChamarMedicos]);
|
||||||
|
|
||||||
@ -167,7 +177,7 @@ const FormNovaConsulta = ({ onCancel, onSave, setAgendamento, agendamento }) =>
|
|||||||
|
|
||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
setShowSuccessModal(false);
|
setShowSuccessModal(false);
|
||||||
onSave({ ...agendamento, horarioInicio: horarioInicio });
|
onSave({ ...agendamento, horarioInicio: horarioInicio, status:status });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -188,170 +198,188 @@ const FormNovaConsulta = ({ onCancel, onSave, setAgendamento, agendamento }) =>
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<form className="form-agendamento" onSubmit={handleSubmit}>
|
<form className="form-agendamento" onSubmit={handleSubmit}>
|
||||||
<h2 className="section-title">Informações do paciente</h2>
|
<h2 className="section-title">Informações do paciente</h2>
|
||||||
|
|
||||||
<div className="campos-informacoes-paciente" id="informacoes-paciente-linha-um">
|
<div className="campos-informacoes-paciente" id="informacoes-paciente-linha-um">
|
||||||
<div className="campo-de-input">
|
<div className="campo-de-input">
|
||||||
<label>CPF do paciente</label>
|
<label>CPF do paciente</label>
|
||||||
<input type="text" name="paciente_cpf" placeholder="000.000.000-00" onChange={handleChange} value={agendamento.paciente_cpf}/>
|
<input
|
||||||
</div>
|
type="text"
|
||||||
|
name="paciente_cpf"
|
||||||
|
placeholder="000.000.000-00"
|
||||||
|
onChange={handleChange}
|
||||||
|
value={agendamento.paciente_cpf}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="campo-de-input">
|
<div className="campo-de-input">
|
||||||
<label>Nome *</label>
|
<label>Nome *</label>
|
||||||
<input type="text" name="paciente_nome" value={agendamento.paciente_nome} placeholder="Insira o nome do paciente" required onChange={handleChange} />
|
<input
|
||||||
</div>
|
type="text"
|
||||||
</div>
|
name="paciente_nome"
|
||||||
|
placeholder="Insira o nome do paciente"
|
||||||
<div className="campos-informacoes-paciente" id="informacoes-paciente-linha-tres">
|
required
|
||||||
<div>
|
onChange={handleChange}
|
||||||
<label>Convênio</label>
|
value={agendamento.paciente_nome}
|
||||||
<select name="convenio" onChange={handleChange} value={agendamento.insurance_provider}>
|
/>
|
||||||
<option value="publico">Público</option>
|
</div>
|
||||||
<option value="unimed">Unimed</option>
|
|
||||||
<option value="bradesco_saude">Bradesco Saúde</option>
|
|
||||||
<option value="hapvida">Hapvida</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 className="section-title">Informações do atendimento</h2>
|
|
||||||
|
|
||||||
<div className="campo-informacoes-atendimento">
|
|
||||||
<div className="campo-de-input-container">
|
|
||||||
<div className="campo-de-input">
|
|
||||||
<label>Nome do profissional *</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="nome_medico"
|
|
||||||
onChange={handleSearchProfissional}
|
|
||||||
value={agendamento?.nome_medico || ''}
|
|
||||||
autoComplete="off"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isDropdownOpen && profissionaisFiltrados.length > 0 && (
|
|
||||||
<div className='dropdown-profissionais'>
|
|
||||||
{profissionaisFiltrados.map((profissional) => (
|
|
||||||
<div
|
|
||||||
key={profissional.id}
|
|
||||||
className='dropdown-item'
|
|
||||||
onClick={() => handleSelectProfissional(profissional)}
|
|
||||||
>
|
|
||||||
{profissional.full_name}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="tipo_atendimento">
|
|
||||||
<label>Tipo de atendimento *</label>
|
|
||||||
<select onChange={handleChange} name="tipo_atendimento" value={agendamento.tipo_atendimento}>
|
|
||||||
<option value="presencial">Presencial</option>
|
|
||||||
<option value="teleconsulta">Teleconsulta</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section id="informacoes-atendimento-segunda-linha">
|
|
||||||
<section id="informacoes-atendimento-segunda-linha-esquerda">
|
|
||||||
<div className="campo-informacoes-atendimento">
|
|
||||||
<div className="campo-de-input">
|
|
||||||
<label>Data *</label>
|
|
||||||
<input type="date" name="dataAtendimento" onChange={handleChange} value={agendamento.dataAtendimento} required />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="linha">
|
|
||||||
<div className="campo-de-input">
|
|
||||||
<label htmlFor="inicio">Início *</label>
|
|
||||||
<select
|
|
||||||
id="inicio"
|
|
||||||
name="inicio"
|
|
||||||
required
|
|
||||||
value={horarioInicio}
|
|
||||||
onChange={(e) => setHorarioInicio(e.target.value)}
|
|
||||||
>
|
|
||||||
<option value="" disabled>Selecione a hora de início</option>
|
|
||||||
{opcoesDeHorario.map((opcao, index) => (
|
|
||||||
<option
|
|
||||||
key={index}
|
|
||||||
value={opcao.value}
|
|
||||||
disabled={opcao.disabled}
|
|
||||||
>
|
|
||||||
{opcao.label}
|
|
||||||
{opcao.disabled && " (Indisponível)"}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='seletor-wrapper'>
|
|
||||||
<label>Número de Sessões *</label>
|
|
||||||
<div className='sessao-contador'>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setSessoes(prev => Math.max(0, prev - 1))}
|
|
||||||
disabled={sessoes === 0}
|
|
||||||
>
|
|
||||||
<i className="bi bi-chevron-compact-left"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<p className='sessao-valor'>{sessoes}</p>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setSessoes(prev => Math.min(3, prev + 1))}
|
|
||||||
disabled={sessoes === 3}
|
|
||||||
>
|
|
||||||
<i className="bi bi-chevron-compact-right"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="campo-de-input">
|
|
||||||
<label htmlFor="termino">Término *</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="termino"
|
|
||||||
name="termino"
|
|
||||||
value={horarioTermino || '— —'}
|
|
||||||
readOnly
|
|
||||||
className="horario-termino-readonly"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="informacoes-atendimento-segunda-linha-direita">
|
|
||||||
<div className="campo-de-input">
|
|
||||||
<label>Observações</label>
|
|
||||||
<textarea name="observacoes" rows="4" cols="1" onChange={handleChange} value={agendamento.observacoes || ''}></textarea>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div className="campo-de-input-check">
|
|
||||||
<input
|
|
||||||
className="form-check-input form-custom-check"
|
|
||||||
type="checkbox"
|
|
||||||
name="status"
|
|
||||||
onChange={handleChange}
|
|
||||||
checked={agendamento.status === 'requested'}
|
|
||||||
/>
|
|
||||||
<label className="form-check-label checkbox-label" htmlFor="status">
|
|
||||||
Adicionar a fila de espera
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-actions">
|
|
||||||
<button type="submit" className="btn-primary">Salvar agendamento</button>
|
|
||||||
<button type="button" className="btn-cancel" onClick={onCancel}>Cancelar</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="campos-informacoes-paciente" id="informacoes-paciente-linha-tres">
|
||||||
|
<div>
|
||||||
|
<label>Convênio</label>
|
||||||
|
<select name="convenio" onChange={handleChange} value={agendamento.insurance_provider}>
|
||||||
|
<option value="publico">Público</option>
|
||||||
|
<option value="unimed">Unimed</option>
|
||||||
|
<option value="bradesco_saude">Bradesco Saúde</option>
|
||||||
|
<option value="hapvida">Hapvida</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 className="section-title">Informações do atendimento</h2>
|
||||||
|
|
||||||
|
<div className="campo-informacoes-atendimento">
|
||||||
|
<div className="campo-de-input-container">
|
||||||
|
<div className="campo-de-input">
|
||||||
|
<label>Nome do profissional *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="nome_medico"
|
||||||
|
onChange={handleSearchProfissional}
|
||||||
|
value={agendamento?.nome_medico || ''}
|
||||||
|
autoComplete="off"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isDropdownOpen && profissionaisFiltrados.length > 0 && (
|
||||||
|
<div className="dropdown-profissionais">
|
||||||
|
{profissionaisFiltrados.map((profissional) => (
|
||||||
|
<div
|
||||||
|
key={profissional.id}
|
||||||
|
className="dropdown-item"
|
||||||
|
onClick={() => handleSelectProfissional(profissional)}
|
||||||
|
>
|
||||||
|
{profissional.full_name}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="tipo_atendimento">
|
||||||
|
<label>Tipo de atendimento *</label>
|
||||||
|
<select name="tipo_atendimento" onChange={handleChange} value={agendamento.tipo_atendimento}>
|
||||||
|
<option value="presencial">Presencial</option>
|
||||||
|
<option value="teleconsulta">Teleconsulta</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section id="informacoes-atendimento-segunda-linha">
|
||||||
|
<div className="campo-informacoes-atendimento">
|
||||||
|
<div className="campo-de-input">
|
||||||
|
<label>Data *</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
name="dataAtendimento"
|
||||||
|
onChange={handleChange}
|
||||||
|
value={agendamento.dataAtendimento}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="linha">
|
||||||
|
<div className="campo-de-input">
|
||||||
|
<label htmlFor="inicio">Início *</label>
|
||||||
|
<select
|
||||||
|
id="inicio"
|
||||||
|
name="inicio"
|
||||||
|
required
|
||||||
|
value={horarioInicio}
|
||||||
|
onChange={(e) => setHorarioInicio(e.target.value)}
|
||||||
|
>
|
||||||
|
<option value="" disabled>Selecione a hora de início</option>
|
||||||
|
{opcoesDeHorario.map((opcao, index) => (
|
||||||
|
<option key={index} value={opcao.value} disabled={opcao.disabled}>
|
||||||
|
{opcao.label}
|
||||||
|
{opcao.disabled && " (Indisponível)"}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="seletor-wrapper">
|
||||||
|
<label>Número de Sessões *</label>
|
||||||
|
<div className="sessao-contador">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setSessoes(prev => Math.max(0, prev - 1))}
|
||||||
|
disabled={sessoes === 0}
|
||||||
|
>
|
||||||
|
<i className="bi bi-chevron-compact-left"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<p className="sessao-valor">{sessoes}</p>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setSessoes(prev => Math.min(3, prev + 1))}
|
||||||
|
disabled={sessoes === 3}
|
||||||
|
>
|
||||||
|
<i className="bi bi-chevron-compact-right"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="campo-de-input">
|
||||||
|
<label htmlFor="termino">Término *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="termino"
|
||||||
|
name="termino"
|
||||||
|
value={horarioTermino || '— —'}
|
||||||
|
readOnly
|
||||||
|
className="horario-termino-readonly"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="campo-de-input observacoes">
|
||||||
|
<label>Observações</label>
|
||||||
|
<textarea
|
||||||
|
name="observacoes"
|
||||||
|
rows="4"
|
||||||
|
cols="1"
|
||||||
|
onChange={handleChange}
|
||||||
|
value={agendamento.observacoes || ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div className="campo-de-input-check">
|
||||||
|
<input
|
||||||
|
className="form-check-input form-custom-check"
|
||||||
|
type="checkbox"
|
||||||
|
name="status"
|
||||||
|
onChange={handleChange}
|
||||||
|
checked={agendamento.status === 'requested'}
|
||||||
|
/>
|
||||||
|
<label className="form-check-label checkbox-label" htmlFor="status">
|
||||||
|
Adicionar à fila de espera
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-actions">
|
||||||
|
<button type="submit" className="btn-primary">Salvar agendamento</button>
|
||||||
|
<button type="button" className="btn-cancel" onClick={onCancel}>Cancelar</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -2,14 +2,30 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import CardConsulta from './CardConsulta';
|
import CardConsulta from './CardConsulta';
|
||||||
import "./style/styleTabelas/tabeladia.css";
|
import "./style/styleTabelas/tabeladia.css";
|
||||||
|
|
||||||
const TabelaAgendamentoDia = ({ handleClickAgendamento, agendamentos, setShowDeleteModal, setDictInfo,selectedID, setSelectedId, setShowConfirmModal, coresConsultas, setListaConsultaID, listaConsultasID }) => {
|
import Spinner from '../Spinner';
|
||||||
const [indiceAcesso, setIndiceAcesso] = useState(0)
|
|
||||||
|
const TabelaAgendamentoDia = ({ agendamentos, setShowDeleteModal, setDictInfo,selectedID, setSelectedId, setShowConfirmModal, coresConsultas, setListaConsultaID, listaConsultasID }) => {
|
||||||
|
const [indiceAcesso, setIndiceAcesso] = useState(null)
|
||||||
const [Dia, setDia] = useState()
|
const [Dia, setDia] = useState()
|
||||||
const agendamentosDoDia = agendamentos?.semana1?.segunda || [];
|
const agendamentosDoDia = agendamentos?.semana1?.segunda || [];
|
||||||
const nomeMedico = agendamentosDoDia.find(item => item.medico)?.medico || 'Profissional';
|
const nomeMedico = agendamentosDoDia.find(item => item.medico)?.medico || 'Profissional';
|
||||||
|
|
||||||
let ListaDiasComAgendamentos = Object.keys(agendamentos)
|
let ListaDiasComAgendamentos = Object.keys(agendamentos)
|
||||||
|
|
||||||
|
const [showSpinner, setShowSpinner] = useState(true);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!agendamentos) return;
|
||||||
|
|
||||||
|
const dias = Object.keys(agendamentos);
|
||||||
|
if (dias.length > 0) {
|
||||||
|
setIndiceAcesso(0); // começa no primeiro dia disponível
|
||||||
|
setDia(dias[0]); // seta o Dia inicial
|
||||||
|
|
||||||
|
setShowSpinner(false)
|
||||||
|
}
|
||||||
|
}, [agendamentos]);
|
||||||
|
|
||||||
|
|
||||||
//console.log(Dia, "hshdhshhsdhs")
|
//console.log(Dia, "hshdhshhsdhs")
|
||||||
@ -71,12 +87,20 @@ const TabelaAgendamentoDia = ({ handleClickAgendamento, agendamentos, setShowDel
|
|||||||
|
|
||||||
<td className='coluna-horario'><p className='horario-texto'>{`${horario[0]}:${horario[1]}`}</p></td>
|
<td className='coluna-horario'><p className='horario-texto'>{`${horario[0]}:${horario[1]}`}</p></td>
|
||||||
<td className='mostrar-horario'>
|
<td className='mostrar-horario'>
|
||||||
<div onClick={() => handleClickAgendamento(agendamento)}>
|
<div>
|
||||||
<CardConsulta DadosConsulta={agendamento} TabelaAgendamento={'dia'} setShowDeleteModal={setShowDeleteModal} setDictInfo={setDictInfo} setSelectedId={setSelectedId} selectedID={selectedID} setShowConfirmModal={setShowConfirmModal} coresConsultas={coresConsultas} setListaConsultaID={setListaConsultaID} listaConsultasID={listaConsultasID}/>
|
<CardConsulta DadosConsulta={agendamento} TabelaAgendamento={'dia'} setShowDeleteModal={setShowDeleteModal} setDictInfo={setDictInfo} setSelectedId={setSelectedId} selectedID={selectedID} setShowConfirmModal={setShowConfirmModal} coresConsultas={coresConsultas} setListaConsultaID={setListaConsultaID} listaConsultasID={listaConsultasID}/>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)})}
|
)})}
|
||||||
|
{showSpinner &&
|
||||||
|
<tr>
|
||||||
|
<td colspan='2'>
|
||||||
|
<Spinner/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -6,15 +6,23 @@ import "./style/styleTabelas/tabelames.css";
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import Spinner from '../Spinner';
|
||||||
|
|
||||||
const TabelaAgendamentoMes = ({ ListarDiasdoMes, agendamentos, setShowDeleteModal, setSelectedId ,setDictInfo, setShowConfirmModal, coresConsultas ,setListaConsultaID, listaConsultasID }) => {
|
const TabelaAgendamentoMes = ({ ListarDiasdoMes, agendamentos, setShowDeleteModal, setSelectedId ,setDictInfo, setShowConfirmModal, coresConsultas ,setListaConsultaID, listaConsultasID }) => {
|
||||||
|
|
||||||
const dataHoje = dayjs();
|
const dataHoje = dayjs();
|
||||||
const AnoAtual = dataHoje.year();
|
const AnoAtual = dataHoje.year();
|
||||||
const mes = dataHoje.month() + 1;
|
const mes = dataHoje.month() + 1;
|
||||||
|
|
||||||
|
const [showSpinner, setShowSpinner] = useState(true)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(agendamentos)
|
||||||
|
|
||||||
let ListaDiasDatas = ListarDiasdoMes(AnoAtual, mes);
|
let ListaDiasDatas = ListarDiasdoMes(AnoAtual, mes);
|
||||||
const [AgendamentosSemanaisOrganizados, setAgendamentosSemanaisOrganizados] = useState({})
|
const [AgendamentosSemanaisOrganizados, setAgendamentosSemanaisOrganizados] = useState({})
|
||||||
const [indice, setIndice] = useState("10")
|
const [indice, setIndice] = useState(mes.toString())
|
||||||
|
|
||||||
const [AgendamentosMensaisOrganizados, setAgendamentosMensaisOrganizados] = useState({
|
const [AgendamentosMensaisOrganizados, setAgendamentosMensaisOrganizados] = useState({
|
||||||
"01": { "nomeDoMes": "janeiro" },
|
"01": { "nomeDoMes": "janeiro" },
|
||||||
@ -33,6 +41,17 @@ const TabelaAgendamentoMes = ({ ListarDiasdoMes, agendamentos, setShowDeleteModa
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!agendamentos) return;
|
||||||
|
|
||||||
|
const meses = Object.keys(agendamentos);
|
||||||
|
if (meses.length > 0) {
|
||||||
|
// começa no primeiro dia disponível
|
||||||
|
setIndice(mes.toString()); // seta o Dia inicial
|
||||||
|
setShowSpinner(false)
|
||||||
|
}
|
||||||
|
}, [agendamentos]);
|
||||||
|
|
||||||
|
|
||||||
const OrganizarAgendamentosSemanais = useMemo(() => {
|
const OrganizarAgendamentosSemanais = useMemo(() => {
|
||||||
if (!agendamentos || Object.keys(agendamentos).length === 0) return {};
|
if (!agendamentos || Object.keys(agendamentos).length === 0) return {};
|
||||||
@ -40,7 +59,6 @@ const TabelaAgendamentoMes = ({ ListarDiasdoMes, agendamentos, setShowDeleteModa
|
|||||||
const DiasComAtendimentos = Object.keys(agendamentos)
|
const DiasComAtendimentos = Object.keys(agendamentos)
|
||||||
const semanas = {}
|
const semanas = {}
|
||||||
|
|
||||||
|
|
||||||
for (let i = 0; i < DiasComAtendimentos.length; i++) {
|
for (let i = 0; i < DiasComAtendimentos.length; i++) {
|
||||||
const DiaComAtendimento = DiasComAtendimentos[i]
|
const DiaComAtendimento = DiasComAtendimentos[i]
|
||||||
const [_, MesDoAgendamento, DiaDoAgendamento] = DiaComAtendimento.split("-")
|
const [_, MesDoAgendamento, DiaDoAgendamento] = DiaComAtendimento.split("-")
|
||||||
@ -202,6 +220,9 @@ const TabelaAgendamentoMes = ({ ListarDiasdoMes, agendamentos, setShowDeleteModa
|
|||||||
{
|
{
|
||||||
semana && typeof semana === "object" && Object.keys(semana).map((dia) => (
|
semana && typeof semana === "object" && Object.keys(semana).map((dia) => (
|
||||||
<td key={dia} >
|
<td key={dia} >
|
||||||
|
<div className='dia-tabelamensal'>
|
||||||
|
<p> {(semana[dia]|| [])[0]?.scheduled_at.split("-")[2].split("T")[0]}</p>
|
||||||
|
</div>
|
||||||
<CardConsulta TabelaAgendamento={'mes'} DadosConsulta={((semana[dia]|| [])[0]) || {status:'vazio'}} setShowDeleteModal={setShowDeleteModal} setSelectedId={setSelectedId} setDictInfo={setDictInfo} setShowConfirmModal={setShowConfirmModal} coresConsultas={coresConsultas} setListaConsultaID={setListaConsultaID} listaConsultasID={listaConsultasID}/>
|
<CardConsulta TabelaAgendamento={'mes'} DadosConsulta={((semana[dia]|| [])[0]) || {status:'vazio'}} setShowDeleteModal={setShowDeleteModal} setSelectedId={setSelectedId} setDictInfo={setDictInfo} setShowConfirmModal={setShowConfirmModal} coresConsultas={coresConsultas} setListaConsultaID={setListaConsultaID} listaConsultasID={listaConsultasID}/>
|
||||||
<CardConsulta TabelaAgendamento={'mes'} DadosConsulta={((semana[dia]|| [])[1]) || {status:'vazio'}} setShowDeleteModal={setShowDeleteModal} setSelectedId={setSelectedId} setDictInfo={setDictInfo} setShowConfirmModal={setShowConfirmModal} coresConsultas={coresConsultas} setListaConsultaID={setListaConsultaID} listaConsultasID={listaConsultasID}/>
|
<CardConsulta TabelaAgendamento={'mes'} DadosConsulta={((semana[dia]|| [])[1]) || {status:'vazio'}} setShowDeleteModal={setShowDeleteModal} setSelectedId={setSelectedId} setDictInfo={setDictInfo} setShowConfirmModal={setShowConfirmModal} coresConsultas={coresConsultas} setListaConsultaID={setListaConsultaID} listaConsultasID={listaConsultasID}/>
|
||||||
<CardConsulta TabelaAgendamento={'mes'} DadosConsulta={((semana[dia]|| [])[2]) || {status:'vazio'}} setShowDeleteModal={setShowDeleteModal} setSelectedId={setSelectedId} setDictInfo={setDictInfo} setShowConfirmModal={setShowConfirmModal} coresConsultas={coresConsultas} setListaConsultaID={setListaConsultaID} listaConsultasID={listaConsultasID}/>
|
<CardConsulta TabelaAgendamento={'mes'} DadosConsulta={((semana[dia]|| [])[2]) || {status:'vazio'}} setShowDeleteModal={setShowDeleteModal} setSelectedId={setSelectedId} setDictInfo={setDictInfo} setShowConfirmModal={setShowConfirmModal} coresConsultas={coresConsultas} setListaConsultaID={setListaConsultaID} listaConsultasID={listaConsultasID}/>
|
||||||
@ -215,8 +236,16 @@ const TabelaAgendamentoMes = ({ ListarDiasdoMes, agendamentos, setShowDeleteModa
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
)})}
|
)})}
|
||||||
|
{showSpinner &&
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan='5'>
|
||||||
|
<Spinner/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,6 +4,11 @@ import "./style/styleTabelas/tabelasemana.css";
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useEffect, useState, useMemo } from 'react';
|
import { useEffect, useState, useMemo } from 'react';
|
||||||
import weekOfYear from 'dayjs/plugin/weekOfYear'
|
import weekOfYear from 'dayjs/plugin/weekOfYear'
|
||||||
|
|
||||||
|
|
||||||
|
import Spinner from '../Spinner';
|
||||||
|
|
||||||
|
|
||||||
dayjs.extend(weekOfYear)
|
dayjs.extend(weekOfYear)
|
||||||
|
|
||||||
const TabelaAgendamentoSemana = ({ agendamentos, ListarDiasdoMes, setShowDeleteModal ,setSelectedId ,setDictInfo, setShowConfirmModal, coresConsultas ,setListaConsultaID, listaConsultasID}) => {
|
const TabelaAgendamentoSemana = ({ agendamentos, ListarDiasdoMes, setShowDeleteModal ,setSelectedId ,setDictInfo, setShowConfirmModal, coresConsultas ,setListaConsultaID, listaConsultasID}) => {
|
||||||
@ -12,6 +17,21 @@ const TabelaAgendamentoSemana = ({ agendamentos, ListarDiasdoMes, setShowDeleteM
|
|||||||
const [semanasOrganizadas, setSemanasOrganizadas] = useState({});
|
const [semanasOrganizadas, setSemanasOrganizadas] = useState({});
|
||||||
// Controla qual semana está sendo exibida (o índice da chave no objeto)
|
// Controla qual semana está sendo exibida (o índice da chave no objeto)
|
||||||
const [Indice, setIndice] = useState(0);
|
const [Indice, setIndice] = useState(0);
|
||||||
|
const [showSpinner, setShowSpinner] = useState(true)
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!agendamentos) return;
|
||||||
|
|
||||||
|
const semanas = Object.keys(agendamentos);
|
||||||
|
if (semanas.length > 0) {
|
||||||
|
|
||||||
|
setIndice(0)
|
||||||
|
setShowSpinner(false)
|
||||||
|
|
||||||
|
}
|
||||||
|
}, [agendamentos]);
|
||||||
|
|
||||||
|
|
||||||
console.log(agendamentos, "agendamentos diarios")
|
console.log(agendamentos, "agendamentos diarios")
|
||||||
|
|
||||||
@ -129,10 +149,10 @@ const TabelaAgendamentoSemana = ({ agendamentos, ListarDiasdoMes, setShowDeleteM
|
|||||||
? `Semana ${chaveDaSemanaAtual.replace('semana', '')} / ${AnoAtual}`
|
? `Semana ${chaveDaSemanaAtual.replace('semana', '')} / ${AnoAtual}`
|
||||||
: 'Nenhuma semana encontrada';
|
: 'Nenhuma semana encontrada';
|
||||||
|
|
||||||
// --- RENDERIZAÇÃO ---
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{/* Container de Navegação */}
|
|
||||||
<div id='tabela-seletor-container'>
|
<div id='tabela-seletor-container'>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@ -214,6 +234,16 @@ const TabelaAgendamentoSemana = ({ agendamentos, ListarDiasdoMes, setShowDeleteM
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)})}
|
)})}
|
||||||
|
|
||||||
|
{showSpinner &&
|
||||||
|
<tr>
|
||||||
|
<td colspan='6'>
|
||||||
|
<Spinner/>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -55,10 +55,7 @@
|
|||||||
|
|
||||||
/* 7. Estilos para os ícones dentro dos botões (já está no JSX com fs-4) */
|
/* 7. Estilos para os ícones dentro dos botões (já está no JSX com fs-4) */
|
||||||
/* .fs-4 do Bootstrap já cuida do tamanho do ícone. Se precisar de mais controle, adicione aqui. */
|
/* .fs-4 do Bootstrap já cuida do tamanho do ícone. Se precisar de mais controle, adicione aqui. */
|
||||||
.action-button .bi {
|
|
||||||
/* Exemplo: se precisar de um ajuste fino além do fs-4 */
|
|
||||||
/* font-size: 1.5rem; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-confirm-style{
|
.btn-confirm-style{
|
||||||
background-color: #5ce687;
|
background-color: #5ce687;
|
||||||
@ -70,3 +67,21 @@
|
|||||||
border: #91d392;
|
border: #91d392;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Aplique isso às classes que contêm os nomes do Médico e do Paciente */
|
||||||
|
|
||||||
|
.cardconsulta-infosecundaria p,
|
||||||
|
.cardconsulta-infoprimaria p {
|
||||||
|
/* 1. Força o texto a não quebrar para a próxima linha */
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
/* 2. Oculta qualquer texto que ultrapasse a largura do contêiner */
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
/* 3. Adiciona reticências (...) ao final do texto truncado */
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabelamensal .container-cardconsulta{
|
||||||
|
width: 24rem;
|
||||||
|
|
||||||
|
}
|
||||||
@ -231,3 +231,9 @@ html[data-bs-theme="dark"] .cards-que-faltam {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dia-tabelamensal p {
|
||||||
|
font-weight: bold; /* Deixa o número em negrito */
|
||||||
|
color: #0078d7; /* Garante que seja preto */
|
||||||
|
font-size: 16px; /* Ajuste o tamanho para harmonizar com o restante */
|
||||||
|
/* Adicione a mesma família de fonte usada para o restante do app, se necessário */
|
||||||
|
}
|
||||||
242
src/components/BotaoVideoChamada.css
Normal file
242
src/components/BotaoVideoChamada.css
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
@keyframes slide-up {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-chat-container {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- O BOTÃO FLUTUANTE (COM CORREÇÃO) --- */
|
||||||
|
.video-chat-button {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 95px;
|
||||||
|
z-index: 9999;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center; /* <-- Correção do alinhamento */
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-chat-button:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- A JANELA DE CHAT --- */
|
||||||
|
.video-chat-window {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 90px;
|
||||||
|
right: 95px;
|
||||||
|
width: 500px;
|
||||||
|
height: 380px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 -5px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 10000;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden; /* Importante para o border-radius */
|
||||||
|
|
||||||
|
/* Animação de "surgir" */
|
||||||
|
animation: slide-up 0.3s ease-out;
|
||||||
|
|
||||||
|
/* Animação "premium" para tela cheia */
|
||||||
|
transition: all 0.4s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- MODO TELA CHEIA (SIMULADO) --- */
|
||||||
|
.video-chat-window.pseudo-fullscreen {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
border: none;
|
||||||
|
z-index: 99999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-chat-window.pseudo-fullscreen .video-chat-header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- HEADER DA JANELA --- */
|
||||||
|
.video-chat-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 15px;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
flex-shrink: 0; /* Impede o header de encolher */
|
||||||
|
}
|
||||||
|
.video-chat-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.video-chat-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.control-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #888;
|
||||||
|
padding: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
.control-btn:hover {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
}
|
||||||
|
.close-btn {
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.fullscreen-btn {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- CORPO DA JANELA (CONSOLIDADO) --- */
|
||||||
|
.video-chat-body {
|
||||||
|
flex-grow: 1; /* Ocupa todo o espaço vertical */
|
||||||
|
overflow-y: hidden; /* Os filhos (lista, call-screen) cuidam do scroll */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0; /* Os filhos cuidam do padding */
|
||||||
|
transition: padding 0.4s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-chat-window.pseudo-fullscreen .video-chat-body {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 1. LISTA DE PACIENTES --- */
|
||||||
|
.patient-list-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.patient-list-container > p {
|
||||||
|
padding: 15px 15px 10px 15px;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #555;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
flex-shrink: 0; /* Impede de encolher */
|
||||||
|
}
|
||||||
|
.patient-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow-y: auto; /* Adiciona scroll SÓ AQUI */
|
||||||
|
flex-grow: 1; /* Ocupa o espaço restante */
|
||||||
|
}
|
||||||
|
.patient-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 15px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
.patient-item:hover {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
.patient-item span {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.call-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
background-color: #28a745;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
.call-btn:hover {
|
||||||
|
background-color: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 2. TELA DE CHAMADA --- */
|
||||||
|
.call-screen {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #2c2c2c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.call-screen h4 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 12px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: rgba(0,0,0,0.2);
|
||||||
|
font-size: 16px;
|
||||||
|
flex-shrink: 0; /* Impede de encolher */
|
||||||
|
}
|
||||||
|
.video-placeholder {
|
||||||
|
flex-grow: 1; /* Ocupa todo o espaço */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: #888;
|
||||||
|
font-style: italic;
|
||||||
|
overflow: hidden; /* Caso o <iframe>/video tente vazar */
|
||||||
|
}
|
||||||
|
.call-actions {
|
||||||
|
padding: 15px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: rgba(0,0,0,0.2);
|
||||||
|
flex-shrink: 0; /* Impede de encolher */
|
||||||
|
}
|
||||||
|
.hang-up-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
background-color: #dc3545;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.hang-up-btn:hover {
|
||||||
|
background-color: #c82333;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
158
src/components/BotaoVideoChamada.jsx
Normal file
158
src/components/BotaoVideoChamada.jsx
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import './BotaoVideoChamada.css';
|
||||||
|
import { FaVideo, FaExpand, FaCompress, FaPhoneSlash } from 'react-icons/fa';
|
||||||
|
import { JitsiMeeting } from '@jitsi/react-sdk';
|
||||||
|
import { db } from '../firebaseConfig';
|
||||||
|
import { ref, set, remove } from "firebase/database";
|
||||||
|
|
||||||
|
// MOCK PACIENTE
|
||||||
|
const mockPacientes = [
|
||||||
|
{ id: 1, name: 'Paciente' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// DADOS DO MÉDICO
|
||||||
|
const MEU_ID_MEDICO = 'medico-99';
|
||||||
|
const MEU_NOME_MEDICO = 'Dr. Rafael';
|
||||||
|
|
||||||
|
const BotaoVideoChamada = () => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [isFullScreen, setIsFullScreen] = useState(false);
|
||||||
|
const [callActive, setCallActive] = useState(false);
|
||||||
|
const [callingPatient, setCallingPatient] = useState(null);
|
||||||
|
const [roomName, setRoomName] = useState('');
|
||||||
|
|
||||||
|
// UseEffect da tecla "Esc"
|
||||||
|
useEffect(() => {
|
||||||
|
const handleEscKey = (event) => {
|
||||||
|
if (event.key === 'Escape' && isFullScreen) {
|
||||||
|
setIsFullScreen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('keydown', handleEscKey);
|
||||||
|
return () => document.removeEventListener('keydown', handleEscKey);
|
||||||
|
}, [isFullScreen]);
|
||||||
|
|
||||||
|
// Função para INICIAR a chamada
|
||||||
|
const handleStartCall = (paciente) => {
|
||||||
|
// Adiciona o #config para pular o lobby
|
||||||
|
const newRoomName = `mediconnect-call-${MEU_ID_MEDICO}-${paciente.id}-${Date.now()}#config.prejoinPageEnabled=false`;
|
||||||
|
const callRef = ref(db, `calls/paciente-${paciente.id}`);
|
||||||
|
|
||||||
|
set(callRef, {
|
||||||
|
incomingCall: {
|
||||||
|
fromId: MEU_ID_MEDICO,
|
||||||
|
fromName: MEU_NOME_MEDICO,
|
||||||
|
roomName: newRoomName
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setRoomName(newRoomName);
|
||||||
|
setCallingPatient(paciente);
|
||||||
|
setCallActive(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Função para ENCERRAR a chamada
|
||||||
|
const handleHangUp = () => {
|
||||||
|
if (callingPatient) {
|
||||||
|
const callRef = ref(db, `calls/paciente-${callingPatient.id}`);
|
||||||
|
remove(callRef);
|
||||||
|
}
|
||||||
|
setCallActive(false);
|
||||||
|
setCallingPatient(null);
|
||||||
|
setRoomName('');
|
||||||
|
console.log("Chamada encerrada.");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Função para fechar a janela
|
||||||
|
const toggleVideoChat = () => {
|
||||||
|
setIsOpen(!isOpen);
|
||||||
|
if (isOpen) {
|
||||||
|
handleHangUp();
|
||||||
|
setIsFullScreen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Função de Tela Cheia
|
||||||
|
const handleFullScreen = () => {
|
||||||
|
setIsFullScreen(!isFullScreen);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="video-chat-container">
|
||||||
|
{isOpen && (
|
||||||
|
<div className={`video-chat-window ${isFullScreen ? 'pseudo-fullscreen' : ''}`}>
|
||||||
|
|
||||||
|
<div className="video-chat-header">
|
||||||
|
<h3>{callActive ? `Em chamada com...` : 'Iniciar Chamada'}</h3>
|
||||||
|
|
||||||
|
{/* ================================== */}
|
||||||
|
{/* BOTÕES DE VOLTA - CORREÇÃO AQUI */}
|
||||||
|
{/* ================================== */}
|
||||||
|
<div className="video-chat-controls">
|
||||||
|
<button onClick={handleFullScreen} className="control-btn fullscreen-btn">
|
||||||
|
{isFullScreen ? <FaCompress size={14} /> : <FaExpand size={14} />}
|
||||||
|
</button>
|
||||||
|
<button onClick={toggleVideoChat} className="control-btn close-btn">
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="video-chat-body">
|
||||||
|
|
||||||
|
{callActive ? (
|
||||||
|
// TELA DE CHAMADA ATIVA (JITSI)
|
||||||
|
<div className="call-screen">
|
||||||
|
<JitsiMeeting
|
||||||
|
roomName={roomName}
|
||||||
|
domain="meet.jit.si"
|
||||||
|
userInfo={{
|
||||||
|
displayName: MEU_NOME_MEDICO
|
||||||
|
}}
|
||||||
|
configOverwrite={{
|
||||||
|
prejoinPageEnabled: false,
|
||||||
|
enableWelcomePage: false,
|
||||||
|
enableClosePage: false,
|
||||||
|
toolbarButtons: [
|
||||||
|
'microphone', 'camera', 'desktop', 'hangup', 'chat', 'settings'
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
interfaceConfigOverwrite={{
|
||||||
|
SHOW_SUBJECT: false,
|
||||||
|
DISABLE_JOIN_LEAVE_NOTIFICATIONS: true,
|
||||||
|
}}
|
||||||
|
getIFrameRef={(iframe) => { iframe.style.height = '100%'; }}
|
||||||
|
onApiReady={(api) => {
|
||||||
|
api.on('videoConferenceLeft', handleHangUp);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
// TELA DE LISTA DE PACIENTES
|
||||||
|
<div className="patient-list-container">
|
||||||
|
<p>Selecione um paciente para iniciar a chamada:</p>
|
||||||
|
<ul className="patient-list">
|
||||||
|
{mockPacientes.map((paciente) => (
|
||||||
|
<li key={paciente.id} className="patient-item">
|
||||||
|
<span>{paciente.name}</span>
|
||||||
|
<button onClick={() => handleStartCall(paciente)} className="call-btn">
|
||||||
|
<FaVideo size={14} /> Chamar
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* Botão flutuante */}
|
||||||
|
<button className="video-chat-button" onClick={toggleVideoChat}>
|
||||||
|
<FaVideo size={22} color="white" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BotaoVideoChamada;
|
||||||
467
src/components/BotaoVideoPaciente.css
Normal file
467
src/components/BotaoVideoPaciente.css
Normal file
@ -0,0 +1,467 @@
|
|||||||
|
/* ARQUIVO CSS COMPLETAMENTE NOVO E SEPARADO */
|
||||||
|
|
||||||
|
@keyframes slide-up-paciente { /* Nome do keyframe mudado */
|
||||||
|
from { opacity: 0; transform: translateY(20px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.paciente-video-container { /* Classe mudada */
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paciente-video-button { /* Classe mudada */
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 95px; /* Posição igual ao outro, ao lado da acessibilidade */
|
||||||
|
z-index: 9999;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #007bff; /* Cor pode ser diferente, se quiser */
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paciente-video-button:hover { /* Classe mudada */
|
||||||
|
transform: scale(1.1);
|
||||||
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.paciente-video-window { /* Classe mudada */
|
||||||
|
position: fixed;
|
||||||
|
bottom: 90px;
|
||||||
|
right: 95px;
|
||||||
|
width: 500px;
|
||||||
|
height: 380px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 -5px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 10000;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
animation: slide-up-paciente 0.3s ease-out; /* Keyframe mudado */
|
||||||
|
transition: all 0.4s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paciente-video-window.pseudo-fullscreen { /* Classe mudada */
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
border: none;
|
||||||
|
z-index: 99999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paciente-video-window.pseudo-fullscreen .paciente-video-header { /* Classe mudada */
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paciente-video-header { /* Classe mudada */
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 15px;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.paciente-video-header h3 { /* Classe mudada */
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.paciente-video-controls { /* Classe mudada */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Os estilos internos (como .control-btn, .call-screen, .patient-list)
|
||||||
|
podem ser mantidos, pois estão "dentro" das classes que mudamos.
|
||||||
|
Mas para garantir 100% de separação, renomeei todos.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.paciente-video-body { /* Classe mudada */
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0;
|
||||||
|
transition: padding 0.4s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paciente-video-window.pseudo-fullscreen .paciente-video-body { /* Classe mudada */
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilos da Lista e Chamada (copiados e prefixados)
|
||||||
|
Não há problema em reutilizar .patient-list, .call-screen, etc,
|
||||||
|
mas vamos renomear para segurança.
|
||||||
|
*/
|
||||||
|
.patient-list-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.patient-list-container > p {
|
||||||
|
padding: 15px 15px 10px 15px;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #555;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.patient-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.patient-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 15px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
.patient-item:hover {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
.patient-item span {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.call-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
background-color: #28a745;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
.call-btn:hover {
|
||||||
|
background-color: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tela de Chamada */
|
||||||
|
.call-screen {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #2c2c2c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.call-screen h4 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 12px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: rgba(0,0,0,0.2);
|
||||||
|
font-size: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.video-placeholder {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: #888;
|
||||||
|
font-style: italic;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.call-actions {
|
||||||
|
padding: 15px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: rgba(0,0,0,0.2);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.hang-up-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
background-color: #dc3545;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.hang-up-btn:hover {
|
||||||
|
background-color: #c82333;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Controles (reutilizados, mas dentro de .paciente-video-header) */
|
||||||
|
.control-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #888;
|
||||||
|
padding: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
.control-btn:hover {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
}
|
||||||
|
.close-btn {
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.fullscreen-btn {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animação de surgir */
|
||||||
|
@keyframes slide-up-paciente {
|
||||||
|
from { opacity: 0; transform: translateY(20px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animação "Pulsar" (Ringing) */
|
||||||
|
@keyframes ringing {
|
||||||
|
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.7); }
|
||||||
|
70% { transform: scale(1.1); box-shadow: 0 0 0 20px rgba(0, 123, 255, 0); }
|
||||||
|
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(0, 123, 255, 0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.paciente-video-container {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Botão flutuante */
|
||||||
|
.paciente-video-button {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 95px;
|
||||||
|
z-index: 9999;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.paciente-video-button:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
/* Aplica a animação "pulsar" */
|
||||||
|
.paciente-video-button.ringing {
|
||||||
|
animation: ringing 1.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Janela de Vídeo */
|
||||||
|
.paciente-video-window {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 90px;
|
||||||
|
right: 95px;
|
||||||
|
width: 500px;
|
||||||
|
height: 380px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 -5px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 10000;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
animation: slide-up-paciente 0.3s ease-out;
|
||||||
|
transition: all 0.4s ease-in-out;
|
||||||
|
}
|
||||||
|
/* Modo Tela Cheia (Simulado) */
|
||||||
|
.paciente-video-window.pseudo-fullscreen {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
border: none;
|
||||||
|
z-index: 99999;
|
||||||
|
}
|
||||||
|
.paciente-video-window.pseudo-fullscreen .paciente-video-header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header da Janela */
|
||||||
|
.paciente-video-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 15px;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.paciente-video-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.paciente-video-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Corpo da Janela */
|
||||||
|
.paciente-video-body {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0;
|
||||||
|
transition: padding 0.4s ease-in-out;
|
||||||
|
}
|
||||||
|
.paciente-video-window.pseudo-fullscreen .paciente-video-body {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- ESTILOS DOS 3 ESTADOS --- */
|
||||||
|
|
||||||
|
/* 1. Tela de Chamada Ativa (Jitsi) */
|
||||||
|
.call-screen {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #2c2c2c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.video-placeholder { /* (Caso o Jitsi não carregue) */
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: #888;
|
||||||
|
font-style: italic;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Tela de Chamada Recebida */
|
||||||
|
.incoming-call-screen {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #f7f9fc;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.incoming-call-screen p {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #555;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.incoming-call-screen h3 {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #333;
|
||||||
|
margin: 10px 0 30px 0;
|
||||||
|
}
|
||||||
|
.incoming-call-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
.incoming-call-actions button {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.incoming-call-actions button:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
.decline-btn { /* Botão Recusar */
|
||||||
|
background-color: #dc3545;
|
||||||
|
}
|
||||||
|
.decline-btn:hover {
|
||||||
|
background-color: #c82333;
|
||||||
|
}
|
||||||
|
.accept-btn { /* Botão Atender */
|
||||||
|
background-color: #28a745;
|
||||||
|
}
|
||||||
|
.accept-btn:hover {
|
||||||
|
background-color: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. Tela de Espera (Ocioso) */
|
||||||
|
.patient-idle-screen {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
color: #888;
|
||||||
|
font-style: italic;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilos dos controles (reutilizados) */
|
||||||
|
.control-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #888;
|
||||||
|
padding: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
.control-btn:hover {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
}
|
||||||
|
.close-btn {
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.fullscreen-btn {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
171
src/components/BotaoVideoPaciente.jsx
Normal file
171
src/components/BotaoVideoPaciente.jsx
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import './BotaoVideoPaciente.css';
|
||||||
|
import { FaVideo, FaExpand, FaCompress, FaPhoneSlash, FaPhone } from 'react-icons/fa';
|
||||||
|
import { JitsiMeeting } from '@jitsi/react-sdk';
|
||||||
|
import { db } from '../firebaseConfig';
|
||||||
|
import { ref, onValue, remove } from "firebase/database";
|
||||||
|
|
||||||
|
// ID DO PACIENTE
|
||||||
|
const MEU_ID_PACIENTE = '1'; // Deve ser '1' para bater com o do médico
|
||||||
|
|
||||||
|
const BotaoVideoPaciente = () => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [isFullScreen, setIsFullScreen] = useState(false);
|
||||||
|
const [callActive, setCallActive] = useState(false);
|
||||||
|
const [roomName, setRoomName] = useState('');
|
||||||
|
const [incomingCallData, setIncomingCallData] = useState(null);
|
||||||
|
const [callerName, setCallerName] = useState('');
|
||||||
|
|
||||||
|
// "Ouvinte" do Firebase
|
||||||
|
useEffect(() => {
|
||||||
|
const callRef = ref(db, `calls/paciente-${MEU_ID_PACIENTE}`);
|
||||||
|
const unsubscribe = onValue(callRef, (snapshot) => {
|
||||||
|
const data = snapshot.val();
|
||||||
|
if (data && data.incomingCall) {
|
||||||
|
setIncomingCallData(data.incomingCall);
|
||||||
|
setCallerName(data.incomingCall.fromName);
|
||||||
|
setIsOpen(true);
|
||||||
|
} else {
|
||||||
|
setIncomingCallData(null);
|
||||||
|
setCallActive(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => unsubscribe();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// UseEffect da tecla "Esc"
|
||||||
|
useEffect(() => {
|
||||||
|
const handleEscKey = (event) => {
|
||||||
|
if (event.key === 'Escape' && isFullScreen) {
|
||||||
|
setIsFullScreen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('keydown', handleEscKey);
|
||||||
|
return () => document.removeEventListener('keydown', handleEscKey);
|
||||||
|
}, [isFullScreen]);
|
||||||
|
|
||||||
|
// Função para ATENDER
|
||||||
|
const handleAcceptCall = () => {
|
||||||
|
if (!incomingCallData) return;
|
||||||
|
setRoomName(incomingCallData.roomName);
|
||||||
|
setCallActive(true);
|
||||||
|
setIncomingCallData(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Função para RECUSAR / DESLIGAR
|
||||||
|
const handleHangUp = () => {
|
||||||
|
const callRef = ref(db, `calls/paciente-${MEU_ID_PACIENTE}`);
|
||||||
|
remove(callRef);
|
||||||
|
setCallActive(false);
|
||||||
|
setRoomName('');
|
||||||
|
setCallerName('');
|
||||||
|
setIncomingCallData(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Função para fechar a janela
|
||||||
|
const toggleVideoChat = () => {
|
||||||
|
setIsOpen(!isOpen);
|
||||||
|
if (isOpen) {
|
||||||
|
handleHangUp();
|
||||||
|
setIsFullScreen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFullScreen = () => {
|
||||||
|
setIsFullScreen(!isFullScreen);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Renderiza o conteúdo (Ocioso, Recebendo, Em Chamada)
|
||||||
|
const renderContent = () => {
|
||||||
|
// 1ª Prioridade: Em chamada ativa
|
||||||
|
if (callActive) {
|
||||||
|
return (
|
||||||
|
<div className="call-screen">
|
||||||
|
<JitsiMeeting
|
||||||
|
roomName={roomName}
|
||||||
|
domain="meet.jit.si"
|
||||||
|
// Informações do Usuário (Paciente)
|
||||||
|
userInfo={{
|
||||||
|
displayName: 'Paciente' // Você pode mudar isso
|
||||||
|
}}
|
||||||
|
// Configurações para pular todas as telas
|
||||||
|
configOverwrite={{
|
||||||
|
prejoinPageEnabled: false,
|
||||||
|
enableWelcomePage: false,
|
||||||
|
enableClosePage: false,
|
||||||
|
toolbarButtons: [
|
||||||
|
'microphone', 'camera', 'hangup', 'chat', 'settings'
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
// Configurações da Interface
|
||||||
|
interfaceConfigOverwrite={{
|
||||||
|
SHOW_SUBJECT: false,
|
||||||
|
DISABLE_JOIN_LEAVE_NOTIFICATIONS: true,
|
||||||
|
}}
|
||||||
|
getIFrameRef={(iframe) => { iframe.style.height = '100%'; }}
|
||||||
|
onApiReady={(api) => {
|
||||||
|
api.on('videoConferenceLeft', handleHangUp);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2ª Prioridade: Recebendo uma chamada
|
||||||
|
if (incomingCallData) {
|
||||||
|
return (
|
||||||
|
<div className="incoming-call-screen">
|
||||||
|
<p>Chamada recebida de:</p>
|
||||||
|
<h3>{callerName || 'Médico'}</h3>
|
||||||
|
<div className="incoming-call-actions">
|
||||||
|
<button className="decline-btn" onClick={handleHangUp}>
|
||||||
|
<FaPhoneSlash size={20} /> Recusar
|
||||||
|
</button>
|
||||||
|
<button className="accept-btn" onClick={handleAcceptCall}>
|
||||||
|
<FaPhone size={20} /> Atender
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3ª Prioridade: Nenhuma chamada, tela de espera
|
||||||
|
return (
|
||||||
|
<div className="patient-idle-screen">
|
||||||
|
<p>Aguardando chamadas do seu médico...</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="paciente-video-container">
|
||||||
|
{isOpen && (
|
||||||
|
<div className={`paciente-video-window ${isFullScreen ? 'pseudo-fullscreen' : ''}`}>
|
||||||
|
<div className="paciente-video-header">
|
||||||
|
<h3>{callActive ? `Em chamada...` : (incomingCallData ? 'Chamada Recebida' : 'Videochamada')}</h3>
|
||||||
|
<div className="paciente-video-controls">
|
||||||
|
<button onClick={handleFullScreen} className="control-btn fullscreen-btn">
|
||||||
|
{isFullScreen ? <FaCompress size={14} /> : <FaExpand size={14} />}
|
||||||
|
</button>
|
||||||
|
<button onClick={toggleVideoChat} className="control-btn close-btn">
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="paciente-video-body">
|
||||||
|
{renderContent()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={`paciente-video-button ${incomingCallData ? 'ringing' : ''}`}
|
||||||
|
onClick={toggleVideoChat}
|
||||||
|
>
|
||||||
|
<FaVideo size={22} color="white" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BotaoVideoPaciente;
|
||||||
@ -1,13 +1,13 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useAuth } from "./utils/AuthProvider";
|
import { useAuth } from "./utils/AuthProvider";
|
||||||
import API_KEY from "./utils/apiKeys";
|
import API_KEY from "./utils/apiKeys";
|
||||||
import "./AgendarConsulta/style/formagendamentos.css";
|
import "./AgendarConsulta/style/formagendamentos.css";
|
||||||
|
import { GetAllDoctors } from './utils/Functions-Endpoints/Doctor';
|
||||||
|
|
||||||
|
|
||||||
const ENDPOINT_CRIAR_EXCECAO = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_exceptions";
|
const ENDPOINT_CRIAR_EXCECAO = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_exceptions";
|
||||||
|
|
||||||
const FormCriarExcecao = ({ onCancel, doctorID }) => {
|
const FormCriarExcecao = ({ onCancel, doctorID }) => {
|
||||||
|
|
||||||
const { getAuthorizationHeader, user, getUserInfo } = useAuth();
|
const { getAuthorizationHeader, user, getUserInfo } = useAuth();
|
||||||
const [dadosAtendimento, setDadosAtendimento] = useState({
|
const [dadosAtendimento, setDadosAtendimento] = useState({
|
||||||
profissional: doctorID || '',
|
profissional: doctorID || '',
|
||||||
@ -18,6 +18,13 @@ const FormCriarExcecao = ({ onCancel, doctorID }) => {
|
|||||||
motivo: ''
|
motivo: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [todosProfissionais, setTodosProfissionais] = useState([]);
|
||||||
|
const [profissionaisFiltrados, setProfissionaisFiltrados] = useState([]);
|
||||||
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||||
|
|
||||||
|
const [doctorSearchName, setDoctorSearchName] = useState('');
|
||||||
|
const [searchingDoctor, setSearchingDoctor] = useState(false);
|
||||||
|
|
||||||
const handleAtendimentoChange = (e) => {
|
const handleAtendimentoChange = (e) => {
|
||||||
const { value, name } = e.target;
|
const { value, name } = e.target;
|
||||||
setDadosAtendimento(prev => ({
|
setDadosAtendimento(prev => ({
|
||||||
@ -26,6 +33,52 @@ const FormCriarExcecao = ({ onCancel, doctorID }) => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadDoctors = async () => {
|
||||||
|
setSearchingDoctor(true);
|
||||||
|
let authHeader = '';
|
||||||
|
try { authHeader = getAuthorizationHeader ? getAuthorizationHeader() : ''; } catch {}
|
||||||
|
try {
|
||||||
|
const Medicos = await GetAllDoctors(authHeader);
|
||||||
|
setTodosProfissionais(Array.isArray(Medicos) ? Medicos : []);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Erro ao carregar médicos:', err);
|
||||||
|
setTodosProfissionais([]);
|
||||||
|
} finally {
|
||||||
|
setSearchingDoctor(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadDoctors();
|
||||||
|
}, [getAuthorizationHeader]);
|
||||||
|
|
||||||
|
const handleSearchProfissional = (e) => {
|
||||||
|
const term = e.target.value;
|
||||||
|
setDoctorSearchName(term);
|
||||||
|
if (term.trim() === '') {
|
||||||
|
setProfissionaisFiltrados([]);
|
||||||
|
setIsDropdownOpen(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const filtered = todosProfissionais.filter(p =>
|
||||||
|
(p.full_name || '').toLowerCase().includes(term.toLowerCase())
|
||||||
|
);
|
||||||
|
setProfissionaisFiltrados(filtered);
|
||||||
|
setIsDropdownOpen(filtered.length > 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelectProfissional = (profissional) => {
|
||||||
|
setDadosAtendimento(prev => ({
|
||||||
|
...prev,
|
||||||
|
profissional: profissional.id
|
||||||
|
}));
|
||||||
|
setDoctorSearchName(profissional.full_name || '');
|
||||||
|
setProfissionaisFiltrados([]);
|
||||||
|
setIsDropdownOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// lista simples de valores permitidos
|
||||||
|
const ALLOWED_KINDS = ['disponibilidade_extra', 'bloqueio'];
|
||||||
|
|
||||||
const handleSubmitExcecao = async (e) => {
|
const handleSubmitExcecao = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
console.log("Tentando criar Exceção.");
|
console.log("Tentando criar Exceção.");
|
||||||
@ -37,6 +90,13 @@ const FormCriarExcecao = ({ onCancel, doctorID }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// usa diretamente o value selecionado (já definido no <select>) e valida
|
||||||
|
const mappedKind = tipoAtendimento;
|
||||||
|
if (!ALLOWED_KINDS.includes(mappedKind)) {
|
||||||
|
alert(`Tipo inválido: "${tipoAtendimento}". Tipos aceitos: ${ALLOWED_KINDS.join(', ')}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const startTime = inicio ? inicio + ":00" : null;
|
const startTime = inicio ? inicio + ":00" : null;
|
||||||
const endTime = termino ? termino + ":00" : null;
|
const endTime = termino ? termino + ":00" : null;
|
||||||
|
|
||||||
@ -70,7 +130,7 @@ const FormCriarExcecao = ({ onCancel, doctorID }) => {
|
|||||||
const raw = JSON.stringify({
|
const raw = JSON.stringify({
|
||||||
doctor_id: profissional,
|
doctor_id: profissional,
|
||||||
date: dataAtendimento,
|
date: dataAtendimento,
|
||||||
kind: tipoAtendimento,
|
kind: mappedKind,
|
||||||
start_time: startTime,
|
start_time: startTime,
|
||||||
end_time: endTime,
|
end_time: endTime,
|
||||||
reason: motivo,
|
reason: motivo,
|
||||||
@ -119,7 +179,30 @@ const FormCriarExcecao = ({ onCancel, doctorID }) => {
|
|||||||
<h2 className="section-title">Informações da Nova Exceção</h2>
|
<h2 className="section-title">Informações da Nova Exceção</h2>
|
||||||
|
|
||||||
<div className="campo-informacoes-atendimento">
|
<div className="campo-informacoes-atendimento">
|
||||||
|
{/* Busca por nome usando filtragem local */}
|
||||||
|
<div className="campo-de-input campo-de-input-container">
|
||||||
|
<label>Nome do médico</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="doctorSearchName"
|
||||||
|
placeholder="Digite o nome do médico"
|
||||||
|
value={doctorSearchName}
|
||||||
|
onChange={handleSearchProfissional}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
{isDropdownOpen && profissionaisFiltrados.length > 0 && (
|
||||||
|
<div className="dropdown-profissionais">
|
||||||
|
{profissionaisFiltrados.map(p => (
|
||||||
|
<div key={p.id} className="dropdown-item" onClick={() => handleSelectProfissional(p)}>
|
||||||
|
{p.full_name}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{searchingDoctor && <small>Carregando médicos...</small>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ID do profissional (preenchido ao selecionar) */}
|
||||||
<div className="campo-de-input">
|
<div className="campo-de-input">
|
||||||
<label>ID do profissional *</label>
|
<label>ID do profissional *</label>
|
||||||
<input
|
<input
|
||||||
@ -134,12 +217,11 @@ const FormCriarExcecao = ({ onCancel, doctorID }) => {
|
|||||||
<div className="campo-de-input">
|
<div className="campo-de-input">
|
||||||
<label>Tipo de exceção *</label>
|
<label>Tipo de exceção *</label>
|
||||||
<select name="tipoAtendimento" onChange={handleAtendimentoChange} value={dadosAtendimento.tipoAtendimento} required>
|
<select name="tipoAtendimento" onChange={handleAtendimentoChange} value={dadosAtendimento.tipoAtendimento} required>
|
||||||
<option value="">Selecione o tipo de exceção</option>
|
<option value="" disabled>Selecione o tipo de exceção</option>
|
||||||
<option value="liberacao" >Liberação (Criar Slot)</option>
|
<option value="disponibilidade_extra" >Liberação</option>
|
||||||
<option value="bloqueio" >Bloqueio (Remover Slot)</option>
|
<option value="bloqueio" >Bloqueio</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section id="informacoes-atendimento-segunda-linha">
|
<section id="informacoes-atendimento-segunda-linha">
|
||||||
|
|||||||
15
src/components/Spinner.jsx
Normal file
15
src/components/Spinner.jsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const Spinner = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="d-flex justify-content-center align-items-center" style={{ height: "100%" }}>
|
||||||
|
<div className="spinner-border text-primary" role="status">
|
||||||
|
<span className="visually-hidden">Carregando...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Spinner
|
||||||
@ -8,7 +8,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Seus Agendamentos",
|
"name": "Seus Agendamentos",
|
||||||
"icon": "calendar",
|
"icon": "calendar",
|
||||||
"url": "/medico/agendamentoMedico"
|
"url": "/medico/agendamento"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
20
src/firebaseConfig.js
Normal file
20
src/firebaseConfig.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// 1. ADICIONE ESTAS DUAS LINHAS NO TOPO
|
||||||
|
import { initializeApp } from "firebase/app";
|
||||||
|
import { getDatabase } from "firebase/database";
|
||||||
|
|
||||||
|
// 2. COLE AQUI O OBJETO QUE VOCÊ COPIOU DO SITE DO FIREBASE
|
||||||
|
const firebaseConfig = {
|
||||||
|
apiKey: "SUA_API_KEY...",
|
||||||
|
authDomain: "medimeconnect.firebaseapp.com",
|
||||||
|
databaseURL: "https://medimeconnect-default-rtdb.firebaseio.com",
|
||||||
|
projectId: "medimeconnect",
|
||||||
|
storageBucket: "medimeconnect.appspot.com",
|
||||||
|
messagingSenderId: "SEU_ID_MESSAGING",
|
||||||
|
appId: "SEU_APP_ID"
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3. TENHA CERTEZA QUE ESSAS LINHAS ESTÃO NO FINAL
|
||||||
|
const app = initializeApp(firebaseConfig);
|
||||||
|
|
||||||
|
// A LINHA MAIS IMPORTANTE QUE ESTÁ FALTANDO É ESTA:
|
||||||
|
export const db = getDatabase(app);
|
||||||
@ -19,6 +19,8 @@ import "./style/Agendamento.css";
|
|||||||
import './style/FilaEspera.css';
|
import './style/FilaEspera.css';
|
||||||
import { Search } from 'lucide-react';
|
import { Search } from 'lucide-react';
|
||||||
|
|
||||||
|
import Spinner from '../components/Spinner.jsx';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -70,8 +72,6 @@ const Agendamento = ({ setDictInfo }) => {
|
|||||||
const cacheMedicos = {};
|
const cacheMedicos = {};
|
||||||
const cachePacientes = {};
|
const cachePacientes = {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useMemo(() => {
|
useMemo(() => {
|
||||||
if (!listaTodosAgendamentos.length) return { agendamentosOrganizados: {}, filaEsperaData: [] };
|
if (!listaTodosAgendamentos.length) return { agendamentosOrganizados: {}, filaEsperaData: [] };
|
||||||
console.log("recarregando")
|
console.log("recarregando")
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { useAuth } from '../components/utils/AuthProvider'
|
|||||||
import { useEffect,useState } from 'react'
|
import { useEffect,useState } from 'react'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { UserInfos } from '../components/utils/Functions-Endpoints/General'
|
import { UserInfos } from '../components/utils/Functions-Endpoints/General'
|
||||||
const AgendamentoCadastroManager = ({setPageConsulta}) => {
|
const AgendamentoCadastroManager = ({setPageConsulta, Dict}) => {
|
||||||
|
|
||||||
const {getAuthorizationHeader} = useAuth()
|
const {getAuthorizationHeader} = useAuth()
|
||||||
const [agendamento, setAgendamento] = useState({status:'confirmed'})
|
const [agendamento, setAgendamento] = useState({status:'confirmed'})
|
||||||
@ -16,6 +16,12 @@ const AgendamentoCadastroManager = ({setPageConsulta}) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
|
if(!Dict){setAgendamento({})}
|
||||||
|
else{
|
||||||
|
console.log(Dict)
|
||||||
|
setAgendamento(Dict)
|
||||||
|
}
|
||||||
|
|
||||||
const ColherInfoUsuario =async () => {
|
const ColherInfoUsuario =async () => {
|
||||||
const result = await UserInfos(authHeader)
|
const result = await UserInfos(authHeader)
|
||||||
|
|
||||||
@ -37,13 +43,14 @@ const AgendamentoCadastroManager = ({setPageConsulta}) => {
|
|||||||
myHeaders.append("Content-Type", "application/json");
|
myHeaders.append("Content-Type", "application/json");
|
||||||
|
|
||||||
var raw = JSON.stringify({
|
var raw = JSON.stringify({
|
||||||
|
|
||||||
"patient_id": Dict.patient_id,
|
"patient_id": Dict.patient_id,
|
||||||
"doctor_id": Dict.doctor_id,
|
"doctor_id": Dict.doctor_id,
|
||||||
"scheduled_at": `${Dict.dataAtendimento}T${Dict.horarioInicio}:00.000Z`,
|
"scheduled_at": `${Dict.dataAtendimento}T${Dict.horarioInicio}:00.000Z`,
|
||||||
"duration_minutes": 30,
|
"duration_minutes": 30,
|
||||||
"appointment_type": Dict.tipo_consulta,
|
"appointment_type": Dict.tipo_consulta,
|
||||||
|
|
||||||
"patient_notes": "Prefiro horário pela manhã",
|
"patient_notes": "",
|
||||||
"insurance_provider": Dict.convenio,
|
"insurance_provider": Dict.convenio,
|
||||||
"status": Dict.status,
|
"status": Dict.status,
|
||||||
"created_by": idUsuario
|
"created_by": idUsuario
|
||||||
|
|||||||
@ -16,12 +16,12 @@ const AgendamentoEditPage = ({setDictInfo, DictInfo}) => {
|
|||||||
|
|
||||||
let id = params.id
|
let id = params.id
|
||||||
|
|
||||||
console.log(DictInfo, "DENTRO DO EDITAR")
|
|
||||||
|
|
||||||
//console.log(DictInfo, 'aqui')
|
//console.log(DictInfo, 'aqui')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDictInfo({...DictInfo, dataAtendimento:DictInfo.scheduled_at.split("T")[0]})
|
setDictInfo({...DictInfo, dataAtendimento:DictInfo.scheduled_at.split("T")[0]})
|
||||||
|
|
||||||
const ColherInfoUsuario =async () => {
|
const ColherInfoUsuario =async () => {
|
||||||
const result = await UserInfos(authHeader)
|
const result = await UserInfos(authHeader)
|
||||||
@ -51,9 +51,7 @@ const AgendamentoEditPage = ({setDictInfo, DictInfo}) => {
|
|||||||
"doctor_id": DictParaPatch.doctor_id,
|
"doctor_id": DictParaPatch.doctor_id,
|
||||||
|
|
||||||
"duration_minutes": 30,
|
"duration_minutes": 30,
|
||||||
|
|
||||||
"chief_complaint": "Dor de cabeça há 3 ",
|
"chief_complaint": "Dor de cabeça há 3 ",
|
||||||
|
|
||||||
"created_by": idUsuario,
|
"created_by": idUsuario,
|
||||||
|
|
||||||
"scheduled_at": `${DictParaPatch.dataAtendimento}T${DictParaPatch.horarioInicio}:00.000Z`,
|
"scheduled_at": `${DictParaPatch.dataAtendimento}T${DictParaPatch.horarioInicio}:00.000Z`,
|
||||||
|
|||||||
@ -127,6 +127,8 @@ function Login({ onEnterSystem }) {
|
|||||||
navigate(`/medico/`);
|
navigate(`/medico/`);
|
||||||
} else if (UserData?.roles?.includes("financeiro")) {
|
} else if (UserData?.roles?.includes("financeiro")) {
|
||||||
navigate(`/financeiro/`);
|
navigate(`/financeiro/`);
|
||||||
|
} else if (UserData?.roles?.includes("paciente")) {
|
||||||
|
navigate(`/paciente/`);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
console.log("ERROROROROROOR")
|
console.log("ERROROROROROOR")
|
||||||
|
|||||||
@ -194,9 +194,7 @@ html[data-bs-theme="dark"] {
|
|||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.unidade-selecionarprofissional input,
|
|
||||||
.unidade-selecionarprofissional select,
|
|
||||||
.busca-atendimento select,
|
|
||||||
.busca-atendimento input {
|
.busca-atendimento input {
|
||||||
background-color: #2c2c2c;
|
background-color: #2c2c2c;
|
||||||
color: #e0e0e0;
|
color: #e0e0e0;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Routes, Route } from "react-router-dom";
|
import { Routes, Route } from "react-router-dom";
|
||||||
import Sidebar from "../../components/Sidebar";
|
import Sidebar from "../../components/Sidebar";
|
||||||
|
import { useState } from "react";
|
||||||
import DoctorRelatorioManager from "../../PagesMedico/DoctorRelatorioManager";
|
import DoctorRelatorioManager from "../../PagesMedico/DoctorRelatorioManager";
|
||||||
import Prontuario from "../../PagesMedico/prontuario";
|
import Prontuario from "../../PagesMedico/prontuario";
|
||||||
import Relatorio from "../../PagesMedico/relatorio";
|
import Relatorio from "../../PagesMedico/relatorio";
|
||||||
@ -9,9 +9,15 @@ import Chat from "../../PagesMedico/Chat";
|
|||||||
import DoctorItems from "../../data/sidebar-items-medico.json";
|
import DoctorItems from "../../data/sidebar-items-medico.json";
|
||||||
import FormNovoRelatorio from "../../PagesMedico/FormNovoRelatorio";
|
import FormNovoRelatorio from "../../PagesMedico/FormNovoRelatorio";
|
||||||
import EditPageRelatorio from "../../PagesMedico/EditPageRelatorio";
|
import EditPageRelatorio from "../../PagesMedico/EditPageRelatorio";
|
||||||
// ...existing code...
|
import BotaoVideoChamada from '../../components/BotaoVideoChamada';
|
||||||
|
|
||||||
|
import DoctorAgendamentoEditPage from "../../PagesMedico/DoctorAgendamentoEditPage";
|
||||||
|
|
||||||
function PerfilMedico() {
|
function PerfilMedico() {
|
||||||
|
|
||||||
|
const [dictInfo, setDictInfo] = useState({})
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div id="app" className="active">
|
<div id="app" className="active">
|
||||||
@ -23,10 +29,15 @@ function PerfilMedico() {
|
|||||||
<Route path="/relatorios/:id/edit" element={<EditPageRelatorio />} />
|
<Route path="/relatorios/:id/edit" element={<EditPageRelatorio />} />
|
||||||
<Route path="/prontuario" element={<Prontuario />} />
|
<Route path="/prontuario" element={<Prontuario />} />
|
||||||
<Route path="/relatorios" element={<DoctorRelatorioManager />} />
|
<Route path="/relatorios" element={<DoctorRelatorioManager />} />
|
||||||
<Route path="/agendamentoMedico" element={<DoctorAgendamentoManager />} />
|
<Route path="/agendamento" element={<DoctorAgendamentoManager setDictInfo={setDictInfo}/>} />
|
||||||
|
<Route path="/agendamento/edit" element={<DoctorAgendamentoEditPage DictInfo={dictInfo} setDictInfo={setDictInfo}/>} />
|
||||||
<Route path="/chat" element={<Chat />} />
|
<Route path="/chat" element={<Chat />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* ADICIONADO AQUI */}
|
||||||
|
<BotaoVideoChamada />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|||||||
@ -6,9 +6,15 @@ import LaudoManager from "../../pages/LaudoManager";
|
|||||||
import ConsultaCadastroManager from "../../PagesPaciente/ConsultaCadastroManager";
|
import ConsultaCadastroManager from "../../PagesPaciente/ConsultaCadastroManager";
|
||||||
import ConsultasPaciente from "../../PagesPaciente/ConsultasPaciente";
|
import ConsultasPaciente from "../../PagesPaciente/ConsultasPaciente";
|
||||||
import ConsultaEditPage from "../../PagesPaciente/ConsultaEditPage";
|
import ConsultaEditPage from "../../PagesPaciente/ConsultaEditPage";
|
||||||
|
|
||||||
|
// 1. IMPORTAÇÃO ADICIONADA
|
||||||
|
import BotaoVideoPaciente from "../../components/BotaoVideoPaciente";
|
||||||
|
|
||||||
function PerfilPaciente({ onLogout }) {
|
function PerfilPaciente({ onLogout }) {
|
||||||
|
|
||||||
const [dadosConsulta, setConsulta] = useState({})
|
|
||||||
|
|
||||||
|
const [DictInfo, setDictInfo] = useState({})
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -19,14 +25,19 @@ const [dadosConsulta, setConsulta] = useState({})
|
|||||||
<div id="main">
|
<div id="main">
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<LaudoManager />} />
|
<Route path="/" element={<LaudoManager />} />
|
||||||
<Route path="agendamento" element={<ConsultasPaciente setConsulta={setConsulta}/>} />
|
<Route path="agendamento" element={<ConsultasPaciente setDictInfo={setDictInfo}/>} />
|
||||||
<Route path="agendamento/criar" element={<ConsultaCadastroManager />} />
|
<Route path="agendamento/criar" element={<ConsultaCadastroManager />} />
|
||||||
<Route path="agendamento/edit" element={<ConsultaEditPage dadosConsulta={dadosConsulta} />} />
|
<Route path="agendamento/edit" element={<ConsultaEditPage DictInfo={DictInfo} />} />
|
||||||
<Route path="laudo" element={<LaudoManager />} />
|
<Route path="laudo" element={<LaudoManager />} />
|
||||||
<Route path="*" element={<h2>Página não encontrada</h2>} />
|
<Route path="*" element={<h2>Página não encontrada</h2>} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 2. COMPONENTE ADICIONADO AQUI */}
|
||||||
|
<BotaoVideoPaciente />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -37,7 +37,7 @@ function PerfilSecretaria({ onLogout }) {
|
|||||||
<Route path="medicos/:id" element={<DoctorDetails />} />
|
<Route path="medicos/:id" element={<DoctorDetails />} />
|
||||||
<Route path="medicos/:id/edit" element={<DoctorEditPage />} />
|
<Route path="medicos/:id/edit" element={<DoctorEditPage />} />
|
||||||
<Route path="agendamento" element={<Agendamento setDictInfo={setDictInfo}/>} />
|
<Route path="agendamento" element={<Agendamento setDictInfo={setDictInfo}/>} />
|
||||||
<Route path="agendamento/:id/edit" element={<AgendamentoEditPage setDictInfo={setDictInfo} DictInfo={DictInfo}/>} />
|
<Route path="agendamento/edit" element={<AgendamentoEditPage setDictInfo={setDictInfo} DictInfo={DictInfo}/>} />
|
||||||
<Route path="laudo" element={<LaudoManager />} />
|
<Route path="laudo" element={<LaudoManager />} />
|
||||||
<Route path="disponibilidade" element={<DisponibilidadesDoctorPage />} />
|
<Route path="disponibilidade" element={<DisponibilidadesDoctorPage />} />
|
||||||
<Route path="horarios" element={<HorariosDisponibilidade/>}/>
|
<Route path="horarios" element={<HorariosDisponibilidade/>}/>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user