feat(mi-dia): app /hoy + CRM ligero para vendedores

- App Mi día: KPIs, mis actividades, mis leads con acciones rápidas
  (contactado/intento/agenda cita/descartar con motivo), captura rápida
  de lead (409 en duplicado por teléfono), vista equipo para coordinadores
- API: GET /api/mi-dia/resumen, POST /api/leads, /leads/:id/estado,
  /leads/:id/descartar; mapeo Usuario.mondayUserId (seed listo para
  cuando inviten a los vendedores a Monday)
- Landing: tarjeta Mi día; tests api 188, mi-dia 8, landing 4
This commit is contained in:
2026-08-28 06:23:15 +00:00
parent 1f5b6ee088
commit bc0f9da8fe
31 changed files with 5917 additions and 3 deletions

View File

@@ -0,0 +1,64 @@
import { describe, expect, it } from 'vitest'
import {
esActividadVencida,
esRolConEquipo,
formatoFechaCorta,
hoyLocal,
MOTIVOS_NO_COMPRA,
ORIGENES_LEAD,
textoDiasRestantes,
} from './mi-dia'
describe('catálogos', () => {
it('orígenes = labels reales de la columna Origen del board Leads', () => {
expect([...ORIGENES_LEAD]).toEqual(['WhatsApp', 'Web', 'Recomendado', 'Facebook', 'Otro'])
})
it('motivos de descarte = vocabulario CRM (igual que el API)', () => {
expect(MOTIVOS_NO_COMPRA).toContain('Precio')
expect(MOTIVOS_NO_COMPRA).toContain('Timing')
expect(MOTIVOS_NO_COMPRA).toHaveLength(7)
})
})
describe('esRolConEquipo', () => {
it('coordinador, corporativo y admin ven equipo; vendedor no', () => {
expect(esRolConEquipo('coordinador')).toBe(true)
expect(esRolConEquipo('corporativo')).toBe(true)
expect(esRolConEquipo('admin')).toBe(true)
expect(esRolConEquipo('vendedor')).toBe(false)
})
})
describe('formatoFechaCorta', () => {
it('recorta el año', () => {
expect(formatoFechaCorta('2026-08-29')).toBe('29/08')
})
it('tolera cadenas mal formadas devolviéndolas tal cual', () => {
expect(formatoFechaCorta('sin-fecha')).toBe('sin-fecha')
})
})
describe('textoDiasRestantes', () => {
it('hoy, mañana y n días', () => {
expect(textoDiasRestantes(0)).toBe('Vence hoy')
expect(textoDiasRestantes(1)).toBe('Vence mañana')
expect(textoDiasRestantes(5)).toBe('Vence en 5 días')
})
})
describe('esActividadVencida', () => {
it('compara contra la fecha de hoy', () => {
expect(esActividadVencida('2026-08-27', '2026-08-28')).toBe(true)
expect(esActividadVencida('2026-08-28', '2026-08-28')).toBe(false)
expect(esActividadVencida('2026-08-29', '2026-08-28')).toBe(false)
expect(esActividadVencida('', '2026-08-28')).toBe(false)
})
})
describe('hoyLocal', () => {
it('devuelve YYYY-MM-DD', () => {
expect(hoyLocal()).toMatch(/^\d{4}-\d{2}-\d{2}$/)
})
})