feat(alta-clientes): telefono obligatorio en alta y afluencia, importacion de informacion Enkontrol por ID en pantalla final y campos de obra movidos a la seccion Obra en captura
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
eliminarBorrador,
|
||||
generarLigaRemota,
|
||||
guardarBorrador,
|
||||
importarInformacionEnkontrol,
|
||||
iniciarProgreso,
|
||||
listarBorradores,
|
||||
obtenerBorrador,
|
||||
@@ -27,7 +28,9 @@ import {
|
||||
subirDocumentos,
|
||||
type BorradorResumen,
|
||||
type LeadResumen,
|
||||
type ResultadoImportacionEnkontrol,
|
||||
} from '../services/api'
|
||||
import { getSesion } from '../services/auth'
|
||||
import { cotizacionAPrellenado } from '../lib/cotizacionAlta'
|
||||
import { apartadoAPrellenado } from '../lib/apartadoAlta'
|
||||
import { leadAPrellenado } from '../lib/leadAlta'
|
||||
@@ -137,6 +140,29 @@ export default function AltaClientePage() {
|
||||
const [ligaCopiada, setLigaCopiada] = useState(false)
|
||||
const [errorLiga, setErrorLiga] = useState('')
|
||||
|
||||
// ---------- Importación del documento de Enkontrol (solo administración) ----------
|
||||
const rolSesion = getSesion()?.usuario.rol ?? ''
|
||||
const puedeImportarEnkontrol = rolSesion === 'admin' || rolSesion === 'manager'
|
||||
const [importandoEnkontrol, setImportandoEnkontrol] = useState(false)
|
||||
const [resultadoImportacion, setResultadoImportacion] = useState<ResultadoImportacionEnkontrol | null>(null)
|
||||
const [errorImportacion, setErrorImportacion] = useState('')
|
||||
const inputImportacionRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
const importarEnkontrol = async (archivo: File | undefined) => {
|
||||
if (!archivo) return
|
||||
setImportandoEnkontrol(true)
|
||||
setErrorImportacion('')
|
||||
setResultadoImportacion(null)
|
||||
try {
|
||||
setResultadoImportacion(await importarInformacionEnkontrol(archivo))
|
||||
} catch (err) {
|
||||
setErrorImportacion(err instanceof Error ? err.message : 'No se pudo importar el documento.')
|
||||
} finally {
|
||||
setImportandoEnkontrol(false)
|
||||
if (inputImportacionRef.current) inputImportacionRef.current.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
// Afluencia prellenada desde Mis oportunidades (Mi día): actualiza la
|
||||
// oportunidad existente en vez de crear una nueva
|
||||
const [afluenciaPrevia, setAfluenciaPrevia] = useState<{
|
||||
@@ -610,6 +636,51 @@ export default function AltaClientePage() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{puedeImportarEnkontrol && (
|
||||
<div className="mt-6 rounded-lg border border-[#E5E7EB] bg-white p-4 dark:border-[#334155] dark:bg-[#0F172A]">
|
||||
<p className="text-sm font-medium text-[#111827] dark:text-[#E2E8F0]">
|
||||
Importar información de Enkontrol
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-[#64748B] dark:text-[#94A3B8]">
|
||||
Sube el documento de Enkontrol (Excel o CSV). Cada fila se empareja con la ficha
|
||||
del cliente usando el ID de Enkontrol como campo determinante y su información se
|
||||
guarda en la ficha.
|
||||
</p>
|
||||
<div className="mt-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => inputImportacionRef.current?.click()}
|
||||
disabled={importandoEnkontrol}
|
||||
className="rounded-lg border border-accent bg-white px-4 py-2 text-xs font-medium text-accent shadow-sm transition-all duration-100 ease-out hover:bg-accent hover:text-white disabled:opacity-50 dark:bg-[#0F172A] dark:border-accent dark:text-accent"
|
||||
>
|
||||
{importandoEnkontrol ? 'Importando…' : 'Seleccionar documento…'}
|
||||
</button>
|
||||
<input
|
||||
ref={inputImportacionRef}
|
||||
type="file"
|
||||
accept=".xlsx,.xls,.csv"
|
||||
className="sr-only"
|
||||
onChange={(e) => void importarEnkontrol(e.target.files?.[0])}
|
||||
/>
|
||||
</div>
|
||||
{resultadoImportacion && (
|
||||
<p role="status" className="mt-3 rounded-lg border border-green-200 bg-green-50 px-3 py-2 text-xs text-green-700 dark:border-green-800 dark:bg-green-900/30 dark:text-green-300">
|
||||
Importación lista: {resultadoImportacion.actualizados} de {resultadoImportacion.totalFilas}{' '}
|
||||
clientes actualizados.
|
||||
{resultadoImportacion.noEncontrados.length > 0 && (
|
||||
<> Sin ficha local: {resultadoImportacion.noEncontrados.slice(0, 10).join(', ')}
|
||||
{resultadoImportacion.noEncontrados.length > 10 ? '…' : ''}</>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
{errorImportacion && (
|
||||
<p role="alert" className="mt-3 rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-700">
|
||||
{errorImportacion}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="mt-6 text-xs text-green-800/80 dark:text-green-400/80">
|
||||
Con la liga de firma, el cliente firma su expediente y sube documentos desde su
|
||||
celular (vigencia 72 h, sin usuario ni contraseña).
|
||||
|
||||
Reference in New Issue
Block a user