fix: use server-side sorting for paginated tables
Upgrade @cameleer/design-system to v0.1.1 which adds onSortChange callback to DataTable. Wire it up in Dashboard (exchanges), AuditLog, and RouteDetail (recent executions) so sorting triggers a new API request with sortField/sortDir instead of only sorting the current page. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
8
ui/package-lock.json
generated
8
ui/package-lock.json
generated
@@ -8,7 +8,7 @@
|
||||
"name": "ui",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@cameleer/design-system": "^0.0.3",
|
||||
"@cameleer/design-system": "^0.1.1",
|
||||
"@tanstack/react-query": "^5.90.21",
|
||||
"openapi-fetch": "^0.17.0",
|
||||
"react": "^19.2.4",
|
||||
@@ -276,9 +276,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cameleer/design-system": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.0.3/design-system-0.0.3.tgz",
|
||||
"integrity": "sha512-x1mZvgYz7j57xFB26pMh9hn5waSJA1CcRWTgkzleLfaO/CmhekLup1HHlbh0b9SxVci6g2HzbcJldr4kvM1yzg==",
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.1/design-system-0.1.1.tgz",
|
||||
"integrity": "sha512-5PNIDuw9vcM0BVQIdiJQalWoMJPUZj01f6rX3unHDCF1gE1mBHCFZ41RPy2QsuKmjgZ1azenXXdnHL/XGVostQ==",
|
||||
"dependencies": {
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"generate-api:live": "curl -s http://localhost:8081/api/v1/api-docs -o src/api/openapi.json && openapi-typescript src/api/openapi.json -o src/api/schema.d.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cameleer/design-system": "^0.0.3",
|
||||
"@cameleer/design-system": "^0.1.1",
|
||||
"@tanstack/react-query": "^5.90.21",
|
||||
"openapi-fetch": "^0.17.0",
|
||||
"react": "^19.2.4",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useMemo } from 'react';
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
import {
|
||||
Badge, DateRangePicker, Input, Select, MonoText, CodeBlock, DataTable,
|
||||
} from '@cameleer/design-system';
|
||||
@@ -60,6 +60,14 @@ export default function AuditLogPage() {
|
||||
const [categoryFilter, setCategoryFilter] = useState('');
|
||||
const [searchFilter, setSearchFilter] = useState('');
|
||||
const [page, setPage] = useState(0);
|
||||
const [sortField, setSortField] = useState<string>('timestamp');
|
||||
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('desc');
|
||||
|
||||
const handleSortChange = useCallback((key: string, dir: 'asc' | 'desc') => {
|
||||
setSortField(key);
|
||||
setSortDir(dir);
|
||||
setPage(0);
|
||||
}, []);
|
||||
|
||||
const { data } = useAuditLog({
|
||||
username: userFilter || undefined,
|
||||
@@ -67,6 +75,8 @@ export default function AuditLogPage() {
|
||||
search: searchFilter || undefined,
|
||||
from: dateRange.start.toISOString(),
|
||||
to: dateRange.end.toISOString(),
|
||||
sort: sortField,
|
||||
order: sortDir,
|
||||
page,
|
||||
size: 25,
|
||||
});
|
||||
@@ -122,6 +132,7 @@ export default function AuditLogPage() {
|
||||
sortable
|
||||
flush
|
||||
pageSize={25}
|
||||
onSortChange={handleSortChange}
|
||||
rowAccent={(row) => row.result === 'FAILURE' ? 'error' : undefined}
|
||||
expandedContent={(row) => (
|
||||
<div className={styles.expandedDetail}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useMemo } from 'react'
|
||||
import { useState, useMemo, useCallback } from 'react'
|
||||
import { useParams, useNavigate } from 'react-router'
|
||||
import {
|
||||
DataTable,
|
||||
@@ -176,12 +176,19 @@ export default function Dashboard() {
|
||||
const navigate = useNavigate()
|
||||
const [selectedId, setSelectedId] = useState<string | undefined>()
|
||||
const [panelOpen, setPanelOpen] = useState(false)
|
||||
const [sortField, setSortField] = useState<string>('startTime')
|
||||
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('desc')
|
||||
|
||||
const { timeRange, statusFilters } = useGlobalFilters()
|
||||
const timeFrom = timeRange.start.toISOString()
|
||||
const timeTo = timeRange.end.toISOString()
|
||||
const timeWindowSeconds = (timeRange.end.getTime() - timeRange.start.getTime()) / 1000
|
||||
|
||||
const handleSortChange = useCallback((key: string, dir: 'asc' | 'desc') => {
|
||||
setSortField(key)
|
||||
setSortDir(dir)
|
||||
}, [])
|
||||
|
||||
// ─── API hooks ───────────────────────────────────────────────────────────
|
||||
const { data: stats } = useExecutionStats(timeFrom, timeTo, routeId, appId)
|
||||
const { data: timeseries } = useStatsTimeseries(timeFrom, timeTo, routeId, appId)
|
||||
@@ -191,6 +198,8 @@ export default function Dashboard() {
|
||||
timeTo,
|
||||
routeId: routeId || undefined,
|
||||
application: appId || undefined,
|
||||
sortField,
|
||||
sortDir,
|
||||
offset: 0,
|
||||
limit: 50,
|
||||
},
|
||||
@@ -385,6 +394,7 @@ export default function Dashboard() {
|
||||
selectedId={selectedId}
|
||||
sortable
|
||||
flush
|
||||
onSortChange={handleSortChange}
|
||||
rowAccent={handleRowAccent}
|
||||
expandedContent={(row: Row) =>
|
||||
row.errorMessage ? (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useMemo } from 'react';
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
import { useParams, useNavigate, Link } from 'react-router';
|
||||
import {
|
||||
KpiStrip,
|
||||
@@ -260,6 +260,13 @@ export default function RouteDetail() {
|
||||
const timeTo = timeRange.end.toISOString();
|
||||
|
||||
const [activeTab, setActiveTab] = useState('performance');
|
||||
const [recentSortField, setRecentSortField] = useState<string>('startTime');
|
||||
const [recentSortDir, setRecentSortDir] = useState<'asc' | 'desc'>('desc');
|
||||
|
||||
const handleRecentSortChange = useCallback((key: string, dir: 'asc' | 'desc') => {
|
||||
setRecentSortField(key);
|
||||
setRecentSortDir(dir);
|
||||
}, []);
|
||||
|
||||
// ── API queries ────────────────────────────────────────────────────────────
|
||||
const { data: catalog } = useRouteCatalog();
|
||||
@@ -272,6 +279,8 @@ export default function RouteDetail() {
|
||||
timeTo,
|
||||
routeId: routeId || undefined,
|
||||
application: appId || undefined,
|
||||
sortField: recentSortField,
|
||||
sortDir: recentSortDir,
|
||||
offset: 0,
|
||||
limit: 50,
|
||||
});
|
||||
@@ -560,6 +569,7 @@ export default function RouteDetail() {
|
||||
onRowClick={(row) => navigate(`/exchanges/${row.executionId}`)}
|
||||
sortable
|
||||
pageSize={20}
|
||||
onSortChange={handleRecentSortChange}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user