SUPPRESSIONS:
- Sidebar latérale gauche complètement retirée
- Statistiques du jour supprimées
- Bouton et modal de simulation d'appel supprimés
- Scrollbars visibles masquées
AJOUTS:
- Zone de notes dynamique avec toggle via bouton 📝
- Sauvegarde des préférences dans localStorage
- Nouveau design moderne avec styles-modern.css
AMÉLIORATIONS:
- Interface épurée maximisant l'espace pour les webviews
- Onglets style Material Design avec animations
- Meilleure gestion de l'espace avec calc() CSS
- Code HTML et JavaScript nettoyé et simplifié
736 lines
25 KiB
JavaScript
736 lines
25 KiB
JavaScript
const { ipcRenderer } = require('electron');
|
|
// Choices est maintenant chargé via CDN dans index.html
|
|
|
|
// Variables globales
|
|
let currentAgent = null;
|
|
let currentCentres = [];
|
|
let activeCenter = null;
|
|
let webviews = {};
|
|
let callStats = {
|
|
calls: 0,
|
|
appointments: 0
|
|
};
|
|
|
|
// === GESTION DE LA CONNEXION ===
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
// Initialiser l'indicateur SignalR
|
|
|
|
// Écouter les changements de statut SignalR
|
|
ipcRenderer.on('signalr-status', (event, status) => {
|
|
updateSignalRIndicator(status);
|
|
// Recharger les terminaux à chaque changement de statut
|
|
loadTerminals();
|
|
});
|
|
|
|
// Obtenir le statut initial SignalR
|
|
const initialStatus = await ipcRenderer.invoke('get-signalr-status');
|
|
updateSignalRIndicator(initialStatus);
|
|
|
|
// Charger immédiatement les terminaux pour la page de login
|
|
await loadTerminals();
|
|
|
|
// Vérifier si un agent est déjà connecté
|
|
const agentData = await ipcRenderer.invoke('get-current-agent');
|
|
if (agentData) {
|
|
currentAgent = agentData.agent;
|
|
currentCentres = agentData.centres;
|
|
showMainPage();
|
|
}
|
|
|
|
// Gestionnaire du formulaire de connexion
|
|
const loginForm = document.getElementById('loginForm');
|
|
if (loginForm) {
|
|
loginForm.addEventListener('submit', handleLogin);
|
|
}
|
|
|
|
// Bouton de déconnexion
|
|
const logoutBtn = document.getElementById('logoutBtn');
|
|
if (logoutBtn) {
|
|
logoutBtn.addEventListener('click', handleLogout);
|
|
}
|
|
|
|
// Bouton sauvegarder notes
|
|
const saveNotesBtn = document.getElementById('saveNotesBtn');
|
|
if (saveNotesBtn) {
|
|
saveNotesBtn.addEventListener('click', saveNotes);
|
|
}
|
|
|
|
// Bouton toggle notes
|
|
const toggleNotesBtn = document.getElementById('toggleNotesBtn');
|
|
if (toggleNotesBtn) {
|
|
toggleNotesBtn.addEventListener('click', toggleNotes);
|
|
}
|
|
|
|
// Bouton fermer notes
|
|
const closeNotesBtn = document.getElementById('closeNotesBtn');
|
|
if (closeNotesBtn) {
|
|
closeNotesBtn.addEventListener('click', hideNotes);
|
|
}
|
|
|
|
// Charger les préférences utilisateur
|
|
loadUserPreferences();
|
|
|
|
// Écouter les appels entrants
|
|
ipcRenderer.on('incoming-call', (event, callData) => {
|
|
handleIncomingCall(callData);
|
|
});
|
|
|
|
// Écouter les événements SignalR de basculement de centre
|
|
ipcRenderer.on('switch-to-center', (event, data) => {
|
|
console.log('Basculement vers le centre:', data.centreName);
|
|
|
|
// Trouver le centre et basculer automatiquement
|
|
const centre = currentCentres.find(c => c.id === data.centreId);
|
|
if (centre) {
|
|
selectCenter(data.centreId);
|
|
|
|
// Afficher une notification
|
|
showNotification(`Appel entrant sur ${data.centreName}`, 'info');
|
|
|
|
// Mettre à jour le statut
|
|
updateAgentStatus('EN APPEL');
|
|
}
|
|
});
|
|
|
|
// Écouter la libération de centre après raccrochage
|
|
ipcRenderer.on('release-center', (event, data) => {
|
|
console.log('Libération de la file:', data.queueName);
|
|
|
|
// Mettre à jour le statut
|
|
updateAgentStatus('DISPONIBLE');
|
|
|
|
// Incrementer le compteur d'appels
|
|
callStats.calls++;
|
|
updateCallStats();
|
|
|
|
// Afficher une notification
|
|
showNotification('Appel terminé', 'success');
|
|
});
|
|
});
|
|
|
|
// Connexion via SignalR
|
|
async function handleLogin(e) {
|
|
e.preventDefault();
|
|
|
|
const accessCode = document.getElementById('accessCode').value;
|
|
const password = document.getElementById('password').value;
|
|
const terminal = document.getElementById('terminal').value;
|
|
const forceDisconnect = document.getElementById('forceDisconnect').checked;
|
|
const errorDiv = document.getElementById('loginError');
|
|
const loginBtn = document.querySelector('#loginForm button[type="submit"]');
|
|
|
|
// Vérifier que le terminal est sélectionné et valide
|
|
if (!terminal) {
|
|
errorDiv.textContent = 'Veuillez sélectionner un poste téléphonique';
|
|
return;
|
|
}
|
|
|
|
// Valider que le terminal existe dans la liste
|
|
if (!validateTerminal(terminal)) {
|
|
errorDiv.textContent = 'Poste téléphonique invalide. Veuillez choisir un poste de la liste.';
|
|
return;
|
|
}
|
|
|
|
// Sauvegarder le terminal sélectionné pour la prochaine fois
|
|
localStorage.setItem('last-terminal', terminal);
|
|
|
|
// Désactiver le bouton pendant la connexion
|
|
loginBtn.disabled = true;
|
|
loginBtn.textContent = forceDisconnect ? 'Reconnexion...' : 'Connexion en cours...';
|
|
errorDiv.textContent = '';
|
|
|
|
try {
|
|
// Préparer les credentials pour SignalR
|
|
const credentials = {
|
|
email: accessCode, // Utiliser directement le code agent comme email
|
|
password: password,
|
|
terminal: terminal,
|
|
forceDisconnect: forceDisconnect // Ajouter l'option de déconnexion forcée
|
|
};
|
|
|
|
// Appeler l'authentification SignalR
|
|
const result = await ipcRenderer.invoke('login-agent', credentials);
|
|
|
|
if (result.success) {
|
|
currentAgent = result.agent;
|
|
currentCentres = result.centres;
|
|
errorDiv.textContent = '';
|
|
console.log('Connexion réussie:', currentAgent.name, 'sur le poste', terminal);
|
|
showMainPage();
|
|
} else {
|
|
errorDiv.textContent = result.message || 'Identifiants incorrects';
|
|
loginBtn.disabled = false;
|
|
loginBtn.textContent = 'Se connecter';
|
|
}
|
|
} catch (error) {
|
|
console.error('Erreur lors de la connexion:', error);
|
|
errorDiv.textContent = 'Erreur de connexion. Veuillez réessayer.';
|
|
loginBtn.disabled = false;
|
|
loginBtn.textContent = 'Se connecter';
|
|
}
|
|
}
|
|
|
|
// Déconnexion
|
|
async function handleLogout() {
|
|
if (confirm('Voulez-vous vraiment vous déconnecter ?')) {
|
|
await ipcRenderer.invoke('logout');
|
|
currentAgent = null;
|
|
currentCentres = [];
|
|
activeCenter = null;
|
|
webviews = {};
|
|
showLoginPage();
|
|
}
|
|
}
|
|
|
|
// === GESTION DES PAGES ===
|
|
function showLoginPage() {
|
|
document.getElementById('loginPage').classList.add('active');
|
|
document.getElementById('mainPage').classList.remove('active');
|
|
}
|
|
|
|
function showMainPage() {
|
|
document.getElementById('loginPage').classList.remove('active');
|
|
document.getElementById('mainPage').classList.add('active');
|
|
|
|
// Afficher le nom de l'agent
|
|
document.getElementById('agentName').textContent = currentAgent.name;
|
|
|
|
// Initialiser l'interface
|
|
initializeCenters();
|
|
updateStatus('available');
|
|
}
|
|
|
|
// === GESTION DES CENTRES ===
|
|
function initializeCenters() {
|
|
const centerTabs = document.getElementById('centerTabs');
|
|
const webviewContainer = document.getElementById('webviewContainer');
|
|
|
|
// Vider les contenus existants
|
|
centerTabs.innerHTML = '';
|
|
webviewContainer.innerHTML = '';
|
|
|
|
// Créer les onglets et webviews
|
|
currentCentres.forEach(centre => {
|
|
|
|
// Onglet
|
|
const tab = document.createElement('div');
|
|
tab.className = 'tab';
|
|
tab.dataset.centerId = centre.id;
|
|
tab.style.borderBottomColor = centre.couleur;
|
|
tab.textContent = centre.id; // Afficher le code du client au lieu du nom
|
|
tab.addEventListener('click', () => selectCenter(centre.id));
|
|
centerTabs.appendChild(tab);
|
|
|
|
// Créer la webview
|
|
const webviewWrapper = document.createElement('div');
|
|
webviewWrapper.className = 'webview-wrapper';
|
|
webviewWrapper.dataset.centerId = centre.id;
|
|
webviewWrapper.style.display = 'none';
|
|
|
|
const webview = document.createElement('webview');
|
|
webview.id = `webview-${centre.id}`;
|
|
webview.src = centre.url;
|
|
webview.className = 'planning-webview';
|
|
webview.setAttribute('partition', `persist:${centre.id}`);
|
|
webview.setAttribute('useragent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
|
|
|
|
// Barre d'outils pour la webview
|
|
const toolbar = document.createElement('div');
|
|
toolbar.className = 'webview-toolbar';
|
|
toolbar.innerHTML = `
|
|
<button onclick="navigateWebview('${centre.id}', 'back')">◀</button>
|
|
<button onclick="navigateWebview('${centre.id}', 'forward')">▶</button>
|
|
<button onclick="navigateWebview('${centre.id}', 'reload')">🔄</button>
|
|
<span class="webview-url" id="url-${centre.id}">${centre.url}</span>
|
|
`;
|
|
|
|
webviewWrapper.appendChild(toolbar);
|
|
webviewWrapper.appendChild(webview);
|
|
webviewContainer.appendChild(webviewWrapper);
|
|
|
|
// Stocker la référence de la webview
|
|
webviews[centre.id] = webview;
|
|
|
|
// Gérer les événements de la webview
|
|
webview.addEventListener('dom-ready', () => {
|
|
console.log(`Webview ${centre.nom} prête`);
|
|
|
|
// Auto-connexion si credentials disponibles
|
|
if (centre.credentials && centre.credentials.username) {
|
|
// Injecter le script de connexion automatique (à adapter selon le site)
|
|
const loginScript = `
|
|
// Script générique de connexion - à adapter selon le planning
|
|
console.log('Tentative de connexion automatique pour ${centre.nom}');
|
|
`;
|
|
webview.executeJavaScript(loginScript);
|
|
}
|
|
});
|
|
|
|
webview.addEventListener('did-navigate', (e) => {
|
|
document.getElementById(`url-${centre.id}`).textContent = e.url;
|
|
});
|
|
|
|
webview.addEventListener('did-fail-load', (e) => {
|
|
console.error(`Erreur chargement ${centre.nom}:`, e);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Sélectionner un centre
|
|
function selectCenter(centerId) {
|
|
// Mettre à jour le centre actif
|
|
activeCenter = centerId;
|
|
|
|
document.querySelectorAll('.tab').forEach(tab => {
|
|
tab.classList.toggle('active', tab.dataset.centerId === centerId);
|
|
});
|
|
|
|
// Afficher la bonne webview
|
|
document.querySelectorAll('.webview-wrapper').forEach(wrapper => {
|
|
wrapper.style.display = wrapper.dataset.centerId === centerId ? 'flex' : 'none';
|
|
});
|
|
}
|
|
|
|
// Navigation dans les webviews
|
|
window.navigateWebview = function(centerId, action) {
|
|
const webview = webviews[centerId];
|
|
if (webview) {
|
|
switch(action) {
|
|
case 'back':
|
|
webview.goBack();
|
|
break;
|
|
case 'forward':
|
|
webview.goForward();
|
|
break;
|
|
case 'reload':
|
|
webview.reload();
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
// === GESTION DES APPELS ===
|
|
function handleIncomingCall(callData) {
|
|
const centre = currentCentres.find(c => c.id === callData.centreId);
|
|
if (!centre) return;
|
|
|
|
// Afficher l'alerte
|
|
const alert = document.getElementById('incomingCallAlert');
|
|
alert.classList.add('active');
|
|
|
|
document.getElementById('callCenterName').textContent = centre.nom;
|
|
document.getElementById('callPatientInfo').textContent =
|
|
`${callData.nom || 'Patient'} - ${callData.numero}`;
|
|
|
|
// Jouer un son (à implémenter)
|
|
playNotificationSound();
|
|
|
|
// Gérer l'acceptation de l'appel
|
|
const acceptBtn = document.getElementById('acceptCallBtn');
|
|
acceptBtn.onclick = () => {
|
|
acceptCall(callData, centre);
|
|
};
|
|
|
|
// Auto-accepter après 3 secondes en mode démo
|
|
setTimeout(() => {
|
|
if (alert.classList.contains('active')) {
|
|
acceptCall(callData, centre);
|
|
}
|
|
}, 3000);
|
|
}
|
|
|
|
function acceptCall(callData, centre) {
|
|
// Masquer l'alerte
|
|
document.getElementById('incomingCallAlert').classList.remove('active');
|
|
|
|
// Sélectionner automatiquement le bon centre
|
|
selectCenter(centre.id);
|
|
|
|
// Mettre à jour le statut
|
|
updateStatus('incall', centre.nom);
|
|
|
|
// Incrémenter les stats
|
|
callStats.calls++;
|
|
|
|
// Sauvegarder l'appel dans l'historique
|
|
ipcRenderer.invoke('save-call-history', {
|
|
...callData,
|
|
centreId: centre.id,
|
|
centreName: centre.nom,
|
|
status: 'answered'
|
|
});
|
|
|
|
// Simuler la fin de l'appel après 30 secondes
|
|
setTimeout(() => {
|
|
endCall();
|
|
}, 30000);
|
|
}
|
|
|
|
function endCall() {
|
|
updateStatus('available');
|
|
callStats.appointments++;
|
|
}
|
|
|
|
|
|
// === UTILITAIRES ===
|
|
function updateStatus(status, details = '') {
|
|
const indicator = document.getElementById('statusIndicator');
|
|
const text = document.getElementById('statusText');
|
|
|
|
switch(status) {
|
|
case 'available':
|
|
indicator.className = 'status-indicator available';
|
|
text.textContent = 'Disponible';
|
|
break;
|
|
case 'incall':
|
|
indicator.className = 'status-indicator busy';
|
|
text.textContent = details ? `En appel - ${details}` : 'En appel';
|
|
break;
|
|
case 'offline':
|
|
indicator.className = 'status-indicator offline';
|
|
text.textContent = 'Hors ligne';
|
|
break;
|
|
}
|
|
}
|
|
|
|
function playNotificationSound() {
|
|
// Créer un bip simple avec l'API Web Audio
|
|
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
const oscillator = audioContext.createOscillator();
|
|
const gainNode = audioContext.createGain();
|
|
|
|
oscillator.connect(gainNode);
|
|
gainNode.connect(audioContext.destination);
|
|
|
|
oscillator.frequency.value = 800;
|
|
oscillator.type = 'sine';
|
|
gainNode.gain.value = 0.3;
|
|
|
|
oscillator.start();
|
|
oscillator.stop(audioContext.currentTime + 0.2);
|
|
}
|
|
|
|
// Fonction pour mettre à jour le statut de l'agent
|
|
function updateAgentStatus(status) {
|
|
const statusElement = document.getElementById('statusText');
|
|
const indicatorElement = document.getElementById('statusIndicator');
|
|
|
|
if (statusElement && indicatorElement) {
|
|
switch(status) {
|
|
case 'DISPONIBLE':
|
|
updateStatus('available');
|
|
break;
|
|
case 'EN APPEL':
|
|
updateStatus('incall');
|
|
break;
|
|
case 'HORS LIGNE':
|
|
updateStatus('offline');
|
|
break;
|
|
default:
|
|
statusElement.textContent = status;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fonction pour afficher une notification
|
|
function showNotification(message, type = 'info') {
|
|
// Créer un élément de notification temporaire
|
|
const notification = document.createElement('div');
|
|
notification.className = `notification notification-${type}`;
|
|
notification.textContent = message;
|
|
|
|
// Styles de base pour la notification
|
|
notification.style.cssText = `
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
padding: 15px 20px;
|
|
background: ${type === 'success' ? '#4caf50' : type === 'error' ? '#f44336' : '#2196f3'};
|
|
color: white;
|
|
border-radius: 5px;
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
|
z-index: 10000;
|
|
animation: slideIn 0.3s ease;
|
|
`;
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
// Retirer la notification après 3 secondes
|
|
setTimeout(() => {
|
|
notification.style.animation = 'slideOut 0.3s ease';
|
|
setTimeout(() => notification.remove(), 300);
|
|
}, 3000);
|
|
|
|
// Jouer un son si c'est une notification d'appel
|
|
if (type === 'info' && message.includes('Appel entrant')) {
|
|
playNotificationSound();
|
|
}
|
|
}
|
|
|
|
// Fonction pour mettre à jour les statistiques d'appels
|
|
function updateCallStats() {
|
|
// Statistiques supprimées de l'interface
|
|
console.log('Stats:', { calls: callStats.calls, appointments: callStats.appointments });
|
|
}
|
|
|
|
async function saveNotes() {
|
|
const notes = document.getElementById('quickNotes').value;
|
|
if (!notes.trim()) return;
|
|
|
|
const result = await ipcRenderer.invoke('save-notes', {
|
|
content: notes,
|
|
centre: activeCenter
|
|
});
|
|
|
|
if (result.success) {
|
|
alert('Notes sauvegardées !');
|
|
document.getElementById('quickNotes').value = '';
|
|
}
|
|
}
|
|
|
|
// === GESTION DES TERMINAUX ===
|
|
let availableTerminals = []; // Stocker les terminaux disponibles
|
|
let terminalChoices = null; // Instance Choices.js
|
|
|
|
async function loadTerminals() {
|
|
const terminalSelect = document.getElementById('terminal');
|
|
if (!terminalSelect) {
|
|
console.log('Element terminal non trouvé');
|
|
return;
|
|
}
|
|
|
|
console.log('Chargement des terminaux...');
|
|
|
|
try {
|
|
// Récupérer les terminaux depuis le serveur SignalR
|
|
const terminals = await ipcRenderer.invoke('get-terminal-list');
|
|
availableTerminals = terminals || [];
|
|
console.log(`${terminals.length} terminaux récupérés`);
|
|
|
|
// Préparer les options pour Choices.js
|
|
const choicesData = [];
|
|
if (terminals && terminals.length > 0) {
|
|
// Afficher tous les terminaux sans groupement
|
|
terminals.forEach(terminal => {
|
|
choicesData.push({
|
|
value: terminal.toString(),
|
|
label: `Poste ${terminal}`
|
|
});
|
|
});
|
|
}
|
|
|
|
// Détruire l'instance existante si elle existe
|
|
if (terminalChoices) {
|
|
terminalChoices.destroy();
|
|
terminalChoices = null;
|
|
}
|
|
|
|
// Vider le select avant d'initialiser Choices
|
|
terminalSelect.innerHTML = '';
|
|
|
|
// Attendre que Choices soit disponible (chargé via CDN)
|
|
if (typeof window.Choices === 'undefined') {
|
|
console.log('Choices.js pas encore chargé, utilisation du select natif');
|
|
// Fallback : utiliser le select natif
|
|
terminalSelect.innerHTML = '<option value="">Sélectionner un poste...</option>';
|
|
if (terminals && terminals.length > 0) {
|
|
terminals.forEach(terminal => {
|
|
const option = document.createElement('option');
|
|
option.value = terminal;
|
|
option.textContent = `Poste ${terminal}`;
|
|
terminalSelect.appendChild(option);
|
|
});
|
|
}
|
|
// Pas de retry automatique pour éviter une boucle infinie
|
|
return;
|
|
}
|
|
|
|
console.log('Initialisation de Choices.js avec', choicesData.length, 'options');
|
|
|
|
try {
|
|
// Créer une nouvelle instance Choices.js
|
|
terminalChoices = new window.Choices(terminalSelect, {
|
|
searchEnabled: true,
|
|
searchPlaceholderValue: 'Rechercher un poste...',
|
|
itemSelectText: '',
|
|
noResultsText: 'Aucun poste trouvé',
|
|
noChoicesText: 'Aucun poste disponible',
|
|
shouldSort: false,
|
|
searchResultLimit: 20,
|
|
renderChoiceLimit: -1,
|
|
placeholder: true,
|
|
placeholderValue: 'Sélectionner un poste téléphonique',
|
|
choices: choicesData.length > 0 ? choicesData : [{value: '', label: 'Aucun poste disponible', disabled: true}],
|
|
searchFields: ['label', 'value'],
|
|
fuseOptions: {
|
|
includeScore: true,
|
|
threshold: 0.3
|
|
},
|
|
classNames: {
|
|
containerOuter: ['choices', 'choices-terminal'],
|
|
containerInner: 'choices__inner',
|
|
input: 'choices__input',
|
|
inputCloned: 'choices__input--cloned',
|
|
list: 'choices__list',
|
|
listItems: 'choices__list--multiple',
|
|
listSingle: 'choices__list--single',
|
|
listDropdown: 'choices__list--dropdown',
|
|
item: 'choices__item',
|
|
itemSelectable: 'choices__item--selectable',
|
|
itemDisabled: 'choices__item--disabled',
|
|
itemChoice: 'choices__item--choice',
|
|
placeholder: 'choices__placeholder',
|
|
group: 'choices__group',
|
|
groupHeading: 'choices__heading',
|
|
button: 'choices__button',
|
|
activeState: 'is-active',
|
|
focusState: 'is-focused',
|
|
openState: 'is-open',
|
|
disabledState: 'is-disabled',
|
|
highlightedState: 'is-highlighted',
|
|
hiddenState: 'is-hidden',
|
|
flippedState: 'is-flipped',
|
|
loadingState: 'is-loading',
|
|
noResults: 'has-no-results',
|
|
noChoices: 'has-no-choices'
|
|
}
|
|
});
|
|
|
|
console.log('Choices.js initialisé avec succès');
|
|
|
|
// Restaurer la dernière sélection si disponible
|
|
const lastTerminal = localStorage.getItem('last-terminal');
|
|
if (lastTerminal && availableTerminals.map(t => t.toString()).includes(lastTerminal)) {
|
|
terminalChoices.setChoiceByValue(lastTerminal.toString());
|
|
}
|
|
|
|
} catch (choicesError) {
|
|
console.error('Erreur lors de l\'initialisation de Choices.js:', choicesError);
|
|
console.error('Stack:', choicesError.stack);
|
|
// En cas d'erreur, utiliser le select natif
|
|
terminalSelect.innerHTML = '<option value="">Erreur de chargement - voir console</option>';
|
|
if (terminals && terminals.length > 0) {
|
|
terminals.forEach(terminal => {
|
|
const option = document.createElement('option');
|
|
option.value = terminal;
|
|
option.textContent = `Poste ${terminal}`;
|
|
terminalSelect.appendChild(option);
|
|
});
|
|
}
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Erreur générale chargement des terminaux:', error);
|
|
// En cas d'erreur, utiliser le select natif
|
|
const terminalSelect = document.getElementById('terminal');
|
|
if (terminalSelect) {
|
|
terminalSelect.innerHTML = '<option value="">Erreur de chargement</option>';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Valider que le terminal saisi existe dans la liste
|
|
function validateTerminal(terminal) {
|
|
return availableTerminals.map(t => t.toString()).includes(terminal.toString());
|
|
}
|
|
|
|
// === GESTION DES NOTES DYNAMIQUES ===
|
|
function toggleNotes() {
|
|
const notesSection = document.getElementById('notesSection');
|
|
const toggleBtn = document.getElementById('toggleNotesBtn');
|
|
|
|
if (notesSection.classList.contains('visible')) {
|
|
hideNotes();
|
|
} else {
|
|
showNotes();
|
|
}
|
|
}
|
|
|
|
function showNotes() {
|
|
const notesSection = document.getElementById('notesSection');
|
|
const toggleBtn = document.getElementById('toggleNotesBtn');
|
|
|
|
notesSection.classList.add('visible');
|
|
toggleBtn.classList.add('active');
|
|
|
|
// Sauvegarder la préférence
|
|
localStorage.setItem('notesVisible', 'true');
|
|
|
|
// Focus sur le textarea
|
|
document.getElementById('quickNotes').focus();
|
|
}
|
|
|
|
function hideNotes() {
|
|
const notesSection = document.getElementById('notesSection');
|
|
const toggleBtn = document.getElementById('toggleNotesBtn');
|
|
|
|
notesSection.classList.remove('visible');
|
|
toggleBtn.classList.remove('active');
|
|
|
|
// Sauvegarder la préférence
|
|
localStorage.setItem('notesVisible', 'false');
|
|
}
|
|
|
|
// === GESTION DES PRÉFÉRENCES UTILISATEUR ===
|
|
function loadUserPreferences() {
|
|
// Charger l'état des notes
|
|
const notesVisible = localStorage.getItem('notesVisible');
|
|
if (notesVisible === 'true') {
|
|
// On n'affiche pas automatiquement les notes au démarrage
|
|
// L'utilisateur devra cliquer pour les afficher
|
|
}
|
|
|
|
// Charger d'autres préférences si nécessaire
|
|
const lastTheme = localStorage.getItem('theme');
|
|
if (lastTheme) {
|
|
document.body.setAttribute('data-theme', lastTheme);
|
|
}
|
|
}
|
|
|
|
function saveUserPreferences() {
|
|
// Cette fonction peut être étendue pour sauvegarder d'autres préférences
|
|
const preferences = {
|
|
notesVisible: document.getElementById('notesSection').classList.contains('visible'),
|
|
theme: document.body.getAttribute('data-theme') || 'light'
|
|
};
|
|
|
|
Object.keys(preferences).forEach(key => {
|
|
localStorage.setItem(key, preferences[key]);
|
|
});
|
|
}
|
|
|
|
// === GESTION INDICATEUR SIGNALR ===
|
|
function updateSignalRIndicator(status) {
|
|
const indicator = document.getElementById('signalrIndicator');
|
|
const text = document.getElementById('signalrText');
|
|
|
|
if (!indicator || !text) return;
|
|
|
|
// Réinitialiser les classes
|
|
indicator.className = 'signalr-indicator';
|
|
|
|
switch(status) {
|
|
case 'connected':
|
|
indicator.classList.add('connected');
|
|
text.textContent = 'Connecté au serveur';
|
|
break;
|
|
case 'connecting':
|
|
indicator.classList.add('connecting');
|
|
text.textContent = 'Connexion en cours...';
|
|
break;
|
|
case 'disconnected':
|
|
indicator.classList.add('disconnected');
|
|
text.textContent = 'Serveur déconnecté';
|
|
break;
|
|
case 'error':
|
|
indicator.classList.add('error');
|
|
text.textContent = 'Erreur de connexion';
|
|
break;
|
|
case 'disabled':
|
|
indicator.classList.add('disabled');
|
|
text.textContent = 'SignalR désactivé';
|
|
break;
|
|
default:
|
|
text.textContent = 'État inconnu';
|
|
}
|
|
} |