revision de cloud code
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 98 KiB |
+16
-81
@@ -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);
|
||||
|
||||
@@ -108,15 +108,6 @@ img { max-width: 100%; display: block; }
|
||||
);
|
||||
}
|
||||
|
||||
/* Fallback gradient when no image is loaded */
|
||||
.hero-bg img[src=""]::before,
|
||||
.hero-bg img:not([src])::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(160deg, var(--bg) 40%, var(--bg-alt) 100%);
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
@@ -190,72 +181,6 @@ img { max-width: 100%; display: block; }
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
/* ============ SERVICES ============ */
|
||||
.services-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.service-card {
|
||||
background: var(--white);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(0,0,0,0.05);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.service-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
.service-img {
|
||||
width: 100%;
|
||||
height: 220px;
|
||||
overflow: hidden;
|
||||
background: var(--bg-alt);
|
||||
}
|
||||
|
||||
.service-img img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.service-body {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.service-body h3 {
|
||||
font-family: 'DM Serif Display', serif;
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.service-list {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.service-list li {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid rgba(0,0,0,0.06);
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
.service-list li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.price {
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ============ GALLERY ============ */
|
||||
.gallery-grid {
|
||||
display: grid;
|
||||
@@ -538,14 +463,6 @@ img { max-width: 100%; display: block; }
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.slot-occupied {
|
||||
background: var(--bg-alt);
|
||||
color: rgba(0,0,0,0.25);
|
||||
text-decoration: line-through;
|
||||
cursor: default;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.slot-selected {
|
||||
background: var(--accent) !important;
|
||||
color: var(--white) !important;
|
||||
@@ -624,11 +541,6 @@ img { max-width: 100%; display: block; }
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.svc-cat-card.svc-cat-locked {
|
||||
opacity: 0.35;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.svc-cat-title {
|
||||
font-family: 'DM Serif Display', serif;
|
||||
font-size: 1rem;
|
||||
@@ -714,9 +626,6 @@ img { max-width: 100%; display: block; }
|
||||
|
||||
/* ============ RESPONSIVE ============ */
|
||||
@media (max-width: 768px) {
|
||||
.services-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.gallery-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user