fix: el paso de especialista solo ofrece a quien da ese servicio
Elegir "Corte + arreglo de barba" y luego cualquier especialista que no fuera el barbero dejaba el asistente atascado: /slots responde 400 "El especialista no ofrece este servicio" y la UI lo pintaba como "No pudimos cargar los horarios. Intenta otra fecha" — un mensaje que manda a probar días al azar cuando ninguna fecha lo resuelve. La raíz es que el payload público listaba todos los empleados activos sin decir qué servicios ofrece cada uno, así que el cliente no tenía con qué filtrar. - GET /api/public/:slug devuelve ahora `service_ids` por empleado. El JOIN contra employees filtra por negocio: employee_services no tiene business_id propio. - El paso 2 lista solo a los elegibles, y si nadie ofrece el servicio en concreto lo dice en vez de mostrar una lista vacía. - Cambiar de servicio suelta un especialista que ya no aplique, en lugar de arrastrar una combinación que el servidor va a rechazar. - El estado de error muestra el motivo real del servidor. booking-e2e.mjs cubre el contrato: cada empleado publica service_ids, quien ofrece el servicio responde 200 y quien no responde 400. 15/15. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f48a9ac3bf
commit
521c206ba7
@@ -28,9 +28,30 @@ bookingRouter.get("/:slug", (req, res) => {
|
||||
const services = db
|
||||
.prepare(`SELECT id, name, description, category, duration_min, price, color FROM services WHERE business_id = ? AND active = 1 ORDER BY category, name`)
|
||||
.all(biz.id);
|
||||
const employees = db
|
||||
const employeeRows = db
|
||||
.prepare(`SELECT id, name, role, color FROM employees WHERE business_id = ? AND active = 1 ORDER BY name`)
|
||||
.all(biz.id);
|
||||
.all(biz.id) as { id: number; name: string; role: string; color: string }[];
|
||||
// Qué servicios ofrece cada especialista. Sin esto el cliente no puede filtrar el
|
||||
// paso "Elige un especialista" y deja escoger a alguien que no da ese servicio: el
|
||||
// siguiente paso responde 400 y la UI lo muestra como "no pudimos cargar los
|
||||
// horarios, intenta otra fecha", que nunca lo arregla.
|
||||
// El JOIN contra employees filtra por negocio: employee_services no tiene
|
||||
// business_id propio, así que es la única forma de no cruzar tenants aquí.
|
||||
const links = db
|
||||
.prepare(
|
||||
`SELECT es.employee_id, es.service_id
|
||||
FROM employee_services es
|
||||
JOIN employees e ON e.id = es.employee_id
|
||||
WHERE e.business_id = ? AND e.active = 1`
|
||||
)
|
||||
.all(biz.id) as { employee_id: number; service_id: number }[];
|
||||
const byEmployee = new Map<number, number[]>();
|
||||
for (const l of links) {
|
||||
const arr = byEmployee.get(l.employee_id) ?? [];
|
||||
arr.push(l.service_id);
|
||||
byEmployee.set(l.employee_id, arr);
|
||||
}
|
||||
const employees = employeeRows.map((e) => ({ ...e, service_ids: byEmployee.get(e.id) ?? [] }));
|
||||
res.json({ business: { ...biz, currency_symbol: biz.currency_symbol || "$" }, services, employees });
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user