diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 0cc9083..6beb7d9 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -33,6 +33,8 @@ import WacrmLeads from './pages/WacrmLeads'; import Chat from './pages/Chat'; import MisVentas from './pages/MisVentas'; import Login from './pages/Login'; +import Reservar from './pages/Reservar'; +import ReservarCita from './pages/ReservarCita'; import './App.css'; const FullScreenLoader: FC = () => ( @@ -135,6 +137,9 @@ const App: FC = () => { } /> + {/* Portal público de reservas: FUERA del gate de autenticación. */} + } /> + } /> } /> } /> } /> diff --git a/frontend/src/components/ReservasOnlinePanel.tsx b/frontend/src/components/ReservasOnlinePanel.tsx new file mode 100644 index 0000000..0ad89a2 --- /dev/null +++ b/frontend/src/components/ReservasOnlinePanel.tsx @@ -0,0 +1,165 @@ +import type { FC, ReactNode } from 'react'; +import { useEffect, useState } from 'react'; +import { CalendarClock, Copy, ExternalLink, Save } from 'lucide-react'; +import { Button, Input, TextArea, toast } from './ui'; +import { + odooApi, RESERVAS_ONLINE_DEFAULT, + type ClinicSettings, type ReservasOnlineSettings, +} from '../services/odoo'; +import { enlaceReserva } from '../lib/publicBooking'; + +// Editor del bloque `reservas_online` (portal público de auto-agendado) y +// botón para copiar/abrir el enlace público de la clínica. + +interface Props { + settings: ClinicSettings; + onSaved: (settings: ClinicSettings) => void; +} + +const ReservasOnlinePanel: FC = ({ settings, onSaved }) => { + const [form, setForm] = useState( + settings.reservas_online || RESERVAS_ONLINE_DEFAULT); + const [saving, setSaving] = useState(false); + const [copiado, setCopiado] = useState(false); + + useEffect(() => { + setForm(settings.reservas_online || RESERVAS_ONLINE_DEFAULT); + }, [settings.reservas_online]); + + const set = (k: K, v: ReservasOnlineSettings[K]) => + setForm((prev) => ({ ...prev, [k]: v })); + + const enlace = enlaceReserva(settings.url_publica); + + const guardar = async () => { + setSaving(true); + try { + const res = await odooApi.putClinicSettings({ + reservas_online: form, + motivo: 'Reservas en línea desde Configuración', + }); + if (res.status === 'success') { + onSaved(res.settings); + toast.success('Reservas en línea guardadas'); + } + } catch (err) { + const msg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message; + toast.error(msg || 'No se pudieron guardar las reservas en línea'); + } finally { + setSaving(false); + } + }; + + const copiar = async () => { + try { + await navigator.clipboard.writeText(enlace); + setCopiado(true); + setTimeout(() => setCopiado(false), 2000); + } catch { + toast.error('No se pudo copiar el enlace'); + } + }; + + return ( +
+
+ +
+

Reservas en línea

+

+ Portal público donde tus clientes agendan solos, sin usuario ni contraseña. + Mientras esté desactivado, nada cambia en el sistema ni se muestra ningún enlace. +

+
+
+ + set('activo', v)} + ayuda="Al desactivarlo, el portal responde deshabilitado y desaparecen los enlaces." + /> + +
+ set('titulo', e.target.value)} /> + set('telefono_contacto', e.target.value)} + placeholder="Opcional, para dudas de los clientes" /> +
+ +