feat: Système de persistance des notes amélioré avec fichier unique par agent
- Un seul fichier notes_{agentId}.json par agent (plus d'accumulation)
- Auto-save après 2 secondes d'inactivité
- Restauration automatique au démarrage depuis fichier ou localStorage
- Historique des 50 dernières versions intégré dans le fichier
- Synchronisation transparente fichier/localStorage
- Notifications visuelles lors de la restauration
This commit is contained in:
@@ -1,5 +1,35 @@
|
||||
# Changelog - SimpleConnect Electron
|
||||
|
||||
## [1.2.14] - 2025-09-04
|
||||
|
||||
### Ajouté
|
||||
- **Système de persistance des notes amélioré** : Sauvegarde et restauration automatiques
|
||||
- Auto-save après 2 secondes d'inactivité
|
||||
- Restauration automatique des notes au démarrage
|
||||
- Synchronisation localStorage + fichier serveur
|
||||
- Notification visuelle lors de la restauration
|
||||
- Historique local des 20 dernières notes dans localStorage
|
||||
|
||||
### Modifié
|
||||
- **Gestion des fichiers de notes** : Un seul fichier par agent au lieu de multiples
|
||||
- Format `notes_{agentId}.json` unique par agent
|
||||
- Mise à jour du même fichier à chaque sauvegarde
|
||||
- Historique des 50 dernières versions intégré dans le fichier
|
||||
- Plus d'accumulation de fichiers datés
|
||||
|
||||
### Amélioré
|
||||
- **Expérience utilisateur des notes** : Persistance transparente
|
||||
- Chargement prioritaire depuis le fichier serveur
|
||||
- Fallback sur localStorage si fichier absent
|
||||
- Bouton "Effacer" vide aussi localStorage
|
||||
- Messages de confirmation et notifications
|
||||
|
||||
### Technique
|
||||
- Nouvelle fonction `loadSavedNotes()` asynchrone
|
||||
- Handler IPC `get-notes` pour récupérer depuis le serveur
|
||||
- Auto-save avec debouncing de 2 secondes
|
||||
- Structure JSON avec note courante + historique
|
||||
|
||||
## [1.2.13] - 2025-09-04
|
||||
|
||||
### Corrigé
|
||||
|
||||
60
main.js
60
main.js
@@ -471,25 +471,73 @@ ipcMain.handle('get-simulated-calls', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Sauvegarder les notes de l'agent
|
||||
// Sauvegarder les notes de l'agent (un seul fichier par agent)
|
||||
ipcMain.handle('save-notes', (event, noteData) => {
|
||||
const notesDir = path.join(__dirname, 'notes');
|
||||
if (!fs.existsSync(notesDir)) {
|
||||
fs.mkdirSync(notesDir);
|
||||
}
|
||||
|
||||
const fileName = `notes_${currentAgent.id}_${Date.now()}.json`;
|
||||
// Un seul fichier par agent, mis à jour à chaque sauvegarde
|
||||
const fileName = `notes_${currentAgent.id}.json`;
|
||||
const filePath = path.join(notesDir, fileName);
|
||||
|
||||
fs.writeFileSync(filePath, JSON.stringify({
|
||||
// Lire l'historique existant si le fichier existe
|
||||
let notesData = {
|
||||
agent: currentAgent.id,
|
||||
timestamp: new Date().toISOString(),
|
||||
...noteData
|
||||
}, null, 2));
|
||||
agentName: currentAgent.name,
|
||||
currentNote: noteData.content,
|
||||
lastModified: new Date().toISOString(),
|
||||
centre: noteData.centre,
|
||||
history: []
|
||||
};
|
||||
|
||||
// Si le fichier existe, préserver l'historique
|
||||
if (fs.existsSync(filePath)) {
|
||||
try {
|
||||
const existingData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
// Ajouter l'ancienne note à l'historique si elle a changé
|
||||
if (existingData.currentNote && existingData.currentNote !== noteData.content) {
|
||||
notesData.history = existingData.history || [];
|
||||
notesData.history.unshift({
|
||||
content: existingData.currentNote,
|
||||
date: existingData.lastModified,
|
||||
centre: existingData.centre
|
||||
});
|
||||
// Limiter l'historique à 50 entrées
|
||||
notesData.history = notesData.history.slice(0, 50);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erreur lecture notes existantes:', error);
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(filePath, JSON.stringify(notesData, null, 2));
|
||||
|
||||
return { success: true, file: fileName };
|
||||
});
|
||||
|
||||
// Récupérer les notes de l'agent
|
||||
ipcMain.handle('get-notes', () => {
|
||||
if (!currentAgent) return null;
|
||||
|
||||
const notesDir = path.join(__dirname, 'notes');
|
||||
const fileName = `notes_${currentAgent.id}.json`;
|
||||
const filePath = path.join(notesDir, fileName);
|
||||
|
||||
if (fs.existsSync(filePath)) {
|
||||
try {
|
||||
const data = fs.readFileSync(filePath, 'utf8');
|
||||
return JSON.parse(data);
|
||||
} catch (error) {
|
||||
console.error('Erreur lecture notes:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
// Obtenir l'historique des appels
|
||||
ipcMain.handle('get-call-history', () => {
|
||||
const historyFile = path.join(__dirname, 'call_history.json');
|
||||
|
||||
117
renderer.js
117
renderer.js
@@ -40,6 +40,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
currentAgent = agentData.agent;
|
||||
currentCentres = agentData.centres;
|
||||
showMainPage();
|
||||
// Les notes seront chargées dans showMainPage()
|
||||
} else {
|
||||
// S'assurer que le formulaire est propre au démarrage
|
||||
resetLoginForm();
|
||||
@@ -437,6 +438,12 @@ function showMainPage() {
|
||||
});
|
||||
selectCenter(sortedCentres[0].id);
|
||||
}
|
||||
|
||||
// Charger les notes sauvegardées APRÈS que la page soit affichée
|
||||
setTimeout(() => {
|
||||
loadSavedNotes();
|
||||
setupAutoSave();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// === GESTION DES CENTRES ===
|
||||
@@ -692,6 +699,11 @@ async function saveNotes() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sauvegarder dans localStorage pour persistance immédiate
|
||||
localStorage.setItem('currentNotes', notes);
|
||||
localStorage.setItem('currentNotesDate', new Date().toISOString());
|
||||
|
||||
// Sauvegarder aussi dans un fichier pour historique
|
||||
const result = await ipcRenderer.invoke('save-notes', {
|
||||
content: notes,
|
||||
centre: activeCenter
|
||||
@@ -699,11 +711,89 @@ async function saveNotes() {
|
||||
|
||||
if (result.success) {
|
||||
showNotification('Notes sauvegardées avec succès !', 'success');
|
||||
// Optionnel : garder les notes ou les effacer
|
||||
// document.getElementById('quickNotes').value = '';
|
||||
// Ajouter à l'historique local
|
||||
addToNotesHistory(notes, activeCenter);
|
||||
}
|
||||
}
|
||||
|
||||
// Charger les notes sauvegardées au démarrage
|
||||
async function loadSavedNotes() {
|
||||
const textarea = document.getElementById('quickNotes');
|
||||
if (!textarea) return;
|
||||
|
||||
// D'abord essayer de charger depuis le fichier serveur
|
||||
try {
|
||||
const serverNotes = await ipcRenderer.invoke('get-notes');
|
||||
if (serverNotes && serverNotes.currentNote) {
|
||||
textarea.value = serverNotes.currentNote;
|
||||
|
||||
// Sauvegarder aussi dans localStorage pour synchronisation
|
||||
localStorage.setItem('currentNotes', serverNotes.currentNote);
|
||||
localStorage.setItem('currentNotesDate', serverNotes.lastModified);
|
||||
|
||||
const date = new Date(serverNotes.lastModified);
|
||||
const formattedDate = date.toLocaleString('fr-FR', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
|
||||
showNotification(`Notes restaurées (${formattedDate})`, 'info');
|
||||
console.log(`Notes chargées depuis le serveur (${formattedDate})`);
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erreur chargement notes serveur:', error);
|
||||
}
|
||||
|
||||
// Sinon, charger depuis localStorage
|
||||
const savedNotes = localStorage.getItem('currentNotes');
|
||||
const savedDate = localStorage.getItem('currentNotesDate');
|
||||
|
||||
if (savedNotes) {
|
||||
textarea.value = savedNotes;
|
||||
|
||||
if (savedDate) {
|
||||
const date = new Date(savedDate);
|
||||
const formattedDate = date.toLocaleString('fr-FR', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
console.log(`Notes restaurées depuis localStorage (${formattedDate})`);
|
||||
}
|
||||
} else {
|
||||
textarea.value = '';
|
||||
console.log('Aucune note sauvegardée à restaurer');
|
||||
}
|
||||
}
|
||||
|
||||
// Ajouter à l'historique des notes
|
||||
function addToNotesHistory(content, centre) {
|
||||
let history = JSON.parse(localStorage.getItem('notesHistory') || '[]');
|
||||
|
||||
history.unshift({
|
||||
content: content,
|
||||
centre: centre,
|
||||
date: new Date().toISOString(),
|
||||
id: Date.now()
|
||||
});
|
||||
|
||||
// Garder seulement les 20 dernières notes
|
||||
history = history.slice(0, 20);
|
||||
|
||||
localStorage.setItem('notesHistory', JSON.stringify(history));
|
||||
}
|
||||
|
||||
// Récupérer l'historique des notes
|
||||
function getNotesHistory() {
|
||||
return JSON.parse(localStorage.getItem('notesHistory') || '[]');
|
||||
}
|
||||
|
||||
// === GESTION DES TERMINAUX ===
|
||||
let availableTerminals = []; // Stocker les terminaux disponibles
|
||||
let terminalChoices = null; // Instance Choices.js
|
||||
@@ -931,7 +1021,30 @@ function clearNotes() {
|
||||
const textarea = document.getElementById('quickNotes');
|
||||
if (textarea && confirm('Êtes-vous sûr de vouloir effacer toutes les notes ?')) {
|
||||
textarea.value = '';
|
||||
// Effacer aussi de localStorage
|
||||
localStorage.removeItem('currentNotes');
|
||||
localStorage.removeItem('currentNotesDate');
|
||||
textarea.focus();
|
||||
showNotification('Notes effacées', 'info');
|
||||
}
|
||||
}
|
||||
|
||||
// Sauvegarder automatiquement les notes en cours de frappe (debounced)
|
||||
let autoSaveTimeout;
|
||||
function setupAutoSave() {
|
||||
const textarea = document.getElementById('quickNotes');
|
||||
if (textarea) {
|
||||
textarea.addEventListener('input', () => {
|
||||
clearTimeout(autoSaveTimeout);
|
||||
autoSaveTimeout = setTimeout(() => {
|
||||
const notes = textarea.value;
|
||||
if (notes.trim()) {
|
||||
localStorage.setItem('currentNotes', notes);
|
||||
localStorage.setItem('currentNotesDate', new Date().toISOString());
|
||||
console.log('Notes auto-sauvegardées');
|
||||
}
|
||||
}, 2000); // Sauvegarder après 2 secondes d'inactivité
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user