Files
rfcp/RFCP-Iteration10.2-Purple-Orange-Gradient.md
2026-01-30 17:02:21 +02:00

8.9 KiB
Raw Blame History

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:

  1. Gradient displays purple (weak) → orange (strong)
  2. No cyan, green, or blue colors anywhere
  3. Legend matches new color scheme
  4. No regression in 50m resolution performance
  5. TypeScript: 0 errors
  6. ESLint: 0 errors
  7. 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:

  1. Search for colorGradient.ts and update gradient array
  2. Search for rsrp-thresholds.ts and update SIGNAL_COLORS
  3. Search for any hardcoded old colors and update them
  4. Run npm run build to verify no errors
  5. 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