From 756815983dfd4ec126666ad8961640ec406b5e39 Mon Sep 17 00:00:00 2001 From: Luis Date: Wed, 26 Nov 2025 08:48:50 -0700 Subject: [PATCH] Subo archivo JSX sin afectar rama principal --- .gitignore | 1 + src/pages/Expedientes.jsx | 492 ++++++++++++++++++++++++-------------- 2 files changed, 320 insertions(+), 173 deletions(-) diff --git a/.gitignore b/.gitignore index 7ceb59f..6ee797a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ dist-ssr *.sln *.sw? .env +*.bak \ No newline at end of file diff --git a/src/pages/Expedientes.jsx b/src/pages/Expedientes.jsx index 401be41..38626c1 100644 --- a/src/pages/Expedientes.jsx +++ b/src/pages/Expedientes.jsx @@ -18,11 +18,13 @@ const API_URL = import.meta.env.VITE_EFC_API_URL; const downloadFile = async (id, filename = 'archivo', setSuccess, setError, showMessage) => { try { + console.log('Descargar: ',id); const res = await fetchWithAuth(`${API_URL}/record/documents/descargar/${id}/`); if (!res.ok) { - alert('No autorizado o error en la descarga'); - return; + //alert('No autorizado o error en la descarga'); + showMessage('No autorizado o error en la descarga.', 'error'); + return false; } const blob = await res.blob(); @@ -38,7 +40,9 @@ const downloadFile = async (id, filename = 'archivo', setSuccess, setError, show } catch (error) { console.error('Error downloading file:', error); showMessage('Error al descargar el archivo', 'error'); + return false; } + return true; }; export default function Documents() { @@ -192,33 +196,142 @@ export default function Documents() { } }; + // // Función para descargar documentos seleccionados + // const handleDownloadSelected = async () => { + // if (selectedDocuments.length === 0) { + // showMessage('No hay documentos seleccionados para descargar', 'warning'); + // return; + // } + + // try { + // showMessage(`Iniciando descarga de ${selectedDocuments.length} documento(s)...`, 'info'); + // const resultados = []; + // for (const docId of selectedDocuments) { + // const document = currentDocuments.find(doc => doc.id === docId); + // if (document) { + // // const exito =await downloadFile(docId, `expediente_${document.pedimento_app}`, null, null, showMessage); + // const exito =await downloadExpediente(docId, `expediente_${document.pedimento_app}`, null, showMessage); + // resultados.push(exito); + // // Pequeña pausa entre descargas para no sobrecargar + // await new Promise(resolve => setTimeout(resolve, 500)); + // } + // } + // const todosExitosos = resultados.every(Boolean); + // if(todosExitosos){ + // showMessage(`Descarga completada de ${selectedDocuments.length} documento(s)`, 'success'); + // }else{ + // showMessage('Algunas descargas fallaron. Revisa los archivos seleccionados.', 'warning'); + // } + + // setSelectedDocuments([]); + // setIsSelectAll(false); + // } catch (error) { + // showMessage('Error durante la descarga masiva', 'error'); + // } + // // showMessage('Error durante la descarga masiva', 'error'); + // }; // Función para descargar documentos seleccionados const handleDownloadSelected = async () => { - if (selectedDocuments.length === 0) { - showMessage('No hay documentos seleccionados para descargar', 'warning'); - return; - } + if (selectedDocuments.length === 0) { + showMessage('No hay documentos seleccionados para descargar', 'warning'); + return; + } - try { - showMessage(`Iniciando descarga de ${selectedDocuments.length} documento(s)...`, 'info'); + try { - for (const docId of selectedDocuments) { - const document = currentDocuments.find(doc => doc.id === docId); - if (document) { - await downloadFile(docId, `expediente_${document.pedimento_app}`, null, null, showMessage); - // Pequeña pausa entre descargas para no sobrecargar - await new Promise(resolve => setTimeout(resolve, 500)); + showMessage(`Iniciando descarga de ${selectedDocuments.length} documento(s) selecciobado(s)...`, 'info'); + + const res = await postWithAuth(`${API_URL}/record/documents/multi-pedimento-zip/`, { + pedimento_ids: selectedDocuments + }); + + if (!res.ok){ + showMessage('Algunas descargas fallaron. Revisa los archivos seleccionados.', 'warning'); + }else { + // Leer el nombre que eligió el backend + const zipFileName = res.headers.get('X-Zip-Filename') || 'expedientes.zip'; + const blob = await res.blob(); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = zipFileName; + a.click(); + a.remove(); + window.URL.revokeObjectURL(url); + showMessage(`Descarga completada de ${selectedDocuments.length} documento(s)`, 'success'); + + setSelectedDocuments([]); + setIsSelectAll(false); } - } - - showMessage(`Descarga completada de ${selectedDocuments.length} documento(s)`, 'success'); - setSelectedDocuments([]); - setIsSelectAll(false); - } catch (error) { - showMessage('Error durante la descarga masiva', 'error'); - } + + } catch (error) { + showMessage('Error durante la descarga masiva', 'error'); + } + // showMessage('Error durante la descarga masiva', 'error'); }; + + // Función para descargar todo el expediente + const handleDownloadTodoElExpediente = async (pedimentoId, pedimentoName) => { + + try { + showMessage(`Iniciando descarga de ${selectedDocuments.length} documento(s)...`, 'info'); + const resultados = []; + + const document = currentDocuments.find(doc => doc.id === pedimentoId); + if (document) { + const exito =await downloadExpediente(pedimentoId, `expediente_${pedimentoName}`, null, showMessage); + resultados.push(exito); + // Pequeña pausa entre descargas para no sobrecargar + await new Promise(resolve => setTimeout(resolve, 500)); + } + + const todosExitosos = resultados.every(Boolean); + if(todosExitosos){ + showMessage(`Descarga completada de ${selectedDocuments.length} documento(s)`, 'success'); + }else{ + // showMessage('Algunas descargas fallaron. Revisa los archivos seleccionados.', 'warning'); + } + + setSelectedDocuments([]); + setIsSelectAll(false); + } catch (error) { + showMessage('Error durante la descarga masiva', 'error'); + } +}; + +const downloadExpediente = async (pedimentoId, pedimentoName, setSuccess, showMessage) => { + try { + const res = await postWithAuth(`${API_URL}/record/documents/expediente-zip/`, { + pedimento_id: pedimentoId, + }); + + if (!res.ok) { + // alert('No autorizado o error en la descarga'); + // showMessage('No autorizado o error en la descarga.', 'error'); + const err = await res.json(); + showMessage(err.error || 'Error al generar ZIP', 'error'); + return false; + } + + const blob = await res.blob(); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `${pedimentoName}.zip`; + // document.body.appendChild(a); + a.click(); + a.remove(); + window.URL.revokeObjectURL(url); + if (setSuccess) setSuccess('Descarga exitosa'); + } catch (error) { + console.error('Error downloading file:', error); + showMessage('Error al descargar el archivo', 'error'); + return false; + } + return true; +}; + // Función para eliminar documentos seleccionados const handleDeleteSelected = async () => { if (selectedDocuments.length === 0) { @@ -485,10 +598,30 @@ export default function Documents() { const uploadEndpoint = `${API_URL}/customs/pedimentos/bulk-create/`; const result = await postFormDataWithAuth(uploadEndpoint, formData); - showMessage( - `${result.uploaded_count || fileCount} archivo(s) subido(s) exitosamente`, - 'success' - ); + if(!result.ok){ + let errorMsg = 'Error al intentar cargar los archivos'; + try { + const errorData = await result.json(); + errorMsg = errorData.error || errorMsg; + } catch { + // Si no es JSON válido, usar texto plano + const text = await result.text(); + errorMsg = text || errorMsg; + } + showMessage(errorMsg, 'error'); + }else{ + showMessage( + `${result.uploaded_count || fileCount} archivo(s) subido(s) exitosamente`, + 'success' + ); + } + + // showMessage( + // `${result.uploaded_count || fileCount} archivo(s) subido(s) exitosamente`, + // 'success' + // ); + + // console.log(result); // Limpiar archivos seleccionados y cerrar modal setSelectedFiles([]); @@ -522,47 +655,60 @@ export default function Documents() { setIsSelectAll(false); }, [currentPage]); + // función que detecta si hay filtros activos + const hasActiveFilters = [ + searchFilter, + pedimentoFilter, + expedienteFilter !== 'all', + contribuyenteFilter, + curpApoderadoFilter, + fechaPagoFilter, + patenteFilter, + aduanaFilter, + tipoOperacionFilter, + clavePedimentoFilter, +].some(Boolean); // El layout principal y la tabla siempre se renderizan, loader/error/empty solo dentro del área de la tabla return ( -
+
-
+
{/* Header mejorado y decorativo */}
-
- +
+
-

+

Expedientes {totalDocuments > 0 && ( - + {totalDocuments} registros )}

-

Gestiona y descarga los documentos de tus pedimentos

+

Gestiona y descarga los documentos de tus pedimentos

{/* Efectos decorativos de fondo modernos */} -
-
+
+
-
-
+
+
{/* Partículas flotantes */}
-
-
-
+
+
+
{/* Animación personalizada para el icono y contador */} @@ -588,16 +734,16 @@ export default function Documents() { (showAnimation && !hasAnimated ? ' animate-fadein-slideup opacity-0' : '') } style={showAnimation && !hasAnimated ? { animation: 'fadein-slideup 0.7s cubic-bezier(0.22,1,0.36,1) 0.15s forwards' } : undefined}> -
+
{/* Filtros avanzados */}
-

+

Filtros de búsqueda

-
+
{/* Search global */}
@@ -631,7 +777,7 @@ export default function Documents() {
{/* Contribuyente combobox */} -
+
{/* Dropdown de sugerencias */} {contribuyenteInput && ( -
+
{contribuyentes.filter(c => c.toLowerCase().includes(contribuyenteInput.toLowerCase())).length === 0 ? (
Sin coincidencias
) : ( @@ -657,7 +803,7 @@ export default function Documents() {
)} -
+
- + Actualización automática cada 30s {loading && ( - + @@ -826,21 +972,21 @@ export default function Documents() { Actualizar Ahora
{success && ( -
+
- +
@@ -855,30 +1001,30 @@ export default function Documents() {
{/* Vista de tabla para pantallas grandes */}
-
+
- - - - - - - - - - - - - + + + + + + + + + + + + @@ -886,8 +1032,8 @@ export default function Documents() { @@ -895,12 +1041,12 @@ export default function Documents() { @@ -912,22 +1058,22 @@ export default function Documents() { type="checkbox" checked={selectedDocuments.includes(ped.id)} onChange={(e) => handleSelectDocument(ped.id, e.target.checked)} - className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" + className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" /> - - - - - + + + + + - - - + + - @@ -1003,37 +1149,37 @@ export default function Documents() { {/* Vista de tarjetas para pantallas pequeñas y medianas */} -
+
{loading ? (
-
- Cargando expedientes... +
+ Cargando expedientes...
) : error ? (
-
- +
+
- Error: {error.message || 'Error al cargar expedientes'} + Error: {error.message || 'Error al cargar expedientes'}
) : currentDocuments.length > 0 ? ( currentDocuments.map(ped => ( -
+
{/* Checkbox en la esquina superior derecha */}
handleSelectDocument(ped.id, e.target.checked)} - className="h-5 w-5 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" + className="w-5 h-5 text-blue-600 border-gray-300 rounded focus:ring-blue-500" />
-
{/* Agregamos pr-8 para dar espacio al checkbox */} +
{/* Agregamos pr-8 para dar espacio al checkbox */}
-
+
@@ -1041,7 +1187,7 @@ export default function Documents() {
{ped.pedimento_app} @@ -1057,7 +1203,7 @@ export default function Documents() {
-
+
Contribuyente: @@ -1071,27 +1217,27 @@ export default function Documents() {
)}
-
+
Tipo Operacion {ped.tipo_operacion}
-
+
Partidas {ped.numero_partidas}
-
+
Fecha de Carga {ped.created_at ? ped.created_at.slice(0, 10) : ''}
-
+
Clave {ped.clave_pedimento}
-
+
No. Archivos {ped.documentos_count || 0}
-
+
Peso Total {typeof ped.documentos_peso_total === 'number' @@ -1104,20 +1250,20 @@ export default function Documents() {
)) ) : ( -
-
+
+
-

No hay expedientes disponibles

-

Intenta ajustar los filtros de búsqueda

+

No hay expedientes disponibles

+

Intenta ajustar los filtros de búsqueda

)}
{/* Paginación moderna y responsiva */} {totalDocuments > 0 && ( -
+
{(() => { const totalPages = Math.max(1, Math.ceil(totalDocuments / itemsPerPage)); const maxPagesToShow = 5; @@ -1132,21 +1278,21 @@ export default function Documents() { pageNumbers.push(i); } return ( -
+
-
+
-
+
{pageNumbers.map(num => (
-
+
{currentPage} / {totalPages} @@ -1199,7 +1345,7 @@ export default function Documents() {
- + Mostrando {((currentPage - 1) * itemsPerPage) + 1} a {Math.min(currentPage * itemsPerPage, totalDocuments)} de {totalDocuments} registros
@@ -1213,13 +1359,13 @@ export default function Documents() { {/* Modal de subida de expedientes */} {showUploadModal && ( -
+
{/* Header del modal */} -
+
-
+
@@ -1237,7 +1383,7 @@ export default function Documents() { setIsUploading(false); setValidationErrors([]); }} - className="text-gray-400 hover:text-gray-600 transition-colors duration-200" + className="text-gray-400 transition-colors duration-200 hover:text-gray-600" > @@ -1247,10 +1393,10 @@ export default function Documents() {
{/* Contenido del modal - con scroll */} -
+
{/* Selector de tipo de subida */}
-
+ PedimentoFecha PagoContribuyenteCURP Apod.PartidasF. CargaTipo Op.ClaveArchivosPeso TotalExpedienteAccionesPedimentoFecha PagoContribuyenteCURP Apod.PartidasF. CargaTipo Op.ClaveArchivosPeso TotalExpedienteAcciones
-
- Cargando expedientes... +
+ Cargando expedientes...
-
- +
+
- Error: {error.message || 'Error al cargar expedientes'} + Error: {error.message || 'Error al cargar expedientes'}
{ped.pedimento_app} {ped.fecha_pago}{ped.contribuyente}{ped.curp_apoderado}{ped.numero_partidas}{ped.created_at ? ped.created_at.slice(0, 10) : ''}{ped.fecha_pago}{ped.contribuyente}{ped.curp_apoderado}{ped.numero_partidas}{ped.created_at ? ped.created_at.slice(0, 10) : ''} {ped.tipo_operacion === 1 @@ -937,9 +1083,9 @@ export default function Documents() { : ped.tipo_operacion} {ped.clave_pedimento}{ped.documentos_count || 0} + {ped.clave_pedimento}{ped.documentos_count || 0}
{typeof ped.documentos_peso_total === 'number' ? (ped.documentos_peso_total / (1024 * 1024)).toFixed(2) + ' MB' @@ -969,13 +1115,13 @@ export default function Documents() { )}
+ @@ -986,12 +1132,12 @@ export default function Documents() {
-
- +
+
-

No hay expedientes

+

No hay expedientes

No se encontraron expedientes con los filtros aplicados.