Ultimas correções
This commit is contained in:
parent
176489f9fd
commit
d67f4d6db4
@ -1,14 +1,21 @@
|
||||
import React, { useState, useMemo, useEffect, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import API_KEY from '../components/utils/apiKeys.js';
|
||||
import AgendamentoCadastroManager from '../pages/AgendamentoCadastroManager.jsx';
|
||||
import { GetAllDoctors } from '../components/utils/Functions-Endpoints/Doctor.js';
|
||||
import { useAuth } from '../components/utils/AuthProvider.js';
|
||||
import dayjs from 'dayjs';
|
||||
import 'dayjs/locale/pt-br';
|
||||
import isBetween from 'dayjs/plugin/isBetween';
|
||||
import localeData from 'dayjs/plugin/localeData';
|
||||
import { Search, ChevronLeft, ChevronRight, Edit, Trash2, CheckCircle } from 'lucide-react';
|
||||
import React, { useState, useMemo, useEffect, useCallback } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import API_KEY from "../components/utils/apiKeys.js";
|
||||
import AgendamentoCadastroManager from "../pages/AgendamentoCadastroManager.jsx";
|
||||
import { GetAllDoctors } from "../components/utils/Functions-Endpoints/Doctor.js";
|
||||
import { useAuth } from "../components/utils/AuthProvider.js";
|
||||
import dayjs from "dayjs";
|
||||
import "dayjs/locale/pt-br";
|
||||
import isBetween from "dayjs/plugin/isBetween";
|
||||
import localeData from "dayjs/plugin/localeData";
|
||||
import {
|
||||
Search,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Edit,
|
||||
Trash2,
|
||||
CheckCircle,
|
||||
} from "lucide-react";
|
||||
import "../pages/style/Agendamento.css";
|
||||
import "../pages/style/FilaEspera.css";
|
||||
import Spinner from "../components/Spinner.jsx";
|
||||
@ -22,7 +29,6 @@ const Agendamento = () => {
|
||||
const { getAuthorizationHeader, user } = useAuth();
|
||||
const authHeader = getAuthorizationHeader();
|
||||
|
||||
|
||||
const ID_MEDICO_ESPECIFICO = "078d2a67-b4c1-43c8-ae32-c1e75bb5b3df";
|
||||
|
||||
const [listaTodosAgendamentos, setListaTodosAgendamentos] = useState([]);
|
||||
@ -55,15 +61,17 @@ const Agendamento = () => {
|
||||
year: currentDate.year(),
|
||||
});
|
||||
|
||||
|
||||
const fetchAppointments = useCallback(async () => {
|
||||
if (!authHeader) return;
|
||||
setShowSpinner(true);
|
||||
const myHeaders = new Headers();
|
||||
myHeaders.append("Authorization", authHeader);
|
||||
myHeaders.append("apikey", API_KEY);
|
||||
const requestOptions = { method: 'GET', headers: myHeaders, redirect: 'follow' };
|
||||
|
||||
const requestOptions = {
|
||||
method: "GET",
|
||||
headers: myHeaders,
|
||||
redirect: "follow",
|
||||
};
|
||||
|
||||
const apiUrl = `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?doctor_id=eq.${ID_MEDICO_ESPECIFICO}&select=*`;
|
||||
|
||||
@ -155,10 +163,12 @@ const Agendamento = () => {
|
||||
if (authHeader) {
|
||||
fetchAppointments();
|
||||
|
||||
if (user?.role !== 'doctor') {
|
||||
GetAllDoctors(authHeader).then(docs => {
|
||||
if (user?.role !== "doctor") {
|
||||
GetAllDoctors(authHeader).then((docs) => {
|
||||
if (docs) {
|
||||
setListaDeMedicos(docs.map(d => ({ nomeMedico: d.full_name, idMedico: d.id })));
|
||||
setListaDeMedicos(
|
||||
docs.map((d) => ({ nomeMedico: d.full_name, idMedico: d.id }))
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -167,7 +177,6 @@ const Agendamento = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const processData = async () => {
|
||||
|
||||
if (!listaTodosAgendamentos.length) {
|
||||
setAgendamentosOrganizados({});
|
||||
setFilaEsperaData([]);
|
||||
@ -176,7 +185,6 @@ const Agendamento = () => {
|
||||
|
||||
setShowSpinner(true);
|
||||
|
||||
|
||||
const appointmentsToShow = listaTodosAgendamentos;
|
||||
|
||||
const patientIdsToFetch = new Set();
|
||||
@ -194,11 +202,14 @@ const Agendamento = () => {
|
||||
const fetchPromises = [];
|
||||
|
||||
if (patientIdsToFetch.size > 0) {
|
||||
const query = `id=in.(${Array.from(patientIdsToFetch).join(',')})`;
|
||||
const query = `id=in.(${Array.from(patientIdsToFetch).join(",")})`;
|
||||
fetchPromises.push(
|
||||
fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients?${query}&select=*`, {
|
||||
headers: { apikey: API_KEY, Authorization: authHeader }
|
||||
}).then(res => res.json())
|
||||
fetch(
|
||||
`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients?${query}&select=*`,
|
||||
{
|
||||
headers: { apikey: API_KEY, Authorization: authHeader },
|
||||
}
|
||||
).then((res) => res.json())
|
||||
);
|
||||
} else {
|
||||
fetchPromises.push(Promise.resolve(null));
|
||||
@ -274,8 +285,6 @@ const Agendamento = () => {
|
||||
processData();
|
||||
}, [listaTodosAgendamentos, authHeader]);
|
||||
|
||||
|
||||
|
||||
const handleEditConsulta = (agendamento) => {
|
||||
setAgendamentoParaEdicao(agendamento);
|
||||
setPageConsulta(true);
|
||||
@ -306,22 +315,48 @@ const Agendamento = () => {
|
||||
};
|
||||
|
||||
const dateGrid = useMemo(() => generateDateGrid(), [currentDate]);
|
||||
const weekDays = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'];
|
||||
const weekDays = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"];
|
||||
const handleDateClick = (day) => setSelectedDay(day);
|
||||
const DeleteModal = () => (
|
||||
<div className="modal fade show delete-modal" style={{ display: "block", backgroundColor: "rgba(0,0,0,0.5)" }} tabIndex="-1">
|
||||
<div
|
||||
className="modal fade show delete-modal"
|
||||
style={{ display: "block", backgroundColor: "rgba(0,0,0,0.5)" }}
|
||||
tabIndex="-1"
|
||||
>
|
||||
<div className="modal-dialog modal-dialog-centered">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header" style={{ backgroundColor: '#dc3545', color: 'white' }}>
|
||||
<div
|
||||
className="modal-header"
|
||||
style={{ backgroundColor: "#dc3545", color: "white" }}
|
||||
>
|
||||
<h5 className="modal-title">Confirmação de Cancelamento</h5>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<p>Qual o motivo do cancelamento?</p>
|
||||
<textarea className="form-control" rows="3" value={motivoCancelamento} onChange={(e) => setMotivoCancelamento(e.target.value)} placeholder="Ex: Motivo pessoal, reagendamento, etc."></textarea>
|
||||
<textarea
|
||||
className="form-control"
|
||||
rows="3"
|
||||
value={motivoCancelamento}
|
||||
onChange={(e) => setMotivoCancelamento(e.target.value)}
|
||||
placeholder="Ex: Motivo pessoal, reagendamento, etc."
|
||||
></textarea>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-primary" onClick={() => { setShowDeleteModal(false); setMotivoCancelamento(""); }}>Cancelar</button>
|
||||
<button type="button" className="btn btn-danger" onClick={() => deleteConsulta(selectedID)}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
onClick={() => {
|
||||
setShowDeleteModal(false);
|
||||
setMotivoCancelamento("");
|
||||
}}
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-danger"
|
||||
onClick={() => deleteConsulta(selectedID)}
|
||||
>
|
||||
Confirmar Cancelamento
|
||||
</button>
|
||||
</div>
|
||||
@ -412,22 +447,9 @@ const DeleteModal = () => (
|
||||
return (
|
||||
<div>
|
||||
<h1>Agendar nova consulta</h1>
|
||||
<div className="btns-gerenciamento-e-consulta" style={{ display: 'flex', gap: '10px', marginBottom: '20px' }}>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={() => {
|
||||
setAgendamentoParaEdicao(null);
|
||||
setPageConsulta(true);
|
||||
}}
|
||||
>
|
||||
<i className="bi bi-plus-circle"></i> Adicionar Consulta
|
||||
</button>
|
||||
<button className="manage-button btn" onClick={() => navigate("/secretaria/excecoes-disponibilidade")}><i className="bi bi-gear-fill me-1"></i> Gerenciar Exceções</button>
|
||||
<button className='manage-button btn' onClick={() => navigate('/secretaria/disponibilidade')}><i className="bi bi-gear-fill me-1"></i> Mudar Disponibilidade</button>
|
||||
</div>
|
||||
{!PageNovaConsulta ? (
|
||||
<div className='atendimento-eprocura'>
|
||||
{user?.role !== 'doctor' && (
|
||||
<div className="atendimento-eprocura">
|
||||
{user?.role !== "doctor" && (
|
||||
<div className="card p-3 mb-3 table-paciente-filters">
|
||||
<h5 className="mb-3">
|
||||
<i className="bi bi-funnel-fill me-2 text-primary"></i>
|
||||
@ -441,10 +463,19 @@ const DeleteModal = () => (
|
||||
value={searchTermDoctor}
|
||||
onChange={(e) => handleSearchMedicos(e.target.value)}
|
||||
/>
|
||||
<small className="text-muted">Buscar médico para filtrar consultas</small>
|
||||
<small className="text-muted">
|
||||
Buscar médico para filtrar consultas
|
||||
</small>
|
||||
|
||||
{searchTermDoctor && FiltredTodosMedicos.length > 0 && (
|
||||
<div className="list-group position-absolute w-100" style={{ zIndex: 1000, maxHeight: '200px', overflowY: 'auto' }}>
|
||||
<div
|
||||
className="list-group position-absolute w-100"
|
||||
style={{
|
||||
zIndex: 1000,
|
||||
maxHeight: "200px",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
{FiltredTodosMedicos.map((medico) => (
|
||||
<button
|
||||
key={medico.idMedico}
|
||||
@ -471,10 +502,10 @@ const DeleteModal = () => (
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close btn-close-white ms-2"
|
||||
style={{ fontSize: '0.6rem' }}
|
||||
style={{ fontSize: "0.6rem" }}
|
||||
onClick={() => {
|
||||
setMedicoFiltrado({ id: "vazio" });
|
||||
setSearchTermDoctor('');
|
||||
setSearchTermDoctor("");
|
||||
}}
|
||||
></button>
|
||||
</span>
|
||||
@ -482,29 +513,114 @@ const DeleteModal = () => (
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className='container-btns-agenda-fila_esepera'>
|
||||
<button className={`btn-agenda ${!FiladeEspera ? "opc-agenda-ativo" : ""}`} onClick={() => { setFiladeEspera(false); }}>Agenda</button>
|
||||
<button className={`btn-fila-espera ${FiladeEspera ? "opc-filaespera-ativo" : ""}`} onClick={() => { setFiladeEspera(true); }}>Fila de espera</button>
|
||||
<div className="container-btns-agenda-fila_esepera">
|
||||
<div className="tabs-agenda-fila">
|
||||
<button
|
||||
className={`btn-agenda ${
|
||||
!FiladeEspera ? "opc-agenda-ativo" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setFiladeEspera(false);
|
||||
}}
|
||||
>
|
||||
Agenda
|
||||
</button>
|
||||
<button
|
||||
className={`btn-fila-espera ${
|
||||
FiladeEspera ? "opc-filaespera-ativo" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setFiladeEspera(true);
|
||||
}}
|
||||
>
|
||||
Fila de espera
|
||||
</button>
|
||||
</div>
|
||||
<section className='calendario-ou-filaespera'>
|
||||
<div
|
||||
className="btns-gerenciamento-e-consulta"
|
||||
style={{ display: "flex", gap: "10px", marginBottom: "20px" }}
|
||||
>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={() => {
|
||||
setAgendamentoParaEdicao(null);
|
||||
setPageConsulta(true);
|
||||
}}
|
||||
>
|
||||
<i className="bi bi-plus-circle"></i> Adicionar Consulta
|
||||
</button>
|
||||
<button
|
||||
className="manage-button btn"
|
||||
onClick={() => navigate("/secretaria/excecoes-disponibilidade")}
|
||||
>
|
||||
<i className="bi bi-gear-fill me-1"></i> Gerenciar Exceções
|
||||
</button>
|
||||
<button
|
||||
className="manage-button btn"
|
||||
onClick={() => navigate("/secretaria/disponibilidade")}
|
||||
>
|
||||
<i className="bi bi-gear-fill me-1"></i> Mudar Disponibilidade
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section className="calendario-ou-filaespera">
|
||||
{!FiladeEspera ? (
|
||||
<div className="calendar-wrapper">
|
||||
<div className="calendar-info-panel">
|
||||
<div className="info-date-display"><span>{selectedDay.format('MMM')}</span><strong>{selectedDay.format('DD')}</strong></div>
|
||||
<div className="info-details"><h3>{selectedDay.format('dddd')}</h3><p>{selectedDay.format('D [de] MMMM [de] YYYY')}</p></div>
|
||||
<div className="info-date-display">
|
||||
<span>{selectedDay.format("MMM")}</span>
|
||||
<strong>{selectedDay.format("DD")}</strong>
|
||||
</div>
|
||||
<div className="info-details">
|
||||
<h3>{selectedDay.format("dddd")}</h3>
|
||||
<p>{selectedDay.format("D [de] MMMM [de] YYYY")}</p>
|
||||
</div>
|
||||
<div className="appointments-list">
|
||||
<h4>Consultas para {selectedDay.format('DD/MM')}</h4>
|
||||
{showSpinner ? <Spinner /> : (DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')]?.filter(app => MedicoFiltrado.id === "vazio" || app.doctor_id === MedicoFiltrado.id).length > 0) ? (
|
||||
DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')]
|
||||
.filter(app => MedicoFiltrado.id === "vazio" || app.doctor_id === MedicoFiltrado.id)
|
||||
.map(app => (
|
||||
<div key={app.id} className="appointment-item" data-status={app.status}>
|
||||
<div className="item-time">{dayjs(app.scheduled_at).format('HH:mm')}</div>
|
||||
<div className="item-details"><span>{app.paciente_nome}</span><small>Dr(a). {app.medico_nome}</small></div>
|
||||
<h4>Consultas para {selectedDay.format("DD/MM")}</h4>
|
||||
{showSpinner ? (
|
||||
<Spinner />
|
||||
) : DictAgendamentosOrganizados[
|
||||
selectedDay.format("YYYY-MM-DD")
|
||||
]?.filter(
|
||||
(app) =>
|
||||
MedicoFiltrado.id === "vazio" ||
|
||||
app.doctor_id === MedicoFiltrado.id
|
||||
).length > 0 ? (
|
||||
DictAgendamentosOrganizados[
|
||||
selectedDay.format("YYYY-MM-DD")
|
||||
]
|
||||
.filter(
|
||||
(app) =>
|
||||
MedicoFiltrado.id === "vazio" ||
|
||||
app.doctor_id === MedicoFiltrado.id
|
||||
)
|
||||
.map((app) => (
|
||||
<div
|
||||
key={app.id}
|
||||
className="appointment-item"
|
||||
data-status={app.status}
|
||||
>
|
||||
<div className="item-time">
|
||||
{dayjs(app.scheduled_at).format("HH:mm")}
|
||||
</div>
|
||||
<div className="item-details">
|
||||
<span>{app.paciente_nome}</span>
|
||||
<small>Dr(a). {app.medico_nome}</small>
|
||||
</div>
|
||||
<div className="appointment-actions">
|
||||
{app.status === 'cancelled' ? (
|
||||
<button className="btn-action btn-edit" onClick={() => { setSelectedId(app.id); confirmConsulta(app.id); }}>
|
||||
<CheckCircle size={16} title="Reverter Cancelamento" />
|
||||
{app.status === "cancelled" ? (
|
||||
<button
|
||||
className="btn-action btn-edit"
|
||||
onClick={() => {
|
||||
setSelectedId(app.id);
|
||||
confirmConsulta(app.id);
|
||||
}}
|
||||
>
|
||||
<CheckCircle
|
||||
size={16}
|
||||
title="Reverter Cancelamento"
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
@ -515,72 +631,156 @@ const DeleteModal = () => (
|
||||
<Edit size={16} />
|
||||
</button>
|
||||
)}
|
||||
{app.status !== 'cancelled' && (
|
||||
<button className="btn-action btn-delete" onClick={() => { setSelectedId(app.id); setShowDeleteModal(true); }} title="Cancelar Agendamento">
|
||||
{app.status !== "cancelled" && (
|
||||
<button
|
||||
className="btn-action btn-delete"
|
||||
onClick={() => {
|
||||
setSelectedId(app.id);
|
||||
setShowDeleteModal(true);
|
||||
}}
|
||||
title="Cancelar Agendamento"
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (<div className="no-appointments-info"><p>Nenhuma consulta agendada.</p></div>)}
|
||||
) : (
|
||||
<div className="no-appointments-info">
|
||||
<p>Nenhuma consulta agendada.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="calendar-main">
|
||||
<div className="calendar-legend">
|
||||
<div className="legend-item" data-status="completed">Realizado</div>
|
||||
<div className="legend-item" data-status="confirmed">Confirmado</div>
|
||||
<div className="legend-item" data-status="agendado">Agendado</div>
|
||||
<div className="legend-item" data-status="cancelled">Cancelado</div>
|
||||
<div className="legend-item" data-status="completed">
|
||||
Realizado
|
||||
</div>
|
||||
<div className="legend-item" data-status="confirmed">
|
||||
Confirmado
|
||||
</div>
|
||||
<div className="legend-item" data-status="agendado">
|
||||
Agendado
|
||||
</div>
|
||||
<div className="legend-item" data-status="cancelled">
|
||||
Cancelado
|
||||
</div>
|
||||
</div>
|
||||
<div className="calendar-controls">
|
||||
<div className="date-indicator">
|
||||
<h2>{currentDate.format('MMMM [de] YYYY')}</h2>
|
||||
<div className="quick-jump-controls" style={{ display: 'flex', gap: '5px', marginTop: '10px' }}>
|
||||
<h2>{currentDate.format("MMMM [de] YYYY")}</h2>
|
||||
<div
|
||||
className="quick-jump-controls"
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: "5px",
|
||||
marginTop: "10px",
|
||||
}}
|
||||
>
|
||||
<select
|
||||
value={quickJump.month}
|
||||
onChange={(e) => handleQuickJumpChange('month', e.target.value)}
|
||||
onChange={(e) =>
|
||||
handleQuickJumpChange("month", e.target.value)
|
||||
}
|
||||
className="form-select form-select-sm w-auto"
|
||||
>
|
||||
{dayjs.months().map((month, index) => (
|
||||
<option key={index} value={index}>{month.charAt(0).toUpperCase() + month.slice(1)}</option>
|
||||
<option key={index} value={index}>
|
||||
{month.charAt(0).toUpperCase() + month.slice(1)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
value={quickJump.year}
|
||||
onChange={(e) => handleQuickJumpChange('year', e.target.value)}
|
||||
onChange={(e) =>
|
||||
handleQuickJumpChange("year", e.target.value)
|
||||
}
|
||||
className="form-select form-select-sm w-auto"
|
||||
>
|
||||
{Array.from({ length: 11 }, (_, i) => dayjs().year() - 5 + i).map(year => (
|
||||
<option key={year} value={year}>{year}</option>
|
||||
{Array.from(
|
||||
{ length: 11 },
|
||||
(_, i) => dayjs().year() - 5 + i
|
||||
).map((year) => (
|
||||
<option key={year} value={year}>
|
||||
{year}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<button
|
||||
className="btn btn-sm btn-outline-primary"
|
||||
onClick={applyQuickJump}
|
||||
disabled={quickJump.month === currentDate.month() && quickJump.year === currentDate.year()}
|
||||
disabled={
|
||||
quickJump.month === currentDate.month() &&
|
||||
quickJump.year === currentDate.year()
|
||||
}
|
||||
>
|
||||
Ir
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="nav-buttons">
|
||||
<button onClick={() => { setCurrentDate(currentDate.subtract(1, 'month')); setSelectedDay(currentDate.subtract(1, 'month')); }}><ChevronLeft size={20} /></button>
|
||||
<button onClick={() => { setCurrentDate(dayjs()); setSelectedDay(dayjs()); }}>Hoje</button>
|
||||
<button onClick={() => { setCurrentDate(currentDate.add(1, 'month')); setSelectedDay(currentDate.add(1, 'month')); }}><ChevronRight size={20} /></button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setCurrentDate(currentDate.subtract(1, "month"));
|
||||
setSelectedDay(currentDate.subtract(1, "month"));
|
||||
}}
|
||||
>
|
||||
<ChevronLeft size={20} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setCurrentDate(dayjs());
|
||||
setSelectedDay(dayjs());
|
||||
}}
|
||||
>
|
||||
Hoje
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setCurrentDate(currentDate.add(1, "month"));
|
||||
setSelectedDay(currentDate.add(1, "month"));
|
||||
}}
|
||||
>
|
||||
<ChevronRight size={20} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="calendar-grid">
|
||||
{weekDays.map(day => <div key={day} className="day-header">{day}</div>)}
|
||||
{weekDays.map((day) => (
|
||||
<div key={day} className="day-header">
|
||||
{day}
|
||||
</div>
|
||||
))}
|
||||
{dateGrid.map((day, index) => {
|
||||
const dayString = day.format('YYYY-MM-DD');
|
||||
const appointmentsOnDay = DictAgendamentosOrganizados[dayString] || [];
|
||||
const filteredAppointments = appointmentsOnDay.filter(app => MedicoFiltrado.id === "vazio" || app.doctor_id === MedicoFiltrado.id);
|
||||
const cellClasses = `day-cell ${day.isSame(currentDate, 'month') ? 'current-month' : 'other-month'} ${day.isSame(dayjs(), 'day') ? 'today' : ''} ${day.isSame(selectedDay, 'day') ? 'selected' : ''}`;
|
||||
const dayString = day.format("YYYY-MM-DD");
|
||||
const appointmentsOnDay =
|
||||
DictAgendamentosOrganizados[dayString] || [];
|
||||
const filteredAppointments = appointmentsOnDay.filter(
|
||||
(app) =>
|
||||
MedicoFiltrado.id === "vazio" ||
|
||||
app.doctor_id === MedicoFiltrado.id
|
||||
);
|
||||
const cellClasses = `day-cell ${
|
||||
day.isSame(currentDate, "month")
|
||||
? "current-month"
|
||||
: "other-month"
|
||||
} ${day.isSame(dayjs(), "day") ? "today" : ""} ${
|
||||
day.isSame(selectedDay, "day") ? "selected" : ""
|
||||
}`;
|
||||
return (
|
||||
<div key={index} className={cellClasses} onClick={() => handleDateClick(day)}>
|
||||
<span>{day.format('D')}</span>
|
||||
{filteredAppointments.length > 0 && <div className="appointments-indicator">{filteredAppointments.length}</div>}
|
||||
<div
|
||||
key={index}
|
||||
className={cellClasses}
|
||||
onClick={() => handleDateClick(day)}
|
||||
>
|
||||
<span>{day.format("D")}</span>
|
||||
{filteredAppointments.length > 0 && (
|
||||
<div className="appointments-indicator">
|
||||
{filteredAppointments.length}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@ -592,37 +792,78 @@ const DeleteModal = () => (
|
||||
<section className="row">
|
||||
<div className="col-12">
|
||||
<div className="card table-paciente-card">
|
||||
<div className="card-header"><h4 className="card-title mb-0">Fila de Espera</h4></div>
|
||||
<div className="card-header">
|
||||
<h4 className="card-title mb-0">Fila de Espera</h4>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<div className="card p-3 mb-3 table-paciente-filters">
|
||||
<h5 className="mb-3"><i className="bi bi-funnel-fill me-2 text-primary"></i> Filtros</h5>
|
||||
<div className="mb-3"><input type="text" className="form-control" placeholder="Buscar por paciente, CPF ou médico..." value={waitlistSearch} onChange={(e) => setWaitlistSearch(e.target.value)} /><small className="text-muted">Digite o nome do paciente, CPF ou nome do médico</small></div>
|
||||
<h5 className="mb-3">
|
||||
<i className="bi bi-funnel-fill me-2 text-primary"></i>{" "}
|
||||
Filtros
|
||||
</h5>
|
||||
<div className="mb-3">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Buscar por paciente, CPF ou médico..."
|
||||
value={waitlistSearch}
|
||||
onChange={(e) =>
|
||||
setWaitlistSearch(e.target.value)
|
||||
}
|
||||
/>
|
||||
<small className="text-muted">
|
||||
Digite o nome do paciente, CPF ou nome do médico
|
||||
</small>
|
||||
</div>
|
||||
<div className="d-flex flex-wrap align-items-center gap-2 mb-3">
|
||||
<div className="d-flex align-items-center gap-2">
|
||||
<span className="me-2 text-muted small">Ordenar por:</span>
|
||||
<span className="me-2 text-muted small">
|
||||
Ordenar por:
|
||||
</span>
|
||||
<select
|
||||
className="form-select compact-select sort-select w-auto"
|
||||
value={waitSortKey ? `${waitSortKey}-${waitSortDir}` : ''}
|
||||
value={
|
||||
waitSortKey
|
||||
? `${waitSortKey}-${waitSortDir}`
|
||||
: ""
|
||||
}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value;
|
||||
if (!v) { setWaitSortKey(null); setWaitSortDir('asc'); return; }
|
||||
const [k, d] = v.split('-');
|
||||
if (!v) {
|
||||
setWaitSortKey(null);
|
||||
setWaitSortDir("asc");
|
||||
return;
|
||||
}
|
||||
const [k, d] = v.split("-");
|
||||
setWaitSortKey(k);
|
||||
setWaitSortDir(d);
|
||||
}}
|
||||
>
|
||||
<option value="">Sem ordenação</option>
|
||||
<option value="paciente-asc">Paciente (A-Z)</option>
|
||||
<option value="paciente-desc">Paciente (Z-A)</option>
|
||||
<option value="paciente-asc">
|
||||
Paciente (A-Z)
|
||||
</option>
|
||||
<option value="paciente-desc">
|
||||
Paciente (Z-A)
|
||||
</option>
|
||||
<option value="medico-asc">Médico (A-Z)</option>
|
||||
<option value="medico-desc">Médico (Z-A)</option>
|
||||
<option value="data-asc">Data (mais antiga)</option>
|
||||
<option value="data-desc">Data (mais recente)</option>
|
||||
<option value="medico-desc">
|
||||
Médico (Z-A)
|
||||
</option>
|
||||
<option value="data-asc">
|
||||
Data (mais antiga)
|
||||
</option>
|
||||
<option value="data-desc">
|
||||
Data (mais recente)
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3">
|
||||
<div className="contador-pacientes">{filaEsperaFiltrada.length} DE {filaEsperaData.length} SOLICITAÇÕES ENCONTRADAS</div>
|
||||
<div className="contador-pacientes">
|
||||
{filaEsperaFiltrada.length} DE{" "}
|
||||
{filaEsperaData.length} SOLICITAÇÕES ENCONTRADAS
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="table-responsive">
|
||||
@ -643,10 +884,21 @@ const DeleteModal = () => (
|
||||
<td>{item?.Infos?.paciente_nome}</td>
|
||||
<td>{item?.Infos?.paciente_cpf}</td>
|
||||
<td>{item?.Infos?.medico_nome}</td>
|
||||
<td>{dayjs(item.agendamento.scheduled_at).format('DD/MM/YYYY')}</td>
|
||||
<td>
|
||||
<button className="btn btn-sm btn-delete" onClick={() => { setSelectedId(item.agendamento.id); setShowDeleteModal(true); }}>
|
||||
<i className="bi bi-trash me-1"></i> Excluir
|
||||
{dayjs(
|
||||
item.agendamento.scheduled_at
|
||||
).format("DD/MM/YYYY")}
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
className="btn btn-sm btn-delete"
|
||||
onClick={() => {
|
||||
setSelectedId(item.agendamento.id);
|
||||
setShowDeleteModal(true);
|
||||
}}
|
||||
>
|
||||
<i className="bi bi-trash me-1"></i>{" "}
|
||||
Excluir
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@ -655,7 +907,16 @@ const DeleteModal = () => (
|
||||
<tr>
|
||||
<td colSpan="5" className="text-center py-4">
|
||||
<div className="text-muted">
|
||||
{showSpinner ? <Spinner /> : (<><i className="bi bi-inbox display-4"></i><p className="mt-2">Nenhuma solicitação encontrada.</p></>)}
|
||||
{showSpinner ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<>
|
||||
<i className="bi bi-inbox display-4"></i>
|
||||
<p className="mt-2">
|
||||
Nenhuma solicitação encontrada.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -665,11 +926,16 @@ const DeleteModal = () => (
|
||||
{filaEsperaFiltrada.length > 0 && (
|
||||
<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>
|
||||
<span className="me-2 text-muted">
|
||||
Itens por página:
|
||||
</span>
|
||||
<select
|
||||
className="form-select form-select-sm w-auto"
|
||||
value={waitPerPage}
|
||||
onChange={(e) => { setWaitPerPage(Number(e.target.value)); setWaitPage(1); }}
|
||||
onChange={(e) => {
|
||||
setWaitPerPage(Number(e.target.value));
|
||||
setWaitPage(1);
|
||||
}}
|
||||
>
|
||||
<option value={5}>5</option>
|
||||
<option value={10}>10</option>
|
||||
@ -678,21 +944,61 @@ const DeleteModal = () => (
|
||||
</select>
|
||||
</div>
|
||||
<div className="d-flex align-items-center">
|
||||
<span className="me-3 text-muted">Página {waitPage} de {waitTotalPages} • Mostrando {waitIndiceInicial + 1}-{Math.min(waitIndiceFinal, filaEsperaFiltrada.length)} de {filaEsperaFiltrada.length}</span>
|
||||
<span className="me-3 text-muted">
|
||||
Página {waitPage} de {waitTotalPages} •
|
||||
Mostrando {waitIndiceInicial + 1}-
|
||||
{Math.min(
|
||||
waitIndiceFinal,
|
||||
filaEsperaFiltrada.length
|
||||
)}{" "}
|
||||
de {filaEsperaFiltrada.length}
|
||||
</span>
|
||||
<nav>
|
||||
<ul className="pagination pagination-sm mb-0">
|
||||
<li className={`page-item ${waitPage === 1 ? 'disabled' : ''}`}>
|
||||
<button className="page-link" onClick={() => setWaitPage(p => Math.max(1, p - 1))}>
|
||||
<li
|
||||
className={`page-item ${
|
||||
waitPage === 1 ? "disabled" : ""
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
className="page-link"
|
||||
onClick={() =>
|
||||
setWaitPage((p) => Math.max(1, p - 1))
|
||||
}
|
||||
>
|
||||
<i className="bi bi-chevron-left"></i>
|
||||
</button>
|
||||
</li>
|
||||
{gerarNumerosWaitPages().map(pagina => (
|
||||
<li key={pagina} className={`page-item ${pagina === waitPage ? 'active' : ''}`}>
|
||||
<button className="page-link" onClick={() => setWaitPage(pagina)}>{pagina}</button>
|
||||
{gerarNumerosWaitPages().map((pagina) => (
|
||||
<li
|
||||
key={pagina}
|
||||
className={`page-item ${
|
||||
pagina === waitPage ? "active" : ""
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
className="page-link"
|
||||
onClick={() => setWaitPage(pagina)}
|
||||
>
|
||||
{pagina}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
<li className={`page-item ${waitPage === waitTotalPages ? 'disabled' : ''}`}>
|
||||
<button className="page-link" onClick={() => setWaitPage(p => Math.min(waitTotalPages, p + 1))}>
|
||||
<li
|
||||
className={`page-item ${
|
||||
waitPage === waitTotalPages
|
||||
? "disabled"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
className="page-link"
|
||||
onClick={() =>
|
||||
setWaitPage((p) =>
|
||||
Math.min(waitTotalPages, p + 1)
|
||||
)
|
||||
}
|
||||
>
|
||||
<i className="bi bi-chevron-right"></i>
|
||||
</button>
|
||||
</li>
|
||||
@ -723,6 +1029,6 @@ const DeleteModal = () => (
|
||||
{showDeleteModal && <DeleteModal />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Agendamento;
|
||||
|
||||
@ -140,6 +140,7 @@
|
||||
border-bottom: 3px solid transparent;
|
||||
padding: 8px;
|
||||
border-radius: 10px 10px 0px 0px;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, useMemo, useCallback } from "react";
|
||||
import './style/FinanceiroDashboard.css';
|
||||
import "./style/FinanceiroDashboard.css";
|
||||
|
||||
const CONVENIOS_LIST = [
|
||||
"Particular",
|
||||
@ -8,7 +8,7 @@ const CONVENIOS_LIST = [
|
||||
"SulAmérica",
|
||||
"Unimed",
|
||||
"Cassio",
|
||||
"Outro"
|
||||
"Outro",
|
||||
];
|
||||
|
||||
function CurrencyInput({ value, onChange, label, id }) {
|
||||
@ -17,22 +17,25 @@ function CurrencyInput({ value, onChange, label, id }) {
|
||||
|
||||
let stringValue = String(numericValue);
|
||||
while (stringValue.length < 3) {
|
||||
stringValue = '0' + stringValue;
|
||||
stringValue = "0" + stringValue;
|
||||
}
|
||||
|
||||
const integerPart = stringValue.slice(0, -2);
|
||||
const decimalPart = stringValue.slice(-2);
|
||||
|
||||
const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ".");
|
||||
|
||||
return `R$ ${formattedInteger},${decimalPart}`;
|
||||
}, [value]);
|
||||
|
||||
const handleKeyDown = useCallback((e) => {
|
||||
const handleKeyDown = useCallback(
|
||||
(e) => {
|
||||
const key = e.key;
|
||||
|
||||
if (['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab'].includes(key)) {
|
||||
if (key === 'Backspace' || key === 'Delete') {
|
||||
if (
|
||||
["Backspace", "Delete", "ArrowLeft", "ArrowRight", "Tab"].includes(key)
|
||||
) {
|
||||
if (key === "Backspace" || key === "Delete") {
|
||||
e.preventDefault();
|
||||
const numericValue = value || 0;
|
||||
let newValueString = String(numericValue);
|
||||
@ -63,7 +66,9 @@ function CurrencyInput({ value, onChange, label, id }) {
|
||||
const newNumericValue = parseInt(newValueString);
|
||||
|
||||
onChange(newNumericValue);
|
||||
}, [value, onChange]);
|
||||
},
|
||||
[value, onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="form-group">
|
||||
@ -91,7 +96,7 @@ function mockFetchPagamentos() {
|
||||
data_vencimento: "2025-09-30",
|
||||
status: "pendente",
|
||||
desconto: 0,
|
||||
observacoes: "Pagamento parcelado em 2x"
|
||||
observacoes: "Pagamento parcelado em 2x",
|
||||
},
|
||||
{
|
||||
id: "PAY-002",
|
||||
@ -101,7 +106,7 @@ function mockFetchPagamentos() {
|
||||
data_vencimento: "2025-09-15",
|
||||
status: "pago",
|
||||
desconto: 1000,
|
||||
observacoes: ""
|
||||
observacoes: "",
|
||||
},
|
||||
{
|
||||
id: "PAY-003",
|
||||
@ -111,7 +116,7 @@ function mockFetchPagamentos() {
|
||||
data_vencimento: "2025-09-20",
|
||||
status: "vencido",
|
||||
desconto: 0,
|
||||
observacoes: "Não respondeu ao contato"
|
||||
observacoes: "Não respondeu ao contato",
|
||||
},
|
||||
{
|
||||
id: "PAY-004",
|
||||
@ -121,8 +126,8 @@ function mockFetchPagamentos() {
|
||||
data_vencimento: "2025-09-29",
|
||||
status: "pago",
|
||||
desconto: 500,
|
||||
observacoes: "Desconto por pagamento adiantado"
|
||||
}
|
||||
observacoes: "Desconto por pagamento adiantado",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@ -132,7 +137,11 @@ export default function FinanceiroDashboard() {
|
||||
const [query, setQuery] = useState("");
|
||||
const [filtroStatus, setFiltroStatus] = useState("Todos");
|
||||
const [novoPagamento, setNovoPagamento] = useState(false);
|
||||
const [summary, setSummary] = useState({ totalRecebido: 0, totalAReceber: 0, totalDescontos: 0 });
|
||||
const [summary, setSummary] = useState({
|
||||
totalRecebido: 0,
|
||||
totalAReceber: 0,
|
||||
totalDescontos: 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const data = mockFetchPagamentos();
|
||||
@ -141,7 +150,13 @@ export default function FinanceiroDashboard() {
|
||||
|
||||
function formatCurrency(centavos) {
|
||||
const valorEmReais = centavos / 100;
|
||||
return "R$ " + valorEmReais.toFixed(2).replace(".", ",").replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
return (
|
||||
"R$ " +
|
||||
valorEmReais
|
||||
.toFixed(2)
|
||||
.replace(".", ",")
|
||||
.replace(/\B(?=(\d{3})+(?!\d))/g, ".")
|
||||
);
|
||||
}
|
||||
|
||||
function getValorLiquido(valor, desconto) {
|
||||
@ -149,10 +164,11 @@ export default function FinanceiroDashboard() {
|
||||
}
|
||||
|
||||
const filteredPagamentos = useMemo(() => {
|
||||
return pagamentos.filter(p => {
|
||||
return pagamentos.filter((p) => {
|
||||
const q = query.toLowerCase();
|
||||
const statusOk = filtroStatus === "Todos" || p.status === filtroStatus;
|
||||
const buscaOk = p.paciente.nome.toLowerCase().includes(q) ||
|
||||
const buscaOk =
|
||||
p.paciente.nome.toLowerCase().includes(q) ||
|
||||
p.id.toLowerCase().includes(q);
|
||||
return statusOk && buscaOk;
|
||||
});
|
||||
@ -163,9 +179,9 @@ export default function FinanceiroDashboard() {
|
||||
let aReceber = 0;
|
||||
let descontos = 0;
|
||||
|
||||
filteredPagamentos.forEach(p => {
|
||||
filteredPagamentos.forEach((p) => {
|
||||
const valorLiquido = getValorLiquido(p.valor, p.desconto);
|
||||
if (p.status === 'pago') {
|
||||
if (p.status === "pago") {
|
||||
recebido += valorLiquido;
|
||||
descontos += p.desconto;
|
||||
} else {
|
||||
@ -176,29 +192,37 @@ export default function FinanceiroDashboard() {
|
||||
setSummary({
|
||||
totalRecebido: recebido,
|
||||
totalAReceber: aReceber,
|
||||
totalDescontos: descontos
|
||||
totalDescontos: descontos,
|
||||
});
|
||||
}, [filteredPagamentos]);
|
||||
|
||||
function handleDelete(id) {
|
||||
if (window.confirm("Tem certeza que deseja excluir este pagamento?")) {
|
||||
setPagamentos(prev => prev.filter(p => p.id !== id));
|
||||
setPagamentos((prev) => prev.filter((p) => p.id !== id));
|
||||
setModalPagamento(null);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSave(pagamento) {
|
||||
if (!pagamento.paciente.nome || !pagamento.valor || !pagamento.data_vencimento || !pagamento.paciente.convenio) {
|
||||
if (
|
||||
!pagamento.paciente.nome ||
|
||||
!pagamento.valor ||
|
||||
!pagamento.data_vencimento ||
|
||||
!pagamento.paciente.convenio
|
||||
) {
|
||||
alert("Preencha Paciente, Convênio, Valor e Data de Vencimento.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (novoPagamento) {
|
||||
const newId = "PAY-" + (pagamentos.length + 1).toString().padStart(3, "0");
|
||||
const newId =
|
||||
"PAY-" + (pagamentos.length + 1).toString().padStart(3, "0");
|
||||
pagamento.id = newId;
|
||||
setPagamentos(prev => [...prev, pagamento]);
|
||||
setPagamentos((prev) => [...prev, pagamento]);
|
||||
} else {
|
||||
setPagamentos(prev => prev.map(p => p.id === pagamento.id ? pagamento : p));
|
||||
setPagamentos((prev) =>
|
||||
prev.map((p) => (p.id === pagamento.id ? pagamento : p))
|
||||
);
|
||||
}
|
||||
setModalPagamento(null);
|
||||
setNovoPagamento(false);
|
||||
@ -234,10 +258,14 @@ export default function FinanceiroDashboard() {
|
||||
className="input-field"
|
||||
placeholder="Buscar paciente"
|
||||
value={query}
|
||||
onChange={e => setQuery(e.target.value)}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
style={{ flexGrow: 1 }}
|
||||
/>
|
||||
<select className="select-field" value={filtroStatus} onChange={e => setFiltroStatus(e.target.value)}>
|
||||
<select
|
||||
className="select-field"
|
||||
value={filtroStatus}
|
||||
onChange={(e) => setFiltroStatus(e.target.value)}
|
||||
>
|
||||
<option value="Todos">Todos</option>
|
||||
<option value="pago">Pago</option>
|
||||
<option value="pendente">Pendente</option>
|
||||
@ -250,10 +278,10 @@ export default function FinanceiroDashboard() {
|
||||
paciente: { nome: "", convenio: CONVENIOS_LIST[0] },
|
||||
valor: 0,
|
||||
forma_pagamento: "Dinheiro",
|
||||
data_vencimento: new Date().toISOString().split('T')[0],
|
||||
data_vencimento: new Date().toISOString().split("T")[0],
|
||||
status: "pendente",
|
||||
desconto: 0,
|
||||
observacoes:""
|
||||
observacoes: "",
|
||||
});
|
||||
setNovoPagamento(true);
|
||||
}}
|
||||
@ -281,21 +309,30 @@ export default function FinanceiroDashboard() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filteredPagamentos.map(p => (
|
||||
{filteredPagamentos.map((p) => (
|
||||
<tr key={p.id}>
|
||||
<td style={{ fontWeight: 600 }}>{p.paciente.nome}</td>
|
||||
<td>{p.paciente.convenio}</td>
|
||||
<td>{formatCurrency(p.valor)}</td>
|
||||
<td>{formatCurrency(p.desconto)}</td>
|
||||
<td style={{ fontWeight: 600 }}>{formatCurrency(getValorLiquido(p.valor, p.desconto))}</td>
|
||||
<td style={{ fontWeight: 600 }}>
|
||||
{formatCurrency(getValorLiquido(p.valor, p.desconto))}
|
||||
</td>
|
||||
<td>{p.forma_pagamento}</td>
|
||||
<td>{p.data_vencimento.split('-').reverse().join('/')}</td>
|
||||
<td><span className={`badge ${p.status}`}>{p.status.toUpperCase()}</span></td>
|
||||
<td>{p.data_vencimento.split("-").reverse().join("/")}</td>
|
||||
<td>
|
||||
<span className={`badge ${p.status}`}>
|
||||
{p.status.toUpperCase()}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div className="action-group">
|
||||
<button
|
||||
className="btn-view"
|
||||
onClick={() => { setModalPagamento({...p}); setNovoPagamento(false); }}
|
||||
onClick={() => {
|
||||
setModalPagamento({ ...p });
|
||||
setNovoPagamento(false);
|
||||
}}
|
||||
>
|
||||
<i className="bi bi-eye me-1"></i> Ver / Editar
|
||||
</button>
|
||||
@ -316,11 +353,20 @@ export default function FinanceiroDashboard() {
|
||||
</div>
|
||||
|
||||
{modalPagamento && (
|
||||
<div className="modal" onClick={(e) => e.target.classList.contains('modal') && closeModal()}>
|
||||
<div
|
||||
className="modal"
|
||||
onClick={(e) => e.target.classList.contains("modal") && closeModal()}
|
||||
>
|
||||
<div className="modal-card">
|
||||
<div className="modal-header">
|
||||
<h2>{novoPagamento ? "Adicionar Pagamento" : `Editar Pagamento - ${modalPagamento.paciente.nome}`}</h2>
|
||||
<button className="close-btn" onClick={closeModal}>×</button>
|
||||
<h2>
|
||||
{novoPagamento
|
||||
? "Adicionar Pagamento"
|
||||
: `Editar Pagamento - ${modalPagamento.paciente.nome}`}
|
||||
</h2>
|
||||
<button className="close-btn" onClick={closeModal}>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="modal-body">
|
||||
@ -330,7 +376,15 @@ export default function FinanceiroDashboard() {
|
||||
id="paciente_nome"
|
||||
className="input-field"
|
||||
value={modalPagamento.paciente.nome}
|
||||
onChange={e => setModalPagamento({...modalPagamento, paciente:{...modalPagamento.paciente, nome:e.target.value}})}
|
||||
onChange={(e) =>
|
||||
setModalPagamento({
|
||||
...modalPagamento,
|
||||
paciente: {
|
||||
...modalPagamento.paciente,
|
||||
nome: e.target.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -340,11 +394,21 @@ export default function FinanceiroDashboard() {
|
||||
id="convenio"
|
||||
className="select-field"
|
||||
value={modalPagamento.paciente.convenio}
|
||||
onChange={e => setModalPagamento({...modalPagamento, paciente:{...modalPagamento.paciente, convenio:e.target.value}})}
|
||||
onChange={(e) =>
|
||||
setModalPagamento({
|
||||
...modalPagamento,
|
||||
paciente: {
|
||||
...modalPagamento.paciente,
|
||||
convenio: e.target.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
>
|
||||
<option value="">Selecione</option>
|
||||
{CONVENIOS_LIST.map(conv => (
|
||||
<option key={conv} value={conv}>{conv}</option>
|
||||
{CONVENIOS_LIST.map((conv) => (
|
||||
<option key={conv} value={conv}>
|
||||
{conv}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
@ -352,14 +416,18 @@ export default function FinanceiroDashboard() {
|
||||
id="valor"
|
||||
label="Valor da consulta (R$)"
|
||||
value={modalPagamento.valor}
|
||||
onChange={newValue => setModalPagamento({...modalPagamento, valor: newValue})}
|
||||
onChange={(newValue) =>
|
||||
setModalPagamento({ ...modalPagamento, valor: newValue })
|
||||
}
|
||||
/>
|
||||
|
||||
<CurrencyInput
|
||||
id="desconto"
|
||||
label="Desconto aplicado (R$)"
|
||||
value={modalPagamento.desconto}
|
||||
onChange={newValue => setModalPagamento({...modalPagamento, desconto: newValue})}
|
||||
onChange={(newValue) =>
|
||||
setModalPagamento({ ...modalPagamento, desconto: newValue })
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="form-group">
|
||||
@ -368,7 +436,12 @@ export default function FinanceiroDashboard() {
|
||||
id="forma"
|
||||
className="select-field"
|
||||
value={modalPagamento.forma_pagamento}
|
||||
onChange={e => setModalPagamento({...modalPagamento, forma_pagamento:e.target.value})}
|
||||
onChange={(e) =>
|
||||
setModalPagamento({
|
||||
...modalPagamento,
|
||||
forma_pagamento: e.target.value,
|
||||
})
|
||||
}
|
||||
>
|
||||
<option>Dinheiro</option>
|
||||
<option>Cartão</option>
|
||||
@ -384,7 +457,12 @@ export default function FinanceiroDashboard() {
|
||||
className="input-field"
|
||||
type="date"
|
||||
value={modalPagamento.data_vencimento}
|
||||
onChange={e => setModalPagamento({...modalPagamento, data_vencimento:e.target.value})}
|
||||
onChange={(e) =>
|
||||
setModalPagamento({
|
||||
...modalPagamento,
|
||||
data_vencimento: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -394,7 +472,12 @@ export default function FinanceiroDashboard() {
|
||||
id="status"
|
||||
className="select-field"
|
||||
value={modalPagamento.status}
|
||||
onChange={e => setModalPagamento({...modalPagamento, status:e.target.value})}
|
||||
onChange={(e) =>
|
||||
setModalPagamento({
|
||||
...modalPagamento,
|
||||
status: e.target.value,
|
||||
})
|
||||
}
|
||||
>
|
||||
<option value="pago">Pago</option>
|
||||
<option value="pendente">Pendente</option>
|
||||
@ -409,24 +492,28 @@ export default function FinanceiroDashboard() {
|
||||
className="input-field"
|
||||
rows={3}
|
||||
value={modalPagamento.observacoes}
|
||||
onChange={e => setModalPagamento({...modalPagamento, observacoes:e.target.value})}
|
||||
onChange={(e) =>
|
||||
setModalPagamento({
|
||||
...modalPagamento,
|
||||
observacoes: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="modal-footer">
|
||||
<button className="btn-view" onClick={() => handleSave(modalPagamento)}>
|
||||
<button
|
||||
className="btn-view"
|
||||
onClick={() => handleSave(modalPagamento)}
|
||||
>
|
||||
<i className="bi bi-check-circle me-1"></i> Salvar
|
||||
</button>
|
||||
<button
|
||||
className="btn-delete"
|
||||
onClick={closeModal}
|
||||
>
|
||||
<button className="btn-delete" onClick={closeModal}>
|
||||
<i className="bi bi-x-circle me-1"></i> Cancelar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -186,9 +186,9 @@ html[data-bs-theme="dark"] .btn-delete {
|
||||
/* Badges de status */
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
padding: 8px 18px !important;
|
||||
border-radius: 9999px;
|
||||
font-size: 12px;
|
||||
font-size: 14px !important;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@ -228,7 +228,6 @@ html[data-bs-theme="dark"] .btn-delete {
|
||||
width: 100%;
|
||||
max-width: 550px;
|
||||
max-height: 85vh;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
@ -243,7 +242,7 @@ html[data-bs-theme="dark"] .btn-delete {
|
||||
.modal-header h2 {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1f2937;
|
||||
color: #fff;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
/* Estatísticas - Paciente */
|
||||
.stats-paciente-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
@ -81,10 +81,10 @@
|
||||
}
|
||||
|
||||
/* Cores dos ícones - Paciente */
|
||||
.stat-paciente-icon-wrapper.blue { background-color: #5d5dff; }
|
||||
.stat-paciente-icon-wrapper.green { background-color: #30d158; }
|
||||
.stat-paciente-icon-wrapper.blue { background-color: #051AFF; }
|
||||
.stat-paciente-icon-wrapper.green { background-color: #5F5DF2; }
|
||||
.stat-paciente-icon-wrapper.purple { background-color: #a272ff; }
|
||||
.stat-paciente-icon-wrapper.orange { background-color: #f1952e; }
|
||||
.stat-paciente-icon-wrapper.orange { background-color: #0065FF; }
|
||||
|
||||
/* Ações Rápidas - Paciente */
|
||||
.quick-actions-paciente h2 {
|
||||
@ -432,14 +432,6 @@ html[data-bs-theme="dark"] .view-all-paciente-button:hover {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.stats-paciente-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.actions-paciente-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.consulta-paciente-info {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user