revision de cloud code

This commit is contained in:
2026-06-21 17:29:16 +02:00
parent 98fd85226b
commit 8c381d98a6
22 changed files with 614 additions and 206 deletions
+16 -81
View File
@@ -5,6 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MAY Studio — Peluquería en Barcelona</title>
<meta name="description" content="MAY Studio, peluquería en Horta, Barcelona. Cortes, color, peinados y tratamientos para señoras, caballeros y niños.">
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/style.css">
@@ -211,7 +214,6 @@
// ---- Service selector state ----
let selectedServices = []; // [{id, nombre, duracion}]
let totalDuration = 0;
let allServices = []; // cache de servicios EA
// ---- Categories and Services from EasyAppointments ----
async function loadCategories() {
@@ -237,7 +239,6 @@
]);
if (!servicesRes.ok) throw new Error('HTTP ' + servicesRes.status);
const services = await servicesRes.json();
allServices = services;
// Filter services to those assigned to this provider
let displayServices = services;
@@ -264,7 +265,6 @@
const catInfo = catMap[catId] || {};
grouped[catId] = {
nombre: catInfo.name || catInfo.title || 'Otros servicios',
imagen: '',
servicios: []
};
}
@@ -570,45 +570,6 @@
document.getElementById('booking-form').style.display = 'none';
}
// Busca cliente por email o lo crea en EasyAppointments
async function findOrCreateCustomer(nombre, email, telefono) {
// Buscar
try {
const searchRes = await fetch(`/ea-api/v1/customers?q=${encodeURIComponent(email)}`);
if (searchRes.ok) {
const list = await searchRes.json();
if (Array.isArray(list) && list.length > 0) {
return list[0].id;
}
}
} catch (e) { console.warn('Búsqueda de cliente falló', e); }
// Crear nuevo
const parts = nombre.trim().split(/\s+/);
const firstName = parts[0] || nombre;
const lastName = parts.slice(1).join(' ') || '';
const createRes = await fetch('/ea-api/v1/customers', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
firstName: firstName,
lastName: lastName,
email: email,
phone: telefono || '',
notes: 'Creado desde web MAY Studio'
})
});
if (!createRes.ok) {
const errText = await createRes.text().catch(() => '');
throw new Error('No se pudo crear el cliente en EasyAppointments: ' + errText);
}
const created = await createRes.json();
return created.id;
}
function initBookingForm() {
document.getElementById('booking-form').addEventListener('submit', async function(e) {
e.preventDefault();
@@ -627,49 +588,23 @@
result.style.display = 'none';
try {
// 1. Cliente (por email)
const customerId = await findOrCreateCustomer(
form.nombre.value.trim(),
email,
form.telefono.value.trim()
);
// 2. Preparar appointment (usamos el servicio principal - el de mayor duración - + notas con todos)
const primaryServiceId = getPrimaryServiceId();
const primaryService = selectedServices.find(s => s.id === primaryServiceId) || selectedServices[0];
if (!primaryService || !primaryService.id) {
throw new Error('No se encontró ID de servicio válido');
}
const start = `${selectedDate} ${selectedHora}:00`;
// Duración total (EA usará la del servicio, pero ponemos nota)
const endDate = new Date(`${selectedDate}T${selectedHora}`);
endDate.setMinutes(endDate.getMinutes() + totalDuration);
const endHour = pad(endDate.getHours()) + ':' + pad(endDate.getMinutes());
const notes = [
`Nombre: ${form.nombre.value.trim()}`,
`Email: ${email}`,
`Teléfono: ${form.telefono.value.trim()}`,
`Servicios: ${selectedServices.map(s => s.nombre).join(' + ')}`,
`Duración total: ${totalDuration} min`,
form.mensaje.value.trim() ? `Mensaje: ${form.mensaje.value.trim()}` : ''
].filter(Boolean).join('\n');
const appointment = {
start: start,
end: `${selectedDate} ${endHour}:00`,
serviceId: primaryService.id,
// El servidor (server-side) busca/crea el cliente y crea la cita
// contra EasyAppointments. El navegador solo envía la selección.
const payload = {
nombre: form.nombre.value.trim(),
email: email,
telefono: form.telefono.value.trim(),
mensaje: form.mensaje.value.trim(),
providerId: PROVIDER_ID,
customerId: customerId,
notes: notes,
status: 'booked'
serviceIds: selectedServices.map(s => s.id).filter(Boolean),
date: selectedDate,
time: selectedHora
};
const res = await fetch('/ea-api/v1/appointments', {
const res = await fetch('/api/book', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(appointment),
body: JSON.stringify(payload),
});
if (res.ok) {
@@ -685,7 +620,7 @@
} else {
const err = await res.json().catch(() => ({}));
result.className = 'booking-result booking-error';
result.textContent = (err.message || err.error || JSON.stringify(err)) || 'Error creando la cita en EasyAppointments.';
result.textContent = err.error || 'No se pudo crear la reserva. Inténtalo de nuevo o llámanos.';
}
} catch (err) {
console.error(err);