From 6255ebfb9d42b1f5428ac3f2459630d24df09911 Mon Sep 17 00:00:00 2001
From: Eduarda-SS <137419071+Eduarda-SS@users.noreply.github.com>
Date: Tue, 28 Oct 2025 22:33:24 -0300
Subject: [PATCH] =?UTF-8?q?Update=20de=20exce=C3=A7=C3=B5es=20para=20API?=
=?UTF-8?q?=20de=20produ=C3=A7=C3=A3o?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/FormCriarExcecao.jsx | 56 ++++--
src/pages/ExcecoesDisponibilidade.jsx | 247 ++++++++++++++++++--------
2 files changed, 214 insertions(+), 89 deletions(-)
diff --git a/src/components/FormCriarExcecao.jsx b/src/components/FormCriarExcecao.jsx
index dcfbfcf0..b53bb264 100644
--- a/src/components/FormCriarExcecao.jsx
+++ b/src/components/FormCriarExcecao.jsx
@@ -1,13 +1,17 @@
// src/components/FormCriarExcecao.jsx
import React, { useState } from "react";
+import { useAuth } from "./utils/AuthProvider"; // <-- added
+import API_KEY from "./utils/apiKeys"; // <-- added
// Assumindo que você usa o mesmo estilo
-import "./AgendarConsulta/style/formagendamentos.css";
+import "./AgendarConsulta/style/formagendamentos.css";
-const ENDPOINT_CRIAR_EXCECAO = "https://mock.apidog.com/m1/1053378-0-default/rest/v1/doctor_exceptions";
+
+const ENDPOINT_CRIAR_EXCECAO = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_exceptions";
const FormCriarExcecao = ({ onCancel, doctorID }) => {
+ const { getAuthorizationHeader, user, getUserInfo } = useAuth(); // useAuth inside component
const [dadosAtendimento, setDadosAtendimento] = useState({
profissional: doctorID || '',
tipoAtendimento: '',
@@ -38,25 +42,57 @@ const FormCriarExcecao = ({ onCancel, doctorID }) => {
}
// Adiciona ":00" se o campo de hora estiver preenchido
- const startTime = inicio ? inicio + ":00" : undefined;
- const endTime = termino ? termino + ":00" : undefined;
+ const startTime = inicio ? inicio + ":00" : null;
+ const endTime = termino ? termino + ":00" : null;
- const payload = {
+ // resolve authorization header and try to get current user id for created_by
+ let authHeader = "";
+ try {
+ authHeader = getAuthorizationHeader ? getAuthorizationHeader() : "";
+ } catch (err) {
+ console.warn("Não foi possível obter Authorization header via useAuth()", err);
+ }
+
+ // try to resolve created_by (se disponível no contexto)
+ let createdBy = user?.id || null;
+ if (!createdBy && typeof getUserInfo === "function") {
+ try {
+ const info = await getUserInfo();
+ createdBy = info?.id || info?.profile?.id || null;
+ } catch (err) {
+ console.warn("getUserInfo falhou:", err);
+ }
+ }
+ // fallback localStorage (opcional)
+ if (!createdBy) {
+ try {
+ const stored = localStorage.getItem("user");
+ if (stored) {
+ const parsed = JSON.parse(stored);
+ createdBy = parsed?.id || parsed?.user?.id || null;
+ }
+ } catch {}
+ }
+
+ const raw = JSON.stringify({
doctor_id: profissional,
date: dataAtendimento,
- start_time: startTime,
+ kind: tipoAtendimento,
+ start_time: startTime,
end_time: endTime,
- kind: tipoAtendimento,
reason: motivo,
- };
+ created_by: createdBy // pode ser null se não encontrado
+ });
var myHeaders = new Headers();
+ if (authHeader) myHeaders.append("Authorization", authHeader);
myHeaders.append("Content-Type", "application/json");
+ if (API_KEY) myHeaders.append("apikey", API_KEY); // <-- added
var requestOptions = {
method: 'POST',
headers: myHeaders,
- body: JSON.stringify(payload),
+ body: raw,
redirect: 'follow'
};
@@ -105,7 +141,7 @@ const FormCriarExcecao = ({ onCancel, doctorID }) => {
diff --git a/src/pages/ExcecoesDisponibilidade.jsx b/src/pages/ExcecoesDisponibilidade.jsx
index 7a234bec..e0f39cae 100644
--- a/src/pages/ExcecoesDisponibilidade.jsx
+++ b/src/pages/ExcecoesDisponibilidade.jsx
@@ -1,49 +1,121 @@
-import React, { useState, useEffect, useCallback } from 'react';
+import React, { useState, useEffect, useCallback, useMemo } from 'react';
import dayjs from 'dayjs';
+// Adicionar locale e plugin se quiser exibir o nome do mês em português
+import 'dayjs/locale/pt-br';
+import weekday from 'dayjs/plugin/weekday';
import FormCriarExcecao from '../components/FormCriarExcecao';
import "../components/AgendarConsulta/style/formagendamentos.css";
import "./style/Agendamento.css";
import './style/FilaEspera.css';
+import { useAuth } from '../components/utils/AuthProvider';
+import API_KEY from '../components/utils/apiKeys';
-const ENDPOINT_LISTAR = "https://mock.apidog.com/m1/1053378-0-default/rest/v1/doctor_exceptions";
-const ENDPOINT_DELETAR = "https://mock.apidog.com/m1/1053378-0-default/rest/v1/doctor_exceptions/";
+dayjs.extend(weekday);
+dayjs.locale('pt-br');
+
+// --- Configurações da API Supabase ---
+const ENDPOINT_BASE = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_exceptions";
+// const AUTH_TOKEN = "SEU_TOKEN_DE_AUTORIZACAO_AQUI"; // removed
+// ------------------------------------
+
+// Função auxiliar para calcular o range de datas
+const getDateRange = (date, view) => {
+ const startDayjs = dayjs(date);
+ let fromDate, toDate, titleRange;
+
+ if (view === 'diario') {
+ fromDate = startDayjs.format('YYYY-MM-DD');
+ toDate = startDayjs.format('YYYY-MM-DD');
+ titleRange = startDayjs.format('DD/MM/YYYY');
+ } else if (view === 'semanal') {
+ // Padrão Dayjs: Sunday=0, Monday=1.
+ // startOf('week') pode ser Domingo ou Segunda, dependendo do locale.
+ // Se precisar forçar a Segunda-feira:
+ let weekStart = startDayjs.startOf('week');
+ if (weekStart.day() !== 1) { // Se não for segunda-feira (1), ajusta
+ weekStart = startDayjs.weekday(1); // Vai para a segunda-feira desta semana
+ }
+
+ // Se a data de filtro for hoje (quinta), weekStart é a segunda-feira.
+ // O Supabase filtra por dia. O range deve incluir a semana inteira.
+ const weekEnd = weekStart.add(6, 'day');
+
+ fromDate = weekStart.format('YYYY-MM-DD');
+ toDate = weekEnd.format('YYYY-MM-DD');
+ titleRange = `Semana de ${weekStart.format('DD/MM')} a ${weekEnd.format('DD/MM')}`;
+
+ } else if (view === 'mensal') {
+ const monthStart = startDayjs.startOf('month');
+ const monthEnd = startDayjs.endOf('month');
+
+ fromDate = monthStart.format('YYYY-MM-DD');
+ toDate = monthEnd.format('YYYY-MM-DD');
+ titleRange = startDayjs.format('MMMM/YYYY').toUpperCase();
+ }
+
+ return { fromDate, toDate, titleRange };
+};
const ExcecoesDisponibilidade = () => {
+ const { getAuthorizationHeader } = useAuth(); // hook must be at top level of component
const [pageNovaExcecao, setPageNovaExcecao] = useState(false);
const [excecoes, setExcecoes] = useState([]);
const [loading, setLoading] = useState(false);
- // Filtros
const [filtroMedicoId, setFiltroMedicoId] = useState('');
const [filtroData, setFiltroData] = useState(dayjs().format('YYYY-MM-DD'));
- // Estado para controlar a visualização (Diário, Semanal)
+ // Estado para controlar a visualização ('diario', 'semanal', 'mensal')
const [visualizacao, setVisualizacao] = useState('diario');
- // Função para buscar as exceções
- const fetchExcecoes = useCallback(async (doctorId, date) => {
- setLoading(true);
- let url = `${ENDPOINT_LISTAR}?select=*`;
+ // helper to get Authorization header string (uses AuthProvider)
+ const resolveAuthHeader = () => {
+ try {
+ const h = getAuthorizationHeader(); // returns "Bearer
" or ''
+ return h || '';
+ } catch {
+ return '';
+ }
+ }
+
+ // Função para buscar as exceções (ACEITA RANGE)
+ const fetchExcecoes = useCallback(async (fromDate, toDate, doctorId) => {
+ setLoading(true);
+
+ let url = `${ENDPOINT_BASE}?select=*`;
+
+ // Filtro por ID do Médico
if (doctorId) {
- url += `&doctor_id=eq.${doctorId}`; // Assume filtro por igualdade de ID
- }
- if (date) {
- url += `&date=eq.${date}`; // Assume filtro por igualdade de data
+ url += `&doctor_id=eq.${doctorId}`;
}
+
+ // FILTRO PRINCIPAL: INTERVALO DE DATAS USANDO gte (>=) e lte (<=)
+ url += `&date=gte.${fromDate}&date=lte.${toDate}`;
+
+ const myHeaders = new Headers();
+ const authHeader = resolveAuthHeader();
+ if (authHeader) myHeaders.append("Authorization", authHeader);
+ myHeaders.append("Content-Type", "application/json");
+ if (API_KEY) myHeaders.append("apikey", API_KEY);
try {
- const requestOptions = { method: 'GET', redirect: 'follow' };
+ const requestOptions = {
+ method: 'GET',
+ headers: myHeaders,
+ redirect: 'follow'
+ };
+
const response = await fetch(url, requestOptions);
- const result = await response.json();
+ const result = await response.json();
if (response.ok && Array.isArray(result)) {
setExcecoes(result);
} else {
setExcecoes([]);
- console.error("Erro ao listar exceções:", result);
- alert("Erro ao carregar lista de exceções.");
+ console.error("Erro ao listar exceções (Status:", response.status, "):", result);
+ alert(`Erro ao carregar lista de exceções. Status: ${response.status}. Detalhes: ${result.message || JSON.stringify(result)}`);
}
} catch (error) {
console.error('Erro na requisição de listagem de exceções:', error);
@@ -52,65 +124,76 @@ const ExcecoesDisponibilidade = () => {
} finally {
setLoading(false);
}
- }, []);
+ }, [getAuthorizationHeader]);
- // Função para deletar uma exceção
- const deletarExcecao = async (id) => {
- if (!window.confirm(`Tem certeza que deseja deletar a exceção com ID: ${id}?`)) return;
+ // Calcula o range de datas e o título a ser exibido
+ const { fromDate, toDate, titleRange } = useMemo(() =>
+ getDateRange(filtroData, visualizacao),
+ [filtroData, visualizacao]
+ );
+
+ // Efeito para carregar exceções quando os filtros e a visualização mudam
+ useEffect(() => {
+ fetchExcecoes(fromDate, toDate, filtroMedicoId);
+ }, [fetchExcecoes, filtroMedicoId, fromDate, toDate]); // Dependências atualizadas
+
+ // Deleta uma exceção pelo id e atualiza a lista localmente
+ const deleteExcecao = async (id) => {
+ if (!window.confirm("Confirma exclusão desta exceção?")) return;
+ const myHeaders = new Headers();
+ const authHeader = resolveAuthHeader();
+ if (authHeader) myHeaders.append("Authorization", authHeader);
+ if (API_KEY) myHeaders.append("apikey", API_KEY);
+ myHeaders.append("Content-Type", "application/json");
try {
- const requestOptions = { method: 'DELETE', redirect: 'follow' };
- const response = await fetch(`${ENDPOINT_DELETAR}${id}`, requestOptions);
-
- if (response.ok || response.status === 204) {
- alert(`Exceção ${id} deletada com sucesso.`);
- fetchExcecoes(filtroMedicoId, filtroData); // Recarrega a lista
+ const res = await fetch(`${ENDPOINT_BASE}?id=eq.${id}`, {
+ method: 'DELETE',
+ headers: myHeaders,
+ redirect: 'follow'
+ });
+ if (res.ok) {
+ // remove locally
+ setExcecoes(prev => prev.filter(x => x.id !== id));
} else {
- const result = await response.json();
- alert(`Erro ao deletar exceção. Detalhes: ${result.message || JSON.stringify(result)}`);
+ const text = await res.text();
+ console.error('Erro ao deletar exceção', res.status, text);
+ alert(`Erro ao excluir exceção. Status: ${res.status}. ${text}`);
}
- } catch (error) {
- console.error('Erro na requisição de deleção:', error);
- alert('Erro de comunicação ao tentar deletar a exceção.');
- }
- };
-
- // Efeito para carregar exceções quando os filtros mudam
- useEffect(() => {
- fetchExcecoes(filtroMedicoId, filtroData);
- }, [fetchExcecoes, filtroMedicoId, filtroData]);
-
- // Handler de cancelamento do formulário de criação
- const handleCancelForm = (recarregar = false) => {
- setPageNovaExcecao(false);
- if (recarregar) {
- fetchExcecoes(filtroMedicoId, filtroData); // Recarrega se a criação foi bem-sucedida
+ } catch (err) {
+ console.error('Erro na requisição de exclusão:', err);
+ alert('Erro ao excluir exceção.');
+ }
+ }
+
+ const handleCancelForm = (recarregar = false) => {
+ setPageNovaExcecao(false);
+ if (recarregar) {
+ fetchExcecoes(fromDate, toDate, filtroMedicoId);
}
}
- // Se o formulário de criação estiver aberto, renderiza apenas ele
if (pageNovaExcecao) {
return ;
}
- // Renderiza a tela de listagem (layout da agenda)
-
return (
-
+ {/* Título e Botão de Criação */}
+
Gerenciar Exceções de Disponibilidade
+
- {/* Filtros e Busca (Adaptados do Agendamento) */}
+ {/* Filtros de Médico e Data */}
@@ -130,8 +213,8 @@ const ExcecoesDisponibilidade = () => {
/>
-
- {/* Botões de Visualização (Dia/Semana/Mês) - Adaptados */}
+
+ {/* Botões de Visualização (Dia/Semana/Mês) */}
- {/* Tabela de Exceções (Simulando a Tabela de Agendamentos) */}
+ {/* Tabela de Exceções (Título usa o titleRange calculado) */}
-
Exceções em {filtroData} ({excecoes.length})
+
Exceções em {titleRange} ({excecoes.length})
{loading ? (
Carregando exceções...
) : excecoes.length === 0 ? (
Nenhuma exceção encontrada para os filtros aplicados.
) : (
-
+
- | ID Exceção |
- ID Médico |
+ Médico (ID) |
Data |
Início |
Término |
- Tipo |
Motivo |
+ Criado por |
Ações |
- {excecoes.map((excecao, index) => (
-
- | {excecao.id || 'N/A'} |
- {excecao.doctor_id} |
- {excecao.date} |
- {excecao.start_time ? excecao.start_time.substring(0, 5) : 'Dia Todo'} |
- {excecao.end_time ? excecao.end_time.substring(0, 5) : 'Dia Todo'} |
+ {excecoes.map((exc) => (
+
+ {exc.doctor_id} |
+ {dayjs(exc.date).format('DD/MM/YYYY')} |
+ {exc.start_time ? dayjs(exc.start_time, 'HH:mm:ss').format('HH:mm') : '—'} |
+ {exc.end_time ? dayjs(exc.end_time, 'HH:mm:ss').format('HH:mm') : '—'} |
+ {exc.reason} |
+ {exc.created_by || '—'} |
-
- {excecao.kind}
-
- |
- {excecao.reason} |
-
- {excecao.id && (
- |
))}