319 lines
19 KiB
JavaScript
319 lines
19 KiB
JavaScript
import React, { useState } from "react";
|
|
|
|
// Animación fade-in/slide-up para bloques
|
|
const fadeInSlideUp = `@keyframes fadein-slideup {
|
|
0% { opacity: 0; transform: translateY(40px); }
|
|
100% { opacity: 1; transform: translateY(0); }
|
|
}`;
|
|
if (typeof document !== 'undefined' && !document.getElementById('fadein-slideup-reportes')) {
|
|
const style = document.createElement('style');
|
|
style.id = 'fadein-slideup-reportes';
|
|
style.innerHTML = fadeInSlideUp;
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
export default function Reportes() {
|
|
const [tipoReporte, setTipoReporte] = useState('');
|
|
const [fechaInicio, setFechaInicio] = useState('');
|
|
const [fechaFin, setFechaFin] = useState('');
|
|
|
|
// Datos de ejemplo
|
|
const reportes = [
|
|
{ id: 1, nombre: 'Reporte de usuarios activos', tipo: 'Usuarios', fecha: '2025-08-07', estado: 'Completado' },
|
|
{ id: 2, nombre: 'Análisis de documentos procesados', tipo: 'Documentos', fecha: '2025-08-06', estado: 'Procesando' },
|
|
{ id: 3, nombre: 'Resumen de procesos aduaneros', tipo: 'Procesos', fecha: '2025-08-05', estado: 'Completado' },
|
|
{ id: 4, nombre: 'Estadísticas generales del sistema', tipo: 'General', fecha: '2025-08-04', estado: 'Completado' },
|
|
{ id: 5, nombre: 'Reporte de expedientes', tipo: 'Documentos', fecha: '2025-08-03', estado: 'Error' },
|
|
];
|
|
|
|
const getEstadoBadge = (estado) => {
|
|
const styles = {
|
|
'Completado': 'bg-emerald-100 text-emerald-800 border-emerald-200',
|
|
'Procesando': 'bg-yellow-100 text-yellow-800 border-yellow-200',
|
|
'Error': 'bg-red-100 text-red-800 border-red-200'
|
|
};
|
|
return styles[estado] || 'bg-gray-100 text-gray-800 border-gray-200';
|
|
};
|
|
|
|
const limpiarFiltros = () => {
|
|
setTipoReporte('');
|
|
setFechaInicio('');
|
|
setFechaFin('');
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-blue-50 p-4 lg:p-6">
|
|
<div className="max-w-7xl mx-auto">
|
|
{/* Header principal */}
|
|
<div className="mb-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="bg-white/80 backdrop-blur-xl rounded-2xl shadow-xl border border-blue-100/50 p-6 lg:p-8">
|
|
<div className="flex items-start space-x-4">
|
|
<div className="p-3 bg-gradient-to-br from-blue-500 to-blue-700 rounded-xl shadow-lg flex-shrink-0">
|
|
<svg className="w-8 h-8 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>
|
|
<div>
|
|
<h1 className="text-2xl lg:text-3xl font-bold text-gray-900 mb-1">
|
|
Centro de Reportes
|
|
</h1>
|
|
<p className="text-gray-600">
|
|
Consulta, genera y descarga reportes relacionados con el sistema aduanero
|
|
</p>
|
|
<div className="mt-3">
|
|
<span className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800">
|
|
📊 {reportes.length} reportes disponibles
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Panel de filtros mejorado */}
|
|
<div className="mb-6 animate-fadein-slideup opacity-0" style={{ animation: 'fadein-slideup 0.7s cubic-bezier(0.22,1,0.36,1) 0.15s forwards' }}>
|
|
<div className="bg-white/80 backdrop-blur-xl rounded-2xl shadow-xl border border-blue-100/50">
|
|
<div className="px-6 py-4 border-b border-gray-200/50">
|
|
<div className="flex items-center gap-3">
|
|
<div className="p-2 bg-gradient-to-br from-blue-500 to-blue-700 rounded-xl">
|
|
<svg className="w-5 h-5 text-white" 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.293A1 1 0 013 6.586V4z" />
|
|
</svg>
|
|
</div>
|
|
<h2 className="text-xl font-bold text-gray-900">Filtros de búsqueda</h2>
|
|
</div>
|
|
</div>
|
|
<div className="p-6">
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 gap-4">
|
|
{/* Tipo de reporte */}
|
|
<div>
|
|
<label className="block text-xs font-semibold text-gray-700 mb-1">Tipo de reporte</label>
|
|
<select
|
|
value={tipoReporte}
|
|
onChange={(e) => setTipoReporte(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-white"
|
|
>
|
|
<option value="">Todos los tipos</option>
|
|
<option value="General">General</option>
|
|
<option value="Usuarios">Usuarios</option>
|
|
<option value="Documentos">Documentos</option>
|
|
<option value="Procesos">Procesos</option>
|
|
</select>
|
|
</div>
|
|
|
|
{/* Fecha inicio */}
|
|
<div>
|
|
<label className="block text-xs font-semibold text-gray-700 mb-1">Fecha inicio</label>
|
|
<input
|
|
type="date"
|
|
value={fechaInicio}
|
|
onChange={(e) => setFechaInicio(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-white"
|
|
/>
|
|
</div>
|
|
|
|
{/* Fecha fin */}
|
|
<div>
|
|
<label className="block text-xs font-semibold text-gray-700 mb-1">Fecha fin</label>
|
|
<input
|
|
type="date"
|
|
value={fechaFin}
|
|
onChange={(e) => setFechaFin(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-white"
|
|
/>
|
|
</div>
|
|
|
|
{/* Botón filtrar */}
|
|
<div className="flex items-end">
|
|
<button className="w-full inline-flex items-center justify-center px-4 py-2 bg-gradient-to-r from-blue-500 to-blue-700 hover:from-blue-600 hover:to-blue-800 text-white text-sm font-medium rounded-lg transition-all duration-200 transform hover:scale-105 shadow-lg">
|
|
<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="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
</svg>
|
|
Filtrar
|
|
</button>
|
|
</div>
|
|
|
|
{/* Botón limpiar */}
|
|
<div className="flex items-end">
|
|
<button
|
|
onClick={limpiarFiltros}
|
|
className="w-full px-4 py-2 text-sm font-medium text-gray-600 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors"
|
|
>
|
|
Limpiar filtros
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Sección de resultados */}
|
|
<div className="bg-white/80 backdrop-blur-xl shadow-xl rounded-2xl border border-blue-100/50 animate-fadein-slideup opacity-0"
|
|
style={{ animation: 'fadein-slideup 0.7s cubic-bezier(0.22,1,0.36,1) 0.25s forwards' }}>
|
|
|
|
{/* Header de la sección */}
|
|
<div className="px-6 py-4 border-b border-gray-200/50">
|
|
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">
|
|
<div>
|
|
<div className="flex items-center gap-3 mb-2">
|
|
<div className="p-2 bg-gradient-to-br from-blue-500 to-blue-700 rounded-xl">
|
|
<svg className="w-5 h-5 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>
|
|
<h2 className="text-xl font-bold text-gray-900">Reportes disponibles</h2>
|
|
</div>
|
|
<span className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800">
|
|
📋 {reportes.length} reportes encontrados
|
|
</span>
|
|
</div>
|
|
|
|
{/* Botón descargar masivo */}
|
|
<div className="flex flex-col sm:flex-row gap-2">
|
|
<button className="inline-flex items-center justify-center px-4 py-2 bg-gradient-to-r from-green-500 to-green-700 hover:from-green-600 hover:to-green-800 text-white text-sm font-medium rounded-xl transition-all duration-200 transform hover:scale-105 shadow-lg">
|
|
<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 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 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>
|
|
Descargar Excel
|
|
</button>
|
|
|
|
<button className="inline-flex items-center justify-center px-4 py-2 bg-gradient-to-r from-purple-500 to-purple-700 hover:from-purple-600 hover:to-purple-800 text-white text-sm font-medium rounded-xl transition-all duration-200 transform hover:scale-105 shadow-lg">
|
|
<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 4v16m8-8H4" />
|
|
</svg>
|
|
Nuevo Reporte
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Contenido de reportes */}
|
|
<div className="p-6">
|
|
{/* Vista Desktop - Tabla */}
|
|
<div className="hidden lg:block">
|
|
<div className="overflow-x-auto">
|
|
<table className="min-w-full divide-y divide-gray-200">
|
|
<thead className="bg-gradient-to-r from-blue-500 to-blue-700">
|
|
<tr>
|
|
<th className="px-4 py-3 text-left text-xs font-bold text-white uppercase tracking-wider">ID</th>
|
|
<th className="px-4 py-3 text-left text-xs font-bold text-white uppercase tracking-wider">Nombre del Reporte</th>
|
|
<th className="px-4 py-3 text-left text-xs font-bold text-white uppercase tracking-wider">Tipo</th>
|
|
<th className="px-4 py-3 text-left text-xs font-bold text-white uppercase tracking-wider">Fecha</th>
|
|
<th className="px-4 py-3 text-left text-xs font-bold text-white uppercase tracking-wider">Estado</th>
|
|
<th className="px-4 py-3 text-center text-xs font-bold text-white uppercase tracking-wider">Acciones</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="bg-white/50 divide-y divide-gray-100">
|
|
{reportes.map((reporte, index) => (
|
|
<tr key={reporte.id} className="hover:bg-blue-50 transition-all duration-200">
|
|
<td className="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">
|
|
#{reporte.id.toString().padStart(3, '0')}
|
|
</td>
|
|
<td className="px-4 py-3 whitespace-nowrap">
|
|
<div className="text-sm font-medium text-gray-900">{reporte.nombre}</div>
|
|
</td>
|
|
<td className="px-4 py-3 whitespace-nowrap">
|
|
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800">
|
|
{reporte.tipo}
|
|
</span>
|
|
</td>
|
|
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-700">
|
|
{new Date(reporte.fecha).toLocaleDateString('es-ES', {
|
|
year: 'numeric',
|
|
month: 'short',
|
|
day: 'numeric'
|
|
})}
|
|
</td>
|
|
<td className="px-4 py-3 whitespace-nowrap">
|
|
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium border ${getEstadoBadge(reporte.estado)}`}>
|
|
{reporte.estado}
|
|
</span>
|
|
</td>
|
|
<td className="px-4 py-3 whitespace-nowrap text-center">
|
|
<div className="flex justify-center space-x-2">
|
|
<button className="inline-flex items-center p-2 border border-gray-300 shadow-sm text-sm font-medium rounded-lg text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-200"
|
|
title="Ver reporte">
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.477 0 8.268 2.943 9.542 7-1.274 4.057-5.065 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
|
</svg>
|
|
</button>
|
|
<button className="inline-flex items-center p-2 border border-blue-300 shadow-sm text-sm font-medium rounded-lg text-blue-700 bg-white hover:bg-blue-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-200"
|
|
title="Descargar">
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 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>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Vista Mobile - Cards */}
|
|
<div className="lg:hidden space-y-4">
|
|
{reportes.map((reporte, index) => (
|
|
<div
|
|
key={reporte.id}
|
|
className="bg-white/80 backdrop-blur-xl rounded-xl shadow-lg border border-gray-200/50 p-4 transition-all duration-200 hover:shadow-xl transform hover:scale-[1.02]"
|
|
style={{ animationDelay: `${index * 100}ms` }}
|
|
>
|
|
<div className="flex items-start justify-between mb-3">
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<span className="text-xs font-medium text-gray-500">#{reporte.id.toString().padStart(3, '0')}</span>
|
|
<span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium border ${getEstadoBadge(reporte.estado)}`}>
|
|
{reporte.estado}
|
|
</span>
|
|
</div>
|
|
<h3 className="text-sm font-semibold text-gray-900 mb-1">
|
|
{reporte.nombre}
|
|
</h3>
|
|
</div>
|
|
<div className="flex space-x-1 ml-2">
|
|
<button className="p-2 text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-colors"
|
|
title="Ver reporte">
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.477 0 8.268 2.943 9.542 7-1.274 4.057-5.065 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
|
</svg>
|
|
</button>
|
|
<button className="p-2 text-blue-600 hover:text-blue-800 hover:bg-blue-50 rounded-lg transition-colors"
|
|
title="Descargar">
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 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>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<div className="flex items-center justify-between text-xs text-gray-600">
|
|
<span className="inline-flex items-center px-2 py-1 rounded-full bg-gray-100 text-gray-800 font-medium">
|
|
{reporte.tipo}
|
|
</span>
|
|
<span className="flex items-center">
|
|
<svg className="w-3 h-3 mr-1" 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>
|
|
{new Date(reporte.fecha).toLocaleDateString('es-ES', {
|
|
year: 'numeric',
|
|
month: 'short',
|
|
day: 'numeric'
|
|
})}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|