import React, { useState, useEffect } from 'react'; import { Calendar, Clock, User, Phone, Mail, MessageCircle, Edit2, Trash2, Plus, ChevronLeft, ChevronRight, CheckCircle2, AlertCircle, LayoutGrid, List, Loader2 } from 'lucide-react'; const API_URL = 'https://capi.orthostudioamaya.com/api.php'; export default function App() { const [appointments, setAppointments] = useState([]); const [isLoading, setIsLoading] = useState(true); const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]); const [viewMode, setViewMode] = useState('day'); // 'day' | 'month' // Estados para Modales const [isAddModalOpen, setIsAddModalOpen] = useState(false); const [rescheduleData, setRescheduleData] = useState(null); // Estado para notificaciones UI const [toast, setToast] = useState(null); // Formulario nuevo const [formData, setFormData] = useState({ patientName: '', phone: '', email: '', time: '08:00', type: 'Valoración Inicial' }); // --- Cargar datos desde la API al iniciar --- useEffect(() => { fetchAppointments(); }, []); const fetchAppointments = async () => { setIsLoading(true); try { const response = await fetch(API_URL); if (!response.ok) throw new Error('Error en la respuesta de la API'); const data = await response.json(); // Si la API devuelve un array, lo guardamos. Si devuelve un objeto de error o está vacío, ponemos array vacío. if (Array.isArray(data)) { setAppointments(data); } else { setAppointments([]); } } catch (error) { console.error("Error obteniendo citas:", error); showToast("Error al conectar con la base de datos", "error"); } finally { setIsLoading(false); } }; const showToast = (message, type = 'success') => { setToast({ message, type }); setTimeout(() => setToast(null), 4000); }; // --- Lógica de Fechas --- const changeDate = (amount) => { const currentDate = new Date(selectedDate + 'T00:00:00'); if (viewMode === 'day') { currentDate.setDate(currentDate.getDate() + amount); } else { currentDate.setMonth(currentDate.getMonth() + amount); } setSelectedDate(currentDate.toISOString().split('T')[0]); }; const formatDateLabel = (dateString) => { const options = viewMode === 'day' ? { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' } : { year: 'numeric', month: 'long' }; const date = new Date(dateString + 'T00:00:00'); // Forzar zona horaria local return date.toLocaleDateString('es-HN', options); }; // --- Operaciones CRUD (Conectadas a la API) --- // CREAR CITA (POST) const handleAddAppointment = async (e) => { e.preventDefault(); const newAptData = { ...formData, date: selectedDate }; try { const response = await fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newAptData) }); if (response.ok) { await fetchAppointments(); // Recargar datos desde la BD setIsAddModalOpen(false); // Generar link de WhatsApp para Confirmación const msg = `¡Hola ${formData.patientName}! Te confirmamos tu cita en Orthostudio Amaya para el ${formatDateLabel(selectedDate)} a las ${formData.time}.`; sendWhatsApp(formData.phone, msg); showToast('Cita agendada exitosamente'); setFormData({ patientName: '', phone: '', email: '', time: '08:00', type: 'Valoración Inicial' }); } else { const errorData = await response.json(); showToast(errorData.error || 'Error al guardar la cita', 'error'); } } catch (error) { console.error("Error:", error); showToast("Error de conexión al guardar", "error"); } }; // ELIMINAR CITA (DELETE) const handleDelete = async (id, patientName, phone) => { if (window.confirm(`¿Estás seguro de cancelar la cita de ${patientName}?`)) { try { // Corrección: Enviamos el ID directamente en la URL para que PHP lo lea sin problemas const response = await fetch(`${API_URL}?id=${id}`, { method: 'DELETE' }); if (response.ok) { await fetchAppointments(); // Recargar datos const msg = `Hola ${patientName}, te informamos que tu cita en Orthostudio Amaya ha sido cancelada. Por favor contáctanos para reprogramar.`; sendWhatsApp(phone, msg); showToast('Cita cancelada exitosamente'); } else { showToast('No se pudo cancelar la cita', 'error'); } } catch (error) { console.error("Error:", error); showToast("Error de conexión al cancelar", "error"); } } }; // REPROGRAMAR CITA (PUT) const handleRescheduleSubmit = async (e) => { e.preventDefault(); try { const response = await fetch(API_URL, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: rescheduleData.id, date: rescheduleData.newDate, time: rescheduleData.newTime }) }); if (response.ok) { await fetchAppointments(); // Recargar datos const msg = `Hola ${rescheduleData.patientName}, te confirmamos que tu cita en Orthostudio Amaya ha sido REPROGRAMADA para el ${formatDateLabel(rescheduleData.newDate)} a las ${rescheduleData.newTime}.`; sendWhatsApp(rescheduleData.phone, msg); setRescheduleData(null); showToast('Cita reprogramada exitosamente'); } else { showToast('No se pudo reprogramar la cita', 'error'); } } catch (error) { console.error("Error:", error); showToast("Error de conexión al actualizar", "error"); } }; // --- Notificaciones --- const sendWhatsApp = (phone, message) => { const url = `https://wa.me/${phone.replace(/\D/g, '')}?text=${encodeURIComponent(message)}`; window.open(url, '_blank'); }; // Filtrar citas del día seleccionado const todaysAppointments = appointments.filter(apt => apt.date === selectedDate); // --- Renderizado de la Vista Mensual --- const renderMonthView = () => { const currentDateObj = new Date(selectedDate + 'T00:00:00'); const year = currentDateObj.getFullYear(); const month = currentDateObj.getMonth(); const firstDay = new Date(year, month, 1).getDay(); // 0 (Dom) - 6 (Sáb) const daysInMonth = new Date(year, month + 1, 0).getDate(); const days = []; // Espacios vacíos previos for (let i = 0; i < firstDay; i++) { days.push(
); } // Días del mes for (let d = 1; d <= daysInMonth; d++) { const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`; const dayAppointments = appointments.filter(a => a.date === dateStr); days.push(
{ setSelectedDate(dateStr); setViewMode('day'); }} className={`p-2 border border-gray-100 min-h-[100px] md:min-h-[120px] cursor-pointer hover:bg-blue-50 transition group flex flex-col ${dateStr === selectedDate ? 'bg-blue-50 ring-2 ring-inset ring-[#0284c7]' : 'bg-white'}`}>
{d} {dayAppointments.length > 0 && {dayAppointments.length}}
{dayAppointments.slice(0, 3).map(apt => (
{apt.time} {apt.patientName.split(' ')[0]}
))} {dayAppointments.length > 3 && (
+{dayAppointments.length - 3} más
)}
); } const weekdays = ['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb']; return (
{weekdays.map(day => (
{day}
))}
{days}
); }; return (
{/* Toast Notification */} {toast && (
{toast.type === 'success' ? : } {toast.message}
)} {/* Header */}

Orthostudio Amaya | Agenda en la Nube

{/* Contenido Principal: Lista de Citas */}
{/* Navegador de Fechas y Vistas */}

{formatDateLabel(selectedDate)}

{viewMode === 'day' && !isLoading && (

{todaysAppointments.length} {todaysAppointments.length === 1 ? 'cita programada' : 'citas programadas'}

)}
{/* Renderizado condicional basado en el estado de carga y vista */} {isLoading ? (

Conectando con la base de datos...

) : viewMode === 'day' ? (
{todaysAppointments.length === 0 ? (

No hay citas para este día

Disfruta el tiempo libre o agrega una nueva cita.

) : ( todaysAppointments.map((apt) => (
{/* Hora */}
{apt.time}
{/* Info Paciente */}

{apt.patientName} {apt.type}

{apt.phone} {apt.email || 'Sin correo'}
{/* Acciones */}
)) )}
) : ( renderMonthView() )}
{/* Sidebar Informativo */}

Automatización

El sistema genera el enlace de WhatsApp listo para enviarse cada vez que programas o cancelas una cita.

🟢 Sistema Activo y Sincronizado
{/* Modal Agregar Cita */} {isAddModalOpen && (

Agendar Nueva Cita

setFormData({...formData, patientName: e.target.value})} />
setFormData({...formData, phone: e.target.value})} />
setFormData({...formData, email: e.target.value})} />
setFormData({...formData, time: e.target.value})} />
)} {/* Modal Reprogramar Cita */} {rescheduleData && (

Reprogramar Cita

Paciente: {rescheduleData.patientName}

setRescheduleData({...rescheduleData, newDate: e.target.value})} />
setRescheduleData({...rescheduleData, newTime: e.target.value})} />
Se actualizará la cita y se abrirá WhatsApp para confirmar el cambio.
)}
); }