feat(cliente): ver y modificar los datos de contacto
Revisión con IA / ai-review (pull_request) Successful in 2m18s

El cliente no tenia forma de corregir su email ni su telefono despues del alta.
Nueva pantalla /cliente/perfil ("Mis datos", enlazada desde el dashboard) que
muestra nombre, email y telefono, y permite editarlos.

- GET/PATCH /api/cliente/perfil, ambos con la sesion del cliente.
- El telefono usa el mismo campo con el +54 9 fijo que el registro, y se guarda
  normalizado en telefonoNorm para que el bot lo siga reconociendo.
- aLocalArgentino() saca el prefijo para mostrar: un cliente dado de alta por
  WhatsApp tiene el numero guardado como 5491141984844 y en el formulario tiene
  que verse 1141984844.
- Email y telefono se validan unicos contra los demas clientes (409), no contra
  uno mismo. Si cambia el email se avisa que con ese va a entrar de ahora en mas,
  porque es la identidad del magic link.
- El middleware protege /cliente/perfil como el resto del area del cliente.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Beldor
2026-08-05 15:54:38 -03:00
parent 2baa7952fa
commit 62dab5e025
5 changed files with 260 additions and 3 deletions
+84
View File
@@ -0,0 +1,84 @@
import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { verificarSesion } from "@/lib/auth-admin";
import { aFormatoWhatsapp, aLocalArgentino, esLocalArgentinoValido } from "@/lib/telefono";
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Datos de contacto del cliente logueado. El teléfono se devuelve en formato local
// (sin el +54 9) porque el formulario lo muestra como prefijo fijo.
export async function GET() {
const sesion = await verificarSesion("cliente_session");
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
const cliente = await prisma.cliente.findUnique({
where: { id: sesion.sub },
select: { nombre: true, apellido: true, email: true, telefono: true, telefonoNorm: true },
});
if (!cliente) return NextResponse.json({ error: "Cliente no encontrado" }, { status: 404 });
return NextResponse.json({
nombre: cliente.nombre,
apellido: cliente.apellido,
email: cliente.email ?? "",
telefono: aLocalArgentino(cliente.telefonoNorm ?? cliente.telefono ?? ""),
});
}
export async function PATCH(req: NextRequest) {
const sesion = await verificarSesion("cliente_session");
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
const { nombre, apellido, email, telefono } = await req.json();
if (!nombre?.trim() || !apellido?.trim())
return NextResponse.json({ error: "Nombre y apellido son obligatorios" }, { status: 400 });
if (!email?.trim() || !EMAIL_RE.test(email.trim()))
return NextResponse.json({ error: "El email no es válido" }, { status: 400 });
if (!esLocalArgentinoValido(telefono ?? ""))
return NextResponse.json(
{ error: "El teléfono debe tener 10 dígitos, sin el 0 de la característica ni el 15" },
{ status: 400 }
);
const emailNuevo = email.trim().toLowerCase();
const telefonoNorm = aFormatoWhatsapp(telefono);
// El email es con lo que se entra al sistema y el teléfono es con lo que el bot
// reconoce al cliente: los dos tienen que seguir siendo únicos.
const emailTomado = await prisma.cliente.findFirst({
where: { email: emailNuevo, id: { not: sesion.sub } },
select: { id: true },
});
if (emailTomado)
return NextResponse.json({ error: "Ese email ya está registrado" }, { status: 409 });
const telefonoTomado = await prisma.cliente.findFirst({
where: { telefonoNorm, id: { not: sesion.sub } },
select: { id: true },
});
if (telefonoTomado)
return NextResponse.json({ error: "Ese teléfono ya está registrado" }, { status: 409 });
const cliente = await prisma.cliente.update({
where: { id: sesion.sub },
data: {
nombre: nombre.trim(),
apellido: apellido.trim(),
email: emailNuevo,
telefono: aLocalArgentino(telefonoNorm),
telefonoNorm,
},
select: { nombre: true, apellido: true, email: true, telefonoNorm: true },
});
return NextResponse.json({
ok: true,
nombre: cliente.nombre,
apellido: cliente.apellido,
email: cliente.email ?? "",
telefono: aLocalArgentino(cliente.telefonoNorm ?? ""),
});
}