diff --git a/apps/mi-dia/src/lib/mi-dia.ts b/apps/mi-dia/src/lib/mi-dia.ts
index 508a694..53f6c1a 100644
--- a/apps/mi-dia/src/lib/mi-dia.ts
+++ b/apps/mi-dia/src/lib/mi-dia.ts
@@ -119,6 +119,18 @@ export const ORIGENES_LEAD = ['PASE', 'PROSPECCION ASESOR', 'ACTIVACION', 'FACEB
// Labels reales de la columna status 'Desarrollo' del board Leads (verificados en Monday)
export const DESARROLLOS_LEAD = ['ATELIER', 'DIAGONAL', 'ESTANZA', 'SENDA', 'SOLANA'] as const
+// Estados de la columna status del board Leads (vocabulario real del API:
+// ESTADOS_LEAD_PERMITIDOS + NUEVO/DESCARTADO); alimenta el filtro por estado
+export const ESTADOS_LEAD = [
+ 'NUEVO',
+ 'CONTACTADO',
+ 'Intento de contacto',
+ 'INTERESADO',
+ 'AGENDA CITA',
+ 'CALIFICADO',
+ 'DESCARTADO',
+] as const
+
// Categorías del dropdown Motivo No Compra (vocabulario CRM FRASA, igual que el API)
export const MOTIVOS_NO_COMPRA = [
'Precio',
@@ -165,6 +177,22 @@ export function componerTelefono(lada: ClaveLada, numero: string): string {
return `${prefijo} ${limpio}`
}
+/**
+ * Normaliza un teléfono a dígitos para enlaces tel:/wa.me. Si el número no trae
+ * lada internacional (10 dígitos), asume México (+52); si ya la trae, la respeta.
+ */
+export function telefonoParaEnlace(telefono: string): string {
+ const digitos = telefono.replace(/\D/g, '')
+ if (digitos.length === 10) return `52${digitos}`
+ return digitos
+}
+
+/** URL de WhatsApp con saludo FRASA prellenado: "Hola {nombre}, soy {vendedor} de FRASA" */
+export function urlWhatsApp(nombre: string, telefono: string, vendedor: string): string {
+ const texto = encodeURIComponent(`Hola ${nombre}, soy ${vendedor} de FRASA`)
+ return `https://wa.me/${telefonoParaEnlace(telefono)}?text=${texto}`
+}
+
// Fases del funnel de Oportunidades (labels REALES del board; el API las valida tal cual)
export const FASES_OPORTUNIDAD = ['Agendo Cita', 'Afluencia', 'Cotizador', 'Aparto', 'No Asistio', 'Abandono'] as const
export type FaseOportunidad = (typeof FASES_OPORTUNIDAD)[number]
diff --git a/apps/mi-dia/src/pages/MiDiaPage.tsx b/apps/mi-dia/src/pages/MiDiaPage.tsx
index d9a03fb..00e8f9c 100644
--- a/apps/mi-dia/src/pages/MiDiaPage.tsx
+++ b/apps/mi-dia/src/pages/MiDiaPage.tsx
@@ -10,10 +10,13 @@ import {
formatoFechaCorta,
hoyLocal,
DESARROLLOS_LEAD,
+ ESTADOS_LEAD,
ETIQUETA_FASE_APARTADO,
ETIQUETA_FASE_ESCRITURACION,
ETIQUETA_FASE_OPORTUNIDAD,
FASES_APARTADO,
+ FASES_ESCRITURACION,
+ FASES_OPORTUNIDAD,
ORIGENES_LEAD,
SLOTS_DOCS_APARTADO,
componerTelefono,
@@ -21,6 +24,7 @@ import {
textoDiasRestantes,
urlAfluenciaParaOportunidad,
urlCotizadorParaOportunidad,
+ urlWhatsApp,
type ApartadoMiDia,
type ClaveLada,
type FaseApartado,
@@ -44,7 +48,7 @@ import {
import { guardarResumen, cargarResumenLocal } from '../services/offline-db'
import { suscribirPush } from '../services/push'
import { exportarLeads, exportarOportunidades, exportarApartados } from '../services/exportar'
-import { FiltrosAvanzados, aplicarFiltros, type FiltrosCRM } from '../components/FiltrosAvanzados'
+import { FiltrosAvanzados, aplicarFiltros, FILTROS_VACIOS, type FiltrosCRM } from '../components/FiltrosAvanzados'
import { getSesion } from '../services/auth'
import DashboardVendedor from '../components/DashboardVendedor'
import ScoreBadge, { calcularScore } from '../components/ScoreBadge'
@@ -107,6 +111,35 @@ const CLASE_INPUT =
const CLASE_LABEL = 'mb-2 block text-xs font-medium text-[#4B5563] dark:text-[#CBD5E1]'
+/** Motivos de no compra: valores EXACTOS que valida el API (z.enum MOTIVOS_NO_COMPRA) */
+const MOTIVOS_NO_COMPRA = ['Precio', 'Producto', 'Ubicacion', 'Financiamiento', 'Competencia', 'Cliente', 'Timing'] as const
+
+/** Teléfono clicable (tel:) + botón de WhatsApp con saludo FRASA prellenado */
+function EnlacesContacto({ nombre, telefono }: { nombre: string; telefono: string }) {
+ const vendedor = getSesion()?.usuario.nombre ?? ''
+ if (telefono.trim() === '') return
Sin teléfono
+ return (
+ <>
+
+ {telefono}
+
+
+ 💬
+
+ >
+ )
+}
+
/** Fila de oportunidad con botones de fase del funnel (Agendo Cita lleva fecha) */
function FilaOportunidad({
@@ -146,7 +179,7 @@ function FilaOportunidad({
- {op.telefono || 'Sin teléfono'}
+
{op.desarrollo ? ` · ${op.desarrollo}` : ''}
{op.modeloInteres ? ` · ${op.modeloInteres}` : ''}
{op.unidadCodigo ? ` · ${op.unidadCodigo}` : ''}
@@ -269,8 +302,8 @@ function ModalDescarteGenerico({
onChange={(e) => setMotivo(e.target.value)}
className="w-full rounded-lg border border-[#D1D5DB] bg-white px-3 dark:border-[#334155] dark:bg-[#0F172A] dark:text-[#E2E8F0] py-2.5 text-sm"
>
- {['Timing', 'Cliente', 'Competencia', 'Financiamiento', 'Ubicación', 'Producto', 'Precio'].map((m) => (
-
+ {MOTIVOS_NO_COMPRA.map((m) => (
+
))}
@@ -351,9 +384,10 @@ function FilaApartado({
disabled={enviando}
onClick={() => {
// Alta Enkontrol: abre el alta de cliente prellenada con
- // todo lo recolectado del apartado (ligada, sin duplicar)
+ // todo lo recolectado del apartado (ligada, sin duplicar).
+ // En pestaña nueva para no perder el estado de Mi día.
if (fase === 'Alta Enkontrol') {
- window.location.href = `/client/?apartado=${apartado.id}`
+ window.open(`/client/?apartado=${apartado.id}`, '_blank')
return
}
if (fase === 'Cancelacion Apartado') {
@@ -598,7 +632,7 @@ function FilaLead({