feat(escrituracion): nueva etapa Escrituración en Mi día
- Nueva pestaña 'Escrituración' después de 'Mis apartados' - 7 fases: Seguimiento, Llamada Escrituras, X Escriturar, Banco/Infonavit, X Autorizar, Escriturado, Cancelo - 'Firma Cliente' en Apartados mueve a pestaña Escrituración - Backend: fases de escrituración añadidas al controller de apartados - UI: cada apartado muestra botones de fases de escrituración - 630 API tests + 5 apps construidas
This commit is contained in:
@@ -172,6 +172,7 @@ export const FASES_APARTADO = [
|
|||||||
'Entrego Documentacion',
|
'Entrego Documentacion',
|
||||||
'Cita Clientes',
|
'Cita Clientes',
|
||||||
'Contrato Enkontrol',
|
'Contrato Enkontrol',
|
||||||
|
'Firma Cliente',
|
||||||
'Cancelacion Apartado',
|
'Cancelacion Apartado',
|
||||||
] as const
|
] as const
|
||||||
export type FaseApartado = (typeof FASES_APARTADO)[number]
|
export type FaseApartado = (typeof FASES_APARTADO)[number]
|
||||||
@@ -183,9 +184,31 @@ export const ETIQUETA_FASE_APARTADO: Record<FaseApartado, string> = {
|
|||||||
'Entrego Documentacion': 'Entregó documentación',
|
'Entrego Documentacion': 'Entregó documentación',
|
||||||
'Cita Clientes': 'Cita cliente',
|
'Cita Clientes': 'Cita cliente',
|
||||||
'Contrato Enkontrol': 'Contrato Enkontrol',
|
'Contrato Enkontrol': 'Contrato Enkontrol',
|
||||||
|
'Firma Cliente': 'Firma cliente →',
|
||||||
'Cancelacion Apartado': 'Cancelación',
|
'Cancelacion Apartado': 'Cancelación',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const FASES_ESCRITURACION = [
|
||||||
|
'Seguimiento',
|
||||||
|
'Llamada Escrituras',
|
||||||
|
'X Escriturar',
|
||||||
|
'Banco/Infonavit',
|
||||||
|
'X Autorizar',
|
||||||
|
'Escriturado',
|
||||||
|
'Cancelo',
|
||||||
|
] as const
|
||||||
|
export type FaseEscrituracion = (typeof FASES_ESCRITURACION)[number]
|
||||||
|
|
||||||
|
export const ETIQUETA_FASE_ESCRITURACION: Record<FaseEscrituracion, string> = {
|
||||||
|
'Seguimiento': 'Seguimiento',
|
||||||
|
'Llamada Escrituras': 'Llamada escrituras',
|
||||||
|
'X Escriturar': 'X Escriturar',
|
||||||
|
'Banco/Infonavit': 'Banco/Infonavit',
|
||||||
|
'X Autorizar': 'X Autorizar',
|
||||||
|
'Escriturado': 'Escriturado',
|
||||||
|
'Cancelo': 'Canceló',
|
||||||
|
}
|
||||||
|
|
||||||
// Texto bonito de los botones de oportunidad
|
// Texto bonito de los botones de oportunidad
|
||||||
export const ETIQUETA_FASE_OPORTUNIDAD: Record<FaseOportunidad, string> = {
|
export const ETIQUETA_FASE_OPORTUNIDAD: Record<FaseOportunidad, string> = {
|
||||||
'Agendo Cita': 'Agendó cita',
|
'Agendo Cita': 'Agendó cita',
|
||||||
|
|||||||
@@ -502,7 +502,7 @@ function PanelDocumentosApartado({ itemId }: { itemId: string }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Tabs del CRM personal: Mis leads / Mis oportunidades / Mis apartados / Mi tablero */
|
/** Tabs del CRM personal: Mis leads / Mis oportunidades / Mis apartados / Mi tablero */
|
||||||
type TabCrm = 'leads' | 'oportunidades' | 'apartados' | 'tablero'
|
type TabCrm = 'leads' | 'oportunidades' | 'apartados' | 'escrituracion' | 'tablero'
|
||||||
|
|
||||||
function TabsCrm({
|
function TabsCrm({
|
||||||
activa,
|
activa,
|
||||||
@@ -517,6 +517,7 @@ function TabsCrm({
|
|||||||
{ id: 'leads', titulo: 'Mis leads' },
|
{ id: 'leads', titulo: 'Mis leads' },
|
||||||
{ id: 'oportunidades', titulo: 'Mis oportunidades' },
|
{ id: 'oportunidades', titulo: 'Mis oportunidades' },
|
||||||
{ id: 'apartados', titulo: 'Mis apartados' },
|
{ id: 'apartados', titulo: 'Mis apartados' },
|
||||||
|
{ id: 'escrituracion', titulo: 'Escrituración' },
|
||||||
{ id: 'tablero', titulo: 'Mi tablero' },
|
{ id: 'tablero', titulo: 'Mi tablero' },
|
||||||
]
|
]
|
||||||
return (
|
return (
|
||||||
@@ -945,7 +946,7 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
|||||||
conteos={{
|
conteos={{
|
||||||
leads: resumen.misLeads.filter((l) => l.fuente === 'lead').length,
|
leads: resumen.misLeads.filter((l) => l.fuente === 'lead').length,
|
||||||
oportunidades: resumen.misOportunidades.items.length,
|
oportunidades: resumen.misOportunidades.items.length,
|
||||||
apartados: resumen.misApartados.length, tablero: 0,
|
apartados: resumen.misApartados.length, escrituracion: resumen.misApartados.length, tablero: 0,
|
||||||
}}
|
}}
|
||||||
onCambiar={setTabCrm}
|
onCambiar={setTabCrm}
|
||||||
/>
|
/>
|
||||||
@@ -1108,7 +1109,13 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
|||||||
<FilaApartado
|
<FilaApartado
|
||||||
apartado={a}
|
apartado={a}
|
||||||
enviando={enviando}
|
enviando={enviando}
|
||||||
onFase={(fase) => void ejecutar(() => cambiarEstadoApartado(a.id, fase), 'No se pudo actualizar el apartado')}
|
onFase={(fase) => {
|
||||||
|
if (fase === 'Firma Cliente') {
|
||||||
|
setTabCrm('escrituracion')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
void ejecutar(() => cambiarEstadoApartado(a.id, fase), 'No se pudo actualizar el apartado')
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
@@ -1118,6 +1125,46 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
|||||||
)
|
)
|
||||||
})()}
|
})()}
|
||||||
|
|
||||||
|
{tabCrm === 'escrituracion' && (
|
||||||
|
<section aria-label="Escrituración">
|
||||||
|
<TituloSeccion>Escrituración</TituloSeccion>
|
||||||
|
<p className="text-sm text-[#64748B] dark:text-[#94A3B8] mb-4">
|
||||||
|
Seguimiento de escrituración de apartados.
|
||||||
|
</p>
|
||||||
|
{resumen.misApartados.length === 0 ? (
|
||||||
|
<EstadoVacio mensaje="No hay apartados en escrituración" />
|
||||||
|
) : (
|
||||||
|
<ul className="space-y-3">
|
||||||
|
{resumen.misApartados.map((apartado) => (
|
||||||
|
<li key={apartado.id} className="rounded-xl border border-[#E5E7EB] bg-white p-4 shadow-sm dark:border-[#1E293B] dark:bg-[#1E293B]">
|
||||||
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-[#111827] dark:text-[#E2E8F0]">{apartado.nombre}</p>
|
||||||
|
<p className="text-xs text-[#64748B] dark:text-[#94A3B8]">{apartado.unidadCodigo || 'Sin unidad'} · {apartado.estado}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 flex flex-col gap-2 sm:flex-row sm:flex-wrap sm:items-center">
|
||||||
|
{['Seguimiento', 'Llamada Escrituras', 'X Escriturar', 'Banco/Infonavit', 'X Autorizar', 'Escriturado', 'Cancelo'].map((fase) => (
|
||||||
|
<button
|
||||||
|
key={fase}
|
||||||
|
type="button"
|
||||||
|
disabled={enviando}
|
||||||
|
onClick={() => void ejecutar(() => cambiarEstadoApartado(apartado.id, fase), 'No se pudo actualizar')}
|
||||||
|
className={fase === apartado.estado
|
||||||
|
? 'rounded-lg border border-accent bg-accent/5 px-3 py-1.5 text-xs font-medium text-accent shadow-sm disabled:opacity-50'
|
||||||
|
: CLASE_BOTON_ACCION}
|
||||||
|
>
|
||||||
|
{fase}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
{tabCrm === 'tablero' && (
|
{tabCrm === 'tablero' && (
|
||||||
<DashboardVendedor />
|
<DashboardVendedor />
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user