diff --git a/ui/package-lock.json b/ui/package-lock.json index 72c9bdfb..2083b354 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -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", diff --git a/ui/package.json b/ui/package.json index b623c98e..4bbd4cca 100644 --- a/ui/package.json +++ b/ui/package.json @@ -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", diff --git a/ui/src/pages/Admin/AuditLogPage.tsx b/ui/src/pages/Admin/AuditLogPage.tsx index 7813e438..2fb77dcb 100644 --- a/ui/src/pages/Admin/AuditLogPage.tsx +++ b/ui/src/pages/Admin/AuditLogPage.tsx @@ -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('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) => (
diff --git a/ui/src/pages/Dashboard/Dashboard.tsx b/ui/src/pages/Dashboard/Dashboard.tsx index b09e2b21..f5f58f01 100644 --- a/ui/src/pages/Dashboard/Dashboard.tsx +++ b/ui/src/pages/Dashboard/Dashboard.tsx @@ -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() const [panelOpen, setPanelOpen] = useState(false) + const [sortField, setSortField] = useState('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 ? ( diff --git a/ui/src/pages/Routes/RouteDetail.tsx b/ui/src/pages/Routes/RouteDetail.tsx index 3572a07d..d1205d3a 100644 --- a/ui/src/pages/Routes/RouteDetail.tsx +++ b/ui/src/pages/Routes/RouteDetail.tsx @@ -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('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} /> )}