# RFCP - Iteration 10: Final Frontend Audit & Polish

**Goal:** Production-ready frontend - zero critical bugs, clean code, polished UX, comprehensive audit.

**Status:** Ready for Implementation  
**Estimated Time:** 8-12 hours (can split into 2 sessions)

---

## 📋 Overview

This is the **final frontend iteration** before moving to backend work. After this iteration, the frontend should be:
- ✅ Bug-free and stable
- ✅ Well-documented code
- ✅ TypeScript strict mode compliant
- ✅ Performance optimized
- ✅ Production-ready UX

---

## 🎯 Four-Phase Approach

```
Phase 1: Critical Fixes (P1)
    ↓
Phase 2: Code Audit & Refactor (P1)
    ↓
Phase 3: UX Polish (P2)
    ↓
Phase 4: Final Audit & Testing (P1)
```

---

## 📍 Phase 1: Critical Fixes

**Priority:** P1 CRITICAL  
**Time:** ~2-3 hours

### 1.1 Stack Overflow at 50m Resolution

**Problem:** RangeError: Maximum call stack size exceeded

**Investigation:**
```bash
# Check Web Worker for recursion
cat public/workers/rf-worker.js

# Look for patterns:
# - Recursive path loss calculation
# - Infinite loop in terrain processing
# - Deep call stacks in Fresnel calculations
```

**Likely culprits:**
1. **Path Loss calculation** - recursive terrain checks
2. **Fresnel zone** - recursive clearance checks
3. **LOS calculation** - recursive elevation sampling

**Solution pattern:**
```javascript
// Add depth guard to any recursive function
function calculatePathLoss(point, site, depth = 0, maxDepth = 20) {
  if (depth > maxDepth) {
    console.warn('Max recursion depth reached in pathLoss');
    return DEFAULT_PATH_LOSS;
  }
  
  // ... calculation logic
  
  if (needsRecursion) {
    return calculatePathLoss(newPoint, site, depth + 1, maxDepth);
  }
}
```

**Files to check:**
- `public/workers/rf-worker.js`
- `src/lib/calculator.ts` (if has additional logic)
- `src/utils/pathLoss.ts` (if exists)

**Testing:**
```bash
# After fix, test all resolutions:
50m  → ✅ No crash, calculates in <10s
100m → ✅ Works smoothly
200m → ✅ Works smoothly
500m → ✅ Works smoothly

# Monitor console for recursion warnings
```

---

### 1.2 Green Coverage Radius Circle

**Problem:** Green/cyan circle visible, conflicts with RSRP gradient

**Investigation:**
```bash
# Find all Circle components
grep -r "Circle" src/ --include="*.tsx"

# Check for green/cyan colors
grep -r "#00ff00\|#00bcd4\|rgb(0,255,0)" src/

# Check for dashed circles
grep -r "dashArray" src/
```

**Likely locations:**
1. `src/components/map/Map.tsx`
2. `src/components/map/SiteMarker.tsx`
3. `src/components/map/CoverageLayer.tsx`

**Solution A: Remove completely**
```tsx
// Find and DELETE:
{showCalculationRadius && (
  <Circle
    center={[site.lat, site.lon]}
    radius={site.radius * 1000}
    pathOptions={{ color: '#00bcd4', ... }}
  />
)}
```

**Solution B: Change to orange (recommended)**
```tsx
<Circle
  center={[site.lat, site.lon]}
  radius={site.radius * 1000}
  pathOptions={{
    color: '#ff9800',      // Orange - NOT in RSRP gradient
    weight: 2,
    opacity: 0.6,
    dashArray: '10, 5',    // Longer dashes
    fillOpacity: 0
  }}
/>
```

**Solution C: Make toggleable**
```tsx
// In settings panel:
<label>
  <input
    type="checkbox"
    checked={showCalculationBounds}
    onChange={(e) => setShowCalculationBounds(e.target.checked)}
  />
  Show Calculation Bounds
</label>
```

**Recommended:** Solution B (orange) or C (toggleable)

---

### 1.3 Any Other Critical Bugs

**From user testing:** (TBD based on screenshots/reports)

---

## 🔍 Phase 2: Code Audit & Refactor

**Priority:** P1  
**Time:** ~3-4 hours

### 2.1 TypeScript Strict Mode

**Enable strict type checking:**

**File:** `tsconfig.json`
```json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "alwaysStrict": true
  }
}
```

**Fix all type errors:**
```bash
npm run type-check
# OR
npx tsc --noEmit
```

**Common fixes needed:**

1. **Replace `any` with proper types:**
```typescript
// ❌ Bad
const data: any = response.data;

// ✅ Good
interface ResponseData {
  sites: Site[];
  coverage: CoveragePoint[];
}
const data: ResponseData = response.data;
```

2. **Null checks:**
```typescript
// ❌ Bad
const site = sites.find(s => s.id === id);
site.name = 'New Name'; // Might be undefined!

// ✅ Good
const site = sites.find(s => s.id === id);
if (!site) return;
site.name = 'New Name';
```

3. **Function signatures:**
```typescript
// ❌ Bad
function calculate(sites, settings) { ... }

// ✅ Good
function calculate(
  sites: Site[],
  settings: CoverageSettings
): CoverageResult { ... }
```

**Files to audit:**
- `src/store/*.ts` - Zustand stores
- `src/lib/*.ts` - Utilities
- `src/components/**/*.tsx` - Components
- `src/utils/*.ts` - Helpers

---

### 2.2 ESLint Configuration

**Install and configure strict linting:**

```bash
npm install --save-dev eslint-config-airbnb-typescript
npm install --save-dev @typescript-eslint/eslint-plugin
npm install --save-dev @typescript-eslint/parser
```

**File:** `eslint.config.js`
```javascript
import js from '@eslint/js';
import typescript from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';

export default [
  js.configs.recommended,
  {
    files: ['**/*.{ts,tsx}'],
    languageOptions: {
      parser: tsParser,
      parserOptions: {
        project: './tsconfig.json',
      },
    },
    plugins: {
      '@typescript-eslint': typescript,
      'react': react,
      'react-hooks': reactHooks,
    },
    rules: {
      // TypeScript
      '@typescript-eslint/no-explicit-any': 'error',
      '@typescript-eslint/explicit-function-return-type': 'warn',
      '@typescript-eslint/no-unused-vars': 'error',
      
      // React
      'react/prop-types': 'off', // Using TypeScript
      'react-hooks/rules-of-hooks': 'error',
      'react-hooks/exhaustive-deps': 'warn',
      
      // General
      'no-console': ['warn', { allow: ['warn', 'error'] }],
      'prefer-const': 'error',
      'no-var': 'error',
    },
  },
];
```

**Run lint:**
```bash
npm run lint
npm run lint -- --fix  # Auto-fix where possible
```

---

### 2.3 Code Organization & Cleanup

**Remove dead code:**
```bash
# Find unused exports
npx ts-prune

# Remove unused imports (auto)
npm run lint -- --fix
```

**Consistent naming:**
- Components: `PascalCase` (SiteForm, HeatmapLegend)
- Files: Match component name (SiteForm.tsx)
- Utilities: `camelCase` (calculateDistance, normalizeRSRP)
- Constants: `UPPER_SNAKE_CASE` (MAX_SITES, DEFAULT_POWER)
- Types/Interfaces: `PascalCase` (Site, CoverageSettings)

**File structure review:**
```
src/
├── components/
│   ├── map/           # Map-related components
│   ├── panels/        # Side panels
│   ├── ui/            # Reusable UI components
│   └── common/        # Common components
├── store/             # Zustand stores
├── lib/               # Business logic
├── utils/             # Pure utility functions
├── types/             # TypeScript types
└── hooks/             # Custom React hooks
```

**Move misplaced files if needed.**

---

### 2.4 Performance Audit

**React.memo for expensive components:**

```typescript
// Components that render frequently but props rarely change
export default React.memo(HeatmapLegend);
export default React.memo(SiteMarker);
export default React.memo(CoverageStats);
```

**useMemo for expensive calculations:**
```typescript
const filteredSites = useMemo(
  () => sites.filter(s => s.frequency === selectedFreq),
  [sites, selectedFreq]
);
```

**useCallback for stable callbacks:**
```typescript
const handleSiteUpdate = useCallback(
  (id: string, updates: Partial<Site>) => {
    updateSite(id, updates);
  },
  [updateSite]
);
```

**Web Worker efficiency:**
- Verify chunking strategy (currently 4 workers max)
- Check worker termination (no leaks)
- Monitor calculation times

**Tile cache optimization:**
```typescript
// HeatmapTileRenderer.ts
constructor(radiusMeters = 400, maxCacheSize = 150) {
  // Verify cache size is appropriate
  // Monitor cache hit rate in dev mode
}
```

**Bundle size check:**
```bash
npm run build
npx vite-bundle-visualizer

# Target: <500KB gzipped for main bundle
```

---

### 2.5 Error Handling

**Add try-catch blocks:**

```typescript
// API calls
async function fetchCoverageData() {
  try {
    const response = await fetch('/api/coverage');
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    return await response.json();
  } catch (error) {
    console.error('Coverage fetch failed:', error);
    toast.error('Failed to load coverage data. Please try again.');
    return null;
  }
}

// Worker communication
worker.onerror = (error) => {
  console.error('Worker error:', error);
  toast.error('Coverage calculation failed. Please try again.');
  terminateWorker();
};

// User actions
const handleDeleteSite = async (id: string) => {
  try {
    await deleteSite(id);
    toast.success('Site deleted');
  } catch (error) {
    console.error('Delete failed:', error);
    toast.error('Failed to delete site. Please try again.');
  }
};
```

**Fallback states everywhere:**
```typescript
// Loading
{isLoading && <LoadingSpinner />}

// Error
{error && <ErrorMessage error={error} onRetry={refetch} />}

// Empty
{sites.length === 0 && <EmptyState message="No sites yet" />}

// Success
{sites.length > 0 && <SiteList sites={sites} />}
```

---

## 🎨 Phase 3: UX Polish

**Priority:** P2  
**Time:** ~2-3 hours

### 3.1 Loading States

**Global loading indicator:**
```tsx
// App.tsx
{isCalculating && (
  <div className="loading-overlay">
    <Spinner />
    <p>Calculating coverage...</p>
  </div>
)}
```

**Component-level loading:**
```tsx
// SiteList.tsx
{isLoading ? (
  <LoadingSkeleton />
) : sites.length === 0 ? (
  <EmptyState />
) : (
  <SiteListItems sites={sites} />
)}
```

**Button loading states:**
```tsx
<button disabled={isLoading}>
  {isLoading ? (
    <>
      <Spinner size="sm" />
      Calculating...
    </>
  ) : (
    'Calculate Coverage'
  )}
</button>
```

---

### 3.2 Empty States

**No sites:**
```tsx
<div className="empty-state">
  <MapIcon size={48} />
  <h3>No Sites Yet</h3>
  <p>Add your first site to start RF coverage planning</p>
  <button onClick={handleAddSite}>
    + Add Site
  </button>
</div>
```

**No coverage:**
```tsx
<div className="empty-state">
  <RadioIcon size={48} />
  <h3>No Coverage Calculated</h3>
  <p>Click "Calculate Coverage" to see RF heatmap</p>
</div>
```

**Search no results:**
```tsx
<div className="empty-state">
  <SearchIcon size={32} />
  <p>No sites found matching "{searchQuery}"</p>
  <button onClick={clearSearch}>Clear Search</button>
</div>
```

---

### 3.3 Error States

**Generic error:**
```tsx
<div className="error-state">
  <AlertCircle size={48} color="red" />
  <h3>Something Went Wrong</h3>
  <p>{error.message}</p>
  <button onClick={handleRetry}>Try Again</button>
</div>
```

**Network error:**
```tsx
<div className="error-state">
  <WifiOff size={48} />
  <h3>Connection Lost</h3>
  <p>Check your internet connection and try again</p>
  <button onClick={handleRetry}>Retry</button>
</div>
```

**Validation error:**
```tsx
<div className="error-message">
  <AlertTriangle size={16} />
  <span>Power must be between 1-100W</span>
</div>
```

---

### 3.4 Keyboard Shortcuts Help Modal

**Create component:**

**File:** `src/components/common/KeyboardShortcutsHelp.tsx`

```tsx
import { useEffect, useState } from 'react';

const SHORTCUTS = [
  { key: 'N', description: 'New Site' },
  { key: 'S', description: 'Save Project' },
  { key: 'C', description: 'Clone Selected Sector' },
  { key: 'Delete', description: 'Delete Selected Site' },
  { key: 'Ctrl+Z', description: 'Undo' },
  { key: 'Ctrl+Shift+Z', description: 'Redo' },
  { key: 'Ctrl+F', description: 'Search Sites' },
  { key: 'Esc', description: 'Close Dialogs' },
  { key: '?', description: 'Show This Help' },
];

export default function KeyboardShortcutsHelp() {
  const [visible, setVisible] = useState(false);

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === '?' && !e.ctrlKey && !e.metaKey) {
        e.preventDefault();
        setVisible(v => !v);
      }
      if (e.key === 'Escape') {
        setVisible(false);
      }
    };

    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, []);

  if (!visible) return null;

  return (
    <div className="modal-overlay" onClick={() => setVisible(false)}>
      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <div className="modal-header">
          <h2>⌨️ Keyboard Shortcuts</h2>
          <button onClick={() => setVisible(false)}>×</button>
        </div>
        
        <div className="shortcuts-list">
          {SHORTCUTS.map((shortcut) => (
            <div key={shortcut.key} className="shortcut-row">
              <kbd className="kbd">{shortcut.key}</kbd>
              <span>{shortcut.description}</span>
            </div>
          ))}
        </div>
        
        <div className="modal-footer">
          <button onClick={() => setVisible(false)}>Close</button>
        </div>
      </div>
    </div>
  );
}
```

**CSS:**
```css
.kbd {
  display: inline-block;
  padding: 3px 8px;
  font-family: monospace;
  font-size: 12px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 4px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.1);
  min-width: 80px;
  text-align: center;
}

.shortcuts-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
}

.shortcut-row {
  display: flex;
  gap: 16px;
  align-items: center;
}
```

**Usage:**
```tsx
// App.tsx
import KeyboardShortcutsHelp from '@/components/common/KeyboardShortcutsHelp';

export default function App() {
  return (
    <>
      {/* ... other components */}
      <KeyboardShortcutsHelp />
    </>
  );
}
```

---

### 3.5 Export/Import Project

**Export all state to JSON:**

**File:** `src/components/panels/ExportPanel.tsx`

```tsx
import { useSitesStore } from '@/store/sites';
import { useCoverageStore } from '@/store/coverage';

export function exportProject() {
  const sites = useSitesStore.getState().sites;
  const settings = useCoverageStore.getState().settings;
  
  const project = {
    version: '1.0.0',
    exportDate: new Date().toISOString(),
    sites,
    settings,
  };
  
  const blob = new Blob([JSON.stringify(project, null, 2)], {
    type: 'application/json',
  });
  
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `rfcp-project-${Date.now()}.json`;
  a.click();
  URL.revokeObjectURL(url);
  
  toast.success('Project exported successfully');
}

export function importProject(file: File) {
  const reader = new FileReader();
  
  reader.onload = (e) => {
    try {
      const project = JSON.parse(e.target?.result as string);
      
      // Validate version
      if (project.version !== '1.0.0') {
        toast.error('Incompatible project version');
        return;
      }
      
      // Import sites
      const { setSites } = useSitesStore.getState();
      setSites(project.sites || []);
      
      // Import settings
      const { updateSettings } = useCoverageStore.getState();
      updateSettings(project.settings || {});
      
      toast.success('Project imported successfully');
    } catch (error) {
      console.error('Import failed:', error);
      toast.error('Failed to import project');
    }
  };
  
  reader.readAsText(file);
}
```

**UI:**
```tsx
// In ProjectPanel or Settings
<div className="export-import">
  <button onClick={exportProject}>
    📥 Export Project
  </button>
  
  <label className="btn-secondary">
    📤 Import Project
    <input
      type="file"
      accept=".json"
      style={{ display: 'none' }}
      onChange={(e) => {
        const file = e.target.files?.[0];
        if (file) importProject(file);
      }}
    />
  </label>
</div>
```

---

### 3.6 Input Validation

**Validate all numeric inputs:**

**File:** `src/utils/validation.ts`

```typescript
export interface ValidationRule {
  min?: number;
  max?: number;
  required?: boolean;
  message?: string;
}

export function validateNumber(
  value: number,
  rules: ValidationRule
): { valid: boolean; error?: string } {
  if (rules.required && (value === null || value === undefined)) {
    return { valid: false, error: rules.message || 'This field is required' };
  }
  
  if (rules.min !== undefined && value < rules.min) {
    return { valid: false, error: `Minimum value is ${rules.min}` };
  }
  
  if (rules.max !== undefined && value > rules.max) {
    return { valid: false, error: `Maximum value is ${rules.max}` };
  }
  
  return { valid: true };
}

// Validation rules
export const VALIDATION_RULES = {
  power: { min: 1, max: 100, required: true, message: 'Power: 1-100W' },
  gain: { min: 0, max: 30, required: true, message: 'Gain: 0-30 dBi' },
  height: { min: 1, max: 500, required: true, message: 'Height: 1-500m' },
  frequency: { min: 700, max: 3800, required: true, message: 'Freq: 700-3800 MHz' },
  azimuth: { min: 0, max: 359, required: true, message: 'Azimuth: 0-359°' },
  beamwidth: { min: 10, max: 360, required: true, message: 'Beamwidth: 10-360°' },
  radius: { min: 1, max: 100, required: true, message: 'Radius: 1-100 km' },
  resolution: { min: 50, max: 500, required: true, message: 'Resolution: 50-500m' },
};
```

**Use in NumberInput component:**

```tsx
// NumberInput.tsx
import { validateNumber, VALIDATION_RULES } from '@/utils/validation';

export function NumberInput({ value, onChange, field, ...props }) {
  const rules = VALIDATION_RULES[field];
  const validation = validateNumber(value, rules);
  
  return (
    <div className="input-group">
      <input
        type="number"
        value={value}
        onChange={(e) => onChange(parseFloat(e.target.value))}
        className={validation.valid ? '' : 'error'}
        {...props}
      />
      {!validation.valid && (
        <span className="error-message">
          {validation.error}
        </span>
      )}
    </div>
  );
}
```

---

### 3.7 Toast System Improvements

**Enhanced toast with actions:**

**File:** `src/components/common/Toast.tsx`

```tsx
export interface ToastAction {
  label: string;
  onClick: () => void;
}

export interface ToastOptions {
  duration?: number;
  action?: ToastAction;
}

export function addToast(
  message: string,
  type: 'success' | 'error' | 'info' | 'warning',
  options?: ToastOptions
) {
  const id = crypto.randomUUID();
  
  const duration = options?.duration || (type === 'error' ? 10000 : 3000);
  
  const toast: Toast = {
    id,
    message,
    type,
    action: options?.action,
    duration,
  };
  
  // Add to store
  useToastStore.getState().addToast(toast);
  
  // Auto-remove after duration
  setTimeout(() => {
    useToastStore.getState().removeToast(id);
  }, duration);
  
  return id;
}

// Usage:
toast.success('Site deleted', {
  duration: 10000,
  action: {
    label: 'Undo',
    onClick: () => restoreSite(deletedSite),
  },
});
```

**Toast container improvements:**
```tsx
// Support multiple toasts
<div className="toast-container">
  {toasts.map((toast) => (
    <div key={toast.id} className={`toast toast-${toast.type}`}>
      <span>{toast.message}</span>
      {toast.action && (
        <button
          onClick={() => {
            toast.action.onClick();
            removeToast(toast.id);
          }}
          className="toast-action"
        >
          {toast.action.label}
        </button>
      )}
      <button onClick={() => removeToast(toast.id)}>×</button>
    </div>
  ))}
  
  {toasts.length > 1 && (
    <button
      className="dismiss-all"
      onClick={() => clearAllToasts()}
    >
      Dismiss All ({toasts.length})
    </button>
  )}
</div>
```

---

### 3.8 Console Cleanup

**Remove dev logs in production:**

```typescript
// utils/logger.ts
const isDev = import.meta.env.DEV;

export const logger = {
  log: (...args: unknown[]) => {
    if (isDev) console.log(...args);
  },
  
  warn: (...args: unknown[]) => {
    if (isDev) console.warn(...args);
  },
  
  error: (...args: unknown[]) => {
    // Always log errors
    console.error(...args);
  },
  
  perf: (label: string, fn: () => void) => {
    if (!isDev) return fn();
    
    const start = performance.now();
    const result = fn();
    const end = performance.now();
    console.log(`⏱️ ${label}: ${(end - start).toFixed(2)}ms`);
    return result;
  },
};

// Replace all console.log with logger.log
// Replace all console.warn with logger.warn
// Keep console.error as-is (or use logger.error)
```

**Find and replace:**
```bash
# Find all console.log/warn
grep -r "console\\.log\\|console\\.warn" src/

# Replace with logger (manual or script)
```

---

### 3.9 Tooltips for Complex Features

**Add tooltips using native `title` or custom component:**

```tsx
// Simple tooltips (native)
<button title="Calculate RF coverage for all sites">
  Calculate Coverage
</button>

// Or custom tooltip component
<Tooltip content="Geographic radius in meters for each coverage point">
  <label>Point Radius</label>
</Tooltip>
```

**Key features needing tooltips:**
- Coverage Settings (what each setting does)
- Antenna types (directional vs omnidirectional)
- RSRP thresholds
- Resolution vs calculation time tradeoff
- Clone vs New Site

---

## ✅ Phase 4: Final Audit & Testing

**Priority:** P1  
**Time:** ~1-2 hours

### 4.1 Functionality Checklist

**Site Management:**
- [ ] Create new site → success
- [ ] Edit site parameters → updates correctly
- [ ] Delete site → confirmation dialog → deleted
- [ ] Clone sector → creates new sector, not site
- [ ] Batch operations work (select multiple, delete)

**Coverage Calculation:**
- [ ] Calculate coverage → completes successfully
- [ ] 50m resolution → no crash
- [ ] 500m resolution → fast calculation
- [ ] Progress indicator shows
- [ ] Results display correctly

**Heatmap:**
- [ ] Geographic scale preserved at all zoom levels
- [ ] Colors match RSRP legend
- [ ] Tile rendering <50ms average
- [ ] Opacity control works
- [ ] Toggle on/off works

**UI/UX:**
- [ ] Keyboard shortcuts work
- [ ] Dark mode consistent
- [ ] No console errors
- [ ] Loading states show
- [ ] Error states helpful
- [ ] Empty states clear

**Data Persistence:**
- [ ] Sites saved to localStorage
- [ ] Settings persist
- [ ] Browser refresh preserves state
- [ ] Export/import works

---

### 4.2 Performance Checklist

**Coverage Calculation:**
- [ ] <5s for typical scenario (10 sites, 200m resolution, 10km radius)
- [ ] <30s for heavy scenario (50 sites, 100m resolution, 20km radius)
- [ ] Web Workers terminate properly (no leaks)

**Tile Rendering:**
- [ ] Average tile render time <50ms
- [ ] No stuttering during pan/zoom
- [ ] Cache hit rate >50% after initial load

**Memory:**
- [ ] No memory leaks (check DevTools Memory tab)
- [ ] Heap size stable after 5 minutes of use
- [ ] Tile cache doesn't grow unbounded

**Bundle Size:**
- [ ] Main bundle <500KB gzipped
- [ ] Initial load <2s on 3G
- [ ] Code splitting working (if applicable)

---

### 4.3 Cross-Browser Testing

**Browsers to test:**
- [ ] Chrome (latest)
- [ ] Firefox (latest)
- [ ] Safari (if on Mac)
- [ ] Edge (latest)

**Mobile (basic):**
- [ ] Responsive layout works
- [ ] Touch gestures work
- [ ] Mobile Chrome/Safari

---

### 4.4 Edge Cases

**Extreme scenarios:**
- [ ] 0 sites → proper empty state
- [ ] 1 site → works correctly
- [ ] 100+ sites → performance acceptable
- [ ] Overlapping sites → coverage blending correct

**Zoom levels:**
- [ ] Zoom 1 (world) → works
- [ ] Zoom 8 (city) → works
- [ ] Zoom 18 (building) → works

**Boundary conditions:**
- [ ] Sites at map edge → no errors
- [ ] Coverage extends beyond viewport → tiles load
- [ ] Very large radius (100km) → calculates

**Input validation:**
- [ ] Invalid power (0W, 200W) → error shown
- [ ] Invalid frequency (100 MHz) → error shown
- [ ] Negative height → error shown
- [ ] Empty required fields → error shown

---

### 4.5 Regression Testing

**Features from previous iterations:**
- [ ] Multi-sector sites work (Iteration 8)
- [ ] Greek naming (Alpha, Beta, Gamma) correct
- [ ] Number inputs with sliders work (Iteration 9)
- [ ] Delete confirmation dialog works (Iteration 9.1)
- [ ] Undo toast works (Iteration 9.1)
- [ ] Keyboard shortcuts no conflicts (Iteration 9)
- [ ] Coverage settings controls work (Iteration 9.1)

---

## 🚀 Deployment

### Build & Test Production Build

```bash
# Clean build
rm -rf dist/
npm run build

# Test production build locally
npm run preview

# Verify:
# - No console errors
# - All features work
# - Performance good
# - Assets load correctly
```

### Pre-Deploy Checklist

- [ ] All tests pass
- [ ] TypeScript strict mode clean
- [ ] ESLint clean (no errors, minimal warnings)
- [ ] Production build successful
- [ ] Bundle size acceptable
- [ ] No TODO/FIXME comments in critical code

### Deploy to VPS

```bash
# On local machine
cd /d/root/rfcp/frontend
npm run build

# Copy to VPS (adjust path/credentials)
scp -r dist/* user@vps:/opt/rfcp/frontend/dist/

# On VPS
sudo systemctl reload caddy
```

### Post-Deploy Verification

```bash
# Check site loads
curl -I https://rfcp.eliah.one

# Monitor logs
sudo journalctl -u caddy -f

# Test in browser
# → https://rfcp.eliah.one
# → All features work
# → No console errors
```

---

## 📝 Documentation Updates

### README.md

```markdown
# RFCP - RF Coverage Planning Tool

Production-ready tactical communications planning.

## Features
- Multi-site RF coverage calculation
- Geographic-scale heatmap visualization
- Professional keyboard shortcuts
- Real-time coverage analysis
- Export/Import projects

## Tech Stack
- React 18 + TypeScript
- Leaflet + custom canvas renderer
- Web Workers for parallel calculation
- Zustand state management
- Vite build system

## Quick Start
\`\`\`bash
npm install
npm run dev
\`\`\`

## Build
\`\`\`bash
npm run build
npm run preview  # Test production build
\`\`\`
```

### CHANGELOG.md

```markdown
# Changelog

## Iteration 10 - Final Frontend Audit (2026-01-30)

### Fixed
- Stack overflow at 50m resolution
- Coverage radius circle color (green → orange)
- TypeScript strict mode compliance
- All ESLint warnings

### Added
- Keyboard shortcuts help modal (press ?)
- Export/Import project functionality
- Input validation with error messages
- Enhanced toast system with actions
- Loading/error/empty states everywhere

### Improved
- Code organization and cleanup
- Performance optimization (React.memo, useMemo)
- Error handling throughout
- Console logging (dev/prod separation)
- Bundle size optimization

### Performance
- Coverage calculation: <5s typical, <30s heavy
- Tile rendering: <50ms average
- Bundle size: <500KB gzipped
```

---

## 🎯 Success Criteria

### Must Have (P1):
- ✅ Zero critical bugs
- ✅ Zero console errors in production
- ✅ All features functional
- ✅ TypeScript strict mode passes
- ✅ Performance targets met
- ✅ Code audit complete

### Should Have (P2):
- ✅ Keyboard shortcuts help
- ✅ Export/Import project
- ✅ Input validation
- ✅ Polish all states (loading/error/empty)
- ✅ Enhanced toast system

### Nice to Have (P3):
- 📱 Mobile responsive improvements
- 🎨 UI micro-animations
- 📚 Comprehensive tooltips
- 🔔 Advanced toast features

---

## 📊 Iteration Timeline

**Session 1 (4-6 hours):**
- Phase 1: Critical Fixes (2-3h)
- Phase 2: Code Audit start (2-3h)

**Session 2 (4-6 hours):**
- Phase 2: Code Audit finish (1-2h)
- Phase 3: UX Polish (2-3h)
- Phase 4: Final Audit (1-2h)

**Total: 8-12 hours**

---

## 🎬 After Iteration 10

### Next Steps:
1. **Backend Audit** (Iteration 11)
   - API endpoints review
   - Database optimization
   - Error handling
   - Logging setup

2. **Documentation** (Iteration 12)
   - Architecture docs
   - Deployment guide
   - API documentation
   - User guide

3. **Production Deployment**
   - VPS hardening
   - SSL/HTTPS
   - Monitoring setup
   - Backup strategy

---

## 🎯 Ready for Implementation!

**Priority Order:**
1. Phase 1 (Critical Fixes) → ASAP
2. Phase 2 (Code Audit) → Next
3. Phase 3 (UX Polish) → Then
4. Phase 4 (Final Audit) → Last

**Let's make this frontend production-ready!** 🚀

---

**Status:** ✅ Specification Complete - Ready for Claude Code  
**Next:** Begin Phase 1 - Critical Fixes
