v1.1.4 - correctif de bug mineur + features.
This commit is contained in:
parent
2f4fe578ab
commit
4dae272706
12
README.md
12
README.md
@ -70,6 +70,18 @@ dd-timer/
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.1.4
|
||||
- ✨ **Reinitialisation des statistiques** — bouton dans l'onglet Statistiques pour remettre a zero tous les bebes, historiques et stats archivees (avec confirmation)
|
||||
- 🔧 Correction "vider l'enclos" / "nouvelle fournee" / "supprimer enclos" — remplacement de confirm() par un dialogue natif Electron pour corriger le bug qui rendait tous les inputs inutilisables
|
||||
- 🔧 Correction du bouton "Quitter" — utilisation de process.exit() pour forcer l'arret complet du processus
|
||||
|
||||
### v1.1.3
|
||||
- ✨ **Inputs intelligents** — les champs numeriques se vident au clic pour saisir facilement, et restaurent la valeur precedente si rien n'est change
|
||||
- 🔧 Correction du bug "enclos vide" — apres avoir vide un enclos ou lance une nouvelle fournee, une dragodinde est automatiquement ajoutee et les inputs restent editables
|
||||
- 🔧 Correction "nouvelle fournee" — ne vide plus l'historique de session des bebes, seul "vider l'enclos" le fait
|
||||
- 🔧 Correction des statistiques globales — supprimer un enclos conserve desormais ses stats dans les statistiques globales (archivage automatique)
|
||||
- 🔧 Correction du bouton "Quitter" — la fermeture complete de l'app fonctionne maintenant correctement
|
||||
|
||||
### v1.1.2
|
||||
- 💾 **Sauvegarde persistante** — les donnees sont maintenant sauvegardees dans un fichier JSON, plus de perte de statistiques apres une mise a jour
|
||||
- ❌ **Dialogue de fermeture** — cliquer sur la croix propose de minimiser en arriere-plan ou de quitter completement
|
||||
|
||||
18
main.js
18
main.js
@ -65,8 +65,8 @@ function createWindow() {
|
||||
detail: 'Minimiser garde l\'app en arriere-plan.\nLes alarmes continueront de sonner.',
|
||||
});
|
||||
if (choice === 1) {
|
||||
isQuitting = true;
|
||||
app.quit();
|
||||
if (tray) { tray.destroy(); tray = null; }
|
||||
process.exit(0);
|
||||
} else {
|
||||
mainWindow.hide();
|
||||
}
|
||||
@ -142,6 +142,20 @@ ipcMain.on('show-notification', (event, { title, body }) => {
|
||||
fireNotification(title, body);
|
||||
});
|
||||
|
||||
// Dialogue de confirmation natif (remplace confirm() du renderer qui casse les inputs)
|
||||
ipcMain.handle('show-confirm', (event, { title, message, detail }) => {
|
||||
const choice = dialog.showMessageBoxSync(mainWindow, {
|
||||
type: 'question',
|
||||
buttons: ['Annuler', 'Confirmer'],
|
||||
defaultId: 0,
|
||||
cancelId: 0,
|
||||
title: title || 'Confirmation',
|
||||
message: message || '',
|
||||
detail: detail || '',
|
||||
});
|
||||
return choice === 1;
|
||||
});
|
||||
|
||||
// ─── SAUVEGARDE FICHIER (persistante entre mises à jour) ─────────────────
|
||||
const dataFile = path.join(app.getPath('userData'), 'dd-timer-data.json');
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "minuteur-dragodinde",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.4",
|
||||
"description": "Minuteur elevage Dragodinde Dofus 3",
|
||||
"main": "main.js",
|
||||
"author": "Mickael",
|
||||
|
||||
@ -12,6 +12,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
showNotification: (title, body) => ipcRenderer.send('show-notification', { title, body }),
|
||||
sendNtfy: (url, title, message) => ipcRenderer.send('send-ntfy', { url, title, message }),
|
||||
focusWindow: () => ipcRenderer.send('focus-window'),
|
||||
showConfirm: (title, message, detail) => ipcRenderer.invoke('show-confirm', { title, message, detail }),
|
||||
onPlayAlarmSound: (cb) => ipcRenderer.on('play-alarm-sound', () => cb()),
|
||||
|
||||
// Version
|
||||
|
||||
@ -569,7 +569,7 @@ const RACES_DATA={
|
||||
};
|
||||
|
||||
|
||||
let S={enclos:[],activeId:null,nextEnclosId:1,alarmSound:'arpege',notifsEnabled:true,ntfyTopic:''};
|
||||
let S={enclos:[],activeId:null,nextEnclosId:1,alarmSound:'arpege',notifsEnabled:true,ntfyTopic:'',archivedStats:[]};
|
||||
const NTFY_BASE='https://ntfy.mickael-pol.fr';
|
||||
const NTFY_REDIRECT='https://ntfy-redirect.mickael-pol.fr';
|
||||
const lastTickIdx={};
|
||||
@ -856,11 +856,22 @@ function addEnclos(){
|
||||
S.enclos.push(e);S.activeId=e.id;
|
||||
save();render();addDD(e.id);
|
||||
}
|
||||
function removeEnclos(id){
|
||||
async function removeEnclos(id){
|
||||
if(S.enclos.length<=1)return;
|
||||
const enc=S.enclos.find(e=>e.id===id);
|
||||
if(!enc)return;
|
||||
const ok=await window.electronAPI.showConfirm('Supprimer l\'enclos',`Supprimer "${enc.name}" ?`,'Les statistiques globales seront conservees.');
|
||||
if(!ok)return;
|
||||
// Archiver les stats (babyHistory) pour les conserver dans les statistiques globales
|
||||
const hist=enc.babyHistory||enc.babies||[];
|
||||
if(hist.length){
|
||||
if(!S.archivedStats)S.archivedStats=[];
|
||||
S.archivedStats.push(...hist.map(b=>({...b,fromEnclos:enc.name})));
|
||||
}
|
||||
S.enclos=S.enclos.filter(e=>e.id!==id);
|
||||
if(S.activeId===id||S.activeId==='dashboard')S.activeId=S.enclos[0].id;
|
||||
save();render();
|
||||
save();
|
||||
render();
|
||||
}
|
||||
function selectEnclos(id){S.activeId=id;save();renderTabs();renderContent();}
|
||||
|
||||
@ -915,20 +926,23 @@ function removeDD(encId,ddId){
|
||||
enc.dragodindes=enc.dragodindes.filter(d=>d.id!==ddId);
|
||||
save();renderDDs(enc);
|
||||
}
|
||||
function viderEnclos(encId){
|
||||
async function viderEnclos(encId){
|
||||
const enc=S.enclos.find(e=>e.id===encId);
|
||||
if(!enc)return;
|
||||
if(!confirm(`Vider "${enc.name}" ?\n\nToutes les dragodindes seront supprimées.\nLes bébés et statistiques sont conservés.`))return;
|
||||
const ok=await window.electronAPI.showConfirm('Vider l\'enclos',`Vider "${enc.name}" ?`,'Toutes les dragodindes seront supprimees.\nLes bebes et statistiques sont conserves.');
|
||||
if(!ok)return;
|
||||
enc.dragodindes=[];
|
||||
enc.nextDdId=1;
|
||||
enc.timer={running:false,startTime:null,pausedAt:null,pausedMs:0,snapGauges:{},snapStats:{}};
|
||||
enc.alerted={};
|
||||
enc.activeGauges=[];
|
||||
Object.keys(enc.gaugeLevels).forEach(k=>{enc.gaugeLevels[k]=0;});
|
||||
// babies session vide mais babyHistory intact
|
||||
enc.babies=[];
|
||||
lockInputs(encId,false);
|
||||
save();renderContent();
|
||||
// Ajouter une DD par défaut
|
||||
const newId=enc.nextDdId++;
|
||||
enc.dragodindes.push({id:newId,name:`Dragodinde ${newId}`,stats:{serenite:0,endurance:0,maturite:0,amour:0,xp:1},targets:{...DEFAULT_TARGETS}});
|
||||
save();
|
||||
render();
|
||||
}
|
||||
function updateName(encId,ddId,v){
|
||||
const enc=S.enclos.find(e=>e.id===encId);
|
||||
@ -956,18 +970,22 @@ function updateStatDirect(encId,ddId,stat,v){
|
||||
save();updateLive();
|
||||
}
|
||||
|
||||
function nouvelleFournee(encId){
|
||||
async function nouvelleFournee(encId){
|
||||
const enc=S.enclos.find(e=>e.id===encId);
|
||||
if(!enc)return;
|
||||
if(!confirm(`Nouvelle fournée pour ${enc.name} ?\n\nLes dragodindes actuelles seront supprimées.\nLes bébés et l'historique sont conservés.`))return;
|
||||
const ok=await window.electronAPI.showConfirm('Nouvelle fournee',`Nouvelle fournee pour ${enc.name} ?`,'Les dragodindes actuelles seront supprimees.\nLes bebes et l\'historique sont conserves.');
|
||||
if(!ok)return;
|
||||
enc.dragodindes=[];
|
||||
enc.nextDdId=1;
|
||||
enc.timer={running:false,startTime:null,pausedAt:null,pausedMs:0,snapGauges:{},snapStats:{}};
|
||||
enc.alerted={};
|
||||
enc.activeGauges=[];
|
||||
Object.keys(enc.gaugeLevels).forEach(k=>{enc.gaugeLevels[k]=0;});
|
||||
enc.babies=[];
|
||||
save();renderContent();
|
||||
// Ajouter une DD par défaut
|
||||
const newId=enc.nextDdId++;
|
||||
enc.dragodindes.push({id:newId,name:`Dragodinde ${newId}`,stats:{serenite:0,endurance:0,maturite:0,amour:0,xp:1},targets:{...DEFAULT_TARGETS}});
|
||||
save();
|
||||
render();
|
||||
}
|
||||
function updateStat(encId,ddId,gid,v){
|
||||
const enc=S.enclos.find(e=>e.id===encId);
|
||||
@ -1070,6 +1088,7 @@ async function load(){
|
||||
S.alarmSound=d.alarmSound||'arpege';
|
||||
S.notifsEnabled=d.notifsEnabled!==undefined?d.notifsEnabled:true;
|
||||
S.ntfyTopic=d.ntfyTopic||'';
|
||||
S.archivedStats=d.archivedStats||[];
|
||||
// Migration: ancien format ntfyUrl → ntfyTopic
|
||||
if(!S.ntfyTopic&&d.ntfyUrl){const m=d.ntfyUrl.match(/\/([^\/]+)$/);if(m)S.ntfyTopic=m[1];}
|
||||
S.enclos.forEach(enc=>{
|
||||
@ -1315,6 +1334,8 @@ function renderGaugesCfg(enc){
|
||||
<input type="number" class="gauge-inp" min="0" max="100000" step="1000"
|
||||
value="${lvl}" style="border-color:${color}44"
|
||||
oninput="updateGaugeLevel(${enc.id},'${gid}',this.value)"
|
||||
onfocus="this.dataset.prev=this.value;this.value=''"
|
||||
onblur="if(this.value==='')this.value=this.dataset.prev"
|
||||
${enc.timer.running?'disabled':''}>
|
||||
<span style="color:var(--muted);font-size:11px">/ 100 000</span>
|
||||
</div>
|
||||
@ -1390,6 +1411,8 @@ function renderDDs(enc){
|
||||
<input type="number" id="pstat-${enc.id}-${dd.id}-${p.stat}"
|
||||
value="${val}" min="${p.min}" max="${p.max}" step="${p.stat==='xp'?1:10}"
|
||||
oninput="updateStatDirect(${enc.id},${dd.id},'${p.stat}',this.value)"
|
||||
onfocus="this.dataset.prev=this.value;this.value=''"
|
||||
onblur="if(this.value==='')this.value=this.dataset.prev"
|
||||
title="${p.stat}">
|
||||
</div>`;
|
||||
});
|
||||
@ -1987,17 +2010,41 @@ function changeCount(delta){
|
||||
// ══════════════════════════════════════════
|
||||
// STATS TAB
|
||||
// ══════════════════════════════════════════
|
||||
async function resetStats(){
|
||||
const ok=await window.electronAPI.showConfirm(
|
||||
'Reinitialiser les statistiques',
|
||||
'Reinitialiser toutes les statistiques ?',
|
||||
'Cette action est irreversible.\nTous les bebes enregistres, l\'historique de chaque enclos et les statistiques archivees seront definitivement supprimes.'
|
||||
);
|
||||
if(!ok)return;
|
||||
S.archivedStats=[];
|
||||
S.enclos.forEach(enc=>{
|
||||
enc.babies=[];
|
||||
enc.babyHistory=[];
|
||||
});
|
||||
save();
|
||||
render();
|
||||
}
|
||||
function renderStats(){
|
||||
const c=document.getElementById('enclos-content');
|
||||
c.removeAttribute('data-enc');
|
||||
|
||||
// Agréger toutes les données
|
||||
// Agréger toutes les données (enclos actifs + stats archivées des enclos supprimés)
|
||||
let totalDD=0,totalBabies=0,totalSessions=0;
|
||||
const raceMap={};
|
||||
const enclosStats=[];
|
||||
|
||||
// Stats archivées (enclos supprimés)
|
||||
const archived=S.archivedStats||[];
|
||||
archived.forEach(b=>{
|
||||
if(!raceMap[b.race])raceMap[b.race]={count:0,gen:b.gen};
|
||||
raceMap[b.race].count+=b.count;
|
||||
totalBabies+=b.count;
|
||||
});
|
||||
const archivedMax=archived.reduce((s,b)=>s+Math.min(5,Math.floor((b.ddCount||1)/2)),0);
|
||||
|
||||
S.enclos.forEach(enc=>{
|
||||
const babies=(enc.babyHistory||enc.babies||[]); // utilise l'historique permanent
|
||||
const babies=(enc.babyHistory||enc.babies||[]);
|
||||
const nbBabies=babies.reduce((s,b)=>s+b.count,0);
|
||||
const nbDD=enc.dragodindes.length;
|
||||
totalDD+=nbDD;
|
||||
@ -2011,8 +2058,6 @@ function renderStats(){
|
||||
});
|
||||
|
||||
if(nbBabies>0){
|
||||
// Calcul taux de réussite basé sur le ddCount stocké lors de l'ajout
|
||||
// → résiste à la suppression/nouvelle fournée de DD
|
||||
const maxPossible=babies.reduce((s,b)=>{
|
||||
const m=Math.min(5,Math.floor((b.ddCount||1)/2));
|
||||
return s+m;
|
||||
@ -2022,8 +2067,8 @@ function renderStats(){
|
||||
}
|
||||
});
|
||||
|
||||
// Taux global basé sur les ddCount stockés
|
||||
const totalMax=S.enclos.reduce((s,e)=>{
|
||||
// Taux global (enclos actifs + archivés)
|
||||
const totalMax=archivedMax+S.enclos.reduce((s,e)=>{
|
||||
const hist=(e.babyHistory||e.babies||[]);
|
||||
return s+hist.reduce((ss,b)=>ss+Math.min(5,Math.floor((b.ddCount||1)/2)),0);
|
||||
},0);
|
||||
@ -2034,8 +2079,16 @@ function renderStats(){
|
||||
const maxRaceCount=racesSorted[0]?.[1].count||1;
|
||||
|
||||
c.innerHTML=`
|
||||
<div style="font-family:'Cinzel',serif;font-size:0.8rem;letter-spacing:2.5px;text-transform:uppercase;color:var(--muted);margin-bottom:6px">
|
||||
Statistiques globales
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
|
||||
<div style="font-family:'Cinzel',serif;font-size:0.8rem;letter-spacing:2.5px;text-transform:uppercase;color:var(--muted)">
|
||||
Statistiques globales
|
||||
</div>
|
||||
<button onclick="resetStats()" style="padding:6px 14px;border-radius:8px;border:1px solid var(--border);background:transparent;color:var(--muted);cursor:pointer;font:700 0.82rem 'Nunito',sans-serif;transition:.15s"
|
||||
onmouseover="this.style.borderColor='var(--amour)';this.style.color='var(--amour)'"
|
||||
onmouseout="this.style.borderColor='var(--border)';this.style.color='var(--muted)'"
|
||||
title="Remettre toutes les statistiques a zero">
|
||||
🗑 Reinitialiser les statistiques
|
||||
</button>
|
||||
</div>
|
||||
<div class="stats-wrap">
|
||||
<div class="stats-section">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user