# RFCP - Iteration 1: Fixes & Features
## Comprehensive Task for Claude Code

Read RFCP-Fixes-Iteration1.md and RFCP-TechSpec-v3.0.md for context.

---

## CRITICAL FIXES (Priority: HIGH)

### 1. Heatmap Color Gradient - More Obvious
**Current:** Green → Yellow → Red (not very distinct)
**New:** Blue → Cyan → Green → Yellow → Orange → Red

**File:** `frontend/src/components/map/Heatmap.tsx`

```typescript
// Update gradient in HeatmapLayer component:
gradient={{
  0.0: '#0d47a1',  // Dark Blue (very weak, -120 dBm)
  0.2: '#00bcd4',  // Cyan (weak, -110 dBm)
  0.4: '#4caf50',  // Green (fair, -100 dBm)
  0.6: '#ffeb3b',  // Yellow (good, -85 dBm)
  0.8: '#ff9800',  // Orange (strong, -70 dBm)
  1.0: '#f44336',  // Red (excellent, > -70 dBm)
}}
```

**Reasoning:** 
- Cold colors (blue/cyan) = weak signal
- Warm colors (yellow/orange/red) = strong signal
- More intuitive for RF planning
- Better contrast and visibility

---

### 2. Coverage Radius - Increase to 100km
**Current:** Max 20km
**New:** Max 100km with larger step

**File:** `frontend/src/components/panels/SiteForm.tsx` or wherever Coverage Settings panel is

```typescript
// Find the radius slider and update:
<Slider
  label="Radius"
  min={1}
  max={100}      // ← Changed from 20
  step={5}       // ← Changed from 1 (easier control)
  value={coverageSettings.radius}
  onChange={(value) => updateCoverageSettings({ radius: value })}
  suffix="km"
  help="Calculation area around each site"
/>
```

**Also update default value:**

**File:** `frontend/src/store/coverage.ts` (or wherever initial settings are)

```typescript
const defaultSettings: CoverageSettings = {
  radius: 10,        // Changed from 20 to 10 (better default)
  resolution: 200,   // 200m
  rsrpThreshold: -120
};
```

---

### 3. "Save & Calculate" Button - Primary Action
**Current:** Separate "Save" and "Calculate" buttons
**New:** "Save & Calculate" as primary action + "Save Only" as secondary

**File:** `frontend/src/components/panels/SiteForm.tsx`

```typescript
// Replace button section:
<div className="flex gap-2">
  <Button 
    onClick={handleSaveAndCalculate}
    variant="primary"
    className="flex-1"
  >
    💾 Save & Calculate
  </Button>
  
  <Button 
    onClick={handleSave}
    variant="secondary"
  >
    💾 Save Only
  </Button>
  
  <Button 
    onClick={handleDelete} 
    variant="danger"
  >
    🗑️
  </Button>
</div>

// Add handler function:
const handleSaveAndCalculate = async () => {
  try {
    // Save the site first
    await handleSave();
    
    // Get all sites from store
    const sites = useSitesStore.getState().sites;
    
    if (sites.length === 0) {
      toast.info('Add at least one site to calculate coverage');
      return;
    }
    
    // Trigger coverage calculation
    const coverageStore = useCoverageStore.getState();
    await coverageStore.calculateCoverage(sites);
    
    // Success feedback
    toast.success(`Coverage calculated for ${sites.length} site(s)!`);
  } catch (error) {
    toast.error(`Failed: ${error.message}`);
  }
};
```

---

### 4. Legend Colors - Match New Gradient
**File:** `frontend/src/components/map/Legend.tsx`

```typescript
const signalRanges = [
  { 
    label: 'Excellent', 
    range: '> -70 dBm', 
    color: '#f44336',      // Red
    description: 'Very strong signal'
  },
  { 
    label: 'Good', 
    range: '-70 to -85 dBm', 
    color: '#ff9800',      // Orange
    description: 'Strong signal'
  },
  { 
    label: 'Fair', 
    range: '-85 to -100 dBm', 
    color: '#ffeb3b',      // Yellow
    description: 'Acceptable signal'
  },
  { 
    label: 'Poor', 
    range: '-100 to -110 dBm', 
    color: '#4caf50',      // Green
    description: 'Weak signal'
  },
  { 
    label: 'Weak', 
    range: '-110 to -120 dBm', 
    color: '#00bcd4',      // Cyan
    description: 'Very weak signal'
  },
  { 
    label: 'No Service', 
    range: '< -120 dBm', 
    color: '#0d47a1',      // Dark Blue
    description: 'No coverage'
  },
];
```

**Also update constants:**

**File:** `frontend/src/constants/rsrp-thresholds.ts`

```typescript
export const SIGNAL_COLORS = {
  excellent: '#f44336',  // Red
  good: '#ff9800',       // Orange
  fair: '#ffeb3b',       // Yellow
  poor: '#4caf50',       // Green
  weak: '#00bcd4',       // Cyan
  'no-service': '#0d47a1' // Dark Blue
} as const;
```

---

## UI IMPROVEMENTS (Priority: HIGH)

### 5. Dark Theme Support 🌙
**Add complete dark mode with toggle**

**Files to create/update:**
- `frontend/src/store/settings.ts` - theme state
- `frontend/src/components/ui/ThemeToggle.tsx` - toggle button
- `frontend/tailwind.config.js` - dark mode config
- `frontend/src/index.css` - dark theme styles

**Implementation:**

**A) Tailwind Config:**

```javascript
// tailwind.config.js
export default {
  darkMode: 'class', // Enable class-based dark mode
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {
      colors: {
        // Custom colors for dark mode
        dark: {
          bg: '#1a1a1a',
          surface: '#2d2d2d',
          border: '#404040',
          text: '#e0e0e0',
        }
      }
    },
  },
  plugins: [],
}
```

**B) Settings Store:**

```typescript
// src/store/settings.ts (create if doesn't exist)
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface SettingsState {
  theme: 'light' | 'dark' | 'system';
  setTheme: (theme: 'light' | 'dark' | 'system') => void;
}

export const useSettingsStore = create<SettingsState>()(
  persist(
    (set) => ({
      theme: 'system',
      setTheme: (theme) => {
        set({ theme });
        applyTheme(theme);
      },
    }),
    {
      name: 'rfcp-settings',
    }
  )
);

function applyTheme(theme: 'light' | 'dark' | 'system') {
  const root = window.document.documentElement;
  
  if (theme === 'system') {
    const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches 
      ? 'dark' 
      : 'light';
    root.classList.toggle('dark', systemTheme === 'dark');
  } else {
    root.classList.toggle('dark', theme === 'dark');
  }
}

// Apply theme on page load
if (typeof window !== 'undefined') {
  const stored = localStorage.getItem('rfcp-settings');
  if (stored) {
    const { theme } = JSON.parse(stored);
    applyTheme(theme);
  }
}
```

**C) Theme Toggle Component:**

```typescript
// src/components/ui/ThemeToggle.tsx
import { useSettingsStore } from '@/store/settings';

export function ThemeToggle() {
  const { theme, setTheme } = useSettingsStore();
  
  const icons = {
    light: '☀️',
    dark: '🌙',
    system: '💻'
  };
  
  return (
    <div className="flex items-center gap-1 bg-gray-100 dark:bg-dark-surface rounded-lg p-1">
      {(['light', 'dark', 'system'] as const).map((t) => (
        <button
          key={t}
          onClick={() => setTheme(t)}
          className={`
            px-3 py-1.5 rounded text-sm transition-all
            ${theme === t 
              ? 'bg-white dark:bg-dark-bg shadow-sm' 
              : 'hover:bg-gray-200 dark:hover:bg-dark-border'
            }
          `}
          title={t.charAt(0).toUpperCase() + t.slice(1)}
        >
          {icons[t]}
        </button>
      ))}
    </div>
  );
}
```

**D) Update App.tsx:**

```typescript
// Add ThemeToggle to header
import { ThemeToggle } from '@/components/ui/ThemeToggle';

function App() {
  return (
    <div className="h-screen flex flex-col bg-white dark:bg-dark-bg">
      <header className="bg-slate-800 dark:bg-slate-900 text-white px-4 py-3 flex items-center justify-between">
        <h1 className="text-xl font-bold">RFCP - RF Coverage Planner</h1>
        <ThemeToggle />
      </header>
      {/* rest of app */}
    </div>
  );
}
```

**E) Dark Mode Styles for Components:**

Update all major components with dark mode classes:

```typescript
// Example pattern:
<div className="bg-white dark:bg-dark-surface text-gray-900 dark:text-dark-text">
  {/* content */}
</div>

// Borders:
className="border border-gray-200 dark:border-dark-border"

// Inputs:
className="bg-white dark:bg-dark-bg border-gray-300 dark:border-dark-border"

// Buttons:
className="bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700"
```

**Apply dark mode to:**
- Map panels (SiteForm, SiteList, Coverage Settings)
- Legend component
- Toast notifications
- Modal overlays
- Input fields and sliders

---

### 6. Sites List - Show More Info
**Current:** Just site name
**New:** Show key parameters inline

**File:** `frontend/src/components/panels/SiteList.tsx`

```typescript
// Update site item rendering:
<div className="site-item p-3 border-b hover:bg-gray-50 dark:hover:bg-dark-surface">
  <div className="flex items-center justify-between">
    <div className="flex items-center gap-3">
      {/* Color indicator */}
      <div 
        className="w-4 h-4 rounded-full border-2 border-white shadow"
        style={{ backgroundColor: site.color }}
      />
      
      {/* Site info */}
      <div>
        <div className="font-medium text-gray-900 dark:text-dark-text">
          {site.name}
        </div>
        <div className="text-sm text-gray-500 dark:text-gray-400">
          📻 {site.frequency} MHz • ⚡ {site.power} dBm • 
          📡 {site.antennaType === 'omni' ? 'Omni' : `Sector ${site.azimuth}°`}
        </div>
      </div>
    </div>
    
    {/* Actions */}
    <div className="flex gap-1">
      <Button onClick={() => onEdit(site.id)} size="sm" variant="ghost">
        Edit
      </Button>
      <Button onClick={() => onDelete(site.id)} size="sm" variant="ghost">
        ×
      </Button>
    </div>
  </div>
</div>
```

---

### 7. Keyboard Shortcuts
**Add useful shortcuts for power users**

**File:** `frontend/src/App.tsx` or create `src/hooks/useKeyboardShortcuts.ts`

```typescript
// Create hook:
// src/hooks/useKeyboardShortcuts.ts
import { useEffect } from 'react';
import { useSitesStore } from '@/store/sites';
import { useCoverageStore } from '@/store/coverage';
import { toast } from '@/components/ui/Toast';

export function useKeyboardShortcuts() {
  useEffect(() => {
    const handleKeyPress = (e: KeyboardEvent) => {
      // Ignore if typing in input
      if (e.target instanceof HTMLInputElement || 
          e.target instanceof HTMLTextAreaElement) {
        return;
      }
      
      const isMac = navigator.platform.toLowerCase().includes('mac');
      const modKey = isMac ? e.metaKey : e.ctrlKey;
      
      if (modKey) {
        switch(e.key.toLowerCase()) {
          case 's': // Ctrl/Cmd+S: Save current site
            e.preventDefault();
            const selectedSite = useSitesStore.getState().selectedSite;
            if (selectedSite) {
              useSitesStore.getState().updateSite(selectedSite.id, selectedSite);
              toast.success('Site saved');
            }
            break;
            
          case 'enter': // Ctrl/Cmd+Enter: Calculate coverage
            e.preventDefault();
            const sites = useSitesStore.getState().sites;
            if (sites.length > 0) {
              useCoverageStore.getState().calculateCoverage(sites);
            } else {
              toast.info('Add sites first');
            }
            break;
            
          case 'n': // Ctrl/Cmd+N: New site (enter placement mode)
            e.preventDefault();
            useSitesStore.getState().setPlacementMode(true);
            toast.info('Click on map to place new site');
            break;
        }
      }
      
      // Non-modifier shortcuts
      switch(e.key) {
        case 'Escape': // Escape: Cancel/close
          useSitesStore.getState().setSelectedSite(null);
          useSitesStore.getState().setPlacementMode(false);
          break;
          
        case 'h': // H: Toggle heatmap
          useCoverageStore.getState().toggleHeatmap();
          break;
      }
    };
    
    window.addEventListener('keydown', handleKeyPress);
    return () => window.removeEventListener('keydown', handleKeyPress);
  }, []);
}

// Use in App.tsx:
function App() {
  useKeyboardShortcuts();
  // ...
}
```

**Add keyboard shortcuts help:**

```typescript
// Add to header or settings:
<button 
  onClick={() => setShowShortcuts(true)}
  className="text-sm text-gray-500 hover:text-gray-700"
  title="Keyboard shortcuts"
>
  ⌨️
</button>

{showShortcuts && (
  <div className="modal">
    <h3>Keyboard Shortcuts</h3>
    <ul>
      <li><kbd>Ctrl/Cmd</kbd> + <kbd>S</kbd> - Save site</li>
      <li><kbd>Ctrl/Cmd</kbd> + <kbd>Enter</kbd> - Calculate coverage</li>
      <li><kbd>Ctrl/Cmd</kbd> + <kbd>N</kbd> - New site</li>
      <li><kbd>Esc</kbd> - Cancel/Close</li>
      <li><kbd>H</kbd> - Toggle heatmap</li>
    </ul>
  </div>
)}
```

---

### 8. Map Controls - Fit to Sites & Reset View
**Add helpful map navigation buttons**

**File:** `frontend/src/components/map/Map.tsx`

```typescript
// Add control buttons:
<div className="absolute top-4 right-4 z-[1000] flex flex-col gap-2">
  {/* Fit to sites */}
  <button
    onClick={handleFitToSites}
    className="
      bg-white dark:bg-dark-surface shadow-lg rounded px-3 py-2
      hover:bg-gray-50 dark:hover:bg-dark-border
      transition-colors
    "
    title="Fit view to all sites"
    disabled={sites.length === 0}
  >
    🎯 Fit
  </button>
  
  {/* Reset to Ukraine */}
  <button
    onClick={handleResetView}
    className="
      bg-white dark:bg-dark-surface shadow-lg rounded px-3 py-2
      hover:bg-gray-50 dark:hover:bg-dark-border
      transition-colors
    "
    title="Reset to Ukraine view"
  >
    🏠 Reset
  </button>
</div>

// Handlers:
const handleFitToSites = () => {
  if (sites.length === 0) return;
  
  const bounds = sites.map(site => [site.lat, site.lon] as [number, number]);
  mapRef.current?.fitBounds(bounds, { padding: [50, 50] });
};

const handleResetView = () => {
  mapRef.current?.setView([48.4, 35.0], 6); // Ukraine center
};
```

---

### 9. Better Error Handling
**Add comprehensive error handling with helpful messages**

**File:** `frontend/src/store/coverage.ts`

```typescript
calculateCoverage: async (sites: Site[]) => {
  if (sites.length === 0) {
    toast.error('No sites to calculate');
    return;
  }
  
  set({ isCalculating: true, error: null });
  
  try {
    const settings = get().settings;
    
    // Validation
    if (settings.radius > 100) {
      throw new Error('Radius too large (max 100km)');
    }
    
    if (settings.resolution < 50) {
      throw new Error('Resolution too fine (min 50m)');
    }
    
    // Calculate
    const result = await calculator.calculateCoverage(sites, bounds, settings);
    
    if (result.points.length === 0) {
      toast.warning('No coverage points found. Try increasing radius or lowering threshold.');
    } else {
      toast.success(`Calculated ${result.points.length.toLocaleString()} points in ${(result.calculationTime / 1000).toFixed(1)}s`);
    }
    
    set({ 
      coveragePoints: result.points,
      isCalculating: false 
    });
    
  } catch (error) {
    console.error('Coverage calculation error:', error);
    
    // User-friendly error messages
    let message = 'Calculation failed';
    
    if (error.message.includes('timeout')) {
      message = 'Calculation timeout. Try reducing radius or increasing resolution.';
    } else if (error.message.includes('worker')) {
      message = 'Web Worker error. Please refresh the page.';
    } else if (error.message.includes('memory')) {
      message = 'Out of memory. Try smaller radius or coarser resolution.';
    }
    
    toast.error(message);
    set({ 
      error: error.message, 
      isCalculating: false 
    });
  }
}
```

---

### 10. Mobile Responsiveness Improvements
**Ensure panels don't block map on mobile**

**Files:** `SiteForm.tsx`, `SiteList.tsx`, `Legend.tsx`

```typescript
// Pattern for mobile-friendly panels:
<div className="
  fixed bottom-0 left-0 right-0        // Mobile: bottom sheet
  md:static md:w-96                    // Desktop: side panel
  bg-white dark:bg-dark-surface 
  shadow-lg rounded-t-lg md:rounded-lg
  z-[1000] md:z-auto
  max-h-[70vh] md:max-h-none           // Mobile: don't cover whole screen
  overflow-y-auto
  transform transition-transform
  ${isOpen ? 'translate-y-0' : 'translate-y-full md:translate-y-0'}
">
  {/* Panel content */}
</div>

// Add drag handle for mobile:
<div className="md:hidden flex justify-center p-2">
  <div className="w-12 h-1 bg-gray-300 rounded-full" />
</div>
```

**Touch-friendly buttons:**
```typescript
// Minimum 44x44px touch targets
<button className="
  min-w-[44px] min-h-[44px]
  px-4 py-2
  text-base md:text-sm
">
  {/* button content */}
</button>
```

---

## OPTIONAL ENHANCEMENTS (if time permits)

### 11. Calculation Stats Display
**Show useful statistics after calculation**

**File:** `frontend/src/components/map/Legend.tsx`

```typescript
{coveragePoints.length > 0 && (
  <div className="mt-4 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg text-sm">
    <h4 className="font-semibold mb-2">📊 Coverage Statistics</h4>
    <div className="space-y-1 text-gray-700 dark:text-gray-300">
      <div>Points: {coveragePoints.length.toLocaleString()}</div>
      <div>Calculation: {(calculationTime / 1000).toFixed(2)}s</div>
      <div>Coverage area: ~{calculateArea()} km²</div>
      <div>Sites: {sites.length}</div>
    </div>
  </div>
)}
```

---

### 12. Templates Verification
**Ensure quick templates work correctly**

**File:** `frontend/src/components/panels/SiteForm.tsx`

```typescript
const QUICK_TEMPLATES = {
  limesdr: {
    name: 'LimeSDR Mini',
    power: 20,
    gain: 2,
    frequency: 1800,
    height: 10,
    antennaType: 'omni' as const
  },
  'low-bbu': {
    name: 'Low Power BBU',
    power: 40,
    gain: 8,
    frequency: 1800,
    height: 20,
    antennaType: 'omni' as const
  },
  'high-bbu': {
    name: 'High Power BBU',
    power: 43,
    gain: 15,
    frequency: 1800,
    height: 30,
    antennaType: 'sector' as const,
    azimuth: 0,
    beamwidth: 65
  }
};

const applyTemplate = (templateId: keyof typeof QUICK_TEMPLATES) => {
  const template = QUICK_TEMPLATES[templateId];
  
  // Apply ALL fields
  setFormData({
    ...formData,
    power: template.power,
    gain: template.gain,
    frequency: template.frequency,
    height: template.height,
    antennaType: template.antennaType,
    azimuth: template.azimuth || 0,
    beamwidth: template.beamwidth || 65
  });
  
  toast.success(`Applied: ${template.name}`);
};
```

---

## TESTING CHECKLIST

After implementation, verify:

### Visual Tests:
- [ ] Dark theme looks good on all components
- [ ] New heatmap gradient is clearly visible (blue=weak, red=strong)
- [ ] Legend matches heatmap colors
- [ ] Sites list shows frequency, power, antenna type
- [ ] Mobile panels don't block map completely

### Functional Tests:
- [ ] "Save & Calculate" button works (saves + calculates)
- [ ] "Save Only" button still works
- [ ] Coverage radius slider goes to 100km
- [ ] Large radius (50-100km) calculates without errors
- [ ] Dark/Light/System theme toggle works
- [ ] Theme persists after page refresh

### Keyboard Shortcuts:
- [ ] Ctrl/Cmd+S saves current site
- [ ] Ctrl/Cmd+Enter calculates coverage
- [ ] Ctrl/Cmd+N enters placement mode
- [ ] Esc cancels/closes
- [ ] H toggles heatmap visibility

### Mobile Tests (if possible):
- [ ] Panels slide up from bottom on mobile
- [ ] Touch targets are large enough (44x44px)
- [ ] Sliders work with touch
- [ ] Map can be panned/zoomed on mobile

### Error Handling:
- [ ] Helpful error messages for calculation failures
- [ ] Toast notifications for all actions
- [ ] No console errors

---

## IMPLEMENTATION NOTES

1. **Start with visual changes** (colors, legend) - quick wins
2. **Then functional changes** (button, radius, dark theme)
3. **Then UX improvements** (shortcuts, mobile, errors)
4. **Test thoroughly** - especially dark theme across all components

**Performance Note:**
Current calculation is instant because it's only FSPL without terrain data.
When Phase 4 (30m terrain) is implemented, large radius will take longer.
Performance warnings can be added then.

---

## FILES TO CREATE/MODIFY

### New Files:
- `frontend/src/store/settings.ts` - theme management
- `frontend/src/components/ui/ThemeToggle.tsx` - theme switcher
- `frontend/src/hooks/useKeyboardShortcuts.ts` - keyboard shortcuts

### Modified Files:
- `frontend/tailwind.config.js` - dark mode config
- `frontend/src/index.css` - dark theme CSS variables
- `frontend/src/App.tsx` - add ThemeToggle, use shortcuts hook
- `frontend/src/components/map/Heatmap.tsx` - new gradient
- `frontend/src/components/map/Legend.tsx` - new colors + stats
- `frontend/src/components/map/Map.tsx` - Fit/Reset buttons
- `frontend/src/components/panels/SiteForm.tsx` - Save&Calculate, radius
- `frontend/src/components/panels/SiteList.tsx` - more info display
- `frontend/src/store/coverage.ts` - better error handling, defaults
- `frontend/src/constants/rsrp-thresholds.ts` - new color constants
- All UI components - add dark mode classes

---

## SUCCESS CRITERIA

✅ New heatmap gradient is clearly visible
✅ Dark theme works flawlessly
✅ Coverage radius goes to 100km
✅ "Save & Calculate" is primary action
✅ Keyboard shortcuts work
✅ Mobile responsive
✅ Better error messages
✅ No TypeScript errors
✅ All existing features still work

---

**Estimated Time:** 20-30 minutes for full implementation
**Complexity:** Medium (mostly UI updates + new theme system)
**Risk:** Low (additive changes, not breaking existing functionality)

Good luck! 🚀
