8.1 KiB
Work Summary: History Page Pagination & Fixed Navbar
Date: 2026-02-04
Status: Complete
Overview
This session addressed critical performance and UX issues with the three history pages (Events, Executions, Enforcements) where unlimited WebSocket updates were causing:
- Performance degradation (sluggish rendering with thousands of items)
- Poor UX (excessive scrolling, hard to visually parse)
- Navbar scrolling with page content instead of remaining fixed
Changes Made
1. Fixed Navigation Bar Position
File: attune/web/src/components/layout/MainLayout.tsx
Problem: The sidebar was scrolling with page content, making navigation inaccessible when scrolling through long lists.
Solution: Made the sidebar fixed and content area independently scrollable:
- Changed parent container from
min-h-screentoh-screenwithoverflow-hidden - Made sidebar
flex-shrink-0to prevent compression - Made navigation section
overflow-y-autoto scroll within sidebar if needed - Made content area
overflow-y-autofor independent scrolling
Result: Sidebar now remains fixed at all times, content scrolls independently.
2. Capped WebSocket List Updates
Problem: All three history pages were adding unlimited items from WebSocket notifications, causing arrays to grow unbounded.
Files Modified:
attune/web/src/hooks/useExecutionStream.tsattune/web/src/hooks/useEnforcementStream.tsattune/web/src/pages/events/EventsPage.tsx(already had cap, but verified)
Solution: Limited lists to maximum 50 items when adding new items via WebSocket:
// Before:
updatedData = [newItem, ...old.data];
// After:
updatedData = [newItem, ...old.data].slice(0, 50);
Also added pagination total count updates when new items arrive.
3. Added Pagination to History Pages
Files Modified:
attune/web/src/pages/executions/ExecutionsPage.tsxattune/web/src/pages/enforcements/EnforcementsPage.tsxattune/web/src/pages/events/EventsPage.tsx(updated from 20 to 50 items)
Implementation Details:
- Page State: Added
pagestate andpageSize = 50constant - Query Params: Included pagination in API query parameters
- Filter Reset: Reset to page 1 when filters change
- Pagination Controls: Added Previous/Next buttons with:
- Mobile-friendly responsive layout
- Disabled states for first/last page
- Item count display ("Showing X to Y of Z items")
- Consistent styling across all three pages
Pagination UI Structure:
- Mobile: Simple Previous/Next buttons
- Desktop: Shows count + Previous/Next buttons
- Only appears when
totalPages > 1
4. Verified API Defaults
Confirmed that the API already supports pagination with sensible defaults:
- Default page: 1
- Default per_page: 20
- Maximum per_page: 100
- Our choice of 50 is within limits and provides good balance
Benefits
Performance
- Eliminated unbounded array growth that caused UI sluggishness
- Limited DOM elements to 50 per page maximum
- Faster rendering with smaller datasets
- Lower memory usage by not keeping thousands of items in memory
User Experience
- Fixed navigation always accessible, never scrolls away
- Manageable list sizes easier to scan and comprehend
- Pagination controls allow browsing through history systematically
- Consistent behavior across all three history pages
- Filter reset ensures users see relevant first page when changing filters
Code Quality
- Consistent pattern across all history pages
- Type-safe pagination implementation
- Proper state management with React Query cache updates
- Responsive design for mobile and desktop
Technical Details
Layout Structure
┌─────────────────────────────────────┐
│ Fixed Sidebar (h-screen) │
│ ┌──────────────────┐ │
│ │ Header (fixed) │ │
│ ├──────────────────┤ │
│ │ Nav (scrollable) │ │
│ │ │ │
│ ├──────────────────┤ │
│ │ Toggle (fixed) │ │
│ ├──────────────────┤ │
│ │ User (fixed) │ │
│ └──────────────────┘ │
├─────────────────────────────────────┤
│ Content Area (overflow-y-auto) │
│ - Scrolls independently │
│ - Unlimited height │
└─────────────────────────────────────┘
WebSocket Update Flow with Cap
New Item Notification
↓
Extract from payload
↓
Check if matches current filters
↓
If page === 1:
→ Add to beginning
→ Slice to 50 items
→ Update total count
Else:
→ Only update total count
↓
Update React Query cache
↓
UI re-renders with capped list
Pagination Logic
- Page 1: Shows items 1-50
- Page 2: Shows items 51-100
- Page N: Shows items
(N-1)*50 + 1toN*50 - WebSocket updates: Only modify page 1, update total count on all pages
Files Modified
attune/web/src/components/layout/MainLayout.tsx- Fixed sidebar positionattune/web/src/hooks/useExecutionStream.ts- Capped list at 50 itemsattune/web/src/hooks/useEnforcementStream.ts- Capped list at 50 itemsattune/web/src/pages/executions/ExecutionsPage.tsx- Added paginationattune/web/src/pages/enforcements/EnforcementsPage.tsx- Added paginationattune/web/src/pages/events/EventsPage.tsx- Updated to 50 items per page
Testing Recommendations
-
Fixed Navbar:
- Scroll down on any history page
- Verify navbar stays fixed at side
- Verify navigation items remain clickable
-
Pagination:
- Generate 100+ items (events/executions/enforcements)
- Verify only 50 items shown per page
- Test Previous/Next navigation
- Verify item count display is accurate
-
WebSocket with Pagination:
- Be on page 1 of any history page
- Create new items via API
- Verify new items appear at top
- Verify list stays at 50 items maximum
- Navigate to page 2 and create items
- Verify total count updates but list doesn't change
-
Filter Reset:
- Navigate to page 2 or 3
- Change a filter
- Verify page resets to 1
-
Responsive Design:
- Test pagination on mobile viewport
- Verify simple Previous/Next buttons show
- Test on desktop viewport
- Verify full pagination with counts shows
No Breaking Changes
All changes are backwards compatible:
- API contracts unchanged
- Database schema unchanged
- WebSocket notification format unchanged
- Existing queries continue to work with default pagination
Future Considerations
- Jump to Page: Could add direct page number input/selection
- Page Size Control: Could allow users to choose 25/50/100 items per page
- Infinite Scroll: Alternative to pagination for some users' preference
- Virtual Scrolling: For even better performance with large datasets
- Pagination Persistence: Remember page number in URL or localStorage
Performance Impact
Before
- Lists could grow to thousands of items
- Rendering time: O(n) where n is unbounded
- Memory usage: Unbounded
- Scroll performance: Degrades with list size
After
- Lists capped at 50 items per page
- Rendering time: O(50) = constant
- Memory usage: Bounded to current page
- Scroll performance: Consistent regardless of total items
User Impact
Positive:
- Much faster page rendering
- Always accessible navigation
- Easier to browse through history systematically
- Clear indication of total items available
Neutral:
- Users must click Next to see more items (standard pagination UX)
- Real-time updates only visible on page 1 (by design for performance)