# Quality Improvement Roadmap

**Date:** November 2, 2025  
**Status:** Post type-check/lint/build fixes  
**Current State:** ✅ 0 TypeScript errors, ✅ Lint passing, ✅ Build successful

---

## 🎯 Context

Le build fonctionne **MAIS** avec des workarounds temporaires:
- **Types `any`** pour Planner (PlannerPreset, PlannerPresetDescriptor, etc.)
- **Mock collaboration** sans implémentation réelle
- **Stubs locationStore** sans logique métier
- **Module social** commenté
- **PlannerPage.vue** de 8327 lignes (CRITIQUE - au-delà des limites maintenables)

Ces fixes permettent de travailler **maintenant** mais créent une **dette technique** importante.

---

## 🚨 Priorité CRITIQUE

### 1. Refactoriser PlannerPage.vue (8327 lignes → max 2000/fichier)

**Problème:**
- Fichier monolithique ingérable
- Type instantiation depth error (workaround @ts-expect-error ligne 5414)
- Cognitive complexity 17/15 (ligne 4964)
- Impossible à review, debug, ou tester efficacement

**Solution:**
```
apps/web/src/modules/planner/
  ├── pages/
  │   └── PlannerPage.vue (orchestrator - 800 lignes max)
  ├── components/
  │   ├── PlannerMap.vue (visualisation + Mapbox - 1500 lignes)
  │   ├── PlannerWizard.vue (formulaire multi-étapes - 1200 lignes)
  │   ├── PlannerPresets.vue (sélection presets - 800 lignes)
  │   └── PlannerStageList.vue (gestion stages - 600 lignes)
  ├── composables/
  │   ├── usePlannerMap.ts (logique carte)
  │   ├── usePlannerForm.ts (gestion formulaire)
  │   ├── usePlannerPresets.ts (presets logic)
  │   └── usePlannerStages.ts (CRUD stages)
```

**Bénéfices:**
- ✅ Type instantiation depth résolu (fichiers plus petits)
- ✅ Cognitive complexity respectée
- ✅ Tests unitaires possibles
- ✅ Code review facilité
- ✅ Réutilisabilité components

**Effort:** 3 jours  
**Impact:** CRITIQUE - Bloquant pour évolution future

---

## 🔧 Priorité HAUTE

### 2. Créer les types Planner dans @my-roadtrip/types

**Fichiers impactés:**
- `apps/web/src/modules/planner/config/presets.ts` (lignes 20-31)
- Tous les composants utilisant presets

**Implémentation:**
```typescript
// packages/types/src/planner.types.ts

export interface PlannerPresetDescriptor {
  id: string
  title: string
  category: 'base' | 'addon'
  tags: string[]
  icon?: string
}

export interface PlannerPreset extends PlannerPresetDescriptor {
  config: Partial<PlannerFormState>
  dependencies?: string[]
}

export interface PlannerPresetBundle {
  base?: PlannerPreset
  addons: PlannerPreset[]
}

export interface PlannerPresetMix {
  id: string
  label: string
  baseId: string
  addonIds: string[]
  description?: string
}

export interface PlannerFocusType {
  id: string
  label: string
  icon: string
}
```

**Effort:** 1 jour  
**Impact:** Type safety complète, autocomplete IDE

---

### 3. Implémenter useCollaboration composable

**Fichiers impactés:**
- `apps/web/src/modules/planner/pages/PlannerPage.vue` (ligne 4316 - mock actuel)

**Implémentation:**
```typescript
// apps/web/src/modules/collab/composables/useCollaboration.ts

import { ref, computed, type Ref } from 'vue'
import { useYjs } from './useYjs'
import { logger } from '@/shared/utils/logger'
import type { Stage } from '@my-roadtrip/types'

export interface CollaborationPresence {
  userId: string
  userName?: string
  cursor?: { lat: number; lng: number }
  lastSeen?: Date | string | number
}

export function useCollaboration(planId: Ref<string>) {
  const { doc, provider } = useYjs(planId)
  
  const presence = ref<CollaborationPresence[]>([])
  const ready = ref(false)
  
  const observeStages = (callback: (stageMap: Map<string, Stage>) => void) => {
    const stagesMap = doc.getMap<Stage>('stages')
    
    const observer = () => {
      const map = new Map<string, Stage>()
      stagesMap.forEach((stage, key) => map.set(key, stage))
      callback(map)
    }
    
    stagesMap.observe(observer)
    return () => stagesMap.unobserve(observer)
  }
  
  const updateStage = (id: string, stage: Stage) => {
    const stagesMap = doc.getMap<Stage>('stages')
    stagesMap.set(id, stage)
  }
  
  const deleteStage = (id: string) => {
    const stagesMap = doc.getMap<Stage>('stages')
    stagesMap.delete(id)
  }
  
  const forceSync = () => {
    provider.sync()
  }
  
  const updatePresence = async (coords: { lat: number; lng: number }) => {
    // Implement presence protocol
    logger.debug('Presence updated', { coords })
  }
  
  return {
    presence: computed(() => presence.value),
    ready: computed(() => ready.value),
    observeStages,
    updateStage,
    deleteStage,
    forceSync,
    updatePresence
  }
}
```

**Dépendances:**
- Yjs (déjà dans package.json)
- y-websocket (ajouter: `pnpm add y-websocket`)
- Backend WebSocket endpoint pour sync

**Effort:** 2 jours  
**Impact:** Collaboration temps réel fonctionnelle

---

### 4. Implémenter locationTrackingStore

**Fichiers impactés:**
- `apps/web/src/modules/location/stores/locationTrackingStore.ts` (lignes 30-40 - stubs actuels)
- `apps/web/src/modules/map/composables/useMapPageController.ts` (usages ligne 411, 413, 424, 761)

**Implémentation:**
```typescript
// Remplacer stubs par logique réelle

const startTracking = (id: string) => {
  if (!isSupported.value || isTracking.value) return
  
  trackingId.value = id
  isTracking.value = true
  
  watchId.value = navigator.geolocation.watchPosition(
    (position) => {
      currentPosition.value = {
        latitude: position.coords.latitude,
        longitude: position.coords.longitude
      }
      // Enregistrer dans history pour calculs distance/vitesse
      addPositionToHistory(position)
    },
    (error) => {
      errorCode.value = error.code.toString()
      logger.error('Geolocation error', { error })
    },
    { enableHighAccuracy: true, maximumAge: 5000, timeout: 10000 }
  )
}

const stopTracking = (id?: string) => {
  if (id && trackingId.value !== id) return
  
  if (watchId.value !== null) {
    navigator.geolocation.clearWatch(watchId.value)
    watchId.value = null
  }
  
  isTracking.value = false
  trackingId.value = null
}

const refreshSummary = async () => {
  summaryLoading.value = true
  try {
    // Calculer depuis history
    const stats = calculateStats(positionHistory.value)
    summary.value = stats
  } finally {
    summaryLoading.value = false
  }
}
```

**Effort:** 1 jour  
**Impact:** GPS tracking fonctionnel

---

### 5. Créer le module Social

**Fichiers impactés:**
- `apps/web/src/modules/planner/pages/LandingPage.vue` (ligne 148 - commenté)

**Implémentation:**
```typescript
// apps/web/src/modules/social/components/SocialFeed.vue

<template>
  <div class="social-feed">
    <div v-for="post in posts" :key="post.id" class="feed-card">
      <q-card class="glass-panel">
        <q-card-section>
          <div class="post-header">
            <q-avatar>
              <img :src="post.user.avatar" />
            </q-avatar>
            <div>
              <div class="text-weight-bold">{{ post.user.name }}</div>
              <div class="text-caption text-grey-6">{{ formatRelativeDate(post.createdAt) }}</div>
            </div>
          </div>
        </q-card-section>
        
        <q-card-section>
          <h6>{{ post.plan.name }}</h6>
          <p>{{ post.plan.description }}</p>
        </q-card-section>
        
        <q-card-actions>
          <q-btn flat icon="favorite" :label="post.likes" @click="toggleLike(post.id)" />
          <q-btn flat icon="comment" :label="post.comments.length" />
          <q-btn flat icon="share" />
        </q-card-actions>
      </q-card>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useSocialStore } from '../stores/socialStore'

const props = defineProps<{ currentUserId: string }>()
const socialStore = useSocialStore()
const posts = ref([])

onMounted(async () => {
  posts.value = await socialStore.fetchPublicPosts()
})

const toggleLike = async (postId: string) => {
  await socialStore.toggleLike(postId, props.currentUserId)
}
</script>
```

**Backend nécessaire:**
```typescript
// apps/api/src/modules/social/social.routes.ts

fastify.get('/api/social/posts', {
  preHandler: [requireAuth],
  handler: async (req, reply) => {
    const posts = await socialService.getPublicPosts(req.user.id, { limit: 20 })
    return reply.send(posts)
  }
})
```

**Effort:** 2 jours  
**Impact:** Landing page complète, engagement utilisateurs

---

## 🧹 Priorité MOYENNE

### 6. Nettoyer les console.log (112+ occurrences)

**Stratégie:**
```bash
# Identifier les debug vs intentionnels
rg "console\.(log|warn|error)" apps/web/src --type vue --type ts > console_audit.txt

# Remplacer par logger structuré
console.log('Debug:', data) → logger.debug('Debug', { data })
console.warn('[Module]', msg) → logger.warn(msg, { module: 'Module' })
console.error('Error:', err) → logger.error('Error', { error: err })

# Supprimer les temporaires
console.log('TODO: implement') → // TODO: implement
```

**Effort:** 0.5 jour  
**Impact:** Logs production propres, debugging facilité

---

### 7. Résoudre les 17 TODOs

**Audit:**
```bash
rg "TODO:|FIXME:|HACK:" apps/web/src --type vue --type ts -n > todos.txt
```

**Priorisation:**
1. **CRITICAL:** Split PlannerPage.vue (ligne 5414)
2. **HIGH:** Implement useCollaboration (ligne 1367, 4311)
3. **HIGH:** Define Planner types (presets.ts lignes 21-23, 28-30)
4. **MEDIUM:** Implement SocialFeed (LandingPage.vue ligne 148, 196)
5. **LOW:** Autres TODOs (refactoring, optimizations)

**Effort:** Intégré aux tâches ci-dessus  
**Impact:** Dette technique réduite

---

## 📊 Métriques de succès

| Métrique | Avant | Cible | Impact |
|----------|-------|-------|--------|
| PlannerPage.vue lignes | 8327 | <2000 | ✅ Maintenabilité |
| Types `any` dans presets.ts | 4 | 0 | ✅ Type safety |
| Modules mockés | 2 | 0 | ✅ Features fonctionnelles |
| console.log | 112+ | <10 | ✅ Logs propres |
| TODOs critiques | 17 | 0 | ✅ Qualité code |
| Type errors | 0 | 0 | ✅ Maintenu |
| Build time | 7.5s | <5s | ✅ Performance |
| Test coverage | 0% | 80% | ✅ Fiabilité |

---

## 🗓️ Planning suggéré

### Sprint 1 (5 jours) - CRITIQUE
- ✅ **Jour 1-3:** Refactoriser PlannerPage.vue
- ✅ **Jour 4:** Créer types Planner dans @my-roadtrip/types
- ✅ **Jour 5:** Tests unitaires nouveaux composants

### Sprint 2 (5 jours) - HAUTE
- ✅ **Jour 1-2:** Implémenter useCollaboration + backend
- ✅ **Jour 3:** Implémenter locationTrackingStore
- ✅ **Jour 4-5:** Créer module Social (feed + store)

### Sprint 3 (2 jours) - MOYENNE
- ✅ **Jour 1:** Nettoyer console.log + TODOs
- ✅ **Jour 2:** Documentation + tests finaux

**Total:** 12 jours développement  
**ROI:** Dette technique éliminée, codebase maintenable long terme

---

## 🎯 Bénéfices finaux

### Qualité Code
- ✅ Fichiers < 2000 lignes (règle FOCUS)
- ✅ Type safety complète (0 `any`)
- ✅ Cognitive complexity < 15
- ✅ Pas de workarounds temporaires

### Productivité Équipe
- ✅ Code review facilité (fichiers small)
- ✅ Onboarding rapide (architecture claire)
- ✅ Tests automatisés (regression prevented)
- ✅ IDE autocomplete fonctionnel

### Features Utilisateurs
- ✅ Collaboration temps réel (CRDT sync)
- ✅ GPS tracking précis
- ✅ Social feed engageant
- ✅ Planner stable et performant

### Maintenance Long Terme
- ✅ Dette technique = 0
- ✅ Documentation à jour
- ✅ Patterns réutilisables
- ✅ Évolution facilitée

---

## 🚀 Action immédiate

**PRIORITÉ 1:** Créer ticket GitHub "Refactor PlannerPage.vue into components"
```markdown
## Context
PlannerPage.vue = 8327 lignes (4x limite recommandée)
- Type instantiation depth error (workaround temporaire)
- Cognitive complexity 17/15
- Impossible à tester/review efficacement

## Objective
Split en 4 fichiers max 2000 lignes:
- PlannerPage.vue (orchestrator)
- PlannerMap.vue (Mapbox)
- PlannerWizard.vue (form)
- PlannerPresets.vue (config)

## Success Criteria
- ✅ 0 type errors
- ✅ Cognitive complexity < 15
- ✅ Tests unitaires 80%+
- ✅ Build time -30%
```

**Assignation:** Lead dev + 1 dev
**Sprint:** Prochain (priorité absolue)
**Review:** Architecture team avant merge

---

*Ce roadmap transforme les **workarounds d'urgence** en **architecture solide** pour les 2 prochaines années.*
