# 🔥 Améliorations du Système de Notifications Mobile

## ✅ Ce qui a été accompli (Étapes 1-3)

### Étape 1: Fix TypeScript ✅
- Ajout de `handleStageComplete()` et `handleStageDelete()` dans PlannerPage.mobile.vue
- Type-check passe (0 erreurs)

### Étape 2: Migration Notify.create() → MobileToast ✅
**Migrations réalisées (10+ notifications):**
- `handleEditStage`: info toast
- `handleNavigateToStage`: success toast
- `handleDeleteStage`: dialog + success/error toast
- `handleDuplicateStage`: info toast
- `handleShareStage`: info toast
- `handleAddStage`: info toast
- `handleReorderStages`: info toast
- `handleExportStages`: info toast
- `handleStageComplete`: success toast
- FAB actions (add-start): success toast
- `toggleScenic`: info toast
- `resetMapBearing`: success toast
- `cycleMapStyle`: info toast

**Résultats:**
- 10+ notifications migrées
- Code plus lisible (1 ligne vs 5-8 lignes)
- UX cohérente (pas de questions dans toasts)

### Étape 3: Tests de base ✅
- Type-check: ✅ Passe
- Build: ✅ Fonctionne
- API: ✅ Simple et moderne

---

## 🚀 Améliorations Identifiées

### 🎯 PRIORITÉ HAUTE

#### 1. **Bottom Sheet Animation améliorée** (MobileDialog)
**Problème actuel:**
- Animation scale depuis le centre
- Pas mobile-first (dialogs mobiles viennent du bas)

**Solution:**
```vue
<style>
@keyframes dialog-enter {
  from {
    opacity: 0;
    transform: translateY(100%); /* ← Vient du bas comme iOS */
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.mobile-dialog {
  position: fixed;
  bottom: 0; /* ← Ancré en bas */
  left: 0;
  right: 0;
  border-radius: 24px 24px 0 0; /* ← Coins arrondis haut seulement */
  max-height: 85vh;
}
</style>
```

**Impact:** Animation native iOS/Android, plus intuitive

---

#### 2. **Toast Queue avec Priority System**
**Problème actuel:**
- FIFO simple (First In First Out)
- Pas de gestion de priorité (errors écrasées par info)

**Solution:**
```typescript
// useMobileToast.ts
enum ToastPriority {
  LOW = 0,    // info
  NORMAL = 1, // success/warning
  HIGH = 2    // error
}

interface ToastInstance {
  vnode: VNode
  container: HTMLDivElement
  priority: ToastPriority
}

const toasts: ToastInstance[] = []

function createToast(message: string, type: ToastType, options) {
  const priority = type === 'error' ? ToastPriority.HIGH : 
                   type === 'info' ? ToastPriority.LOW : ToastPriority.NORMAL
  
  // Si queue pleine ET nouvelle toast plus prioritaire → remplacer la moins prioritaire
  if (toasts.length >= MAX_TOASTS) {
    const lowestPriority = Math.min(...toasts.map(t => t.priority))
    if (priority > lowestPriority) {
      const index = toasts.findIndex(t => t.priority === lowestPriority)
      const removed = toasts.splice(index, 1)[0]
      destroyToast(removed)
    } else {
      return // Ne rien faire si pas assez prioritaire
    }
  }
  
  // Reste du code...
}
```

**Impact:** Erreurs critiques jamais perdues dans la queue

---

#### 3. **Swipe to Dismiss** (Geste natif)
**Implémentation:**
```vue
<!-- MobileToast.vue -->
<script setup>
const startX = ref(0)
const currentX = ref(0)
const isDragging = ref(false)

function handleTouchStart(e: TouchEvent) {
  startX.value = e.touches[0].clientX
  isDragging.value = true
}

function handleTouchMove(e: TouchEvent) {
  if (!isDragging.value) return
  currentX.value = e.touches[0].clientX
  const deltaX = currentX.value - startX.value
  
  // Visual feedback
  const container = e.currentTarget as HTMLElement
  container.style.transform = `translateX(${deltaX}px)`
  container.style.opacity = `${1 - Math.abs(deltaX) / 200}`
}

function handleTouchEnd(e: TouchEvent) {
  if (!isDragging.value) return
  const deltaX = currentX.value - startX.value
  
  if (Math.abs(deltaX) > 100) {
    // Dismiss
    hide()
  } else {
    // Reset
    const container = e.currentTarget as HTMLElement
    container.style.transition = 'transform 0.2s, opacity 0.2s'
    container.style.transform = 'translateX(0)'
    container.style.opacity = '1'
  }
  
  isDragging.value = false
}
</script>

<template>
  <div
    class="mobile-toast"
    @touchstart="handleTouchStart"
    @touchmove="handleTouchMove"
    @touchend="handleTouchEnd"
  >
    <!-- content -->
  </div>
</template>
```

**Impact:** Geste iOS/Android natif, UX premium

---

#### 4. **Haptic Feedback amélioré**
**Problème actuel:**
- Vibration simple (10ms/20ms)
- Pas de différenciation par type

**Solution:**
```typescript
// utils/haptics.ts
export const haptics = {
  light() {
    if ('vibrate' in navigator) navigator.vibrate(10)
  },
  medium() {
    if ('vibrate' in navigator) navigator.vibrate(20)
  },
  heavy() {
    if ('vibrate' in navigator) navigator.vibrate(40)
  },
  success() {
    if ('vibrate' in navigator) navigator.vibrate([10, 50, 10])
  },
  error() {
    if ('vibrate' in navigator) navigator.vibrate([20, 100, 20, 100, 20])
  },
  warning() {
    if ('vibrate' in navigator) navigator.vibrate([15, 75, 15])
  }
}

// Dans MobileToast
onMounted(() => {
  switch (props.type) {
    case 'success': haptics.success(); break
    case 'error': haptics.error(); break
    case 'warning': haptics.warning(); break
    default: haptics.light()
  }
})
```

**Impact:** Feedback tactile contextualisé

---

### 🎨 PRIORITÉ MOYENNE

#### 5. **Dark Mode Adaptatif**
**Solution:**
```vue
<!-- MobileToast.vue -->
<script setup>
import { useDark } from '@vueuse/core'
const isDark = useDark()

const backgroundGradient = computed(() => {
  if (props.type === 'success') {
    return isDark.value
      ? 'linear-gradient(135deg, rgba(16, 185, 129, 0.95), rgba(5, 150, 105, 0.95))'
      : 'linear-gradient(135deg, rgba(16, 185, 129, 1), rgba(5, 150, 105, 1))'
  }
  // ... autres types
})
</script>

<style>
.mobile-toast {
  background: v-bind(backgroundGradient);
}
</style>
```

**Impact:** Cohérence visuelle avec le thème système

---

#### 6. **Toast Stacking avec Offset**
**Problème actuel:**
- 3 toasts superposés exactement
- Pas de visual feedback qu'il y en a plusieurs

**Solution:**
```scss
.mobile-toast {
  &:nth-child(1) {
    top: calc(env(safe-area-inset-top, 0) + 12px);
    z-index: 9999;
  }
  &:nth-child(2) {
    top: calc(env(safe-area-inset-top, 0) + 72px);
    z-index: 9998;
    transform: scale(0.96);
    opacity: 0.8;
  }
  &:nth-child(3) {
    top: calc(env(safe-area-inset-top, 0) + 128px);
    z-index: 9997;
    transform: scale(0.92);
    opacity: 0.6;
  }
}
```

**Impact:** Visual feedback du stack

---

#### 7. **Auto-batching des Toasts similaires**
**Cas d'usage:** 5 stages supprimées → 1 toast "5 étapes supprimées" au lieu de 5 toasts

**Solution:**
```typescript
const pendingToasts = new Map<string, { count: number, timeout: number }>()

function createToast(message: string, type: ToastType, options) {
  const key = `${type}-${message}`
  
  if (pendingToasts.has(key)) {
    const pending = pendingToasts.get(key)!
    pending.count++
    clearTimeout(pending.timeout)
    
    // Attendre 300ms avant de créer le toast batché
    pending.timeout = setTimeout(() => {
      const count = pending.count
      pendingToasts.delete(key)
      createActualToast(`${message} (×${count})`, type, options)
    }, 300)
  } else {
    pendingToasts.set(key, { count: 1, timeout: setTimeout(() => {
      pendingToasts.delete(key)
      createActualToast(message, type, options)
    }, 300) })
  }
}
```

**Impact:** Moins de spam, info plus claire

---

### 🔬 PRIORITÉ BASSE

#### 8. **Analytics Tracking**
```typescript
// useMobileToast.ts
function createToast(message, type, options) {
  // ... existing code
  
  // Track analytics
  if (window.gtag) {
    window.gtag('event', 'notification_shown', {
      notification_type: type,
      notification_message: message.substring(0, 50) // Truncate
    })
  }
}
```

---

#### 9. **Persistent Toasts** (Pour errors critiques)
```typescript
function createToast(message, type, options) {
  const persist = options?.persist ?? (type === 'error' && options?.timeout === 0)
  
  if (persist) {
    // Ne pas auto-hide
    // Ajouter bouton close explicite
    // Stocker dans localStorage pour survie au refresh
  }
}
```

---

#### 10. **Sound Notifications** (Opt-in)
```typescript
const audioContext = new AudioContext()

const sounds = {
  success: () => playBeep(880, 0.1, 'sine'), // Beep aigu
  error: () => playBeep(220, 0.2, 'square'),  // Beep grave
  warning: () => playBeep(440, 0.15, 'triangle')
}

function playBeep(frequency: number, duration: number, type: OscillatorType) {
  const oscillator = audioContext.createOscillator()
  const gainNode = audioContext.createGain()
  
  oscillator.connect(gainNode)
  gainNode.connect(audioContext.destination)
  
  oscillator.frequency.value = frequency
  oscillator.type = type
  
  gainNode.gain.setValueAtTime(0.1, audioContext.currentTime)
  gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + duration)
  
  oscillator.start(audioContext.currentTime)
  oscillator.stop(audioContext.currentTime + duration)
}
```

---

## 📊 Impact Estimé des Améliorations

| Amélioration | Difficulté | Impact UX | Impact Perf | Priorité |
|--------------|------------|-----------|-------------|----------|
| Bottom sheet animation | 🟢 Facile | 🔥🔥🔥 High | 🟢 Aucun | **HIGH** |
| Priority queue | 🟡 Moyen | 🔥🔥🔥 High | 🟢 Positif | **HIGH** |
| Swipe to dismiss | 🟡 Moyen | 🔥🔥🔥 High | 🟢 Positif | **HIGH** |
| Haptic feedback | 🟢 Facile | 🔥🔥 Medium | 🟢 Aucun | **HIGH** |
| Dark mode | 🟢 Facile | 🔥🔥 Medium | 🟢 Aucun | MEDIUM |
| Toast stacking | 🟢 Facile | 🔥 Low | 🟢 Aucun | MEDIUM |
| Auto-batching | 🟡 Moyen | 🔥🔥 Medium | 🔥 High | MEDIUM |
| Analytics | 🟢 Facile | - | 🟢 Aucun | LOW |
| Persistent toasts | 🟡 Moyen | 🔥 Low | 🟡 Moyen | LOW |
| Sound | 🔴 Difficile | 🔥 Low | 🟡 Moyen | LOW |

---

## 🎯 Plan d'Action Recommandé

### Phase 1: Quick Wins (1-2h)
1. ✅ Bottom sheet animation (30 min)
2. ✅ Haptic feedback amélioré (20 min)
3. ✅ Dark mode adaptatif (30 min)
4. ✅ Toast stacking visuel (20 min)

### Phase 2: Core Improvements (2-3h)
5. Priority queue system (1h)
6. Swipe to dismiss (1.5h)
7. Auto-batching (30 min)

### Phase 3: Polish (1h)
8. Analytics tracking (20 min)
9. Persistent toasts (30 min)
10. Sound notifications (opt-in) (10 min setup)

---

## 💡 Bonus: Amélioration Inattendue Découverte

### **11. Toast Position Dynamique selon le Contexte**

**Idée:** Toasts en haut par défaut, MAIS en bas si clavier virtuel ouvert (évite que le toast cache l'input)

```typescript
import { useKeyboard } from '@vueuse/core'

const { isOpen: isKeyboardOpen } = useKeyboard()

const toastPosition = computed(() => {
  return isKeyboardOpen.value ? 'bottom' : 'top'
})
```

**Impact:** UX mobile++, toast jamais cache l'input actif

---

## 🏆 Résumé

**Ce qui a été fait:**
- ✅ 10+ notifications migrées
- ✅ Type-check passe
- ✅ Build fonctionne
- ✅ API moderne (async/await)

**Ce qui peut être amélioré (11 idées):**
- 🔥 4 HIGH priority (bottom sheet, priority queue, swipe, haptics)
- 🎨 3 MEDIUM priority (dark mode, stacking, batching)
- 🔬 4 LOW priority (analytics, persistent, sound, dynamic position)

**Temps total estimé:** 4-6h pour tout implémenter

**Impact global:** Système de notifications mobile **premium** qui rivalise avec les apps natives iOS/Android
