Add pagination to dataset gallery (50 per page, newest first)

This commit is contained in:
2026-01-13 12:48:37 -03:00
parent c2fb62ab7a
commit eb19a557c3
2 changed files with 78 additions and 10 deletions

View File

@@ -227,14 +227,34 @@ def dataset_count():
@app.route("/dataset/list")
def dataset_list():
"""Lista todas las imágenes del dataset"""
"""Lista las imágenes del dataset con paginación"""
from flask import request
try:
page = int(request.args.get('page', 1))
per_page = int(request.args.get('per_page', 50))
files = [f for f in os.listdir(DATASET_DIR) if f.endswith('.jpg')]
# Ordenar por fecha (más recientes primero)
files.sort(reverse=True)
# Ordenar por fecha de modificación (más recientes primero)
files_with_time = []
for f in files:
filepath = os.path.join(DATASET_DIR, f)
mtime = os.path.getmtime(filepath)
files_with_time.append((f, mtime))
files_with_time.sort(key=lambda x: x[1], reverse=True)
sorted_files = [f[0] for f in files_with_time]
# Paginación
total = len(sorted_files)
total_pages = (total + per_page - 1) // per_page
start = (page - 1) * per_page
end = start + per_page
page_files = sorted_files[start:end]
images = []
for f in files[:50]: # Limitar a últimas 50
for f in page_files:
parts = f.replace('.jpg', '').split('_')
plate = parts[0] if parts else 'Unknown'
images.append({
@@ -243,7 +263,13 @@ def dataset_list():
'url': f'/dataset/images/{f}'
})
return {"images": images, "total": len(files)}
return {
"images": images,
"total": total,
"page": page,
"per_page": per_page,
"total_pages": total_pages
}
except Exception as e:
return {"images": [], "total": 0, "error": str(e)}

View File

@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react';
import axios from 'axios';
import io from 'socket.io-client';
import { Users, CheckCircle, XCircle, Shield, Trash2, Camera, AlertCircle, Database, X, Image } from 'lucide-react';
import { Users, CheckCircle, XCircle, Shield, Trash2, Camera, AlertCircle, Database, X, Image, ChevronLeft, ChevronRight } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import LanguageSelector from '../components/LanguageSelector';
@@ -31,6 +31,9 @@ function AdminDashboard({ token }) {
const [showDatasetModal, setShowDatasetModal] = useState(false);
const [datasetImages, setDatasetImages] = useState([]);
const [selectedImage, setSelectedImage] = useState(null);
const [datasetPage, setDatasetPage] = useState(1);
const [datasetTotalPages, setDatasetTotalPages] = useState(1);
const [datasetTotal, setDatasetTotal] = useState(0);
useEffect(() => {
fetchData();
@@ -110,20 +113,31 @@ function AdminDashboard({ token }) {
}
};
const fetchDatasetImages = async () => {
const fetchDatasetImages = async (page = 1) => {
try {
const res = await axios.get('/dataset/list');
const res = await axios.get(`/dataset/list?page=${page}&per_page=50`);
setDatasetImages(res.data.images || []);
setDatasetPage(res.data.page || 1);
setDatasetTotalPages(res.data.total_pages || 1);
setDatasetTotal(res.data.total || 0);
} catch (err) {
console.error('Error fetching dataset images');
}
};
const openDatasetModal = () => {
fetchDatasetImages();
setDatasetPage(1);
fetchDatasetImages(1);
setShowDatasetModal(true);
};
const handleDatasetPageChange = (newPage) => {
if (newPage >= 1 && newPage <= datasetTotalPages) {
setDatasetPage(newPage);
fetchDatasetImages(newPage);
}
};
const handleSearchRut = (e) => {
e.preventDefault();
const normalizedRut = searchRut.replace(/\./g, '').toUpperCase();
@@ -512,7 +526,7 @@ function AdminDashboard({ token }) {
<div className="flex items-center gap-3">
<Database className="text-emerald-400" size={24} />
<h2 className="text-xl font-bold text-white">Dataset de Capturas</h2>
<span className="bg-emerald-600 px-2 py-1 rounded text-sm font-mono">{datasetImages.length} imágenes</span>
<span className="bg-emerald-600 px-2 py-1 rounded text-sm font-mono">{datasetTotal} imágenes</span>
</div>
<button
onClick={() => { setShowDatasetModal(false); setSelectedImage(null); }}
@@ -572,6 +586,34 @@ function AdminDashboard({ token }) {
</div>
)}
</div>
{/* Pagination Footer */}
{!selectedImage && datasetTotalPages > 1 && (
<div className="flex items-center justify-center gap-4 p-4 border-t border-slate-700 bg-slate-800/50">
<button
onClick={() => handleDatasetPageChange(datasetPage - 1)}
disabled={datasetPage === 1}
className="flex items-center gap-1 px-3 py-2 rounded-lg bg-slate-700 hover:bg-slate-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
<ChevronLeft size={18} />
Anterior
</button>
<div className="flex items-center gap-2 text-slate-300">
<span>Página</span>
<span className="bg-emerald-600 px-3 py-1 rounded font-mono font-bold">{datasetPage}</span>
<span>de</span>
<span className="font-mono font-bold">{datasetTotalPages}</span>
</div>
<button
onClick={() => handleDatasetPageChange(datasetPage + 1)}
disabled={datasetPage === datasetTotalPages}
className="flex items-center gap-1 px-3 py-2 rounded-lg bg-slate-700 hover:bg-slate-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
Siguiente
<ChevronRight size={18} />
</button>
</div>
)}
</div>
</div>
)}