diff --git a/DELETE/Agendamento.css b/DELETE/Agendamento.css index ffa5552..ef2c6e4 100644 --- a/DELETE/Agendamento.css +++ b/DELETE/Agendamento.css @@ -6,18 +6,58 @@ /* --- Posiciona a barra de busca corretamente --- */ .busca-atendimento { display: flex; - align-items: center; /* Alinha os itens verticalmente */ - margin-top: 20px; /* Espaço acima da barra de busca */ - padding: 0 10px; /* Adiciona um padding lateral para alinhar com o resto */ + align-items: center; + margin-top: 20px; + padding: 0 10px; gap: 15px; } .busca-atendimento > div:first-child { - width: 400px; /* Define um tamanho para a barra de pesquisa */ + width: 400px; display: flex; align-items: center; } +@media (max-width: 768px) { + .busca-atendimento { + flex-direction: column; + align-items: stretch; + gap: 10px; + } + + .busca-atendimento > div:first-child { + width: 100%; + } + + .btns-e-legenda-container { + flex-direction: column; + align-items: flex-start; + gap: 10px; + } + + .legenda-tabela { + flex-wrap: wrap; + gap: 8px; + } +} + +@media (max-width: 576px) { + .busca-atendimento { + padding: 0 5px; + } + + .btns-e-legenda-container { + padding: 0 5px; + } + + .btn-selecionar-tabeladia, + .btn-selecionar-tabelasemana, + .btn-selecionar-tabelames { + padding: 6px 8px; + font-size: medium; + } +} + .busca-atendimento input { margin-left: 8px; border-radius: 8px; @@ -126,13 +166,20 @@ } .container-btns-agenda-fila_esepera { - margin-top: 30px; display: flex; flex-direction: row; + justify-content: space-between; + align-items: flex-end; gap: 20px; - margin-left: 20px; + + margin-top: 0; + margin-bottom: 0; + margin-left: 0; + padding: 0 20px 0; + border-bottom: 1px solid #edf1f7; } + .btn-fila-espera, .btn-agenda { background-color: transparent; @@ -140,6 +187,7 @@ border-bottom: 3px solid transparent; padding: 8px; border-radius: 10px 10px 0px 0px; + color: #fff; font-weight: bold; cursor: pointer; } @@ -228,4 +276,10 @@ html[data-bs-theme="dark"] .legenda-item-agendado { background-color: #2e2e1e; border: 3px solid #4d4d2e; color: #f7f7c4; +} +.calendar-legend { + margin-top: 8px; + display: flex; + gap: 8px; + justify-content: center; } \ No newline at end of file diff --git a/src/_assets/css/components/Header.css b/src/_assets/css/components/Header.css index 2335a2e..f875868 100644 --- a/src/_assets/css/components/Header.css +++ b/src/_assets/css/components/Header.css @@ -223,6 +223,8 @@ z-index: 2001; margin-top: 80px; margin-right: 20px; + /* Adicionado para responsividade */ + max-width: 90vw; } .suporte-card { @@ -309,6 +311,8 @@ z-index: 3001; margin-top: 80px; margin-right: 20px; + /* Adicionado para responsividade */ + max-width: 90vw; } .chat-online { @@ -478,6 +482,7 @@ .suporte-card-container, .chat-container { + margin-top: 60px; margin-right: 10px; margin-left: 10px; } @@ -489,28 +494,101 @@ } } -/* permite que cliques "passem" através do header (exceto para os elementos interativos) */ -.header-container { - pointer-events: none; /* header não captura cliques */ +@media (max-width: 576px) { + .header-container { + padding: 8px 10px; + } + + .right-corner-elements { + gap: 10px; + } + + .profile-picture-container { + width: 35px; + height: 35px; + } + + .phone-icon-container { + font-size: 20px; + } + + .suporte-card-container, + .chat-container { + margin-top: 50px; + margin-right: 5px; + margin-left: 5px; + } + + .suporte-card { + padding: 1rem; + } + + .chat-online { + width: calc(100vw - 10px); + height: 80vh; /* Limita a altura para telas pequenas */ + } + + .chat-input { + padding: 0.75rem; + } + + .chat-campo { + padding: 0.5rem; + } + + .chat-enviar { + padding: 0.5rem 1rem; + } } -/* mas permite que os controles no canto (telefone e profile) continuem clicáveis */ +@media (max-width: 768px) { + .header-container { + padding: 10px 15px; + } + + .right-corner-elements { + gap: 15px; + } + + .profile-picture-container { + width: 40px; + height: 40px; + } + + .suporte-card-container, + .chat-container { + margin-right: 10px; + margin-left: 10px; + } + + .suporte-card, + .chat-online { + width: calc(100vw - 20px); + max-width: none; + } +} + +.header-container { + pointer-events: none; +} + + .phone-icon-container, .profile-section { pointer-events: auto; } -/* Garantir pointer-events nos elementos do header e overlays criados por portal */ + .header-container { pointer-events: auto; } .phone-icon-container, .profile-section { pointer-events: auto; } -/* Força que os overlays criados por portal fiquem por cima */ + .logout-modal-overlay, .suporte-card-overlay, .chat-overlay { z-index: 110000 !important; pointer-events: auto !important; } -/* Pequeno ajuste visual dos botões do modal (pode se misturar com seu CSS atual) */ + .logout-cancel-button { padding: 10px 18px; border-radius: 8px; diff --git a/src/_assets/css/components/agendamento/CardConsulta.css b/src/_assets/css/components/agendamento/CardConsulta.css index 4be1d8b..9f2dfa2 100644 --- a/src/_assets/css/components/agendamento/CardConsulta.css +++ b/src/_assets/css/components/agendamento/CardConsulta.css @@ -1,4 +1,12 @@ /* card-consulta.css */ + +@media (max-width: 768px) { + .container-cardconsulta { + padding-right: 80px; /* Espaço para os botões */ + position: relative; + } +} + .actions-container { display: flex; gap: 8px; @@ -85,4 +93,50 @@ .tabelamensal .container-cardconsulta{ width: 24rem; +} + +@media (max-width: 768px) { + .actions-container { + opacity: 1; + visibility: visible; + background: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; + border: none; + box-shadow: none; + margin-left: 0; + padding: 0; + justify-content: flex-end; + position: absolute; + top: 10px; + right: 10px; + } + + .container-cardconsulta:hover .actions-container { + transform: none; + } + + .tabelamensal .container-cardconsulta { + width: 100%; + max-width: 100%; + } +} + +@media (max-width: 576px) { + .container-cardconsulta { + padding-right: 10px; /* Remove o padding extra para telas muito pequenas */ + } + + .actions-container { + position: static; + margin-top: 10px; + justify-content: flex-start; + } + + .btn-edit-custom-style, + .btn-delete-custom-style, + .btn-confirm-style { + padding: 6px 10px; + font-size: 0.8rem; + } } \ No newline at end of file diff --git a/src/_assets/css/components/agendamento/FormAgendamento.css b/src/_assets/css/components/agendamento/FormAgendamento.css index d3fc9ae..6c4a903 100644 --- a/src/_assets/css/components/agendamento/FormAgendamento.css +++ b/src/_assets/css/components/agendamento/FormAgendamento.css @@ -1,3 +1,5 @@ +/* formagendamentos.css */ + @import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined'); .icons-container { @@ -183,6 +185,67 @@ textarea{ gap: 16px; } +@media (max-width: 768px) { + .campos-informacoes-paciente, + .campo-informacoes-atendimento { + flex-direction: column; + gap: 10px; + } + + #informacoes-atendimento-segunda-linha { + flex-direction: column; + gap: 10px; + } + + #informacoes-atendimento-segunda-linha-esquerda select[name="unidade"], + input[type="time"], + select[name=solicitante], + .campo-de-input { + width: 100% !important; + max-width: 100%; + } + + .tipo_atendimento { + margin-left: 0; + } + + .linha { + flex-direction: column; + align-items: stretch; + gap: 10px; + } + + .sessao-contador { + width: 100px; + } +} + +@media (max-width: 576px) { + .form-container { + padding: 15px; + } + + .form-title { + font-size: 22px; + } + + .form-agendamento input, + .form-agendamento select, + .form-agendamento textarea { + font-size: 13px; + } + + .form-actions { + flex-direction: column; + gap: 10px; + } + + .btn-primary, + .btn-cancel { + width: 100%; + } +} + .campo-de-input { flex: 1; display: flex; @@ -689,4 +752,94 @@ input[name="paciente_cpf"]{ z-index: 100; max-height: 200px; overflow-y: auto; +} +#informacoes-atendimento-segunda-linha .linha-horarios { + display: flex; + gap: 16px; + align-items: flex-end; /* alinha pela base dos inputs */ +} + +#informacoes-atendimento-segunda-linha .linha-horarios .campo-de-input { + flex: 1; +} +.campo-de-input-container { + display: flex; + gap: 16px; /* nome e cpf na mesma linha */ + flex-wrap: wrap; +} + +.campo-de-input { + display: flex; + flex-direction: column; + margin-bottom: 12px; +} + +.campo-de-input label { + font-size: 14px; + font-weight: 600; + margin-bottom: 4px; +} + +.campo-de-input input, +.campo-de-input select, +.campo-de-input textarea { + width: 220px; /* ajuste pro layout que você quer */ + padding: 6px 10px; + border: 1px solid #ccc; + border-radius: 3px; + font-size: 14px; +} + +/* placeholder visível e suave */ +.campo-de-input input::placeholder { + color: #999; + opacity: 1; /* garante no Firefox */ +} +/* bloco da coluna esquerda (Data, Início, Término) */ +#informacoes-atendimento-segunda-linha-esquerda { + display: flex; + flex-direction: column; + gap: 12px; +} + +/* linha com Início e Término */ +#informacoes-atendimento-segunda-linha-esquerda .linha { + display: flex; + gap: 16px; + align-items: flex-end; +} + +/* mesma largura pros três campos */ +#informacoes-atendimento-segunda-linha-esquerda .campo-de-input input, +#informacoes-atendimento-segunda-linha-esquerda .campo-de-input select { + width: 230px; + box-sizing: border-box; +} +.informacoes-atendimento-segunda-linha-direita { + width: 100%; +} + +.informacoes-atendimento-segunda-linha-direita .campo-de-input textarea { + width: 100%; /* ocupa toda a coluna da direita */ + min-height: 150px; /* aumenta a altura (muda pra 200, 250 se quiser maior) */ + resize: vertical; + box-sizing: border-box; +} +#informacoes-atendimento-segunda-linha { + display: grid; + grid-template-columns: auto 1.8fr; /* coluna da direita grande, mas não infinita */ + gap: 24px; +} + +/* garante que o container da direita não estoure */ +.informacoes-atendimento-segunda-linha-direita { + max-width: 800px; /* ajusta se quiser menor/maior */ + width: 100%; +} + +.informacoes-atendimento-segunda-linha-direita .campo-de-input textarea { + width: 100%; + min-height: 150px; + resize: vertical; + box-sizing: border-box; } \ No newline at end of file diff --git a/src/_assets/css/components/paciente/CardConsultaPaciente.css b/src/_assets/css/components/paciente/CardConsultaPaciente.css index 2d0b21f..89a3f0c 100644 --- a/src/_assets/css/components/paciente/CardConsultaPaciente.css +++ b/src/_assets/css/components/paciente/CardConsultaPaciente.css @@ -1,14 +1,72 @@ +/* PagesPaciente/style.css */ /* Estilo geral do card para agrupar e dar um formato */ .card-consulta { - background-color: #007bff; /* Um tom de azul padrão */ - display: flex; /* Para colocar horário e info lado a lado */ - border-radius: 10px; /* Cantos arredondados */ - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Sombra suave */ - overflow: hidden; /* Garante que o fundo azul não 'vaze' */ - /* width: 280px; /* Largura de exemplo */ + background-color: #007bff; + display: flex; + border-radius: 10px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + overflow: hidden; margin: 20px; - font-family: Arial, sans-serif; /* Fonte legível */ + font-family: Arial, sans-serif; +} + +@media (max-width: 768px) { + .card-consulta { + flex-direction: column; + margin: 10px; + } + + .horario-container { + border-right: none; + border-bottom: 1px solid #0056b3; + padding: 10px 15px; + } + + .horario { + font-size: 1.8em; + } + + .info-container { + flex-direction: column; + align-items: flex-start; + gap: 1rem; + padding: 15px; + } + + .actions-container { + opacity: 1; + visibility: visible; + background: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; + border: none; + box-shadow: none; + margin-left: 0; + padding: 0; + justify-content: flex-start; + margin-top: 10px; + } + + .card-consulta:hover .actions-container { + transform: none; + } +} + +@media (max-width: 576px) { + .horario { + font-size: 1.5em; + } + + .informacao { + font-size: 1em; + } + + .btn-edit-custom-style, + .btn-delete-custom-style { + padding: 6px 10px; + font-size: 0.8rem; + } } /* 1. Estilo para o Horário (Fundo Azul e Texto Branco/Grande) */ diff --git a/src/_assets/css/index.css b/src/_assets/css/index.css index ec2585e..b71da03 100644 --- a/src/_assets/css/index.css +++ b/src/_assets/css/index.css @@ -1,3 +1,9 @@ +*, +*::before, +*::after { + box-sizing: border-box; +} + body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', @@ -10,4 +16,4 @@ body { code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; -} +} \ No newline at end of file diff --git a/src/_assets/css/pages/admin/DashboardUsuarios.css b/src/_assets/css/pages/admin/DashboardUsuarios.css index 65c5a54..ad1b849 100644 --- a/src/_assets/css/pages/admin/DashboardUsuarios.css +++ b/src/_assets/css/pages/admin/DashboardUsuarios.css @@ -7,7 +7,6 @@ min-height: 100vh; } - .dashboard-header { display: flex; justify-content: space-between; @@ -35,143 +34,133 @@ border: none; border-radius: 8px; font-size: 1rem; + font-weight: 600; cursor: pointer; - transition: background-color 0.3s, transform 0.25s ease, box-shadow 0.25s ease; + transition: all 0.3s ease; + box-shadow: 0 2px 8px rgba(30, 58, 138, 0.3); } .new-user-btn:hover { background-color: #162d6b; transform: translateY(-2px); - box-shadow: 0px 4px 12px rgba(30, 58, 138, 0.3); + box-shadow: 0 4px 12px rgba(30, 58, 138, 0.4); } - .filters-container { background: #fff; border-radius: 12px; - padding: 1.5rem; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + padding: 1.2rem; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); margin-bottom: 2rem; - transition: transform 0.2s ease, box-shadow 0.2s ease; -} - -.filters-container:hover { - transform: translateY(-3px); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); } .filters-title { - font-size: 18px; + font-size: 16px; font-weight: bold; margin-bottom: 0.3rem; color: #333; } .filters-subtitle { - font-size: 0.9rem; + font-size: 0.85rem; color: #666; margin-bottom: 1rem; } .filters-content { display: flex; - gap: 1rem; + gap: 0.8rem; align-items: center; } .filters-input { flex: 1; - padding: 0.6rem 1rem; + padding: 0.5rem 0.8rem; border: 1px solid #d1d5db; - border-radius: 8px; - font-size: 0.95rem; + border-radius: 6px; + font-size: 0.9rem; color: #333; - transition: border-color 0.2s, box-shadow 0.2s; + min-width: 200px; + transition: all 0.2s ease; } .filters-input:focus { border-color: #1e3a8a; - box-shadow: 0px 0px 0px 3px rgba(30, 58, 138, 0.2); + box-shadow: 0 0 0 2px rgba(30, 58, 138, 0.1); outline: none; } .filters-select { - padding: 0.6rem 1rem; + padding: 0.5rem 0.8rem; border: 1px solid #d1d5db; - border-radius: 8px; - font-size: 0.95rem; + border-radius: 6px; + font-size: 0.9rem; background: #fff; color: #333; cursor: pointer; - transition: border-color 0.2s, box-shadow 0.2s; + min-width: 140px; + transition: all 0.2s ease; } .filters-select:focus { border-color: #1e3a8a; - box-shadow: 0px 0px 0px 3px rgba(30, 58, 138, 0.2); + box-shadow: 0 0 0 2px rgba(30, 58, 138, 0.1); outline: none; } - .cards-container { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 1.5rem; + gap: 1.2rem; margin-bottom: 2rem; } .card { background-color: white; - padding: 1.5rem; - border-radius: 12px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + padding: 1.2rem; + border-radius: 10px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); border: 1px solid transparent; - transition: transform 0.25s ease, box-shadow 0.25s ease, border 0.25s ease, background 0.25s ease; + transition: all 0.25s ease; cursor: pointer; } .highlight:hover { - transform: translateY(-6px); - box-shadow: 0 8px 20px rgba(30, 58, 138, 0.2); + transform: translateY(-4px); + box-shadow: 0 6px 16px rgba(30, 58, 138, 0.2); background: #f8faff; border: 1px solid #1e3a8a33; } .card-label { - font-size: 0.9rem; + font-size: 0.85rem; color: #999; margin-bottom: 0.5rem; } .card-value { - font-size: 1.8rem; + font-size: 1.6rem; font-weight: bold; margin: 0; color: #333; } .card-extra { - font-size: 0.85rem; + font-size: 0.8rem; color: #666; } .card-extra.positive { color: #1e3a8a; + font-weight: 600; } - .user-table-container { background: #fff; border-radius: 12px; - padding: 1.5rem; - box-shadow: 0 4px 6px rgba(0,0,0,0.1); + padding: 1.2rem; + box-shadow: 0 2px 8px rgba(0,0,0,0.1); margin-top: 2rem; - transition: transform 0.25s ease, box-shadow 0.25s ease; -} - -.user-table-container:hover { - transform: translateY(-4px); - box-shadow: 0 6px 14px rgba(0,0,0,0.15); } .user-table-container h2 { @@ -194,7 +183,7 @@ .user-table th, .user-table td { - padding: 12px 15px; + padding: 10px 12px; text-align: left; border-bottom: 1px solid #e0e0e0; } @@ -203,10 +192,11 @@ background-color: #f3f4f6; color: #333; font-weight: 600; + font-size: 0.9rem; } .user-table tr { - transition: background-color 0.25s ease; + transition: background-color 0.2s ease; } .user-table tr:hover { @@ -215,44 +205,115 @@ .profile-badge { background-color: #1e3a8a; - color: #f7f7f7; - padding: 3px 8px; - border-radius: 8px; - font-size: 0.85rem; + color: white; + padding: 4px 10px; + border-radius: 6px; + font-size: 0.8rem; + font-weight: 500; display: inline-block; } .status-badge { - padding: 3px 8px; - border-radius: 8px; - font-size: 0.85rem; + padding: 4px 10px; + border-radius: 6px; + font-size: 0.8rem; color: #fff; + font-weight: 500; display: inline-block; text-transform: capitalize; } .status-badge.ativo { - background-color: #28a745; + background-color: #1e3a8a; } .status-badge.inativo { - background-color: #dc3545; + background-color: #6c757d; } .actions { display: flex; - gap: 10px; + gap: 8px; + flex-wrap: wrap; } -.action-icon { +.action-btn { + border: none; + padding: 6px 12px; + font-size: 0.8rem; + font-weight: 500; cursor: pointer; - color: #555; - transition: color 0.2s, transform 0.2s; + transition: all 0.2s ease; + border-radius: 4px; + display: inline-flex; + align-items: center; + gap: 4px; } -.action-icon:hover { - color: #1e3a8a; - transform: scale(1.2); +.action-btn.detalhes { + background-color: #e6f2ff; + color: #004085; + border: 1px solid #b8d4ff; +} + +.action-btn.detalhes:hover { + background-color: #cce4ff; + transform: translateY(-1px); +} + +.action-btn.editar { + background-color: #fff3cd; + color: #856405; + border: 1px solid #ffeaa7; +} + +.action-btn.editar:hover { + background-color: #ffeaa7; + transform: translateY(-1px); +} + +.action-btn.excluir { + background-color: #f8d7da; + color: #721c24; + border: 1px solid #f1b0b7; +} + +.action-btn.excluir:hover { + background-color: #f1b0b7; + transform: translateY(-1px); +} + +.save-btn { + background-color: #1e3a8a; + color: white; + border: none; + padding: 8px 16px; + border-radius: 6px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; +} + +.save-btn:hover { + background-color: #162d6b; + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(30, 58, 138, 0.3); +} + +.edit-btn { + background-color: #fff3cd; + color: #856405; + border: 1px solid #ffeaa7; + padding: 8px 16px; + border-radius: 6px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; +} + +.edit-btn:hover { + background-color: #ffeaa7; + transform: translateY(-1px); } html[data-bs-theme="dark"] .dashboard-container { @@ -267,18 +328,17 @@ html[data-bs-theme="dark"] .dashboard-subtitle { } html[data-bs-theme="dark"] .new-user-btn { - background-color: #2563eb; - color: #fff; + background-color: #1e3a8a; } html[data-bs-theme="dark"] .new-user-btn:hover { - background-color: #1e40af; + background-color: #162d6b; } html[data-bs-theme="dark"] .filters-container, html[data-bs-theme="dark"] .user-table-container { background: #1a1a1a; - box-shadow: 0 4px 6px rgba(0,0,0,0.4); + box-shadow: 0 2px 8px rgba(0,0,0,0.4); } html[data-bs-theme="dark"] .filters-title, @@ -300,19 +360,19 @@ html[data-bs-theme="dark"] .filters-select { html[data-bs-theme="dark"] .filters-input:focus, html[data-bs-theme="dark"] .filters-select:focus { - border-color: #2563eb; - box-shadow: 0px 0px 0px 3px rgba(37, 99, 235, 0.2); + border-color: #1e3a8a; + box-shadow: 0 0 0 2px rgba(30, 58, 138, 0.2); } html[data-bs-theme="dark"] .cards-container .card { background-color: #181818; color: #e0e0e0; - box-shadow: 0 4px 6px rgba(0,0,0,0.4); + box-shadow: 0 2px 6px rgba(0,0,0,0.4); } html[data-bs-theme="dark"] .highlight:hover { - background: #232a3a; - border: 1px solid #2563eb33; + background: #1a1f2e; + border: 1px solid #1e3a8a33; } html[data-bs-theme="dark"] .card-label { @@ -328,7 +388,7 @@ html[data-bs-theme="dark"] .card-extra { } html[data-bs-theme="dark"] .card-extra.positive { - color: #2563eb; + color: #1e3a8a; } html[data-bs-theme="dark"] .user-table th { @@ -342,26 +402,39 @@ html[data-bs-theme="dark"] .user-table td { } html[data-bs-theme="dark"] .user-table tr:hover { - background-color: #232a3a; + background-color: #1a1f2e; } html[data-bs-theme="dark"] .profile-badge { - background-color: #2563eb; - color: #fff; + background-color: #1e3a8a; } -html[data-bs-theme="dark"] .status-badge.ativo { - background-color: #28a745; +html[data-bs-theme="dark"] .action-btn.detalhes { + background-color: #e6f2ff; + color: #004085; + border: 1px solid #b8d4ff; } -html[data-bs-theme="dark"] .status-badge.inativo { - background-color: #dc3545; +html[data-bs-theme="dark"] .action-btn.detalhes:hover { + background-color: #cce4ff; } -html[data-bs-theme="dark"] .action-icon { - color: #bdbdbd; +html[data-bs-theme="dark"] .action-btn.editar { + background-color: #fff3cd; + color: #856405; + border: 1px solid #ffeaa7; } -html[data-bs-theme="dark"] .action-icon:hover { - color: #2563eb; +html[data-bs-theme="dark"] .action-btn.editar:hover { + background-color: #ffeaa7; +} + +html[data-bs-theme="dark"] .action-btn.excluir { + background-color: #f8d7da; + color: #721c24; + border: 1px solid #f1b0b7; +} + +html[data-bs-theme="dark"] .action-btn.excluir:hover { + background-color: #f1b0b7; } \ No newline at end of file diff --git a/src/_assets/css/pages/agendamento/Agendamento.css b/src/_assets/css/pages/agendamento/Agendamento.css index 6a4964f..5edcca3 100644 --- a/src/_assets/css/pages/agendamento/Agendamento.css +++ b/src/_assets/css/pages/agendamento/Agendamento.css @@ -57,7 +57,7 @@ background-color: transparent; border: 0; border-bottom: 2px solid transparent; - padding: 12px 24px; + padding: 10px 12px; border-radius: 0; font-weight: 600; color: #718096; @@ -108,7 +108,9 @@ 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; } @@ -135,6 +137,7 @@ .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; } @@ -181,7 +184,6 @@ color: #C53030; } - .appointment-item { display: flex; align-items: center; @@ -224,80 +226,194 @@ background-color: #C53030; } +.table-wrapper { + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; +} + + +table { + width: 100%; + border-collapse: collapse; + min-width: 600px; +} + @media (max-width: 768px) { - .unidade-selecionarprofissional { flex-direction: column; align-items: stretch; gap: 12px; } - .calendar-wrapper { flex-direction: column; padding: 16px; } - .calendar-info-panel { flex: 0 0 auto; border-right: none; border-bottom: 1px solid #E2E8F0; padding-right: 0; padding-bottom: 16px; } + .busca-atendimento { + flex-direction: column; + gap: 10px; + } + + .container-btns-agenda-fila_esepera { + flex-direction: column; + align-items: flex-start; + gap: 10px; + flex-wrap: wrap; + } + + .btns-gerenciamento-e-consulta { + width: 100%; + justify-content: space-between; + flex-wrap: wrap; + } + + .btn-adicionar-consulta { + padding: 8px 12px; + font-size: 0.8rem; + white-space: normal; + text-align: center; + } + + .unidade-selecionarprofissional { + flex-direction: column; + align-items: stretch; + gap: 12px; + } + + .calendar-wrapper { + flex-direction: column; + padding: 16px; + } + + .calendar-info-panel { + border-right: none; + border-bottom: 1px solid #E2E8F0; + padding-right: 0; + padding-bottom: 16px; + } + .calendar-grid { grid-template-columns: repeat(4, 1fr); } .calendar-controls { flex-direction: column; align-items: flex-start; gap: 8px; } } - @media (max-width: 576px) { - .calendar-grid { grid-template-columns: repeat(2, 1fr); } + .calendar-grid { grid-template-columns: 1fr; } .date-indicator h2 { font-size: 1.25rem; } .legend-item { font-size: 0.75rem; padding: 4px 8px; } .appointment-item { flex-direction: column; align-items: stretch; gap: 8px; } .appointment-actions { width: 100%; } .btn-action { width: 100%; } } -.btn-adicionar-consulta { - background-color: #2a67e2; - color: #fff; - padding: 10px 24px; - border-radius: 8px; - border: none; - font-weight: 600; - font-size: 1rem; - display: flex; - align-items: center; - gap: 8px; - cursor: pointer; - transition: background 0.2s; + +@media (max-width: 425px) { + .calendar-main { + overflow-x: auto; + } + .calendar-grid { + min-width: 400px; + grid-template-columns: repeat(7, 1fr); + } + .day-cell { + min-height: 80px; + } + + + .table-wrapper { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + border: 1px solid #E2E8F0; + border-radius: 8px; + margin-bottom: 16px; + } + + table { + min-width: 600px; + font-size: 0.875rem; + } + + table th, + table td { + padding: 8px; + white-space: nowrap; } -.btn-adicionar-consulta:hover { - background-color: #1d4ed8; -} -.btn-adicionar-consulta i { - font-size: 1.2em; - vertical-align: middle; -} -.btn-adicionar-consulta i { - font-size: 1.2em; - vertical-align: middle; - display: flex; - align-items: center; - justify-content: center; } .container-btns-agenda-fila_esepera { display: flex; - justify-content: space-between; + justify-content: space-between; /* abas à esquerda, botões à direita */ align-items: center; - margin: 15px 0 20px; -} - -.tabs-agenda-fila { - display: flex; - gap: 10px; + gap: 16px; /* opcional: espaço entre os blocos */ + width: 100%; } +/* garante que os botões fiquem em linha */ .btns-gerenciamento-e-consulta { display: flex; - gap: 10px; + gap: 8px; } + +/* em telas muito pequenas, pode empilhar verticalmente */ +@media (max-width: 768px) { + .container-btns-agenda-fila_esepera { + flex-direction: column; + align-items: stretch; + } + + .btns-gerenciamento-e-consulta { + justify-content: flex-start; /* ou center se preferir */ + flex-wrap: wrap; + } +} +/* barra de abas + botões */ .container-btns-agenda-fila_esepera { - display: flex; - justify-content: space-between; - align-items: center; - margin: 0 0 8px; + margin-bottom: 0; /* cola com a linha de baixo */ } - +/* dá só um respiro pequeno entre as abas e o conteúdo, + mas igual para Agenda e Fila de espera */ .calendario-ou-filaespera { - margin-top: 0; + margin-top: 4px; /* aumenta um pouquinho, não 20px */ padding-top: 0; } +/* se o título "Fila de Espera" estiver mais distante, + aproxima só o conteúdo dessa área também */ +.page-content.table-paciente-container { + margin-top: 4px; +} +/* 1) container das abas + botões: encostado no topo */ +.container-btns-agenda-fila_esepera { + margin-bottom: 0; +} -.calendar-wrapper { - margin-top: 0; +/* 2) sempre cria um espaçamento logo DEPOIS das abas, + antes de qualquer conteúdo (Agenda ou Fila) */ +.container-btns-agenda-fila_esepera + .calendario-ou-filaespera { + margin-top: 8px; /* aumenta ou diminui aqui */ + padding-top: 0; +} + +/* 3) garante que o primeiro filho da section não roube/colapse margens */ +.calendario-ou-filaespera > *:first-child { + margin-top: 0 !important; +} +/* mesmos blocos azuis e quadrados */ +.btn-consulta-paciente { + display: inline-flex; + align-items: center; /* alinha ícone + texto verticalmente */ + justify-content: center; + gap: 6px; /* espaço entre ícone e texto */ + padding: 10px 22px; /* altura/largura parecidas com os azuis */ + border-radius: 6px; /* se os outros forem 6px, mantém igual */ + font-weight: 500; + border: none; +} + +/* garante mesma cor dos blocos da secretaria */ +.btn-consulta-paciente.btn-primary { + background-color: #1d4ed8; /* azul escuro do primeiro bloco */ +} + +/* se quiser hover igual */ +.btn-consulta-paciente.btn-primary:hover { + background-color: #1437a3; +} +.btn-consulta-paciente { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + padding: 10px 22px; + border-radius: 6px; + font-weight: 500; + border: none; } \ No newline at end of file diff --git a/src/_assets/css/pages/agendamento/FilaEspera.css b/src/_assets/css/pages/agendamento/FilaEspera.css index bf6dafa..19aedce 100644 --- a/src/_assets/css/pages/agendamento/FilaEspera.css +++ b/src/_assets/css/pages/agendamento/FilaEspera.css @@ -190,6 +190,12 @@ html, body { } /* ===== Fila de Espera ===== */ + +@media (max-width: 992px) { + .fila-container { + overflow-x: auto; + } +} .fila-container { width: 100%; max-width: none; @@ -286,6 +292,13 @@ html, body { transition: border-color 0.2s; } +@media (max-width: 768px) { + .busca-fila-espera { + width: 100%; + position: static; + } +} + .busca-fila-espera:focus { border-color: #888; } diff --git a/src/_assets/css/pages/financeiro/DashboardFinanceiro.css b/src/_assets/css/pages/financeiro/DashboardFinanceiro.css index d9caac7..279ab48 100644 --- a/src/_assets/css/pages/financeiro/DashboardFinanceiro.css +++ b/src/_assets/css/pages/financeiro/DashboardFinanceiro.css @@ -1,3 +1,5 @@ +/* FinanceiroDashboard.css */ + /* GERAL */ .financeiro-wrap { display: flex; @@ -21,6 +23,12 @@ margin-bottom: 10px; } +@media (max-width: 1200px) { + .summary-card { + min-width: 180px; + } +} + .summary-card { flex: 1; min-width: 200px; @@ -39,6 +47,7 @@ margin: 0 0 8px 0; font-size: 14px; font-weight: 500; + color: #fff; opacity: 0.9; } @@ -107,43 +116,87 @@ } /* Botões de ação */ -.action-group { - display: flex; - gap: 8px; - align-items: center; + +.btn-view { + background-color: #E6F2FF !important; + color: #004085 !important; + border: 1px solid #B8D4F0 !important; + padding: 0.375rem 0.75rem; + font-size: 0.875rem; + border-radius: 6px; + cursor: pointer; + transition: all 0.15s ease-in-out; + text-decoration: none; + display: inline-block; + text-align: center; } -.action-btn { - cursor: pointer; - padding: 6px 12px; - border-radius: 6px; - border: 1px solid #d7e6fb; - background: #fff; - transition: all 0.2s ease; - font-size: 13px; +.btn-view:hover { + background-color: #D1E7FF !important; + border-color: #9EC5FE !important; } -.action-btn:hover { - background: #f6f9fc; - border-color: #93c5fd; +.btn-edit { + background-color: #FFF3CD !important; + color: #856404 !important; + border: 1px solid #FFEAA7 !important; + padding: 0.375rem 0.75rem; + font-size: 0.875rem; + border-radius: 6px; + cursor: pointer; + transition: all 0.15s ease-in-out; + text-decoration: none; + display: inline-block; + text-align: center; } -.action-btn.delete { - border-color: #fca5a5; - color: #b91c1c; +.btn-edit:hover { + background-color: #FFEEBA !important; + border-color: #FFE087 !important; } -.action-btn.delete:hover { - background: #fee2e2; - border-color: #ef4444; +.btn-delete:hover { + background-color: #F1B0B7 !important; + border-color: #ED969E !important; +} +.btn-delete { + background-color: #F8D7DA !important; + color: #721C24 !important; + border: 1px solid #F5C6CB !important; + padding: 0.375rem 0.75rem; + font-size: 0.875rem; + border-radius: 6px; + cursor: pointer; + transition: all 0.15s ease-in-out; + text-decoration: none; + display: inline-block; + text-align: center; +} + +html[data-bs-theme="dark"] .btn-view { + background-color: #1e3a8a !important; + color: #e0e0e0 !important; + border-color: #374151 !important; +} + +html[data-bs-theme="dark"] .btn-edit { + background-color: #78350f !important; + color: #fef3c7 !important; + border-color: #374151 !important; +} + +html[data-bs-theme="dark"] .btn-delete { + background-color: #7f1d1d !important; + color: #fee2e2 !important; + border-color: #374151 !important; } /* Badges de status */ .badge { display: inline-block; - padding: 4px 10px; + padding: 8px 18px !important; border-radius: 9999px; - font-size: 12px; + font-size: 14px !important; font-weight: 600; text-transform: uppercase; } @@ -182,12 +235,18 @@ padding: 24px; width: 100%; max-width: 550px; - max-height: 90vh; - overflow-y: auto; + max-height: 85vh; box-sizing: border-box; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1); } +@media (max-width: 576px) { + .modal-card { + padding: 16px; + max-height: 95vh; + } +} + .modal-header { display: flex; justify-content: space-between; @@ -198,7 +257,7 @@ .modal-header h2 { font-size: 20px; font-weight: 700; - color: #1f2937; + color: #fff; margin: 0; } @@ -208,6 +267,12 @@ gap: 16px; } +.modal-card .input-field, +.modal-card .select-field, +.modal-card textarea { + width: 100%; +} + .form-group { display: flex; flex-direction: column; @@ -241,12 +306,23 @@ gap: 10px; margin-top: 24px; } +.input-field, +.select-field, +textarea { + padding: 10px 12px; + border: 1px solid #d1d5db; + border-radius: 8px; + box-sizing: border-box; + font-size: 14px; + transition: border-color 0.2s, box-shadow 0.2s; + background-color: #fff; +} + /* Inputs e selects */ .input-field, .select-field, textarea { - width: 100%; padding: 10px 12px; border: 1px solid #d1d5db; border-radius: 8px; @@ -269,6 +345,26 @@ textarea { min-height: 80px; } +.financeiro-wrap .input-field:not(.modal-card *), +.financeiro-wrap .select-field:not(.modal-card *), +.financeiro-wrap textarea:not(.modal-card *) { + width: 30%; +} + +@media (max-width: 768px) { + .financeiro-wrap .input-field:not(.modal-card *), + .financeiro-wrap .select-field:not(.modal-card *), + .financeiro-wrap textarea:not(.modal-card *) { + width: 100%; + } +} + +.modal-card .input-field, +.modal-card .select-field, +.modal-card textarea { + width: 100%; +} + /* Mensagem quando não há pagamentos */ .empty { text-align: center; diff --git a/src/_assets/css/pages/geral/Dashboard.css b/src/_assets/css/pages/geral/Dashboard.css index 32a4c58..b69463a 100644 --- a/src/_assets/css/pages/geral/Dashboard.css +++ b/src/_assets/css/pages/geral/Dashboard.css @@ -1,5 +1,136 @@ /* Inicio.css */ +/* Container Principal */ + +/* Responsividade */ +@media (max-width: 1200px) { + .dashboard-container { + padding: 1.5rem; + } +} + +@media (max-width: 768px) { + .dashboard-container { + padding: 1rem; + } + + .dashboard-header h1 { + font-size: 1.5rem; + } + + .dashboard-header p { + margin-bottom: 1.5rem; + } + + .stats-grid { + grid-template-columns: 1fr 1fr; /* 2 colunas em tablets */ + gap: 1rem; + } + + .stat-value { + font-size: 1.5rem; + } + + .stat-icon-wrapper { + width: 40px; + height: 40px; + } + + .stat-icon { + font-size: 1rem; + } + + .actions-grid { + grid-template-columns: 1fr; /* 1 coluna em tablets */ + gap: 1rem; + } + + .action-icon { + font-size: 1.8rem; + } + + .action-title { + font-size: 0.9rem; + } + + .appointments-section { + padding: 1.5rem; + } + + .agendamento-info { + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + } + + .agendamento-time-date { + flex-direction: row; + gap: 1rem; + min-width: auto; + } + + .agendamento-detalhes { + min-width: auto; + } +} + +@media (max-width: 576px) { + .dashboard-container { + padding: 0.5rem; + } + + .dashboard-header h1 { + font-size: 1.3rem; + } + + .dashboard-header p { + margin-bottom: 1rem; + } + + .stats-grid { + grid-template-columns: 1fr; /* 1 coluna em celulares */ + } + + .stat-card { + padding: 1rem; + } + + .stat-value { + font-size: 1.3rem; + } + + .action-button { + padding: 1rem; + } + + .appointments-section { + padding: 1rem; + } + + .agendamento-item { + padding: 0.75rem 1rem; + } + + .agendamento-hora { + font-size: 1.1rem; + } + + .agendamento-data { + font-size: 0.7rem; + } + + .agendamento-paciente, + .agendamento-medico { + font-size: 0.85rem; + } + + .manage-button, + .view-all-button { + padding: 0.6rem 1.2rem; + font-size: 0.8rem; + } +} + /* Container Principal */ .dashboard-container { padding: 2rem; @@ -72,10 +203,10 @@ } /* Cores dos ícones */ -.stat-icon-wrapper.blue { background-color: #5d5dff; } -.stat-icon-wrapper.green { background-color: #30d158; } -.stat-icon-wrapper.purple { background-color: #a272ff; } -.stat-icon-wrapper.orange { background-color: #f1952e; } +.stat-icon-wrapper.blue { background-color: #1D3B88; } +.stat-icon-wrapper.green { background-color: #399CE5; } +.stat-icon-wrapper.purple { background-color: #5F5DF2; } +.stat-icon-wrapper.orange { background-color: #051AFF; } /* Seção de Ações Rápidas */ .quick-actions h2 { diff --git a/src/_assets/css/pages/medico/RelatorioAudio.css b/src/_assets/css/pages/medico/RelatorioAudio.css new file mode 100644 index 0000000..06c759b --- /dev/null +++ b/src/_assets/css/pages/medico/RelatorioAudio.css @@ -0,0 +1,121 @@ +/* NovoRelatorioAudio.css */ + +/* Container que ocupa toda a altura da tela (menos o header do site) */ +.ai-editor-container { + display: flex; + min-height: calc(100vh - 80px); /* Ajuste conforme seu header */ + background-color: #e9ecef; /* Cor de "Mesa" */ + overflow: hidden; +} + +/* --- LADO ESQUERDO: PAINEL DE CONTROLE --- */ +.editor-sidebar { + width: 400px; + background-color: #212529; /* Dark Mode para o painel */ + color: #fff; + padding: 20px; + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 15px; + box-shadow: 4px 0 10px rgba(0,0,0,0.1); +} + +.editor-sidebar h4 { + color: #0dcaf0; /* Ciano para destaque */ + margin-bottom: 20px; + font-weight: 700; +} + +.editor-sidebar label { + font-size: 0.85rem; + color: #adb5bd; + margin-bottom: 5px; +} + +/* Estilo customizado para inputs no fundo escuro */ +.dark-input { + background-color: #343a40; + border: 1px solid #495057; + color: #fff; + border-radius: 6px; +} +.dark-input:focus { + background-color: #3b4248; + color: #fff; + border-color: #0dcaf0; + box-shadow: none; +} + +/* Área de Upload Destacada */ +.ai-upload-box { + border: 2px dashed #0dcaf0; + border-radius: 10px; + padding: 20px; + text-align: center; + background: rgba(13, 202, 240, 0.1); + transition: 0.3s; + cursor: pointer; +} +.ai-upload-box:hover { + background: rgba(13, 202, 240, 0.2); +} + +/* --- LADO DIREITO: PREVIEW A4 --- */ +.preview-area { + flex: 1; + display: flex; + justify-content: center; + align-items: flex-start; + padding: 40px; + overflow-y: auto; +} + +.paper-a4 { + width: 210mm; + min-height: 297mm; + background: white; + padding: 25mm; + box-shadow: 0 0 20px rgba(0,0,0,0.15); + color: #000; + font-family: 'Georgia', serif; /* Fonte mais séria para o documento */ + font-size: 12pt; + line-height: 1.6; +} + +/* Responsividade: Em celular vira coluna única */ +@media (max-width: 900px) { + .ai-editor-container { + flex-direction: column; + min-height: auto; + } + .editor-sidebar { + width: 100%; + height: auto; + box-shadow: 0 4px 10px rgba(0,0,0,0.1); + } + .preview-area { + padding: 20px; + } + .paper-a4 { + width: 100%; + min-height: auto; + padding: 15mm; + box-shadow: none; + } +} + +@media (max-width: 576px) { + .ai-editor-container { + padding: 0; + } + .editor-sidebar { + padding: 15px; + } + .preview-area { + padding: 10px; + } + .paper-a4 { + padding: 10mm; + } +} \ No newline at end of file diff --git a/src/_assets/css/pages/secretaria/TabelaMedicos.css b/src/_assets/css/pages/secretaria/TabelaMedicos.css index da439c5..1a6b5f1 100644 --- a/src/_assets/css/pages/secretaria/TabelaMedicos.css +++ b/src/_assets/css/pages/secretaria/TabelaMedicos.css @@ -1,3 +1,16 @@ +/* TableDoctor.css */ + +.table-doctor-container { + line-height: 2.5; +} + +/* Adiciona responsividade para a tabela */ +@media (max-width: 992px) { + .table-doctor-card { + overflow-x: auto; + } +} + .table-doctor-container { line-height: 2.5; } diff --git a/src/_assets/css/pages/secretaria/TabelaPacientes.css b/src/_assets/css/pages/secretaria/TabelaPacientes.css index 55e3557..c4fa388 100644 --- a/src/_assets/css/pages/secretaria/TabelaPacientes.css +++ b/src/_assets/css/pages/secretaria/TabelaPacientes.css @@ -1,3 +1,16 @@ +/* TablePaciente.css */ + +.table-paciente-container { + line-height: 2.5; +} + +/* Adiciona responsividade para a tabela */ +@media (max-width: 992px) { + .table-paciente-card { + overflow-x: auto; + } +} + .table-paciente-container { line-height: 2.5; } diff --git a/src/components/agendamento/FormAgendamento.jsx b/src/components/agendamento/FormAgendamento.jsx index 50b4f18..be0ada7 100644 --- a/src/components/agendamento/FormAgendamento.jsx +++ b/src/components/agendamento/FormAgendamento.jsx @@ -1,6 +1,7 @@ //FormNovaConsulta.jsx import { useState, useEffect, useCallback } from "react"; +import InputMask from "react-input-mask"; import { GetPatientByCPF, GetAllPatients } from "../../_assets/utils/Functions-Endpoints/Patient"; import { GetAllDoctors } from "../../_assets/utils/Functions-Endpoints/Doctor"; import { useAuth } from "../../_assets/utils/AuthProvider"; @@ -13,92 +14,151 @@ const FormNovaConsulta = ({ onCancel, onSave, setAgendamento, agendamento }) => const [sessoes, setSessoes] = useState(1); const [tempoBaseConsulta] = useState(30); - const [showSuccessModal, setShowSuccessModal] = useState(false); + const [horarioInicio, setHorarioInicio] = useState(""); + const [horarioTermino, setHorarioTermino] = useState(""); + const [horariosDisponiveis, sethorariosDisponiveis] = useState([]); + const [status, setStatus] = useState(agendamento?.status || "confirmed"); + const [isSubmitting, setIsSubmitting] = useState(false); + const [todosProfissionais, setTodosProfissionais] = useState([]); const [profissionaisFiltrados, setProfissionaisFiltrados] = useState([]); - const [isDropdownOpen, setIsDropdownOpen] = useState(false); - const [horarioInicio, setHorarioInicio] = useState(''); - const [horarioTermino, setHorarioTermino] = useState(''); - const [horariosDisponiveis, sethorariosDisponiveis] = useState([]); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); - const [todosPacientes, setTodosPacientes] = useState([]) - const [pacientesFiltrados, setPacientesFiltrados] = useState([]) - const [isDropdownPacienteOpen, setIsDropdownPacienteOpen] = useState(false) + const [todosPacientes, setTodosPacientes] = useState([]); + const [pacientesFiltrados, setPacientesFiltrados] = useState([]); + const [isDropdownPacienteOpen, setIsDropdownPacienteOpen] = useState(false); - const [status, setStatus] = useState("confirmed") + const authHeader = getAuthorizationHeader(); - let authHeader = getAuthorizationHeader() - const FormatCPF = (valor) => { - const digits = String(valor).replace(/\D/g, '').slice(0, 11); + const digits = String(valor).replace(/\D/g, "").slice(0, 11); return digits - .replace(/(\d{3})(\d)/, '$1.$2') - .replace(/(\d{3})(\d)/, '$1.$2') - .replace(/(\d{3})(\d{1,2})$/, '$1-$2'); + .replace(/(\d{3})(\d)/, "$1.$2") + .replace(/(\d{3})(\d)/, "$1.$2") + .replace(/(\d{3})(\d{1,2})$/, "$1-$2"); }; const handleChange = (e) => { const { value, name } = e.target; - - if (name === 'email') { - setAgendamento(prev => ({ - ...prev, - contato: { ...prev.contato, email: value } - })); - } else if (name === 'status') { - setAgendamento(prev => ({ + + if (name === "email") { + setAgendamento((prev) => ({ ...prev, - status: prev.status === 'requested' ? 'confirmed' : 'requested' + contato: { + ...(prev.contato || {}), + email: value, + }, })); - } else if (name === 'paciente_cpf') { + } else if (name === "status") { + setAgendamento((prev) => ({ + ...prev, + status: prev.status === "requested" ? "confirmed" : "requested", + })); + setStatus((prev) => (prev === "confirmed" ? "requested" : "confirmed")); + } else if (name === "paciente_cpf") { const cpfFormatted = FormatCPF(value); const fetchPatient = async () => { const patientData = await GetPatientByCPF(cpfFormatted, authHeader); if (patientData) { - setAgendamento(prev => ({ + setAgendamento((prev) => ({ ...prev, paciente_nome: patientData.full_name, - patient_id: patientData.id + patient_id: patientData.id, })); } }; - setAgendamento(prev => ({ ...prev, paciente_cpf: cpfFormatted })); + setAgendamento((prev) => ({ ...prev, paciente_cpf: cpfFormatted })); fetchPatient(); - } else if (name === 'convenio') { - setAgendamento(prev => ({ ...prev, insurance_provider: value })); + } else if (name === "convenio") { + setAgendamento((prev) => ({ ...prev, insurance_provider: value })); } else { - setAgendamento(prev => ({ ...prev, [name]: value })); + setAgendamento((prev) => ({ ...prev, [name]: value })); } }; const ChamarMedicos = useCallback(async () => { const Medicos = await GetAllDoctors(authHeader); - setTodosProfissionais(Medicos); + setTodosProfissionais(Medicos || []); }, [authHeader]); - const ChamarPacientes = useCallback (async () => { - const Pacientes = await GetAllPatients(authHeader); - setTodosPacientes(Pacientes) - console.log("pacientes") - console.log(Pacientes) - }, [authHeader]) + const ChamarPacientes = useCallback(async () => { + const Pacientes = await GetAllPatients(authHeader); + setTodosPacientes(Pacientes || []); + }, [authHeader]); + + // AUTOCOMPLETE PACIENTE + const handleSearchPaciente = (e) => { + const term = e.target.value; + setAgendamento((prev) => ({ ...prev, paciente_nome: term })); + + if (term.trim() === "") { + setPacientesFiltrados([]); + setIsDropdownPacienteOpen(false); + return; + } + + const filtered = todosPacientes.filter((p) => + p.full_name.toLowerCase().includes(term.toLowerCase()) + ); + + setPacientesFiltrados(filtered); + setIsDropdownPacienteOpen(filtered.length > 0); + }; + + const handleSelectPaciente = (paciente) => { + setAgendamento((prev) => ({ + ...prev, + patient_id: paciente.id, + paciente_nome: paciente.full_name, + paciente_cpf: paciente.cpf, + })); + setPacientesFiltrados([]); + setIsDropdownPacienteOpen(false); + }; + + // AUTOCOMPLETE PROFISSIONAL + const handleSearchProfissional = (e) => { + const term = e.target.value; + handleChange(e); + + if (term.trim() === "") { + setProfissionaisFiltrados([]); + setIsDropdownOpen(false); + return; + } + + const filtered = todosProfissionais.filter((p) => + p.full_name.toLowerCase().includes(term.toLowerCase()) + ); + + setProfissionaisFiltrados(filtered); + setIsDropdownOpen(filtered.length > 0); + }; + + const handleSelectProfissional = (profissional) => { + setAgendamento((prev) => ({ + ...prev, + doctor_id: profissional.id, + nome_medico: profissional.full_name, + })); + setProfissionaisFiltrados([]); + setIsDropdownOpen(false); + }; + + const formatarHora = (datetimeString) => { + return datetimeString?.substring(11, 16) || ""; + }; useEffect(() => { - - console.log("Horario","tessssste" ) - if (agendamento?.scheduled_at) { - setHorarioInicio(formatarHora(agendamento.scheduled_at)); - } - }, []) + if (agendamento?.scheduled_at) { + setHorarioInicio(formatarHora(agendamento.scheduled_at)); + } + }, []); - useEffect(() => { + useEffect(() => { ChamarMedicos(); - }, [ChamarMedicos]); - - useEffect(() => { - - ChamarPacientes() - }, [ChamarPacientes]) + ChamarPacientes(); + }, [ChamarMedicos, ChamarPacientes]); useEffect(() => { if (!agendamento.dataAtendimento || !agendamento.doctor_id) return; @@ -106,7 +166,7 @@ const FormNovaConsulta = ({ onCancel, onSave, setAgendamento, agendamento }) => const myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); myHeaders.append("apikey", API_KEY); - myHeaders.append("Authorization", `Bearer ${authHeader.split(' ')[1]}`); + myHeaders.append("Authorization", `Bearer ${authHeader.split(" ")[1]}`); const raw = JSON.stringify({ doctor_id: agendamento.doctor_id, @@ -115,322 +175,305 @@ const FormNovaConsulta = ({ onCancel, onSave, setAgendamento, agendamento }) => }); const requestOptions = { - method: 'POST', + method: "POST", headers: myHeaders, body: raw, }; - fetch("https://yuanqfswhberkoevtmfr.supabase.co/functions/v1/get-available-slots", requestOptions) - .then(response => response.json()) - .then(result => sethorariosDisponiveis(result)) - .catch(error => console.log('error', error)); + fetch( + "https://yuanqfswhberkoevtmfr.supabase.co/functions/v1/get-available-slots", + requestOptions + ) + .then((response) => response.json()) + .then((result) => { + sethorariosDisponiveis(result); + }) + .catch((error) => console.log("error", error)); }, [agendamento.dataAtendimento, agendamento.doctor_id, authHeader]); - const handleSearchProfissional = (e) => { - const term = e.target.value; - handleChange(e); - - if (term.trim() === '') { - setProfissionaisFiltrados([]); - setIsDropdownOpen(false); - return; - } + const slotsArray = Array.isArray(horariosDisponiveis) + ? horariosDisponiveis + : horariosDisponiveis?.slots || []; - const filtered = todosProfissionais.filter(p => - p.full_name.toLowerCase().includes(term.toLowerCase()) - ); - - setProfissionaisFiltrados(filtered); - setIsDropdownOpen(filtered.length > 0); - }; - - const handleSearchPaciente = (e) => { - const term = e.target.value; - handleChange(e); - - if (term.trim() === '') { - setPacientesFiltrados([]); - setIsDropdownPacienteOpen(false); - return; - } - - const filtered = todosPacientes.filter(p => - p.full_name.toLowerCase().includes(term.toLowerCase()) - ); - console.log(filtered.length > 0, "filtrados") - - setPacientesFiltrados(filtered); - setIsDropdownPacienteOpen(filtered.length > 0); - } - - const handleSelectProfissional = (profissional) => { - setAgendamento(prev => ({ - ...prev, - doctor_id: profissional.id, - nome_medico: profissional.full_name - })); - setProfissionaisFiltrados([]); - setIsDropdownOpen(false); - }; - - const handleSelectPaciente = (paciente) => { - setAgendamento(prev => ({ - ...prev, - patient_id:paciente.id, - paciente_nome: paciente.full_name, - paciente_cpf: paciente.cpf - })) - setProfissionaisFiltrados([]) - setIsDropdownPacienteOpen(false) - - } - - const formatarHora = (datetimeString) => { - return datetimeString?.substring(11, 16) || ''; - }; - - const opcoesDeHorario = horariosDisponiveis?.slots?.map(item => ({ + const opcoesDeHorario = slotsArray.map((item) => ({ value: formatarHora(item.datetime), label: formatarHora(item.datetime), - disabled: !item.available - })) || []; + disabled: !item.available, + })); - const calcularHorarioTermino = useCallback((inicio, sessoes, tempoBase) => { - if (!inicio || inicio.length !== 5 || !inicio.includes(':')) return ''; + const calcularHorarioTermino = useCallback((inicio, sessoesParam, tempoBase) => { + if (!inicio || inicio.length !== 5 || !inicio.includes(":")) return ""; - const [horas, minutos] = inicio.split(':').map(Number); - const minutosInicio = (horas * 60) + minutos; - const duracaoTotalMinutos = sessoes * tempoBase; + const [horas, minutos] = inicio.split(":").map(Number); + const minutosInicio = horas * 60 + minutos; + const duracaoTotalMinutos = sessoesParam * tempoBase; const minutosTermino = minutosInicio + duracaoTotalMinutos; const horaTermino = Math.floor(minutosTermino / 60) % 24; const minutoTermino = minutosTermino % 60; - const formatar = (num) => String(num).padStart(2, '0'); + const formatar = (num) => String(num).padStart(2, "0"); return `${formatar(horaTermino)}:${formatar(minutoTermino)}`; + }, []); useEffect(() => { - const novoTermino = calcularHorarioTermino(horarioInicio, sessoes, tempoBaseConsulta); + const novoTermino = calcularHorarioTermino( + horarioInicio, + sessoes, + tempoBaseConsulta +); + setHorarioTermino(novoTermino); - setAgendamento(prev => ({ + setAgendamento((prev) => ({ ...prev, - horarioTermino: novoTermino + horarioTermino: novoTermino, })); - }, [horarioInicio, sessoes, tempoBaseConsulta, setAgendamento, calcularHorarioTermino]); + }, [horarioInicio, sessoes, tempoBaseConsulta, calcularHorarioTermino, setAgendamento]); const handleSubmit = (e) => { e.preventDefault(); - setShowSuccessModal(true); - }; + if (isSubmitting) return; - const handleCloseModal = () => { - setShowSuccessModal(false); - onSave({ ...agendamento, horarioInicio: horarioInicio, status:status }); + if ( + !agendamento.doctor_id || + !agendamento.patient_id || + !agendamento.dataAtendimento || + !horarioInicio + ) { + alert( + "Por favor, preencha o profissional, paciente, data e horário de início." + ); + return; + } + + setIsSubmitting(true); + + const payload = { + ...agendamento, + horarioInicio, + status, + duration_minutes: sessoes * tempoBaseConsulta, + }; + + onSave?.(payload); + setIsSubmitting(false); }; const handleCheckbox = () => { - if(status === "confirmed"){ - setStatus("requested") - }else{ - setStatus("confirmed") + if (status === "confirmed") { + setStatus("requested"); + setAgendamento((prev) => ({ ...prev, status: "requested" })); + } else { + setStatus("confirmed"); + setAgendamento((prev) => ({ ...prev, status: "confirmed" })); } - - } + }; - return (
- {showSuccessModal && ( -
-
-
-
Sucesso
-
-
-

Agendamento salvo com sucesso!

-
-
- -
-
-
- )} + return ( +
+
+

Informações do paciente

- -

Informações do paciente

+
+
+
+ + +
-
-
-
- - handleSearchPaciente(e)} - value={agendamento?.paciente_nome || ""} - autoComplete="off" - /> -
- {isDropdownPacienteOpen && pacientesFiltrados.length > 0 && ( -
- {pacientesFiltrados.map((paciente) => ( -
handleSelectPaciente(paciente)} - > - {`${paciente.full_name.split(" ")[0]} ${paciente.full_name.split(" ")[1]} - ${paciente.cpf}`} +
+ + +
+ + {isDropdownPacienteOpen && pacientesFiltrados.length > 0 && ( +
+ {pacientesFiltrados.map((paciente) => ( +
handleSelectPaciente(paciente)} + > + {`${paciente.full_name} - ${paciente.cpf}`} +
+ ))}
- ))} + )}
- )}
-
- - -
-
- -
-
- - -
-
- -

Informações do atendimento

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

Informações do atendimento

+ +
+
+
+ + +
+ + {isDropdownOpen && profissionaisFiltrados.length > 0 && ( +
+ {profissionaisFiltrados.map((profissional) => ( +
handleSelectProfissional(profissional)} + > + {profissional.full_name} +
+ ))} +
+ )} +
+ +
+ +
-
-
- - +
+
+ + +
+ + +
+
- -
- - -
- -
- ); }; -export default FormNovaConsulta; +export default FormNovaConsulta; \ No newline at end of file diff --git a/src/components/medico/FormRelatorio.jsx b/src/components/medico/FormRelatorio.jsx index 320578a..5f33e77 100644 --- a/src/components/medico/FormRelatorio.jsx +++ b/src/components/medico/FormRelatorio.jsx @@ -1,5 +1,3 @@ -//Nesta página falta: ajustar caminho do CSS - import { useState } from 'react' import { useNavigate } from 'react-router-dom' import { useAuth } from '../_assets/utils/AuthProvider' @@ -9,49 +7,92 @@ import html2pdf from 'html2pdf.js' import '../../_assets/css/medico/FormRelatorio.css' -const FormRelatorio = ({onSave, DictInfo, setDictInfo }) => { - const {getAuthorizationHeader} = useAuth() +const FormRelatorio = ({ onSave, DictInfo, setDictInfo }) => { + const { getAuthorizationHeader } = useAuth() let authHeader = getAuthorizationHeader() - const navigate= useNavigate() - + const navigate = useNavigate() + const [showModal, setShowModal] = useState(false) + + // --- NOVO: Estado para controlar o loading da transcrição --- + const [isTranscribing, setIsTranscribing] = useState(false); + + // --- NOVA FUNÇÃO: Envia o áudio e preenche o formulário --- + const handleAudioUpload = async (e) => { + const file = e.target.files[0]; + if (!file) return; + + setIsTranscribing(true); // Ativa o spinner + + const formData = new FormData(); + formData.append('audio', file); // 'audio' deve ser o nome esperado no backend + + try { + // ⚠️ ATENÇÃO: Substitua essa URL pela rota do seu backend que criamos + const response = await fetch('http://localhost:3001/api/transcrever-relatorio', { + method: 'POST', + body: formData, + // headers: { 'Authorization': authHeader } // Descomente se seu backend precisar de token + }); + + if (!response.ok) throw new Error("Falha na transcrição"); + + const data = await response.json(); + + // Atualiza o DictInfo com os dados vindos da IA + setDictInfo((prev) => ({ + ...prev, + exam: data.exam || prev.exam, // Preenche se a IA achou, senão mantém o antigo + diagnostico: data.diagnostico || prev.diagnostico, + conclusao: data.conclusao || prev.conclusao + })); + + } catch (error) { + console.error("Erro no upload de áudio:", error); + alert("Não foi possível gerar o relatório por áudio. Verifique o backend."); + } finally { + setIsTranscribing(false); // Desativa o spinner + e.target.value = null; // Limpa o input para permitir enviar o mesmo arquivo novamente se quiser + } + }; + // ----------------------------------------------------------- const BaixarPDFdoRelatorio = () => { const elemento = document.getElementById("folhaA4"); // tua div do relatório - const opt = { - margin: 0, - filename: `relatorio_${DictInfo?.paciente_nome || "paciente"}.pdf`, - html2canvas: { scale: 2 }, - jsPDF: { unit: "mm", format: "a4", orientation: "portrait" }, - }; + const opt = { + margin: 0, + filename: `relatorio_${DictInfo?.paciente_nome || "paciente"}.pdf`, + html2canvas: { scale: 2 }, + jsPDF: { unit: "mm", format: "a4", orientation: "portrait" }, + }; - html2pdf().set(opt).from(elemento).save(); + html2pdf().set(opt).from(elemento).save(); } const handleChange = (e) => { const { name, value } = e.target; console.log(name, value) - if(name === 'paciente_cpf') { - const formattedCPF = FormatCPF(value); - setDictInfo((prev) => ({ ...prev, [name]: formattedCPF })); + if (name === 'paciente_cpf') { + const formattedCPF = FormatCPF(value); + setDictInfo((prev) => ({ ...prev, [name]: formattedCPF })); - const fetchPatient = async () => { - const patientData = await GetPatientByCPF(formattedCPF, authHeader); - if (patientData) { - setDictInfo((prev) => ({ - ...prev, - paciente_cpf:value, - paciente_nome: patientData.full_name, - paciente_id: patientData.id - })); + const fetchPatient = async () => { + const patientData = await GetPatientByCPF(formattedCPF, authHeader); + if (patientData) { + setDictInfo((prev) => ({ + ...prev, + paciente_cpf: value, + paciente_nome: patientData.full_name, + paciente_id: patientData.id + })); + } + + }; + if (formattedCPF.length === 14) { + fetchPatient(); } - - }; - if(formattedCPF.length === 14){ - fetchPatient(); - } - }else{ - setDictInfo((prev) => ({ ...prev, [name]: value })); + } else { + setDictInfo((prev) => ({ ...prev, [name]: value })); } } @@ -59,145 +100,164 @@ const FormRelatorio = ({onSave, DictInfo, setDictInfo }) => { e.preventDefault(); console.log(DictInfo) setShowModal(true) - - -onSave({ - "patient_id": DictInfo.paciente_id, - - "exam": DictInfo.exam, - "diagnosis": DictInfo.diagnosis, - "conclusion": DictInfo.conclusao, - "status": "draft", - "requested_by": DictInfo.requested_by, - - "hide_date": false, - "hide_signature": false, -}); + onSave({ + "patient_id": DictInfo.paciente_id, + "exam": DictInfo.exam, + "diagnosis": DictInfo.diagnostico, // Garanta que o backend espera 'diagnosis' mas seu state usa 'diagnostico' + "conclusion": DictInfo.conclusao, + "status": "draft", + "requested_by": DictInfo.requested_by, + "hide_date": false, + "hide_signature": false, + }); } - return ( -
- {showModal &&( -
-
-
-
-
Relatório criado com sucesso
- + return ( +
+ {showModal && ( +
+
+
+
+
Relatório criado com sucesso
+ +
+
+

Você também pode baixa-lo agora em pdf

+
+
+ + + +
+
+
-
-

Você também pode baixa-lo agora em pdf

-
-
- + )} - -
-
-
-
- )} - -
- -
-
- -
- - -
- -
- - -
- - -
- - -
- -
- - -
- - -
- - -
- -
- -
-
- - -
- -
- - -
-
- - +
- -
+ {/* --- ÁREA DE UPLOAD DE ÁUDIO (INSERIDA AQUI) --- */} +
+ +
+ {isTranscribing ? ( +
+
+ A IA está gerando o relatório... aguarde. +
+ ) : ( + + )} +
+ Envie um áudio ditando o exame, diagnóstico e conclusão. +
+ {/* ----------------------------------------------- */} -

Modelo do relatório

-
+
+
-
-

Clinica Rise up

-

Dr {DictInfo.requested_by} - CRM/SP 123456

-

Avenida - (79) 9 4444-4444

-
+
+ + +
-
-

Paciente: {DictInfo?.paciente_nome}

-

Data de nascimento:

+
+ + +
-

Data do exame: {DictInfo.data_exam}

-

Exame: {DictInfo.exam}

+
+ + +
-

Diagnostico: {DictInfo.diagnostico}

+
+ + +
-

Conclusão: {DictInfo.conclusao}

- -
-
-

Dr {DictInfo.requested_by}

-

Emitido em: 0

-
+
+ + +
-
- -
- ) +
+ +
+
+ + +
+ +
+ + +
+
+ + + + +
+ +

Modelo do relatório

+
+ +
+

Clinica Rise up

+

Dr {DictInfo.requested_by} - CRM/SP 123456

+

Avenida - (79) 9 4444-4444

+
+ +
+

Paciente: {DictInfo?.paciente_nome}

+

Data de nascimento:

+ + {/* Corrigi de data_exam para data_exame para bater com o state */} +

Data do exame: {DictInfo.data_exame}

+ +

Exame: {DictInfo.exam}

+ +

Diagnostico: {DictInfo.diagnostico}

+ +

Conclusão: {DictInfo.conclusao}

+ +
+ +
+

Dr {DictInfo.requested_by}

+

Emitido em: {new Date().toLocaleDateString()}

+
+ +
+ +
+ ) } export default FormRelatorio \ No newline at end of file diff --git a/src/data/sidebar-items-adm.json b/src/data/sidebar-items-adm.json index 5b4c554..884b4a7 100644 --- a/src/data/sidebar-items-adm.json +++ b/src/data/sidebar-items-adm.json @@ -25,5 +25,10 @@ "name": "Painel Administrativo", "icon": "file-bar-graph-fill", "url": "/admin/painel" + }, + { + "name": "Gestão de Usuários", + "icon": "people-fill", + "url": "/admin/gestao" } -] +] \ No newline at end of file diff --git a/src/data/sidebar-items-medico.json b/src/data/sidebar-items-medico.json index c008eeb..1b23f18 100644 --- a/src/data/sidebar-items-medico.json +++ b/src/data/sidebar-items-medico.json @@ -5,11 +5,17 @@ "url": "/medico/agendamento" }, + { + "name": "Relatório por Áudio", + "icon": "file-earmark-plus-fill", + "url": "/medico/novo-relatorio-audio" + }, + { "name": "Relatórios", "icon": "file-earmark-text-fill", "url": "/medico/relatorios" - }, + }, { "name": "Chat com pacientes", diff --git a/src/pages/admin/DashboardUsuarios.jsx b/src/pages/admin/DashboardUsuarios.jsx index f7d43e2..38bfe3c 100644 --- a/src/pages/admin/DashboardUsuarios.jsx +++ b/src/pages/admin/DashboardUsuarios.jsx @@ -1,13 +1,12 @@ //gestao.jsx import { FaEdit, FaTrash } from "react-icons/fa"; +import React from "react"; import "../../_assets/css/pages/admin/DashboardUsuarios.css"; function UserDashboard() { return ( - -
- +

Gestão de Usuários

@@ -90,8 +89,9 @@ function UserDashboard() { Ativo 20/12/2024, 08:30 - - + + + @@ -102,8 +102,9 @@ function UserDashboard() { Ativo 19/12/2024, 14:20 - - + + + @@ -114,8 +115,9 @@ function UserDashboard() { Ativo 20/12/2024, 07:45 - - + + + @@ -126,8 +128,9 @@ function UserDashboard() { Inativo 15/12/2024, 16:30 - - + + + @@ -137,5 +140,4 @@ function UserDashboard() { ); } - export default UserDashboard; \ No newline at end of file diff --git a/src/pages/financeiro/DashboardFinanceiro.jsx b/src/pages/financeiro/DashboardFinanceiro.jsx index 51bc7a3..08a206a 100644 --- a/src/pages/financeiro/DashboardFinanceiro.jsx +++ b/src/pages/financeiro/DashboardFinanceiro.jsx @@ -10,72 +10,77 @@ const CONVENIOS_LIST = [ "SulAmérica", "Unimed", "Cassio", - "Outro" + "Outro", ]; function CurrencyInput({ value, onChange, label, id }) { const formattedValue = useMemo(() => { let numericValue = Number(value) || 0; - + let stringValue = String(numericValue); while (stringValue.length < 3) { - stringValue = '0' + stringValue; + stringValue = "0" + stringValue; } const integerPart = stringValue.slice(0, -2); const decimalPart = stringValue.slice(-2); - - const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, '.'); + + const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, "."); return `R$ ${formattedInteger},${decimalPart}`; }, [value]); - const handleKeyDown = useCallback((e) => { - const key = e.key; + const handleKeyDown = useCallback( + (e) => { + const key = e.key; - if (['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab'].includes(key)) { - if (key === 'Backspace' || key === 'Delete') { + if ( + ["Backspace", "Delete", "ArrowLeft", "ArrowRight", "Tab"].includes(key) + ) { + if (key === "Backspace" || key === "Delete") { e.preventDefault(); const numericValue = value || 0; let newValueString = String(numericValue); if (newValueString.length <= 1) { - onChange(0); + onChange(0); } else { - const newNumericValue = parseInt(newValueString.slice(0, -1)) || 0; - onChange(newNumericValue); + const newNumericValue = parseInt(newValueString.slice(0, -1)) || 0; + onChange(newNumericValue); } + } + return; + } + + if (!/^\d$/.test(key)) { + e.preventDefault(); + return; } - return; - } - if (!/^\d$/.test(key)) { e.preventDefault(); - return; - } - - e.preventDefault(); - - const digit = key; - const numericValue = value || 0; - let newValueString = String(numericValue) + digit; - - if (newValueString.length > 10) return; + const digit = key; + const numericValue = value || 0; - const newNumericValue = parseInt(newValueString); + let newValueString = String(numericValue) + digit; - onChange(newNumericValue); - }, [value, onChange]); + if (newValueString.length > 10) return; + + const newNumericValue = parseInt(newValueString); + + onChange(newNumericValue); + }, + [value, onChange] + ); return (
- {}} + className="input-field currency-input" + type="text" + value={formattedValue} + onChange={() => {}} onKeyDown={handleKeyDown} placeholder="R$ 0,00" /> @@ -88,12 +93,12 @@ function mockFetchPagamentos() { { id: "PAY-001", paciente: { nome: "Sarah Oliveira", convenio: "Unimed" }, - valor: 20000, + valor: 20000, forma_pagamento: "Cartão", data_vencimento: "2025-09-30", status: "pendente", desconto: 0, - observacoes: "Pagamento parcelado em 2x" + observacoes: "Pagamento parcelado em 2x", }, { id: "PAY-002", @@ -102,8 +107,8 @@ function mockFetchPagamentos() { forma_pagamento: "Dinheiro", data_vencimento: "2025-09-15", status: "pago", - desconto: 1000, - observacoes: "" + desconto: 1000, + observacoes: "", }, { id: "PAY-003", @@ -113,18 +118,18 @@ function mockFetchPagamentos() { data_vencimento: "2025-09-20", status: "vencido", desconto: 0, - observacoes: "Não respondeu ao contato" + observacoes: "Não respondeu ao contato", }, - { + { id: "PAY-004", paciente: { nome: "Carlos Almeida", convenio: "Particular" }, valor: 10000, forma_pagamento: "Transferência", data_vencimento: "2025-09-29", status: "pago", - desconto: 500, - observacoes: "Desconto por pagamento adiantado" - } + desconto: 500, + observacoes: "Desconto por pagamento adiantado", + }, ]; } @@ -134,7 +139,11 @@ export default function FinanceiroDashboard() { const [query, setQuery] = useState(""); const [filtroStatus, setFiltroStatus] = useState("Todos"); const [novoPagamento, setNovoPagamento] = useState(false); - const [summary, setSummary] = useState({ totalRecebido: 0, totalAReceber: 0, totalDescontos: 0 }); + const [summary, setSummary] = useState({ + totalRecebido: 0, + totalAReceber: 0, + totalDescontos: 0, + }); useEffect(() => { const data = mockFetchPagamentos(); @@ -143,7 +152,13 @@ export default function FinanceiroDashboard() { function formatCurrency(centavos) { const valorEmReais = centavos / 100; - return "R$ " + valorEmReais.toFixed(2).replace(".", ",").replace(/\B(?=(\d{3})+(?!\d))/g, '.'); + return ( + "R$ " + + valorEmReais + .toFixed(2) + .replace(".", ",") + .replace(/\B(?=(\d{3})+(?!\d))/g, ".") + ); } function getValorLiquido(valor, desconto) { @@ -151,11 +166,12 @@ export default function FinanceiroDashboard() { } const filteredPagamentos = useMemo(() => { - return pagamentos.filter(p => { + return pagamentos.filter((p) => { const q = query.toLowerCase(); const statusOk = filtroStatus === "Todos" || p.status === filtroStatus; - const buscaOk = p.paciente.nome.toLowerCase().includes(q) || - p.id.toLowerCase().includes(q); + const buscaOk = + p.paciente.nome.toLowerCase().includes(q) || + p.id.toLowerCase().includes(q); return statusOk && buscaOk; }); }, [pagamentos, query, filtroStatus]); @@ -165,47 +181,55 @@ export default function FinanceiroDashboard() { let aReceber = 0; let descontos = 0; - filteredPagamentos.forEach(p => { + filteredPagamentos.forEach((p) => { const valorLiquido = getValorLiquido(p.valor, p.desconto); - if (p.status === 'pago') { + if (p.status === "pago") { recebido += valorLiquido; descontos += p.desconto; } else { - aReceber += p.valor; + aReceber += valorLiquido; } }); setSummary({ totalRecebido: recebido, totalAReceber: aReceber, - totalDescontos: descontos + totalDescontos: descontos, }); }, [filteredPagamentos]); function handleDelete(id) { if (window.confirm("Tem certeza que deseja excluir este pagamento?")) { - setPagamentos(prev => prev.filter(p => p.id !== id)); + setPagamentos((prev) => prev.filter((p) => p.id !== id)); setModalPagamento(null); } } function handleSave(pagamento) { - if (!pagamento.paciente.nome || !pagamento.valor || !pagamento.data_vencimento || !pagamento.paciente.convenio) { - alert("Preencha Paciente, Convênio, Valor e Data de Vencimento."); - return; + if ( + !pagamento.paciente.nome || + !pagamento.valor || + !pagamento.data_vencimento || + !pagamento.paciente.convenio + ) { + alert("Preencha Paciente, Convênio, Valor e Data de Vencimento."); + return; } if (novoPagamento) { - const newId = "PAY-" + (pagamentos.length + 1).toString().padStart(3, "0"); - pagamento.id = newId; - setPagamentos(prev => [...prev, pagamento]); + const newId = + "PAY-" + (pagamentos.length + 1).toString().padStart(3, "0"); + pagamento.id = newId; + setPagamentos((prev) => [...prev, pagamento]); } else { - setPagamentos(prev => prev.map(p => p.id === pagamento.id ? pagamento : p)); + setPagamentos((prev) => + prev.map((p) => (p.id === pagamento.id ? pagamento : p)) + ); } setModalPagamento(null); setNovoPagamento(false); } - + const closeModal = () => { setModalPagamento(null); setNovoPagamento(false); @@ -214,51 +238,54 @@ export default function FinanceiroDashboard() { return (

Controle Financeiro

- +
-

Total Recebido (Filtrado)

-

{formatCurrency(summary.totalRecebido)}

+

Total Recebido (Filtrado)

+

{formatCurrency(summary.totalRecebido)}

-

Total a Receber (Filtrado)

-

{formatCurrency(summary.totalAReceber)}

+

Total a Receber (Filtrado)

+

{formatCurrency(summary.totalAReceber)}

-

Descontos Aplicados

-

{formatCurrency(summary.totalDescontos)}

+

Descontos Aplicados

+

{formatCurrency(summary.totalDescontos)}

-
-
- +
+ setQuery(e.target.value)} + placeholder="Buscar paciente" + value={query} + onChange={(e) => setQuery(e.target.value)} style={{ flexGrow: 1 }} /> - setFiltroStatus(e.target.value)} + > - - - {gerarNumerosWaitPages().map(pagina => ( -
  • - -
  • - ))} -
  • - -
  • - - -
    -
    - )} -
    -
    -
    -
    - -
    - )} - + ); + })} +
    +
    ) : ( - { - setPageConsulta(false); - fetchAppointments(); - }} - /> +
    +
    +
    +
    +
    +

    Fila de Espera

    +
    +
    +
    +
    + {" "} + Filtros +
    +
    + + setWaitlistSearch(e.target.value) + } + /> + + Digite o nome do paciente, CPF ou nome do médico + +
    +
    +
    + + Ordenar por: + + +
    +
    +
    +
    + {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?.medico_nome} + {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 && } +
    - ); -} + ) : ( + { + setPageConsulta(false); + fetchAppointments(); + }} + /> + )} + {showDeleteModal && } +
    + ); +}; export default Agendamento; \ No newline at end of file diff --git a/src/pages/medico/FormRelatorio.jsx b/src/pages/medico/FormRelatorio.jsx index 56c7ec9..909dbbd 100644 --- a/src/pages/medico/FormRelatorio.jsx +++ b/src/pages/medico/FormRelatorio.jsx @@ -1,7 +1,7 @@ //FormNovoRelatorio.jsx import { useEffect, useState, useRef } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useLocation } from 'react-router-dom'; import { useAuth } from '../../_assets/utils/AuthProvider'; import { GetAllPatients } from '../../_assets/utils/Functions-Endpoints/Patient'; import { GetAllDoctors } from '../../_assets/utils/Functions-Endpoints/Doctor'; @@ -16,6 +16,7 @@ const FormNovoRelatorio = () => { const { getAuthorizationHeader } = useAuth(); const authHeader = getAuthorizationHeader(); const navigate = useNavigate(); + const location = useLocation(); const [patients, setPatients] = useState([]); const [doctors, setDoctors] = useState([]); @@ -37,6 +38,7 @@ const FormNovoRelatorio = () => { const [showDoctorDropdown, setShowDoctorDropdown] = useState(false); const patientRef = useRef(); const doctorRef = useRef(); + const [lockedFromAppointment, setLockedFromAppointment] = useState(false); useEffect(() => { let mounted = true; @@ -133,7 +135,24 @@ const FormNovoRelatorio = () => { const handleEditorChange = (html) => setForm(prev => ({ ...prev, contentHtml: html })); - // 🔹 Agora com created_by sendo o ID do usuário logado + useEffect(() => { + if (location && location.state && location.state.appointment) { + const appt = location.state.appointment; + const paciente_nome = location.state.paciente_nome || appt.paciente_nome || ''; + const medico_nome = location.state.medico_nome || appt.medico_nome || ''; + setForm(prev => ({ + ...prev, + patient_id: appt.patient_id || prev.patient_id, + patient_name: paciente_nome || prev.patient_name, + patient_birth: prev.patient_birth || '', + doctor_id: appt.doctor_id || prev.doctor_id, + doctor_name: medico_nome || prev.doctor_name, + contentHtml: generateTemplate(paciente_nome, prev.patient_birth || '', medico_nome) + })); + setLockedFromAppointment(true); + } + }, [location]); + const handleSubmit = async (e) => { e.preventDefault(); if (!form.patient_id) return alert('Selecione o paciente (clicando no item) antes de salvar.'); @@ -153,7 +172,6 @@ const FormNovoRelatorio = () => { requested_by: form.doctor_name || '' }; - // Busca o id do usuário logado (via token) let userId = null; try { const token = authHeader?.replace(/^Bearer\s+/i, '') || ''; @@ -193,8 +211,6 @@ const FormNovoRelatorio = () => { } const created = await res.json(); - console.log('Relatório criado:', created); - window.dispatchEvent(new Event('reports:refresh')); alert('Relatório criado com sucesso!'); navigate('/medico/relatorios'); @@ -215,11 +231,12 @@ const FormNovoRelatorio = () => { { setPatientQuery(e.target.value); setShowPatientDropdown(true); }} - onFocus={() => setShowPatientDropdown(true)} + value={lockedFromAppointment ? form.patient_name : patientQuery} + onChange={(e) => { if (!lockedFromAppointment) { setPatientQuery(e.target.value); setShowPatientDropdown(true); } }} + onFocus={() => { if (!lockedFromAppointment) setShowPatientDropdown(true); }} + disabled={lockedFromAppointment} /> - {showPatientDropdown && patientQuery && ( + {!lockedFromAppointment && showPatientDropdown && patientQuery && (