fix(cancelar): botones Cancelación Apartado y Cancelo funcionan con motivo
- ModalDescarteGenerico restaurado (motivo + comentario) - Cancelación Apartado: abre modal → confirma → cambia estado - Cancelo en Escrituración: abre modal → confirma → cambia estado - onFase intercepta ambas fases y abre el modal de cancelación
This commit is contained in:
@@ -233,6 +233,73 @@ function FilaOportunidad({
|
||||
}
|
||||
|
||||
/** Fila de apartado con botones de fase del pipeline Enkontrol */
|
||||
function ModalDescarteGenerico({
|
||||
item,
|
||||
titulo,
|
||||
onCancelar,
|
||||
onConfirmar,
|
||||
enviando,
|
||||
}: {
|
||||
item: { id: string; nombre: string }
|
||||
titulo: string
|
||||
onCancelar: () => void
|
||||
onConfirmar: (motivo: string, subcategoria: string | undefined, comentario: string) => void
|
||||
enviando: boolean
|
||||
}) {
|
||||
const [motivo, setMotivo] = useState<string>('Timing')
|
||||
const [subcategoria] = useState('')
|
||||
const [comentario, setComentario] = useState('')
|
||||
return (
|
||||
<div className="fixed inset-0 z-10 flex items-center justify-center bg-black/40 px-4" role="dialog" aria-modal="true">
|
||||
<div className="w-full max-w-md rounded-2xl border border-[#E5E7EB] bg-white p-6 dark:border-[#1E293B] dark:bg-[#1E293B] shadow-xl">
|
||||
<p className="text-xs font-semibold uppercase tracking-wider text-[#64748B] dark:text-[#94A3B8]">{titulo}</p>
|
||||
<p className="mt-2 text-lg font-medium text-[#111827] dark:text-[#E2E8F0]">{item.nombre}</p>
|
||||
<form
|
||||
className="mt-6 space-y-4"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
onConfirmar(motivo, subcategoria === '' ? undefined : subcategoria, comentario.trim())
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<label htmlFor="motivo-cancel" className="block mb-2 text-xs font-medium text-[#4B5563] dark:text-[#CBD5E1]">Motivo</label>
|
||||
<select
|
||||
id="motivo-cancel"
|
||||
value={motivo}
|
||||
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) => (
|
||||
<option key={m} value={m}>{m}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="comentario-cancel" className="block mb-2 text-xs font-medium text-[#4B5563] dark:text-[#CBD5E1]">Comentario (opcional)</label>
|
||||
<textarea
|
||||
id="comentario-cancel"
|
||||
value={comentario}
|
||||
onChange={(e) => setComentario(e.target.value)}
|
||||
rows={3}
|
||||
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"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-end gap-3">
|
||||
<button type="button" onClick={onCancelar} disabled={enviando}
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-5 py-2 text-sm font-medium text-[#374151] dark:border-[#334155] dark:bg-[#0F172A] dark:text-[#E2E8F0] hover:bg-[#F9FAFB] disabled:opacity-50">
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="submit" disabled={enviando}
|
||||
className="rounded-lg bg-red-600 px-5 py-2 text-sm font-medium text-white shadow-sm hover:bg-red-700 disabled:opacity-50">
|
||||
{enviando ? 'Procesando…' : 'Confirmar'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function FilaApartado({
|
||||
apartado,
|
||||
onFase,
|
||||
@@ -290,6 +357,7 @@ function FilaApartado({
|
||||
return
|
||||
}
|
||||
if (fase === 'Cancelacion Apartado') {
|
||||
onFase(fase)
|
||||
return
|
||||
}
|
||||
onFase(fase)
|
||||
@@ -593,6 +661,7 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
||||
} | null>(null)
|
||||
const [tabCrm, setTabCrm] = useState<TabCrm>('leads')
|
||||
const [leadSeleccionado, setLeadSeleccionado] = useState<LeadMiDia | OportunidadMiDia | ApartadoMiDia | null>(null)
|
||||
const [apartadoCancelando, setApartadoCancelando] = useState<{ id: string; nombre: string } | null>(null)
|
||||
const [filtros, setFiltros] = useState<FiltrosCRM>({ texto: '', desarrollo: '', origen: '', fechaDesde: '', fechaHasta: '' })
|
||||
const [offline, setOffline] = useState(!navigator.onLine)
|
||||
const [offlineFecha, setOfflineFecha] = useState<string | null>(null)
|
||||
@@ -1153,6 +1222,7 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
||||
disabled={enviando}
|
||||
onClick={() => {
|
||||
if (fase === 'Cancelo') {
|
||||
setApartadoCancelando({ id: apartado.id, nombre: apartado.nombre })
|
||||
return
|
||||
}
|
||||
void ejecutar(() => cambiarEstadoApartado(apartado.id, fase), 'No se pudo actualizar')
|
||||
@@ -1496,6 +1566,23 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
||||
|
||||
|
||||
|
||||
{apartadoCancelando && (
|
||||
<ModalDescarteGenerico
|
||||
item={apartadoCancelando}
|
||||
titulo="Cancelar apartado"
|
||||
enviando={enviando}
|
||||
onCancelar={() => setApartadoCancelando(null)}
|
||||
onConfirmar={(motivo, subcategoria, comentario) => {
|
||||
const item = apartadoCancelando
|
||||
setApartadoCancelando(null)
|
||||
void ejecutar(
|
||||
() => cambiarEstadoApartado(item.id, 'Cancelacion Apartado', motivo, subcategoria, comentario),
|
||||
'No se pudo cancelar el apartado',
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{leadSeleccionado && (
|
||||
<DetalleLeadModal
|
||||
item={leadSeleccionado}
|
||||
|
||||
Reference in New Issue
Block a user