fix(admin): add pagination to /admin/orders with prev/next and total counter
This commit is contained in:
@@ -8146,7 +8146,7 @@
|
|||||||
"description": "Admin orders page shows only 20 orders with no pagination. Add offset/limit controls, prev/next buttons, and total counter.",
|
"description": "Admin orders page shows only 20 orders with no pagination. Add offset/limit controls, prev/next buttons, and total counter.",
|
||||||
"priority": "med",
|
"priority": "med",
|
||||||
"risk": "low",
|
"risk": "low",
|
||||||
"status": "pending",
|
"status": "in_progress",
|
||||||
"created_at": "2026-08-26",
|
"created_at": "2026-08-26",
|
||||||
"gates": {
|
"gates": {
|
||||||
"reviewer": false,
|
"reviewer": false,
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ export default function OrdersPage() {
|
|||||||
const [filterState, setFilterState] = useState('');
|
const [filterState, setFilterState] = useState('');
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [debouncedSearch, setDebouncedSearch] = useState('');
|
const [debouncedSearch, setDebouncedSearch] = useState('');
|
||||||
|
const [total, setTotal] = useState(0);
|
||||||
|
const [offset, setOffset] = useState(0);
|
||||||
|
const limit = 20;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const t = setTimeout(() => setDebouncedSearch(search), 400);
|
const t = setTimeout(() => setDebouncedSearch(search), 400);
|
||||||
@@ -78,14 +81,21 @@ export default function OrdersPage() {
|
|||||||
const data = await ordersApi.list({
|
const data = await ordersApi.list({
|
||||||
status: filterState || undefined,
|
status: filterState || undefined,
|
||||||
q: debouncedSearch || undefined,
|
q: debouncedSearch || undefined,
|
||||||
limit: 20,
|
limit,
|
||||||
|
offset,
|
||||||
});
|
});
|
||||||
setOrders(data.items);
|
setOrders(data.items);
|
||||||
|
setTotal(data.total);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : 'Error al cargar');
|
setError(err instanceof Error ? err.message : 'Error al cargar');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
}, [filterState, debouncedSearch, offset]);
|
||||||
|
|
||||||
|
// Reset to first page when filters change
|
||||||
|
useEffect(() => {
|
||||||
|
setOffset(0);
|
||||||
}, [filterState, debouncedSearch]);
|
}, [filterState, debouncedSearch]);
|
||||||
|
|
||||||
useEffect(() => { load(); }, [load]);
|
useEffect(() => { load(); }, [load]);
|
||||||
@@ -95,7 +105,7 @@ export default function OrdersPage() {
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<h1 className="text-2xl font-bold text-gray-900">Pedidos</h1>
|
<h1 className="text-2xl font-bold text-gray-900">Pedidos</h1>
|
||||||
<p className="text-sm text-gray-500 mt-0.5">{orders?.length ?? 0} pedidos</p>
|
<p className="text-sm text-gray-500 mt-0.5">{total} pedidos</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Filters */}
|
{/* Filters */}
|
||||||
@@ -206,6 +216,32 @@ export default function OrdersPage() {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
)}
|
)}
|
||||||
|
{/* Pagination footer */}
|
||||||
|
{!loading && !error && orders && orders.length > 0 && (
|
||||||
|
<div className="flex items-center justify-between px-4 py-3 border-t border-gray-200 bg-gray-50">
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
Mostrando {offset + 1}–{Math.min(offset + limit, total)} de {total}
|
||||||
|
</p>
|
||||||
|
<div className="flex gap-1">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setOffset(Math.max(0, offset - limit))}
|
||||||
|
disabled={offset === 0}
|
||||||
|
className="px-3 py-1.5 text-xs border border-gray-300 rounded-lg disabled:opacity-40 hover:bg-white"
|
||||||
|
>
|
||||||
|
← Anterior
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setOffset(offset + limit)}
|
||||||
|
disabled={offset + limit >= total}
|
||||||
|
className="px-3 py-1.5 text-xs border border-gray-300 rounded-lg disabled:opacity-40 hover:bg-white"
|
||||||
|
>
|
||||||
|
Siguiente →
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
"feature_id": "CHECKOUT-STOCK-RECHECK",
|
"feature_id": "CHECKOUT-STOCK-RECHECK",
|
||||||
"stage": "build",
|
"stage": "build",
|
||||||
"agent": "implementer",
|
"agent": "implementer",
|
||||||
"action": "Implementar admin Club: endpoints backend + página dashboard",
|
"action": "Add pagination to admin orders page",
|
||||||
"state": "running",
|
"state": "running",
|
||||||
"next_agent": "reviewer",
|
"next_agent": "reviewer",
|
||||||
"waiting_for": "build",
|
"waiting_for": "build",
|
||||||
"updated_at": "2026-08-26T18:49:57.402761Z",
|
"updated_at": "2026-08-26T20:42:28.158071Z",
|
||||||
"timeline": [
|
"timeline": [
|
||||||
{
|
{
|
||||||
"ts": "2026-08-26T17:21:37Z",
|
"ts": "2026-08-26T17:21:37Z",
|
||||||
@@ -245,6 +245,20 @@
|
|||||||
"stage": "build",
|
"stage": "build",
|
||||||
"state": "running",
|
"state": "running",
|
||||||
"message": "Implementar recovery codes para CLUB-004"
|
"message": "Implementar recovery codes para CLUB-004"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ts": "2026-08-26T20:42:28.158071Z",
|
||||||
|
"agent": "architect",
|
||||||
|
"stage": "design",
|
||||||
|
"state": "done",
|
||||||
|
"message": "Diseño: pagination prev/next + total counter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ts": "2026-08-26T20:42:28.158071Z",
|
||||||
|
"agent": "implementer",
|
||||||
|
"stage": "build",
|
||||||
|
"state": "running",
|
||||||
|
"message": "Add pagination to admin orders page"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"gates": {
|
"gates": {
|
||||||
|
|||||||
Reference in New Issue
Block a user