111 lines
5.0 KiB
Markdown
111 lines
5.0 KiB
Markdown
# MAY Studio — Landing Page + EasyAppointments
|
|
|
|
Landing page bonita para MAY Studio (Horta, Barcelona) con reservas gestionadas por **EasyAppointments**.
|
|
|
|
El frontend (HTML/JS puro) sigue siendo el de la landing, pero **las reservas, clientes y servicios se gestionan completamente con EasyAppointments**.
|
|
|
|
## Stack actual
|
|
|
|
- **Web (estático)**: Go + proxy reverso a EasyAppointments API
|
|
- **Gestión de citas, clientes y servicios**: EasyAppointments (PHP + MySQL)
|
|
- **Base de datos**: MySQL (para EA)
|
|
- **Docker Compose**: todo junto (web + easyappointments + mysql)
|
|
|
|
## Cómo ejecutar (Desarrollo y Producción)
|
|
|
|
### 1. Configurar secretos
|
|
|
|
```sh
|
|
cp .env.example .env
|
|
# Edita .env: pon tu EA_API_KEY y una MYSQL_ROOT_PASSWORD fuerte.
|
|
```
|
|
|
|
`.env` está en `.gitignore` y nunca se sube al repositorio.
|
|
|
|
### 2. Levantar todo
|
|
|
|
```sh
|
|
docker compose up -d --build
|
|
```
|
|
|
|
- Landing + reservas: **http://localhost:8234**
|
|
- Panel de administración EasyAppointments: **http://localhost:8888**
|
|
|
|
### 3. Primer arranque (importante)
|
|
|
|
1. Abre http://localhost:8888
|
|
2. Completa el asistente de instalación de EasyAppointments.
|
|
3. Crea un **usuario administrador**.
|
|
4. Ve a **Providers** → crea al menos un proveedor (ej. "May").
|
|
5. Ve a **Services** → crea tus servicios (puedes usar la lista anterior de servicios como referencia).
|
|
6. Asigna los servicios al proveedor.
|
|
7. Configura el **Working Plan** del proveedor (horario).
|
|
8. **Anota el ID del Provider** (normalmente 1) y edita `static/index.html` → línea `const PROVIDER_ID = 1;`
|
|
|
|
### Variables (en `.env`)
|
|
|
|
- `EA_API_KEY` — token de API de EasyAppointments (Settings → API en el panel).
|
|
- `MYSQL_ROOT_PASSWORD` — contraseña de la base de datos.
|
|
- `EA_BASE_URL` — URL pública del panel de EA (cámbiala en producción).
|
|
|
|
### Actualizar
|
|
|
|
```sh
|
|
git pull
|
|
docker compose up -d --build
|
|
```
|
|
|
|
## Notas sobre la integración
|
|
|
|
**EasyAppointments es la única fuente de verdad.** El servidor Go no guarda nada:
|
|
ni clientes, ni citas, ni servicios. Todo se lee y se escribe contra la API de EA.
|
|
|
|
- Los servicios y la disponibilidad los lee el frontend de EA (a través del proxy de solo lectura).
|
|
- El calendario deshabilita los días sin ningún hueco vía `GET /api/month-availability`
|
|
(agregado server-side día a día contra EA, con caché de 10 min por mes+servicio).
|
|
- Al reservar, el navegador envía la selección a `POST /api/book` y **el servidor**:
|
|
- lee los servicios de EA (fuente de verdad de nombre y duración),
|
|
- busca o crea el cliente por **teléfono** (identifica al cliente; el email es opcional),
|
|
- crea **una** cita con el servicio de mayor duración y el bloque total en las notas.
|
|
- La gestión completa (clientes, servicios, citas, proveedores, etc.) se hace desde http://localhost:8888
|
|
|
|
## Seguridad del proxy
|
|
|
|
El servidor Go **no** expone la API de EasyAppointments tal cual:
|
|
|
|
- El proxy `/ea-api/` solo deja pasar **lecturas no sensibles** (categorías, servicios,
|
|
proveedor, disponibilidad), añadiendo la auth admin en el servidor. La lista blanca
|
|
está en `main.go` (`eaAllowedRoutes`); cualquier otra ruta/método recibe `404`
|
|
**antes** de inyectar credenciales.
|
|
- Las **escrituras y la búsqueda de clientes** no pasan por el proxy: se orquestan
|
|
server-side en `POST /api/book`. Así el navegador nunca puede listar/borrar/modificar
|
|
clientes ni citas, ni volcar datos personales de EA.
|
|
|
|
## Comportamiento multiservicio
|
|
|
|
El formulario permite seleccionar **varios servicios** (multi-checkbox).
|
|
|
|
Cómo funciona actualmente con EasyAppointments:
|
|
|
|
- **UI**: Puedes seleccionar múltiples servicios. Se muestra el resumen con nombres unidos por "+" y la duración total sumada.
|
|
- **Comprobación de disponibilidad**: Se consulta el endpoint de EA usando el servicio de **mayor duración** de los seleccionados. Luego se filtra client-side para exigir un bloque contiguo de la **duración total** (suma de todos).
|
|
- Ejemplo: servicios de 45min + 30min → total 75 min. Solo se ofrecen horas de inicio desde las que quepan 75 min seguidos sin solape.
|
|
- **Al crear la reserva**:
|
|
- Se crea **una sola cita** en EasyAppointments.
|
|
- Se usa como `serviceId` el servicio de mayor duración (para que coincida con la disponibilidad consultada).
|
|
- Se calcula la hora de fin sumando la **duración total** de todos los servicios seleccionados y se envía explícitamente el campo `end`. Así EA bloquea el tiempo completo en el calendario de la empleada.
|
|
- Solo una empleada (May) por ahora → el bloque queda reservado y no se puede reservar otra cosa en ese intervalo.
|
|
- En el campo `notes` se guarda el detalle completo:
|
|
```
|
|
Nombre: ...
|
|
Teléfono: ...
|
|
Email: ... (solo si el cliente lo dio)
|
|
Servicios: Corte + Tinte + ...
|
|
Duración total: 120 min
|
|
Mensaje: ...
|
|
```
|
|
|
|
**Limitación**: EasyAppointments modela una cita = un servicio principal. Los servicios adicionales quedan documentados en las notas del cliente y de la cita. No se crean múltiples registros de cita.
|
|
|
|
Recomendación: Si usas combinaciones frecuentes, crea "servicios combinados" directamente en EasyAppointments (ej. "Corte + Tinte Señora - 120min").
|