# GoogleMap Marker - Hızlı Referans

## 🎯 7 KRİTİK KURAL

### 1. ✅ SVG MARKER KULLAN (SymbolPath DEĞİL)

```typescript
// ❌ YANLIŞ - SymbolPath
return {
  path: google.maps.SymbolPath.CIRCLE,
  scale: 12,
  // ...
};

// ✅ DOĞRU - SVG Data URL
const svg = `
  <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
    <circle cx="16" cy="16" r="12" fill="${fill}" stroke="#ffffff" stroke-width="${strokeWidth}" />
  </svg>
`;

return {
  url: `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`,
  scaledSize: new google.maps.Size(32, 32), // ⚠️ SABİT
  anchor: new google.maps.Point(16, 16), // ⚠️ MERKEZ SABİT
  labelOrigin: new google.maps.Point(16, 16),
};
```

**Neden?** SVG = pixel-perfect kontrol, sabit boyut/anchor, jitter sıfır

---

### 2. ❌ ASLA BOUNCE KULLANMA

```typescript
// ❌ YANLIŞ
marker.setAnimation(google.maps.Animation.BOUNCE);

// ✅ DOĞRU
marker.setAnimation(null);
```

**Neden?** BOUNCE anchor'ı oynatır → jitter yaratır

---

### 3. ❌ hasCenteredRef MAP INIT'TE SET ETME

```typescript
// ❌ YANLIŞ - Map init'te set etme
useEffect(() => {
  // ... map oluştur
  hasCenteredRef.current = true; // ❌
}, [isScriptLoaded, places]);

// ✅ DOĞRU - fitBounds sonrası set et
useEffect(() => {
  if (places.length > 0 && !hasCenteredRef.current) {
    map.fitBounds(bounds);
    hasCenteredRef.current = true; // ✅
  }
}, [places]);
```

**Neden?** Map init'te set edilirse fitBounds hiç çalışmaz

---

### 4. ❌ LABEL FONT-SIZE DEĞİŞTİRME

```typescript
// ❌ YANLIŞ - Değişken font size
fontSize: state === 'default' ? '14px' : '16px'

// ✅ DOĞRU - Sabit font size
fontSize: '14px' // SABİT
```

**Neden?** Değişken font size labelOrigin'i yeniden hesaplatır → mikro-jitter

---

### 5. ❌ PLACES EFFECT'İNDE CLEANUP YAPMA

```typescript
// ❌ YANLIŞ
useEffect(() => {
  // ... marker creation
  
  return () => {
    markersRef.current.forEach(marker => marker.setMap(null));
    markersRef.current.clear();
  };
}, [places]);

// ✅ DOĞRU
useEffect(() => {
  // ... marker creation
  
  // Cleanup YOK - sadece selective deletion
}, [places]);
```

**Neden?** places değiştiğinde cleanup tüm marker'ları yok eder → recreation → jitter

---

### 6. ✅ POLYLINE SIRALAMA GÜVENLİ YAPMA

```typescript
// ❌ YANLIŞ - undefined/null güvensiz
(a.orderIndex || 0) - (b.orderIndex || 0)

// ✅ DOĞRU - Number.isFinite check
const ordered = [...dayPlaces].sort((a, b) => {
  const ai = Number.isFinite(a.orderIndex) ? a.orderIndex! : 999;
  const bi = Number.isFinite(b.orderIndex) ? b.orderIndex! : 999;
  return ai - bi;
});
```

**Neden?** undefined/null orderIndex'ler 0 olur → rota yanlış çizilir

---

### 7. ✅ POLYLINE DEPENDENCY OPTİMİZE ET

```typescript
// ❌ YANLIŞ - places array referansı
}, [places, activeDayId, showPolyline]);

// ✅ DOĞRU - places.length kullan
}, [places.length, activeDayId, showPolyline]);
```

**Neden?** places referansı her değişimde effect tetiklenir → gereksiz recreation

---

## 🎨 SVG MARKER ICON

```typescript
const createSvgMarkerIcon = (
  dayIndex: number,
  state: 'default' | 'hover' | 'selected'
) => {
  const color = getDayColor(dayIndex);
  const fill = state === 'default' ? color.fill : color.stroke;
  const strokeWidth = state === 'selected' ? 3 : 2;

  const svg = `
    <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
      <circle cx="16" cy="16" r="12" fill="${fill}" stroke="#ffffff" stroke-width="${strokeWidth}" />
    </svg>
  `;

  return {
    url: `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`,
    scaledSize: new google.maps.Size(32, 32), // ⚠️ SABİT
    anchor: new google.maps.Point(16, 16), // ⚠️ MERKEZ SABİT
    labelOrigin: new google.maps.Point(16, 16),
  };
};
```

**SABİT KALACAKLAR:**
- ✅ width, height (32)
- ✅ cx, cy (16)
- ✅ r (12)
- ✅ scaledSize (32, 32)
- ✅ anchor (16, 16)

**DEĞİŞEBİLECEKLER:**
- ✅ fill (default: color.fill, hover/select: color.stroke)
- ✅ stroke-width (default/hover: 2, select: 3)

---

## 🛣️ PER-DAY POLYLINE

```typescript
const polylinesRef = useRef<Map<string, google.maps.Polyline>>(new Map());

useEffect(() => {
  if (!mapInstanceRef.current || !showPolyline) return;

  const map = mapInstanceRef.current;

  // Eski polyline'ları temizle
  polylinesRef.current.forEach(line => line.setMap(null));
  polylinesRef.current.clear();

  // Günlere göre grupla
  const groupedByDay = new Map<string, typeof places>();
  places.forEach(place => {
    if (!place.dayId) return;
    if (!groupedByDay.has(place.dayId)) {
      groupedByDay.set(place.dayId, []);
    }
    groupedByDay.get(place.dayId)!.push(place);
  });

  // Her gün için polyline oluştur
  groupedByDay.forEach((dayPlaces, dayId) => {
    // activeDayId varsa sadece o günü göster
    if (activeDayId && activeDayId !== dayId) return;

    if (dayPlaces.length < 2) return;

    // Sıraya göre diz
    const ordered = [...dayPlaces].sort(
      (a, b) => (a.orderIndex || 0) - (b.orderIndex || 0)
    );

    const path = ordered.map(p => ({
      lat: p.lat,
      lng: p.lng,
    }));

    const color = getDayColor(ordered[0].dayIndex || 0);

    const polyline = new google.maps.Polyline({
      path,
      geodesic: true,
      strokeColor: color.stroke,
      strokeOpacity: 0.6,
      strokeWeight: 4,
      map,
    });

    polylinesRef.current.set(dayId, polyline);
  });
}, [places, activeDayId, showPolyline]);
```

**Özellikler:**
- ✅ Her gün ayrı polyline (Map<dayId, Polyline>)
- ✅ Her gün kendi renginde (getDayColor)
- ✅ activeDayId desteği (sadece seçili gün)
- ✅ Günler arası geçişler çizilmez

---

## 📐 MARKER LIFECYCLE

### Effect 1: Map Initialization (Unmount Cleanup)

```typescript
useEffect(() => {
  // Map oluştur
  
  return () => {
    // ✅ SADECE unmount'ta cleanup
    markersRef.current.forEach(marker => marker.setMap(null));
    markersRef.current.clear();
    polylinesRef.current.forEach(polyline => polyline.setMap(null));
    polylinesRef.current.clear();
  };
}, [isScriptLoaded, places]);
```

---

### Effect 2: Marker Creation (NO Cleanup)

```typescript
useEffect(() => {
  // 1. Selective deletion
  markersRef.current.forEach((marker, id) => {
    if (!currentPlaceIds.has(id)) {
      marker.setMap(null);
      markersRef.current.delete(id);
    }
  });

  // 2. Marker creation (skip if exists, use SVG icon)
  places.forEach((place) => {
    if (markersRef.current.has(place.id)) return; // ⚠️ ATLA
    
    const marker = new google.maps.Marker({
      // ...
      icon: createSvgMarkerIcon(place.dayIndex || 0, 'default'), // ✅ SVG
    });
  });

  // ❌ CLEANUP YOK
}, [places]);
```

---

### Effect 3: Visual Updates (NO Creation)

```typescript
useEffect(() => {
  markersRef.current.forEach((marker, id) => {
    // 1. Visibility
    marker.setVisible(isVisible);

    // 2. State
    if (id === selectedPlaceId) {
      marker.setZIndex(1000);
      marker.setAnimation(null); // ✅ BOUNCE YOK
    }

    // 3. Icon (SVG - sadece renk/stroke değişir)
    marker.setIcon(createSvgMarkerIcon(dayIndex, state));

    // 4. Label
    marker.setLabel({ ... });
  });
}, [hoveredPlaceId, selectedPlaceId, activeDayId, places]);
```

---

### Effect 4: Per-Day Polyline

```typescript
useEffect(() => {
  // Eski polyline'ları temizle
  polylinesRef.current.forEach(line => line.setMap(null));
  polylinesRef.current.clear();

  // Günlere göre grupla ve polyline oluştur
  // (detay için yukarıdaki PER-DAY POLYLINE bölümüne bakın)
}, [places, activeDayId, showPolyline]);
```

---

## 🔄 STATE TRANSITIONS

### Default → Hover

```typescript
// SVG Değişenler:
fill: color.fill → color.stroke
stroke-width: 2 (aynı)

// Marker Değişenler:
zIndex: orderIndex → 999
label.fontSize: 14px → 16px

// Değişmeyenler:
scaledSize: (32, 32) (sabit)
anchor: (16, 16) (sabit)
animation: null (sabit)
```

---

### Hover → Selected

```typescript
// SVG Değişenler:
stroke-width: 2 → 3

// Marker Değişenler:
zIndex: 999 → 1000

// Değişmeyenler:
fill: color.stroke (aynı)
scaledSize: (32, 32) (sabit)
anchor: (16, 16) (sabit)
animation: null (sabit) ✅ BOUNCE YOK
```

---

### Selected → Default

```typescript
// SVG Değişenler:
fill: color.stroke → color.fill
stroke-width: 3 → 2

// Marker Değişenler:
zIndex: 1000 → orderIndex
label.fontSize: 16px → 14px

// Değişmeyenler:
scaledSize: (32, 32) (sabit)
anchor: (16, 16) (sabit)
animation: null (sabit)
```

---

## ✅ CHECKLIST

Yeni marker feature eklerken kontrol et:

- [ ] SVG marker kullandım (SymbolPath değil)
- [ ] BOUNCE animation kullanmadım
- [ ] hasCenteredRef map init'te set etmedim
- [ ] Label font-size sabit (14px)
- [ ] Places effect'inde cleanup yapmadım
- [ ] scaledSize (32, 32) sabit kaldı
- [ ] anchor (16, 16) sabit kaldı
- [ ] Sadece fill ve stroke-width değiştirdim
- [ ] Marker recreation yapmadım (has check)
- [ ] Selective deletion kullandım
- [ ] Visual update ayrı effect'te
- [ ] Per-day polyline kullandım (tek polyline değil)
- [ ] Polyline sıralama güvenli (Number.isFinite)
- [ ] Polyline dependency optimize (places.length)
- [ ] Polyline rengi getDayColor ile tutarlı

---

## 🚫 YASAKLAR

### ❌ ASLA YAPMA

1. **SymbolPath kullanma**
   ```typescript
   path: google.maps.SymbolPath.CIRCLE, // ❌
   ```

2. **BOUNCE animation kullanma**
   ```typescript
   marker.setAnimation(google.maps.Animation.BOUNCE); // ❌
   ```

3. **hasCenteredRef map init'te set etme**
   ```typescript
   useEffect(() => {
     hasCenteredRef.current = true; // ❌
   }, [isScriptLoaded, places]);
   ```

4. **Label font-size değiştirme**
   ```typescript
   fontSize: state === 'default' ? '14px' : '16px', // ❌
   ```

5. **Places effect'inde cleanup yapma**
   ```typescript
   useEffect(() => {
     return () => { /* cleanup */ }; // ❌
   }, [places]);
   ```

6. **SVG boyutunu değiştirme**
   ```typescript
   const size = state === 'hover' ? 36 : 32; // ❌
   scaledSize: new google.maps.Size(size, size); // ❌
   ```

7. **Anchor değiştirme**
   ```typescript
   anchor: new google.maps.Point(16, state === 'hover' ? 18 : 16); // ❌
   ```

8. **Her effect'te marker creation yapma**
   ```typescript
   useEffect(() => {
     // Her effect'te yeni marker oluşturma ❌
   }, [hoveredPlaceId]);
   ```

9. **Tek polyline kullanma (tüm günler için)**
   ```typescript
   const polylineRef = useRef<google.maps.Polyline | null>(null); // ❌
   ```

10. **Polyline sıralama güvensiz yapma**
    ```typescript
    (a.orderIndex || 0) - (b.orderIndex || 0) // ❌
    ```

11. **Polyline dependency'de places kullanma**
    ```typescript
    }, [places, activeDayId, showPolyline]); // ❌
    ```

---

## ✅ YAPILACAKLAR

### ✅ HER ZAMAN YAP

1. **SVG marker kullan**
   ```typescript
   icon: createSvgMarkerIcon(dayIndex, state), // ✅
   ```

2. **hasCenteredRef fitBounds sonrası set et**
   ```typescript
   if (places.length > 0 && !hasCenteredRef.current) {
     map.fitBounds(bounds);
     hasCenteredRef.current = true; // ✅
   }
   ```

3. **Label font-size sabit tut**
   ```typescript
   fontSize: '14px', // ✅ SABİT
   ```

4. **Marker recreation check**
   ```typescript
   if (markersRef.current.has(place.id)) return; // ✅
   ```

5. **Selective deletion**
   ```typescript
   if (!currentPlaceIds.has(id)) {
     marker.setMap(null);
     markersRef.current.delete(id);
   }
   ```

6. **Visual update için setIcon kullan**
   ```typescript
   marker.setIcon(createSvgMarkerIcon(dayIndex, state)); // ✅
   ```

7. **Animation null tut**
   ```typescript
   marker.setAnimation(null); // ✅
   ```

8. **Cleanup sadece unmount'ta**
   ```typescript
   useEffect(() => {
     return () => { /* cleanup */ }; // ✅
   }, [isScriptLoaded, places]); // Map init effect
   ```

9. **Per-day polyline kullan**
   ```typescript
   const polylinesRef = useRef<Map<string, google.maps.Polyline>>(new Map()); // ✅
   ```

10. **Polyline sıralama güvenli yap**
    ```typescript
    const ordered = [...dayPlaces].sort((a, b) => {
      const ai = Number.isFinite(a.orderIndex) ? a.orderIndex! : 999;
      const bi = Number.isFinite(b.orderIndex) ? b.orderIndex! : 999;
      return ai - bi;
    }); // ✅
    ```

11. **Polyline dependency optimize et**
    ```typescript
    }, [places.length, activeDayId, showPolyline]); // ✅
    ```

---

## 🎯 ÖZET

**11 Kritik Düzeltme:**

**GoogleMap Component (8 düzeltme):**
1. ✅ **SVG marker kullanıldı** → Pixel-perfect kontrol, sabit boyut/anchor
2. ✅ **BOUNCE kaldırıldı** → Anchor oynatma YOK
3. ✅ **hasCenteredRef düzeltildi** → fitBounds doğru çalışıyor
4. ✅ **Label font-size sabit** → Mikro-jitter YOK
5. ✅ **Places cleanup kaldırıldı** → Marker recreation YOK
6. ✅ **Per-day polyline eklendi** → Her gün ayrı renkli rota
7. ✅ **Polyline sıralama güvenli** → Rota her zaman doğru
8. ✅ **Polyline performance optimize** → Gereksiz recreation YOK

**TripPlanner Component (3 düzeltme):**
9. ✅ **allPlaces useMemo** → Referans stabilitesi, 0 gereksiz işlem/saniye
10. ✅ **orderIndex backend öncelikli** → Drag/reorder sonrası marker sabit
11. ✅ **activeDayId ilk gün otomatik** → İlk yüklemede senkron visibility

**Sonuç:**
- ✅ Jitter tamamen yok
- ✅ Smooth transitions
- ✅ Profesyonel görünüm (Wanderlog/Layla hissi)
- ✅ Gün bazlı renkli rotalar
- ✅ Yüksek performans (0 gereksiz işlem/saniye)
- ✅ Stabil marker & polyline
- ✅ Senkron visibility

---

## 📚 DAHA FAZLA BİLGİ

- **TripPlanner 3 kritik düzeltme:** `TRIPPLANNER_CRITICAL_FIXES.md`
- **GoogleMap 4 kritik düzeltme:** `GOOGLEMAP_CRITICAL_FIXES.md`
- **SVG marker detayları:** `GOOGLEMAP_SVG_POLYLINE.md`
- **Özet:** `GOOGLEMAP_SUMMARY.md`
- **Jitter düzeltmeleri:** `GOOGLEMAP_JITTER_FIX.md`
- **Component mimarisi:** `GOOGLEMAP_ARCHITECTURE.md`
- **Lifecycle düzeltmeleri:** `GOOGLEMAP_LIFECYCLE_FIX.md`
