feat: el cliente puede ver y modificar sus datos de contacto #6
Reference in New Issue
Block a user
Delete Branch "feat/datos-de-contacto"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
El cliente no tenia forma de corregir su email ni su telefono despues del alta. Ahora hay una pantalla Mis datos (
/cliente/perfil), enlazada desde el dashboard al lado de "Salir", que muestra nombre, email y telefono y permite editarlos.Detalles que importan
+54 9fijo que el registro, y se guarda normalizado entelefonoNorm, asi que si el cliente lo corrige el bot lo sigue reconociendo.aLocalArgentino(): un cliente dado de alta por WhatsApp tiene el numero guardado como5491141984844, y en el formulario tiene que verse1141984844. Sin esto, editar sus datos le duplicaba el prefijo.id: { not: sesion.sub }), asi guardar sin cambiar nada no da conflicto. Devuelve 409 con mensaje claro en vez de reventar contra el indice unico./cliente/perfilcomo el resto del area del cliente.Verificacion
Contra una copia de la base de produccion, con el dev server:
GETdevuelve el telefono en formato local aunque este guardado con prefijo.PATCHcambia apellido y telefono; en la base quedatelefono=3515554444ytelefonoNorm=5493515554444.tscylintlimpios.🤖 Generated with Claude Code
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>Inline review
@@ -0,0 +48,4 @@// 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({Condición de carrera: la verificación de unicidad de email/teléfono y el update no son atómicos. Dos requests simultáneos pueden pasar la verificación y fallar en el update (o peor, si no hay constraint única en BD, duplicar datos). Usá una transacción de Prisma o confia en el constraint único y atrapá el error P2002.
#ai-review-inline
@@ -0,0 +68,4 @@nombre: nombre.trim(),apellido: apellido.trim(),email: emailNuevo,telefono: aLocalArgentino(telefonoNorm),Guardás el teléfono en formato local en el campo
telefonoy en formato WhatsApp entelefonoNorm. El GET leetelefonoNormprimero y hace fallback atelefono. SitelefonoNormexiste,telefonoes redundante. Confirmá que el schema tenga@@unique([telefonoNorm])y@@unique([email])para que la BD sea la fuente de verdad.#ai-review-inline
@@ -0,0 +28,4 @@const [guardado, setGuardado] = useState(false);useEffect(() => {fetch("/api/cliente/perfil")El fetch inicial no maneja errores de red: si falla la request (p.ej. sin conexión), el
.thenno se ejecuta yloadingqueda entruepara siempre. Agregá.catch(() => { setLoading(false); setError('Error de conexión'); })o usá try/catch con async/await.#ai-review-inline
Inline review
Inline review
Inline review
@@ -0,0 +1,84 @@import { NextRequest, NextResponse } from "next/server";import { prisma } from "@/lib/prisma";import { verificarSesion } from "@/lib/auth-admin";Importás
verificarSesiondesdeauth-admin.tspara validar la cookiecliente_session. Verificá que esa función valide correctamente el rol de cliente y no solo la existencia de la sesión de admin. Si reutiliza lógica de admin, podría haber escalada de privilegios.#ai-review-inline
@@ -0,0 +69,4 @@}setGuardando(true);setError("");const res = await fetch("/api/cliente/perfil", {El PATCH no maneja 401 (sesión expirada durante la edición). El usuario ve un error genérico y no sabe que debe reloguearse. Agregá
if (res.status === 401) { router.push('/cliente'); return; }antes de leerres.json().#ai-review-inline
Inline review
Qué cambia
Se agrega la funcionalidad completa para que el cliente logueado vea y edite su perfil (nombre, apellido, email, teléfono). Incluye: endpoint
GET/PATCH /api/cliente/perfilcon validación de unicidad de email y teléfono, página/cliente/perfilcon formulario controlado y feedback, link "Mis datos" en el dashboard, y protección de la ruta en el middleware. También se sumaaLocalArgentinoenlib/telefono.tspara mostrar el número sin el prefijo +54 9.Riesgos
findFirst+updateno es atómico; dos requests simultáneos con el mismo email/teléfono pueden pasar la validación y fallar en la BD con error P2002 no manejado.telefono: el campotelefonoa veces guarda formato local y a veces WhatsApp; el código confía entelefonoNormcomo fuente de verdad, pero si hay datos legacy mezclados,aLocalArgentinopodría devolver valores inesperados.Antes de aprobar
emailytelefonoNormtienen@@uniqueen el schema de Prisma.telefonoa formato local ytelefonoNorma WhatsApp para evitar sorpresas.#ai-review-summary