diff --git a/README.md b/README.md index d93aab9..cb836a8 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,17 @@ dd-timer/ ## Changelog +### v1.1.5 +- ✨ **Onglet Accouplement** — selection des 2 parents, deduction automatique du bebe, saisie du nombre de couples et bebes obtenus pour alimenter les statistiques globales +- ✨ **Sidebar navigation** — menu hamburger avec panneau lateral overlay (Dashboard, Enclos, Accouplement, Reappro, Inventaire, Workflows, Parametres) +- ✨ **Onglet Parametres** — son d'alarme, notifications PC et mobile regroupes dans une vue dediee accessible via la sidebar +- ✨ **Reappro ♂/♀** — chaque etape de l'arbre affiche les genres necessaires (♂/♀) avec toggle d'inversion par croisement +- 🎨 Statistiques globales integrees au Dashboard (bebes, couples, taux de reussite, races obtenues) +- 🎨 Onglets horizontaux simplifies — seuls Dashboard et Enclos restent, le reste est dans la sidebar +- 🔧 Retrait du systeme de bebes des enclos — les accouplements se font desormais via l'onglet dedie +- 🔧 Migration automatique des donnees bebes existantes vers les statistiques archivees +- 🗑 Suppression de l'onglet Statistiques (donnees conservees dans le Dashboard) + ### v1.1.4 - ✨ **Cible serenite par DD** — champ cible avec ETA en temps reel (calcul automatique baffeur/caresseur necessaire) - ✨ **Cible niveau par DD** — champ cible avec ETA en temps reel (modele XP lineaire base sur le tier de la mangeoire) diff --git a/docs/plans/2026-03-27-v1.2.0-design.md b/docs/plans/2026-03-27-v1.2.0-design.md new file mode 100644 index 0000000..8b5b7b1 --- /dev/null +++ b/docs/plans/2026-03-27-v1.2.0-design.md @@ -0,0 +1,47 @@ +# Design — v1.2.0 (Accouplement, Sidebar, Réappro ♂/♀) + +## 1. Sidebar navigation + +Bouton hamburger (☰) fixe en haut à gauche. Panneau overlay glissant depuis la gauche par-dessus le contenu (fond semi-transparent). Se ferme au clic sur un item ou sur le fond. + +Contenu : +- Dashboard +- Enclos 1 à 6 (dynamique, nom personnalisé) +- Accouplement (nouveau) +- Réappro +- Inventaire +- Workflows + +La barre d'onglets horizontale actuelle reste en place. + +## 2. Dashboard enrichi + +Ajouts : +- **Section "Statistiques globales"** : tout ce qui est dans l'onglet Stats actuel (KPIs, bébés par race, taux de réussite, stats par enclos). L'onglet Stats est supprimé de SPECIAL_TABS. +- **Section "Paramètres"** : card dédiée en bas avec sélecteur de son, toggle notifications Windows, bouton ntfy mobile. Retiré du header dashboard. + +## 3. Onglet Accouplement + +Workflow : +1. Sélection Parent 1 : grille de cards filtrables par gen (image + badge gen + nom) +2. Sélection Parent 2 : filtré aux races compatibles avec Parent 1 +3. Résultat : card du bébé déduit (image + gen + nom) +4. Saisie : nombre de couples + nombre de bébés obtenus +5. Bouton "Enregistrer" → alimente les stats globales + +Données : `S.accouplements` — tableau de `{ parentA, parentB, baby, gen, couples, babiesObtained, date }` + +Stats globales agrègent ces données comme elles agrègent actuellement `enc.babyHistory`. + +## 4. Retrait des bébés des enclos + +- Suppression bouton "Ajouter bébé", sous-onglets "Élevage / Historique bébés" +- Suppression de `enc.babies` et `enc.babyHistory` dans les nouveaux enclos +- Migration : les `enc.babyHistory` existants → `S.archivedStats` au premier lancement +- `isBabyUnlocked()` supprimé + +## 5. Réappro ♂/♀ + +Chaque étape de l'arbre indique ♂ et ♀ nécessaires. Convention : Parent A = ♂, Parent B = ♀ dans BREEDING_RECIPES. Toggle pour inverser par étape. + +Affichage : `×4 ♂` ou `×4 ♀` sur chaque mini-card parent. Résumé gen 1 : "4♂ Rousse + 4♀ Dorée". diff --git a/docs/plans/2026-03-27-v1.2.0-implementation.md b/docs/plans/2026-03-27-v1.2.0-implementation.md new file mode 100644 index 0000000..ef6ec41 --- /dev/null +++ b/docs/plans/2026-03-27-v1.2.0-implementation.md @@ -0,0 +1,640 @@ +# 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=`
Dofus 3 · Gestion multi-enclos en temps réel ·