diff --git a/src/pages/DisponibilidadesDoctorPage.jsx b/src/pages/DisponibilidadesDoctorPage.jsx index 3f2e27f..9d70dd5 100644 --- a/src/pages/DisponibilidadesDoctorPage.jsx +++ b/src/pages/DisponibilidadesDoctorPage.jsx @@ -1,115 +1,166 @@ -import React, { useState, useEffect, useCallback, useMemo } from "react"; +import React, { useState, useEffect, useMemo } from "react"; import HorariosDisponibilidade from "../components/doctors/HorariosDisponibilidade"; import { useAuth } from "../components/utils/AuthProvider"; import API_KEY from "../components/utils/apiKeys"; -import { GetAllDoctors } from "../components/utils/Functions-Endpoints/Doctor"; +import "./style/DisponibilidadesDoctorPage.css"; -const ENDPOINT = - "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability"; +const ENDPOINT = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability"; +const DOCTORS_ENDPOINT = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors"; -const diasDaSemana = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"]; +const diasDaSemana = ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"]; -const DisponibilidadesDoctorPage = () => { +const DisponibilidadesDoctorPage = ( ) => { const { getAuthorizationHeader } = useAuth(); const [disponibilidades, setDisponibilidades] = useState([]); - const [loading, setLoading] = useState(false); const [doctors, setDoctors] = useState([]); const [searchTerm, setSearchTerm] = useState(""); - const [selectedDoctor, setSelectedDoctor] = useState(null); const [editando, setEditando] = useState(null); - const [doctorsLoading, setDoctorsLoading] = useState(true); - - useEffect(() => { - const fetchDoctors = async () => { - try { - setDoctorsLoading(true); - const data = await GetAllDoctors(); - console.log("Médicos recebidos:", data); - setDoctors(Array.isArray(data) ? data : []); - } catch (error) { - console.error("Erro ao carregar médicos:", error); - setDoctors([]); - } finally { - setDoctorsLoading(false); - } - }; - fetchDoctors(); - }, []); - - const resolveAuthHeader = () => { - try { - const h = getAuthorizationHeader(); - return h || ""; - } catch { - return ""; - } - }; + const [expandedDoctors, setExpandedDoctors] = useState({}); + const [showSuggestions, setShowSuggestions] = useState(false); const getHeaders = () => { const myHeaders = new Headers(); - const authHeader = resolveAuthHeader(); + const authHeader = getAuthorizationHeader(); if (authHeader) myHeaders.append("Authorization", authHeader); myHeaders.append("Content-Type", "application/json"); if (API_KEY) myHeaders.append("apikey", API_KEY); return myHeaders; }; - const fetchDisponibilidades = useCallback(async (doctorId = null) => { - setLoading(true); - let url = ENDPOINT; - if (doctorId) { - url += `?doctor_id=eq.${doctorId}&select=*&order=weekday.asc,start_time.asc`; - } else { - url += `?select=*&order=doctor_id.asc,weekday.asc,start_time.asc`; - } - try { - const res = await fetch(url, { method: "GET", headers: getHeaders() }); - if (!res.ok) throw new Error(`Erro HTTP: ${res.status}`); - const data = await res.json(); - setDisponibilidades(Array.isArray(data) ? data : []); - } catch (e) { - console.error("Erro ao buscar disponibilidades:", e); - alert("Erro ao carregar disponibilidades"); - setDisponibilidades([]); - } finally { - setLoading(false); - } - }, []); + useEffect(() => { + const fetchDoctors = async () => { + try { + const requestOptions = { + method: 'GET', + headers: getHeaders(), + }; + + const response = await fetch(DOCTORS_ENDPOINT, requestOptions); + const result = await response.json(); + setDoctors(Array.isArray(result) ? result : []); + } catch (error) { + console.error("Erro ao carregar médicos:", error); + setDoctors([]); + } + }; + fetchDoctors(); + }, [getAuthorizationHeader]); useEffect(() => { - if (selectedDoctor) { - fetchDisponibilidades(selectedDoctor.id); - } else { - fetchDisponibilidades(null); - } - }, [selectedDoctor, fetchDisponibilidades]); + const fetchDisponibilidades = async () => { + try { + const res = await fetch(ENDPOINT, { + method: "GET", + headers: getHeaders() + }); + if (res.ok) { + const data = await res.json(); + setDisponibilidades(Array.isArray(data) ? data : []); + } + } catch (error) { + console.error("Erro ao buscar disponibilidades:", error); + setDisponibilidades([]); + } + }; + fetchDisponibilidades(); + }, [getAuthorizationHeader]); - const atualizarDisponibilidade = async (id, dadosAtualizados) => { + const toggleExpandDoctor = (doctorId) => { + setExpandedDoctors((prev) => ({ + ...prev, + [doctorId]: !prev[doctorId], + })); + }; + + const salvarTodasDisponibilidades = async (doctorId, horariosAtualizados) => { try { - const res = await fetch(`${ENDPOINT}?id=eq.${id}`, { - method: "PATCH", - headers: getHeaders(), - body: JSON.stringify(dadosAtualizados), + const headers = getHeaders(); + + // 1. Obter as disponibilidades existentes para este médico + const existingDisponibilidadesRes = await fetch(`${ENDPOINT}?doctor_id=eq.${String(doctorId)}`, { + method: "GET", + headers, + }); + const existingDisponibilidades = existingDisponibilidadesRes.ok ? await existingDisponibilidadesRes.json() : []; + + const disponibilidadesParaManter = new Set(); + + for (const dia of horariosAtualizados) { + if (dia.isChecked && dia.blocos.length > 0) { + // Processar blocos de horários + for (const bloco of dia.blocos) { + const inicio = bloco.inicio.includes(":") ? bloco.inicio : bloco.inicio + ":00"; + const termino = bloco.termino.includes(":") ? bloco.termino : bloco.termino + ":00"; + + const payload = { + doctor_id: doctorId, + weekday: dia.weekday, + start_time: inicio, + end_time: termino, + slot_minutes: bloco.slot_minutes || 30, + appointment_type: bloco.appointment_type || "presencial", + active: true, + }; + + if (bloco.id && !bloco.isNew) { + // Atualizar disponibilidade existente + await fetch(`${ENDPOINT}?id=eq.${bloco.id}`, { + method: "PATCH", + headers, + body: JSON.stringify(payload), + }); + disponibilidadesParaManter.add(bloco.id); + } else { + // Criar nova disponibilidade + const postRes = await fetch(ENDPOINT, { + method: "POST", + headers, + body: JSON.stringify(payload), + }); + if (postRes.ok) { + const newDisp = await postRes.json(); + // Adicionar o ID da nova disponibilidade para evitar exclusão acidental + if (newDisp && newDisp.length > 0) { + disponibilidadesParaManter.add(newDisp[0].id); + } + } + } + } + } + } + + // 2. Excluir disponibilidades antigas que não foram mantidas + const disponibilidadesParaExcluir = existingDisponibilidades.filter( + (disp) => !disponibilidadesParaManter.has(disp.id) + ); + + for (const disp of disponibilidadesParaExcluir) { + await fetch(`${ENDPOINT}?id=eq.${disp.id}`, { + method: "DELETE", + headers, + }); + } + + alert("Horários atualizados com sucesso!"); + setEditando(null); + + const res = await fetch(ENDPOINT, { + method: "GET", + headers: getHeaders() }); if (res.ok) { - alert("Disponibilidade atualizada com sucesso!"); - setEditando(null); - if (selectedDoctor) fetchDisponibilidades(selectedDoctor.id); - else fetchDisponibilidades(); - } else { - const errorData = await res.json(); - console.error("Erro na resposta:", errorData); - alert("Erro ao atualizar disponibilidade"); - } - } catch (error) { - console.error("Erro:", error); - alert("Falha ao conectar com o servidor"); + const data = await res.json(); + setDisponibilidades(Array.isArray(data) ? data : []); + } + + } catch (error) { + console.error("Erro ao salvar disponibilidades:", error); + alert("Erro ao salvar os horários"); } }; const deletarDisponibilidade = async (id) => { - if (!window.confirm("Deseja realmente excluir esta disponibilidade?")) - return; + if (!window.confirm("Deseja realmente excluir esta disponibilidade?")) return; try { const res = await fetch(`${ENDPOINT}?id=eq.${id}`, { method: "DELETE", @@ -118,10 +169,6 @@ const DisponibilidadesDoctorPage = () => { if (res.ok) { alert("Disponibilidade excluída com sucesso!"); setDisponibilidades((prev) => prev.filter((d) => d.id !== id)); - } else { - const errorData = await res.json(); - console.error("Erro na resposta:", errorData); - alert("Erro ao excluir disponibilidade"); } } catch (error) { console.error("Erro:", error); @@ -129,228 +176,322 @@ const DisponibilidadesDoctorPage = () => { } }; - const initialAvailabilityParaEdicao = useMemo( - () => - diasDaSemana.map((dia, weekdayIndex) => { - const blocosDoDia = disponibilidades - .filter((d) => d.weekday === weekdayIndex && d.active !== false) - .map((d) => ({ - id: d.id, - inicio: d.start_time ? d.start_time.substring(0, 5) : "07:00", - termino: d.end_time ? d.end_time.substring(0, 5) : "17:00", - isNew: false, - slot_minutes: d.slot_minutes || 30, - appointment_type: d.appointment_type || "presencial", - active: d.active !== false, - })); - return { - dia, - weekday: weekdayIndex, - isChecked: blocosDoDia.length > 0, - blocos: blocosDoDia, - }; - }), - [disponibilidades] - ); + const disponibilidadesAgrupadas = useMemo(() => { + const agrupadas = {}; + + doctors.forEach((doctor) => { + agrupadas[doctor.id] = { + doctor_id: doctor.id, + doctor_name: doctor.full_name || doctor.name, + disponibilidades: [], + }; + }); + + disponibilidades.forEach((disp) => { + if (agrupadas[disp.doctor_id]) { + agrupadas[disp.doctor_id].disponibilidades.push(disp); + } + }); + + Object.values(agrupadas).forEach((grupo) => { + if (grupo.disponibilidades.length === 0) { + grupo.disponibilidades.push({ + id: `empty-${grupo.doctor_id}`, + doctor_id: grupo.doctor_id, + doctor_name: grupo.doctor_name, + is_empty: true, + }); + } + }); + + let resultado = Object.values(agrupadas); + + if (searchTerm) { + resultado = resultado.filter(grupo => + grupo.doctor_name.toLowerCase().includes(searchTerm.toLowerCase()) + ); + } + + return resultado; + }, [disponibilidades, doctors, searchTerm]); + + const formatTime = (timeString) => { + if (!timeString) return ""; + return timeString.includes(":") ? timeString.substring(0, 5) : timeString; + }; + + const getDiaSemana = (weekday) => { + const dias = { + 0: "Domingo", + 1: "Segunda", + 2: "Terça", + 3: "Quarta", + 4: "Quinta", + 5: "Sexta", + 6: "Sábado", + sunday: "Domingo", + monday: "Segunda", + tuesday: "Terça", + wednesday: "Quarta", + thursday: "Quinta", + friday: "Sexta", + saturday: "Sábado", + }; + + const key = typeof weekday === "string" ? weekday.toLowerCase() : weekday; + return dias[key] || "Desconhecido"; + }; + + const initialAvailabilityParaEdicao = useMemo(() => { + if (!editando) return []; + + const disponibilidadesMedico = disponibilidades.filter( + (d) => String(d.doctor_id) === String(editando) + ); + + return [1, 2, 3, 4, 5, 6, 0].map((weekday) => { + const blocosDoDia = disponibilidadesMedico + .filter((d) => d.weekday === weekday && d.active !== false) + .map((d) => ({ + id: d.id, + inicio: formatTime(d.start_time) || "07:00", + termino: formatTime(d.end_time) || "17:00", + slot_minutes: d.slot_minutes || 30, + appointment_type: d.appointment_type || "presencial", + isNew: false, + })); + + return { + dia: diasDaSemana[weekday], + weekday: weekday, + isChecked: blocosDoDia.length > 0, + blocos: blocosDoDia.length > 0 ? blocosDoDia : [{ + id: null, + inicio: "07:00", + termino: "17:00", + slot_minutes: 30, + appointment_type: "presencial", + isNew: true, + }], + }; + }); + }, [disponibilidades, editando]); const handleUpdateHorarios = (horariosAtualizados) => { - const bloco = horariosAtualizados - .flatMap((d) => d.blocos) - .find((b) => b.id === editando); - if (!bloco) return alert("Bloco não encontrado."); - const dadosAtualizados = { - start_time: bloco.inicio + ":00", - end_time: bloco.termino + ":00", - slot_minutes: bloco.slot_minutes, - appointment_type: bloco.appointment_type, - active: bloco.active, - }; - atualizarDisponibilidade(editando, dadosAtualizados); + if (!editando) return; + salvarTodasDisponibilidades(editando, horariosAtualizados); }; const filteredDoctors = useMemo(() => { if (!searchTerm) return doctors; return doctors.filter((doc) => - doc.name.toLowerCase().includes(searchTerm.toLowerCase()) + (doc.full_name || doc.name).toLowerCase().includes(searchTerm.toLowerCase()) ); }, [doctors, searchTerm]); + const handleCancelarEdicao = () => { + setEditando(null); + }; + + const handleDoctorSelect = (doctor) => { + setSearchTerm(doctor.full_name || doctor.name); + setShowSuggestions(false); + }; + + const handleClearSearch = () => { + setSearchTerm(""); + setShowSuggestions(false); + }; + + const getStatusBadgeClass = (disp) => { + if (disp.is_empty) return "status-badge status-not-configured"; + if (disp.active === false) return "status-badge status-inactive"; + return "status-badge status-active"; + }; + + const getStatusText = (disp) => { + if (disp.is_empty) return "Não configurado"; + if (disp.active === false) return "Inativa"; + return "Ativa"; + }; + return ( -
-

+
+

Disponibilidades dos Médicos

-
- { - setSearchTerm(e.target.value); - setSelectedDoctor(null); - }} - style={{ - border: "1px solid #ccc", - borderRadius: "4px", - padding: "6px", - width: "300px", - }} - /> - {searchTerm && ( -
    +
    + { + setSearchTerm(e.target.value); + setShowSuggestions(true); }} - > - {filteredDoctors.length > 0 ? ( - filteredDoctors.map((doc) => ( -
  • { - setSelectedDoctor(doc); - setSearchTerm(doc.name); - }} - style={{ - padding: "6px 8px", - cursor: "pointer", - borderBottom: "1px solid #eee", - }} - > - {doc.name} -
  • - )) - ) : ( -
  • - Nenhum médico encontrado -
  • - )} -
+ onFocus={() => setShowSuggestions(true)} + className="search-input" + /> + {searchTerm && ( + + )} +
+ + {showSuggestions && searchTerm && filteredDoctors.length > 0 && ( +
+ {filteredDoctors.map((doc) => ( +
handleDoctorSelect(doc)} + className="suggestion-item" + > + {doc.full_name || doc.name} +
+ ))} +
)}
-

- {editando ? "Editar Disponibilidade" : "Lista de Disponibilidades"}{" "} - ({disponibilidades.length}) +

+ {editando ? `Editar Horários` : "Lista de Disponibilidades"}

- {loading ? ( -

Carregando...

- ) : disponibilidades.length === 0 ? ( -

Nenhuma disponibilidade encontrada.

+ {doctors.length === 0 ? ( +

Carregando médicos...

) : editando ? ( <> - - +
+ {initialAvailabilityParaEdicao.length > 0 ? ( + + ) : ( +

Carregando horários para edição...

+ )} +
+ +
+ + + +
) : ( - - - - - - - - - - - - - - - {disponibilidades.map((disp) => { - const medico = doctors.find((d) => d.id === disp.doctor_id); - return ( - - - - - - - - - - - ); - })} - -
MédicoDia da SemanaInícioTérminoIntervalo (min)TipoStatusAções
{medico ? medico.name : disp.doctor_id}{diasDaSemana[disp.weekday]}{disp.start_time}{disp.end_time}{disp.slot_minutes || 30}{disp.appointment_type || "presencial"} - - {disp.active === false ? "Inativa" : "Ativa"} +
+ {disponibilidadesAgrupadas.length === 0 ? ( +

Nenhum médico encontrado

+ ) : ( + disponibilidadesAgrupadas.map((grupo) => ( +
+
toggleExpandDoctor(grupo.doctor_id)} + > +

+ {grupo.doctor_name} + + ({grupo.disponibilidades.filter(d => !d.is_empty).length} horários) -

-
- + + + ▼ + +
- + {expandedDoctors[grupo.doctor_id] && ( +
+
+ +
+ +
+ + + + + + + + + + + + + + {grupo.disponibilidades.map((disp) => ( + + + + + + + + + + ))} + +
Dia da SemanaInícioTérminoIntervalo (min)TipoStatusAções
+ {disp.is_empty ? "Nenhum horário cadastrado" : getDiaSemana(disp.weekday)} + + {disp.is_empty ? "-" : formatTime(disp.start_time)} + + {disp.is_empty ? "-" : formatTime(disp.end_time)} + + {disp.is_empty ? "-" : disp.slot_minutes || 30} + + {disp.is_empty ? "-" : disp.appointment_type || "presencial"} + + + {getStatusText(disp)} + + + {!disp.is_empty && ( + + )} +
+
-
+ )} +
+ )) + )} +

)} diff --git a/src/pages/style/DisponibilidadesDoctorPage.css b/src/pages/style/DisponibilidadesDoctorPage.css new file mode 100644 index 0000000..27d9d52 --- /dev/null +++ b/src/pages/style/DisponibilidadesDoctorPage.css @@ -0,0 +1,370 @@ +.disponibilidades-container { + padding: 20px; + background: #f5f7fa; + min-height: 100vh; +} + +.disponibilidades-title { + font-size: 1.5rem; + font-weight: bold; + color: #2c3e50; + margin-bottom: 20px; +} + +.search-container { + margin: 10px 0 25px 0; + position: relative; + background-color: white; + padding: 15px; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0,0,0,0.05); + border: 1px solid #e1e8ed; +} + +.search-input-container { + position: relative; +} + +.search-input { + border: 1px solid #dce1e6; + border-radius: 6px; + padding: 10px 40px 10px 12px; + width: 100%; + font-size: 14px; + box-sizing: border-box; + transition: border-color 0.2s; +} + +.search-input:focus { + outline: none; + border-color: #3498db; + box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.1); +} + +.clear-search-btn { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + background: none; + border: none; + font-size: 18px; + cursor: pointer; + color: #7f8c8d; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: background-color 0.2s; +} + +.clear-search-btn:hover { + background-color: #f8f9fa; + color: #e74c3c; +} + +.suggestions-dropdown { + border: 1px solid #e1e8ed; + border-radius: 6px; + background-color: white; + position: absolute; + z-index: 1000; + width: calc(100% - 30px); + max-height: 200px; + overflow-y: auto; + margin-top: 8px; + box-shadow: 0 4px 12px rgba(0,0,0,0.1); + left: 15px; + right: 15px; +} + +.suggestion-item { + padding: 10px 12px; + cursor: pointer; + border-bottom: 1px solid #f8f9fa; + transition: background-color 0.2s; + font-size: 14px; + color: #2c3e50; +} + +.suggestion-item:hover { + background-color: #f8f9fa; +} + +.suggestion-item:last-child { + border-bottom: none; +} + +/* Collapsible Styles */ +.doctor-group { + background-color: white; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + margin-bottom: 8px; + overflow: hidden; + border: 1px solid #e1e8ed; +} + +.doctor-header { + padding: 16px 20px; + background-color: #f1f3f5; + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; + transition: background-color 0.2s; + border-bottom: 1px solid #e1e8ed; +} + +.doctor-header:hover { + background-color: #e9ecef; +} +.doctor-name { + font-size: 1.1rem; + font-weight: 600; + color: #2c3e50; + margin: 0; +} + +.doctor-hours { + font-size: 0.9rem; + font-weight: normal; + color: #7f8c8d; + margin-left: 8px; +} + +.expand-icon { + font-size: 1rem; + color: #7f8c8d; + transform: rotate(0deg); + transition: transform 0.3s ease; +} + +.expand-icon.expanded { + transform: rotate(180deg); +} + +.doctor-content { + padding: 0; + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease-out; +} + +.doctor-group.expanded .doctor-content { + max-height: 5000px; +} + +/* Table Styles */ +.table-container { + overflow-x: auto; + padding: 0 10px 10px 10px; +} + +.disponibilidades-table { + width: 100%; + border-collapse: collapse; + font-size: 0.875rem; + background-color: white; +} + +.disponibilidades-table thead { + background-color: #f8f9fa; +} + +.disponibilidades-table thead th { + padding: 6px 16px; + text-align: left; + font-weight: 600; + color: #2c3e50; + border-bottom: 1px solid #e1e8ed; + font-size: 0.875rem; +} + +.disponibilidades-table tbody tr { + transition: background-color 0.2s; + border-bottom: 1px solid #f1f3f5; +} + +.disponibilidades-table tbody tr:last-child { + border-bottom: none; +} + +.disponibilidades-table tbody tr:hover { + background-color: #f8f9fa; +} + +.disponibilidades-table td { + padding: 12px 16px; + color: #495057; + border-bottom: 1px solid #f1f3f5; +} + +/* Status Badges */ +.status-badge { + padding: 4px 8px; + border-radius: 12px; + font-size: 0.75rem; + font-weight: 600; + color: white; + display: inline-block; + text-align: center; + min-width: 80px; +} + +.status-active { + background-color: #27ae60; +} + +.status-inactive { + background-color: #e74c3c; +} + +.status-not-configured { + background-color: #7f8c8d; +} + +/* Buttons */ +.edit-btn-container { + padding: 8px 10px 0px 10px; + background-color: #f8f9fa; +} + +.disp-btn-edit { + background-color: #ffe8a1; + color: #2c3e50; + border: 1px solid #f0d860; + border-radius: 6px; + padding: 8px 16px; + cursor: pointer; + font-weight: 600; + font-size: 0.875rem; + transition: all 0.2s; +} + +.disp-btn-edit:hover { + background-color: #ffcc00; + border-color: #e6b800; +} + +.disp-btn-delete { + background-color: #f8d7da; + color: #721c24; + border: 1px solid #f5c6cb; + border-radius: 4px; + padding: 6px 12px; + cursor: pointer; + font-weight: 500; + font-size: 0.75rem; + transition: all 0.2s; +} + +.disp-btn-delete:hover { + background-color: #f1b0b7; + border-color: #e89ca6; +} + +/* Edit Mode Styles */ +.edit-container { + background-color: white; + border-radius: 8px; + padding: 20px; + margin-bottom: 20px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + border: 1px solid #e1e8ed; +} + +.disp-buttons-container { + display: flex; + gap: 12px; + margin-top: 20px; + justify-content: flex-start; +} + +.disp-btn-primary { + padding: 10px 20px; + font-size: 0.875rem; + font-weight: 600; + border-radius: 6px; + background-color: #3498db; + color: white; + border: none; + cursor: pointer; + transition: background-color 0.2s; +} + +.disp-btn-primary:hover { + background-color: #2980b9; +} + +.disp-btn-danger { + padding: 10px 20px; + font-size: 0.875rem; + font-weight: 600; + border-radius: 6px; + background-color: #95a5a6; + color: white; + border: none; + cursor: pointer; + transition: background-color 0.2s; +} + +.disp-btn-danger:hover { + background-color: #7f8c8d; +} + +/* Section Titles */ +.section-title { + font-size: 1.25rem; + font-weight: 600; + color: #2c3e50; + margin-bottom: 20px; + padding-bottom: 12px; + border-bottom: 1px solid #e1e8ed; +} + +/* Loading and Empty States */ +.loading-text, .no-results { + text-align: center; + padding: 40px 20px; + color: #7f8c8d; + font-size: 1rem; +} + +.no-results { + background-color: white; + border-radius: 8px; + margin: 20px 0; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); +} + +/* Responsive Design */ +@media (max-width: 768px) { + .disponibilidades-container { + padding: 16px; + } + + .disponibilidades-table { + font-size: 0.75rem; + } + + .disponibilidades-table th, + .disponibilidades-table td { + padding: 8px 12px; + } + + .doctor-header { + padding: 12px 16px; + } + + .disp-buttons-container { + flex-direction: column; + } + + .disp-btn-primary, + .disp-btn-danger { + width: 100%; + } +}