# v1.2.0 Implementation Plan — Accouplement, Sidebar, Réappro ♂/♀ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Ajouter un onglet Accouplement dédié, une sidebar de navigation overlay, intégrer les stats dans le dashboard, et ajouter les contraintes ♂/♀ dans la réappro. **Architecture:** Tout le code est dans `src/index.html` (monolithique). On modifie le CSS, le HTML, et le JS inline. Pas de framework. Rendu impératif via innerHTML. État central dans `S`, persisté via `save()`. **Tech Stack:** Vanilla JS, CSS custom properties, Electron IPC. **Fichier principal:** `/mnt/c/Users/micka/Desktop/dd-timer/src/index.html` --- ## Task 1 : Retrait des bébés des enclos + migration **But :** Supprimer le système de bébés des enclos et migrer les données existantes vers `S.archivedStats`. **Files:** - Modify: `src/index.html` **Step 1 : Migration des données au chargement** Dans la fonction `load()` ou juste après le chargement de `S`, ajouter une migration one-shot : ```javascript // Migration v1.2.0 : archiver les babyHistory des enclos if(!S._migratedBabies120){ S.enclos.forEach(enc=>{ if(enc.babyHistory&&enc.babyHistory.length){ if(!S.archivedStats)S.archivedStats={babies:[],totalMax:0}; if(!S.archivedStats.babies)S.archivedStats.babies=[]; enc.babyHistory.forEach(b=>{ S.archivedStats.babies.push({...b,enclosName:enc.name||('Enclos '+(S.enclos.indexOf(enc)+1))}); S.archivedStats.totalMax=(S.archivedStats.totalMax||0)+Math.min(5,Math.floor((b.ddCount||2)/2)); }); } }); S._migratedBabies120=true; save(); } ``` **Step 2 : Supprimer le code des bébés dans les enclos** Supprimer ou commenter les fonctions et le code suivant : - `isBabyUnlocked()` (ligne ~2095) - `updateBabyBtnState()` (ligne ~2101) - `maxBabiesForEnclos()` (ligne ~2115) - `currentBabiesCount()` (ligne ~2118) - `openBabyModal()` (ligne ~2127) - `closeBabyModal()` (ligne ~2134) - `modalBack()`, `modalNext()`, `renderModalStep()` (lignes ~2138-2250) - `selectModalGen()`, `selectModalRaceByIdx()`, `selectModalRace()`, `changeCount()` (lignes ~2250-2285) - `renderBabies()` (ligne ~2015) - `renderBabyHistory()` (ligne ~2049) - `switchSubTab()` (ligne ~2001) - `removeBaby()` (ligne ~2079) - `clearSessionBabies()` (ligne ~2087) - Le HTML du modal bébé (lignes ~352-369) **Step 3 : Nettoyer renderContent pour les enclos** Dans `renderContent()` (ligne ~1465), retirer les sous-onglets "elevage"/"history" et le bouton bébé du rendu des enclos. **Step 4 : Commit** ``` feat: retire bébés des enclos, migre données vers archivedStats ``` --- ## Task 2 : Onglet Accouplement — données et constante **But :** Créer la structure de données et le lookup inversé pour les accouplements. **Files:** - Modify: `src/index.html` **Step 1 : Ajouter 'accouplement' dans SPECIAL_TABS** Ligne ~698 : ```javascript const SPECIAL_TABS=['dashboard','appro','inventaire','accouplement','workflows']; // Note : 'stats' retiré (sera dans le dashboard) ``` **Step 2 : Créer le lookup inversé BREEDING_BY_PARENTS** Après `BREEDING_RECIPES` (ligne ~677) : ```javascript // Lookup inversé : "ParentA|ParentB" → babyRace const BREEDING_BY_PARENTS={}; Object.entries(BREEDING_RECIPES).forEach(([baby,[a,b]])=>{ BREEDING_BY_PARENTS[a+'|'+b]=baby; if(a!==b) BREEDING_BY_PARENTS[b+'|'+a]=baby; }); // Lookup : pour un parent donné, quels partenaires sont possibles ? const COMPATIBLE_PARTNERS={}; Object.entries(BREEDING_RECIPES).forEach(([baby,[a,b]])=>{ if(!COMPATIBLE_PARTNERS[a])COMPATIBLE_PARTNERS[a]=[]; COMPATIBLE_PARTNERS[a].push({partner:b,baby,gen:RACE_GEN[baby]}); if(a!==b){ if(!COMPATIBLE_PARTNERS[b])COMPATIBLE_PARTNERS[b]=[]; COMPATIBLE_PARTNERS[b].push({partner:a,baby,gen:RACE_GEN[baby]}); } }); ``` **Step 3 : Initialiser S.accouplements** Dans le chargement de S (après `load()`) : ```javascript if(!S.accouplements) S.accouplements=[]; ``` **Step 4 : Commit** ``` feat: ajoute structures données accouplement et lookup inversé ``` --- ## Task 3 : Onglet Accouplement — rendu UI **But :** Interface complète de l'onglet accouplement. **Files:** - Modify: `src/index.html` (CSS + JS) **Step 1 : CSS pour l'onglet accouplement** Ajouter dans la section ``, ligne ~313) : ```css /* Accouplement */ .accoup-grid{display:flex;flex-wrap:wrap;gap:10px;justify-content:center} .accoup-card{background:var(--bg2);border:2px solid var(--border);border-radius:var(--r);padding:10px;cursor:pointer;text-align:center;width:100px;transition:border-color .2s,transform .15s} .accoup-card:hover{border-color:var(--text);transform:translateY(-2px)} .accoup-card.selected{border-color:var(--ok);box-shadow:0 0 12px rgba(40,232,136,.25)} .accoup-result{display:flex;align-items:center;gap:20px;justify-content:center;flex-wrap:wrap;padding:20px 0} .accoup-arrow{font-size:2rem;color:var(--muted)} .accoup-inputs{display:flex;gap:16px;align-items:center;justify-content:center;flex-wrap:wrap;padding:16px 0} .accoup-input-group{display:flex;flex-direction:column;align-items:center;gap:6px} .accoup-input-group label{font-size:.82rem;color:var(--muted)} .accoup-input-group input{width:70px;background:var(--bg2);border:1px solid var(--border);border-radius:8px;color:var(--text);padding:8px;font:700 1rem 'Nunito',sans-serif;text-align:center} .accoup-gen-tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px;justify-content:center} .accoup-gen-tab{padding:4px 12px;border-radius:8px;font-size:.78rem;font-weight:700;cursor:pointer;border:1px solid var(--border);background:var(--bg2);color:var(--muted);transition:all .2s} .accoup-gen-tab.active{background:var(--ok);color:#fff;border-color:var(--ok)} ``` **Step 2 : État local de l'onglet** ```javascript const accoupState={parent1:null,parent2:null,filterGen:null,couples:'',babies:''}; ``` **Step 3 : Fonction renderAccouplement()** ```javascript function renderAccouplement(){ const c=document.getElementById('enclos-content'); c.removeAttribute('data-enc'); const allRaces=Object.keys(RACE_GEN).sort((a,b)=>(RACE_GEN[a]-RACE_GEN[b])||a.localeCompare(b)); const gens=[...new Set(Object.values(RACE_GEN))].sort((a,b)=>a-b); // Si parent1 sélectionné, filtrer les partenaires compatibles let availableRaces=allRaces; if(accoupState.parent1){ const partners=COMPATIBLE_PARTNERS[accoupState.parent1]||[]; availableRaces=partners.map(p=>p.partner); } // Filtrer par gen const fg=accoupState.parent1?null:accoupState.filterGen; const filtered=fg?availableRaces.filter(r=>RACE_GEN[r]===fg):availableRaces; // Déduire le bébé let baby=null,babyGen=null; if(accoupState.parent1&&accoupState.parent2){ baby=BREEDING_BY_PARENTS[accoupState.parent1+'|'+accoupState.parent2]||null; if(baby)babyGen=RACE_GEN[baby]; } // Gen tabs (uniquement si pas de parent1 sélectionné) let genTabs=''; if(!accoupState.parent1){ genTabs=`