Se mejoro estetica y estandarizaron estilos
This commit is contained in:
@@ -64,9 +64,14 @@ export default function Procesos() {
|
||||
}
|
||||
try {
|
||||
const token = localStorage.getItem('access');
|
||||
if (!token) {
|
||||
alert('No hay token de autenticación. Por favor, inicia sesión nuevamente.');
|
||||
setExecutingId(null);
|
||||
return;
|
||||
}
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { 'Authorization': `Bearer ${token}` } : {}),
|
||||
'Authorization': `Bearer ${token}`,
|
||||
};
|
||||
const body = JSON.stringify({
|
||||
pedimento: typeof proc.pedimento === 'object' && proc.pedimento !== null ? proc.pedimento.id : proc.pedimento,
|
||||
@@ -90,14 +95,19 @@ export default function Procesos() {
|
||||
// Cierra el dropdown si se hace click fuera
|
||||
useEffect(() => {
|
||||
if (openDropdownId === null) return;
|
||||
function handleClick(e) {
|
||||
function handleClickOutside(e) {
|
||||
const el = document.getElementById(`dropdown-acciones-${openDropdownId}`);
|
||||
if (el && !el.contains(e.target)) {
|
||||
setOpenDropdownId(null);
|
||||
}
|
||||
}
|
||||
document.addEventListener('mousedown', handleClick);
|
||||
return () => document.removeEventListener('mousedown', handleClick);
|
||||
// Usar setTimeout para evitar que el click que abre el dropdown lo cierre inmediatamente
|
||||
setTimeout(() => {
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
}, 100);
|
||||
return () => {
|
||||
document.removeEventListener('click', handleClickOutside);
|
||||
};
|
||||
}, [openDropdownId]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -106,6 +116,10 @@ export default function Procesos() {
|
||||
setError('');
|
||||
try {
|
||||
const token = localStorage.getItem('access');
|
||||
if (!token) {
|
||||
setError('No hay token de autenticación. Por favor, inicia sesión nuevamente.');
|
||||
return;
|
||||
}
|
||||
// Construir query params
|
||||
const params = new URLSearchParams();
|
||||
params.append('page', String(page));
|
||||
@@ -117,10 +131,21 @@ export default function Procesos() {
|
||||
params.append('ordering', (sortOrder === 'desc' ? '-' : '') + sortField);
|
||||
}
|
||||
const API_URL = import.meta.env.VITE_EFC_API_URL;
|
||||
const headers = token ? { 'Authorization': `Bearer ${token}` } : {};
|
||||
const headers = {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
console.log('Fetching procesos with token:', token ? 'Token present' : 'No token');
|
||||
console.log('URL:', `${API_URL}/customs/procesamientopedimentos/?${params.toString()}`);
|
||||
const res = await fetch(`${API_URL}/customs/procesamientopedimentos/?${params.toString()}`, { headers });
|
||||
if (!res.ok) throw new Error('Error al obtener procesamiento de pedimentos');
|
||||
console.log('Response status:', res.status);
|
||||
if (!res.ok) {
|
||||
const errorText = await res.text();
|
||||
console.log('Error response:', errorText);
|
||||
throw new Error(`Error al obtener procesamiento de pedimentos: ${res.status} - ${errorText}`);
|
||||
}
|
||||
const data = await res.json();
|
||||
console.log('Data received:', data);
|
||||
setProcesos(data.results || []);
|
||||
setCount(data.count || 0);
|
||||
} catch (err) {
|
||||
@@ -133,41 +158,48 @@ export default function Procesos() {
|
||||
}, [page, itemsPerPage, pedimentoPedimentoFilter, estadoFilter, servicioFilter, sortField, sortOrder]);
|
||||
|
||||
return (
|
||||
<div className="p-6 bg-gray-50 min-h-screen">
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-100 p-4 sm:p-6 lg:p-8">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="mb-8 relative overflow-hidden rounded-2xl shadow bg-gradient-to-r from-blue-50 via-white to-indigo-50 border border-blue-100 p-8 flex items-center gap-6 animate-fadein-slideup opacity-0" style={{ animation: 'fadein-slideup 0.7s cubic-bezier(0.22,1,0.36,1) 0.05s forwards' }}>
|
||||
<div className="flex-shrink-0 bg-blue-100 rounded-full p-4 shadow-md animate-bounce-slow">
|
||||
<svg className="h-10 w-10 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
{/* Header mejorado y responsivo */}
|
||||
<div className="mb-6 sm:mb-8 relative overflow-hidden rounded-3xl shadow-2xl bg-gradient-to-r from-blue-600 via-blue-700 to-blue-800 p-6 sm:p-8 flex items-center gap-4 sm:gap-6 animate-fadein-slideup opacity-0"
|
||||
style={{ animation: 'fadein-slideup 0.7s cubic-bezier(0.22,1,0.36,1) 0.05s forwards' }}>
|
||||
<div className="flex-shrink-0 bg-white/20 backdrop-blur-sm rounded-full p-3 sm:p-4 shadow-lg animate-bounce-slow">
|
||||
<svg className="h-8 w-8 sm:h-10 sm:w-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-3xl font-extrabold text-blue-900 tracking-tight mb-1 flex items-center gap-2">
|
||||
Procesos del Sistema
|
||||
<span className="inline-block bg-blue-200 text-blue-800 text-xs font-semibold px-2 py-0.5 rounded-full ml-2 animate-fade-in" title="Total de procesos">
|
||||
{count}
|
||||
</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-extrabold text-white tracking-tight mb-1 flex flex-col sm:flex-row sm:items-center gap-2">
|
||||
<span>Procesos del Sistema</span>
|
||||
{count > 0 && (
|
||||
<span className="inline-block bg-white/20 backdrop-blur-sm text-white text-xs sm:text-sm font-semibold px-3 py-1 rounded-full shadow-lg animate-fade-in">
|
||||
{count} procesos
|
||||
</span>
|
||||
)}
|
||||
</h1>
|
||||
<p className="text-lg text-blue-700/80 font-medium">Estado actual de los procesos de la agencia aduanal</p>
|
||||
<p className="text-sm sm:text-lg text-blue-100 font-medium leading-relaxed">Estado actual de los procesos de la agencia aduanal</p>
|
||||
</div>
|
||||
<div className="absolute -top-10 -right-10 opacity-30 pointer-events-none select-none">
|
||||
<svg width="120" height="120" viewBox="0 0 120 120" fill="none">
|
||||
<circle cx="60" cy="60" r="50" fill="url(#grad1)" />
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0" y1="0" x2="120" y2="120" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#3b82f6" stopOpacity="0.15" />
|
||||
<stop offset="1" stopColor="#6366f1" stopOpacity="0.10" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
{/* Efectos decorativos de fondo modernos */}
|
||||
<div className="absolute -top-10 -right-10 opacity-20 pointer-events-none select-none">
|
||||
<div className="w-32 h-32 bg-white/10 rounded-full blur-xl"></div>
|
||||
</div>
|
||||
<div className="absolute -bottom-6 -left-6 opacity-15 pointer-events-none select-none">
|
||||
<div className="w-24 h-24 bg-white/10 rounded-full blur-lg"></div>
|
||||
</div>
|
||||
{/* Partículas flotantes */}
|
||||
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<div className="absolute top-1/4 left-1/4 w-2 h-2 bg-white/30 rounded-full animate-ping"></div>
|
||||
<div className="absolute top-3/4 right-1/3 w-1 h-1 bg-white/40 rounded-full animate-pulse"></div>
|
||||
<div className="absolute top-1/2 right-1/4 w-3 h-3 bg-white/20 rounded-full animate-bounce"></div>
|
||||
</div>
|
||||
{/* Animaciones CSS */}
|
||||
<style>{`
|
||||
@keyframes bounce-slow {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-8px); }
|
||||
0%, 100% { transform: translateY(0) scale(1); }
|
||||
50% { transform: translateY(-8px) scale(1.05); }
|
||||
}
|
||||
.animate-bounce-slow {
|
||||
animation: bounce-slow 2.2s infinite;
|
||||
animation: bounce-slow 3s infinite;
|
||||
}
|
||||
@keyframes fadein-slideup {
|
||||
0% { opacity: 0; transform: translateY(40px); }
|
||||
@@ -177,200 +209,440 @@ export default function Procesos() {
|
||||
animation: fadein-slideup 0.7s cubic-bezier(0.22,1,0.36,1) 0.05s forwards;
|
||||
}
|
||||
@keyframes fade-in {
|
||||
from { opacity: 0; transform: scale(0.9); }
|
||||
to { opacity: 1; transform: scale(1); }
|
||||
from { opacity: 0; transform: scale(0.9) translateY(10px); }
|
||||
to { opacity: 1; transform: scale(1) translateY(0); }
|
||||
}
|
||||
.animate-fade-in {
|
||||
animation: fade-in 0.7s ease;
|
||||
animation: fade-in 0.8s ease-out;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl shadow-lg border border-gray-200 p-8 animate-fadein-slideup opacity-0" style={{ animation: 'fadein-slideup 0.7s cubic-bezier(0.22,1,0.36,1) 0.15s forwards' }}>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h2 className="text-2xl font-bold text-blue-800">Procesamiento de Pedimentos</h2>
|
||||
{/* Contenido principal */}
|
||||
<div className="bg-white rounded-3xl shadow-2xl border border-gray-100 p-4 sm:p-6 lg:p-8 animate-fadein-slideup opacity-0"
|
||||
style={{ animation: 'fadein-slideup 0.7s cubic-bezier(0.22,1,0.36,1) 0.15s forwards' }}>
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between mb-6 gap-4">
|
||||
<h2 className="text-xl sm:text-2xl font-bold text-gray-900 flex items-center gap-3">
|
||||
<div className="bg-gradient-to-br from-blue-500 to-blue-600 rounded-xl p-2 shadow-lg">
|
||||
<svg className="w-5 h-5 sm:w-6 sm:h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||
</svg>
|
||||
</div>
|
||||
Procesamiento de Pedimentos
|
||||
</h2>
|
||||
{count > 0 && (
|
||||
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl px-4 py-2 border border-blue-100">
|
||||
<span className="text-sm font-medium text-blue-700">Total de registros: </span>
|
||||
<span className="text-lg font-bold text-blue-800">{count}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Filtros */}
|
||||
<div className="mb-4 flex flex-wrap gap-4 items-end justify-between">
|
||||
<div className="flex flex-col flex-1 min-w-[150px]">
|
||||
<label className="text-xs font-semibold text-gray-700 mb-1">Pedimento</label>
|
||||
<input
|
||||
type="text"
|
||||
value={pedimentoPedimentoFilter}
|
||||
onChange={e => setPedimentoPedimentoFilter(e.target.value)}
|
||||
placeholder="Buscar por pedimento..."
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-gray-50"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col flex-1 min-w-[150px]">
|
||||
<label className="text-xs font-semibold text-gray-700 mb-1">Estado</label>
|
||||
<select
|
||||
value={estadoFilter}
|
||||
onChange={e => setEstadoFilter(e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-gray-50"
|
||||
>
|
||||
<option value="">Todos</option>
|
||||
<option value="1">En Espera</option>
|
||||
<option value="2">Procesando</option>
|
||||
<option value="3">Finalizado</option>
|
||||
<option value="4">Error</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex flex-col flex-1 min-w-[150px]">
|
||||
<label className="text-xs font-semibold text-gray-700 mb-1">Servicio</label>
|
||||
<select
|
||||
value={servicioFilter}
|
||||
onChange={e => setServicioFilter(e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-gray-50"
|
||||
>
|
||||
<option value="">Todos</option>
|
||||
<option value="1">Estado de pedimento</option>
|
||||
<option value="2">Listado de pedimentos</option>
|
||||
<option value="3">Pedimento Completo</option>
|
||||
<option value="4">Pedimento Partidas</option>
|
||||
<option value="5">Pedimento Remesas</option>
|
||||
<option value="6">Acuse</option>
|
||||
<option value="7">EDocument</option>
|
||||
<option value="8">Cove</option>
|
||||
<option value="9">Acuse Cove</option>
|
||||
|
||||
</select>
|
||||
|
||||
{/* Filtros responsivos mejorados */}
|
||||
<div className="mb-6 bg-gradient-to-r from-gray-50 to-slate-50 rounded-2xl p-4 sm:p-6 border border-gray-100">
|
||||
<h3 className="text-lg font-semibold text-gray-800 mb-4 flex items-center gap-2">
|
||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.414A1 1 0 013 6.707V4z" />
|
||||
</svg>
|
||||
Filtros de búsqueda
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-semibold text-gray-700 flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-blue-500 rounded-full"></div>
|
||||
Pedimento
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={pedimentoPedimentoFilter}
|
||||
onChange={e => setPedimentoPedimentoFilter(e.target.value)}
|
||||
placeholder="Buscar por pedimento..."
|
||||
className="w-full border border-gray-300 rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white shadow-sm transition-all duration-200 hover:shadow-md"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-semibold text-gray-700 flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
|
||||
Estado
|
||||
</label>
|
||||
<select
|
||||
value={estadoFilter}
|
||||
onChange={e => setEstadoFilter(e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white shadow-sm transition-all duration-200 hover:shadow-md"
|
||||
>
|
||||
<option value="">Todos los estados</option>
|
||||
<option value="1">En Espera</option>
|
||||
<option value="2">Procesando</option>
|
||||
<option value="3">Finalizado</option>
|
||||
<option value="4">Error</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="space-y-2 sm:col-span-2 lg:col-span-1">
|
||||
<label className="text-sm font-semibold text-gray-700 flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-orange-500 rounded-full"></div>
|
||||
Servicio
|
||||
</label>
|
||||
<select
|
||||
value={servicioFilter}
|
||||
onChange={e => setServicioFilter(e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white shadow-sm transition-all duration-200 hover:shadow-md"
|
||||
>
|
||||
<option value="">Todos los servicios</option>
|
||||
<option value="1">Estado de pedimento</option>
|
||||
<option value="2">Listado de pedimentos</option>
|
||||
<option value="3">Pedimento Completo</option>
|
||||
<option value="4">Pedimento Partidas</option>
|
||||
<option value="5">Pedimento Remesas</option>
|
||||
<option value="6">Acuse</option>
|
||||
<option value="7">EDocument</option>
|
||||
<option value="8">Cove</option>
|
||||
<option value="9">Acuse Cove</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Estados de carga y error mejorados */}
|
||||
{loading ? (
|
||||
<div className="text-center text-gray-500 py-8">Cargando procesos...</div>
|
||||
<div className="flex flex-col items-center justify-center py-12">
|
||||
<div className="relative">
|
||||
<div className="animate-spin rounded-full h-16 w-16 border-b-2 border-blue-600"></div>
|
||||
<div className="absolute inset-0 bg-blue-500/10 rounded-full blur-xl animate-pulse"></div>
|
||||
</div>
|
||||
<p className="mt-4 text-gray-600 font-medium">Cargando procesos...</p>
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="text-center text-danger-600 py-8">{error}</div>
|
||||
<div className="bg-red-50 border border-red-200 rounded-2xl p-6 text-center">
|
||||
<div className="bg-red-100 rounded-full p-3 w-12 h-12 mx-auto mb-4 flex items-center justify-center">
|
||||
<svg className="w-6 h-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-red-800 mb-2">Error al cargar</h3>
|
||||
<p className="text-red-600">{error}</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="overflow-x-auto">
|
||||
<div style={{ minHeight: 'calc(6 * 56px)', maxHeight: 'calc(6 * 56px)', overflowY: 'auto', position: 'relative' }}>
|
||||
<table className="min-w-full divide-y divide-gray-200 rounded-lg overflow-hidden sticky text-xs">
|
||||
<thead className="bg-gradient-to-r from-gray-50 sticky top-0 z-20">
|
||||
<>
|
||||
{/* Vista de tabla para pantallas grandes */}
|
||||
<div className="hidden lg:block overflow-x-auto bg-white rounded-2xl border border-gray-200 shadow-sm">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gradient-to-r from-gray-50 to-slate-50">
|
||||
<tr>
|
||||
<th className="px-2 py-2 text-center font-bold text-blue-700 uppercase tracking-wider border-b border-gray-200 whitespace-nowrap cursor-pointer select-none"
|
||||
<th className="px-4 py-4 text-center text-xs font-bold text-gray-600 uppercase tracking-wider cursor-pointer select-none hover:bg-gray-100 transition-colors duration-200 rounded-tl-2xl"
|
||||
onClick={() => {
|
||||
setSortField('id');
|
||||
setSortOrder(sortField === 'id' && sortOrder === 'asc' ? 'desc' : 'asc');
|
||||
}}
|
||||
>
|
||||
ID {sortField === 'id' && (sortOrder === 'asc' ? '▲' : '▼')}
|
||||
<div className="flex items-center justify-center gap-1">
|
||||
ID {sortField === 'id' && (sortOrder === 'asc' ? '▲' : '▼')}
|
||||
</div>
|
||||
</th>
|
||||
<th className="px-2 py-2 text-left font-bold uppercase tracking-wider border-b border-gray-200 whitespace-nowrap cursor-pointer select-none"
|
||||
<th className="px-4 py-4 text-left text-xs font-bold text-gray-600 uppercase tracking-wider cursor-pointer select-none hover:bg-gray-100 transition-colors duration-200"
|
||||
onClick={() => {
|
||||
setSortField('organizacion_name');
|
||||
setSortOrder(sortField === 'organizacion_name' && sortOrder === 'asc' ? 'desc' : 'asc');
|
||||
}}
|
||||
>
|
||||
Organización {sortField === 'organizacion_name' && (sortOrder === 'asc' ? '▲' : '▼')}
|
||||
<div className="flex items-center gap-1">
|
||||
Organización {sortField === 'organizacion_name' && (sortOrder === 'asc' ? '▲' : '▼')}
|
||||
</div>
|
||||
</th>
|
||||
<th className="px-2 py-2 text-left font-bold uppercase tracking-wider border-b border-gray-200 whitespace-nowrap cursor-pointer select-none"
|
||||
<th className="px-4 py-4 text-left text-xs font-bold text-gray-600 uppercase tracking-wider cursor-pointer select-none hover:bg-gray-100 transition-colors duration-200"
|
||||
onClick={() => {
|
||||
setSortField('estado');
|
||||
setSortOrder(sortField === 'estado' && sortOrder === 'asc' ? 'desc' : 'asc');
|
||||
}}
|
||||
>
|
||||
Estado {sortField === 'estado' && (sortOrder === 'asc' ? '▲' : '▼')}
|
||||
<div className="flex items-center gap-1">
|
||||
Estado {sortField === 'estado' && (sortOrder === 'asc' ? '▲' : '▼')}
|
||||
</div>
|
||||
</th>
|
||||
<th className="px-2 py-2 text-left font-bold uppercase tracking-wider border-b border-gray-200 whitespace-nowrap cursor-pointer select-none"
|
||||
<th className="px-4 py-4 text-left text-xs font-bold text-gray-600 uppercase tracking-wider cursor-pointer select-none hover:bg-gray-100 transition-colors duration-200"
|
||||
onClick={() => {
|
||||
setSortField('pedimento');
|
||||
setSortOrder(sortField === 'pedimento' && sortOrder === 'asc' ? 'desc' : 'asc');
|
||||
}}
|
||||
>
|
||||
Pedimento {sortField === 'pedimento' && (sortOrder === 'asc' ? '▲' : '▼')}
|
||||
<div className="flex items-center gap-1">
|
||||
Pedimento {sortField === 'pedimento' && (sortOrder === 'asc' ? '▲' : '▼')}
|
||||
</div>
|
||||
</th>
|
||||
<th className="px-2 py-2 text-left font-bold uppercase tracking-wider border-b border-gray-200 whitespace-nowrap cursor-pointer select-none"
|
||||
<th className="px-4 py-4 text-left text-xs font-bold text-gray-600 uppercase tracking-wider cursor-pointer select-none hover:bg-gray-100 transition-colors duration-200"
|
||||
onClick={() => {
|
||||
setSortField('servicio');
|
||||
setSortOrder(sortField === 'servicio' && sortOrder === 'asc' ? 'desc' : 'asc');
|
||||
}}
|
||||
>
|
||||
Servicio {sortField === 'servicio' && (sortOrder === 'asc' ? '▲' : '▼')}
|
||||
<div className="flex items-center gap-1">
|
||||
Servicio {sortField === 'servicio' && (sortOrder === 'asc' ? '▲' : '▼')}
|
||||
</div>
|
||||
</th>
|
||||
<th className="px-2 py-2 text-center font-bold text-blue-700 uppercase tracking-wider border-b border-gray-200 whitespace-nowrap">
|
||||
<th className="px-4 py-4 text-center text-xs font-bold text-gray-600 uppercase tracking-wider rounded-tr-2xl">
|
||||
Acciones
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-100" style={{ position: 'relative', minHeight: 'calc(12 * 40px)' }}>
|
||||
{procesos.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={8} className="text-center py-8 text-gray-500">No hay registros</td>
|
||||
</tr>
|
||||
) : (
|
||||
procesos.map((proc) => (
|
||||
<tr key={proc.id} className="transition-all duration-200 hover:bg-blue-100 hover:shadow-lg">
|
||||
<td className="px-2 py-2 text-center align-middle whitespace-nowrap">{proc.id}</td>
|
||||
<td className="px-2 py-2 whitespace-nowrap align-middle">{proc.organizacion_name || '-'}</td>
|
||||
<td className="px-2 py-2 whitespace-nowrap align-middle">{
|
||||
proc.estado === 1 ? 'En Espera'
|
||||
: proc.estado === 2 ? 'Procesando'
|
||||
: proc.estado === 3 ? 'Finalizado'
|
||||
: proc.estado === 4 ? 'Error'
|
||||
: String(proc.estado)
|
||||
}</td>
|
||||
<td className="px-2 py-2 whitespace-nowrap align-middle">{
|
||||
typeof proc.pedimento === 'object' && proc.pedimento !== null
|
||||
? proc.pedimento.pedimento || JSON.stringify(proc.pedimento)
|
||||
: proc.pedimento
|
||||
}</td>
|
||||
<td className="px-2 py-2 whitespace-nowrap align-middle">{
|
||||
proc.servicio === 1 ? 'Estado de pedimento'
|
||||
: proc.servicio === 2 ? 'Listado de pedimentos'
|
||||
: proc.servicio === 3 ? 'Pedimento Completo'
|
||||
: proc.servicio === 4 ? 'Pedimento Partidas'
|
||||
: proc.servicio === 5 ? 'Pedimento Remesas'
|
||||
: proc.servicio === 6 ? 'Acuse'
|
||||
: proc.servicio === 7 ? 'EDocument'
|
||||
: proc.servicio === 8 ? 'Cove'
|
||||
: proc.servicio === 9 ? 'Acuse Cove'
|
||||
: String(proc.servicio)
|
||||
}</td>
|
||||
<td className="px-2 py-2 text-center align-middle whitespace-nowrap">
|
||||
<div className="relative inline-block text-left" id={`dropdown-acciones-${proc.id}`}>
|
||||
<button
|
||||
className="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-3 py-1 bg-white text-xs font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all duration-200 transform hover:scale-105"
|
||||
type="button"
|
||||
onClick={() => setOpenDropdownId(openDropdownId === proc.id ? null : proc.id)}
|
||||
>
|
||||
Acciones
|
||||
<svg className="ml-1 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" /></svg>
|
||||
</button>
|
||||
{openDropdownId === proc.id && (
|
||||
<div className="absolute right-0 mt-2 w-32 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-20">
|
||||
<div className="py-1">
|
||||
<button
|
||||
className="block w-full text-left px-4 py-2 text-xs text-blue-700 hover:bg-blue-100 disabled:opacity-60"
|
||||
onClick={() => handleEjecutarServicio(proc)}
|
||||
disabled={
|
||||
executingId === proc.id ||
|
||||
proc.estado === 2 || // Procesando
|
||||
proc.estado === 3 || // Finalizado
|
||||
proc.estado === 4 // Error
|
||||
}
|
||||
>
|
||||
{executingId === proc.id ? 'Ejecutando...' : 'Ejecutar Servicio'}
|
||||
</button>
|
||||
<button
|
||||
className={`block w-full text-left px-4 py-2 text-xs text-gray-700 hover:bg-blue-100${(proc.estado === 2 || proc.estado === 4) ? '' : ' opacity-50 cursor-not-allowed'}`}
|
||||
disabled={!(proc.estado === 2 || proc.estado === 4)}
|
||||
>
|
||||
Pasar a espera
|
||||
</button>
|
||||
<button className="block w-full text-left px-4 py-2 text-xs text-gray-700 hover:bg-blue-100">Editar</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<tbody className="bg-white divide-y divide-gray-100">
|
||||
{procesos.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={6} className="text-center py-12">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="bg-gray-100 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<svg className="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<p className="text-gray-500 font-medium">No hay procesos disponibles</p>
|
||||
<p className="text-gray-400 text-sm mt-1">Intenta ajustar los filtros de búsqueda</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
) : (
|
||||
procesos.map((proc) => (
|
||||
<tr key={proc.id} className="transition-all duration-200 hover:bg-gradient-to-r hover:from-blue-50 hover:to-indigo-50 hover:shadow-lg">
|
||||
<td className="px-4 py-4 text-center align-middle whitespace-nowrap">
|
||||
<span className="bg-gray-100 text-gray-800 px-2 py-1 rounded-lg text-sm font-semibold">{proc.id}</span>
|
||||
</td>
|
||||
<td className="px-4 py-4 whitespace-nowrap align-middle text-sm font-medium text-gray-900">{proc.organizacion_name || '-'}</td>
|
||||
<td className="px-4 py-4 whitespace-nowrap align-middle">
|
||||
{(() => {
|
||||
const estado = proc.estado === 1 ? { text: 'En Espera', color: 'bg-yellow-100 text-yellow-800 border-yellow-200' }
|
||||
: proc.estado === 2 ? { text: 'Procesando', color: 'bg-blue-100 text-blue-800 border-blue-200' }
|
||||
: proc.estado === 3 ? { text: 'Finalizado', color: 'bg-green-100 text-green-800 border-green-200' }
|
||||
: proc.estado === 4 ? { text: 'Error', color: 'bg-red-100 text-red-800 border-red-200' }
|
||||
: { text: String(proc.estado), color: 'bg-gray-100 text-gray-800 border-gray-200' };
|
||||
return (
|
||||
<span className={`inline-flex items-center px-2.5 py-1 rounded-lg text-xs font-semibold border ${estado.color}`}>
|
||||
{estado.text}
|
||||
</span>
|
||||
);
|
||||
})()}
|
||||
</td>
|
||||
<td className="px-4 py-4 whitespace-nowrap align-middle text-sm text-gray-900 font-mono">
|
||||
{typeof proc.pedimento === 'object' && proc.pedimento !== null
|
||||
? proc.pedimento.pedimento || JSON.stringify(proc.pedimento)
|
||||
: proc.pedimento}
|
||||
</td>
|
||||
<td className="px-4 py-4 whitespace-nowrap align-middle text-sm text-gray-700">
|
||||
{proc.servicio === 1 ? 'Estado de pedimento'
|
||||
: proc.servicio === 2 ? 'Listado de pedimentos'
|
||||
: proc.servicio === 3 ? 'Pedimento Completo'
|
||||
: proc.servicio === 4 ? 'Pedimento Partidas'
|
||||
: proc.servicio === 5 ? 'Pedimento Remesas'
|
||||
: proc.servicio === 6 ? 'Acuse'
|
||||
: proc.servicio === 7 ? 'EDocument'
|
||||
: proc.servicio === 8 ? 'Cove'
|
||||
: proc.servicio === 9 ? 'Acuse Cove'
|
||||
: String(proc.servicio)}
|
||||
</td>
|
||||
<td className="px-4 py-4 text-center align-middle whitespace-nowrap">
|
||||
<div className="relative inline-block text-left z-30" id={`dropdown-acciones-${proc.id}`}>
|
||||
<button
|
||||
className="inline-flex justify-center items-center rounded-xl border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all duration-200 transform hover:scale-105 active:bg-gray-100"
|
||||
type="button"
|
||||
onClick={() => setOpenDropdownId(openDropdownId === proc.id ? null : proc.id)}
|
||||
>
|
||||
Acciones
|
||||
<svg className="ml-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
{openDropdownId === proc.id && (
|
||||
<div className="absolute right-0 mt-2 w-48 rounded-xl shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-[9999] border border-gray-200">
|
||||
<div className="py-2">
|
||||
<button
|
||||
className="flex items-center w-full text-left px-4 py-3 text-sm text-blue-700 hover:bg-blue-50 disabled:opacity-60 disabled:cursor-not-allowed transition-colors duration-200"
|
||||
onClick={() => {
|
||||
handleEjecutarServicio(proc);
|
||||
setOpenDropdownId(null); // Cerrar dropdown después de ejecutar
|
||||
}}
|
||||
disabled={
|
||||
executingId === proc.id ||
|
||||
proc.estado === 2 || // Procesando
|
||||
proc.estado === 3 || // Finalizado
|
||||
proc.estado === 4 // Error
|
||||
}
|
||||
>
|
||||
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h1m4 0h1m-6 4h8m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
{executingId === proc.id ? 'Ejecutando...' : 'Ejecutar Servicio'}
|
||||
</button>
|
||||
<button
|
||||
className={`flex items-center w-full text-left px-4 py-3 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-200 ${(proc.estado === 2 || proc.estado === 4) ? '' : ' opacity-50 cursor-not-allowed'}`}
|
||||
disabled={!(proc.estado === 2 || proc.estado === 4)}
|
||||
onClick={() => {
|
||||
setOpenDropdownId(null); // Cerrar dropdown
|
||||
// Aquí iría la lógica para pasar a espera
|
||||
}}
|
||||
>
|
||||
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
Pasar a espera
|
||||
</button>
|
||||
<button
|
||||
className="flex items-center w-full text-left px-4 py-3 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-200"
|
||||
onClick={() => {
|
||||
setOpenDropdownId(null); // Cerrar dropdown
|
||||
// Aquí iría la lógica para editar
|
||||
}}
|
||||
>
|
||||
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||
</svg>
|
||||
Editar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/* Paginación igual a Documents.jsx */}
|
||||
|
||||
{/* Vista de tarjetas para pantallas pequeñas y medianas */}
|
||||
<div className="lg:hidden space-y-4">
|
||||
{procesos.length === 0 ? (
|
||||
<div className="bg-gray-50 rounded-2xl p-8 text-center">
|
||||
<div className="bg-gray-100 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<svg className="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<p className="text-gray-500 font-medium">No hay procesos disponibles</p>
|
||||
<p className="text-gray-400 text-sm mt-1">Intenta ajustar los filtros de búsqueda</p>
|
||||
</div>
|
||||
) : (
|
||||
procesos.map((proc) => (
|
||||
<div key={proc.id} className={`bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-300 relative ${openDropdownId === proc.id ? 'z-[100]' : ''}`}>
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="bg-blue-100 rounded-xl p-2 flex-shrink-0">
|
||||
<svg className="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<h3 className="text-lg font-semibold text-gray-900">Proceso #{proc.id}</h3>
|
||||
<p className="text-sm text-gray-500">{proc.organizacion_name || 'Sin organización'}</p>
|
||||
</div>
|
||||
</div>
|
||||
{(() => {
|
||||
const estado = proc.estado === 1 ? { text: 'En Espera', color: 'bg-yellow-100 text-yellow-800 border-yellow-200' }
|
||||
: proc.estado === 2 ? { text: 'Procesando', color: 'bg-blue-100 text-blue-800 border-blue-200' }
|
||||
: proc.estado === 3 ? { text: 'Finalizado', color: 'bg-green-100 text-green-800 border-green-200' }
|
||||
: proc.estado === 4 ? { text: 'Error', color: 'bg-red-100 text-red-800 border-red-200' }
|
||||
: { text: String(proc.estado), color: 'bg-gray-100 text-gray-800 border-gray-200' };
|
||||
return (
|
||||
<span className={`inline-flex items-center px-2.5 py-1 rounded-lg text-xs font-semibold border ${estado.color}`}>
|
||||
{estado.text}
|
||||
</span>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 mb-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-gray-600">Pedimento:</span>
|
||||
<span className="text-sm font-mono text-gray-900 bg-gray-100 px-2 py-1 rounded">
|
||||
{typeof proc.pedimento === 'object' && proc.pedimento !== null
|
||||
? proc.pedimento.pedimento || JSON.stringify(proc.pedimento)
|
||||
: proc.pedimento}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-gray-600">Servicio:</span>
|
||||
<span className="text-sm text-gray-900 text-right max-w-[60%]">
|
||||
{proc.servicio === 1 ? 'Estado de pedimento'
|
||||
: proc.servicio === 2 ? 'Listado de pedimentos'
|
||||
: proc.servicio === 3 ? 'Pedimento Completo'
|
||||
: proc.servicio === 4 ? 'Pedimento Partidas'
|
||||
: proc.servicio === 5 ? 'Pedimento Remesas'
|
||||
: proc.servicio === 6 ? 'Acuse'
|
||||
: proc.servicio === 7 ? 'EDocument'
|
||||
: proc.servicio === 8 ? 'Cove'
|
||||
: proc.servicio === 9 ? 'Acuse Cove'
|
||||
: String(proc.servicio)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-30" id={`dropdown-acciones-${proc.id}`}>
|
||||
<button
|
||||
className="w-full inline-flex justify-center items-center rounded-xl border border-gray-300 shadow-sm px-4 py-3 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all duration-200 active:bg-gray-100"
|
||||
type="button"
|
||||
onClick={() => setOpenDropdownId(openDropdownId === proc.id ? null : proc.id)}
|
||||
>
|
||||
Acciones
|
||||
<svg className="ml-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
{openDropdownId === proc.id && (
|
||||
<div className="absolute left-0 right-0 mt-2 rounded-xl shadow-2xl bg-white ring-2 ring-gray-300 z-[9999] border border-gray-200 overflow-hidden"
|
||||
style={{ boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)' }}>
|
||||
<div className="py-1">
|
||||
<button
|
||||
className="flex items-center w-full text-left px-4 py-4 text-sm text-blue-700 hover:bg-blue-50 disabled:opacity-60 disabled:cursor-not-allowed transition-colors duration-200 border-b border-gray-100"
|
||||
onClick={() => {
|
||||
handleEjecutarServicio(proc);
|
||||
setOpenDropdownId(null); // Cerrar dropdown después de ejecutar
|
||||
}}
|
||||
disabled={
|
||||
executingId === proc.id ||
|
||||
proc.estado === 2 || // Procesando
|
||||
proc.estado === 3 || // Finalizado
|
||||
proc.estado === 4 // Error
|
||||
}
|
||||
>
|
||||
<svg className="w-4 h-4 mr-3 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h1m4 0h1m-6 4h8m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
<span className="font-medium">
|
||||
{executingId === proc.id ? 'Ejecutando...' : 'Ejecutar Servicio'}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={`flex items-center w-full text-left px-4 py-4 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-200 border-b border-gray-100 ${(proc.estado === 2 || proc.estado === 4) ? '' : ' opacity-50 cursor-not-allowed'}`}
|
||||
disabled={!(proc.estado === 2 || proc.estado === 4)}
|
||||
onClick={() => {
|
||||
setOpenDropdownId(null); // Cerrar dropdown
|
||||
// Aquí iría la lógica para pasar a espera
|
||||
}}
|
||||
>
|
||||
<svg className="w-4 h-4 mr-3 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span className="font-medium">Pasar a espera</span>
|
||||
</button>
|
||||
<button
|
||||
className="flex items-center w-full text-left px-4 py-4 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-200"
|
||||
onClick={() => {
|
||||
setOpenDropdownId(null); // Cerrar dropdown
|
||||
// Aquí iría la lógica para editar
|
||||
}}
|
||||
>
|
||||
<svg className="w-4 h-4 mr-3 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||
</svg>
|
||||
<span className="font-medium">Editar</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Paginación compartida mejorada */}
|
||||
{count > 0 && (
|
||||
<div className="bg-white px-4 py-3 flex flex-col sm:flex-row items-center justify-between border-t border-gray-200">
|
||||
<div className="bg-gradient-to-r from-gray-50 to-slate-50 px-4 sm:px-6 py-4 mt-6 rounded-2xl border border-gray-200 flex flex-col sm:flex-row items-center justify-between gap-4">
|
||||
{(() => {
|
||||
const totalPages = Math.max(1, Math.ceil(count / itemsPerPage));
|
||||
const maxPagesToShow = 5;
|
||||
@@ -385,26 +657,26 @@ export default function Procesos() {
|
||||
pageNumbers.push(i);
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-col sm:flex-row sm:items-center w-full gap-2 sm:gap-4 mt-2 sm:mt-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<label htmlFor="itemsPerPage" className="text-xs text-gray-600 font-medium">Registros por página:</label>
|
||||
<>
|
||||
<div className="flex items-center gap-3">
|
||||
<label htmlFor="itemsPerPage" className="text-sm text-gray-600 font-medium">Registros por página:</label>
|
||||
<select
|
||||
id="itemsPerPage"
|
||||
value={itemsPerPage}
|
||||
onChange={e => { setItemsPerPage(Number(e.target.value)); setPage(1); }}
|
||||
className="border border-gray-300 rounded px-2 py-1 text-xs focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white"
|
||||
className="border border-gray-300 rounded-xl px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white shadow-sm"
|
||||
>
|
||||
{[5, 8, 12, 20, 50, 100].map(size => (
|
||||
<option key={size} value={size}>{size}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 flex-wrap">
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={e => { e.preventDefault(); setPage(1); }}
|
||||
disabled={page === 1}
|
||||
className={`px-2 py-1 rounded border text-xs font-semibold transition-colors duration-150 ${page === 1 ? 'bg-gray-100 text-gray-400 border-gray-200 cursor-not-allowed' : 'bg-white text-blue-700 border-blue-200 hover:bg-blue-50 hover:text-blue-900'}`}
|
||||
className={`px-3 py-2 rounded-xl border text-sm font-semibold transition-all duration-200 ${page === 1 ? 'bg-gray-100 text-gray-400 border-gray-200 cursor-not-allowed' : 'bg-white text-blue-700 border-blue-200 hover:bg-blue-50 hover:text-blue-900 hover:shadow-md'}`}
|
||||
>
|
||||
«
|
||||
</button>
|
||||
@@ -412,7 +684,7 @@ export default function Procesos() {
|
||||
type="button"
|
||||
onClick={e => { e.preventDefault(); setPage(p => Math.max(1, p - 1)); }}
|
||||
disabled={page === 1}
|
||||
className={`px-2 py-1 rounded border text-xs font-semibold transition-colors duration-150 ${page === 1 ? 'bg-gray-100 text-gray-400 border-gray-200 cursor-not-allowed' : 'bg-white text-blue-700 border-blue-200 hover:bg-blue-50 hover:text-blue-900'}`}
|
||||
className={`px-3 py-2 rounded-xl border text-sm font-semibold transition-all duration-200 ${page === 1 ? 'bg-gray-100 text-gray-400 border-gray-200 cursor-not-allowed' : 'bg-white text-blue-700 border-blue-200 hover:bg-blue-50 hover:text-blue-900 hover:shadow-md'}`}
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
@@ -421,7 +693,7 @@ export default function Procesos() {
|
||||
type="button"
|
||||
key={num}
|
||||
onClick={e => { e.preventDefault(); setPage(num); }}
|
||||
className={`px-2 py-1 rounded border text-xs font-semibold transition-colors duration-150 ${num === page ? 'bg-blue-600 text-white border-blue-700 cursor-default' : 'bg-white text-blue-700 border-blue-200 hover:bg-blue-50 hover:text-blue-900'}`}
|
||||
className={`px-3 py-2 rounded-xl border text-sm font-semibold transition-all duration-200 ${num === page ? 'bg-blue-600 text-white border-blue-700 cursor-default shadow-lg' : 'bg-white text-blue-700 border-blue-200 hover:bg-blue-50 hover:text-blue-900 hover:shadow-md'}`}
|
||||
disabled={num === page}
|
||||
>
|
||||
{num}
|
||||
@@ -431,7 +703,7 @@ export default function Procesos() {
|
||||
type="button"
|
||||
onClick={e => { e.preventDefault(); setPage(p => p + 1); }}
|
||||
disabled={page >= totalPages}
|
||||
className={`px-2 py-1 rounded border text-xs font-semibold transition-colors duration-150 ${(page >= totalPages) ? 'bg-gray-100 text-gray-400 border-gray-200 cursor-not-allowed' : 'bg-white text-blue-700 border-blue-200 hover:bg-blue-50 hover:text-blue-900'}`}
|
||||
className={`px-3 py-2 rounded-xl border text-sm font-semibold transition-all duration-200 ${(page >= totalPages) ? 'bg-gray-100 text-gray-400 border-gray-200 cursor-not-allowed' : 'bg-white text-blue-700 border-blue-200 hover:bg-blue-50 hover:text-blue-900 hover:shadow-md'}`}
|
||||
>
|
||||
›
|
||||
</button>
|
||||
@@ -439,18 +711,20 @@ export default function Procesos() {
|
||||
type="button"
|
||||
onClick={e => { e.preventDefault(); setPage(totalPages); }}
|
||||
disabled={page >= totalPages}
|
||||
className={`px-2 py-1 rounded border text-xs font-semibold transition-colors duration-150 ${(page >= totalPages) ? 'bg-gray-100 text-gray-400 border-gray-200 cursor-not-allowed' : 'bg-white text-blue-700 border-blue-200 hover:bg-blue-50 hover:text-blue-900'}`}
|
||||
className={`px-3 py-2 rounded-xl border text-sm font-semibold transition-all duration-200 ${(page >= totalPages) ? 'bg-gray-100 text-gray-400 border-gray-200 cursor-not-allowed' : 'bg-white text-blue-700 border-blue-200 hover:bg-blue-50 hover:text-blue-900 hover:shadow-md'}`}
|
||||
>
|
||||
»
|
||||
</button>
|
||||
<span className="ml-3 text-xs text-gray-500">Página <span className="font-bold">{page}</span> de <span className="font-bold">{totalPages}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-sm text-gray-600">
|
||||
Página <span className="font-bold text-gray-800">{page}</span> de <span className="font-bold text-gray-800">{totalPages}</span>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user