- Added references to existing test files in the UI/UX testing plan. - Updated CI failure remediation plan with improved file paths and clarity. - Expanded CrowdSec full implementation documentation with detailed configuration steps and scripts. - Improved CrowdSec testing plan with clearer objectives and expected results. - Updated current specification documentation with additional context on CVE remediation. - Enhanced docs-to-issues workflow documentation for better issue tracking. - Corrected numbering in UI/UX bugfixes specification for clarity. - Improved WAF testing plan with detailed curl commands and expected results. - Updated QA reports for CrowdSec implementation and UI/UX testing with detailed results and coverage metrics. - Fixed rate limit integration test summary with clear identification of issues and resolutions. - Enhanced rate limit test status report with detailed root causes and next steps for follow-up.
16 KiB
📋 Plan: UI/UX and Backend Bug Fixes - Multi-Issue Resolution
Created: December 5, 2025 Status: Planning Complete - Ready for Implementation
🧐 UX & Context Analysis
The user has identified 12 distinct issues affecting usability, consistency, and functionality. These span both frontend (UI/UX) and backend (API/data) concerns.
Issue Summary Matrix
| # | Issue | Category | Severity | Component |
|---|---|---|---|---|
| 1 | Uptime card not updated when editing proxy host | Backend/Frontend | High | ProxyHostForm, UptimeService |
| 2 | Certificates missing delete action | Frontend | Medium | CertificateList |
| 3 | Inconsistent app sizing between IP and domain access | Frontend/CSS | Medium | index.css, Layout |
| 4 | Notification Provider template dropdown invisible text | Frontend | High | Notifications |
| 5 | Templates should be in Provider section with assignment | Frontend | Medium | Notifications |
| 6 | Banner/header sizing issues (tiny on desktop, huge on mobile) | Frontend | Medium | Layout |
| 7 | Mobile drawer icon should be on left side | Frontend | Low | Layout |
| 8 | Mobile view should show logo instead of banner | Frontend | Low | Layout |
| 9 | CrowdSec card buttons truncated on smaller screens | Frontend | Medium | Security |
| 10 | /security/crowdsec shows blank page | Frontend | High | CrowdSecConfig, Layout |
| 11 | Reorganize sidebar: Users → Account Management under Settings | Frontend | Medium | Layout, Router |
| 12 | Missing loading overlay when adding/removing ACL from proxy host | Frontend | High | ProxyHosts, useProxyHosts |
🤝 Handoff Contracts (API Specifications)
Issue #1: Uptime Card Sync on Proxy Host Edit
Problem: When editing a proxy host (e.g., changing name or domain), the associated UptimeMonitor is not updated. Users must manually delete and recreate uptime cards.
Current Behavior: syncMonitors() only creates new monitors or updates name; it doesn't handle domain/URL changes properly.
Required Backend Changes:
// PUT /api/v1/proxy-hosts/:uuid
// Backend should automatically trigger uptime monitor sync when relevant fields change
{
"request_payload": {
"name": "Updated Name",
"domain_names": "new.example.com",
"forward_host": "192.168.1.100",
"forward_port": 8080
},
"response_success": {
"uuid": "abc123",
"name": "Updated Name",
"domain_names": "new.example.com",
"uptime_monitor_synced": true
}
}
Implementation: Modify updateProxyHost handler to call UptimeService.SyncMonitorForHost(hostID) after successful update. The sync should update URL, name, and upstream_host on the linked monitor.
Issue #2: Certificate Delete Action
Problem: Certificates page shows no actions in the table. Delete button only appears conditionally and conditions are too restrictive.
Frontend Fix: Always show delete action for custom and staging certificates. Improve visibility logic.
// CertificateList.tsx - Actions column should always render delete for deletable certs
// Current condition is too restrictive:
// cert.id && (cert.provider === 'custom' || cert.issuer?.toLowerCase().includes('staging'))
// AND status check: cert.status !== 'valid' && cert.status !== 'expiring'
// New condition: Remove status restriction, allow delete for custom/staging certs
// Only block if certificate is in use by a proxy host
Issue #3: Inconsistent App Sizing
Root Cause: body { zoom: 0.75; } in index.css causes different rendering based on browser zoom behavior when accessed via IP vs domain.
Solution: Remove the zoom: 0.75 property and instead use proper CSS scaling or adjust layout max-widths for consistent sizing.
/* BEFORE */
body {
zoom: 0.75; /* REMOVE THIS */
}
/* AFTER */
body {
margin: 0;
min-width: 320px;
min-height: 100vh;
/* Use consistent font sizing and container widths instead */
}
Issue #4: Notification Template Dropdown Invisible Text
Problem: In ProviderForm, the template <select> dropdown options have text that matches the background color.
Fix: Add proper text color classes to the select element.
// BEFORE (line ~119 in Notifications.tsx)
<select {...register('template')} className="mt-1 block w-full rounded-md border-gray-300">
// AFTER
<select {...register('template')} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white sm:text-sm">
Issue #5: Template Assignment UX Redesign
Current Flow:
- User creates provider
- User separately manages templates
- No direct way to assign template to provider
New Flow:
- Provider form includes template selector dropdown
- Dropdown shows: "Minimal (built-in)", "Detailed (built-in)", "Custom", and any saved external templates
- If "Custom" selected, inline textarea appears
- Templates can be saved with a name for reuse
// ProviderForm structure update - consolidate template selection
<div>
<label>Message Template</label>
<select value={templateSelection}>
<optgroup label="Built-in Templates">
<option value="minimal">Minimal</option>
<option value="detailed">Detailed</option>
</optgroup>
<optgroup label="Saved Templates">
{externalTemplates.map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
</optgroup>
<option value="custom">Custom...</option>
</select>
{templateSelection === 'custom' && (
<>
<textarea value={customTemplate} />
<button onClick={saveAsTemplate}>Save as Template</button>
</>
)}
</div>
Issue #6, #7, #8: Mobile/Desktop Header & Banner Fixes
Problems:
- Banner tiny on desktop header
- Banner huge on mobile header
- Drawer toggle icon on right (should be left)
- Mobile should show logo instead of banner
Layout.tsx Changes:
// Mobile Header (line ~97) - Move menu button to left, show logo instead of banner
// BEFORE:
<div className="lg:hidden fixed top-0 left-0 right-0 h-16 ...">
<img src="/banner.png" alt="Charon" height={1280} width={640} />
<div className="flex items-center gap-2">
...
<Button variant="ghost" size="sm" onClick={() => setMobileSidebarOpen(!mobileSidebarOpen)}>
{mobileSidebarOpen ? '✕' : '☰'}
</Button>
</div>
</div>
// AFTER:
<div className="lg:hidden fixed top-0 left-0 right-0 h-16 ...">
<div className="flex items-center gap-2">
<Button variant="ghost" size="sm" onClick={() => setMobileSidebarOpen(!mobileSidebarOpen)}>
<Menu className="w-5 h-5" />
</Button>
<img src="/logo.png" alt="Charon" className="h-10 w-auto" />
</div>
<div className="flex items-center gap-2">
<NotificationCenter />
<ThemeToggle />
</div>
</div>
// Sidebar banner (line ~109) - consistent sizing
<div className="h-20 flex items-center justify-center ...">
{isCollapsed ? (
<img src="/logo.png" alt="Charon" className="h-12 w-auto" />
) : (
<img src="/banner.png" alt="Charon" className="h-12 w-auto max-w-[200px]" />
)}
</div>
Issue #9: CrowdSec Card Button Truncation
Problem: On smaller screens, CrowdSec card buttons overflow and get cut off.
Fix: Make button container responsive with flex-wrap.
// Security.tsx - CrowdSec card buttons (around line ~173)
// BEFORE:
<div className="mt-4 flex gap-2">
<Button>View Logs</Button>
<Button>Export</Button>
<Button>Configure</Button>
<div className="flex gap-2 w-full">
<Button>Start</Button>
<Button>Stop</Button>
</div>
</div>
// AFTER:
<div className="mt-4 grid grid-cols-2 gap-2">
<Button variant="secondary" size="sm" className="text-xs">Logs</Button>
<Button variant="secondary" size="sm" className="text-xs">Export</Button>
<Button variant="secondary" size="sm" className="text-xs">Configure</Button>
<Button variant="primary" size="sm" className="text-xs">Start</Button>
<Button variant="secondary" size="sm" className="text-xs" disabled={!crowdsecStatus?.running}>Stop</Button>
</div>
Issue #10: /security/crowdsec Blank Page
Problem: The CrowdSecConfig page renders blank with no header/sidebar.
Root Cause: The route /security/crowdsec exists and is inside the Layout wrapper in App.tsx. However, in CrowdSecConfig.tsx, the early return if (!status) return <div>Loading...</div> renders a plain div without proper styling.
Fix: Ensure loading states are styled consistently.
// CrowdSecConfig.tsx (line ~75)
// BEFORE:
if (!status) return <div className="p-8 text-center">Loading...</div>
// This is fine - the issue might be elsewhere. Check if the query is failing silently.
// Add error handling:
const { data: status, isLoading, error } = useQuery({ queryKey: ['security-status'], queryFn: getSecurityStatus })
if (isLoading) return <div className="p-8 text-center text-white">Loading CrowdSec configuration...</div>
if (error) return <div className="p-8 text-center text-red-500">Failed to load security status</div>
if (!status) return <div className="p-8 text-center text-gray-400">No security status available</div>
Issue #11: Sidebar Reorganization
Current Structure:
- Users (/users)
- Settings
- System
- Email (SMTP)
- Account
New Structure:
- Settings
- System
- Email (SMTP)
- Accounts (/settings/accounts)
- Account Management (/settings/accounts/management)
Changes Required:
- Layout.tsx - Update navigation array:
// Remove standalone Users item
// Update Settings children:
{
name: 'Settings',
path: '/settings',
icon: '⚙️',
children: [
{ name: 'System', path: '/settings/system', icon: '⚙️' },
{ name: 'Email (SMTP)', path: '/settings/smtp', icon: '📧' },
{ name: 'Accounts', path: '/settings/accounts', icon: '🛡️' },
{ name: 'Account Management', path: '/settings/account/management', icon: '👥' },
]
}
- App.tsx - Update routes:
// Remove: <Route path="users" element={<UsersPage />} />
// Add under settings:
<Route path="settings/account-management" element={<UsersPage />} />
Issue #12: Missing ACL Loading Overlay
Problem: When adding/removing ACL from proxy host (single or bulk), no loading overlay appears during Caddy reload.
Current Code Analysis: ProxyHosts.tsx uses ConfigReloadOverlay but the overlay condition checks:
const isApplyingConfig = isCreating || isUpdating || isDeleting || isBulkUpdating
Analysis: The isBulkUpdating comes from useProxyHosts hook's bulkUpdateACLMutation.isPending. This should work.
Potential Issue: The single host ACL update uses updateHost(uuid, { access_list_id: id }) which triggers isUpdating. This should also work.
Fix: Verify the overlay is not being hidden by other elements (z-index), and ensure the mutation states are properly connected.
// ProxyHosts.tsx - Verify overlay is at highest z-index
{isApplyingConfig && (
<ConfigReloadOverlay
message={message}
submessage={submessage}
type="charon"
/>
)}
// Also check in ProxyHostForm.tsx for ACL changes within the form
// The AccessListSelector onChange should trigger the same loading state
🏗️ Phase 1: Backend Implementation (Go)
Files to Modify
-
backend/internal/services/uptime_service.go- Add
SyncMonitorForHost(hostID uint)method - Update existing linked monitor's URL, name, and upstream_host
- Add
-
backend/internal/api/handlers/proxy_host_handler.go- In
updateProxyHost, call uptime sync after successful update
- In
New Method in uptime_service.go
// SyncMonitorForHost updates the uptime monitor linked to a specific proxy host
func (s *UptimeService) SyncMonitorForHost(hostID uint) error {
var host models.ProxyHost
if err := s.DB.First(&host, hostID).Error; err != nil {
return err
}
var monitor models.UptimeMonitor
if err := s.DB.Where("proxy_host_id = ?", hostID).First(&monitor).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// No monitor exists, nothing to sync
return nil
}
return err
}
// Update monitor fields
domains := strings.Split(host.DomainNames, ",")
firstDomain := strings.TrimSpace(domains[0])
scheme := "http"
if host.SSLForced {
scheme = "https"
}
monitor.Name = host.Name
if monitor.Name == "" {
monitor.Name = firstDomain
}
monitor.URL = fmt.Sprintf("%s://%s", scheme, firstDomain)
monitor.UpstreamHost = host.ForwardHost
return s.DB.Save(&monitor).Error
}
Handler modification in proxy_host_handler.go
// In UpdateProxyHost handler, after successful save:
func (h *ProxyHostHandler) UpdateProxyHost(c *gin.Context) {
// ... existing code ...
if err := h.DB.Save(&host).Error; err != nil {
// ... error handling ...
}
// Sync associated uptime monitor
if h.UptimeService != nil {
if err := h.UptimeService.SyncMonitorForHost(host.ID); err != nil {
logger.Log().WithError(err).Warn("Failed to sync uptime monitor for host")
// Don't fail the request, just log the warning
}
}
// ... rest of handler ...
}
🎨 Phase 2: Frontend Implementation (React)
Files to Modify
| File | Changes |
|---|---|
frontend/src/index.css |
Remove zoom: 0.75 |
frontend/src/components/Layout.tsx |
Mobile header, sidebar nav, banner sizing |
frontend/src/components/CertificateList.tsx |
Fix delete action visibility |
frontend/src/pages/Notifications.tsx |
Fix template dropdown styling, UX flow |
frontend/src/pages/Security.tsx |
Responsive CrowdSec card buttons |
frontend/src/pages/CrowdSecConfig.tsx |
Fix loading/error states |
frontend/src/App.tsx |
Route reorganization |
Implementation Priority
-
Critical (Broken functionality):
- Issue #10: CrowdSec blank page
- Issue #4: Notification dropdown text invisible
- Issue #12: ACL loading overlay
-
High (Poor UX):
- Issue #1: Uptime card not syncing
- Issue #2: Certificate delete missing
- Issue #3: Inconsistent sizing
-
Medium (Polish):
- Issues #6-8: Mobile header/banner
- Issue #9: CrowdSec button truncation
- Issue #11: Sidebar reorganization
-
Low (Enhancement):
- Issue #5: Template UX redesign
🕵️ Phase 3: QA & Security
Test Scenarios
-
Uptime Sync:
- Edit proxy host name → Verify uptime card updates
- Edit proxy host domain → Verify uptime URL updates
- Delete proxy host with uptime → Verify cleanup
-
Certificate Delete:
- Delete custom certificate (not in use)
- Attempt delete certificate in use → Expect error
- Delete staging certificate
-
Responsive Testing:
- Access via localhost:8080 vs domain
- Mobile viewport (< 768px)
- Tablet viewport (768px - 1024px)
- Desktop viewport (> 1024px)
-
ACL Operations:
- Add ACL to single host → Verify overlay
- Remove ACL from single host → Verify overlay
- Bulk add ACL → Verify overlay with progress
- Bulk remove ACL → Verify overlay
-
CrowdSec Page:
- Navigate to /security/crowdsec directly
- Navigate via Security dashboard button
- Verify header/sidebar present
-
Sidebar Navigation:
- Verify Users moved under Settings
- Verify old /users route redirects or 404s appropriately
- Verify Account Management accessible
📚 Phase 4: Documentation
Files to Update
docs/features.md- Update if any new features added- Component JSDoc comments for modified files
Implementation Sequence
1. Frontend Dev: Fix CSS zoom issue (#3) - Quick win, affects all users
2. Frontend Dev: Fix notification dropdown (#4) - High visibility bug
3. Frontend Dev: Fix CrowdSec page (#10) - Route/layout issue
4. Backend Dev: Implement uptime sync on proxy host edit (#1)
5. Frontend Dev: Fix certificate delete visibility (#2)
6. Frontend Dev: Verify ACL operation loading overlay (#12)
7. Frontend Dev: Mobile header/banner fixes (#6, #7, #8)
8. Frontend Dev: CrowdSec card responsive buttons (#9)
9. Frontend Dev: Sidebar reorganization (#11)
10. Frontend Dev: Notification template UX improvement (#5)
11. QA and Security: Test all changes
Subagent Assignments
| Agent | Tasks |
|---|---|
| Backend Dev | Issue #1 (uptime sync on proxy host edit) |
| Frontend Dev | Issues #2, #3, #4, #5, #6, #7, #8, #9, #10, #11, #12 |
| QA and Security | All verification testing |
| Doc Writer | Update docs/features.md if needed |