feat(leads): captura rápida pide email y desarrollo de interés
- Columna 'Desarrollo' (status, 5 labels) creada en el board Leads de Monday (color_mm6sw4c2) - API: crearLead acepta email y desarrollo; el desarrollo se valida contra los labels reales de la columna (mismo patrón que Origen) - Mi día: formulario de captura con campos Email y Desarrollo de interés (select con los 5 desarrollos, opcional) - Tests: api 296, mi-dia 12
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
esRolConEquipo,
|
||||
formatoFechaCorta,
|
||||
hoyLocal,
|
||||
DESARROLLOS_LEAD,
|
||||
MOTIVOS_NO_COMPRA,
|
||||
ORIGENES_LEAD,
|
||||
textoDiasRestantes,
|
||||
@@ -14,6 +15,10 @@ describe('catálogos', () => {
|
||||
expect([...ORIGENES_LEAD]).toEqual(['WhatsApp', 'Web', 'Recomendado', 'Facebook', 'Otro'])
|
||||
})
|
||||
|
||||
it('desarrollos = labels reales de la columna Desarrollo del board Leads', () => {
|
||||
expect([...DESARROLLOS_LEAD]).toEqual(['ATELIER', 'DIAGONAL', 'ESTANZA', 'SENDA', 'SOLANA'])
|
||||
})
|
||||
|
||||
it('motivos de descarte = vocabulario CRM (igual que el API)', () => {
|
||||
expect(MOTIVOS_NO_COMPRA).toContain('Precio')
|
||||
expect(MOTIVOS_NO_COMPRA).toContain('Timing')
|
||||
|
||||
@@ -53,6 +53,9 @@ export interface ResumenMiDia {
|
||||
// Labels reales de la columna status 'Origen' del board Leads (verificados en Monday)
|
||||
export const ORIGENES_LEAD = ['WhatsApp', 'Web', 'Recomendado', 'Facebook', 'Otro'] as const
|
||||
|
||||
// Labels reales de la columna status 'Desarrollo' del board Leads (verificados en Monday)
|
||||
export const DESARROLLOS_LEAD = ['ATELIER', 'DIAGONAL', 'ESTANZA', 'SENDA', 'SOLANA'] as const
|
||||
|
||||
// Categorías del dropdown Motivo No Compra (vocabulario CRM FRASA, igual que el API)
|
||||
export const MOTIVOS_NO_COMPRA = [
|
||||
'Precio',
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
esRolConEquipo,
|
||||
formatoFechaCorta,
|
||||
hoyLocal,
|
||||
DESARROLLOS_LEAD,
|
||||
MOTIVOS_NO_COMPRA,
|
||||
ORIGENES_LEAD,
|
||||
textoDiasRestantes,
|
||||
@@ -186,7 +187,9 @@ export default function MiDiaPage() {
|
||||
// Captura rápida
|
||||
const [capNombre, setCapNombre] = useState('')
|
||||
const [capTelefono, setCapTelefono] = useState('')
|
||||
const [capEmail, setCapEmail] = useState('')
|
||||
const [capOrigen, setCapOrigen] = useState<string>(ORIGENES_LEAD[0])
|
||||
const [capDesarrollo, setCapDesarrollo] = useState<string>('')
|
||||
const [capNotas, setCapNotas] = useState('')
|
||||
const [capEnviando, setCapEnviando] = useState(false)
|
||||
const [capMensaje, setCapMensaje] = useState<{ tipo: 'ok' | 'error'; texto: string } | null>(null)
|
||||
@@ -229,12 +232,16 @@ export default function MiDiaPage() {
|
||||
await crearLead({
|
||||
nombre: capNombre.trim(),
|
||||
...(capTelefono.trim() !== '' ? { telefono: capTelefono.trim() } : {}),
|
||||
...(capEmail.trim() !== '' ? { email: capEmail.trim() } : {}),
|
||||
...(capOrigen !== '' ? { origen: capOrigen } : {}),
|
||||
...(capDesarrollo !== '' ? { desarrollo: capDesarrollo } : {}),
|
||||
...(capNotas.trim() !== '' ? { notas: capNotas.trim() } : {}),
|
||||
})
|
||||
setCapMensaje({ tipo: 'ok', texto: 'Lead registrado, se asignará por round-robin' })
|
||||
setCapNombre('')
|
||||
setCapTelefono('')
|
||||
setCapEmail('')
|
||||
setCapDesarrollo('')
|
||||
setCapNotas('')
|
||||
} catch (err) {
|
||||
setCapMensaje({ tipo: 'error', texto: err instanceof Error ? err.message : 'No se pudo registrar el lead' })
|
||||
@@ -388,6 +395,18 @@ export default function MiDiaPage() {
|
||||
className="w-full border border-neutral-300 px-4 py-3 text-sm focus:border-accent focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="cap-email" className="mb-2 block text-xs font-bold uppercase tracking-widest text-neutral-500">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
id="cap-email"
|
||||
type="email"
|
||||
value={capEmail}
|
||||
onChange={(e) => setCapEmail(e.target.value)}
|
||||
className="w-full border border-neutral-300 px-4 py-3 text-sm focus:border-accent focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="cap-origen" className="mb-2 block text-xs font-bold uppercase tracking-widest text-neutral-500">
|
||||
Origen
|
||||
@@ -405,6 +424,24 @@ export default function MiDiaPage() {
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="cap-desarrollo" className="mb-2 block text-xs font-bold uppercase tracking-widest text-neutral-500">
|
||||
Desarrollo de interés
|
||||
</label>
|
||||
<select
|
||||
id="cap-desarrollo"
|
||||
value={capDesarrollo}
|
||||
onChange={(e) => setCapDesarrollo(e.target.value)}
|
||||
className="w-full border border-neutral-300 px-4 py-3 text-sm focus:border-accent focus:outline-none"
|
||||
>
|
||||
<option value="">Sin especificar</option>
|
||||
{DESARROLLOS_LEAD.map((d) => (
|
||||
<option key={d} value={d}>
|
||||
{d}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="cap-notas" className="mb-2 block text-xs font-bold uppercase tracking-widest text-neutral-500">
|
||||
Notas
|
||||
|
||||
@@ -27,7 +27,9 @@ export interface LeadCreado {
|
||||
export async function crearLead(datos: {
|
||||
nombre: string
|
||||
telefono?: string
|
||||
email?: string
|
||||
origen?: string
|
||||
desarrollo?: string
|
||||
notas?: string
|
||||
}): Promise<LeadCreado> {
|
||||
return fetchJson<LeadCreado>('/leads', {
|
||||
|
||||
Reference in New Issue
Block a user