Se agregaron fecha de pago en PedimentoDetail y expedientes

This commit is contained in:
2025-08-07 22:08:41 -06:00
parent 9fc592b127
commit be970b5a89
10 changed files with 1170 additions and 869 deletions

4
.env
View File

@@ -1,4 +1,4 @@
VITE_DEBUG_MODE=true VITE_DEBUG_MODE=true
VITE_EFC_API_URL=https://api.efc-aduanasoft.com/api/v1 VITE_EFC_API_URL=http://192.168.1.195:8000/api/v1
VITE_EFC_MICROSERVICE_URL=https://api.efc-aduanasoft.com/microservice/api/v1 VITE_EFC_MICROSERVICE_URL=http://192.168.1.195:8001/api/v1

0
.env.example Normal file
View File

0
NOTIFICATIONS_README.md Normal file
View File

View File

View File

View File

View File

@@ -454,7 +454,7 @@ export default function Documents() {
{ped.pedimento} {ped.pedimento}
</Link> </Link>
</td> </td>
<td className="px-4 py-4 whitespace-nowrap text-sm text-gray-700">{ped.fechapago}</td> <td className="px-4 py-4 whitespace-nowrap text-sm text-gray-700">{ped.fecha_pago}</td>
<td className="px-4 py-4 whitespace-nowrap text-sm text-gray-700 max-w-xs truncate" title={ped.contribuyente}>{ped.contribuyente}</td> <td className="px-4 py-4 whitespace-nowrap text-sm text-gray-700 max-w-xs truncate" title={ped.contribuyente}>{ped.contribuyente}</td>
<td className="px-4 py-4 whitespace-nowrap text-sm text-gray-700">{ped.curp_apoderado}</td> <td className="px-4 py-4 whitespace-nowrap text-sm text-gray-700">{ped.curp_apoderado}</td>
<td className="px-4 py-4 whitespace-nowrap text-sm text-gray-900 font-semibold">${ped.importe_total}</td> <td className="px-4 py-4 whitespace-nowrap text-sm text-gray-900 font-semibold">${ped.importe_total}</td>

File diff suppressed because it is too large Load Diff

View File

@@ -1,77 +1,315 @@
import React from "react"; 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() { 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 ( return (
<div className="p-6 bg-gradient-to-br from-blue-50 to-blue-100 min-h-screen"> <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-5xl mx-auto"> <div className="max-w-7xl mx-auto">
<h1 className="text-3xl font-extrabold mb-2 text-blue-900 tracking-tight">Reportes</h1> {/* Header principal */}
<p className="mb-6 text-gray-600">Consulta y descarga reportes relacionados con el sistema.</p> <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' }}>
{/* Filtros de ejemplo */} <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 flex-wrap gap-4 mb-6 bg-white p-4 rounded-lg shadow-sm"> <div className="flex items-start space-x-4">
<div> <div className="p-3 bg-gradient-to-br from-blue-500 to-blue-700 rounded-xl shadow-lg flex-shrink-0">
<label className="block text-xs font-semibold text-gray-500 mb-1">Tipo de reporte</label> <svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<select className="border rounded px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-300"> <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" />
<option>General</option> </svg>
<option>Usuarios</option> </div>
<option>Documentos</option> <div>
<option>Procesos</option> <h1 className="text-2xl lg:text-3xl font-bold text-gray-900 mb-1">
</select> Centro de Reportes
</div> </h1>
<div> <p className="text-gray-600">
<label className="block text-xs font-semibold text-gray-500 mb-1">Fecha inicio</label> Consulta, genera y descarga reportes relacionados con el sistema aduanero
<input type="date" className="border rounded px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-300" /> </p>
</div> <div className="mt-3">
<div> <span className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800">
<label className="block text-xs font-semibold text-gray-500 mb-1">Fecha fin</label> 📊 {reportes.length} reportes disponibles
<input type="date" className="border rounded px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-300" /> </span>
</div> </div>
<div className="flex items-end"> </div>
<button className="bg-blue-600 text-white px-4 py-2 rounded shadow hover:bg-blue-700 transition">Filtrar</button> </div>
</div> </div>
</div> </div>
{/* Tabla de reportes de ejemplo */} {/* Panel de filtros mejorado */}
<div className="bg-white rounded-lg shadow p-6"> <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="flex justify-between items-center mb-4"> <div className="bg-white/80 backdrop-blur-xl rounded-2xl shadow-xl border border-blue-100/50">
<h2 className="text-lg font-bold text-blue-800">Resultados</h2> <div className="px-6 py-4 border-b border-gray-200/50">
<button className="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600 transition flex items-center gap-2"> <div className="flex items-center gap-3">
<svg className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" d="M12 4v16m8-8H4" /></svg> <div className="p-2 bg-gradient-to-br from-blue-500 to-blue-700 rounded-xl">
Descargar Excel <svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
</button> <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>
<div className="overflow-x-auto"> </div>
<table className="min-w-full text-sm text-left">
<thead> {/* Sección de resultados */}
<tr className="bg-blue-100 text-blue-900"> <div className="bg-white/80 backdrop-blur-xl shadow-xl rounded-2xl border border-blue-100/50 animate-fadein-slideup opacity-0"
<th className="py-2 px-4 font-semibold">ID</th> style={{ animation: 'fadein-slideup 0.7s cubic-bezier(0.22,1,0.36,1) 0.25s forwards' }}>
<th className="py-2 px-4 font-semibold">Nombre</th>
<th className="py-2 px-4 font-semibold">Tipo</th> {/* Header de la sección */}
<th className="py-2 px-4 font-semibold">Fecha</th> <div className="px-6 py-4 border-b border-gray-200/50">
<th className="py-2 px-4 font-semibold">Acciones</th> <div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">
</tr> <div>
</thead> <div className="flex items-center gap-3 mb-2">
<tbody> <div className="p-2 bg-gradient-to-br from-blue-500 to-blue-700 rounded-xl">
<tr className="border-b hover:bg-blue-50"> <svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<td className="py-2 px-4">1</td> <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" />
<td className="py-2 px-4">Reporte de usuarios</td> </svg>
<td className="py-2 px-4">Usuarios</td> </div>
<td className="py-2 px-4">2025-07-22</td> <h2 className="text-xl font-bold text-gray-900">Reportes disponibles</h2>
<td className="py-2 px-4"> </div>
<button className="text-blue-600 hover:underline">Ver</button> <span className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800">
</td> 📋 {reportes.length} reportes encontrados
</tr> </span>
<tr className="border-b hover:bg-blue-50"> </div>
<td className="py-2 px-4">2</td>
<td className="py-2 px-4">Reporte de documentos</td> {/* Botón descargar masivo */}
<td className="py-2 px-4">Documentos</td> <div className="flex flex-col sm:flex-row gap-2">
<td className="py-2 px-4">2025-07-21</td> <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">
<td className="py-2 px-4"> <svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<button className="text-blue-600 hover:underline">Ver</button> <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" />
</td> </svg>
</tr> Descargar Excel
{/* Más filas de ejemplo aquí */} </button>
</tbody>
</table> <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>
</div> </div>

View File

@@ -1,313 +1,315 @@
import React, { useState, useEffect } from 'react'; import React, { useState } from "react";
export default function Reports() {
const tabs = [
{ key: 'pedimentos', label: 'Generar Reporte de Pedimentos' },
{ key: 'datastage', label: 'Generar Reporte de Datastage' },
{ key: 'minimos', label: 'Generar Reporte de Mínimos' },
{ key: 'coves', label: 'Generar Reporte de COVES' },
];
// Columnas por tipo de reporte y tipo de registro para datastage // Animación fade-in/slide-up para bloques
const columnasPorTab = { const fadeInSlideUp = `@keyframes fadein-slideup {
pedimentos: [ 0% { opacity: 0; transform: translateY(40px); }
{ key: 'id', label: 'ID' }, 100% { opacity: 1; transform: translateY(0); }
{ key: 'nombre', label: 'Nombre' }, }`;
{ key: 'pedimento', label: 'Pedimento' }, if (typeof document !== 'undefined' && !document.getElementById('fadein-slideup-reportes')) {
{ key: 'fecha', label: 'Fecha' }, const style = document.createElement('style');
{ key: 'estado', label: 'Estado' }, style.id = 'fadein-slideup-reportes';
], style.innerHTML = fadeInSlideUp;
datastage: { document.head.appendChild(style);
entrada: [ }
{ key: 'id', label: 'ID' },
{ key: 'nombre', label: 'Nombre' },
{ key: 'fecha', label: 'Fecha' },
{ key: 'usuario', label: 'Usuario' },
{ key: 'entrada', label: 'Entrada' },
],
salida: [
{ key: 'id', label: 'ID' },
{ key: 'nombre', label: 'Nombre' },
{ key: 'fecha', label: 'Fecha' },
{ key: 'usuario', label: 'Usuario' },
{ key: 'salida', label: 'Salida' },
],
proceso: [
{ key: 'id', label: 'ID' },
{ key: 'nombre', label: 'Nombre' },
{ key: 'fecha', label: 'Fecha' },
{ key: 'usuario', label: 'Usuario' },
{ key: 'proceso', label: 'Proceso' },
],
default: [
{ key: 'id', label: 'ID' },
{ key: 'nombre', label: 'Nombre' },
{ key: 'fecha', label: 'Fecha' },
{ key: 'usuario', label: 'Usuario' },
],
},
minimos: [
{ key: 'id', label: 'ID' },
{ key: 'nombre', label: 'Nombre' },
{ key: 'minimo', label: 'Mínimo' },
{ key: 'fecha', label: 'Fecha' },
],
coves: [
{ key: 'id', label: 'ID' },
{ key: 'nombre', label: 'Nombre' },
{ key: 'cove', label: 'COVE' },
{ key: 'fecha', label: 'Fecha' },
],
};
const [activeTab, setActiveTab] = useState('pedimentos'); export default function Reportes() {
const [nombreReporte, setNombreReporte] = useState(''); const [tipoReporte, setTipoReporte] = useState('');
const [columnas, setColumnas] = useState(['id', 'nombre']);
const [fechaInicio, setFechaInicio] = useState(''); const [fechaInicio, setFechaInicio] = useState('');
const [fechaFin, setFechaFin] = useState(''); const [fechaFin, setFechaFin] = useState('');
const [pedimento, setPedimento] = useState('');
const [tipoRegistro, setTipoRegistro] = useState('');
const handleColumnaChange = (col) => { // Datos de ejemplo
setColumnas((prev) => const reportes = [
prev.includes(col) { id: 1, nombre: 'Reporte de usuarios activos', tipo: 'Usuarios', fecha: '2025-08-07', estado: 'Completado' },
? prev.filter((c) => c !== col) { id: 2, nombre: 'Análisis de documentos procesados', tipo: 'Documentos', fecha: '2025-08-06', estado: 'Procesando' },
: [...prev, col] { 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 handleGenerarReporte = (e) => { const limpiarFiltros = () => {
e.preventDefault(); setTipoReporte('');
alert(`Generando reporte: ${nombreReporte}\nTipo: ${activeTab}\nColumnas: ${columnas.join(', ')}\nPedimento: ${pedimento}\nFecha: ${fechaInicio} a ${fechaFin}`); setFechaInicio('');
setFechaFin('');
}; };
// Reset columnas al cambiar de tab o tipo de registro en datastage
useEffect(() => {
if (activeTab === 'datastage') {
if (tipoRegistro && columnasPorTab.datastage[tipoRegistro]) {
setColumnas(['id', 'nombre']);
} else {
setColumnas(['id', 'nombre']);
}
} else {
setColumnas(['id', 'nombre']);
}
}, [activeTab, tipoRegistro]);
return ( return (
<div className="p-6 bg-gray-50 min-h-screen"> <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"> <div className="max-w-7xl mx-auto">
{/* Header decorativo */} {/* Header principal */}
<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"> <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="flex-shrink-0 bg-blue-100 rounded-full p-4 shadow-md animate-bounce-slow"> <div className="bg-white/80 backdrop-blur-xl rounded-2xl shadow-xl border border-blue-100/50 p-6 lg:p-8">
<svg className="h-10 w-10 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <div className="flex items-start space-x-4">
<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" /> <div className="p-3 bg-gradient-to-br from-blue-500 to-blue-700 rounded-xl shadow-lg flex-shrink-0">
</svg> <svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
</div> <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" />
<div> </svg>
<h1 className="text-4xl font-extrabold text-blue-900 tracking-tight mb-1 flex items-center gap-2"> </div>
Reportes
</h1>
<p className="text-lg text-blue-700/80 font-medium">Consulta y descarga reportes relacionados con el sistema.</p>
</div>
{/* Efecto decorativo de fondo */}
<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>
</div>
</div>
{/* Animación personalizada para el icono */}
<style>{`
@keyframes bounce-slow {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-8px); }
}
.animate-bounce-slow {
animation: bounce-slow 2.2s infinite;
}
`}</style>
{/* Tabs y formulario en tarjeta */}
<div className="mb-8 bg-white shadow-lg rounded-xl border border-gray-200">
<div className="flex gap-2 mb-4 px-6 pt-6">
{tabs.map(tab => (
<button
key={tab.key}
className={`px-4 py-2 rounded-t font-semibold border-b-2 transition-all ${activeTab === tab.key ? 'bg-white border-blue-700 text-blue-800' : 'bg-blue-100 border-transparent text-blue-500 hover:bg-blue-200'}`}
onClick={() => setActiveTab(tab.key)}
type="button"
>
{tab.label}
</button>
))}
</div>
<div className="px-6 pb-6 min-h-[340px] flex flex-col justify-between">
<h2 className="text-xl font-bold text-blue-800 mb-2">{tabs.find(t => t.key === activeTab)?.label}</h2>
<p className="text-gray-600 mb-4">Selecciona los campos y filtros que deseas incluir en tu reporte personalizado.</p>
<form className="grid grid-cols-1 md:grid-cols-2 gap-4" onSubmit={handleGenerarReporte}>
<div> <div>
<label className="block text-xs font-semibold text-gray-500 mb-1">Nombre del reporte</label> <h1 className="text-2xl lg:text-3xl font-bold text-gray-900 mb-1">
<input type="text" value={nombreReporte} onChange={e => setNombreReporte(e.target.value)} placeholder="Ej: Reporte personalizado" className="border rounded px-3 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-300" /> Centro de Reportes
</div> </h1>
{/* Pedimento y fechas para cada tab según requerimiento */} <p className="text-gray-600">
{activeTab === 'pedimentos' && ( Consulta, genera y descarga reportes relacionados con el sistema aduanero
<div> </p>
<label className="block text-xs font-semibold text-gray-500 mb-1">Pedimento específico</label> <div className="mt-3">
<input type="text" value={pedimento} onChange={e => setPedimento(e.target.value)} placeholder="Ej: 12345678" className="border rounded px-3 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-300" /> <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>
)}
{activeTab === 'datastage' && (
<>
<div>
<label className="block text-xs font-semibold text-gray-500 mb-1">Pedimento</label>
<input type="text" value={pedimento} onChange={e => setPedimento(e.target.value)} placeholder="Ej: 12345678" className="border rounded px-3 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-300" />
</div>
<div>
<label className="block text-xs font-semibold text-gray-500 mb-1">Fecha inicial</label>
<input type="date" value={fechaInicio} onChange={e => setFechaInicio(e.target.value)} className="border rounded px-3 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-300" />
</div>
<div>
<label className="block text-xs font-semibold text-gray-500 mb-1">Fecha final</label>
<input type="date" value={fechaFin} onChange={e => setFechaFin(e.target.value)} className="border rounded px-3 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-300" />
</div>
<div>
<label className="block text-xs font-semibold text-gray-500 mb-1">Tipo de registro</label>
<select
className="border rounded px-3 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-300"
value={tipoRegistro}
onChange={e => setTipoRegistro(e.target.value)}
>
<option value="">Selecciona...</option>
<option value="entrada">Entrada</option>
<option value="salida">Salida</option>
<option value="proceso">Proceso</option>
{/* Agrega más opciones según los registros disponibles */}
</select>
</div>
</>
)}
{activeTab === 'coves' && (
<>
<div>
<label className="block text-xs font-semibold text-gray-500 mb-1">Pedimento</label>
<input type="text" value={pedimento} onChange={e => setPedimento(e.target.value)} placeholder="Ej: 12345678" className="border rounded px-3 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-300" />
</div>
<div>
<label className="block text-xs font-semibold text-gray-500 mb-1">Fecha inicial</label>
<input type="date" value={fechaInicio} onChange={e => setFechaInicio(e.target.value)} className="border rounded px-3 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-300" />
</div>
<div>
<label className="block text-xs font-semibold text-gray-500 mb-1">Fecha final</label>
<input type="date" value={fechaFin} onChange={e => setFechaFin(e.target.value)} className="border rounded px-3 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-300" />
</div>
</>
)}
{activeTab === 'minimos' && (
<>
<div>
<label className="block text-xs font-semibold text-gray-500 mb-1">Pedimento</label>
<input type="text" value={pedimento} onChange={e => setPedimento(e.target.value)} placeholder="Ej: 12345678" className="border rounded px-3 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-300" />
</div>
<div>
<label className="block text-xs font-semibold text-gray-500 mb-1">Fecha inicial</label>
<input type="date" value={fechaInicio} onChange={e => setFechaInicio(e.target.value)} className="border rounded px-3 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-300" />
</div>
<div>
<label className="block text-xs font-semibold text-gray-500 mb-1">Fecha final</label>
<input type="date" value={fechaFin} onChange={e => setFechaFin(e.target.value)} className="border rounded px-3 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-300" />
</div>
</>
)}
{/* Columnas a incluir solo si no es minimos */}
{activeTab !== 'minimos' && (
<div className="md:col-span-2">
<label className="block text-xs font-semibold text-gray-500 mb-1">Columnas a incluir</label>
<div className="flex flex-wrap gap-4">
{activeTab === 'datastage'
? (columnasPorTab.datastage[tipoRegistro] || columnasPorTab.datastage.default).map(col => (
<label key={col.key} className="inline-flex items-center">
<input
type="checkbox"
className="mr-2"
checked={columnas.includes(col.key)}
onChange={() => handleColumnaChange(col.key)}
/>
{col.label}
</label>
))
: columnasPorTab[activeTab].map(col => (
<label key={col.key} className="inline-flex items-center">
<input
type="checkbox"
className="mr-2"
checked={columnas.includes(col.key)}
onChange={() => handleColumnaChange(col.key)}
/>
{col.label}
</label>
))}
</div>
</div>
)}
{/* Fechas para los demás tabs - ya incluidas arriba */}
<div className="md:col-span-2 flex justify-end mt-2">
<button type="submit" className="bg-blue-700 text-white px-6 py-2 rounded shadow hover:bg-blue-800 transition">Generar reporte</button>
</div> </div>
</form> </div>
</div> </div>
</div> </div>
{/* Tabla de reportes de ejemplo */} {/* Panel de filtros mejorado */}
<div className="bg-white shadow-lg rounded-xl border border-gray-200 mt-8"> <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="flex justify-between items-center mb-4 px-6 pt-6"> <div className="bg-white/80 backdrop-blur-xl rounded-2xl shadow-xl border border-blue-100/50">
<h2 className="text-lg font-bold text-blue-800">Resultados</h2> <div className="px-6 py-4 border-b border-gray-200/50">
<button className="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600 transition flex items-center gap-2"> <div className="flex items-center gap-3">
<svg className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" d="M12 4v16m8-8H4" /></svg> <div className="p-2 bg-gradient-to-br from-blue-500 to-blue-700 rounded-xl">
Descargar Excel <svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
</button> <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>
<div className="overflow-x-auto px-6 pb-6"> </div>
<table className="min-w-full text-sm text-left">
<thead> {/* Sección de resultados */}
<tr className="bg-blue-100 text-blue-900"> <div className="bg-white/80 backdrop-blur-xl shadow-xl rounded-2xl border border-blue-100/50 animate-fadein-slideup opacity-0"
<th className="py-2 px-4 font-semibold">ID</th> style={{ animation: 'fadein-slideup 0.7s cubic-bezier(0.22,1,0.36,1) 0.25s forwards' }}>
<th className="py-2 px-4 font-semibold">Nombre</th>
<th className="py-2 px-4 font-semibold">Tipo</th> {/* Header de la sección */}
<th className="py-2 px-4 font-semibold">Pedimento</th> <div className="px-6 py-4 border-b border-gray-200/50">
<th className="py-2 px-4 font-semibold">Fecha</th> <div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">
<th className="py-2 px-4 font-semibold">Acciones</th> <div>
</tr> <div className="flex items-center gap-3 mb-2">
</thead> <div className="p-2 bg-gradient-to-br from-blue-500 to-blue-700 rounded-xl">
<tbody> <svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<tr className="border-b hover:bg-blue-50"> <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" />
<td className="py-2 px-4">1</td> </svg>
<td className="py-2 px-4">Reporte de usuarios</td> </div>
<td className="py-2 px-4">Usuarios</td> <h2 className="text-xl font-bold text-gray-900">Reportes disponibles</h2>
<td className="py-2 px-4">12345678</td> </div>
<td className="py-2 px-4">2025-07-22</td> <span className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800">
<td className="py-2 px-4"> 📋 {reportes.length} reportes encontrados
<button className="text-blue-600 hover:underline">Ver</button> </span>
</td> </div>
</tr>
<tr className="border-b hover:bg-blue-50"> {/* Botón descargar masivo */}
<td className="py-2 px-4">2</td> <div className="flex flex-col sm:flex-row gap-2">
<td className="py-2 px-4">Reporte de documentos</td> <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">
<td className="py-2 px-4">Documentos</td> <svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<td className="py-2 px-4">87654321</td> <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" />
<td className="py-2 px-4">2025-07-21</td> </svg>
<td className="py-2 px-4"> Descargar Excel
<button className="text-blue-600 hover:underline">Ver</button> </button>
</td>
</tr> <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">
{/* Más filas de ejemplo aquí */} <svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
</tbody> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 4v16m8-8H4" />
</table> </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>
</div> </div>