8.9 KiB
RFCP - Iteration 10.2: Purple → Orange Gradient
Goal: Replace current warm palette (maroon→yellow) with purple→orange palette for better map contrast and intuitive UX
Priority: P2 (UX Polish)
Estimated Time: 15-20 minutes
Status: Ready for Implementation
📋 Overview
After Iteration 10.1 fixed the cyan/green color conflict, user feedback indicated the current warm palette (yellow=good, red=bad) feels counter-intuitive. This iteration implements a purple→orange gradient that:
- Avoids map feature conflicts (no purple on maps)
- Provides intuitive "heat" visualization (orange = hot/strong signal)
- Maintains professional RF tool aesthetic
🐛 Current Issue
User Feedback: "тепер зона червона, а градієнт жовтий :) хз навіть як краще було"
Problem:
- Yellow (#ffeb3b) = excellent signal → near antenna center
- Red/Maroon (#8b0000) = weak signal → coverage edges
- This feels counter-intuitive (users expect red = danger/bad)
Current Palette (Iteration 10.1):
Deep Maroon → Red → Orange → Yellow
(weak) ←──────────────→ (strong)
#4a0000 → #8b0000 → #ff9800 → #ffeb3b
✅ Solution: Purple → Orange Palette
New Palette:
Deep Purple → Purple → Lavender → Peach → Orange → Bright Orange
(weak) ←────────────────────────────────────→ (strong)
Color Values:
export function createRsrpGradient(): string[] {
return [
'#1a0033', // Deep purple (very poor signal, -130 dBm)
'#4a148c', // Dark purple
'#7b1fa2', // Purple
'#ab47bc', // Light purple / Lavender
'#ff8a65', // Peach / Light orange
'#ff6f00', // Dark orange
'#ffb74d', // Bright orange (excellent signal, -50 dBm)
];
}
Why Purple → Orange:
- ✅ No map conflicts: Purple doesn't appear on OpenTopoMap or OpenStreetMap
- ✅ Intuitive heat: Orange = "hot" = strong signal (like thermal imaging)
- ✅ Professional aesthetic: Used in professional RF planning tools
- ✅ Good contrast: Purple/Orange highly distinguishable
- ✅ Colorblind-friendly: Purple-orange works for most color vision types
📁 Files to Modify
1. src/utils/colorGradient.ts
Current Code (lines ~5-15):
export function createRsrpGradient(): string[] {
return [
'#4a0000', // Deep maroon (very poor signal)
'#8b0000', // Dark red
'#cc0000', // Red
'#ff0000', // Bright red
'#ff4444', // Orange-red
'#ff9800', // Orange
'#ffeb3b', // Yellow (excellent signal)
];
}
Replace With:
export function createRsrpGradient(): string[] {
return [
'#1a0033', // Deep purple (very poor signal, -130 dBm)
'#4a148c', // Dark purple
'#7b1fa2', // Purple
'#ab47bc', // Light purple / Lavender
'#ff8a65', // Peach / Light orange
'#ff6f00', // Dark orange
'#ffb74d', // Bright orange (excellent signal, -50 dBm)
];
}
2. src/constants/rsrp-thresholds.ts
Find and Update SIGNAL_COLORS:
// Old:
export const SIGNAL_COLORS = {
excellent: '#ffeb3b', // Yellow
good: '#ff9800', // Orange
fair: '#ff4444', // Orange-red
poor: '#ff0000', // Red
veryPoor: '#cc0000', // Dark red
terrible: '#8b0000', // Very dark red
};
// New:
export const SIGNAL_COLORS = {
excellent: '#ffb74d', // Bright orange (-50 to -70 dBm)
good: '#ff6f00', // Dark orange (-70 to -85 dBm)
fair: '#ff8a65', // Peach (-85 to -100 dBm)
poor: '#ab47bc', // Light purple (-100 to -110 dBm)
veryPoor: '#7b1fa2', // Purple (-110 to -120 dBm)
terrible: '#4a148c', // Dark purple (-120 to -130 dBm)
};
3. Check Legend Component (if exists)
Search for hardcoded colors:
grep -rn "#ffeb3b\|#ff9800\|#8b0000" src/
If found in legend or other components, update to match new palette.
🔍 Investigation Commands
Before making changes, verify file locations:
# Find gradient file
find /opt/rfcp/frontend/src -name "colorGradient.ts" -o -name "*gradient*"
# Find threshold constants
find /opt/rfcp/frontend/src -name "*threshold*" -o -name "*rsrp*"
# Check for hardcoded old colors
grep -rn "#ffeb3b\|#4a0000\|#8b0000" /opt/rfcp/frontend/src/
# Check legend component
find /opt/rfcp/frontend/src -name "*Legend*" -o -name "*legend*"
🧪 Testing Checklist
Visual Testing
- Gradient smoothness: Colors transition smoothly from purple to orange
- Center coverage: Bright orange (#ffb74d) visible near antenna
- Edge coverage: Deep purple (#1a0033) visible at coverage edges
- Map contrast: No color conflicts with OpenTopoMap features
- Different zoom levels: Gradient looks correct at zoom 8, 12, 16
Legend Testing
- Legend colors match: RSRP legend shows new color scheme
- Labels correct: dBm values still display correctly
- Readability: Legend text readable against new colors
Regression Testing
- 50m resolution: Still works without crash (Iteration 10.1 fix intact)
- Delete confirmation: Still shows dialog (Iteration 10.1 fix intact)
- Keyboard shortcuts: All shortcuts still functional
- Multi-sector: Coverage calculation still correct
Edge Cases
- Single site: Gradient displays correctly
- Multiple sites: Overlapping coverage shows strongest signal
- Terrain enabled: Gradient still visible with terrain layer
- Dark mode: Colors work in dark mode (if applicable)
🏗️ Build & Deploy
# Navigate to frontend
cd /opt/rfcp/frontend
# Install dependencies (if needed)
npm install
# Run development server to test
npm run dev
# Build for production
npm run build
# Check for errors
# Expected: 0 TypeScript errors, 0 ESLint errors
# Deploy (reload Caddy)
sudo systemctl reload caddy
# Verify deployment
curl -s https://rfcp.eliah.one | head -20
📝 Commit Message
feat(heatmap): replace warm palette with purple→orange gradient
- Changed RSRP gradient from maroon→yellow to purple→orange
- Updated SIGNAL_COLORS constants to match new palette
- Better map contrast (purple not present on maps)
- More intuitive heat visualization (orange = strong signal)
Colors: #1a0033 (weak) → #ffb74d (strong)
Iteration: 10.2
Fixes: User feedback on gradient aesthetics
✅ Success Criteria
Must Pass:
- ✅ Gradient displays purple (weak) → orange (strong)
- ✅ No cyan, green, or blue colors anywhere
- ✅ Legend matches new color scheme
- ✅ No regression in 50m resolution performance
- ✅ TypeScript: 0 errors
- ✅ ESLint: 0 errors
- ✅ Production build succeeds
User Acceptance:
- Олег confirms: "виглядає краще" or "норм"
- If not satisfied → Iteration 10.3 with alternative palette
🎨 Alternative Palettes (If Needed)
Option C: Grayscale → Orange (Fallback)
return [
'#1a1a1a', // Near black (very poor)
'#4a4a4a', // Dark gray
'#808080', // Medium gray
'#b3b3b3', // Light gray
'#ff6f00', // Dark orange
'#ff9800', // Orange
'#ffb74d', // Light orange (excellent)
];
Option D: Blue → Red (Classic Heat)
return [
'#0d47a1', // Dark blue (very poor)
'#1976d2', // Blue
'#42a5f5', // Light blue
'#fff176', // Yellow
'#ff9800', // Orange
'#f44336', // Red
'#b71c1c', // Dark red (excellent)
];
📊 Color Reference Table
| RSRP Range | Signal Quality | Old Color | New Color | Hex |
|---|---|---|---|---|
| > -70 dBm | Excellent | Yellow | Bright Orange | #ffb74d |
| -70 to -85 | Good | Orange | Dark Orange | #ff6f00 |
| -85 to -100 | Fair | Orange-Red | Peach | #ff8a65 |
| -100 to -110 | Poor | Red | Light Purple | #ab47bc |
| -110 to -120 | Very Poor | Dark Red | Purple | #7b1fa2 |
| < -120 dBm | Terrible | Maroon | Dark Purple | #4a148c |
| < -130 dBm | No Service | Deep Maroon | Deep Purple | #1a0033 |
🤖 Instructions for Claude Code
Context:
- Project: RFCP (RF Coverage Planning) -
/opt/rfcp/frontend/ - Framework: React 18 + TypeScript + Vite + Leaflet
- This is a simple color palette change in 2-3 files
Implementation Steps:
- Search for
colorGradient.tsand update gradient array - Search for
rsrp-thresholds.tsand update SIGNAL_COLORS - Search for any hardcoded old colors and update them
- Run
npm run buildto verify no errors - Test at https://rfcp.eliah.one after deploy
Priority: Quick UX fix, should take ~15 minutes
Success: User sees purple (weak) → orange (strong) gradient on map
Document Created: 2025-01-30
Author: Claude (Sonnet 4.5) + Олег
Status: Ready for Implementation
Next: Віддати Claude Code → Test → Screenshot → Confirm