From ce3f8e23dd9fbfb118ceecc2bc5b9bda95e8b2b9 Mon Sep 17 00:00:00 2001 From: joao_pedro Date: Fri, 31 Oct 2025 20:12:42 -0300 Subject: [PATCH 01/19] =?UTF-8?q?Toggle=20finalizado=20sem=20estiliza?= =?UTF-8?q?=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Sidebar.jsx | 95 ++++++++++--------- .../perfil_secretaria/PerfilSecretaria.jsx | 2 +- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index e05dbeb..d920d0c 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -2,6 +2,15 @@ import React, { useState, useEffect } from "react"; import { Link, useNavigate } from "react-router-dom"; import TrocardePerfis from "./TrocardePerfis"; import MobileMenuToggle from "./MobileMenuToggle"; +import ToggleSidebar from "./ToggleSidebar"; +import { useAuth } from "./utils/AuthProvider"; + +import PacienteItems from "../data/sidebar-items-paciente.json" +import DoctorItems from "../data/sidebar-items-medico.json" +import admItems from "../data/sidebar-items-adm.json" +import SecretariaItems from "../data/sidebar-items-secretaria.json" + +import { UserInfos } from "./utils/Functions-Endpoints/General"; function Sidebar({ menuItems }) { const [isActive, setIsActive] = useState(true); @@ -10,6 +19,16 @@ function Sidebar({ menuItems }) { const [showLogoutModal, setShowLogoutModal] = useState(false); const navigate = useNavigate(); + const [roleUser, setRoleUser] = useState([]) + + const {getAuthorizationHeader} = useAuth(); + + const authHeader = getAuthorizationHeader(); + + + + + // Detecta se é mobile/tablet useEffect(() => { const checkScreenSize = () => { @@ -18,6 +37,15 @@ function Sidebar({ menuItems }) { setIsActive(!mobile); }; + const fetchInfoUser = async () => { + const InfoUser = await UserInfos(authHeader); + console.log(InfoUser.roles, "dados") + + setRoleUser(InfoUser.roles) + } + + fetchInfoUser() + checkScreenSize(); window.addEventListener("resize", checkScreenSize); return () => window.removeEventListener("resize", checkScreenSize); @@ -89,8 +117,16 @@ function Sidebar({ menuItems }) { } }; + useEffect(() => { + if(roleUser.includes("admin")){ + console.log("tem") + } + console.log(roleUser) + }, [roleUser]) + const handleLogoutCancel = () => setShowLogoutModal(false); + const renderLink = (item) => { if (item.url && item.url.startsWith("/")) { return ( @@ -213,52 +249,25 @@ function Sidebar({ menuItems }) {
+ + + + } + + ) +} + +export default ToggleSidebar \ No newline at end of file From d8e63f8abee1021249d1874775578419ea81c3d5 Mon Sep 17 00:00:00 2001 From: Caio Miguel Lima Nunes Date: Tue, 4 Nov 2025 15:39:02 -0300 Subject: [PATCH 03/19] =?UTF-8?q?Atualiza=C3=A7=C3=B5es=20de=20estilos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 14 - .../AgendarConsulta/FormNovaConsulta.jsx | 1 - .../style/formagendamentos.css | 248 +++++++++++--- src/components/Estilo/Toggle.css | 310 +++++++++++++++++- src/components/Sidebar.jsx | 10 +- src/components/ToggleSidebar.jsx | 191 ++++++----- src/components/doctors/DoctorForm.jsx | 1 - src/data/sidebar-items-adm.json | 4 - src/data/sidebar-items-financeiro.json | 4 - src/data/sidebar-items-medico.json | 4 - src/data/sidebar-items-secretaria.json | 5 - 11 files changed, 624 insertions(+), 168 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1663be2..3b9ac4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29151,18 +29151,6 @@ "minimalistic-assert": "^1.0.0" } }, -<<<<<<< HEAD - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "license": "MIT", - "peer": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", @@ -29172,8 +29160,6 @@ "node": ">= 8" } }, -======= ->>>>>>> c180e9a5c9e6bdaf28914332ba603acaf6d028a5 "node_modules/web-vitals": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", diff --git a/src/components/AgendarConsulta/FormNovaConsulta.jsx b/src/components/AgendarConsulta/FormNovaConsulta.jsx index 2dc2ef5..a3ec8f3 100644 --- a/src/components/AgendarConsulta/FormNovaConsulta.jsx +++ b/src/components/AgendarConsulta/FormNovaConsulta.jsx @@ -177,7 +177,6 @@ const FormNovaConsulta = ({ onCancel, onSave, setAgendamento, agendamento }) =>
Sucesso
-

Agendamento salvo com sucesso!

diff --git a/src/components/AgendarConsulta/style/formagendamentos.css b/src/components/AgendarConsulta/style/formagendamentos.css index 2e90f74..76ac620 100644 --- a/src/components/AgendarConsulta/style/formagendamentos.css +++ b/src/components/AgendarConsulta/style/formagendamentos.css @@ -414,96 +414,248 @@ html[data-bs-theme="dark"] svg { } +/* ========== Modal Overlay ========== */ .modal-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center; - z-index: 1000; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 9999; + animation: fadeIn 0.3s ease-in; } -.modal-content { - background: white; - padding: 2rem; - border-radius: 12px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); - max-width: 400px; - width: 90%; - text-align: center; - animation: modalAppear 0.3s ease-out; -} - -@keyframes modalAppear { +@keyframes fadeIn { from { opacity: 0; - transform: scale(0.9) translateY(-20px); } to { opacity: 1; - transform: scale(1) translateY(0); } } -.modal-header h3 { - margin: 0 0 1rem 0; - color: #28a745; - font-size: 1.5rem; - font-weight: 600; +/* ========== Modal Content ========== */ +.modal-content { + background-color: #fff; + border-radius: 10px; + width: 400px; + max-width: 90%; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); + overflow: hidden; + animation: slideIn 0.3s ease-out; } -.modal-body { - margin-bottom: 1.5rem; +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(-20px) scale(0.95); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } } -.modal-body p { +/* ========== Modal Header ========== */ +.modal-header { + background-color: #1e3a8a; + padding: 15px 20px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal-header.success { + background-color: #1e3a8a !important; +} + +.modal-header.error { + background-color: #dc3545 !important; +} + +.modal-header .modal-title { + color: #fff; margin: 0; - color: #333; - font-size: 1.1rem; - line-height: 1.5; + font-size: 1.2rem; + font-weight: bold; } +.modal-close-btn { + background: none; + border: none; + font-size: 20px; + color: #fff; + cursor: pointer; + padding: 0; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + transition: background-color 0.2s; +} + +.modal-close-btn:hover { + background-color: rgba(255, 255, 255, 0.2); +} + +/* ========== Modal Body ========== */ +.modal-body { + padding: 25px 20px; + background: #fff; +} + +.modal-body .modal-message { + color: #111; + font-size: 1.1rem; + margin: 0; + font-weight: 600; + line-height: 1.4; + text-align: center; +} + +.modal-submessage { + color: #666; + font-size: 0.9rem; + margin: 10px 0 0 0; + line-height: 1.4; + text-align: center; +} + +/* ========== Modal Footer ========== */ .modal-footer { display: flex; - justify-content: center; + justify-content: flex-end; + padding: 15px 20px; + border-top: 1px solid #ddd; + background: #fff; } -.modal-footer .btn-primary { - background: #28a745; - padding: 10px 24px; +.modal-confirm-btn { + background-color: #1e3a8a; + color: #fff; + border: none; + padding: 8px 20px; + border-radius: 6px; + cursor: pointer; font-size: 1rem; - font-weight: 500; + font-weight: bold; + transition: all 0.2s ease; } -.modal-footer .btn-primary:hover { - background: #218838; +.modal-confirm-btn:hover { + background-color: #1e40af; + transform: translateY(-1px); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} + +.modal-confirm-btn.success { + background-color: #1e3a8a !important; +} + +.modal-confirm-btn.success:hover { + background-color: #1e40af !important; +} + +.modal-confirm-btn.error { + background-color: #dc3545 !important; +} + +.modal-confirm-btn.error:hover { + background-color: #c82333 !important; } +/* ========== Dark Mode ========== */ html[data-bs-theme="dark"] .modal-content { background: #232323 !important; - color: #e0e0e0 !important; - border: 1px solid #404053 !important; + border: 1px solid #404053; } -html[data-bs-theme="dark"] .modal-header h3 { - color: #4ade80 !important; +html[data-bs-theme="dark"] .modal-header { + background: #1e3a8a !important; } -html[data-bs-theme="dark"] .modal-body p { +html[data-bs-theme="dark"] .modal-header.success { + background-color: #1e3a8a !important; +} + +html[data-bs-theme="dark"] .modal-header.error { + background-color: #dc3545 !important; +} + +html[data-bs-theme="dark"] .modal-header .modal-title, +html[data-bs-theme="dark"] .modal-close-btn { + color: white !important; +} + +html[data-bs-theme="dark"] .modal-body { + background: #232323 !important; +} + +html[data-bs-theme="dark"] .modal-body .modal-message { color: #e0e0e0 !important; } -html[data-bs-theme="dark"] .modal-footer .btn-primary { - background: #16a34a !important; +html[data-bs-theme="dark"] .modal-submessage { + color: #b0b7c3 !important; } -html[data-bs-theme="dark"] .modal-footer .btn-primary:hover { - background: #15803d !important; +html[data-bs-theme="dark"] .modal-footer { + background: #232323 !important; + border-top: 1px solid #404053; +} + +html[data-bs-theme="dark"] .modal-confirm-btn { + background: #2563eb !important; +} + +html[data-bs-theme="dark"] .modal-confirm-btn:hover { + background: #1e40af !important; +} + +/* ========== Responsive ========== */ +@media (max-width: 768px) { + .modal-content { + width: 95%; + margin: 1rem; + } + + .modal-body { + padding: 20px 15px; + } + + .modal-message { + font-size: 1rem; + } +} + +@media (max-width: 480px) { + .modal-header { + padding: 12px 15px; + } + + .modal-header .modal-title { + font-size: 1.1rem; + } + + .modal-body { + padding: 15px; + } + + .modal-footer { + padding: 12px 15px; + } + + .modal-confirm-btn { + padding: 6px 16px; + font-size: 0.9rem; + } } diff --git a/src/components/Estilo/Toggle.css b/src/components/Estilo/Toggle.css index 53827c6..22dac7b 100644 --- a/src/components/Estilo/Toggle.css +++ b/src/components/Estilo/Toggle.css @@ -1,9 +1,309 @@ -.toggle-title{ - color:#25396f; - font-weight: 1000; - font-size: 20px; +/* ========== Variáveis CSS ========== */ +:root { + --primary-blue: #4a90e2; + --primary-blue-light: #5da3f5; + --primary-blue-lighter: #70b4ff; + --dark-blue: #1a3a5c; + --dark-blue-deep: #0d2640; + --white: #ffffff; + --transition: all 0.3s ease; } -.container-title{ +/* ========== Container Principal ========== */ +.toggle-sidebar-wrapper { + background: linear-gradient(135deg, var(--primary-blue) 0%, var(--primary-blue-light) 100%); + border-radius: 12px; + padding: 8px; + margin-bottom: 20px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); + border: 1px solid var(--primary-blue-lighter); +} + +.container-title { display: flex; + align-items: center; + padding: 14px 20px; + cursor: pointer; + background-color: var(--primary-blue); + border-radius: 8px; + margin-bottom: 12px; + transition: var(--transition); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25); + border: 1px solid var(--primary-blue-light); +} + +.container-title:hover { + background-color: var(--primary-blue-light); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + transform: translateY(-1px); +} + +.container-title:hover .toggle-arrow { + color: var(--dark-blue-deep) !important; + transform: scale(1.1); +} + +.toggle-title { + color: var(--white) !important; + font-weight: 700; + font-size: 18px; + margin: 0; + flex-grow: 1; + user-select: none; +} + +.toggle-arrow { + transition: var(--transition); + color: var(--dark-blue) !important; +} + +/* ========== Menu Lista ========== */ +.sidebar-menu-list { + list-style: none; + padding: 0; + margin: 0; + animation: fadeIn 0.3s ease; +} + +.sidebar-item { + list-style: none; + margin: 6px 0; + border-radius: 6px; + transition: all 0.2s ease; +} + +/* ========== Links da Sidebar ========== */ +.sidebar-link { + display: flex; + align-items: center; + text-decoration: none; + color: var(--white) !important; + padding: 12px 20px; + border-radius: 6px; + transition: all 0.2s ease; + font-size: 16px; + width: 100%; + text-align: left; + border: none; + background: none; + cursor: pointer; + position: relative; + overflow: hidden; + font-weight: 500; +} + +.sidebar-link::before { + content: ''; + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 3px; + background-color: var(--dark-blue); + transform: scaleY(0); + transition: transform 0.2s ease; +} + +.sidebar-link i { + margin-right: 12px; + font-size: 19px; + transition: transform 0.2s ease; + color: var(--dark-blue) !important; +} + +.sidebar-link:hover { + color: var(--white) !important; + background-color: var(--primary-blue-light); + padding-left: 25px; +} + +.sidebar-link:hover::before { + transform: scaleY(1); +} + +.sidebar-link:hover i { + transform: scale(1.1); + color: var(--dark-blue-deep) !important; +} + +.sidebar-link.active { + color: var(--white) !important; + background-color: #1e3a8a; + font-weight: 600; + box-shadow: 0 4px 12px rgba(30, 58, 138, 0.4); + padding-left: 25px; +} + +.sidebar-link.active::before { + transform: scaleY(1); + background-color: var(--primary-blue-lighter); +} + +.sidebar-link.active i { + color: var(--white) !important; +} + +/* ========== Título da Sidebar ========== */ +.sidebar-title { + padding: 20px 20px 8px; + font-size: 14px; + color: var(--white) !important; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 1px; + position: relative; +} + +.sidebar-title::after { + content: ''; + position: absolute; + bottom: 4px; + left: 20px; + right: 20px; + height: 2px; + background: linear-gradient(90deg, var(--white), transparent); +} + +/* ========== Itens com Submenu ========== */ +.sidebar-item.has-sub { + border-radius: 6px 6px 0 0; + overflow: hidden; + background-color: rgba(74, 144, 226, 0.6); +} + +.sidebar-item.has-sub.active { + background-color: var(--primary-blue-light); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} + +.sidebar-item.has-sub .sidebar-link { + background-color: var(--primary-blue); + color: var(--white) !important; +} + +.sidebar-item.has-sub .sidebar-link::after { + content: "\F282"; + font-family: "bootstrap-icons"; + font-weight: 900; + margin-left: auto; + transition: var(--transition); + display: flex; + align-items: center; + font-size: 14px; + color: var(--dark-blue) !important; +} + +.sidebar-item.has-sub.active .sidebar-link::after, +.sidebar-item.has-sub .sidebar-link:hover::after { + color: var(--dark-blue-deep) !important; +} + +.sidebar-item.has-sub.active .sidebar-link::after { + transform: rotate(180deg); +} + +.sidebar-item.has-sub .sidebar-link.submenu-active, +.sidebar-item.has-sub.active .sidebar-link.submenu-active { + color: var(--white) !important; +} + +.sidebar-item.has-sub .sidebar-link.submenu-active i { + color: var(--dark-blue) !important; +} + +.sidebar-item.has-sub.active .sidebar-link.submenu-active { + background-color: #1e3a8a; +} + +.sidebar-item.has-sub.active .sidebar-link.submenu-active i { + color: var(--white) !important; +} + +/* ========== Submenu ========== */ +.submenu { + list-style: none; + padding-left: 0; + margin: 0; + max-height: 0; + overflow: hidden; + transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1); + background-color: rgba(74, 144, 226, 0.9); + border-radius: 0 0 6px 6px; +} + +.sidebar-item.has-sub.active .submenu { + max-height: 800px; +} + +.submenu-item { + position: relative; +} + +.submenu-item .sidebar-link { + padding-left: 45px; + font-size: 15px; + color: var(--white) !important; + background-color: transparent; + font-weight: 500; +} + +.submenu-item .sidebar-link::before { + left: 30px; + width: 2px; + background-color: var(--dark-blue); +} + +.submenu-item .sidebar-link:hover { + background-color: var(--primary-blue-lighter); + color: var(--white) !important; + padding-left: 48px; +} + +.submenu-item .sidebar-link:hover::before { + background-color: var(--dark-blue-deep); +} + +.submenu-item:last-child .sidebar-link { + border-radius: 0 0 6px 6px; +} + +.sidebar-item.has-sub.active .submenu-item:first-child .sidebar-link { + border-radius: 0; +} + +.sidebar-link.submenu-link.active::before { + background-color: var(--primary-blue-lighter); +} + +/* ========== Ícones e Indicadores ========== */ +.external-icon { + font-size: 12px; + margin-left: 8px; + opacity: 0.8; + color: var(--dark-blue) !important; +} + +.active-indicator { + position: absolute; + right: 15px; + top: 50%; + transform: translateY(-50%); + width: 8px; + height: 8px; + background-color: var(--primary-blue-lighter); + border-radius: 50%; + animation: pulse 2s infinite; + box-shadow: 0 0 10px rgba(112, 180, 255, 0.6); +} + +/* ========== Animações ========== */ +@keyframes fadeIn { + from { opacity: 0; transform: translateY(-10px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes pulse { + 0% { transform: translateY(-50%) scale(1); opacity: 1; } + 50% { transform: translateY(-50%) scale(1.3); opacity: 0.8; } + 100% { transform: translateY(-50%) scale(1); opacity: 1; } } \ No newline at end of file diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 223e491..5922c08 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -1,6 +1,5 @@ import React, { useState, useEffect } from "react"; import { Link, useNavigate } from "react-router-dom"; -import TrocardePerfis from "./TrocardePerfis"; import MobileMenuToggle from "./MobileMenuToggle"; import ToggleSidebar from "./ToggleSidebar"; import { useAuth } from "./utils/AuthProvider"; @@ -9,6 +8,7 @@ import PacienteItems from "../data/sidebar-items-paciente.json" import DoctorItems from "../data/sidebar-items-medico.json" import admItems from "../data/sidebar-items-adm.json" import SecretariaItems from "../data/sidebar-items-secretaria.json" +import FinanceiroItems from "../data/sidebar-items-financeiro.json" import { UserInfos } from "./utils/Functions-Endpoints/General"; @@ -264,13 +264,15 @@ function Sidebar({ menuItems }) { :null } + {roleUser.includes("admin") || roleUser.includes("financeiro") ? + + :null + } + {roleUser.includes("admin") || roleUser.includes("paciente") ? : null } - {/* Logout */} - -
diff --git a/src/components/ToggleSidebar.jsx b/src/components/ToggleSidebar.jsx index fc8e103..2065de1 100644 --- a/src/components/ToggleSidebar.jsx +++ b/src/components/ToggleSidebar.jsx @@ -1,102 +1,137 @@ import React from 'react' -import {useState} from 'react' -import { Link } from 'react-router-dom' +import { useState, useEffect } from 'react' +import { Link, useLocation } from 'react-router-dom' import "./Estilo/Toggle.css" -const ToggleSidebar = ({perfil, items}) => { - const [isOpen, setOpen] = useState(false) +const ToggleSidebar = ({ perfil, items, defaultOpen = false }) => { + const [isOpen, setOpen] = useState(defaultOpen) + const [openSubmenu, setOpenSubmenu] = useState(null) + const [activeLink, setActiveLink] = useState('') + const location = useLocation() - const [openSubmenu, setOpenSubmenu] = useState(null); + useEffect(() => { + const currentPath = location.pathname + setActiveLink(currentPath) + + const findActiveSubmenu = () => { + for (let item of items) { + if (item.submenu) { + const activeSubItem = item.submenu.find(subItem => + subItem.url === currentPath + ) + if (activeSubItem) { + setOpenSubmenu(item.key) + return + } + } else if (item.url === currentPath) { + setActiveLink(currentPath) + } + } + } + + findActiveSubmenu() + }, [location.pathname, items]) - console.log(items) + const OpenClose = () => { + setOpen(!isOpen) + } + + const handleSubmenuClick = (key) => { + setOpenSubmenu(openSubmenu === key ? null : key) + } + + const isLinkActive = (url) => { + return activeLink === url + } + + const renderLink = (item, isSubmenu = false) => { + const isActive = isLinkActive(item.url) + const linkClass = `sidebar-link ${isActive ? 'active' : ''} ${isSubmenu ? 'submenu-link' : ''}` - const renderLink = (item) => { if (item.url && item.url.startsWith("/")) { return ( - + !isSubmenu && setActiveLink(item.url)} + > {item.icon && } {item.name} + {isActive &&
} - ); + ) } + return ( - + {item.icon && } {item.name} + - ); - }; - - const OpenClose = () => { - if(isOpen){ - setOpen(false) - }else if(!isOpen){ - setOpen(true) - } - } + ) + } return ( -
-
-

OpenClose()} className='toggle-title' > {perfil}

- - {isOpen ? : } -
- {isOpen && -
- {items.map((item, index) => { +
+
+

{perfil}

+ {isOpen + ? + : + } +
- if (item.isTitle) - return ( -
  • - {null} + {isOpen && ( +
      + {items.map((item, index) => { + if (item.isTitle) { + return ( +
    • + {item.name} +
    • + ) + } + + if (item.submenu) { + const isSubmenuActive = openSubmenu === item.key + return ( +
    • + + +
        + {item.submenu.map((subItem, subIndex) => ( +
      • + {renderLink(subItem, true)}
      • - ); + ))} +
      +
    • + ) + } - if (item.submenu) - return ( -
    • - -
        - {item.submenu.map((subItem, subIndex) => ( -
      • - {renderLink(subItem)} -
      • - ))} -
      -
    • - ); - - return ( -
    • - {renderLink(item)} -
    • - ); - -})} - - -
  • - - - - } + return ( +
  • + {renderLink(item)} +
  • + ) + })} + + )}
    ) } diff --git a/src/components/doctors/DoctorForm.jsx b/src/components/doctors/DoctorForm.jsx index 22b5c42..013680d 100644 --- a/src/components/doctors/DoctorForm.jsx +++ b/src/components/doctors/DoctorForm.jsx @@ -275,7 +275,6 @@ const handleAvailabilityUpdate = useCallback((newAvailability) => { if (formData.availability && formData.availability.length > 0) { } - alert("Médico salvo com sucesso!"); } catch (error) { console.error("Erro ao salvar médico:", error); alert("Erro ao salvar médico."); diff --git a/src/data/sidebar-items-adm.json b/src/data/sidebar-items-adm.json index 4ab0555..be4969a 100644 --- a/src/data/sidebar-items-adm.json +++ b/src/data/sidebar-items-adm.json @@ -1,8 +1,4 @@ [ - { - "name": "Menu", - "isTitle": true - }, { "name": "Lista de Pacientes", "icon": "clipboard-heart-fill", diff --git a/src/data/sidebar-items-financeiro.json b/src/data/sidebar-items-financeiro.json index dfce9bc..0495b53 100644 --- a/src/data/sidebar-items-financeiro.json +++ b/src/data/sidebar-items-financeiro.json @@ -1,8 +1,4 @@ [ - { - "name": "Menu-Financeiro", - "isTitle": true - }, { "name": "Controle Financeiro", "icon": "cash-coin", diff --git a/src/data/sidebar-items-medico.json b/src/data/sidebar-items-medico.json index e31bf12..e7f7617 100644 --- a/src/data/sidebar-items-medico.json +++ b/src/data/sidebar-items-medico.json @@ -1,8 +1,4 @@ [ - { - "name": "Menu", - "isTitle": true - }, { "name": "Prontuário", diff --git a/src/data/sidebar-items-secretaria.json b/src/data/sidebar-items-secretaria.json index afe9e0a..c74cbf5 100644 --- a/src/data/sidebar-items-secretaria.json +++ b/src/data/sidebar-items-secretaria.json @@ -1,9 +1,4 @@ [ - { - "name": "Menu", - "isTitle": true - }, - { "name":"Início", "url": "/secretaria/", From e007c167e72add5f84927b197049d3f87a934a80 Mon Sep 17 00:00:00 2001 From: Caio Miguel Lima Nunes Date: Wed, 5 Nov 2025 21:27:13 -0300 Subject: [PATCH 04/19] Adicionado-novas-tabelas --- src/pages/Agendamento.jsx | 846 ++++++++++++++++++++------------ src/pages/style/Agendamento.css | 18 + 2 files changed, 553 insertions(+), 311 deletions(-) diff --git a/src/pages/Agendamento.jsx b/src/pages/Agendamento.jsx index 2e819a7..5325a5c 100644 --- a/src/pages/Agendamento.jsx +++ b/src/pages/Agendamento.jsx @@ -23,112 +23,116 @@ import { Search } from 'lucide-react'; -const Agendamento = ({setDictInfo}) => { +const Agendamento = ({ setDictInfo }) => { const navigate = useNavigate(); - + const [listaTodosAgendamentos, setListaTodosAgendamentos] = useState([]) - - const [selectedID, setSelectedId] = useState('0') + + const [selectedID, setSelectedId] = useState('0') const [filaEsperaData, setFilaEsperaData] = useState([]) const [FiladeEspera, setFiladeEspera] = useState(false); const [tabela, setTabela] = useState('diario'); const [PageNovaConsulta, setPageConsulta] = useState(false); const [searchTerm, setSearchTerm] = useState(''); - const [agendamentos, setAgendamentos] = useState() - const {getAuthorizationHeader} = useAuth() - const [DictAgendamentosOrganizados, setAgendamentosOrganizados ] = useState({}) + const [agendamentos, setAgendamentos] = useState() + const { getAuthorizationHeader } = useAuth() + const [DictAgendamentosOrganizados, setAgendamentosOrganizados] = useState({}) const [showDeleteModal, setShowDeleteModal] = useState(false) const [AgendamentoFiltrado, setAgendamentoFiltrado] = useState() - + const [ListaDeMedicos, setListaDeMedicos] = useState([]) const [FiltredTodosMedicos, setFiltredTodosMedicos] = useState([]) const [searchTermDoctor, setSearchTermDoctor] = useState(''); - const [MedicoFiltrado, setMedicoFiltrado] = useState({id:"vazio"}) + const [MedicoFiltrado, setMedicoFiltrado] = useState({ id: "vazio" }) - const [cacheAgendamentos, setCacheAgendamentos] = useState([]) + const [cacheAgendamentos, setCacheAgendamentos] = useState([]) - const [showConfirmModal, setShowConfirmModal] = useState(false) + const [showConfirmModal, setShowConfirmModal] = useState(false) - const [motivoCancelamento, setMotivoCancelamento] = useState("") + const [motivoCancelamento, setMotivoCancelamento] = useState("") - const [corModal, setCorModal] = useState("") + const [corModal, setCorModal] = useState("") - const [listaConsultasID, setListaConsultaID] = useState([]) - const [coresConsultas,setCoresConsultas] = useState([]) + const [listaConsultasID, setListaConsultaID] = useState([]) + const [coresConsultas, setCoresConsultas] = useState([]) + + // 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); + + let authHeader = getAuthorizationHeader() + + const cacheMedicos = {}; + const cachePacientes = {}; - let authHeader = getAuthorizationHeader() - -const cacheMedicos = {}; -const cachePacientes = {}; + useMemo(() => { + if (!listaTodosAgendamentos.length) return { agendamentosOrganizados: {}, filaEsperaData: [] }; + console.log("recarregando") + const DictAgendamentosOrganizados = {}; + const ListaFilaDeEspera = []; + const fetchDados = async () => { + for (const agendamento of listaTodosAgendamentos) { + if (agendamento.status === "requested") { + // Cache de médico e paciente + if (!cacheMedicos[agendamento.doctor_id]) { + cacheMedicos[agendamento.doctor_id] = await GetDoctorByID(agendamento.doctor_id, authHeader); + } + if (!cachePacientes[agendamento.patient_id]) { + cachePacientes[agendamento.patient_id] = await GetPatientByID(agendamento.patient_id, authHeader); + } - -useMemo(() => { - if (!listaTodosAgendamentos.length) return { agendamentosOrganizados: {}, filaEsperaData: [] }; -console.log("recarregando") - const DictAgendamentosOrganizados = {}; - const ListaFilaDeEspera = []; + const medico = cacheMedicos[agendamento.doctor_id]; + const paciente = cachePacientes[agendamento.patient_id]; - const fetchDados = async () => { - for (const agendamento of listaTodosAgendamentos) { - if (agendamento.status === "requested") { - // Cache de médico e paciente - if (!cacheMedicos[agendamento.doctor_id]) { - cacheMedicos[agendamento.doctor_id] = await GetDoctorByID(agendamento.doctor_id, authHeader); - } - if (!cachePacientes[agendamento.patient_id]) { - cachePacientes[agendamento.patient_id] = await GetPatientByID(agendamento.patient_id, authHeader); - } - - const medico = cacheMedicos[agendamento.doctor_id]; - const paciente = cachePacientes[agendamento.patient_id]; - - ListaFilaDeEspera.push({ - agendamento, - Infos: { - nome_medico: medico[0]?.full_name, - doctor_id: medico[0]?.id, - patient_id: paciente[0]?.id, - paciente_nome: paciente[0]?.full_name, - paciente_cpf: paciente[0]?.cpf, - }, - }); - } else { - const DiaAgendamento = agendamento.scheduled_at.split("T")[0]; - - if (DiaAgendamento in DictAgendamentosOrganizados) { - DictAgendamentosOrganizados[DiaAgendamento].push(agendamento); + ListaFilaDeEspera.push({ + agendamento, + Infos: { + nome_medico: medico[0]?.full_name, + doctor_id: medico[0]?.id, + patient_id: paciente[0]?.id, + paciente_nome: paciente[0]?.full_name, + paciente_cpf: paciente[0]?.cpf, + }, + }); } else { - DictAgendamentosOrganizados[DiaAgendamento] = [agendamento]; + const DiaAgendamento = agendamento.scheduled_at.split("T")[0]; + + if (DiaAgendamento in DictAgendamentosOrganizados) { + DictAgendamentosOrganizados[DiaAgendamento].push(agendamento); + } else { + DictAgendamentosOrganizados[DiaAgendamento] = [agendamento]; + } } } - } - // Ordenar por data - for (const DiaAgendamento in DictAgendamentosOrganizados) { - DictAgendamentosOrganizados[DiaAgendamento].sort((a, b) => a.scheduled_at.localeCompare(b.scheduled_at)); - } + // Ordenar por data + for (const DiaAgendamento in DictAgendamentosOrganizados) { + DictAgendamentosOrganizados[DiaAgendamento].sort((a, b) => a.scheduled_at.localeCompare(b.scheduled_at)); + } - const chavesOrdenadas = Object.keys(DictAgendamentosOrganizados).sort(); + const chavesOrdenadas = Object.keys(DictAgendamentosOrganizados).sort(); - const DictAgendamentosFinal = {}; - for (const data of chavesOrdenadas) { - DictAgendamentosFinal[data] = DictAgendamentosOrganizados[data]; - } + const DictAgendamentosFinal = {}; + for (const data of chavesOrdenadas) { + DictAgendamentosFinal[data] = DictAgendamentosOrganizados[data]; + } - setAgendamentosOrganizados(DictAgendamentosFinal); - setFilaEsperaData(ListaFilaDeEspera); - }; + setAgendamentosOrganizados(DictAgendamentosFinal); + setFilaEsperaData(ListaFilaDeEspera); + }; - fetchDados(); + fetchDados(); - return { agendamentosOrganizados: DictAgendamentosOrganizados, filaEsperaData: ListaFilaDeEspera }; -}, [listaTodosAgendamentos]); // 👉 só recalcula quando a lista muda + return { agendamentosOrganizados: DictAgendamentosOrganizados, filaEsperaData: ListaFilaDeEspera }; + }, [listaTodosAgendamentos]); // 👉 só recalcula quando a lista muda useEffect(() => { @@ -144,15 +148,16 @@ console.log("recarregando") fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?select&doctor_id&patient_id&status&scheduled_at&order&limit&offset", requestOptions) .then(response => response.json()) - .then(result => {setListaTodosAgendamentos(result);console.log(result)}) + .then(result => { setListaTodosAgendamentos(result); console.log(result) }) .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 })} + for (let d = 0; TodosOsMedicos.length > d; d++) { + lista.push({ nomeMedico: TodosOsMedicos[d].full_name, idMedico: TodosOsMedicos[d].id }) + } setListaDeMedicos(lista) } PegarTodosOsMedicos() @@ -160,55 +165,57 @@ console.log("recarregando") }, []) -const deleteConsulta = (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":"cancelled", - "cancellation_reason": motivoCancelamento - }); - - - 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 = (selectedPatientId) => { + var myHeaders = new Headers(); + myHeaders.append("Content-Type", "application/json"); + myHeaders.append('apikey', API_KEY) + myHeaders.append("authorization", authHeader) -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)); -} + var raw = JSON.stringify({ + "status": "cancelled", + "cancellation_reason": motivoCancelamento + }); + + + 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 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)); + + } @@ -247,7 +254,7 @@ const confirmConsulta = (selectedPatientId) => { default: break } } - let ListaDiasDatas = {segundas:segundas,tercas:tercas,quartas: quartas,quintas: quintas,sextas: sextas} + let ListaDiasDatas = { segundas: segundas, tercas: tercas, quartas: quartas, quintas: quintas, sextas: sextas } return ListaDiasDatas } @@ -255,137 +262,137 @@ const confirmConsulta = (selectedPatientId) => { const handleClickAgendamento = (agendamento) => { if (agendamento.status !== 'vazio') return else setPageConsulta(true) -}; + }; - useEffect(() => { - console.log("mudou FiltredTodosMedicos:", FiltredTodosMedicos); - if (MedicoFiltrado.id != "vazio" ) { - const unicoMedico = MedicoFiltrado; + useEffect(() => { + console.log("mudou FiltredTodosMedicos:", FiltredTodosMedicos); + if (MedicoFiltrado.id != "vazio") { + const unicoMedico = MedicoFiltrado; console.log(unicoMedico) - const idMedicoFiltrado = unicoMedico.idMedico; + const idMedicoFiltrado = unicoMedico.idMedico; console.log(`Médico único encontrado: ${unicoMedico.nomeMedico}. ID: ${idMedicoFiltrado}`); - + const agendamentosDoMedico = filtrarAgendamentosPorMedico( - DictAgendamentosOrganizados, - idMedicoFiltrado - ); + 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) setListaTodosAgendamentos(agendamentosDoMedico) - - } - }, [FiltredTodosMedicos, MedicoFiltrado]); -const filtrarAgendamentosPorMedico = (dictAgendamentos, idMedicoFiltrado) => { + } + }, [FiltredTodosMedicos, MedicoFiltrado]); + + const filtrarAgendamentosPorMedico = (dictAgendamentos, idMedicoFiltrado) => { setCacheAgendamentos(DictAgendamentosOrganizados); - + const todasAsListasDeAgendamentos = Object.values(dictAgendamentos); const todosOsAgendamentos = todasAsListasDeAgendamentos.flat(); - const agendamentosFiltrados = todosOsAgendamentos.filter(agendamento => - agendamento.doctor_id === idMedicoFiltrado + const agendamentosFiltrados = todosOsAgendamentos.filter(agendamento => + agendamento.doctor_id === idMedicoFiltrado ); return agendamentosFiltrados; -}; + }; -const handleSearchMedicos = (term) => { + const handleSearchMedicos = (term) => { setSearchTermDoctor(term); if (term.trim() === '') { - if(MedicoFiltrado.id !== "vazio"){ + if (MedicoFiltrado.id !== "vazio") { console.log("Medico escolhido, mas vai ser apagado") console.log(cacheAgendamentos, "cache ") - - } - - + + } + + setFiltredTodosMedicos([]); - setMedicoFiltrado({id:"vazio"}) - + setMedicoFiltrado({ id: "vazio" }) + //2 FiltrarAgendamentos() - return; + return; } - if (FiltredTodosMedicos.length === 1){ - setMedicoFiltrado({...FiltredTodosMedicos[0]}) + if (FiltredTodosMedicos.length === 1) { + setMedicoFiltrado({ ...FiltredTodosMedicos[0] }) } - - const filtered = ListaDeMedicos.filter(medico => - medico.nomeMedico.toLowerCase().includes(term.toLowerCase()) + + const filtered = ListaDeMedicos.filter(medico => + medico.nomeMedico.toLowerCase().includes(term.toLowerCase()) ); setFiltredTodosMedicos(filtered); -}; - + }; + return (

    Agendar nova consulta

    - - + +
    - - - - - + + + + +
    {!PageNovaConsulta ? (
    -
    - - {/* Bloco de busca por médico */} -
    -
    -
    +
    + + {/* Bloco de busca por médico */} +
    +
    +
    - - handleSearchMedicos(e.target.value)} - /> + + handleSearchMedicos(e.target.value)} + />
    -
    - - {/* DROPDOWN (RENDERIZAÇÃO CONDICIONAL) */} +
    + + {/* DROPDOWN (RENDERIZAÇÃO CONDICIONAL) */} {searchTermDoctor && FiltredTodosMedicos.length > 0 && (
    - {FiltredTodosMedicos.map((medico) => ( -
    { - setSearchTermDoctor(medico.nomeMedico); - setFiltredTodosMedicos([]); - setMedicoFiltrado(medico) - - }} - > -

    {medico.nomeMedico}

    -
    - ))} + {FiltredTodosMedicos.map((medico) => ( +
    { + setSearchTermDoctor(medico.nomeMedico); + setFiltredTodosMedicos([]); + setMedicoFiltrado(medico) + + }} + > +

    {medico.nomeMedico}

    +
    + ))}
    - )} + )} +
    -
    - +
    @@ -434,71 +441,285 @@ const handleSearchMedicos = (term) => {
    Cancelado
    - - {/* Componentes de Tabela - Adicionado props de delete da main */} + + {/* Componentes de Tabela - Adicionado props de delete da main */} {tabela === "diario" && } - {tabela === 'semanal' && } - {tabela === 'mensal' && } + {tabela === 'semanal' && } + {tabela === 'mensal' && }
    ) : ( -
    -
    - setSearchTerm(e.target.value)} - /> -

    Fila de Espera

    -
    - - - - {/* Ajustado o cabeçalho */} - {/* Ajustado o cabeçalho */} - {/* Ajustado o cabeçalho */} - {/* Ajustado o cabeçalho */} - - - - - {filaEsperaData.map((item, index) => ( - - - - - - - - ))} - -
    Nome do PacienteCPFMédico SolicitadoData da SolicitaçãoAções

    {item.Infos?.paciente_nome}

    {item.Infos?.paciente_cpf}

    {item.Infos?.nome_medico}

    {dayjs(item.agendamento.created_at).format('DD/MM/YYYY HH:mm')}
    - - - +
    +
    +
    +
    +
    +

    Fila de Espera

    +
    - -
    +
    + {/* Filtros */} +
    +
    + Filtros +
    + +
    + setSearchTermFila(e.target.value)} + /> + + Digite o nome completo ou número do CPF + +
    + +
    + setFiltroMedicoFila(e.target.value || 'Todos')} + /> + + Digite o nome do médico para filtrar + +
    + +
    + +
    + +
    +
    + {(() => { + const filtrados = filaEsperaData.filter((item) => { + const buscaNome = item.Infos?.paciente_nome?.toLowerCase().includes(searchTermFila.toLowerCase()); + const buscaCPF = item.Infos?.paciente_cpf?.toLowerCase().includes(searchTermFila.toLowerCase()); + const passaBusca = searchTermFila === "" || buscaNome || buscaCPF; + const passaMedico = filtroMedicoFila === "Todos" || item.Infos?.nome_medico?.toLowerCase().includes(filtroMedicoFila.toLowerCase()); + return passaBusca && passaMedico; + }); + return filtrados.length; + })()} DE {filaEsperaData.length} SOLICITAÇÕES ENCONTRADAS +
    +
    +
    + + {/* Filtros Ativos */} + {(searchTermFila || filtroMedicoFila !== "Todos") && ( +
    + Filtros ativos: +
    + {searchTermFila && Busca: "{searchTermFila}"} + {filtroMedicoFila !== "Todos" && Médico: {filtroMedicoFila}} +
    +
    + )} + + {/* Tabela */} +
    + + + + + + + + + + + + {(() => { + // Filtrar dados + const filaFiltrada = filaEsperaData.filter((item) => { + const buscaNome = item.Infos?.paciente_nome?.toLowerCase().includes(searchTermFila.toLowerCase()); + const buscaCPF = item.Infos?.paciente_cpf?.toLowerCase().includes(searchTermFila.toLowerCase()); + const passaBusca = searchTermFila === "" || buscaNome || buscaCPF; + const passaMedico = filtroMedicoFila === "Todos" || item.Infos?.nome_medico?.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) => ( + + + + + + + + )); + } else { + return ( + + + + ); + } + })()} + +
    Nome do PacienteCPFMédico SolicitadoData da SolicitaçãoAções
    +
    + {item.Infos?.paciente_nome} +
    +
    {item.Infos?.paciente_cpf} + + {item.Infos?.nome_medico || 'Não informado'} + + {dayjs(item.agendamento.created_at).format('DD/MM/YYYY HH:mm')} +
    + + + +
    +
    +
    + +

    Nenhuma solicitação encontrada com os filtros aplicados.

    + {(searchTermFila || filtroMedicoFila !== "Todos") && ( + + )} +
    +
    + + {/* Paginação */} + {(() => { + const filaFiltrada = filaEsperaData.filter((item) => { + const buscaNome = item.Infos?.paciente_nome?.toLowerCase().includes(searchTermFila.toLowerCase()); + const buscaCPF = item.Infos?.paciente_cpf?.toLowerCase().includes(searchTermFila.toLowerCase()); + const passaBusca = searchTermFila === "" || buscaNome || buscaCPF; + const passaMedico = filtroMedicoFila === "Todos" || item.Infos?.nome_medico?.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 ( +
    +
    + Itens por página: + +
    + +
    + + Página {paginaAtualFila} de {totalPaginasFila} • + Mostrando {indiceInicial + 1}-{Math.min(indiceFinal, filaFiltrada.length)} de {filaFiltrada.length} solicitações + + + +
    +
    + ); + } + return null; + })()} +
    +
    +
    +
    +
    ) } @@ -508,8 +729,8 @@ const handleSearchMedicos = (term) => { )} - {/* Modal de Confirmação de Exclusão */} - {showDeleteModal && ( + {/* Modal de Confirmação de Exclusão */} + {showDeleteModal && (
    { >
    - +
    Confirmação de Cancelamento @@ -537,47 +758,49 @@ const handleSearchMedicos = (term) => {

    - Qual o motivo do cancelamento? + Qual o motivo do cancelamento?

    -
    - - -
    - - - -
    - - -
    - -
    - - -
    - +
    + + +
    + +
    +
    + + +
    +
    + +

    Informações do atendimento

    + +
    +
    +
    + + +
    + + {isDropdownOpen && profissionaisFiltrados.length > 0 && ( +
    + {profissionaisFiltrados.map((profissional) => ( +
    handleSelectProfissional(profissional)} + > + {profissional.full_name} +
    + ))} +
    + )} +
    + +
    + + +
    +
    + +
    +
    +
    + + +
    + +
    +
    + + +
    + +
    + +
    + + +

    {sessoes}

    + + +
    +
    + +
    + + +
    +
    +
    + +
    + + +
    +
    + + +
    - - +
    - ) : ( - - )} - - - {showConfirmModal &&( -
    - e.target.classList.contains("modal") && setShowDeleteModal(false) - } - > -
    -
    - -
    -
    - Confirmação de edição -
    - -
    - -
    -

    - Tem certeza que deseja retirar o cancelamento ? -

    -
    - -
    - - - - - -
    + +
    -
    -
    )} + {!PageNovaConsulta ? ( +
    +
    +
    +
    +
    +
    + handleSearchMedicos(e.target.value)} /> +
    +
    + {searchTermDoctor && FiltredTodosMedicos.length > 0 && ( +
    + {FiltredTodosMedicos.map((medico) => ( +
    { + setSearchTermDoctor(medico.nomeMedico); + setFiltredTodosMedicos([]); + setMedicoFiltrado(medico); + }} + > +

    {medico.nomeMedico}

    +
    + ))} +
    + )} +
    +
    +
    +
    + + +
    +
    + {FiladeEspera === false ? ( +
    +
    +
    {selectedDay.format('MMM')}{selectedDay.format('DD')}
    +

    {selectedDay.format('dddd')}

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

    +
    +

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

    + {showSpinner ? : (DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')]?.length > 0) ? ( + DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')] + .filter(app => MedicoFiltrado.id === "vazio" || app.doctor_id === MedicoFiltrado.id) + .map(app => ( +
    +
    {dayjs(app.scheduled_at).format('HH:mm')}
    +
    {app.paciente_nome}Dr(a). {app.medico_nome}
    +
    + {app.status === 'cancelled' ? ( + + ) : ( + + )} + {app.status !== 'cancelled' && ( + + )} +
    +
    + ))) : (

    Nenhuma consulta agendada.

    )} +
    +
    +
    +
    +
    Realizado
    +
    Confirmado
    +
    Agendado
    +
    Cancelado
    +
    +
    +
    +

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

    +
    + + + +
    +
    - {showDeleteModal && ( -
    - e.target.classList.contains("modal") && setShowDeleteModal(false) - } - > -
    -
    - -
    -
    - Confirmação de Cancelamento -
    - -
    +
    + + + +
    +
    -
    -

    - Qual o motivo do cancelamento? -

    -
    - - +
    +
    + + +
    -
    - -
    - - - - - -
    -
    -
    )} - - {showConfirmModal &&( -
    - e.target.classList.contains("modal") && setShowDeleteModal(false) - } - > -
    -
    - -
    -
    - Confirmação de edição -
    - -
    - -
    -

    - Tem certeza que deseja retirar o cancelamento ? -

    -
    - -
    - - - - - -
    -
    -
    - )} + ); -
    - ) + const ConfirmEditModal = () => ( +
    +
    +
    +
    +
    Confirmação de edição
    + +
    +
    +

    Tem certeza que deseja retirar o cancelamento?

    + Isso reverterá o status do agendamento para Agendado. +
    +
    + + +
    +
    +
    +
    + ); + + + return ( +
    +

    Agendar nova consulta

    +
    + {/* LIMPA O OBJETO DE EDIÇÃO AO CLICAR EM "ADICIONAR" */} + + + +
    + {!PageNovaConsulta ? ( +
    +
    +
    +
    +
    +
    + handleSearchMedicos(e.target.value)} /> +
    +
    + {searchTermDoctor && FiltredTodosMedicos.length > 0 && (
    {FiltredTodosMedicos.map((medico) => (
    { setSearchTermDoctor(medico.nomeMedico); setFiltredTodosMedicos([]); setMedicoFiltrado(medico); }}>

    {medico.nomeMedico}

    ))}
    )} +
    +
    +
    +
    + + +
    +
    + {FiladeEspera === false ? ( +
    +
    +
    {selectedDay.format('MMM')}{selectedDay.format('DD')}
    +

    {selectedDay.format('dddd')}

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

    +
    +

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

    + {showSpinner ? : (DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')]?.length > 0) ? (DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')].map(app => ( +
    +
    {dayjs(app.scheduled_at).format('HH:mm')}
    +
    {app.paciente_nome}Dr(a). {app.medico_nome}
    +
    + {app.status === 'cancelled' ? ( + + ) : ( + <> + {/* MUDANÇA: PASSA O OBJETO COMPLETO 'app' PARA O ESTADO DE EDIÇÃO */} + + {/* Botão de Cancelar */} + + + )} +
    +
    + ))) : (

    Nenhuma consulta agendada.

    )} +
    +
    +
    +
    +
    Realizado
    Confirmado
    Agendado
    Cancelado
    +
    +
    +
    +

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

    +
    + + + +
    +
    + +
    + + + +
    +
    + +
    + {weekDays.map(day =>
    {day}
    )} + {dateGrid.map((day, index) => { + const appointmentsOnDay = DictAgendamentosOrganizados[day.format('YYYY-MM-DD')] || []; + const cellClasses = `day-cell ${day.isSame(currentDate, 'month') ? 'current-month' : 'other-month'} ${day.isSame(dayjs(), 'day') ? 'today' : ''} ${day.isSame(selectedDay, 'day') ? 'selected' : ''}`; + return (
    handleDateClick(day)}>{day.format('D')}{appointmentsOnDay.length > 0 &&
    {appointmentsOnDay.length}
    }
    ); + })} +
    +
    +
    + ) : ( +
    +
    +
    +
    +

    Fila de Espera

    +
    +
    +
    Filtros
    +
    setWaitlistSearch(e.target.value)} />Digite o nome do paciente, CPF ou nome do médico
    +
    +
    + Ordenar por: + {(() => { const sortValue = waitSortKey ? `${waitSortKey}-${waitSortDir}` : ''; return ();})()} +
    +
    +
    {filaEsperaFiltrada.length} DE {filaEsperaData.length} SOLICITAÇÕES ENCONTRADAS
    +
    +
    + + + + {filaEsperaPaginada.length > 0 ? (filaEsperaPaginada.map((item, index) => ( + + ))) : ()} + +
    Nome do PacienteCPFMédico SolicitadoData da SolicitaçãoAções
    {item?.Infos?.paciente_nome}{item?.Infos?.paciente_cpf}{item?.Infos?.nome_medico}{dayjs(item.agendamento.scheduled_at).format('DD/MM/YYYY')}
    {showSpinner ? : (<>

    Nenhuma solicitação encontrada.

    )}
    + {filaEsperaFiltrada.length > 0 && (
    +
    Itens por página:
    +
    Página {waitPage} de {waitTotalPages} • Mostrando {waitIndiceInicial + 1}-{Math.min(waitIndiceFinal, filaEsperaFiltrada.length)} de {filaEsperaFiltrada.length}
    +
    )} +
    +
    +
    +
    +
    +
    + )} +
    +
    + ) : ( + + + )} + {showDeleteModal && } + {showConfirmModal && } +
    + ) } export default Agendamento; \ No newline at end of file diff --git a/src/pages/AgendamentoCadastroManager.jsx b/src/pages/AgendamentoCadastroManager.jsx index 881462e..76396e1 100644 --- a/src/pages/AgendamentoCadastroManager.jsx +++ b/src/pages/AgendamentoCadastroManager.jsx @@ -1,82 +1,182 @@ -import React from 'react' -import FormNovaConsulta from '../components/AgendarConsulta/FormNovaConsulta' -import API_KEY from '../components/utils/apiKeys' -import { useAuth } from '../components/utils/AuthProvider' -import { useEffect,useState } from 'react' -import dayjs from 'dayjs' -import { UserInfos } from '../components/utils/Functions-Endpoints/General' -const AgendamentoCadastroManager = ({setPageConsulta, Dict}) => { +import React, { useEffect, useState } from 'react'; +import FormNovaConsulta from '../components/AgendarConsulta/FormNovaConsulta'; +import API_KEY from '../components/utils/apiKeys'; +import { useAuth } from '../components/utils/AuthProvider'; +import dayjs from 'dayjs'; +import { UserInfos } from '../components/utils/Functions-Endpoints/General'; +import { toast } from 'react-toastify'; +const AgendamentoCadastroManager = ({ setPageConsulta, agendamentoInicial }) => { - const {getAuthorizationHeader} = useAuth() - const [agendamento, setAgendamento] = useState({status:'confirmed'}) - const [idUsuario, setIDusuario] = useState('0') + const { getAuthorizationHeader } = useAuth(); + + const [agendamento, setAgendamento] = useState({ status: 'agendado' }); + const [idUsuario, setIDusuario] = useState('0'); + const [isEditMode, setIsEditMode] = useState(false); - let authHeader = getAuthorizationHeader() + let authHeader = getAuthorizationHeader(); + useEffect(() => { + + + if (agendamentoInicial && agendamentoInicial.id) { + + const scheduled_at = dayjs(agendamentoInicial.scheduled_at); - useEffect(() => { + setAgendamento({ + ...agendamentoInicial, + + dataAtendimento: scheduled_at.format('YYYY-MM-DD'), + horarioInicio: scheduled_at.format('HH:mm'),         + + + tipo_consulta: agendamentoInicial.appointment_type || 'Presencial', + convenio: agendamentoInicial.insurance_provider || 'Público', + + + paciente_nome: agendamentoInicial.paciente_nome || '', + paciente_cpf: agendamentoInicial.paciente_cpf || '', + medico_nome: agendamentoInicial.medico_nome || '', + + + patient_id: agendamentoInicial.patient_id, + doctor_id: agendamentoInicial.doctor_id, + status: agendamentoInicial.status, + }); + setIsEditMode(true); + + } else { + + setAgendamento({ + status: 'agendado', + patient_id: null, + doctor_id: null, + dataAtendimento: dayjs().format('YYYY-MM-DD'), + horarioInicio: '', + tipo_consulta: 'Presencial', + convenio: 'Público', + paciente_nome: '', + paciente_cpf: '', + medico_nome: '' + }); + setIsEditMode(false); + } - if(!Dict){setAgendamento({})} - else{ - console.log(Dict) - setAgendamento(Dict) - } - - const ColherInfoUsuario =async () => { - const result = await UserInfos(authHeader) - - setIDusuario(result?.profile?.id) - - } - ColherInfoUsuario() - - - }, []) - - - - const handleSave = (Dict) => { - let DataAtual = dayjs() + + const ColherInfoUsuario = async () => { + const result = await UserInfos(authHeader); + setIDusuario(result?.profile?.id); + }; + ColherInfoUsuario(); + + + }, [agendamentoInicial, authHeader]); + const handleSave = async (Dict) => { var myHeaders = new Headers(); myHeaders.append("apikey", API_KEY); myHeaders.append("Authorization", authHeader); myHeaders.append("Content-Type", "application/json"); + myHeaders.append("Prefer", "return=representation"); + + var raw = JSON.stringify({ + "patient_id": Dict.patient_id, + "doctor_id": Dict.doctor_id, + "scheduled_at": `${Dict.dataAtendimento}T${Dict.horarioInicio}:00.000Z`, + "duration_minutes": Dict.duration_minutes || 30, + "appointment_type": Dict.tipo_consulta, + "insurance_provider": Dict.convenio, + "status": Dict.status || 'agendado', + "created_by": idUsuario, + "created_at": dayjs().toISOString(), + + }); + + var requestOptions = { + method: 'POST', + headers: myHeaders, + body: raw, + redirect: 'follow' + }; + + try { + const response = await fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments", requestOptions); + if (response.ok) { + toast.success("Agendamento criado com sucesso! ✅"); + setPageConsulta(false); + } else { + const errorText = await response.text(); + console.error('Erro ao cadastrar agendamento:', errorText); + toast.error(`Falha ao criar agendamento: ${JSON.parse(errorText)?.message || 'Erro desconhecido'}`); + } + } catch (error) { + console.error('Erro de rede:', error); + toast.error("Erro de rede ao salvar. ❌"); + } + } + const handleUpdate = async (Dict) => { + const appointmentId = agendamentoInicial.id; + + var myHeaders = new Headers(); + myHeaders.append("apikey", API_KEY); + myHeaders.append("Authorization", authHeader); + myHeaders.append("Content-Type", "application/json"); + myHeaders.append("Prefer", "return=representation"); - var raw = JSON.stringify({ - - "patient_id": Dict.patient_id, - "doctor_id": Dict.doctor_id, - "scheduled_at": `${Dict.dataAtendimento}T${Dict.horarioInicio}:00.000Z`, - "duration_minutes": 30, - "appointment_type": Dict.tipo_consulta, - - "patient_notes": "", - "insurance_provider": Dict.convenio, - "status": Dict.status, - "created_by": idUsuario - }); - - var requestOptions = { - method: 'POST', - headers: myHeaders, - body: raw, - redirect: 'follow' - }; - - fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments", requestOptions) - .then(response => response.text()) - .then(result => console.log(result)) - .catch(error => console.log('error', error)); - - } + var raw = JSON.stringify({ + "patient_id": Dict.patient_id, + "doctor_id": Dict.doctor_id, + "scheduled_at": `${Dict.dataAtendimento}T${Dict.horarioInicio}:00.000Z`, + "duration_minutes": Dict.duration_minutes || 30, + "appointment_type": Dict.tipo_consulta, + "insurance_provider": Dict.convenio, + "status": Dict.status, + "updated_at": dayjs().toISOString(), + "updated_by": idUsuario, + }); + + var requestOptions = { + method: 'PATCH', + headers: myHeaders, + body: raw, + redirect: 'follow' + }; + + try { + const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${appointmentId}`, requestOptions); + if (response.ok) { + toast.success("Agendamento atualizado com sucesso! 📝"); + setPageConsulta(false); + } else { + const errorText = await response.text(); + console.error('Erro ao atualizar agendamento:', errorText); + toast.error(`Falha ao atualizar agendamento: ${JSON.parse(errorText)?.message || 'Erro desconhecido'}`); + } + } catch (error) { + console.error('Erro de rede:', error); + toast.error("Erro de rede ao atualizar. ❌"); + } + } + const handleFormSubmit = (Dict) => { + if (isEditMode) { + + handleUpdate(Dict); + } else { + + handleSave(Dict); + } + } - return ( -
    - setPageConsulta(false)}/> + return ( +
    + {} + setPageConsulta(false)} + /> +
    + ); +}; -
    - ) -} - -export default AgendamentoCadastroManager \ No newline at end of file +export default AgendamentoCadastroManager; \ No newline at end of file diff --git a/src/pages/MedicoAgendamento.jsx b/src/pages/MedicoAgendamento.jsx new file mode 100644 index 0000000..2770a4c --- /dev/null +++ b/src/pages/MedicoAgendamento.jsx @@ -0,0 +1,139 @@ +import React, { useState, useMemo, useEffect } from 'react'; +import dayjs from 'dayjs'; +import CalendarComponent from '../components/AgendarConsulta/CalendarComponent.jsx'; +import { useAuth } from '../components/utils/AuthProvider.js'; +import TabelaAgendamentoDia from '../components/AgendarConsulta/TabelaAgendamentoDia'; + +dayjs.locale('pt-br'); + +const MedicoAgendamento = () => { + const { getAuthorizationHeader, user } = useAuth(); + const [currentDate, setCurrentDate] = useState(dayjs()); + const [selectedDay, setSelectedDay] = useState(dayjs()); + const [DictAgendamentosOrganizados, setAgendamentosOrganizados] = useState({}); + const [showSpinner, setShowSpinner] = useState(true); + const [modoVisualizacao, setModoVisualizacao] = useState('Dia'); + + + const [quickJump, setQuickJump] = useState({ + month: currentDate.month(), + year: currentDate.year() + }); + + const handleQuickJumpChange = (type, value) => { + setQuickJump(prev => ({ ...prev, [type]: Number(value) })); + }; + + const applyQuickJump = () => { + let newDate = dayjs().year(quickJump.year).month(quickJump.month).date(1); + setCurrentDate(newDate); + setSelectedDay(newDate); + }; + + + const [selectedID, setSelectedId] = useState('0'); + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [showConfirmModal, setShowConfirmModal] = useState(false); t + + + useEffect(() => { + + const mockAgendamentos = { + [dayjs().format('YYYY-MM-DD')]: [ + { id: 1, scheduled_at: dayjs().set('hour', 10).set('minute', 0).toISOString(), paciente_nome: "Paciente Teste 1", medico_nome: "Dr. Mock", status: "agendado" }, + { id: 2, scheduled_at: dayjs().set('hour', 11).set('minute', 30).toISOString(), paciente_nome: "Paciente Teste 2", medico_nome: "Dr. Mock", status: "confirmed" }, + ], + + '2025-10-27': [ + { id: 3, scheduled_at: '2025-10-27T19:30:00Z', paciente_nome: 'Davi Andrade', medico_nome: 'Dr. João', status: 'agendado' }, + { id: 4, scheduled_at: '2025-10-27T20:00:00Z', paciente_nome: 'Davi Andrade', medico_nome: 'Dr. João', status: 'agendado' }, + { id: 5, scheduled_at: '2025-10-27T21:30:00Z', paciente_nome: 'Davi Andrade', medico_nome: 'Dr. João', status: 'agendado' }, + ] + }; + + const today = dayjs(); + const startOfMonth = today.startOf('month'); + const nov11 = startOfMonth.add(10, 'day').format('YYYY-MM-DD'); + + + mockAgendamentos[nov11] = [ + { id: 6, scheduled_at: `${nov11}T10:30:00Z`, paciente_nome: 'Paciente C', medico_nome: 'Isaac Kauã', status: 'agendado' }, + { id: 7, scheduled_at: `${nov11}T11:00:00Z`, paciente_nome: 'João Gustavo', medico_nome: 'João Gustavo', status: 'agendado' }, + { id: 8, scheduled_at: `${nov11}T12:30:00Z`, paciente_nome: 'João Gustavo', medico_nome: 'João Gustavo', status: 'agendado' }, + { id: 9, scheduled_at: `${nov11}T15:00:00Z`, paciente_nome: 'Pedro Abravanel', medico_nome: 'Fernando Prichowski', status: 'agendado' }, + ]; + + + setAgendamentosOrganizados(mockAgendamentos); + setShowSpinner(false); + }, []); + + + const handleSelectSlot = (timeSlot, doctorId) => { + alert(`Abrir tela de Nova Consulta para o dia ${selectedDay.format('DD/MM/YYYY')} às ${timeSlot} com o Médico ID: ${doctorId}`); + + }; + + const isMedico = true; + const medicoLogadoID = user?.doctor_id || "ID_MEDICO_DEFAULT"; + + return ( +
    +

    Agenda do Médico: {user?.full_name || "Nome do Médico"}

    +
    + + +
    + +
    + + + +
    + +
    + {} +
    + +
    + + {} +
    + {modoVisualizacao === 'Dia' && ( + + )} + {} + {} + {} +
    +
    +
    + ); +}; + +export default MedicoAgendamento; \ No newline at end of file diff --git a/src/pages/TabelaAgendamentoDia.jsx b/src/pages/TabelaAgendamentoDia.jsx new file mode 100644 index 0000000..34725e7 --- /dev/null +++ b/src/pages/TabelaAgendamentoDia.jsx @@ -0,0 +1,132 @@ +import React, { useState, useEffect } from 'react'; +import dayjs from 'dayjs'; +import 'dayjs/locale/pt-br'; +import { ChevronLeft, ChevronRight, Edit, Trash2, User, Stethoscope } from 'lucide-react'; + + +// Configura o Day.js para usar o idioma português do Brasil +dayjs.locale('pt-br'); + + +const TabelaAgendamentoDia = ({ + agendamentos, + setDictInfo, + setShowDeleteModal, + setSelectedId, + setShowConfirmModal, + listaConsultasID, + coresConsultas +}) => { + const [currentDate, setCurrentDate] = useState(dayjs()); + const [appointmentsForDay, setAppointmentsForDay] = useState([]); + + + useEffect(() => { + const formattedDate = currentDate.format('YYYY-MM-DD'); + const dailyAppointments = agendamentos[formattedDate] || []; + + + const appointmentsComStatusAtualizado = dailyAppointments.map(app => { + const index = listaConsultasID.indexOf(app.id); + if (index > -1) { + return { ...app, status: coresConsultas[index] }; + } + return app; + }); + + + setAppointmentsForDay(appointmentsComStatusAtualizado); + }, [currentDate, agendamentos, listaConsultasID, coresConsultas]); + + + const handlePrevDay = () => { + setCurrentDate(currentDate.subtract(1, 'day')); + }; + + + const handleNextDay = () => { + setCurrentDate(currentDate.add(1, 'day')); + }; + + + const handleEdit = (agendamento) => { + // Adapte para a sua lógica de edição, talvez abrindo um modal + console.log("Editar:", agendamento); + setDictInfo(agendamento); + }; + + + const handleDelete = (id) => { + setSelectedId(id); + setShowDeleteModal(true); + }; + + + // Gera os horários do dia (ex: 08:00 às 18:00) + const renderTimeSlots = () => { + const slots = []; + for (let i = 8; i <= 18; i++) { + const time = `${i.toString().padStart(2, '0')}:00`; + const hourlyAppointments = appointmentsForDay.filter(app => + dayjs(app.scheduled_at).format('HH:mm') === time + ); + + + slots.push( +
    +
    {time}
    +
    + {hourlyAppointments.length > 0 ? ( + hourlyAppointments.map(app => ( +
    +
    +
    + + Dr(a): {app.medico_nome} +
    +
    + + Paciente: {app.paciente_nome} +
    +
    +
    + + +
    +
    + )) + ) : ( +
    + )} +
    +
    + ); + } + return slots; + }; + + + return ( +
    +
    + +

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

    + +
    +
    + {renderTimeSlots()} +
    +
    + ); +}; + + +export default TabelaAgendamentoDia; diff --git a/src/pages/style/Agendamento.css b/src/pages/style/Agendamento.css index 137e79b..d887b3a 100644 --- a/src/pages/style/Agendamento.css +++ b/src/pages/style/Agendamento.css @@ -1,3 +1,5 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); + .filtros-container select, .filtros-container input { padding: 0.5rem; @@ -14,667 +16,210 @@ cursor: pointer; } -.unidade-selecionarprofissional{ - background-color: #fdfdfdde; - padding: 20px 10px; +.unidade-selecionarprofissional { + background-color: #ffffff; + padding: 20px 20px; display: flex; - border-radius:10px ; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + justify-content: flex-start; + align-items: center; + border-radius: 10px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); + margin-bottom: 20px; } -.unidade-selecionarprofissional input, .unidade-selecionarprofissional select { - margin-left: 8px; - border-radius: 8px; - padding: 5px; - width: 20%; -} - -.unidade-selecionarprofissional select{ - width: 7%; -} - -.busca-atendimento{ +.busca-atendimento { display: flex; flex-direction: row; - margin:10px; + margin: 0; justify-content: flex-start; } -.busca-atendimento select{ - padding:5px; - border-radius:8px ; - margin-left: 15px; - background-color: #0078d7; - color: white; - font-weight: bold; -} - -.busca-atendimento input{ - margin-left: 8px; +.busca-atendimento input { + border: 2px solid #000000; border-radius: 8px; - padding: 5px; + padding: 10px 15px; width: 100%; + font-size: 1rem; + margin-left: 0; } -.btn-selecionar-tabeladia, .btn-selecionar-tabelasemana, .btn-selecionar-tabelames { - background-color: rgba(231, 231, 231, 0.808); - padding:8px 10px; - font-size: larger; - font-weight: bold; - border-style: hidden; -} - -.btn-selecionar-tabeladia{ - border-radius: 10px 0px 0px 10px; -} - -.btn-selecionar-tabelames{ - border-radius: 0px 10px 10px 0px; -} - -.btn-selecionar-tabeladia.ativo, .btn-selecionar-tabelasemana.ativo, .btn-selecionar-tabelames.ativo{ - background-color: lightcyan; - border-color: darkcyan; - font-weight: bolder; -} - -.legenda-tabela{ - display: flex; - - margin-top: 30px; - margin-bottom: 10px; - gap: 15px; - justify-content: flex-end; -} - -.legenda-item-realizado{ - background-color: #2c5e37; -} - -.legenda-item-confirmed{ - background-color: #1e90ff; -} -.legenda-item-cancelado{ - background-color: #d9534f; -} - -.legenda-item-agendado{ - background-color: #f0ad4e; -} - -#status-card-consulta-completed, .legenda-item-realizado { - background-color: #b7ffbd; - border:3px solid #91d392; - padding: 5px; - font-weight: bold; - border-radius: 10px; -} - -#status-card-consulta-cancelled, .legenda-item-cancelado { - background-color: #ffb7cc; - border:3px solid #ff6c84; - padding: 5px; - font-weight: bold; - border-radius: 10px; -} - -#status-card-consulta-confirmed, .legenda-item-confirmed { - background-color: #eef8fb; - border:3px solid #d8dfe7; - padding: 5px; - font-weight: bold; - border-radius: 10px; -} - -#status-card-consulta-agendado, .legenda-item-agendado { - background-color: #f7f7c4; - border:3px solid #f3ce67; - padding: 5px; - font-weight: bold; - border-radius: 10px; -} - -.btns-e-legenda-container{ - display: flex; - justify-content: space-between; - flex-direction: row; - margin-top: 10px; -} - -.calendario { - border-collapse: collapse; - width: 100%; - border-radius: 10px; - overflow: hidden; - box-shadow: 0 4px 12px rgb(255, 255, 255); - border: 10px solid #ffffffc5; - background-color: rgb(253, 253, 253); -} - -.calendario-ou-filaespera{ - margin-top: 0; -} - -.container-btns-agenda-fila_esepera{ - margin-top: 30px; +.container-btns-agenda-fila_esepera { + margin-top: 20px; + margin-left: 0; display: flex; flex-direction: row; - gap: 20px; - margin-left:20px ; + gap: 0; + border-bottom: 2px solid #E2E8F0; + margin-bottom: 20px; } -.btn-fila-espera, .btn-agenda{ +.btn-fila-espera, .btn-agenda { background-color: transparent; - border: 0px ; - border-bottom: 3px solid rgb(253, 253, 253); - padding: 8px; - border-radius: 10px 10px 0px 0px; - font-weight: bold; -} - -.opc-filaespera-ativo, .opc-agenda-ativo{ - color: white; - background-color: #5980fd; -} - -html[data-bs-theme="dark"] { - body { - background-color: #121212; - color: #e0e0e0; - } - - .calendario { - background-color: #1e1e1e; - border: 10px solid #333; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); - } - - .unidade-selecionarprofissional { - background-color: #1e1e1e; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); - } - - - .busca-atendimento input { - background-color: #2c2c2c; - color: #e0e0e0; - border: 1px solid #444; - } - - .btn-buscar, - .btn-selecionar-tabeladia, - .btn-selecionar-tabelasemana, - .btn-selecionar-tabelames { - background-color: #2c2c2c; - color: #e0e0e0; - border: none; - } - - .btn-selecionar-tabeladia.ativo, - .btn-selecionar-tabelasemana.ativo, - .btn-selecionar-tabelames.ativo { - background-color: #005a9e; - border-color: #004578; - color: #fff; - } - - .legenda-item-realizado { - background-color: #14532d; - border-color: #166534; - } - - .legenda-item-confirmado { - background-color: #1e3a8a; - border-color: #2563eb; - } - - .legenda-item-cancelado { - background-color: #7f1d1d; - border-color: #dc2626; - } - - .legenda-item-agendado { - background-color: #78350f; - border-color: #f59e0b; - } - - #status-card-consulta-realizado, - .legenda-item-realizado { - background-color: #14532d; - border: 3px solid #166534; - color: #e0e0e0; - } - - #status-card-consulta-cancelado, - .legenda-item-cancelado { - background-color: #7f1d1d; - border: 3px solid #dc2626; - color: #e0e0e0; - } - - #status-card-consulta-confirmado, - .legenda-item-confirmado { - background-color: #1e3a8a; - border: 3px solid #2563eb; - color: #e0e0e0; - } - - #status-card-consulta-agendado, - .legenda-item-agendado { - background-color: #78350f; - border: 3px solid #f59e0b; - color: #e0e0e0; - } - - .btns-e-legenda-container { - background-color: #181818; - } - - .container-btns-agenda-fila_esepera { - background-color: #181818; - } - - .btn-fila-espera, - .btn-agenda { - background-color: #2c2c2c; - color: #e0e0e0; - border-bottom: 3px solid #333; - } - - .opc-filaespera-ativo, - .opc-agenda-ativo { - color: #fff; - background-color: #005a9e; - } -} - -/* Estilo para o botão de Editar */ -.btn-edit-custom { - background-color: #FFF3CD; - color: #856404; -} - -/* Estilo para o botão de Excluir (Deletar) */ -.btn-delete-custom { - background-color: #F8D7DA; - color: #721C24; - padding: 10px; -} - -.cardconsulta{ - display:flex; - align-items: center; - flex-direction: row; -} - -.container-botons{ - display: flex; - flex-direction: column; -} - -#tabela-seletor-container { - display: flex; - align-items: center; - justify-content: center; - gap: 12px; - - background-color: #fff; - border-radius: 8px; - padding: 6px 12px; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08); - - font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto; - width: fit-content; - margin: 0 auto; -} - -#tabela-seletor-container p { - margin: 0; - font-size: 23px; - font-weight: 500; - color: #4085f6; - text-align: center; - white-space: nowrap; -} - -#tabela-seletor-container button { - background: transparent; - border: none; - color: #555; - font-size: 20px; + border: 0; + border-bottom: 2px solid transparent; + padding: 12px 24px; + border-radius: 0; + font-weight: 600; + color: #718096; cursor: pointer; - padding: 4px 6px; - border-radius: 6px; - transition: all 0.2s ease-in-out; + margin-bottom: -2px; + transition: color 0.2s, border-color 0.2s; } -#tabela-seletor-container button:hover { - background-color: rgba(0, 0, 0, 0.05); - color: #000; +.btn-fila-espera:hover, .btn-agenda:hover { + color: #2B6CB0; } -#tabela-seletor-container i { - pointer-events: none; +.opc-filaespera-ativo, .opc-agenda-ativo { + color: #4299E1; + background-color: transparent; + border-bottom: 2px solid #4299E1; } - .input-e-dropdown-wrapper { position: relative; - width: 350px; - margin-left: auto; -} - -.busca-atendimento > div { width: 100%; + margin: 0; } -.busca-atendimento input { - /* Garante que o input preencha a largura disponível dentro do seu contêiner */ - width: calc(100% - 40px); /* Exemplo: 100% menos a largura do ícone (aprox. 40px) */ - /* ... outros estilos de borda, padding, etc. do seu input ... */ -} - - -/* 3. O Dropdown: Posicionamento e Estilização */ .dropdown-medicos { - /* POSICIONAMENTO: Faz o dropdown flutuar */ position: absolute; - - /* POSICIONAMENTO: Coloca o topo do dropdown logo abaixo do input */ top: 100%; left: 0; - - /* LARGURA: Essencial. Ocupa 100% do .input-e-dropdown-wrapper, limitando-se a ele. */ width: 100%; - - /* SOBREPOSIÇÃO: Garante que fique acima de outros elementos (como a Fila de Espera) */ z-index: 1000; - - /* ESTILIZAÇÃO: Aparência */ - background-color: #ffffff; /* Fundo branco para não vazar */ - border: 1px solid #ccc; /* Borda leve */ - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Sombra para profundidade */ - border-top: none; /* Deixa o visual mais integrado ao input */ - - /* COMPORTAMENTO: Limite de altura e scroll */ + background-color: #ffffff; + border: 1px solid #ccc; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + border-top: none; max-height: 250px; overflow-y: auto; } +.dropdown-item { padding: 10px 15px; cursor: pointer; } +.dropdown-item:hover { background-color: #f0f0f0; } -/* 4. Estilização de cada item do dropdown */ -.dropdown-item { - padding: 10px 15px; - cursor: pointer; - font-size: 14px; +.calendar-wrapper { + display: flex; + gap: 24px; + background-color: #fff; + border-radius: 12px; + padding: 24px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); + font-family: 'Inter', sans-serif; + margin-top: 20px; +} +.calendar-info-panel { flex: 0 0 300px; border-right: 1px solid #E2E8F0; padding-right: 24px; display: flex; flex-direction: column; } +.info-date-display { background-color: #EDF2F7; border-radius: 8px; padding: 12px; text-align: center; margin-bottom: 16px; } +.info-date-display span { font-weight: 600; color: #718096; text-transform: uppercase; font-size: 0.9rem; } +.info-date-display strong { display: block; font-size: 2.5rem; font-weight: 700; color: #2D3748; } +.info-details { text-align: center; margin-bottom: 24px; } +.info-details h3 { font-size: 1.25rem; font-weight: 600; color: #2D3748; margin: 0; text-transform: capitalize; } +.info-details p { color: #718096; margin: 0; } +.appointments-list { flex-grow: 1; overflow-y: auto; } +.appointments-list h4 { font-size: 1rem; font-weight: 600; color: #4A5568; margin-bottom: 12px; position: sticky; top: 0; background-color: #fff; padding-bottom: 8px; } +.appointment-item { display: flex; gap: 12px; padding: 10px; border-radius: 6px; border-left: 4px solid; margin-bottom: 8px; background-color: #F7FAFC; } +.item-time { font-weight: 600; color: #2B6CB0; } +.item-details { display: flex; flex-direction: column; } +.item-details span { font-weight: 500; color: #2D3748; } +.item-details small { color: #718096; } +.no-appointments-info { text-align: center; padding: 20px; color: #A0AEC0; } +.appointment-item[data-status="confirmed"] { border-color: #4299E1; } +.appointment-item[data-status="completed"] { border-color: #48BB78; } +.appointment-item[data-status="cancelled"] { border-color: #F56565; } +.appointment-item[data-status="agendado"], +.appointment-item[data-status="requested"] { border-color: #ED8936; } +.calendar-main { flex-grow: 1; } +.calendar-controls { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; } +.date-indicator h2 { font-size: 1.5rem; font-weight: 600; color: #2D3748; margin: 0; text-transform: capitalize; } +.nav-buttons { display: flex; gap: 8px; } +.nav-buttons button { padding: 8px 12px; border-radius: 6px; border: 1px solid #CBD5E0; background-color: #fff; font-weight: 600; cursor: pointer; transition: all 0.2s; } +.nav-buttons button:hover { background-color: #EDF2F7; } +.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; } +.day-header { font-weight: 600; color: #718096; text-align: center; padding: 8px 0; font-size: 0.875rem; } +.day-cell { min-height: 110px; border-radius: 8px; border: 1px solid #E2E8F0; padding: 8px; transition: background-color 0.2s, border-color 0.2s; cursor: pointer; position: relative; } +.day-cell span { font-weight: 600; color: #4A5568; } +.day-cell:hover { background-color: #EDF2F7; border-color: #BEE3F8; } +.day-cell.other-month { background-color: #F7FAFC; } +.day-cell.other-month span { color: #A0AEC0; } +.day-cell.today span { background-color: #4299E1; color: #fff; border-radius: 50%; padding: 2px 6px; display: inline-block; } +.day-cell.selected { background-color: #BEE3F8; border-color: #4299E1; } +.appointments-indicator { background-color: #4299E1; color: white; font-size: 0.7rem; font-weight: bold; border-radius: 50%; width: 18px; height: 18px; display: flex; justify-content: center; align-items: center; position: absolute; bottom: 8px; right: 8px; } + +.calendar-legend { + display: flex; + justify-content: flex-end; + gap: 12px; + margin-bottom: 16px; +} + +.legend-item { + padding: 6px 12px; + border-radius: 6px; + font-size: 0.8rem; + font-weight: 600; color: #333; - /* Evita que nomes muito longos quebrem ou saiam da caixa */ - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; } -.dropdown-item:hover { - background-color: #f0f0f0; /* Cor ao passar o mouse */ - color: #007bff; +.legend-item[data-status="completed"] { + background-color: #C6F6D5; + border: 1px solid #9AE6B4; + color: #2F855A; +} +.legend-item[data-status="confirmed"] { + background-color: #EBF8FF; + border: 1px solid #BEE3F8; + color: #3182CE; +} +.legend-item[data-status="agendado"] { + background-color: #FEFCBF; + border: 1px solid #F6E05E; + color: #B7791F; +} +.legend-item[data-status="cancelled"] { + background-color: #FED7D7; + border: 1px solid #FEB2B2; + color: #C53030; } -.input-modal{ - width: 80%; +.appointment-item { + display: flex; + align-items: center; + justify-content: space-between; } -/* Estilo para o botão Excluir nos modais de Agendamento - vermelho escuro */ -.modal-footer .btn-danger, -button.btn.btn-danger { - background-color: #c82333 !important; - border-color: #bd2130 !important; - color: #ffffff !important; +.item-details { + flex-grow: 1; } -.modal-footer .btn-danger:hover, -.modal-footer .btn-danger:focus, -.modal-footer .btn-danger:active, -button.btn.btn-danger:hover, -button.btn.btn-danger:focus, -button.btn.btn-danger:active { - background-color: #a71d2a !important; - border-color: #9c1c28 !important; - color: #ffffff !important; +.appointment-actions { + display: flex; + gap: 8px; } -/* ===== ESTILOS REUTILIZADOS DA TABELA DE PACIENTES ===== */ - -/* Containers e Cards */ -.table-paciente-container { - line-height: 2.5; -} - -.table-paciente-card { - border: none; - box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); -} - -.table-paciente-card .card-header { - background-color: #f8f9fa; - border-bottom: 1px solid #dee2e6; - padding: 1rem 1.25rem; -} - -/* Filtros */ -.table-paciente-filters { - background-color: #f8f9fa; - border: 1px solid #dee2e6; - border-radius: 0.375rem; -} - -.table-paciente-filters h5 { - color: #495057; - font-weight: 600; -} - -/* Tabela */ -.table-paciente-table { - width: 100%; - border-collapse: collapse; -} - -.table-paciente-table th { - background-color: #f8f9fa; - color: #495057; - font-weight: 600; - padding: 15px 8px; - border-bottom: 2px solid #dee2e6; - vertical-align: middle; -} - -.table-paciente-table td { - padding: 15px 8px; - vertical-align: middle; - border-bottom: 1px solid #dee2e6; -} - -.table-paciente-table tbody tr:hover { - background-color: rgba(0, 0, 0, 0.025); - transition: background-color 0.15s ease-in-out; -} - -/* Badges e Contador */ -.contador-pacientes { - background-color: #1e3a8a; - color: white; - padding: 0.5em 0.75em; - font-size: 0.875em; - font-weight: 500; - border-radius: 0.375rem; - text-align: center; - display: inline-block; -} - -.filters-active .badge { - font-size: 0.75em; - padding: 0.4em 0.65em; - margin-bottom: 0.25rem; -} - -/* Botões */ -.btn-view { - background-color: #E6F2FF !important; - color: #004085 !important; - border: 1px solid #B8D4F0; - padding: 0.375rem 0.75rem; - font-size: 0.875rem; - transition: all 0.15s ease-in-out; -} - -.btn-view:hover { - background-color: #D1E7FF !important; - border-color: #9EC5FE; -} - -.btn-edit { - background-color: #FFF3CD !important; - color: #856404 !important; - border: 1px solid #FFEAA7; - padding: 0.375rem 0.75rem; - font-size: 0.875rem; - transition: all 0.15s ease-in-out; -} - -.btn-edit:hover { - background-color: #FFEEBA !important; - border-color: #FFE087; -} - -.btn-delete { - background-color: #F8D7DA !important; - color: #721C24 !important; - border: 1px solid #F5C6CB; - padding: 0.375rem 0.75rem; - font-size: 0.875rem; - transition: all 0.15s ease-in-out; -} - -.btn-delete:hover { - background-color: #F1B0B7 !important; - border-color: #ED969E; -} - -/* Selects compactos */ -.compact-select { - font-size: 1.0rem; - padding: 0.45rem 0.5rem; -} - -.compact-select option { - font-size: 0.875rem; -} - -/* Paginação */ -.pagination { - margin-bottom: 0; -} - -.page-link { - color: #495057; - border: 1px solid #dee2e6; - padding: 0.375rem 0.75rem; - font-size: 0.875rem; -} - -.page-link:hover { - color: #1e3a8a; - background-color: #e9ecef; - border-color: #dee2e6; -} - -.page-item.active .page-link { - background-color: #1e3a8a; - border-color: #1e3a8a; - color: white; -} - -.page-item.disabled .page-link { - color: #6c757d; - background-color: #f8f9fa; - border-color: #dee2e6; -} - -.d-flex.justify-content-between.align-items-center { - border-top: 1px solid #dee2e6; - padding-top: 1rem; - margin-top: 1rem; -} - -/* Empty State */ -.text-center.py-4 .text-muted { - padding: 2rem; -} - -.text-center.py-4 .bi-inbox { - font-size: 3rem; - opacity: 0.5; -} - -.text-center.py-4 p { - margin-bottom: 0.5rem; - font-size: 1.1rem; -} - -.text-center.py-4 td { - border-bottom: none; - padding: 2rem !important; -} - -/* Responsividade */ -@media (max-width: 768px) { - .table-paciente-table { - font-size: 0.875rem; - } - - .table-paciente-table th, - .table-paciente-table td { - padding: 10px 6px; - } - - .btn-view, - .btn-edit, - .btn-delete { - padding: 0.25rem 0.5rem; - font-size: 0.75rem; - } - - .table-paciente-filters .d-flex { - flex-direction: column; - gap: 0.5rem; - } - - .table-paciente-filters .form-select { - min-width: 100% !important; - } - - .d-flex.justify-content-between.align-items-center { - flex-direction: column; - gap: 1rem; - align-items: stretch !important; - } - - .d-flex.justify-content-between.align-items-center > div { - justify-content: center !important; - } - - .pagination { - flex-wrap: wrap; +.btn-action { + padding: 8px; + border-radius: 8px; + border: none; + cursor: pointer; + display: flex; + align-items: center; justify-content: center; - } - - .me-3.text-muted { - text-align: center; - margin-bottom: 0.5rem; - font-size: 0.8rem; - } - - .contador-pacientes { - font-size: 0.8rem; - padding: 0.4em 0.6em; - } + transition: background-color 0.2s; +} + +.btn-action.btn-edit { + background-color: #FEFCBF; + color: #B7791F; +} +.btn-action.btn-edit:hover { + background-color: #F6E05E; +} + +.btn-action.btn-delete { + background-color: #E53E3E; + color: #fff; +} +.btn-action.btn-delete:hover { + background-color: #C53030; } From 981c4bac6e345a8b637b091c4c94957d787b80b3 Mon Sep 17 00:00:00 2001 From: joao_pedro Date: Thu, 13 Nov 2025 08:33:29 -0300 Subject: [PATCH 13/19] =?UTF-8?q?bug:=20solu=C3=A7=C3=A3o=20do=20edit=20pa?= =?UTF-8?q?ra=20o=20paciente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/DoctorDetails.jsx | 24 ++------- src/pages/DoctorEditPage.jsx | 40 +++------------ src/pages/DoctorTable.jsx | 12 ++--- src/pages/EditPage.jsx | 50 +++---------------- src/perfis/Perfil_adm/Perfiladm.jsx | 21 ++++---- .../perfil_secretaria/PerfilSecretaria.jsx | 6 +-- 6 files changed, 41 insertions(+), 112 deletions(-) diff --git a/src/pages/DoctorDetails.jsx b/src/pages/DoctorDetails.jsx index 9a6a179..e426f75 100644 --- a/src/pages/DoctorDetails.jsx +++ b/src/pages/DoctorDetails.jsx @@ -4,36 +4,22 @@ import { useParams,Link, useNavigate, useLocation } from "react-router-dom"; import { GetDoctorByID } from "../components/utils/Functions-Endpoints/Doctor"; import { useAuth } from "../components/utils/AuthProvider"; -const Details = () => { +const DoctorDetails = ({doctor}) => { const {getAuthorizationHeader} = useAuth(); - const [doctor, setDoctor] = useState({}); const Parametros = useParams() const navigate = useNavigate(); const location = useLocation(); + - const Voltar = () => { +const Voltar = () => { const prefixo = location.pathname.split("/")[1]; navigate(`/${prefixo}/medicos`); } - const doctorID = Parametros.id - useEffect(() => { - if (!doctorID) return; - - const authHeader = getAuthorizationHeader() - - GetDoctorByID(doctorID, authHeader) - .then((data) => { - console.log(data, "médico vindo da API"); - setDoctor(data[0]) - ; // supabase retorna array - }) - .catch((err) => console.error("Erro ao buscar paciente:", err)); - }, [doctorID]); - //if (!doctor) return

    Carregando...

    ; + return ( <> @@ -156,4 +142,4 @@ const Details = () => { ); }; -export default Details; \ No newline at end of file +export default DoctorDetails; diff --git a/src/pages/DoctorEditPage.jsx b/src/pages/DoctorEditPage.jsx index 4115823..1b75ffa 100644 --- a/src/pages/DoctorEditPage.jsx +++ b/src/pages/DoctorEditPage.jsx @@ -8,49 +8,23 @@ import API_KEY from "../components/utils/apiKeys"; const ENDPOINT_AVAILABILITY = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability"; -const DoctorEditPage = () => { +const DoctorEditPage = ({DictInfo}) => { const { getAuthorizationHeader } = useAuth(); const [DoctorToPUT, setDoctorPUT] = useState({}); const Parametros = useParams(); const [searchParams] = useSearchParams(); - const DoctorID = Parametros.id; + + const DoctorID = "b24c88b2-1d51-4c04-8fe8-e75c3f2817d1"; const availabilityId = searchParams.get("availabilityId"); const [availabilityToPATCH, setAvailabilityToPATCH] = useState(null); const [mode, setMode] = useState("doctor"); + console.log("teste", DictInfo) - useEffect(() => { - const authHeader = getAuthorizationHeader(); - - if (availabilityId) { - setMode("availability"); - - fetch(`${ENDPOINT_AVAILABILITY}?id=eq.${availabilityId}&select=*`, { - method: "GET", - headers: { - apikey: API_KEY, - Authorization: authHeader, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (data && data.length > 0) { - setAvailabilityToPATCH(data[0]); - console.log("Disponibilidade vinda da API:", data[0]); - } - }) - .catch((err) => console.error("Erro ao buscar disponibilidade:", err)); - } else { - setMode("doctor"); - GetDoctorByID(DoctorID, authHeader) - .then((data) => { - console.log(data, "médico vindo da API"); - setDoctorPUT(data[0]); - }) - .catch((err) => console.error("Erro ao buscar paciente:", err)); - } - }, [DoctorID, availabilityId, getAuthorizationHeader]); + useEffect(() => { + setDoctorPUT(DictInfo) + }, [DictInfo]); const HandlePutDoctor = async () => { const authHeader = getAuthorizationHeader(); diff --git a/src/pages/DoctorTable.jsx b/src/pages/DoctorTable.jsx index e5c2d76..cdcce24 100644 --- a/src/pages/DoctorTable.jsx +++ b/src/pages/DoctorTable.jsx @@ -4,7 +4,7 @@ import { useAuth } from "../components/utils/AuthProvider"; import { Link } from "react-router-dom"; import "./style/TableDoctor.css"; -function TableDoctor() { +function TableDoctor({setDictInfo}) { const { getAuthorizationHeader, isAuthenticated } = useAuth(); const [medicos, setMedicos] = useState([]); @@ -437,14 +437,14 @@ function TableDoctor() { {medico.email || 'Não informado'}
    - - - - @@ -596,4 +596,4 @@ function TableDoctor() { ); } -export default TableDoctor; \ No newline at end of file +export default TableDoctor; diff --git a/src/pages/EditPage.jsx b/src/pages/EditPage.jsx index 35a33fa..82f1008 100644 --- a/src/pages/EditPage.jsx +++ b/src/pages/EditPage.jsx @@ -1,40 +1,21 @@ import React from 'react' - import PatientForm from '../components/patients/PatientForm' - import {useEffect, useState} from 'react' import { GetPatientByID } from '../components/utils/Functions-Endpoints/Patient' import API_KEY from '../components/utils/apiKeys' import {useNavigate, useParams } from 'react-router-dom' import { useAuth } from '../components/utils/AuthProvider' -const EditPage = (DictInfo) => { +const EditPage = ({DictInfo}) => { const navigate = useNavigate() - const Parametros = useParams() const [PatientToPUT, setPatientPUT] = useState({}) const { getAuthorizationHeader, isAuthenticated } = useAuth(); -console.log(DictInfo, "usuario vindo do set") -const PatientID = Parametros.id - - useEffect(() => { - const authHeader = getAuthorizationHeader() - console.log(DictInfo.DictInfo.id, "id do cabra") - - GetPatientByID(DictInfo.DictInfo.id, authHeader) - .then((data) => { - console.log(data[0], "paciente vindo da API"); - setPatientPUT(data[0]); // supabase retorna array - }) - .catch((err) => console.error("Erro ao buscar paciente:", err)); - - - + setPatientPUT(DictInfo) }, [DictInfo]) - const HandlePutPatient = async () => { const authHeader = getAuthorizationHeader() @@ -44,9 +25,9 @@ const HandlePutPatient = async () => { myHeaders.append("Authorization", authHeader); myHeaders.append("Content-Type", "application/json"); - var raw = JSON.stringify(PatientToPUT); + var raw = JSON.stringify({...PatientToPUT, bmi:Number(PatientToPUT) || null}); - console.log("Enviando paciente para atualização:", PatientToPUT); + console.log("Enviando atualização:", PatientToPUT); var requestOptions = { method: 'PATCH', @@ -55,26 +36,11 @@ const HandlePutPatient = async () => { redirect: 'follow' }; - try { - const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients?id=eq.${PatientID}`,requestOptions); - console.log(response) - + fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients?id=eq.${PatientToPUT.id}`,requestOptions) + .then(response => response.json) + .then(result => console.log(result)) + .catch(console.log("erro")) - if(response.ok === false){ - console.error("Erro ao atualizar paciente:"); - } - else{ - console.log("ATUALIZADO COM SUCESSO"); - navigate('/secretaria/pacientes') - } - - return response; - } catch (error) { - console.error("Erro ao atualizar paciente:", error); - throw error; - } - - }; return ( diff --git a/src/perfis/Perfil_adm/Perfiladm.jsx b/src/perfis/Perfil_adm/Perfiladm.jsx index 2632fbe..73496df 100644 --- a/src/perfis/Perfil_adm/Perfiladm.jsx +++ b/src/perfis/Perfil_adm/Perfiladm.jsx @@ -14,11 +14,14 @@ import DoctorEditPage from "../../pages/DoctorEditPage"; import UserDashboard from '../../PagesAdm/gestao.jsx'; import PainelAdministrativo from '../../PagesAdm/painel.jsx'; import admItems from "../../data/sidebar-items-adm.json"; - +import {useState} from "react" // ...restante do código... function Perfiladm() { - return ( + + const [DictInfo, setDictInfo] = useState({}) + + return (
    @@ -27,12 +30,12 @@ function Perfiladm() { } /> } /> } /> - } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> } /> } /> @@ -46,4 +49,4 @@ function Perfiladm() { ); } -export default Perfiladm; \ No newline at end of file +export default Perfiladm; diff --git a/src/perfis/perfil_secretaria/PerfilSecretaria.jsx b/src/perfis/perfil_secretaria/PerfilSecretaria.jsx index 514c29e..f76b9bc 100644 --- a/src/perfis/perfil_secretaria/PerfilSecretaria.jsx +++ b/src/perfis/perfil_secretaria/PerfilSecretaria.jsx @@ -31,11 +31,11 @@ function PerfilSecretaria({ onLogout }) { } /> } /> } /> - } /> + } /> } /> } /> - } /> - } /> + } /> + } /> } /> } /> } /> From bff5c42a4e379c916844be1b6198c2131bdb45d0 Mon Sep 17 00:00:00 2001 From: Caio Miguel Lima Nunes Date: Thu, 13 Nov 2025 09:30:03 -0300 Subject: [PATCH 14/19] Paciente-inicio --- src/data/sidebar-items-paciente.json | 10 +- src/pages/Inicio.jsx | 185 ++++++- src/pages/inicioPaciente.jsx | 273 +++++++++++ src/pages/style/Inicio.css | 188 ++++++++ src/pages/style/inicioPaciente.css | 454 ++++++++++++++++++ src/perfis/Perfil_paciente/Perfilpaciente.jsx | 3 +- 6 files changed, 1099 insertions(+), 14 deletions(-) create mode 100644 src/pages/inicioPaciente.jsx create mode 100644 src/pages/style/inicioPaciente.css diff --git a/src/data/sidebar-items-paciente.json b/src/data/sidebar-items-paciente.json index bb15caf..d0442e4 100644 --- a/src/data/sidebar-items-paciente.json +++ b/src/data/sidebar-items-paciente.json @@ -1,13 +1,17 @@ [ -{ + { + "name": "Início", + "icon": "house-fill", + "url": "/paciente" + }, + { "name": "Minhas consulta", "icon": "calendar-plus-fill", "url": "/paciente/agendamento" }, - { "name": "Meus laudos", "icon": "table", "url": "/paciente/laudo" } -] +] \ No newline at end of file diff --git a/src/pages/Inicio.jsx b/src/pages/Inicio.jsx index 7bdbe78..1db9708 100644 --- a/src/pages/Inicio.jsx +++ b/src/pages/Inicio.jsx @@ -1,6 +1,8 @@ import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { FaUser, FaUserPlus, FaCalendarAlt, FaCalendarCheck } from 'react-icons/fa'; +import { useAuth } from '../components/utils/AuthProvider'; +import API_KEY from '../components/utils/apiKeys'; import './style/Inicio.css'; import { Link } from 'react-router-dom'; @@ -8,18 +10,141 @@ import { Link } from 'react-router-dom'; function Inicio() { const navigate = useNavigate(); + const { getAuthorizationHeader, isAuthenticated } = useAuth(); const [pacientes, setPacientes] = useState([]); + const [medicos, setMedicos] = useState([]); const [agendamentos, setAgendamentos] = useState([]); + const [agendamentosComPacientes, setAgendamentosComPacientes] = useState([]); + const [loading, setLoading] = useState(true); + useEffect(() => { + const fetchPacientes = async () => { + try { + const authHeader = getAuthorizationHeader(); + + const myHeaders = new Headers(); + myHeaders.append("apikey", API_KEY); + myHeaders.append("Authorization", authHeader); + + const requestOptions = { + method: 'GET', + headers: myHeaders, + redirect: 'follow' + }; + + const response = await fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients", requestOptions); + + if (response.ok) { + const data = await response.json(); + setPacientes(data); + console.log('Pacientes carregados:', data.length); + } else { + console.error(' Erro ao buscar pacientes:', response.status); + } + } catch (error) { + console.error(' Erro ao buscar pacientes:', error); + } + }; + + const fetchMedicos = async () => { + try { + const authHeader = getAuthorizationHeader(); + + const myHeaders = new Headers(); + myHeaders.append("apikey", API_KEY); + myHeaders.append("Authorization", authHeader); + + const requestOptions = { + method: 'GET', + headers: myHeaders, + redirect: 'follow' + }; + + const response = await fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors", requestOptions); + + if (response.ok) { + const data = await response.json(); + setMedicos(data); + console.log(' Médicos carregados:', data.length); + } else { + console.error('Erro ao buscar médicos:', response.status); + } + } catch (error) { + console.error(' Erro ao buscar médicos:', error); + } + }; + + const fetchAgendamentos = async () => { + try { + const authHeader = getAuthorizationHeader(); + + const myHeaders = new Headers(); + myHeaders.append("apikey", API_KEY); + myHeaders.append("Authorization", authHeader); + + const requestOptions = { + method: 'GET', + headers: myHeaders, + redirect: 'follow' + }; + + const response = await fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments", requestOptions); + + if (response.ok) { + const data = await response.json(); + setAgendamentos(data); + console.log(' Agendamentos carregados:', data.length); + } else { + console.error(' Erro ao buscar agendamentos:', response.status); + } + } catch (error) { + console.error(' Erro ao buscar agendamentos:', error); + } finally { + setLoading(false); + } + }; + + if (isAuthenticated) { + fetchPacientes(); + fetchMedicos(); + fetchAgendamentos(); + } + }, [isAuthenticated, getAuthorizationHeader]); + + + useEffect(() => { + if (agendamentos.length > 0 && pacientes.length > 0 && medicos.length > 0) { + const agendamentosComNomes = agendamentos.map(agendamento => { + const paciente = pacientes.find(p => p.id === agendamento.patient_id); + const medico = medicos.find(m => m.id === agendamento.doctor_id); + return { + ...agendamento, + nomePaciente: paciente?.full_name || 'Paciente não encontrado', + nomeMedico: medico?.full_name || 'Médico não encontrado', + especialidadeMedico: medico?.specialty || '' + }; + }); + setAgendamentosComPacientes(agendamentosComNomes); + } + }, [agendamentos, pacientes, medicos]); const totalPacientes = pacientes.length; - const novosEsseMes = pacientes.filter(p => p.createdAt && new Date(p.createdAt).getMonth() === new Date().getMonth()).length; + const novosEsseMes = pacientes.filter(p => p.created_at && new Date(p.created_at).getMonth() === new Date().getMonth()).length; const hoje = new Date(); - const agendamentosDoDia = agendamentos.filter( - a => a.data && new Date(a.data).getDate() === hoje.getDate() - ); + hoje.setHours(0, 0, 0, 0); + + const agendamentosDoDia = agendamentosComPacientes.filter(a => { + if (!a.scheduled_at) return false; + const dataAgendamento = new Date(a.scheduled_at); + dataAgendamento.setHours(0, 0, 0, 0); + return dataAgendamento.getTime() === hoje.getTime(); + }); + const agendamentosHoje = agendamentosDoDia.length; + + + const pendencias = agendamentos.filter(a => a.status === 'pending' || a.status === 'scheduled').length; return (
    @@ -57,7 +182,7 @@ function Inicio() {
    PENDÊNCIAS - 0 + {loading ? '...' : pendencias}
    @@ -92,14 +217,54 @@ function Inicio() {

    Próximos Agendamentos

    - {agendamentosHoje > 0 ? ( -
    - {agendamentosDoDia.map(agendamento => ( + {loading ? ( +
    +

    Carregando agendamentos...

    +
    + ) : agendamentosHoje > 0 ? ( +
    + {agendamentosDoDia.slice(0, 5).map(agendamento => (
    -

    {agendamento.nomePaciente}

    -

    {new Date(agendamento.data).toLocaleTimeString()}

    +
    +
    +

    + {new Date(agendamento.scheduled_at).toLocaleTimeString('pt-BR', { + hour: '2-digit', + minute: '2-digit' + })} +

    +

    + {new Date(agendamento.scheduled_at).toLocaleDateString('pt-BR', { + day: '2-digit', + month: '2-digit', + year: 'numeric' + })} +

    +
    +
    +

    + Paciente: {agendamento.nomePaciente} +

    +

    + Dr(a): {agendamento.nomeMedico} + {agendamento.especialidadeMedico && ` - ${agendamento.especialidadeMedico}`} +

    +
    + + {agendamento.status === 'scheduled' ? 'Agendado' : + agendamento.status === 'completed' ? 'Concluído' : + agendamento.status === 'pending' ? 'Pendente' : + agendamento.status === 'cancelled' ? 'Cancelado' : + agendamento.status === 'requested' ? '' : agendamento.status} + +
    ))} + {agendamentosHoje > 5 && ( + + )}
    ) : (
    diff --git a/src/pages/inicioPaciente.jsx b/src/pages/inicioPaciente.jsx new file mode 100644 index 0000000..9e95061 --- /dev/null +++ b/src/pages/inicioPaciente.jsx @@ -0,0 +1,273 @@ +import React, { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { FaCalendarAlt, FaCalendarCheck, FaFileAlt, FaUserMd, FaClock } from 'react-icons/fa'; +import { useAuth } from '../components/utils/AuthProvider'; +import API_KEY from '../components/utils/apiKeys'; +import './style/inicioPaciente.css'; + +function InicioPaciente() { + const navigate = useNavigate(); + const { getAuthorizationHeader, isAuthenticated } = useAuth(); + const [agendamentos, setAgendamentos] = useState([]); + const [medicos, setMedicos] = useState([]); + const [agendamentosComMedicos, setAgendamentosComMedicos] = useState([]); + const [loading, setLoading] = useState(true); + const [pacienteId, setPacienteId] = useState(null); + + useEffect(() => { + const userId = localStorage.getItem('user_id') || localStorage.getItem('patient_id'); + setPacienteId(userId); + }, []); + + useEffect(() => { + const fetchMedicos = async () => { + try { + const authHeader = getAuthorizationHeader(); + + const myHeaders = new Headers(); + myHeaders.append("apikey", API_KEY); + myHeaders.append("Authorization", authHeader); + + const requestOptions = { + method: 'GET', + headers: myHeaders, + redirect: 'follow' + }; + + const response = await fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors", requestOptions); + + if (response.ok) { + const data = await response.json(); + setMedicos(data); + console.log(' Médicos carregados:', data.length); + } else { + console.error(' Erro ao buscar médicos:', response.status); + } + } catch (error) { + console.error(' Erro ao buscar médicos:', error); + } + }; + + const fetchAgendamentos = async () => { + try { + const authHeader = getAuthorizationHeader(); + + const myHeaders = new Headers(); + myHeaders.append("apikey", API_KEY); + myHeaders.append("Authorization", authHeader); + + const requestOptions = { + method: 'GET', + headers: myHeaders, + redirect: 'follow' + }; + + // Buscar todos os agendamentos (depois filtraremos pelo paciente) + const response = await fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments", requestOptions); + + if (response.ok) { + const data = await response.json(); + setAgendamentos(data); + console.log(' Agendamentos carregados:', data.length); + } else { + console.error(' Erro ao buscar agendamentos:', response.status); + } + } catch (error) { + console.error(' Erro ao buscar agendamentos:', error); + } finally { + setLoading(false); + } + }; + + if (isAuthenticated) { + fetchMedicos(); + fetchAgendamentos(); + } + }, [isAuthenticated, getAuthorizationHeader]); + + useEffect(() => { + if (agendamentos.length > 0 && medicos.length > 0) { + const agendamentosComNomes = agendamentos.map(agendamento => { + const medico = medicos.find(m => m.id === agendamento.doctor_id); + return { + ...agendamento, + nomeMedico: medico?.full_name || 'Médico não encontrado', + especialidadeMedico: medico?.specialty || '' + }; + }); + setAgendamentosComMedicos(agendamentosComNomes); + } + }, [agendamentos, medicos]); + + const meusAgendamentos = agendamentosComMedicos.filter(a => + pacienteId ? a.patient_id === pacienteId : true + ); + + const hoje = new Date(); + hoje.setHours(0, 0, 0, 0); + + const agendamentosFuturos = meusAgendamentos.filter(a => { + if (!a.scheduled_at) return false; + const dataAgendamento = new Date(a.scheduled_at); + return dataAgendamento >= hoje && a.status !== 'cancelled' && a.status !== 'completed'; + }).sort((a, b) => new Date(a.scheduled_at) - new Date(b.scheduled_at)); + + const proximasConsultas = agendamentosFuturos.length; + const consultasHoje = agendamentosFuturos.filter(a => { + const dataAgendamento = new Date(a.scheduled_at); + dataAgendamento.setHours(0, 0, 0, 0); + return dataAgendamento.getTime() === hoje.getTime(); + }).length; + + const consultasPendentes = meusAgendamentos.filter(a => + a.status === 'pending' || a.status === 'requested' + ).length; + + const historicoConsultas = meusAgendamentos.filter(a => + a.status === 'completed' + ).length; + + return ( +
    +
    +

    Bem-vindo ao MediConnect

    +

    Gerencie suas consultas e acompanhe seu histórico médico

    +
    + +
    +
    +
    + Próximas Consultas + {proximasConsultas} +
    +
    + +
    +
    + +
    +
    + Consultas Hoje + {consultasHoje} +
    +
    + +
    +
    + +
    +
    + Aguardando + {loading ? '...' : consultasPendentes} +
    +
    + +
    +
    + +
    +
    + Realizadas + {historicoConsultas} +
    +
    + +
    +
    +
    + +
    +

    Acesso Rápido

    +
    +
    navigate('/paciente/agendamento')}> + +
    + Minhas Consultas + Ver todos os agendamentos +
    +
    + +
    navigate('/paciente/laudo')}> + +
    + Meus Laudos + Acessar documentos médicos +
    +
    + +
    navigate('/paciente/agendamento')}> + +
    + Meus Médicos + Ver histórico de atendimentos +
    +
    +
    +
    + +
    +

    Próximas Consultas

    + {loading ? ( +
    +

    Carregando suas consultas...

    +
    + ) : agendamentosFuturos.length > 0 ? ( +
    + {agendamentosFuturos.slice(0, 3).map(agendamento => ( +
    +
    +
    +

    + {new Date(agendamento.scheduled_at).toLocaleTimeString('pt-BR', { + hour: '2-digit', + minute: '2-digit' + })} +

    +

    + {new Date(agendamento.scheduled_at).toLocaleDateString('pt-BR', { + day: '2-digit', + month: 'short', + year: 'numeric' + })} +

    +
    +
    +

    + + Dr(a): {agendamento.nomeMedico} +

    + {agendamento.especialidadeMedico && ( +

    + {agendamento.especialidadeMedico} +

    + )} +
    + + {agendamento.status === 'scheduled' ? 'Confirmado' : + agendamento.status === 'pending' ? 'Aguardando' : + agendamento.status === 'requested' ? 'Solicitado' : agendamento.status} + +
    +
    + ))} + {agendamentosFuturos.length > 3 && ( + + )} +
    + ) : ( +
    + +

    Você não tem consultas agendadas

    + +
    + )} +
    +
    + ); +} + +export default InicioPaciente; \ No newline at end of file diff --git a/src/pages/style/Inicio.css b/src/pages/style/Inicio.css index 889e7bf..21e4058 100644 --- a/src/pages/style/Inicio.css +++ b/src/pages/style/Inicio.css @@ -228,4 +228,192 @@ html[data-bs-theme="dark"] .manage-button { html[data-bs-theme="dark"] .manage-button:hover { background-color: #2323b0; +} + +/* Lista de Agendamentos */ +.agendamentos-list { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.agendamento-item { + background-color: #f9fafb; + border-left: 4px solid #5d5dff; + border-radius: 8px; + padding: 1rem 1.25rem; + transition: all 0.2s ease; +} + +.agendamento-item:hover { + background-color: #f0f2f5; + transform: translateX(5px); +} + +.agendamento-info { + display: flex; + align-items: center; + gap: 1.5rem; + flex-wrap: wrap; +} + +.agendamento-time-date { + display: flex; + flex-direction: column; + align-items: center; + min-width: 90px; +} + +.agendamento-hora { + font-size: 1.3rem; + font-weight: 700; + color: #5d5dff; + margin: 0; + line-height: 1.2; +} + +.agendamento-data { + font-size: 0.75rem; + font-weight: 500; + color: #888; + margin: 0; + margin-top: 0.25rem; +} + +.agendamento-detalhes { + display: flex; + flex-direction: column; + gap: 0.5rem; + flex: 1; + min-width: 300px; +} + +.agendamento-paciente, +.agendamento-medico { + font-size: 0.95rem; + color: #444; + margin: 0; + line-height: 1.4; +} + +.agendamento-paciente strong, +.agendamento-medico strong { + font-weight: 600; + color: #333; +} + +.agendamento-status { + font-size: 0.75rem; + font-weight: 600; + padding: 0.4rem 0.8rem; + border-radius: 20px; + text-transform: uppercase; +} + +.agendamento-status.status-scheduled { + background-color: #e3f2fd; + color: #1976d2; +} + +.agendamento-status.status-completed { + background-color: #e8f5e9; + color: #388e3c; +} + +.agendamento-status.status-pending { + background-color: #fff3e0; + color: #f57c00; +} + +.agendamento-status.status-cancelled { + background-color: #ffebee; + color: #d32f2f; +} + +.agendamento-status.status-requested { + background-color: #f3e5f5; + color: #7b1fa2; +} + +.view-all-button { + width: 100%; + margin-top: 1rem; + background-color: #f0f2f5; + color: #5d5dff; + border: 2px solid #5d5dff; + border-radius: 8px; + padding: 0.75rem 1.5rem; + font-size: 0.9rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; +} + +.view-all-button:hover { + background-color: #5d5dff; + color: #fff; +} + +/* Dark Mode - Agendamentos */ +html[data-bs-theme="dark"] .agendamento-item { + background-color: #2a2a2a; + border-left-color: #6c6cff; +} + +html[data-bs-theme="dark"] .agendamento-item:hover { + background-color: #333; +} + +html[data-bs-theme="dark"] .agendamento-hora { + color: #8888ff; +} + +html[data-bs-theme="dark"] .agendamento-data { + color: #999; +} + +html[data-bs-theme="dark"] .agendamento-paciente, +html[data-bs-theme="dark"] .agendamento-medico { + color: #d0d0d0; +} + +html[data-bs-theme="dark"] .agendamento-paciente strong, +html[data-bs-theme="dark"] .agendamento-medico strong { + color: #e0e0e0; +} + +html[data-bs-theme="dark"] .agendamento-status.status-scheduled { + background-color: #1a3a52; + color: #64b5f6; +} + +html[data-bs-theme="dark"] .agendamento-status.status-completed { + background-color: #1b3a1f; + color: #81c784; +} + +html[data-bs-theme="dark"] .agendamento-status.status-pending { + background-color: #3d2817; + color: #ffb74d; +} + +html[data-bs-theme="dark"] .agendamento-status.status-cancelled { + background-color: #3d1f1f; + color: #e57373; +} + +html[data-bs-theme="dark"] .agendamento-status.status-requested { + background-color: #2d1f3d; + color: #ba68c8; +} + +html[data-bs-theme="dark"] .view-all-button { + background-color: #2a2a2a; + color: #8888ff; + border-color: #6c6cff; +} + +html[data-bs-theme="dark"] .view-all-button:hover { + background-color: #6c6cff; + color: #fff; } \ No newline at end of file diff --git a/src/pages/style/inicioPaciente.css b/src/pages/style/inicioPaciente.css new file mode 100644 index 0000000..3990267 --- /dev/null +++ b/src/pages/style/inicioPaciente.css @@ -0,0 +1,454 @@ +.dashboard-paciente-container { + padding: 2rem; + background-color: #f7f9fc; + flex-grow: 1; + min-height: 100vh; +} + +/* Header - Paciente */ +.dashboard-paciente-header { + margin-bottom: 2rem; +} + +.dashboard-paciente-header h1 { + font-size: 2rem; + font-weight: 600; + color: #333; + margin-bottom: 0.5rem; +} + +.dashboard-paciente-header p { + font-size: 1rem; + color: #666; +} + +/* Estatísticas - Paciente */ +.stats-paciente-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 1.5rem; + margin-bottom: 2.5rem; +} + +.stat-paciente-card { + background-color: #fff; + border-radius: 12px; + padding: 1.5rem; + display: flex; + justify-content: space-between; + align-items: center; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05); + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +.stat-paciente-card:hover { + transform: translateY(-3px); + box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1); +} + +.stat-paciente-info { + display: flex; + flex-direction: column; +} + +.stat-paciente-label { + font-size: 0.75rem; + font-weight: 600; + color: #888; + margin-bottom: 0.5rem; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.stat-paciente-value { + font-size: 2.2rem; + font-weight: 700; + color: #444; +} + +.stat-paciente-icon-wrapper { + width: 55px; + height: 55px; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; +} + +.stat-paciente-icon { + font-size: 1.4rem; + color: #fff; +} + +/* Cores dos ícones - Paciente */ +.stat-paciente-icon-wrapper.blue { background-color: #5d5dff; } +.stat-paciente-icon-wrapper.green { background-color: #30d158; } +.stat-paciente-icon-wrapper.purple { background-color: #a272ff; } +.stat-paciente-icon-wrapper.orange { background-color: #f1952e; } + +/* Ações Rápidas - Paciente */ +.quick-actions-paciente h2 { + font-size: 1.3rem; + font-weight: 600; + color: #333; + margin-bottom: 1.5rem; +} + +.actions-paciente-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 1.5rem; + margin-bottom: 2.5rem; +} + +.action-paciente-button { + background-color: #fff; + border-radius: 12px; + padding: 1.5rem; + display: flex; + align-items: center; + cursor: pointer; + transition: all 0.2s ease-in-out; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05); +} + +.action-paciente-button:hover { + transform: translateY(-5px); + box-shadow: 0 6px 15px rgba(0, 0, 0, 0.12); +} + +.action-paciente-icon { + font-size: 2.5rem; + margin-right: 1.2rem; + color: #5d5dff; +} + +.action-paciente-info { + display: flex; + flex-direction: column; +} + +.action-paciente-title { + font-size: 1.05rem; + font-weight: 600; + color: #444; + margin-bottom: 0.25rem; +} + +.action-paciente-desc { + font-size: 0.85rem; + color: #888; +} + +/* Próximas Consultas - Paciente */ +.proximas-consultas-section { + background-color: #fff; + border-radius: 12px; + padding: 2rem; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05); +} + +.proximas-consultas-section h2 { + font-size: 1.3rem; + font-weight: 600; + color: #333; + margin-bottom: 1.5rem; +} + +/* Lista de Consultas - Paciente */ +.consultas-paciente-list { + display: flex; + flex-direction: column; + gap: 1.2rem; +} + +.consulta-paciente-item { + background: linear-gradient(135deg, #f9fafb 0%, #ffffff 100%); + border-left: 5px solid #5d5dff; + border-radius: 10px; + padding: 1.25rem 1.5rem; + transition: all 0.3s ease; +} + +.consulta-paciente-item:hover { + background: linear-gradient(135deg, #f0f2f5 0%, #fafbfc 100%); + transform: translateX(8px); + box-shadow: 0 4px 12px rgba(93, 93, 255, 0.15); +} + +.consulta-paciente-info { + display: flex; + align-items: center; + gap: 2rem; + flex-wrap: wrap; +} + +.consulta-paciente-time-date { + display: flex; + flex-direction: column; + align-items: center; + min-width: 90px; + padding: 0.5rem; + background-color: #f0f2ff; + border-radius: 8px; +} + +.consulta-paciente-hora { + font-size: 1.5rem; + font-weight: 700; + color: #5d5dff; + margin: 0; + line-height: 1.2; +} + +.consulta-paciente-data { + font-size: 0.8rem; + font-weight: 500; + color: #7777aa; + margin: 0; + margin-top: 0.25rem; + text-transform: capitalize; +} + +.consulta-paciente-detalhes { + display: flex; + flex-direction: column; + gap: 0.5rem; + flex: 1; + min-width: 250px; +} + +.consulta-paciente-medico { + font-size: 1rem; + color: #444; + margin: 0; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.consulta-icon { + color: #5d5dff; + font-size: 1.1rem; +} + +.consulta-paciente-medico strong { + font-weight: 600; + color: #333; +} + +.consulta-paciente-especialidade { + font-size: 0.85rem; + color: #666; + margin: 0; + margin-left: 1.6rem; + font-style: italic; +} + +.consulta-paciente-status { + font-size: 0.75rem; + font-weight: 600; + padding: 0.5rem 1rem; + border-radius: 20px; + text-transform: uppercase; + white-space: nowrap; +} + +.consulta-paciente-status.status-scheduled { + background-color: #e3f2fd; + color: #1976d2; +} + +.consulta-paciente-status.status-pending { + background-color: #fff3e0; + color: #f57c00; +} + +.consulta-paciente-status.status-requested { + background-color: #f3e5f5; + color: #7b1fa2; +} + +/* Sem Consultas */ +.no-consultas-content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 3rem 1rem; +} + +.no-consultas-icon { + font-size: 4rem; + color: #bbb; + margin-bottom: 1.5rem; +} + +.no-consultas-content p { + font-size: 1.1rem; + color: #666; + margin-bottom: 2rem; +} + +.agendar-paciente-button, +.view-all-paciente-button { + background-color: #5d5dff; + color: #fff; + border: none; + border-radius: 8px; + padding: 0.875rem 2rem; + font-size: 1rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; +} + +.agendar-paciente-button:hover, +.view-all-paciente-button:hover { + background-color: #4444ff; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(93, 93, 255, 0.3); +} + +.view-all-paciente-button { + width: 100%; + margin-top: 1rem; + background-color: #f0f2f5; + color: #5d5dff; + border: 2px solid #5d5dff; +} + +.view-all-paciente-button:hover { + background-color: #5d5dff; + color: #fff; +} + +/* Dark Mode - Paciente */ +html[data-bs-theme="dark"] .dashboard-paciente-container { + background-color: #121212; + color: #e0e0e0; +} + +html[data-bs-theme="dark"] .dashboard-paciente-header h1, +html[data-bs-theme="dark"] .dashboard-paciente-header p, +html[data-bs-theme="dark"] .quick-actions-paciente h2, +html[data-bs-theme="dark"] .proximas-consultas-section h2, +html[data-bs-theme="dark"] .action-paciente-title, +html[data-bs-theme="dark"] .stat-paciente-value { + color: #e0e0e0; +} + +html[data-bs-theme="dark"] .stat-paciente-card, +html[data-bs-theme="dark"] .action-paciente-button, +html[data-bs-theme="dark"] .proximas-consultas-section { + background-color: #1e1e1e; + box-shadow: 0 4px 10px rgba(0,0,0,0.3); +} + +html[data-bs-theme="dark"] .stat-paciente-label, +html[data-bs-theme="dark"] .action-paciente-desc, +html[data-bs-theme="dark"] .no-consultas-content p { + color: #b0b0b0; +} + +html[data-bs-theme="dark"] .consulta-paciente-item { + background: linear-gradient(135deg, #2a2a2a 0%, #1e1e1e 100%); + border-left-color: #6c6cff; +} + +html[data-bs-theme="dark"] .consulta-paciente-item:hover { + background: linear-gradient(135deg, #333 0%, #252525 100%); + box-shadow: 0 4px 12px rgba(108, 108, 255, 0.2); +} + +html[data-bs-theme="dark"] .consulta-paciente-time-date { + background-color: #2a2a3a; +} + +html[data-bs-theme="dark"] .consulta-paciente-hora { + color: #8888ff; +} + +html[data-bs-theme="dark"] .consulta-paciente-data { + color: #9999cc; +} + +html[data-bs-theme="dark"] .consulta-paciente-medico, +html[data-bs-theme="dark"] .consulta-paciente-especialidade { + color: #d0d0d0; +} + +html[data-bs-theme="dark"] .consulta-paciente-medico strong { + color: #e0e0e0; +} + +html[data-bs-theme="dark"] .consulta-icon, +html[data-bs-theme="dark"] .action-paciente-icon { + color: #8888ff; +} + +html[data-bs-theme="dark"] .consulta-paciente-status.status-scheduled { + background-color: #1a3a52; + color: #64b5f6; +} + +html[data-bs-theme="dark"] .consulta-paciente-status.status-pending { + background-color: #3d2817; + color: #ffb74d; +} + +html[data-bs-theme="dark"] .consulta-paciente-status.status-requested { + background-color: #2d1f3d; + color: #ba68c8; +} + +html[data-bs-theme="dark"] .no-consultas-icon { + color: #666; +} + +html[data-bs-theme="dark"] .agendar-paciente-button { + background-color: #6c6cff; +} + +html[data-bs-theme="dark"] .agendar-paciente-button:hover { + background-color: #5555dd; +} + +html[data-bs-theme="dark"] .view-all-paciente-button { + background-color: #2a2a2a; + color: #8888ff; + border-color: #6c6cff; +} + +html[data-bs-theme="dark"] .view-all-paciente-button:hover { + background-color: #6c6cff; + color: #fff; +} + +/* Responsivo */ +@media (max-width: 768px) { + .dashboard-paciente-container { + 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; + gap: 1rem; + } + + .consulta-paciente-time-date { + width: 100%; + flex-direction: row; + justify-content: space-around; + } +} \ No newline at end of file diff --git a/src/perfis/Perfil_paciente/Perfilpaciente.jsx b/src/perfis/Perfil_paciente/Perfilpaciente.jsx index 9155987..86ef589 100644 --- a/src/perfis/Perfil_paciente/Perfilpaciente.jsx +++ b/src/perfis/Perfil_paciente/Perfilpaciente.jsx @@ -2,6 +2,7 @@ import { Routes, Route } from "react-router-dom"; import Sidebar from "../../components/Sidebar"; import PacienteItems from "../../data/sidebar-items-paciente.json"; import { useState } from "react"; +import InicioPaciente from "../../pages/inicioPaciente"; import LaudoManager from "../../pages/LaudoManager"; import ConsultaCadastroManager from "../../PagesPaciente/ConsultaCadastroManager"; import ConsultasPaciente from "../../PagesPaciente/ConsultasPaciente"; @@ -16,7 +17,7 @@ const [dadosConsulta, setConsulta] = useState({})
    - } /> + } /> } /> } /> } /> From b64b664621626393863bd367709ca331df3ec00c Mon Sep 17 00:00:00 2001 From: joao_pedro Date: Thu, 13 Nov 2025 10:17:29 -0300 Subject: [PATCH 15/19] =?UTF-8?q?design:=20mudan=C3=A7a=20no=20bot=C3=A3o?= =?UTF-8?q?=20de=20alternar=20no=20agendamento=20=20e=20mudan=C3=A7a=20na?= =?UTF-8?q?=20sidebar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Sidebar.jsx | 35 +++++++++++++------------------- src/components/ToggleSidebar.jsx | 2 +- src/pages/Agendamento.jsx | 17 ++++++++++++---- src/pages/Login.jsx | 5 +++-- 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 6f21395..006a396 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -18,15 +18,23 @@ function Sidebar({ menuItems }) { const [isMobile, setIsMobile] = useState(false); const [showLogoutModal, setShowLogoutModal] = useState(false); const navigate = useNavigate(); - + const [roleUser, setRoleUser] = useState([]) const {getAuthorizationHeader} = useAuth(); const authHeader = getAuthorizationHeader(); + let pathname = window.location.pathname.split("/")[1] +// useEffect para definir quais toggle da sidebar devem aparecer + useEffect(() => { + let teste = localStorage.getItem("roleUser") + setRoleUser(teste) + + }, [authHeader]) + // Detecta se é mobile/tablet @@ -37,14 +45,6 @@ function Sidebar({ menuItems }) { setIsActive(!mobile); }; - const fetchInfoUser = async () => { - const InfoUser = await UserInfos(authHeader); - console.log(InfoUser.roles, "dados") - - setRoleUser(InfoUser.roles) - } - - fetchInfoUser() checkScreenSize(); window.addEventListener("resize", checkScreenSize); @@ -117,13 +117,6 @@ function Sidebar({ menuItems }) { } }; - useEffect(() => { - if(roleUser.includes("admin")){ - console.log("tem") - } - console.log(roleUser) - }, [roleUser]) - const handleLogoutCancel = () => setShowLogoutModal(false); @@ -251,26 +244,26 @@ function Sidebar({ menuItems }) {
      {roleUser.includes("admin") && - + } {roleUser.includes("admin") || roleUser.includes("secretaria") ? - + : null } {roleUser.includes("admin") || roleUser.includes("medico") ? - + :null } {roleUser.includes("admin") || roleUser.includes("financeiro") ? - + :null } {roleUser.includes("admin") || roleUser.includes("paciente") ? - + : null } diff --git a/src/components/ToggleSidebar.jsx b/src/components/ToggleSidebar.jsx index 2065de1..01eca61 100644 --- a/src/components/ToggleSidebar.jsx +++ b/src/components/ToggleSidebar.jsx @@ -136,4 +136,4 @@ const ToggleSidebar = ({ perfil, items, defaultOpen = false }) => { ) } -export default ToggleSidebar \ No newline at end of file +export default ToggleSidebar diff --git a/src/pages/Agendamento.jsx b/src/pages/Agendamento.jsx index dd01ddd..dd874f1 100644 --- a/src/pages/Agendamento.jsx +++ b/src/pages/Agendamento.jsx @@ -260,15 +260,24 @@ const Agendamento = ({ setDictInfo }) => { return (
      -

      Agendar nova consulta

      +

      Gerenciar consultas

      {/* LIMPA O OBJETO DE EDIÇÃO AO CLICAR EM "ADICIONAR" */} - - + : + + + } +
      {!PageNovaConsulta ? ( @@ -443,4 +452,4 @@ const Agendamento = ({ setDictInfo }) => { ) } -export default Agendamento; \ No newline at end of file +export default Agendamento; diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index 831b94a..76a46e9 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -118,7 +118,8 @@ function Login({ onEnterSystem }) { if (data.access_token) { const UserData = await UserInfos(`bearer ${data.access_token}`); console.log(UserData, "Dados do usuário"); - + localStorage.setItem("roleUser", UserData.roles) + if (UserData?.roles?.includes("admin")) { navigate(`/admin/`); } else if (UserData?.roles?.includes("secretaria")) { @@ -131,7 +132,7 @@ function Login({ onEnterSystem }) { navigate(`/paciente/`); } }else{ - console.log("ERROROROROROOR") + console.log("Erro na tentativa de login") setShowCabecalho(true) } } else { From 8879c43fa80e996eab66b21fd0141b1c7d10548e Mon Sep 17 00:00:00 2001 From: GilenoNeto901 Date: Fri, 14 Nov 2025 21:05:01 -0300 Subject: [PATCH 16/19] agendamento resolvido --- src/PagesMedico/DoctorAgendamentoManager.jsx | 483 ++++++++++------- src/PagesPaciente/ConsultasPaciente.jsx | 543 +++++++++++-------- src/pages/Agendamento.jsx | 91 ++-- src/pages/style/FilaEspera.css | 2 +- 4 files changed, 632 insertions(+), 487 deletions(-) diff --git a/src/PagesMedico/DoctorAgendamentoManager.jsx b/src/PagesMedico/DoctorAgendamentoManager.jsx index 4d521a6..7f0d77e 100644 --- a/src/PagesMedico/DoctorAgendamentoManager.jsx +++ b/src/PagesMedico/DoctorAgendamentoManager.jsx @@ -2,38 +2,43 @@ 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 { GetPatientByID } from '../components/utils/Functions-Endpoints/Patient.js'; -import { GetAllDoctors, GetDoctorByID } from '../components/utils/Functions-Endpoints/Doctor.js'; +// Removidos imports não utilizados no novo fluxo +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 { 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'; +import Spinner from '../components/Spinner.jsx'; + dayjs.locale('pt-br'); dayjs.extend(isBetween); dayjs.extend(localeData); -const Agendamento = ({ setDictInfo }) => { + +const Agendamento = () => { const navigate = useNavigate(); + const { getAuthorizationHeader, user } = useAuth(); + const authHeader = getAuthorizationHeader(); + + // ID do médico que você quer visualizar + const ID_MEDICO_ESPECIFICO = "078d2a67-b4c1-43c8-ae32-c1e75bb5b3df"; + const [listaTodosAgendamentos, setListaTodosAgendamentos] = useState([]); const [selectedID, setSelectedId] = useState('0'); const [filaEsperaData, setFilaEsperaData] = useState([]); const [FiladeEspera, setFiladeEspera] = useState(false); const [PageNovaConsulta, setPageConsulta] = useState(false); - const [searchTerm, setSearchTerm] = useState(''); - const { getAuthorizationHeader } = useAuth(); const [DictAgendamentosOrganizados, setAgendamentosOrganizados] = useState({}); const [showDeleteModal, setShowDeleteModal] = useState(false); const [ListaDeMedicos, setListaDeMedicos] = useState([]); const [FiltredTodosMedicos, setFiltredTodosMedicos] = useState([]); const [searchTermDoctor, setSearchTermDoctor] = useState(''); const [MedicoFiltrado, setMedicoFiltrado] = useState({ id: "vazio" }); - const [cacheAgendamentos, setCacheAgendamentos] = useState([]); const [motivoCancelamento, setMotivoCancelamento] = useState(""); const [showSpinner, setShowSpinner] = useState(true); const [waitlistSearch, setWaitlistSearch] = useState(''); @@ -41,38 +46,49 @@ const Agendamento = ({ setDictInfo }) => { const [waitSortDir, setWaitSortDir] = useState('asc'); const [waitPage, setWaitPage] = useState(1); const [waitPerPage, setWaitPerPage] = useState(10); - const authHeader = getAuthorizationHeader(); - const cacheMedicos = useMemo(() => ({}), []); - const cachePacientes = useMemo(() => ({}), []); + const [cacheMedicos, setCacheMedicos] = useState({}); + const [cachePacientes, setCachePacientes] = useState({}); const [currentDate, setCurrentDate] = useState(dayjs()); const [selectedDay, setSelectedDay] = useState(dayjs()); const [agendamentoParaEdicao, setAgendamentoParaEdicao] = useState(null); - const [quickJump, setQuickJump] = useState({ month: currentDate.month(), year: currentDate.year() }); + // ✨ ALTERAÇÃO PRINCIPAL: A busca agora filtra pelo ID do médico direto na API const fetchAppointments = useCallback(async () => { - const myHeaders = new Headers(); myHeaders.append("Authorization", authHeader); myHeaders.append("apikey", API_KEY); + 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' }; + + // A URL agora contém o filtro para o ID do médico específico + const apiUrl = `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?doctor_id=eq.${ID_MEDICO_ESPECIFICO}&select=*`; + try { - const res = await fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?select=*", requestOptions); + const res = await fetch(apiUrl, requestOptions); const data = await res.json(); - setListaTodosAgendamentos(data); + setListaTodosAgendamentos(data || []); } catch (err) { console.error('Erro ao buscar agendamentos', err); + setListaTodosAgendamentos([]); } finally { - if (!listaTodosAgendamentos.length) { - setShowSpinner(false); - } + setShowSpinner(false); } - }, [authHeader, listaTodosAgendamentos.length]); + }, [authHeader, ID_MEDICO_ESPECIFICO]); + const updateAppointmentStatus = useCallback(async (id, updates) => { - const myHeaders = new Headers(); myHeaders.append("Authorization", authHeader); myHeaders.append("apikey", API_KEY); myHeaders.append("Content-Type", "application/json"); myHeaders.append("Prefer", "return=representation"); + setShowSpinner(true); + const myHeaders = new Headers(); + myHeaders.append("Authorization", authHeader); + myHeaders.append("apikey", API_KEY); + myHeaders.append("Content-Type", "application/json"); + myHeaders.append("Prefer", "return=representation"); const requestOptions = { method: 'PATCH', headers: myHeaders, body: JSON.stringify(updates) }; - try { const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${id}`, requestOptions); if (response.ok) { @@ -85,14 +101,14 @@ const Agendamento = ({ setDictInfo }) => { } catch (error) { console.error('Erro de rede/servidor:', error); return false; + } finally { + setShowSpinner(false); } }, [authHeader, fetchAppointments]); + const deleteConsulta = useCallback(async (id) => { - setShowSpinner(true); - const success = await updateAppointmentStatus(id, { status: "cancelled", cancellation_reason: motivoCancelamento, updated_at: new Date().toISOString() }); - setShowSpinner(false); if (success) { setShowDeleteModal(false); setMotivoCancelamento(""); @@ -102,12 +118,9 @@ const Agendamento = ({ setDictInfo }) => { } }, [motivoCancelamento, updateAppointmentStatus]); - + const confirmConsulta = useCallback(async (id) => { - setShowSpinner(true); - const success = await updateAppointmentStatus(id, { status: "agendado", cancellation_reason: null, updated_at: new Date().toISOString() }); - setShowSpinner(false); if (success) { setSelectedId('0'); } else { @@ -115,87 +128,125 @@ const Agendamento = ({ setDictInfo }) => { } }, [updateAppointmentStatus]); - const handleEditConsulta = (agendamento) => { - setAgendamentoParaEdicao(agendamento); - setPageConsulta(true); - }; - - const handleQuickJumpChange = (type, value) => { - setQuickJump(prev => ({ ...prev, [type]: Number(value) })); - }; - - const applyQuickJump = () => { - let newDate = dayjs().year(quickJump.year).month(quickJump.month).date(1); - setCurrentDate(newDate); - setSelectedDay(newDate); - }; + useEffect(() => { + if(authHeader) { + fetchAppointments(); + // A busca de todos os médicos pode continuar caso a secretária precise ver a lista + if (user?.role !== 'doctor') { + GetAllDoctors(authHeader).then(docs => { + if (docs) { + setListaDeMedicos(docs.map(d => ({ nomeMedico: d.full_name, idMedico: d.id }))); + } + }); + } + } + }, [authHeader, fetchAppointments, user?.role]); useEffect(() => { - setQuickJump({ - month: currentDate.month(), - year: currentDate.year() - }); - }, [currentDate]); + const processData = async () => { + // Como os dados já vêm filtrados da API, não precisamos mais da verificação de 'user' aqui + if (!listaTodosAgendamentos.length) { + setAgendamentosOrganizados({}); + setFilaEsperaData([]); + return; + } - useEffect(() => { - if (!listaTodosAgendamentos.length && !showSpinner) { return; } - setShowSpinner(true); + setShowSpinner(true); + + // ✨ SIMPLIFICAÇÃO: Não é mais necessário filtrar por `user.role`, + // pois a API já retornou apenas os agendamentos do médico desejado. + const appointmentsToShow = listaTodosAgendamentos; + + const patientIdsToFetch = new Set(); + const doctorIdsToFetch = new Set(); + + appointmentsToShow.forEach(ag => { + if (ag.patient_id && !cachePacientes[ag.patient_id]) { + patientIdsToFetch.add(ag.patient_id); + } + if (ag.doctor_id && !cacheMedicos[ag.doctor_id]) { + doctorIdsToFetch.add(ag.doctor_id); + } + }); + + const fetchPromises = []; + + if (patientIdsToFetch.size > 0) { + 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()) + ); + } else { + fetchPromises.push(Promise.resolve(null)); // Mantém a ordem do Promise.all + } + + if (doctorIdsToFetch.size > 0) { + const query = `id=in.(${Array.from(doctorIdsToFetch).join(',')})`; + fetchPromises.push( + fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors?${query}&select=id,full_name`, { + headers: { apikey: API_KEY, Authorization: authHeader } + }).then(res => res.json()) + ); + } else { + fetchPromises.push(Promise.resolve(null)); + } + + const [newPatients, newDoctors] = await Promise.all(fetchPromises); + + const updatedPatientCache = { ...cachePacientes }; + if (newPatients) newPatients.forEach(p => updatedPatientCache[p.id] = p); + + const updatedDoctorCache = { ...cacheMedicos }; + if (newDoctors) newDoctors.forEach(d => updatedDoctorCache[d.id] = d); + + setCachePacientes(updatedPatientCache); + setCacheMedicos(updatedDoctorCache); - const fetchDados = async () => { const newDict = {}; const newFila = []; - for (const agendamento of listaTodosAgendamentos) { - if (!agendamento.doctor_id || !agendamento.patient_id) continue; + for (const agendamento of appointmentsToShow) { + const medico = updatedDoctorCache[agendamento.doctor_id]; + const paciente = updatedPatientCache[agendamento.patient_id]; - if (!cacheMedicos[agendamento.doctor_id]) { - const doctorData = await GetDoctorByID(agendamento.doctor_id, authHeader); - cacheMedicos[agendamento.doctor_id] = doctorData[0]; - } - if (!cachePacientes[agendamento.patient_id]) { - const patientData = await GetPatientByID(agendamento.patient_id, authHeader); - cachePacientes[agendamento.patient_id] = patientData[0]; - } + if (!medico || !paciente) continue; - const medico = cacheMedicos[agendamento.doctor_id]; - const paciente = cachePacientes[agendamento.patient_id]; const agendamentoMelhorado = { ...agendamento, - medico_nome: medico?.full_name, - paciente_nome: paciente?.full_name, - paciente_cpf: paciente?.cpf + medico_nome: medico.full_name || 'N/A', + paciente_nome: paciente.full_name || 'N/A', + paciente_cpf: paciente.cpf || 'N/A' }; if (agendamento.status === "requested") { - newFila.push({ - agendamento: agendamentoMelhorado, - Infos: agendamentoMelhorado - }); + newFila.push({ agendamento: agendamentoMelhorado, Infos: agendamentoMelhorado }); } else { const DiaAgendamento = dayjs(agendamento.scheduled_at).format("YYYY-MM-DD"); - if (newDict[DiaAgendamento]) { - newDict[DiaAgendamento].push(agendamentoMelhorado); - } else { - newDict[DiaAgendamento] = [agendamentoMelhorado]; - } + if (!newDict[DiaAgendamento]) newDict[DiaAgendamento] = []; + newDict[DiaAgendamento].push(agendamentoMelhorado); } } for (const key in newDict) { - newDict[key].sort((a, b) => a.scheduled_at.localeCompare(b.scheduled_at)); + newDict[key].sort((a, b) => new Date(a.scheduled_at) - new Date(b.scheduled_at)); } setAgendamentosOrganizados(newDict); setFilaEsperaData(newFila); setShowSpinner(false); }; - fetchDados(); - }, [listaTodosAgendamentos, authHeader, cacheMedicos, cachePacientes, showSpinner]); - useEffect(() => { - fetchAppointments(); - GetAllDoctors(authHeader).then(docs => setListaDeMedicos(docs.map(d => ({ nomeMedico: d.full_name, idMedico: d.id })))); - }, [authHeader, fetchAppointments]); + processData(); + }, [listaTodosAgendamentos, authHeader]); // Removido 'user' das dependências pois não é mais usado aqui + + // O restante do código permanece o mesmo... + + const handleEditConsulta = (agendamento) => { + setAgendamentoParaEdicao(agendamento); + setPageConsulta(true); + }; const handleSearchMedicos = (term) => { setSearchTermDoctor(term); @@ -209,54 +260,7 @@ const Agendamento = ({ setDictInfo }) => { setMedicoFiltrado({ id: "vazio" }); } }; - - const filaEsperaFiltrada = useMemo(() => { - if (!waitlistSearch.trim()) return filaEsperaData; - const term = waitlistSearch.toLowerCase(); - return filaEsperaData.filter(item => - (item?.Infos?.paciente_nome?.toLowerCase() || '').includes(term) || - (item?.Infos?.paciente_cpf?.toLowerCase() || '').includes(term) || - (item?.Infos?.medico_nome?.toLowerCase() || '').includes(term) - ); - }, [waitlistSearch, filaEsperaData]); - - const applySortingWaitlist = useCallback((arr) => { - if (!Array.isArray(arr) || !waitSortKey) return arr; - const copy = [...arr]; - if (waitSortKey === 'paciente') { - copy.sort((a, b) => (a?.Infos?.paciente_nome || '').localeCompare((b?.Infos?.paciente_nome || ''))); - } else if (waitSortKey === 'medico') { - copy.sort((a, b) => (a?.Infos?.medico_nome || '').localeCompare((b?.Infos?.medico_nome || ''))); - } else if (waitSortKey === 'data') { - copy.sort((a, b) => new Date(a?.agendamento?.scheduled_at || 0) - new Date(b?.agendamento?.scheduled_at || 0)); - } - if (waitSortDir === 'desc') copy.reverse(); - return copy; - }, [waitSortKey, waitSortDir]); - - const filaEsperaOrdenada = useMemo(() => applySortingWaitlist(filaEsperaFiltrada), [filaEsperaFiltrada, applySortingWaitlist]); - - const waitTotalPages = Math.ceil(filaEsperaOrdenada.length / waitPerPage) || 1; - const waitIndiceInicial = (waitPage - 1) * waitPerPage; - const waitIndiceFinal = waitIndiceInicial + waitPerPage; - const filaEsperaPaginada = filaEsperaOrdenada.slice(waitIndiceInicial, waitIndiceFinal); - - const gerarNumerosWaitPages = () => { - const paginas = []; - const paginasParaMostrar = 5; - let inicio = Math.max(1, waitPage - Math.floor(paginasParaMostrar / 2)); - let fim = Math.min(waitTotalPages, inicio + paginasParaMostrar - 1); - inicio = Math.max(1, fim - paginasParaMostrar + 1); - for (let i = inicio; i <= fim; i++) { - paginas.push(i); - } - return paginas; - }; - - useEffect(() => { - setWaitPage(1); - }, [waitlistSearch, waitSortKey, waitSortDir]); - + const generateDateGrid = () => { const grid = []; const startOfMonth = currentDate.startOf('month'); @@ -271,26 +275,91 @@ const Agendamento = ({ setDictInfo }) => { const dateGrid = useMemo(() => generateDateGrid(), [currentDate]); const weekDays = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb']; const handleDateClick = (day) => setSelectedDay(day); - const DeleteModal = () => ( -
      -
      -
      -
      -
      Confirmação de Cancelamento
      - -
      -
      -

      Qual o motivo do cancelamento?

      - -
      -
      - - -
      +const DeleteModal = () => ( +
      +
      +
      +
      +
      Confirmação de Cancelamento
      + +
      +
      +

      Qual o motivo do cancelamento? (Opcional)

      + +
      +
      + + {/* ✨ Botão sempre ativo ✨ */} +
      - ); +
      +); + + + useEffect(() => { + setQuickJump({ + month: currentDate.month(), + year: currentDate.year() + }); + }, [currentDate]); + + const filaEsperaFiltrada = useMemo(() => { + if (!waitlistSearch.trim()) return filaEsperaData; + const term = waitlistSearch.toLowerCase(); + return filaEsperaData.filter(item => + (item?.Infos?.paciente_nome?.toLowerCase() || '').includes(term) || + (item?.Infos?.paciente_cpf?.toLowerCase() || '').includes(term) || + (item?.Infos?.medico_nome?.toLowerCase() || '').includes(term) + ); + }, [waitlistSearch, filaEsperaData]); + + const applySortingWaitlist = useCallback((arr) => { + if (!Array.isArray(arr) || !waitSortKey) return arr; + const copy = [...arr]; + const key = waitSortKey; + const dir = waitSortDir === 'asc' ? 1 : -1; + copy.sort((a, b) => { + const valA = key === 'data' ? new Date(a.agendamento.scheduled_at) : (a.Infos?.[`${key}_nome`] || ''); + const valB = key === 'data' ? new Date(b.agendamento.scheduled_at) : (b.Infos?.[`${key}_nome`] || ''); + if (valA < valB) return -1 * dir; + if (valA > valB) return 1 * dir; + return 0; + }); + return copy; + }, [waitSortKey, waitSortDir]); + + const filaEsperaOrdenada = useMemo(() => applySortingWaitlist(filaEsperaFiltrada), [filaEsperaFiltrada, applySortingWaitlist]); + const waitTotalPages = Math.ceil(filaEsperaOrdenada.length / waitPerPage) || 1; + const waitIndiceInicial = (waitPage - 1) * waitPerPage; + const waitIndiceFinal = waitIndiceInicial + waitPerPage; + const filaEsperaPaginada = filaEsperaOrdenada.slice(waitIndiceInicial, waitIndiceFinal); + + const gerarNumerosWaitPages = () => { + const paginas = []; + const paginasParaMostrar = 5; + let inicio = Math.max(1, waitPage - Math.floor(paginasParaMostrar / 2)); + let fim = Math.min(waitTotalPages, inicio + paginasParaMostrar - 1); + inicio = Math.max(1, fim - paginasParaMostrar + 1); + for (let i = inicio; i <= fim; i++) paginas.push(i); + return paginas; + }; + + useEffect(() => { setWaitPage(1); }, [waitlistSearch, waitSortKey, waitSortDir]); + + const handleQuickJumpChange = (type, value) => { + setQuickJump(prev => ({ ...prev, [type]: Number(value) })); + }; + + const applyQuickJump = () => { + let newDate = dayjs().year(quickJump.year).month(quickJump.month).date(1); + setCurrentDate(newDate); + setSelectedDay(newDate); + }; + return (

      Agendar nova consulta

      @@ -309,47 +378,49 @@ const Agendamento = ({ setDictInfo }) => {
      {!PageNovaConsulta ? (
      -
      -
      -
      -
      -
      - handleSearchMedicos(e.target.value)} /> + {user?.role !== 'doctor' && ( +
      +
      +
      +
      +
      + handleSearchMedicos(e.target.value)} /> +
      + {searchTermDoctor && FiltredTodosMedicos.length > 0 && ( +
      + {FiltredTodosMedicos.map((medico) => ( +
      { + setSearchTermDoctor(medico.nomeMedico); + setFiltredTodosMedicos([]); + setMedicoFiltrado({ id: medico.idMedico }); + }} + > +

      {medico.nomeMedico}

      +
      + ))} +
      + )}
      - {searchTermDoctor && FiltredTodosMedicos.length > 0 && ( -
      - {FiltredTodosMedicos.map((medico) => ( -
      { - setSearchTermDoctor(medico.nomeMedico); - setFiltredTodosMedicos([]); - setMedicoFiltrado(medico); - }} - > -

      {medico.nomeMedico}

      -
      - ))} -
      - )}
      -
      + )}
      - - + +
      - {FiladeEspera === false ? ( + {!FiladeEspera ? (
      {selectedDay.format('MMM')}{selectedDay.format('DD')}

      {selectedDay.format('dddd')}

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

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

      - {showSpinner ? : (DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')]?.length > 0) ? ( + {showSpinner ? : (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 => ( @@ -377,7 +448,8 @@ const Agendamento = ({ setDictInfo }) => { )}
      - ))) : (

      Nenhuma consulta agendada.

      )} + )) + ) : (

      Nenhuma consulta agendada.

      )}
      @@ -418,14 +490,12 @@ const Agendamento = ({ setDictInfo }) => {
      -
      -
      {weekDays.map(day =>
      {day}
      )} {dateGrid.map((day, index) => { @@ -445,10 +515,6 @@ const Agendamento = ({ setDictInfo }) => {
      ) : (
      - {} - - {} -
      @@ -460,30 +526,25 @@ const Agendamento = ({ setDictInfo }) => {
      Ordenar por: - {(() => { - const sortValue = waitSortKey ? `${waitSortKey}-${waitSortDir}` : ''; - return ( - - ); - })()} +
      @@ -571,7 +632,6 @@ const Agendamento = ({ setDictInfo }) => {
      -
      )}
    @@ -580,12 +640,15 @@ const Agendamento = ({ setDictInfo }) => { { + setPageConsulta(false); + fetchAppointments(); + }} /> )} {showDeleteModal && } - {}
    - ) + ); } -export default Agendamento; \ No newline at end of file +export default Agendamento; diff --git a/src/PagesPaciente/ConsultasPaciente.jsx b/src/PagesPaciente/ConsultasPaciente.jsx index 5922997..24a4811 100644 --- a/src/PagesPaciente/ConsultasPaciente.jsx +++ b/src/PagesPaciente/ConsultasPaciente.jsx @@ -2,8 +2,6 @@ import React, { useState, useMemo, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import API_KEY from '../components/utils/apiKeys.js'; import AgendamentoCadastroManager from '../pages/AgendamentoCadastroManager.jsx'; -import { GetPatientByID } from '../components/utils/Functions-Endpoints/Patient.js'; -import { GetAllDoctors, GetDoctorByID } from '../components/utils/Functions-Endpoints/Doctor.js'; import { useAuth } from '../components/utils/AuthProvider.js'; import dayjs from 'dayjs'; import 'dayjs/locale/pt-br'; @@ -14,233 +12,289 @@ import "../pages/style/Agendamento.css"; import '../pages/style/FilaEspera.css'; import Spinner from '../components/Spinner.jsx'; + dayjs.locale('pt-br'); dayjs.extend(isBetween); dayjs.extend(localeData); + const Agendamento = ({ setDictInfo }) => { const navigate = useNavigate(); - const [listaTodosAgendamentos, setListaTodosAgendamentos] = useState([]); - const [selectedID, setSelectedId] = useState('0'); - const [filaEsperaData, setFilaEsperaData] = useState([]); + const { getAuthorizationHeader, user } = useAuth(); + + + + const [isLoading, setIsLoading] = useState(true); + const [DictAgendamentosOrganizados, setDictAgendamentosOrganizados] = useState({}); + + + const [filaEsperaData, setFilaDeEsperaData] = useState([]); + const [FiladeEspera, setFiladeEspera] = useState(false); const [PageNovaConsulta, setPageConsulta] = useState(false); - const { getAuthorizationHeader } = useAuth(); - const [DictAgendamentosOrganizados, setAgendamentosOrganizados] = useState({}); - const [ListaDeMedicos, setListaDeMedicos] = useState([]); - const [showSpinner, setShowSpinner] = useState(true); - const [waitlistSearch, setWaitlistSearch] = useState(''); - const [waitSortKey, setWaitSortKey] = useState(null); - const [waitSortDir, setWaitSortDir] = useState('asc'); - const [waitPage, setWaitPage] = useState(1); - const [waitPerPage, setWaitPerPage] = useState(10); - const authHeader = getAuthorizationHeader(); - const cacheMedicos = useMemo(() => ({}), []); - const cachePacientes = useMemo(() => ({}), []); + + const [currentDate, setCurrentDate] = useState(dayjs()); const [selectedDay, setSelectedDay] = useState(dayjs()); - const [quickJump, setQuickJump] = useState({ month: currentDate.month(), year: currentDate.year() }); - const fetchAppointments = async () => { - const myHeaders = new Headers(); myHeaders.append("Authorization", authHeader); myHeaders.append("apikey", API_KEY); - const requestOptions = { method: 'GET', headers: myHeaders, redirect: 'follow' }; - try { - const res = await fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?select=*", requestOptions); - const data = await res.json(); - setListaTodosAgendamentos(data); - } catch (err) { - console.error('Erro ao buscar agendamentos', err); - } - }; + + + const [isCancelModalOpen, setIsCancelModalOpen] = useState(false); + const [appointmentToCancel, setAppointmentToCancel] = useState(null); + const [cancellationReason, setCancellationReason] = useState(''); + + const authHeader = useMemo(() => getAuthorizationHeader(), [getAuthorizationHeader]); + + + + useEffect(() => { + const carregarDados = async () => { + + const patientId = user?.patient_id || "6e7f8829-0574-42df-9290-8dbb70f75ada"; + + if (!authHeader) { + console.warn("Header de autorização não disponível."); + setIsLoading(false); + return; + } + + + setIsLoading(true); + try { + + const myHeaders = new Headers({ "Authorization": authHeader, "apikey": API_KEY }); + const requestOptions = { method: 'GET', headers: myHeaders }; + const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?select=*,doctors(full_name)&patient_id=eq.${patientId}`, requestOptions); + + if (!response.ok) throw new Error(`Erro na requisição: ${response.statusText}`); + + const consultasBrutas = await response.json() || []; + + + + const newDict = {}; + const newFila = []; + + + for (const agendamento of consultasBrutas) { + const agendamentoMelhorado = { + ...agendamento, + medico_nome: agendamento.doctors?.full_name || 'Médico não informado' + }; + + if (agendamento.status === "requested") { + newFila.push({ agendamento: agendamentoMelhorado, Infos: agendamentoMelhorado }); + } else { + const diaAgendamento = dayjs(agendamento.scheduled_at).format("YYYY-MM-DD"); + if (newDict[diaAgendamento]) { + newDict[diaAgendamento].push(agendamentoMelhorado); + } else { + newDict[diaAgendamento] = [agendamentoMelhorado]; + } + } + } + + for (const key in newDict) { + newDict[key].sort((a, b) => a.scheduled_at.localeCompare(b.scheduled_at)); + } + + setDictAgendamentosOrganizados(newDict); + setFilaDeEsperaData(newFila); + + + } catch (err) { + console.error('Falha ao buscar ou processar agendamentos:', err); + setDictAgendamentosOrganizados({}); + setFilaDeEsperaData([]); + } finally { + setIsLoading(false); + } + }; + + + carregarDados(); + }, [authHeader, user]); + const updateAppointmentStatus = async (id, updates) => { - const myHeaders = new Headers(); myHeaders.append("Authorization", authHeader); myHeaders.append("apikey", API_KEY); myHeaders.append("Content-Type", "application/json"); myHeaders.append("Prefer", "return=representation"); + const myHeaders = new Headers({ + "Authorization": authHeader, "apikey": API_KEY, "Content-Type": "application/json" + }); const requestOptions = { method: 'PATCH', headers: myHeaders, body: JSON.stringify(updates) }; try { const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${id}`, requestOptions); - if (response.ok) { - await fetchAppointments(); - return true; - } else { - console.error('Erro ao atualizar agendamento:', await response.text()); - return false; - } + if (!response.ok) throw new Error('Falha ao atualizar o status.'); + return true; } catch (error) { console.error('Erro de rede/servidor:', error); return false; } }; - const deleteConsulta = async (id) => { - const confirmation = window.confirm("Tem certeza que deseja CANCELAR este agendamento? Esta ação é irreversível."); - if (!confirmation) return; - - setShowSpinner(true); - const success = await updateAppointmentStatus(id, { status: "cancelled", cancellation_reason: "Cancelado pela Secretaria", updated_at: new Date().toISOString() }); - setShowSpinner(false); - if (success) { - alert("Consulta cancelada com sucesso!"); - } else { - alert("Falha ao cancelar a consulta."); - } - }; - const confirmConsulta = async (id) => { - const confirmation = window.confirm("Tem certeza que deseja CONFIRMAR/REVERTER o cancelamento deste agendamento para 'agendado'?"); - if (!confirmation) return; - setShowSpinner(true); - const success = await updateAppointmentStatus(id, { status: "agendado", cancellation_reason: null, updated_at: new Date().toISOString() }); - setShowSpinner(false); - if (success) { - alert("Agendamento confirmado/revertido para 'agendado' com sucesso!"); - } else { - alert("Falha ao reverter o cancelamento."); - } - }; - - const handleQuickJumpChange = (type, value) => { - setQuickJump(prev => ({ ...prev, [type]: Number(value) })); - }; - - useEffect(() => { - setQuickJump({ - month: currentDate.month(), - year: currentDate.year() - }); - }, [currentDate]); - - const applyQuickJump = () => { - let newDate = dayjs().year(quickJump.year).month(quickJump.month).date(1); - setCurrentDate(newDate); - setSelectedDay(newDate); - }; - - useEffect(() => { - if (!listaTodosAgendamentos.length) { setShowSpinner(false); return; } - setShowSpinner(true); - const fetchDados = async () => { - const newDict = {}; const newFila = []; - for (const agendamento of listaTodosAgendamentos) { - if (!agendamento.doctor_id || !agendamento.patient_id) continue; - if (!cacheMedicos[agendamento.doctor_id]) { cacheMedicos[agendamento.doctor_id] = (await GetDoctorByID(agendamento.doctor_id, authHeader))[0]; } - if (!cachePacientes[agendamento.patient_id]) { cachePacientes[agendamento.patient_id] = (await GetPatientByID(agendamento.patient_id, authHeader))[0]; } - const medico = cacheMedicos[agendamento.doctor_id]; - const paciente = cachePacientes[agendamento.patient_id]; - - const agendamentoMelhorado = { ...agendamento, medico_nome: medico?.full_name, paciente_nome: paciente?.full_name, paciente_cpf: paciente?.cpf }; - - if (agendamento.status === "requested") { - newFila.push({ agendamento: agendamentoMelhorado, Infos: agendamentoMelhorado }); - } - else { - const DiaAgendamento = dayjs(agendamento.scheduled_at).format("YYYY-MM-DD"); - if (newDict[DiaAgendamento]) { - newDict[DiaAgendamento].push(agendamentoMelhorado); - } - else { - newDict[DiaAgendamento] = [agendamentoMelhorado]; - } - } - } - for (const key in newDict) { newDict[key].sort((a, b) => a.scheduled_at.localeCompare(b.scheduled_at)); } - - setAgendamentosOrganizados(newDict); - setFilaEsperaData(newFila); - setShowSpinner(false); - }; - fetchDados(); - }, [listaTodosAgendamentos, authHeader, cacheMedicos, cachePacientes]); - - useEffect(() => { - fetchAppointments(); - GetAllDoctors(authHeader).then(docs => setListaDeMedicos(docs.map(d => ({ nomeMedico: d.full_name, idMedico: d.id })))); - }, [authHeader]); - - const filaEsperaFiltrada = useMemo(() => { - if (!waitlistSearch.trim()) return filaEsperaData; - const term = waitlistSearch.toLowerCase(); - return filaEsperaData.filter(item => (item?.Infos?.paciente_nome?.toLowerCase() || '').includes(term) || (item?.Infos?.paciente_cpf?.toLowerCase() || '').includes(term) || (item?.Infos?.nome_medico?.toLowerCase() || '').includes(term)); - }, [waitlistSearch, filaEsperaData]); - const applySortingWaitlist = (arr) => { - if (!Array.isArray(arr) || !waitSortKey) return arr; - const copy = [...arr]; - if (waitSortKey === 'paciente') { copy.sort((a, b) => (a?.Infos?.paciente_nome || '').localeCompare((b?.Infos?.paciente_nome || ''))); } - else if (waitSortKey === 'medico') { copy.sort((a, b) => (a?.Infos?.nome_medico || '').localeCompare((b?.Infos?.nome_medico || ''))); } - else if (waitSortKey === 'data') { copy.sort((a, b) => new Date(a?.agendamento?.scheduled_at || 0) - new Date(b?.agendamento?.scheduled_at || 0)); } - if (waitSortDir === 'desc') copy.reverse(); - return copy; + + const handleCancelClick = (appointmentId) => { + setAppointmentToCancel(appointmentId); + setCancellationReason(''); + setIsCancelModalOpen(true); }; - const filaEsperaOrdenada = applySortingWaitlist(filaEsperaFiltrada); - const waitTotalPages = Math.ceil(filaEsperaOrdenada.length / waitPerPage) || 1; - const waitIndiceInicial = (waitPage - 1) * waitPerPage; - const waitIndiceFinal = waitIndiceInicial + waitPerPage; - const filaEsperaPaginada = filaEsperaOrdenada.slice(waitIndiceInicial, waitIndiceFinal); - const gerarNumerosWaitPages = () => { - const paginas = []; const paginasParaMostrar = 5; - let inicio = Math.max(1, waitPage - Math.floor(paginasParaMostrar / 2)); - let fim = Math.min(waitTotalPages, inicio + paginasParaMostrar - 1); - inicio = Math.max(1, fim - paginasParaMostrar + 1); - for (let i = inicio; i <= fim; i++) { paginas.push(i); } - return paginas; - }; - useEffect(() => { setWaitPage(1); }, [waitlistSearch, waitSortKey, waitSortDir]); - // Funções do Calendário - const generateDateGrid = () => { - const grid = []; const startOfMonth = currentDate.startOf('month'); - let currentDay = startOfMonth.subtract(startOfMonth.day(), 'day'); - for (let i = 0; i < 42; i++) { grid.push(currentDay); currentDay = currentDay.add(1, 'day'); } - return grid; + + + const executeCancellation = async () => { + if (!appointmentToCancel) return; + + setIsLoading(true); + + + const motivo = cancellationReason.trim() || "Cancelado pelo paciente (motivo não especificado)"; + + const success = await updateAppointmentStatus(appointmentToCancel, { + status: "cancelled", + cancellation_reason: motivo, + updated_at: new Date().toISOString() + }); + + + setIsCancelModalOpen(false); + setAppointmentToCancel(null); + setCancellationReason(''); + + + if (success) { + alert("Solicitação cancelada com sucesso!"); + + setDictAgendamentosOrganizados(prev => { + const newDict = { ...prev }; + for (const date in newDict) { + newDict[date] = newDict[date].filter(app => app.id !== appointmentToCancel); + } + return newDict; + }); + setFilaDeEsperaData(prev => prev.filter(item => item.agendamento.id !== appointmentToCancel)); + } else { + alert("Falha ao cancelar a solicitação."); + } + setIsLoading(false); }; - const dateGrid = useMemo(() => generateDateGrid(), [currentDate]); + + + const handleQuickJumpChange = (type, value) => setQuickJump(prev => ({ ...prev, [type]: Number(value) })); + const applyQuickJump = () => { + const newDate = dayjs().year(quickJump.year).month(quickJump.month).date(1); + setCurrentDate(newDate); + setSelectedDay(newDate); + }; + const dateGrid = useMemo(() => { + const grid = []; + const startOfMonth = currentDate.startOf('month'); + let currentDay = startOfMonth.subtract(startOfMonth.day(), 'day'); + for (let i = 0; i < 42; i++) { + grid.push(currentDay); + currentDay = currentDay.add(1, 'day'); + } + return grid; + }, [currentDate]); const weekDays = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb']; const handleDateClick = (day) => setSelectedDay(day); + const activeButtonStyle = { + backgroundColor: '#1B2A41', + color: 'white', + padding: '6px 12px', + fontSize: '0.875rem', + borderRadius: '8px', + border: '1px solid white', + display: 'flex', + alignItems: 'center', + gap: '8px', + cursor: 'pointer' + }; + + const inactiveButtonStyle = { + backgroundColor: '#1B2A41', + color: 'white', + padding: '6px 12px', + fontSize: '0.875rem', + borderRadius: '8px', + border: '1px solid #1B2A41', + display: 'flex', + alignItems: 'center', + gap: '8px', + cursor: 'pointer' + }; + + + + if (isLoading) { + return ( +
    + +
    + ); + } + + return (

    Minhas consultas

    - {}
    + +
    - {} {!PageNovaConsulta ? (
    - {FiladeEspera === false ? ( + {!FiladeEspera ? ( +
    - {}
    {selectedDay.format('MMM')}{selectedDay.format('DD')}

    {selectedDay.format('dddd')}

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

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

    - {showSpinner ? : (DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')]?.length > 0) ? (DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')].map(app => ( -
    -
    {dayjs(app.scheduled_at).format('HH:mm')}
    -
    {app.paciente_nome}Dr(a). {app.medico_nome}
    - {} -
    - ))) : (

    Nenhuma consulta agendada.

    )} + {(DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')]?.length > 0) ? ( + DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')].map(app => ( +
    +
    {dayjs(app.scheduled_at).format('HH:mm')}
    +
    + Consulta com Dr(a). {app.medico_nome} +
    +
    + {app.status !== 'cancelled' && dayjs(app.scheduled_at).isAfter(dayjs()) && ( + + )} +
    +
    + )) + ) : (

    Nenhuma consulta agendada para esta data.

    )}
    - {}
    Realizado
    Confirmado
    Agendado
    Cancelado
    @@ -249,41 +303,21 @@ const Agendamento = ({ setDictInfo }) => {

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

    - handleQuickJumpChange('month', e.target.value)} className="form-select form-select-sm w-auto"> + {dayjs.months().map((month, index) => ())} - handleQuickJumpChange('year', e.target.value)} className="form-select form-select-sm w-auto"> + {Array.from({ length: 11 }, (_, i) => dayjs().year() - 5 + i).map(year => ())} - +
    -
    - - - + + +
    -
    {weekDays.map(day =>
    {day}
    )} {dateGrid.map((day, index) => { @@ -292,11 +326,7 @@ const Agendamento = ({ setDictInfo }) => { return (
    handleDateClick(day)}> {day.format('D')} - {appointmentsOnDay.length > 0 && -
    - {appointmentsOnDay.length} -
    - } + {appointmentsOnDay.length > 0 &&
    {appointmentsOnDay.length}
    }
    ); })} @@ -305,51 +335,45 @@ const Agendamento = ({ setDictInfo }) => {
    ) : (
    -
    +
    -

    Fila de Espera

    +

    Minhas Solicitações em Fila de Espera

    -
    -
    Filtros
    -
    setWaitlistSearch(e.target.value)} />Digite o nome do paciente, CPF ou nome do médico
    -
    -
    - Ordenar por: - {(() => { const sortValue = waitSortKey ? `${waitSortKey}-${waitSortDir}` : ''; return ();})()} -
    -
    -
    {filaEsperaFiltrada.length} DE {filaEsperaData.length} SOLICITAÇÕES ENCONTRADAS
    -
    - - +
    Nome do PacienteCPFMédico SolicitadoData da SolicitaçãoAções
    + + + + + + + - {filaEsperaPaginada.length > 0 ? (filaEsperaPaginada.map((item, index) => ( - - + {filaEsperaData.length > 0 ? (filaEsperaData.map((item) => ( + + + + - ))) : ()} + ))) : ( + + + + )}
    Médico SolicitadoData da SolicitaçãoAções
    {item?.Infos?.paciente_nome}{item?.Infos?.paciente_cpf}{item?.Infos?.nome_medico}{dayjs(item.agendamento.scheduled_at).format('DD/MM/YYYY')} - {} - -
    Dr(a). {item.Infos?.medico_nome}{dayjs(item.agendamento.created_at).format('DD/MM/YYYY HH:mm')} + +
    {showSpinner ? : (<>

    Nenhuma solicitação encontrada.

    )}
    +
    Nenhuma solicitação na fila de espera.
    +
    - {filaEsperaFiltrada.length > 0 && (
    -
    Itens por página:
    -
    Página {waitPage} de {waitTotalPages} • Mostrando {waitIndiceInicial + 1}-{Math.min(waitIndiceFinal, filaEsperaFiltrada.length)} de {filaEsperaFiltrada.length}
    -
    )}
    -
    +
    )}
    @@ -357,8 +381,49 @@ const Agendamento = ({ setDictInfo }) => { ) : ( )} + + + {} + {isCancelModalOpen && ( +
    +
    +
    +

    Confirmação de Cancelamento

    + +
    +
    +

    Qual o motivo do cancelamento?

    + +
    +
    + + +
    +
    +
    + )} + {}
    ) } -export default Agendamento; \ No newline at end of file + +export default Agendamento; diff --git a/src/pages/Agendamento.jsx b/src/pages/Agendamento.jsx index dd01ddd..d959cb7 100644 --- a/src/pages/Agendamento.jsx +++ b/src/pages/Agendamento.jsx @@ -12,16 +12,18 @@ 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 } from 'lucide-react'; -import CalendarComponent from '../components/AgendarConsulta/CalendarComponent.jsx'; +import localeData from 'dayjs/plugin/localeData'; +import { Search, ChevronLeft, ChevronRight, Edit, Trash2 } from 'lucide-react'; +import CalendarComponent from '../components/AgendarConsulta/CalendarComponent.jsx'; import "./style/Agendamento.css"; import './style/FilaEspera.css'; import Spinner from '../components/Spinner.jsx'; + dayjs.locale('pt-br'); dayjs.extend(isBetween); -dayjs.extend(localeData); +dayjs.extend(localeData); + const Agendamento = ({ setDictInfo }) => { const navigate = useNavigate(); @@ -39,7 +41,7 @@ const Agendamento = ({ setDictInfo }) => { const [searchTermDoctor, setSearchTermDoctor] = useState(''); const [MedicoFiltrado, setMedicoFiltrado] = useState({ id: "vazio" }); const [cacheAgendamentos, setCacheAgendamentos] = useState([]); - const [appointmentToEdit, setAppointmentToEdit] = useState(null); + const [appointmentToEdit, setAppointmentToEdit] = useState(null); const [showConfirmModal, setShowConfirmModal] = useState(false); const [motivoCancelamento, setMotivoCancelamento] = useState(""); const [showSpinner, setShowSpinner] = useState(true); @@ -54,13 +56,14 @@ const Agendamento = ({ setDictInfo }) => { const [currentDate, setCurrentDate] = useState(dayjs()); const [selectedDay, setSelectedDay] = useState(dayjs()); - const [editingAppointmentId, setEditingAppointmentId] = useState(null); + const [editingAppointmentId, setEditingAppointmentId] = useState(null); - const [quickJump, setQuickJump] = useState({ - month: currentDate.month(), - year: currentDate.year() + const [quickJump, setQuickJump] = useState({ + month: currentDate.month(), + year: currentDate.year() }); + const fetchAppointments = async () => { const myHeaders = new Headers(); myHeaders.append("Authorization", authHeader); myHeaders.append("apikey", API_KEY); const requestOptions = { method: 'GET', headers: myHeaders, redirect: 'follow' }; @@ -73,6 +76,7 @@ const Agendamento = ({ setDictInfo }) => { } }; + const updateAppointmentStatus = async (id, updates) => { const myHeaders = new Headers(); myHeaders.append("Authorization", authHeader); myHeaders.append("apikey", API_KEY); myHeaders.append("Content-Type", "application/json"); myHeaders.append("Prefer", "return=representation"); const requestOptions = { method: 'PATCH', headers: myHeaders, body: JSON.stringify(updates) }; @@ -80,7 +84,7 @@ const Agendamento = ({ setDictInfo }) => { try { const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${id}`, requestOptions); if (response.ok) { - await fetchAppointments(); + await fetchAppointments(); return true; } else { console.error('Erro ao atualizar agendamento:', await response.text()); @@ -92,6 +96,7 @@ const Agendamento = ({ setDictInfo }) => { } }; + const deleteConsulta = async (id) => { setShowSpinner(true); const success = await updateAppointmentStatus(id, { status: "cancelled", cancellation_reason: motivoCancelamento, updated_at: new Date().toISOString() }); @@ -117,10 +122,12 @@ const Agendamento = ({ setDictInfo }) => { } }; + const handleQuickJumpChange = (type, value) => { setQuickJump(prev => ({ ...prev, [type]: Number(value) })); }; + useEffect(() => { setQuickJump({ month: currentDate.month(), @@ -128,12 +135,14 @@ const Agendamento = ({ setDictInfo }) => { }); }, [currentDate]); + const applyQuickJump = () => { let newDate = dayjs().year(quickJump.year).month(quickJump.month).date(1); setCurrentDate(newDate); - setSelectedDay(newDate); + setSelectedDay(newDate); }; + useEffect(() => { if (!listaTodosAgendamentos.length) { setShowSpinner(false); return; } setShowSpinner(true); @@ -145,12 +154,12 @@ const Agendamento = ({ setDictInfo }) => { if (!cachePacientes[agendamento.patient_id]) { cachePacientes[agendamento.patient_id] = (await GetPatientByID(agendamento.patient_id, authHeader))[0]; } const medico = cacheMedicos[agendamento.doctor_id]; const paciente = cachePacientes[agendamento.patient_id]; - - const agendamentoMelhorado = { - ...agendamento, - medico_nome: medico?.full_name, - paciente_nome: paciente?.full_name, - paciente_cpf: paciente?.cpf + + const agendamentoMelhorado = { + ...agendamento, + medico_nome: medico?.full_name, + paciente_nome: paciente?.full_name, + paciente_cpf: paciente?.cpf }; if (agendamento.status === "requested") { newFila.push({ agendamento: agendamentoMelhorado, Infos: agendamentoMelhorado }); } @@ -168,12 +177,15 @@ const Agendamento = ({ setDictInfo }) => { fetchDados(); }, [listaTodosAgendamentos, authHeader, cacheMedicos, cachePacientes]); + useEffect(() => { - fetchAppointments(); + fetchAppointments(); GetAllDoctors(authHeader).then(docs => setListaDeMedicos(docs.map(d => ({ nomeMedico: d.full_name, idMedico: d.id })))); }, [authHeader]); - const handleSearchMedicos = (term) => {  }; + + const handleSearchMedicos = (term) => { }; + const filaEsperaFiltrada = useMemo(() => { if (!waitlistSearch.trim()) return filaEsperaData; @@ -205,6 +217,7 @@ const Agendamento = ({ setDictInfo }) => { }; useEffect(() => { setWaitPage(1); }, [waitlistSearch, waitSortKey, waitSortDir]); + const generateDateGrid = () => { const grid = []; const startOfMonth = currentDate.startOf('month'); let currentDay = startOfMonth.subtract(startOfMonth.day(), 'day'); @@ -215,6 +228,7 @@ const Agendamento = ({ setDictInfo }) => { const weekDays = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb']; const handleDateClick = (day) => setSelectedDay(day); + const DeleteModal = () => (
    @@ -229,12 +243,13 @@ const Agendamento = ({ setDictInfo }) => {
    - +
    - ); + ); + const ConfirmEditModal = () => (
    @@ -255,7 +270,8 @@ const Agendamento = ({ setDictInfo }) => {
    - ); + ); + return ( @@ -263,9 +279,9 @@ const Agendamento = ({ setDictInfo }) => {

    Agendar nova consulta

    {/* LIMPA O OBJETO DE EDIÇÃO AO CLICAR EM "ADICIONAR" */} - @@ -309,12 +325,12 @@ const Agendamento = ({ setDictInfo }) => { ) : ( <> {/* MUDANÇA: PASSA O OBJETO COMPLETO 'app' PARA O ESTADO DE EDIÇÃO */} -
    ) : ( - )} {showDeleteModal && } @@ -442,5 +458,6 @@ const Agendamento = ({ setDictInfo }) => { ) } - -export default Agendamento; \ No newline at end of file + +export default Agendamento; + \ No newline at end of file diff --git a/src/pages/style/FilaEspera.css b/src/pages/style/FilaEspera.css index 358a21f..eb2777d 100644 --- a/src/pages/style/FilaEspera.css +++ b/src/pages/style/FilaEspera.css @@ -322,4 +322,4 @@ html[data-bs-theme="dark"] .busca-fila-espera { html[data-bs-theme="dark"] .busca-fila-espera:focus { border-color: #5980fd !important; -} \ No newline at end of file +} From 815ce759cde5a6d08b84766fd631bee880c9e3fc Mon Sep 17 00:00:00 2001 From: GilenoNeto901 Date: Fri, 14 Nov 2025 21:27:04 -0300 Subject: [PATCH 17/19] agendamento resolvido --- src/pages/Agendamento.jsx | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/src/pages/Agendamento.jsx b/src/pages/Agendamento.jsx index 5e1adb1..89e9388 100644 --- a/src/pages/Agendamento.jsx +++ b/src/pages/Agendamento.jsx @@ -276,30 +276,15 @@ const Agendamento = ({ setDictInfo }) => { return (
    -

    Gerenciar consultas

    +

    Agendar nova consulta

    {/* LIMPA O OBJETO DE EDIÇÃO AO CLICAR EM "ADICIONAR" */} -<<<<<<< HEAD - : - - - } - +
    {!PageNovaConsulta ? ( @@ -473,11 +458,5 @@ const Agendamento = ({ setDictInfo }) => {
    ) } -<<<<<<< HEAD export default Agendamento; - -======= - -export default Agendamento; ->>>>>>> b64b664621626393863bd367709ca331df3ec00c From 1d048023b11183cb4f884289af33cddc65edd1ef Mon Sep 17 00:00:00 2001 From: joao_pedro Date: Sat, 15 Nov 2025 19:03:36 -0300 Subject: [PATCH 18/19] bug: detalhes e editar do doctor --- src/components/patients/PatientForm.jsx | 27 +++++++++++---- src/pages/DoctorDetails.jsx | 46 +++++++++++-------------- src/pages/DoctorEditPage.jsx | 20 ++--------- src/pages/EditPage.jsx | 2 +- 4 files changed, 44 insertions(+), 51 deletions(-) diff --git a/src/components/patients/PatientForm.jsx b/src/components/patients/PatientForm.jsx index c554020..ddb76e4 100644 --- a/src/components/patients/PatientForm.jsx +++ b/src/components/patients/PatientForm.jsx @@ -52,6 +52,8 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { })); }; + + useEffect(() => { const peso = parseFloat(formData.weight_kg); const altura = parseFloat(formData.height_m); @@ -63,9 +65,20 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { } }, [formData.weight_kg, formData.height_m, setFormData]); + const handleCep = (value) => { + if(value.length === 8){ + fetch(`https://viacep.com.br/ws/${value}/json/`) + .then(response => response.json()) + .then(result => {setFormData(prev => ({...prev, street:result.logradouro,neighborhood:result.bairro,city:result.localidade, + state:result.estado + + }))}) + } + } + const handleChange = (e) => { const { name, value, type, checked, files } = e.target; - + console.log(name, value) if (value && emptyFields.includes(name)) { setEmptyFields(prev => prev.filter(field => field !== name)); } @@ -101,8 +114,11 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { } else if (name.includes('weight_kg') || name.includes('height_m')) { setFormData(prev => ({ ...prev, [name]: FormatPeso(value) })); } else if (name === 'rn_in_insurance' || name === 'vip' || name === 'validadeIndeterminada') { - setFormData(prev => ({ ...prev, [name]: checked })); - } else { + setFormData(prev => ({ ...prev, [name]: checked })); + } else if(name === 'cep'){ + handleCep(value) + setFormData(prev => ({...prev, [name]: value})) + }else { setFormData(prev => ({ ...prev, [name]: value })); } }; @@ -189,9 +205,6 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - if (!emailRegex.test(formData.email)) { - throw new Error('Email inválido. Por favor, verifique o email digitado.'); - } await onSave({ ...formData, bmi: parseFloat(formData.bmi) || null }); }; @@ -647,4 +660,4 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { ); } -export default PatientForm; \ No newline at end of file +export default PatientForm; diff --git a/src/pages/DoctorDetails.jsx b/src/pages/DoctorDetails.jsx index e426f75..23e59d4 100644 --- a/src/pages/DoctorDetails.jsx +++ b/src/pages/DoctorDetails.jsx @@ -4,11 +4,12 @@ import { useParams,Link, useNavigate, useLocation } from "react-router-dom"; import { GetDoctorByID } from "../components/utils/Functions-Endpoints/Doctor"; import { useAuth } from "../components/utils/AuthProvider"; -const DoctorDetails = ({doctor}) => { +const DoctorDetails = ({DictInfo}) => { const {getAuthorizationHeader} = useAuth(); const Parametros = useParams() const navigate = useNavigate(); const location = useLocation(); + const Voltar = () => { @@ -16,11 +17,6 @@ const Voltar = () => { navigate(`/${prefixo}/medicos`); } - - - - - return ( <>
    @@ -36,12 +32,12 @@ const Voltar = () => {
    - {doctor.nome || "Nome Completo"} -

    {doctor.cpf || "CPF"}

    + {DictInfo.full_name || "Nome Completo"} +

    {DictInfo.cpf || "CPF"}

    - @@ -55,29 +51,29 @@ const Voltar = () => {
    -

    {doctor.full_name || "-"}

    +

    {DictInfo.full_name || "-"}

    -

    {doctor.birth_date || "-"}

    +

    {DictInfo.birth_date || "-"}

    -

    {doctor.cpf || "-"}

    +

    {DictInfo.cpf || "-"}

    -

    {doctor.crm || "-"}

    +

    {DictInfo.crm || "-"}

    -

    {doctor.crm_uf || "-"}

    +

    {DictInfo.crm_uf || "-"}

    -

    {doctor.specialty || "-"}

    +

    {DictInfo.specialty || "-"}

    @@ -89,31 +85,31 @@ const Voltar = () => {
    -

    {doctor.cep || "-"}

    +

    {DictInfo.cep || "-"}

    -

    {doctor.street || "-"}

    +

    {DictInfo.street || "-"}

    -

    {doctor.neighborhood || "-"}

    +

    {DictInfo.neighborhood || "-"}

    -

    {doctor.city || "-"}

    +

    {DictInfo.city || "-"}

    -

    {doctor.state || "-"}

    +

    {DictInfo.state || "-"}

    -

    {doctor.number || "-"}

    +

    {DictInfo.number || "-"}

    -

    {doctor.complement || "-"}

    +

    {DictInfo.complement || "-"}

    @@ -125,15 +121,15 @@ const Voltar = () => {
    -

    {doctor.email || "-"}

    +

    {DictInfo.email || "-"}

    -

    {doctor.phone_mobile || "-"}

    +

    {DictInfo.phone_mobile || "-"}

    -

    {doctor.phone2 || "-"}

    +

    {DictInfo.phone2 || "-"}

    diff --git a/src/pages/DoctorEditPage.jsx b/src/pages/DoctorEditPage.jsx index 1b75ffa..f50b934 100644 --- a/src/pages/DoctorEditPage.jsx +++ b/src/pages/DoctorEditPage.jsx @@ -45,18 +45,8 @@ const DoctorEditPage = ({DictInfo}) => { redirect: "follow", }; - try { - const response = await fetch( - `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors?id=eq.${DoctorID}`, - requestOptions - ); - console.log("Resposta PUT Doutor:", response); - alert("Dados do médico atualizados com sucesso!"); - } catch (error) { - console.error("Erro ao atualizar médico:", error); - alert("Erro ao atualizar dados do médico."); - throw error; - } + fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors?id=eq.${DictInfo.id}`,requestOptions) + .then(response => console.log(response)) }; // 2. Função para Atualizar DISPONIBILIDADE (PATCH) @@ -97,12 +87,6 @@ const DoctorEditPage = ({DictInfo}) => { return (
    -

    - {mode === "availability" - ? `Editar Horário Disponível (ID: ${availabilityId.substring(0, 8)})` - : `Editar Médico (ID: ${DoctorID})`} -

    - { }; fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients?id=eq.${PatientToPUT.id}`,requestOptions) - .then(response => response.json) + .then(response => console.log(response)) .then(result => console.log(result)) .catch(console.log("erro")) From ad7c300a440bbcc85ba2a92949e64936cfc569ba Mon Sep 17 00:00:00 2001 From: joao_pedro Date: Wed, 19 Nov 2025 09:01:44 -0300 Subject: [PATCH 19/19] concertos no ver detalhes e editar --- src/pages/Details.jsx | 14 +++++++++++--- src/pages/DoctorDetails.jsx | 9 ++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/pages/Details.jsx b/src/pages/Details.jsx index f7fe388..5734046 100644 --- a/src/pages/Details.jsx +++ b/src/pages/Details.jsx @@ -22,6 +22,16 @@ const Details = (DictInfo) => { navigate(`/${prefixo}/pacientes`); } + +const navigateEdit = () => { + const prefixo = location.pathname.split("/")[1]; + navigate(`/${prefixo}/medicos/edit`); + } + + + + + useEffect(() => { if (!DictInfo) return; console.log(patientID, 'teu id') @@ -82,11 +92,9 @@ const Details = (DictInfo) => {
    - - - diff --git a/src/pages/DoctorDetails.jsx b/src/pages/DoctorDetails.jsx index 23e59d4..c0b9244 100644 --- a/src/pages/DoctorDetails.jsx +++ b/src/pages/DoctorDetails.jsx @@ -10,6 +10,11 @@ const DoctorDetails = ({DictInfo}) => { const navigate = useNavigate(); const location = useLocation(); +const navigateEdit = () => { + const prefixo = location.pathname.split("/")[1]; + navigate(`/${prefixo}/medicos/edit`); + } + const Voltar = () => { @@ -36,11 +41,9 @@ const Voltar = () => {

    {DictInfo.cpf || "CPF"}

    - - -