From cc299cdc11b8f0ff1eabe006fea0768415743491 Mon Sep 17 00:00:00 2001 From: Beldor Date: Tue, 23 Jun 2026 11:08:11 -0300 Subject: [PATCH] =?UTF-8?q?fix:=20validar=20km=20m=C3=A1ximo=201M=20y=20mo?= =?UTF-8?q?strar=20error=20si=20falla=20la=20reserva?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Si alguien ingresaba un km fuera del rango integer de Postgres la app crasheaba silenciosamente — el cliente no veía ningún mensaje. Se agrega validación de rango (1–1.000.000) en frontend y backend, y se muestra el error de la API debajo del botón de confirmar. Co-Authored-By: Claude Sonnet 4.6 --- src/app/api/cliente/turnos/route.ts | 6 +++++- src/app/cliente/turnos/page.tsx | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/app/api/cliente/turnos/route.ts b/src/app/api/cliente/turnos/route.ts index 233a15f..478f836 100644 --- a/src/app/api/cliente/turnos/route.ts +++ b/src/app/api/cliente/turnos/route.ts @@ -24,6 +24,10 @@ export async function POST(req: NextRequest) { if (!autoId || !tipoServicioId || !fechaInicio || !kilometros) return NextResponse.json({ error: "Datos incompletos" }, { status: 400 }); + const km = Number(kilometros); + if (!Number.isInteger(km) || km <= 0 || km > 1000000) + return NextResponse.json({ error: "Kilometraje inválido (debe ser entre 1 y 1.000.000)" }, { status: 400 }); + // Verificar que el auto pertenece al cliente const auto = await prisma.auto.findFirst({ where: { id: autoId, clienteId: sesion.sub }, @@ -42,7 +46,7 @@ export async function POST(req: NextRequest) { return NextResponse.json({ error: "El horario ya no está disponible" }, { status: 409 }); const turno = await prisma.turno.create({ - data: { clienteId: sesion.sub, autoId, tipoServicioId, fechaInicio: inicio, fechaFin: fin, kilometros: Number(kilometros) }, + data: { clienteId: sesion.sub, autoId, tipoServicioId, fechaInicio: inicio, fechaFin: fin, kilometros: km }, include: { auto: true, tipoServicio: true }, }); diff --git a/src/app/cliente/turnos/page.tsx b/src/app/cliente/turnos/page.tsx index 68bf6f6..9169552 100644 --- a/src/app/cliente/turnos/page.tsx +++ b/src/app/cliente/turnos/page.tsx @@ -79,6 +79,7 @@ function TurnosContent() { const [kilometros, setKilometros] = useState(""); const [kmError, setKmError] = useState(""); const [reservando, setReservando] = useState(false); + const [reservaError, setReservaError] = useState(""); const [turnoCreado, setTurnoCreado] = useState(null); useEffect(() => { @@ -127,8 +128,10 @@ function TurnosContent() { setKilometros(val); if (val === "") { setKmError(""); - } else if (!/^\d+$/.test(val) || Number(val) < 0) { + } else if (!/^\d+$/.test(val) || Number(val) <= 0) { setKmError("Ingresá solo números enteros positivos"); + } else if (Number(val) > 1000000) { + setKmError("El kilometraje no puede superar 1.000.000 km"); } else { setKmError(""); } @@ -137,6 +140,7 @@ function TurnosContent() { async function reservar() { if (!slotSel || !servicioSel || !autoId || !kilometros || kmError) return; setReservando(true); + setReservaError(""); const res = await fetch("/api/cliente/turnos", { method: "POST", headers: { "Content-Type": "application/json" }, @@ -144,7 +148,13 @@ function TurnosContent() { }); const data = await res.json(); setReservando(false); - if (res.ok) { setTurnoCreado(data); setDiaSeleccionado(null); setStep("confirmado"); } + if (res.ok) { + setTurnoCreado(data); + setDiaSeleccionado(null); + setStep("confirmado"); + } else { + setReservaError(data?.error || "Ocurrió un error al reservar. Intentá de nuevo."); + } } // Navegación de mes @@ -364,6 +374,7 @@ function TurnosContent() { + {reservaError &&

{reservaError}

} )}