feat(9-mejoras): limpieza leads + admin CRUD + calendario + CRM360 FRASA
1. Leads limpiados (207 eliminados, 18 apartados conservados) 2. Round-robin desactivado (config round_robin_habilitado=false) 3. Admin puede borrar/reasignar/mover leads (DELETE/PATCH/POST) 4. Calendario: fechaCita guardada y pasada correctamente 5. Avisos Monday eliminados (BannerModoLocal, ChipPendienteSync, vinculación) 6. Contactos filtrados por usuario asignado 7. Modal bitácora: notas persisten entre fases 8. Fases oportunidades: Agendo→Afluencia→Cotizar→Aparto 9. Toast de nota eliminado en cambios de status 10. Branding: CRM360 FRASA + logo en todas las apps 11. 7 apps construidas
This commit is contained in:
@@ -26,7 +26,7 @@ export default function LoginScreen({ onLogin }: { onLogin: (usuario: UsuarioSes
|
||||
<div className="w-full max-w-sm">
|
||||
<div className="mb-8 text-center">
|
||||
<p className="text-lg font-bold tracking-widest text-[#111827] dark:text-[#E2E8F0]">
|
||||
FRASA<span aria-hidden="true" className="text-[#F97316]">.</span>
|
||||
CRM360 FRASA<span aria-hidden="true" className="text-[#F97316]">.</span>
|
||||
</p>
|
||||
<p className="mt-2 text-xs font-medium uppercase tracking-wider text-[#64748B] dark:text-[#94A3B8]">
|
||||
Acceso al ecosistema
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { useCallback, useEffect, useState, type FormEvent } from 'react'
|
||||
import KpiCard from '../components/KpiCard'
|
||||
import BannerModoLocal from '../components/BannerModoLocal'
|
||||
import DarkModeToggle from '../components/DarkModeToggle'
|
||||
import { SkeletonList } from "../components/Skeleton"
|
||||
import toast from "react-hot-toast"
|
||||
import ChipPendienteSync from '../components/ChipPendienteSync'
|
||||
import type { AccionesSesion } from '../components/AuthGate'
|
||||
import {
|
||||
esActividadVencida,
|
||||
@@ -338,9 +336,9 @@ function FilaOportunidad({
|
||||
enviando: boolean
|
||||
acciones?: string[]
|
||||
}) {
|
||||
const [conCita, setConCita] = useState(false)
|
||||
const [fechaCita, setFechaCita] = useState('')
|
||||
const fases = ['Agendo Cita', 'Afluencia', 'Cotizador'] as const
|
||||
const [conCita, setConCita] = useState(!!op.fechaCita)
|
||||
const [fechaCita, setFechaCita] = useState(op.fechaCita ?? '')
|
||||
const fases = ['Agendo Cita', 'Afluencia', 'Cotizador', 'Aparto'] as const
|
||||
return (
|
||||
<li className="rounded-xl border border-[#E5E7EB] bg-white p-4 dark:border-[#1E293B] dark:bg-[#1E293B] shadow-sm">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
@@ -353,7 +351,7 @@ function FilaOportunidad({
|
||||
>
|
||||
{op.nombre}
|
||||
</button>
|
||||
{op.pendienteSync === true && <ChipPendienteSync />}
|
||||
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-[#64748B] dark:text-[#94A3B8]">
|
||||
{op.telefono || 'Sin teléfono'}
|
||||
@@ -469,7 +467,7 @@ function FilaApartado({
|
||||
>
|
||||
{apartado.nombre}
|
||||
</button>
|
||||
{apartado.pendienteSync === true && <ChipPendienteSync />}
|
||||
|
||||
{apartado.unidadCodigo ? (
|
||||
<span className="text-xs font-normal text-[#94A3B8]">{apartado.unidadCodigo}</span>
|
||||
) : null}
|
||||
@@ -732,7 +730,7 @@ function FilaLead({
|
||||
{lead.nombre}
|
||||
</button>
|
||||
<ScoreBadge score={calcularScore(lead)} />
|
||||
{lead.pendienteSync === true && <ChipPendienteSync />}
|
||||
|
||||
{/* Insignia de procedencia: gris = tablero Leads, azul = Oportunidades */}
|
||||
<span
|
||||
className={
|
||||
@@ -816,7 +814,6 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
||||
fuente: 'lead' | 'oportunidad'
|
||||
} | null>(null)
|
||||
const [tabCrm, setTabCrm] = useState<TabCrm>('leads')
|
||||
const [nota, setNota] = useState<string | null>(null)
|
||||
const [apartadoSeleccionado, setApartadoSeleccionado] = useState<ApartadoMiDia | null>(null)
|
||||
const [leadSeleccionado, setLeadSeleccionado] = useState<LeadMiDia | OportunidadMiDia | ApartadoMiDia | null>(null)
|
||||
const [apartadoCancelando, setApartadoCancelando] = useState<{
|
||||
@@ -993,13 +990,11 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
||||
async function convertir(lead: LeadMiDia) {
|
||||
setEnviando(true)
|
||||
setAviso(null)
|
||||
setNota(null)
|
||||
try {
|
||||
await convertirLead(lead.id)
|
||||
await cargar()
|
||||
setNota(`${lead.nombre} pasó a Mis oportunidades (Agendo Cita).`)
|
||||
} catch (err) {
|
||||
setAviso(err instanceof Error ? err.message : 'No se pudo convertir el lead')
|
||||
toast.error(err instanceof Error ? err.message : 'No se pudo convertir el lead')
|
||||
} finally {
|
||||
setEnviando(false)
|
||||
}
|
||||
@@ -1017,7 +1012,7 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
||||
try {
|
||||
await cambiarEtapaOportunidad(op.id, 'Cotizador')
|
||||
} catch {
|
||||
setAviso('No se pudo actualizar la etapa en Monday; el cotizador se abrirá de todos modos.')
|
||||
toast.error('No se pudo actualizar la etapa en Monday; el cotizador se abrirá de todos modos.')
|
||||
}
|
||||
window.location.href = urlCotizadorParaOportunidad(op)
|
||||
}
|
||||
@@ -1079,7 +1074,7 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
||||
← Menú
|
||||
</a>
|
||||
<p className="shrink-0 text-xs font-semibold uppercase tracking-wider text-accent">
|
||||
FRASA <span aria-hidden="true" className="text-[#F97316]">·</span> Mi día
|
||||
CRM360 FRASA <span aria-hidden="true" className="text-[#F97316]">·</span> Mi día
|
||||
</p>
|
||||
<span aria-hidden="true" className="hidden h-4 w-px bg-[#E5E7EB] sm:block" />
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
@@ -1120,7 +1115,7 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<BannerModoLocal />
|
||||
|
||||
|
||||
{offline && (
|
||||
<div className="px-4 pt-4 sm:px-6 lg:px-8">
|
||||
@@ -1146,11 +1141,6 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
||||
{aviso}
|
||||
</p>
|
||||
)}
|
||||
{nota && (
|
||||
<p role="status" className="mb-6 rounded-lg border border-green-200 bg-green-50 px-4 py-3 text-sm font-medium text-green-700">
|
||||
{nota}
|
||||
</p>
|
||||
)}
|
||||
{resumen?.sinMapeoMonday && (
|
||||
<p className="mb-6 rounded-lg border border-[#E5E7EB] bg-white px-4 py-3 text-sm font-medium text-[#64748B] dark:text-[#94A3B8] shadow-sm dark:bg-[#1E293B] dark:border-[#1E293B]">
|
||||
Tu usuario aún no está vinculado a Monday; las acciones siguen funcionando
|
||||
@@ -1457,7 +1447,7 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
||||
<div>
|
||||
<p className="flex items-center gap-2 text-sm font-medium">
|
||||
{c.nombre}
|
||||
{c.pendienteSync === true && <ChipPendienteSync />}
|
||||
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-[#64748B] dark:text-[#94A3B8]">
|
||||
{c.telefono || 'Sin teléfono'}
|
||||
|
||||
Reference in New Issue
Block a user