Commit Graph

18 Commits

Author SHA1 Message Date
Beldor f4df90ac1e fix(auth): comprobar el rol de la sesion en todas las rutas
Revisión con IA / ai-review (pull_request) Successful in 1m24s
Las tres sesiones (admin, cliente, jefe) se firman con el mismo secreto, asi que
verificar solo la firma alcanzaba para que un token de un rol pasara por la
cookie de otro. Verificado antes del cambio: un token de admin en
cliente_session daba 200 en /api/cliente/autos, y uno de jefe cargaba
/cliente/dashboard. Despues: 401 y 307.

- Las 26 llamadas a verificarSesion pasan el rol esperado.
- El middleware, que hacia su propio jwtVerify sin mirar el rol, ahora tambien
  lo comprueba.
- Se sacan las comprobaciones sueltas de rol en las rutas de jefe, que quedaron
  redundantes.

Matriz probada contra la app: los tres roles siguen entrando en su area (200) y
ninguno entra en la de los otros, ni por API (401) ni por pagina (307).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:45:33 -03:00
Beldor ce7063514c fix(perfil): detalles senalados en la revision
Revisión con IA / ai-review (pull_request) Successful in 24s
- verificarSesion acepta el rol esperado. Las tres sesiones (admin, cliente,
  jefe) se firman con el mismo secreto, asi que un token de jefe puesto en la
  cookie cliente_session pasaba la verificacion de firma; ahora el rol se
  comprueba. Verificado: con un JWT de jefe, /api/cliente/perfil da 401.
- El update traduce el P2002 de los indices unicos a un 409 legible, por si otra
  request toma el mismo email o telefono entre la comprobacion y el update.
- La pantalla ya no queda en "Cargando..." para siempre si falla el pedido.
- Si la sesion vence mientras edita, el PATCH manda a entrar de nuevo en vez de
  mostrar un error suelto.

Queda como mejora aparte pasarle el rol esperado al resto de las rutas del area
del cliente, que hoy solo validan la firma.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:09:40 -03:00
Beldor 62dab5e025 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>
2026-08-05 15:54:38 -03:00
Beldor ffa32a113d fix(registro): guardar el telefono en el formato que usa WhatsApp
Revisión con IA / ai-review (pull_request) Successful in 2m12s
Un cliente registrado por la web no lo reconocia el bot: el formulario dejaba
escribir el numero en cualquier formato (1141984844, 01157260394) mientras que
Meta manda el wa_id 5491141984844, asi que la busqueda por telefonoNorm no
encontraba nada, mandaba al alta y ahi chocaba contra el email ya registrado.
Quedaba en un callejon sin salida.

Ahora el campo muestra "+54 9" fijo y grisado y el cliente completa solo la
caracteristica (sin el 0) y el abonado (sin el 15), 10 digitos. El backend
valida ese formato y guarda telefonoNorm ya como lo espera el bot. Se rechaza
tambien un telefono repetido, que antes reventaba con el indice unico.

Se agrega scripts/chat-bot.ts, que es como se encontro esto: permite conversar
con el bot desde la consola contra cualquier base, sin Meta ni credenciales.
Corriendolo contra una copia de la base de produccion se ve el ciclo completo:
alta por web -> el bot lo reconoce por telefono -> pide el email -> reserva.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 14:56:15 -03:00
Beldor 91112862a5 fix: los cuatro hallazgos reales de la revision con IA
Revisión con IA / ai-review (pull_request) Successful in 29s
- crearTurno verificaba disponibilidad y creaba el turno en dos operaciones
  sueltas: dos clientes podian pasar la verificacion a la vez y sobrevender el
  mismo horario. Ahora va todo en una transaccion, serializada por jefe con
  pg_advisory_xact_lock. slotEstaDisponible acepta el cliente de la transaccion.
  Probado contra un Postgres real: 5 reservas simultaneas del mismo slot con
  capacidad 1 dan 1 ok + 4 conflictos y una sola fila; sacando el lock, entran
  las 5.
- La deduplicacion del webhook estaba en memoria y solo servia con una
  instancia. Pasa a la base con el modelo MensajeWhatsappProcesado: el insert
  del id es atomico por el indice unico, asi que aguanta reintentos
  concurrentes de Meta (verificado). Se limpian los ids de mas de 7 dias.
- postMensaje no reintentaba: ahora reintenta 3 veces ante 429, 5xx y fallas
  de red, con backoff exponencial y jitter. Un 4xx nuestro no se reintenta.
- /api/cliente/servicios devolvia los trabajos de un jefe dado de baja; ahora
  responde 404 si el jefe no existe o no esta activo.

test:envio suma los casos de reintento (35 asserts). El Prisma en memoria del
test del bot ahora entiende $transaction y $executeRaw.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 11:21:35 -03:00
Beldor 44aeb58e77 feat(cliente): invertir el flujo web a jefe -> trabajo
Revisión con IA / ai-review (pull_request) Failing after 2m12s
Los trabajos ahora son por jefe mecanico, asi que el cliente elige primero
el mecanico y despues el trabajo, igual que en el bot de WhatsApp. Antes
listaba todos los servicios del taller y recien despues pedia el jefe.

- src/lib/servicios.ts: regla unica de que trabajos ofrece un jefe = los
  suyos + los globales del taller (jefeMecanicoId null, los carga el admin).
  Asi no se rompe lo que ya existe en produccion, donde todos los servicios
  son globales.
- /api/cliente/servicios ahora exige jefeMecanicoId y aplica esa regla.
- /api/cliente/slots y crearTurno rechazan un trabajo propio de OTRO jefe
  (400): antes se podia reservar con el jefe A la duracion del trabajo del B.
- whatsapp-bot: usa la misma regla, asi la web y el bot ofrecen lo mismo.
- cliente/turnos: pasos invertidos, cabeceras y back acordes, y estado vacio
  propio cuando el jefe no tiene trabajos.
- test-whatsapp-bot: soporte de OR en el Prisma en memoria, trabajo global en
  el seed y escenario D (globales visibles, trabajo ajeno rechazado). 31 asserts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 09:55:47 -03:00
Beldor 0046b6c3cf feat(whatsapp): máquina de conversación del bot de reservas (F2) + test
- lib/turnos.ts: crearTurno() compartido (valida servicio/jefe/slot); cliente/turnos POST lo reutiliza
- lib/telefono.ts: normalización de teléfono (E.164 sin '+') para matchear el wa_id
- lib/whatsapp-bot.ts: procesarMensaje(telefono, texto), state machine completa:
  identidad por teléfono + verificación por email registrado / alta nueva,
  elegir mecánico (botones) -> trabajo (letras) -> horario (letras, con "más") -> confirmar,
  crea turno sin auto/km; expiración 30min, "cancelar" global, manejo de inválidos y 409.
  Devuelve objetos-respuesta (texto/botones) -> independiente de Meta (lo traduce F0).
- scripts/test-whatsapp-bot.ts + npm run test:bot: corre la máquina de estados con un
  Prisma en memoria (sin DB ni Meta), ejercitando scheduling y crearTurno reales. 27/27 OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-05 09:55:47 -03:00
Beldor 0792ac3767 Add jefe mecánico to client booking flow
Deploy / deploy (push) Successful in 6m52s
Cliente now picks a jefe mecánico after choosing the service so the
calendar shows that specific jefe's availability. Each jefe has its own
configurable parallel slot capacity (replacing the global SLOTS_PARALELOS
= 5). Admins manage jefes from /admin/jefes; jefes log in to /jefe to
see their daily agenda.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-29 22:28:44 -03:00
Beldor cc299cdc11 fix: validar km máximo 1M y mostrar error si falla la reserva
Deploy / deploy (push) Successful in 4m39s
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 <noreply@anthropic.com>
2026-06-23 11:08:11 -03:00
Beldor 0497e936a4 feat: calendario mensual para reservar turnos
Deploy / deploy (push) Successful in 4m3s
- Reemplaza lista de slots por calendario cuadrado mensual
- Sábados y domingos grisados, días sin slots deshabilitados
- Click en día hábil abre modal con horarios disponibles
- Slot dot indicator en días con turnos disponibles
- Amplía búsqueda a 60 días / 500 slots para cubrir todo el mes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 23:33:29 -03:00
Beldor 4a6023f9f3 feat: historial muestra todos los turnos del auto con estado, km y comentario
Deploy / deploy (push) Successful in 3m52s
Reemplaza HistorialServicio por Turno en ambas APIs y frontends (cliente y admin).
Muestra: servicio, fecha, horario, estado (badge), km al ingreso, observaciones del jefe.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 23:19:53 -03:00
Beldor 4d259558c6 feat: agrega km y chasis al turno/auto, combobox de marcas y modelos
Deploy / deploy (push) Failing after 13m41s
- Turno.kilometros (requerido al reservar)
- Auto.nroChasis (opcional)
- Combobox con 60 marcas mundiales y modelos precargados
- Modelo se habilita al seleccionar marca

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 16:01:19 -03:00
Beldor b6705239a1 fix(cliente): cargar servicios desde endpoint de cliente en vez de admin
Deploy / deploy (push) Failing after 33h14m30s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 23:20:25 -03:00
Beldor 4554543260 fix: migrate registro email from Resend to Nodemailer+Gmail
Deploy / deploy (push) Successful in 3m6s
2026-06-19 14:55:44 -03:00
Beldor 4db113b674 fix: redirigir emails a jpbeldomenico@gmail.com para testing con sandbox Resend
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:22:24 -03:00
Beldor acf4b7f053 fix: usar dominio sandbox de Resend para emails
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:14:19 -03:00
Beldor 4e993a95c4 feat: email de bienvenida al registrarse + fix estilos Input
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:05:01 -03:00
Beldor 93ad1cb8c1 feat: bootstrap taller-turnos project
Estructura inicial con Next.js 14, PostgreSQL/Prisma 7, autenticación por
magic link (clientes) y JWT (admins), lógica de scheduling con 4 slots
paralelos y API routes completas para cliente y panel admin.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 11:51:19 -03:00