diff --git a/src/components/FormCriarExcecao.jsx b/src/components/FormCriarExcecao.jsx index dcfbfcf..b53bb26 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 7a234be..e0f39ca 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.

) : ( - +
- - + - + - {excecoes.map((excecao, index) => ( - - - - - - + {excecoes.map((exc) => ( + + + + + + + - - ))}
ID ExceçãoID MédicoMédico (ID) Data Início TérminoTipo MotivoCriado por Ações
{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'}

{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 && ( - - )} + + +