Implement standardized skeleton loading placeholders to reduce perceived
loading time and prevent layout shift during data fetches. These components
match actual content dimensions exactly, improving perceived responsiveness.
New skeleton components in src/components/skeletons/:
- SkeletonTable: Table/grid loading with customizable rows and cells
- SkeletonTableRow: Individual animated skeleton row
- SkeletonChart: Chart/graph loading with bars matching dimensions
- SkeletonStat: Stat card loading with label and value
- SkeletonFormField: Form input loading placeholder
- PageLoadingSkeleton: Convenience wrapper for page-level loading states
Implementation details:
- All skeletons use global 'skeleton-pulse' animation (2s cycle)
- Dimensions match real content to prevent layout shift on arrival
- Marked with aria-hidden and role=presentation for accessibility
- Theme-aware colors using Fluent UI tokens
- Respects prefers-reduced-motion setting
Updates:
- ChartStateWrapper: Uses SkeletonChart instead of spinner
- PageFeedback: Added PageLoadingSkeleton component
- App.tsx: Injects skeleton styles at startup
- Web-Design.md: Added § 8a with loading UX guidance and usage examples
All components tested (22 tests, 100% passing) and linted.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Task 1: Mark imported blocklist IP addresses
- Add BanOrigin type and _derive_origin() to ban.py model
- Populate origin field in ban_service list_bans() and bans_by_country()
- BanTable and MapPage companion table show origin badge column
- Tests: origin derivation in test_ban_service.py and test_dashboard.py
- Task 2: Add origin filter to dashboard and world map
- ban_service: _origin_sql_filter() helper; origin param on list_bans()
and bans_by_country()
- dashboard router: optional origin query param forwarded to service
- Frontend: BanOriginFilter type + BAN_ORIGIN_FILTER_LABELS in ban.ts
- fetchBans / fetchBansByCountry forward origin to API
- useBans / useMapData accept and pass origin; page resets on change
- BanTable accepts origin prop; DashboardPage adds segmented filter
- MapPage adds origin Select next to time-range picker
- Tests: origin filter assertions in test_ban_service and test_dashboard
- Implement ban model, service, and router endpoints in backend
- Add ban table component and dashboard integration in frontend
- Update ban-related types and API endpoints
- Add comprehensive tests for ban service and dashboard router
- Update documentation (Features, Tasks, Architecture, Web-Design)
- Clean up old fail2ban configuration files
- Update Makefile with new commands