feat: Ajout système de logging SignalR, icônes SVG et suppression menu
- Système de logging SignalR complet dans ~/.simpleconnect-ng/signalr.log - Capture de tous les événements SignalR avec format JSON structuré - Remplacement des emojis par icônes SVG pour compatibilité Linux - Suppression complète de la barre de menu Electron - Configuration build Linux multi-architecture (x64/arm64) - Support AppImage, .deb et .rpm - Bump version 1.2.16
This commit is contained in:
57
build-linux-x64.sh
Executable file
57
build-linux-x64.sh
Executable file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script de build pour Linux AMD64 depuis Mac ARM64
|
||||||
|
# Ce script utilise electron-builder pour créer un package Linux x64
|
||||||
|
|
||||||
|
echo "🚀 Build SimpleConnect pour Linux AMD64"
|
||||||
|
echo "========================================="
|
||||||
|
|
||||||
|
# Vérifier que nous sommes dans le bon répertoire
|
||||||
|
if [ ! -f "package.json" ]; then
|
||||||
|
echo "❌ Erreur: package.json non trouvé. Exécutez ce script depuis la racine du projet."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Nettoyer les builds précédents
|
||||||
|
echo "🧹 Nettoyage des builds précédents..."
|
||||||
|
rm -rf dist/
|
||||||
|
|
||||||
|
# Installer les dépendances si nécessaire
|
||||||
|
echo "📦 Vérification des dépendances..."
|
||||||
|
if [ ! -d "node_modules" ]; then
|
||||||
|
echo "Installation des dépendances..."
|
||||||
|
npm install
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configuration pour le cross-compilation
|
||||||
|
export ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=true
|
||||||
|
|
||||||
|
# Build pour Linux x64
|
||||||
|
echo "🔨 Build en cours pour Linux x64..."
|
||||||
|
echo "Note: Le build cross-platform peut prendre quelques minutes..."
|
||||||
|
|
||||||
|
# Utiliser npx pour garantir l'utilisation de electron-builder local
|
||||||
|
npx electron-builder --linux --x64
|
||||||
|
|
||||||
|
# Vérifier le succès du build
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo ""
|
||||||
|
echo "✅ Build terminé avec succès!"
|
||||||
|
echo ""
|
||||||
|
echo "📦 Fichiers générés dans ./dist/ :"
|
||||||
|
echo "========================================="
|
||||||
|
ls -lh dist/*.AppImage 2>/dev/null && echo " ✓ AppImage (format universel)"
|
||||||
|
ls -lh dist/*.deb 2>/dev/null && echo " ✓ Package Debian/Ubuntu"
|
||||||
|
ls -lh dist/*.rpm 2>/dev/null && echo " ✓ Package RedHat/Fedora"
|
||||||
|
echo ""
|
||||||
|
echo "📋 Instructions de déploiement:"
|
||||||
|
echo "1. Copier le fichier approprié sur la machine Linux cible"
|
||||||
|
echo "2. Pour AppImage: chmod +x SimpleConnect-*.AppImage && ./SimpleConnect-*.AppImage"
|
||||||
|
echo "3. Pour .deb: sudo dpkg -i simpleconnect-electron_*.deb"
|
||||||
|
echo "4. Pour .rpm: sudo rpm -i simpleconnect-electron-*.rpm"
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "❌ Erreur lors du build"
|
||||||
|
echo "Vérifiez les messages d'erreur ci-dessus"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -1,5 +1,47 @@
|
|||||||
# Changelog - SimpleConnect Electron
|
# Changelog - SimpleConnect Electron
|
||||||
|
|
||||||
|
## [1.2.16] - 2025-09-05
|
||||||
|
|
||||||
|
### Ajouté
|
||||||
|
- **Système de logging SignalR complet** : Capture et analyse de tous les événements
|
||||||
|
- Fichier de log centralisé dans `~/.simpleconnect-ng/signalr.log`
|
||||||
|
- Logger universel pour tous les messages SignalR reçus
|
||||||
|
- Écoute de 13 types d'événements potentiels (IpbxEvent, AgentStatusChanged, QueueUpdate, etc.)
|
||||||
|
- Format JSON structuré avec timestamp, arguments et contexte agent
|
||||||
|
- Logs des méthodes invoquées (AgentLogin, AgentLogoff, GetTerminalListByServiceProvider)
|
||||||
|
- Identification des codes IPBX 0-5 avec descriptions détaillées
|
||||||
|
|
||||||
|
### Corrigé
|
||||||
|
- **Icônes manquantes sur Linux** : Remplacement des emojis par des SVG
|
||||||
|
- Icônes SVG inline pour les boutons Rafraîchir et Notes
|
||||||
|
- Compatibilité universelle (Windows, Mac, Linux)
|
||||||
|
- Style adaptatif suivant le thème (currentColor)
|
||||||
|
- Animations au survol et lors des actions
|
||||||
|
|
||||||
|
- **Barre de menu Electron** : Suppression complète sur tous les OS
|
||||||
|
- Ajout de `autoHideMenuBar: true` dans BrowserWindow
|
||||||
|
- `setMenuBarVisibility(false)` pour forcer la suppression
|
||||||
|
- `Menu.setApplicationMenu(null)` pour suppression globale
|
||||||
|
- Interface épurée sans menu "File, Edit, View, Window, Help"
|
||||||
|
|
||||||
|
### Modifié
|
||||||
|
- **Configuration de build Linux** : Support multi-architectures
|
||||||
|
- Ajout des cibles AppImage, .deb et .rpm
|
||||||
|
- Support x64 et arm64
|
||||||
|
- Scripts npm dédiés : `build:linux-x64` et `build:linux-arm64`
|
||||||
|
- Métadonnées Linux enrichies (maintainer, vendor, synopsis)
|
||||||
|
|
||||||
|
### Technique
|
||||||
|
- Module `os` ajouté pour accès au répertoire home utilisateur
|
||||||
|
- Fonctions de logging : `ensureLogDirectory()`, `logToSignalRFile()`, `logSignalR()`
|
||||||
|
- CSS pour icônes SVG avec transitions et animations
|
||||||
|
- Build cross-platform depuis Mac M1 vers Linux AMD64
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- Instructions complètes pour le build Linux
|
||||||
|
- Guide d'utilisation des fichiers AppImage, .deb et .rpm
|
||||||
|
- Explication du poids des fichiers AppImage (106 MB)
|
||||||
|
|
||||||
## [1.2.15] - 2025-09-04
|
## [1.2.15] - 2025-09-04
|
||||||
|
|
||||||
### Corrigé
|
### Corrigé
|
||||||
|
|||||||
14
index.html
14
index.html
@@ -85,10 +85,20 @@
|
|||||||
<span id="statusText">Disponible</span>
|
<span id="statusText">Disponible</span>
|
||||||
</div>
|
</div>
|
||||||
<button id="refreshBtn" class="btn-icon" title="Rafraîchir la page">
|
<button id="refreshBtn" class="btn-icon" title="Rafraîchir la page">
|
||||||
<span class="icon-refresh">🔄</span>
|
<svg class="icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M23 4v6h-6"></path>
|
||||||
|
<path d="M1 20v-6h6"></path>
|
||||||
|
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button id="toggleNotesBtn" class="btn-icon" title="Afficher/Masquer les notes">
|
<button id="toggleNotesBtn" class="btn-icon" title="Afficher/Masquer les notes">
|
||||||
<span class="icon-notes">📝</span>
|
<svg class="icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
||||||
|
<polyline points="14 2 14 8 20 8"></polyline>
|
||||||
|
<line x1="16" y1="13" x2="8" y2="13"></line>
|
||||||
|
<line x1="16" y1="17" x2="8" y2="17"></line>
|
||||||
|
<polyline points="10 9 9 9 8 9"></polyline>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button id="logoutBtn" class="btn-secondary">Quitter</button>
|
<button id="logoutBtn" class="btn-secondary">Quitter</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
234
main.js
234
main.js
@@ -1,6 +1,7 @@
|
|||||||
const { app, BrowserWindow, ipcMain, session } = require('electron');
|
const { app, BrowserWindow, ipcMain, session } = require('electron');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
const signalR = require('@microsoft/signalr');
|
const signalR = require('@microsoft/signalr');
|
||||||
|
|
||||||
let mainWindow;
|
let mainWindow;
|
||||||
@@ -11,6 +12,41 @@ let signalRConnection = null;
|
|||||||
let signalRStatus = 'disconnected'; // disconnected, connecting, connected, error
|
let signalRStatus = 'disconnected'; // disconnected, connecting, connected, error
|
||||||
let agentConnectionInfo = null; // Informations complètes retournées par SignalR
|
let agentConnectionInfo = null; // Informations complètes retournées par SignalR
|
||||||
|
|
||||||
|
// Configuration du système de logs SignalR
|
||||||
|
const SIGNALR_LOG_DIR = path.join(os.homedir(), '.simpleconnect-ng');
|
||||||
|
const SIGNALR_LOG_FILE = path.join(SIGNALR_LOG_DIR, 'signalr.log');
|
||||||
|
|
||||||
|
// Créer le répertoire de logs s'il n'existe pas
|
||||||
|
function ensureLogDirectory() {
|
||||||
|
if (!fs.existsSync(SIGNALR_LOG_DIR)) {
|
||||||
|
fs.mkdirSync(SIGNALR_LOG_DIR, { recursive: true });
|
||||||
|
console.log(`📁 Répertoire de logs créé: ${SIGNALR_LOG_DIR}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction pour écrire dans le fichier de log SignalR
|
||||||
|
function logToSignalRFile(message, data = null) {
|
||||||
|
ensureLogDirectory();
|
||||||
|
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
let logEntry = `[${timestamp}] ${message}`;
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
logEntry += '\n' + JSON.stringify(data, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
logEntry += '\n' + '─'.repeat(80) + '\n';
|
||||||
|
|
||||||
|
// Ajouter au fichier (append)
|
||||||
|
fs.appendFileSync(SIGNALR_LOG_FILE, logEntry, 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logger dans la console ET dans le fichier
|
||||||
|
function logSignalR(message, data = null) {
|
||||||
|
console.log(message, data || '');
|
||||||
|
logToSignalRFile(message, data);
|
||||||
|
}
|
||||||
|
|
||||||
// Charger la configuration
|
// Charger la configuration
|
||||||
function loadConfig() {
|
function loadConfig() {
|
||||||
const configPath = path.join(__dirname, 'config.json');
|
const configPath = path.join(__dirname, 'config.json');
|
||||||
@@ -30,9 +66,13 @@ function createWindow() {
|
|||||||
webSecurity: false
|
webSecurity: false
|
||||||
},
|
},
|
||||||
icon: path.join(__dirname, 'icon.png'),
|
icon: path.join(__dirname, 'icon.png'),
|
||||||
title: 'SimpleConnect'
|
title: 'SimpleConnect',
|
||||||
|
autoHideMenuBar: true // Cache la barre de menu par défaut
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Supprimer complètement le menu (pour Linux/Windows)
|
||||||
|
mainWindow.setMenuBarVisibility(false);
|
||||||
|
|
||||||
// Charger l'interface HTML
|
// Charger l'interface HTML
|
||||||
mainWindow.loadFile('index.html');
|
mainWindow.loadFile('index.html');
|
||||||
|
|
||||||
@@ -67,18 +107,30 @@ function initializeSignalR() {
|
|||||||
// Gérer les changements d'état
|
// Gérer les changements d'état
|
||||||
signalRConnection.onreconnecting(() => {
|
signalRConnection.onreconnecting(() => {
|
||||||
console.log('SignalR: Reconnexion en cours...');
|
console.log('SignalR: Reconnexion en cours...');
|
||||||
|
logSignalR('🔄 SignalR en reconnexion...', {
|
||||||
|
previousStatus: signalRStatus,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
signalRStatus = 'connecting';
|
signalRStatus = 'connecting';
|
||||||
sendSignalRStatus();
|
sendSignalRStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
signalRConnection.onreconnected(() => {
|
signalRConnection.onreconnected(() => {
|
||||||
console.log('SignalR: Reconnecté');
|
console.log('SignalR: Reconnecté');
|
||||||
|
logSignalR('🔗 SignalR reconnecté avec succès', {
|
||||||
|
connectionId: signalRConnection.connectionId,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
signalRStatus = 'connected';
|
signalRStatus = 'connected';
|
||||||
sendSignalRStatus();
|
sendSignalRStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
signalRConnection.onclose(() => {
|
signalRConnection.onclose(() => {
|
||||||
console.log('SignalR: Connexion fermée');
|
console.log('SignalR: Connexion fermée');
|
||||||
|
logSignalR('🔌 SignalR déconnecté', {
|
||||||
|
lastConnectionId: signalRConnection.connectionId,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
signalRStatus = 'disconnected';
|
signalRStatus = 'disconnected';
|
||||||
sendSignalRStatus();
|
sendSignalRStatus();
|
||||||
});
|
});
|
||||||
@@ -97,35 +149,136 @@ function initializeSignalR() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setupSignalRMethods() {
|
function setupSignalRMethods() {
|
||||||
// Écouter les événements IPBX
|
// === LOGGER UNIVERSEL POUR TOUS LES MESSAGES SIGNALR ===
|
||||||
signalRConnection.on('IpbxEvent', (name, args) => {
|
// Intercepter TOUS les messages reçus du serveur pour découvrir les événements disponibles
|
||||||
|
|
||||||
|
// Initialiser le fichier de log avec une session
|
||||||
|
logSignalR('════════════════════════════════════════════════════════════');
|
||||||
|
logSignalR('🚀 NOUVELLE SESSION SIGNALR DÉMARRÉE');
|
||||||
|
logSignalR(`Application: SimpleConnect v${app.getVersion()}`);
|
||||||
|
logSignalR(`Serveur SignalR: ${config.signalR.serverUrl}`);
|
||||||
|
logSignalR(`Service Provider: ${config.signalR.serviceProvider}`);
|
||||||
|
logSignalR('════════════════════════════════════════════════════════════');
|
||||||
|
|
||||||
|
// Liste des événements connus pour les logger différemment
|
||||||
|
const knownEvents = ['IpbxEvent'];
|
||||||
|
|
||||||
|
// Créer un proxy pour intercepter tous les appels .on()
|
||||||
|
const originalOn = signalRConnection.on.bind(signalRConnection);
|
||||||
|
|
||||||
|
// Logger tous les événements possibles en essayant d'écouter les plus communs
|
||||||
|
const possibleEvents = [
|
||||||
|
'IpbxEvent', // Événements téléphoniques (confirmé)
|
||||||
|
'AgentStatusChanged', // Changement de statut agent
|
||||||
|
'QueueUpdate', // Mise à jour des files
|
||||||
|
'CallReceived', // Appel entrant
|
||||||
|
'CallEnded', // Fin d'appel
|
||||||
|
'MessageReceived', // Messages
|
||||||
|
'Notification', // Notifications générales
|
||||||
|
'StatusUpdate', // Mises à jour de statut
|
||||||
|
'SystemMessage', // Messages système
|
||||||
|
'BroadcastMessage', // Messages broadcast
|
||||||
|
'AgentUpdate', // Mises à jour agent
|
||||||
|
'QueueStatistics', // Statistiques de file
|
||||||
|
'PresenceUpdate' // Mise à jour de présence
|
||||||
|
];
|
||||||
|
|
||||||
|
// Écouter tous les événements possibles et logger ce qu'on reçoit
|
||||||
|
possibleEvents.forEach(eventName => {
|
||||||
|
signalRConnection.on(eventName, (...args) => {
|
||||||
|
// Logger dans la console avec formatage
|
||||||
|
console.log('═══════════════════════════════════════════════════════════');
|
||||||
|
console.log(`📨 MESSAGE SIGNALR REÇU: ${eventName}`);
|
||||||
|
console.log('Timestamp:', new Date().toISOString());
|
||||||
|
console.log('Nombre d\'arguments:', args.length);
|
||||||
|
|
||||||
|
// Logger chaque argument en détail
|
||||||
|
args.forEach((arg, index) => {
|
||||||
|
console.log(`Argument ${index}:`, JSON.stringify(arg, null, 2));
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('═══════════════════════════════════════════════════════════');
|
||||||
|
|
||||||
|
// Logger dans le fichier avec structure
|
||||||
|
const logData = {
|
||||||
|
event: eventName,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
argumentCount: args.length,
|
||||||
|
arguments: args.map((arg, index) => ({
|
||||||
|
index: index,
|
||||||
|
type: typeof arg,
|
||||||
|
value: arg
|
||||||
|
})),
|
||||||
|
agent: currentAgent ? {
|
||||||
|
id: currentAgent.id,
|
||||||
|
name: currentAgent.name,
|
||||||
|
terminal: currentTerminal
|
||||||
|
} : null
|
||||||
|
};
|
||||||
|
|
||||||
|
logSignalR(`📨 MESSAGE SIGNALR REÇU: ${eventName}`, logData);
|
||||||
|
|
||||||
|
// Si c'est IpbxEvent, traiter comme avant
|
||||||
|
if (eventName === 'IpbxEvent') {
|
||||||
|
handleIpbxEventOriginal(args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fonction originale pour traiter les IpbxEvent
|
||||||
|
function handleIpbxEventOriginal(args) {
|
||||||
if (!args || !agentConnectionInfo) return;
|
if (!args || !agentConnectionInfo) return;
|
||||||
|
|
||||||
const event = args[0];
|
const [name, eventArgs] = args;
|
||||||
console.log('Événement IPBX reçu:', {
|
const event = eventArgs?.[0] || args[0];
|
||||||
|
|
||||||
|
console.log('🔍 Traitement IpbxEvent:', {
|
||||||
eventCode: event.eventCode,
|
eventCode: event.eventCode,
|
||||||
terminal: event.terminal,
|
terminal: event.terminal,
|
||||||
queueName: event.queueName
|
queueName: event.queueName,
|
||||||
|
fullEvent: event // Logger l'objet complet pour voir tous les champs
|
||||||
});
|
});
|
||||||
|
|
||||||
// Vérifier que l'événement est pour notre terminal
|
// Vérifier que l'événement est pour notre terminal
|
||||||
if (event.terminal !== currentTerminal) {
|
if (event.terminal !== currentTerminal) {
|
||||||
console.log('Événement ignoré - Terminal différent:', event.terminal, '!==', currentTerminal);
|
console.log('⚠️ Événement ignoré - Terminal différent:', event.terminal, '!==', currentTerminal);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gérer les différents types d'événements
|
// Gérer les différents types d'événements
|
||||||
switch(event.eventCode) {
|
switch(event.eventCode) {
|
||||||
|
case 0:
|
||||||
|
console.log('📞 Code 0: Appel entrant/sonnerie (non implémenté)');
|
||||||
|
break;
|
||||||
case 1: // Appel décroché
|
case 1: // Appel décroché
|
||||||
|
console.log('✅ Code 1: Appel décroché - Traitement...');
|
||||||
handleCallPickedUp(event);
|
handleCallPickedUp(event);
|
||||||
break;
|
break;
|
||||||
case 2: // Appel raccroché
|
case 2: // Appel raccroché
|
||||||
|
console.log('📴 Code 2: Appel raccroché - Traitement...');
|
||||||
handleCallHungUp(event);
|
handleCallHungUp(event);
|
||||||
break;
|
break;
|
||||||
|
case 3:
|
||||||
|
console.log('⏸️ Code 3: Mise en attente (non implémenté)');
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
console.log('↔️ Code 4: Transfert d\'appel (non implémenté)');
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
console.log('👥 Code 5: Conférence (non implémenté)');
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.log('Code événement non géré:', event.eventCode);
|
console.log('❓ Code événement inconnu:', event.eventCode);
|
||||||
|
console.log('Données complètes:', JSON.stringify(event, null, 2));
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// Logger aussi les méthodes qu'on peut invoquer
|
||||||
|
console.log('📋 MÉTHODES SIGNALR DISPONIBLES POUR INVOCATION:');
|
||||||
|
console.log('- AgentLogin(email, password, terminal)');
|
||||||
|
console.log('- AgentLogoff(accessCode)');
|
||||||
|
console.log('- GetTerminalListByServiceProvider(serviceProvider)');
|
||||||
|
console.log('ℹ️ D\'autres méthodes peuvent être disponibles sur le serveur');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gérer un appel entrant
|
// Gérer un appel entrant
|
||||||
@@ -170,14 +323,28 @@ async function startSignalRConnection() {
|
|||||||
try {
|
try {
|
||||||
signalRStatus = 'connecting';
|
signalRStatus = 'connecting';
|
||||||
sendSignalRStatus();
|
sendSignalRStatus();
|
||||||
|
logSignalR('🔌 Tentative de connexion SignalR...', {
|
||||||
|
serverUrl: config.signalR.serverUrl,
|
||||||
|
status: 'connecting'
|
||||||
|
});
|
||||||
|
|
||||||
await signalRConnection.start();
|
await signalRConnection.start();
|
||||||
console.log('SignalR: Connexion établie');
|
console.log('SignalR: Connexion établie');
|
||||||
|
logSignalR('✅ Connexion SignalR établie avec succès', {
|
||||||
|
connectionId: signalRConnection.connectionId,
|
||||||
|
status: 'connected',
|
||||||
|
serverUrl: config.signalR.serverUrl
|
||||||
|
});
|
||||||
signalRStatus = 'connected';
|
signalRStatus = 'connected';
|
||||||
sendSignalRStatus();
|
sendSignalRStatus();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erreur connexion SignalR:', error);
|
console.error('Erreur connexion SignalR:', error);
|
||||||
|
logSignalR('❌ Erreur de connexion SignalR', {
|
||||||
|
error: error.message,
|
||||||
|
stack: error.stack,
|
||||||
|
serverUrl: config.signalR.serverUrl
|
||||||
|
});
|
||||||
signalRStatus = 'error';
|
signalRStatus = 'error';
|
||||||
sendSignalRStatus();
|
sendSignalRStatus();
|
||||||
|
|
||||||
@@ -198,6 +365,10 @@ function sendSignalRStatus() {
|
|||||||
|
|
||||||
// Initialisation de l'application
|
// Initialisation de l'application
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
|
// Supprimer le menu de l'application complètement (toutes plateformes)
|
||||||
|
const { Menu } = require('electron');
|
||||||
|
Menu.setApplicationMenu(null);
|
||||||
|
|
||||||
// Configuration de la session pour éviter les problèmes CORS
|
// Configuration de la session pour éviter les problèmes CORS
|
||||||
session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
|
session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
|
||||||
details.requestHeaders['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
|
details.requestHeaders['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
|
||||||
@@ -271,12 +442,21 @@ ipcMain.handle('get-terminal-list', async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('Récupération des terminaux pour:', config.signalR.serviceProvider);
|
console.log('Récupération des terminaux pour:', config.signalR.serviceProvider);
|
||||||
|
logSignalR('📡 Invocation SignalR: GetTerminalListByServiceProvider', {
|
||||||
|
method: 'GetTerminalListByServiceProvider',
|
||||||
|
serviceProvider: config.signalR.serviceProvider
|
||||||
|
});
|
||||||
|
|
||||||
const terminals = await signalRConnection.invoke(
|
const terminals = await signalRConnection.invoke(
|
||||||
'GetTerminalListByServiceProvider',
|
'GetTerminalListByServiceProvider',
|
||||||
config.signalR.serviceProvider
|
config.signalR.serviceProvider
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('Terminaux disponibles:', terminals);
|
console.log('Terminaux disponibles:', terminals);
|
||||||
|
logSignalR('📞 Terminaux récupérés', {
|
||||||
|
count: terminals.length,
|
||||||
|
terminals: terminals
|
||||||
|
});
|
||||||
return terminals || [];
|
return terminals || [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erreur récupération terminaux:', error);
|
console.error('Erreur récupération terminaux:', error);
|
||||||
@@ -297,6 +477,11 @@ ipcMain.handle('login-agent', async (event, credentials) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('Tentative de connexion agent:', credentials.email, 'Terminal:', credentials.terminal);
|
console.log('Tentative de connexion agent:', credentials.email, 'Terminal:', credentials.terminal);
|
||||||
|
logSignalR('🔐 Tentative de connexion agent', {
|
||||||
|
email: credentials.email,
|
||||||
|
terminal: credentials.terminal,
|
||||||
|
forceDisconnect: credentials.forceDisconnect || false
|
||||||
|
});
|
||||||
|
|
||||||
// Si déconnexion forcée demandée, déconnecter d'abord la session précédente
|
// Si déconnexion forcée demandée, déconnecter d'abord la session précédente
|
||||||
if (credentials.forceDisconnect) {
|
if (credentials.forceDisconnect) {
|
||||||
@@ -305,13 +490,26 @@ ipcMain.handle('login-agent', async (event, credentials) => {
|
|||||||
// Tenter la déconnexion avec le code d'accès
|
// Tenter la déconnexion avec le code d'accès
|
||||||
await signalRConnection.invoke('AgentLogoff', credentials.email);
|
await signalRConnection.invoke('AgentLogoff', credentials.email);
|
||||||
console.log('Session précédente déconnectée avec succès');
|
console.log('Session précédente déconnectée avec succès');
|
||||||
|
logSignalR('🔓 Session précédente déconnectée (forceDisconnect)', {
|
||||||
|
email: credentials.email
|
||||||
|
});
|
||||||
} catch (logoffError) {
|
} catch (logoffError) {
|
||||||
console.warn('Erreur lors de la déconnexion forcée (session peut-être déjà fermée):', logoffError.message);
|
console.warn('Erreur lors de la déconnexion forcée (session peut-être déjà fermée):', logoffError.message);
|
||||||
|
logSignalR('⚠️ Erreur déconnexion forcée', {
|
||||||
|
email: credentials.email,
|
||||||
|
error: logoffError.message
|
||||||
|
});
|
||||||
// Continuer même si la déconnexion échoue - la session est peut-être déjà fermée
|
// Continuer même si la déconnexion échoue - la session est peut-être déjà fermée
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Appel SignalR pour l'authentification
|
// Appel SignalR pour l'authentification
|
||||||
|
logSignalR('📡 Invocation SignalR: AgentLogin', {
|
||||||
|
method: 'AgentLogin',
|
||||||
|
email: credentials.email,
|
||||||
|
terminal: credentials.terminal
|
||||||
|
});
|
||||||
|
|
||||||
const result = await signalRConnection.invoke('AgentLogin',
|
const result = await signalRConnection.invoke('AgentLogin',
|
||||||
credentials.email,
|
credentials.email,
|
||||||
credentials.password,
|
credentials.password,
|
||||||
@@ -320,6 +518,14 @@ ipcMain.handle('login-agent', async (event, credentials) => {
|
|||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log('Connexion réussie:', result);
|
console.log('Connexion réussie:', result);
|
||||||
|
logSignalR('✅ Connexion agent réussie', {
|
||||||
|
accessCode: result.accessCode,
|
||||||
|
firstName: result.firstName,
|
||||||
|
lastName: result.lastName,
|
||||||
|
terminal: credentials.terminal,
|
||||||
|
connListCount: result.connList ? result.connList.length : 0,
|
||||||
|
connList: result.connList
|
||||||
|
});
|
||||||
|
|
||||||
// Stocker les informations de connexion
|
// Stocker les informations de connexion
|
||||||
agentConnectionInfo = result;
|
agentConnectionInfo = result;
|
||||||
@@ -419,8 +625,16 @@ ipcMain.handle('logout', async () => {
|
|||||||
if (currentAgent && signalRConnection && signalRStatus === 'connected') {
|
if (currentAgent && signalRConnection && signalRStatus === 'connected') {
|
||||||
try {
|
try {
|
||||||
// Appeler SignalR pour la déconnexion
|
// Appeler SignalR pour la déconnexion
|
||||||
|
logSignalR('📡 Invocation SignalR: AgentLogoff', {
|
||||||
|
method: 'AgentLogoff',
|
||||||
|
accessCode: currentAgent.accessCode
|
||||||
|
});
|
||||||
await signalRConnection.invoke('AgentLogoff', currentAgent.accessCode);
|
await signalRConnection.invoke('AgentLogoff', currentAgent.accessCode);
|
||||||
console.log('Agent déconnecté du serveur SignalR');
|
console.log('Agent déconnecté du serveur SignalR');
|
||||||
|
logSignalR('👋 Agent déconnecté avec succès', {
|
||||||
|
accessCode: currentAgent.accessCode,
|
||||||
|
name: currentAgent.name
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erreur lors de la déconnexion SignalR:', error);
|
console.error('Erreur lors de la déconnexion SignalR:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
30
package.json
30
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "simpleconnect-electron",
|
"name": "simpleconnect-electron",
|
||||||
"version": "1.0.0",
|
"version": "1.2.16",
|
||||||
"description": "Application de gestion centralisée des plannings médicaux pour centres d'appels",
|
"description": "Application de gestion centralisée des plannings médicaux pour centres d'appels",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
"build:win": "electron-builder --win",
|
"build:win": "electron-builder --win",
|
||||||
"build:mac": "electron-builder --mac",
|
"build:mac": "electron-builder --mac",
|
||||||
"build:linux": "electron-builder --linux",
|
"build:linux": "electron-builder --linux",
|
||||||
|
"build:linux-x64": "electron-builder --linux --x64",
|
||||||
|
"build:linux-arm64": "electron-builder --linux --arm64",
|
||||||
"dist": "electron-builder",
|
"dist": "electron-builder",
|
||||||
"postinstall": "electron-builder install-app-deps"
|
"postinstall": "electron-builder install-app-deps"
|
||||||
},
|
},
|
||||||
@@ -48,9 +50,31 @@
|
|||||||
"icon": "icon.ico"
|
"icon": "icon.ico"
|
||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
"target": "AppImage",
|
"target": [
|
||||||
|
{
|
||||||
|
"target": "AppImage",
|
||||||
|
"arch": ["x64", "arm64"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "deb",
|
||||||
|
"arch": ["x64", "arm64"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "rpm",
|
||||||
|
"arch": ["x64", "arm64"]
|
||||||
|
}
|
||||||
|
],
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
"category": "Office"
|
"category": "Office",
|
||||||
|
"maintainer": "SimpleConnect Team",
|
||||||
|
"vendor": "SimpleConnect",
|
||||||
|
"synopsis": "Gestion centralisée des plannings médicaux",
|
||||||
|
"description": "Application de gestion centralisée des plannings médicaux pour centres d'appels avec intégration CTI",
|
||||||
|
"desktop": {
|
||||||
|
"StartupNotify": "true",
|
||||||
|
"Encoding": "UTF-8",
|
||||||
|
"MimeType": "x-scheme-handler/simpleconnect"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"nsis": {
|
"nsis": {
|
||||||
"oneClick": false,
|
"oneClick": false,
|
||||||
|
|||||||
@@ -404,6 +404,28 @@ body {
|
|||||||
border-color: #667eea;
|
border-color: #667eea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Style pour les icônes SVG */
|
||||||
|
.btn-icon svg.icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 2;
|
||||||
|
fill: none;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon:hover svg.icon {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon.active svg.icon {
|
||||||
|
stroke: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon.rotating svg.icon {
|
||||||
|
animation: rotate 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-icon.rotating .icon-refresh {
|
.btn-icon.rotating .icon-refresh {
|
||||||
animation: rotate 1s linear;
|
animation: rotate 1s linear;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
Reference in New Issue
Block a user