Files
PTT-Live/electron/ui/index.html
T
benoit 9a2bec6d2f feat: page Routing complète + gestion server_audio_users dans Config
- Page Routing : matrices Entrées→Groupes et Groupes→Sorties avec checkboxes,
  éditeur de noms de canaux (ajout/suppression dynamique), sauvegarde YAML directe
- Page Config : section Utilisateurs Audio Serveur (CRUD complet, modal avec
  sélecteur de groupe, canaux entrée/sortie)
- IPC main.js : handlers server-audio-users:list/create/update/delete,
  routing:get, routing:save
- preload.js : namespaces electronAPI.serverAudioUsers et electronAPI.routing
- showModal : support du type 'select' avec options
- loadViewData : routing et config lisibles sans serveur (YAML direct)
2026-07-01 14:27:35 +02:00

285 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PTT Live Server</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- Toast Container -->
<div class="toast-container" id="toast-container"></div>
<div id="app">
<!-- Header -->
<header class="header">
<div class="header-left">
<h1>🎙️ PTT Live Server</h1>
<span class="version" id="version">v0.3.0</span>
</div>
<div class="header-right">
<div class="server-status">
<span class="status-indicator" id="status-indicator"></span>
<span id="status-text">Arrêté</span>
</div>
<button id="btn-start" class="btn btn-primary">Démarrer</button>
<button id="btn-stop" class="btn btn-secondary" disabled>Arrêter</button>
</div>
</header>
<!-- Main Content -->
<main class="main-content">
<!-- Sidebar Navigation -->
<nav class="sidebar">
<button class="nav-item active" data-view="dashboard">
📊 Dashboard
</button>
<button class="nav-item" data-view="config">
⚙️ Configuration
</button>
<button class="nav-item" data-view="groups">
👥 Groupes
</button>
<button class="nav-item" data-view="routing">
🔀 Routing
</button>
<button class="nav-item" data-view="monitoring">
📈 Monitoring
</button>
<button class="nav-item" data-view="logs">
📝 Logs
</button>
</nav>
<!-- Content Area -->
<div class="content">
<!-- Dashboard View -->
<div id="view-dashboard" class="view active">
<h2>Dashboard</h2>
<!-- Stats Cards -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">Uptime</div>
<div class="stat-value" id="stat-uptime">--</div>
</div>
<div class="stat-card">
<div class="stat-label">Utilisateurs</div>
<div class="stat-value" id="stat-users">--</div>
</div>
<div class="stat-card">
<div class="stat-label">Groupes actifs</div>
<div class="stat-value" id="stat-groups">--</div>
</div>
<div class="stat-card">
<div class="stat-label">Connexions totales</div>
<div class="stat-value" id="stat-total-connections">--</div>
</div>
</div>
<!-- QR Code Section -->
<div class="section">
<h3>📱 Connexion rapide clients</h3>
<div class="qr-container">
<div class="qr-wrapper">
<img id="qr-code" width="256" height="256" alt="QR Code connexion" />
<div class="qr-placeholder" id="qr-placeholder">
<span class="qr-placeholder-icon">📷</span>
<span>En attente du démarrage du serveur</span>
</div>
</div>
<div class="qr-info">
<p><strong>URL clients :</strong></p>
<p class="url-text" id="client-url">--</p>
<button class="btn btn-small" id="btn-copy-url">Copier l'URL</button>
</div>
</div>
</div>
<!-- Active Users -->
<div class="section">
<h3>👤 Utilisateurs connectés</h3>
<div id="users-list" class="users-list">
<p class="empty-state">Aucun utilisateur connecté</p>
</div>
</div>
</div>
<!-- Configuration View -->
<div id="view-config" class="view">
<h2>Configuration Audio</h2>
<div class="section">
<h3>🔌 Périphériques Audio</h3>
<div class="form-group">
<label>Device Input</label>
<select id="input-device" class="form-control">
<option>Chargement...</option>
</select>
</div>
<div class="form-group">
<label>Device Output</label>
<select id="output-device" class="form-control">
<option>Chargement...</option>
</select>
</div>
<button class="btn btn-primary" id="btn-save-device">Appliquer</button>
</div>
<div class="section">
<h3>🔗 Utilisateurs Audio Serveur</h3>
<p class="config-note" style="margin-bottom: 1rem">Participants LiveKit côté serveur avec canaux physiques d'entrée et de sortie dédiés (mix-minus natif). Voir aussi la page <strong>Routing</strong> pour le câblage entre canaux et groupes.</p>
<button class="btn btn-primary btn-small" id="btn-add-server-audio-user"> Ajouter</button>
<div id="server-audio-users-list" class="groups-list" style="margin-top: 1rem">
<p class="empty-state">Chargement...</p>
</div>
</div>
<div class="section">
<h3>💾 Sauvegarde de configuration</h3>
<div class="config-actions">
<button class="btn btn-secondary" id="btn-export-config">Exporter config.yaml</button>
<button class="btn btn-secondary" id="btn-import-config">Importer config.yaml</button>
</div>
<p class="config-note">L'import remplace config.yaml (backup automatique en .bak). Redémarrez le serveur pour appliquer.</p>
</div>
<div class="section">
<h3>🎚️ Paramètres Audio</h3>
<div class="form-group">
<label>Sample Rate</label>
<select id="sample-rate" class="form-control">
<option value="44100">44.1 kHz</option>
<option value="48000" selected>48 kHz</option>
<option value="96000">96 kHz</option>
</select>
</div>
<div class="form-group">
<label>Bitrate par défaut (kbps)</label>
<input type="number" id="default-bitrate" class="form-control" value="96" min="32" max="320" step="32">
</div>
<div class="form-group">
<label>Jitter Buffer (ms)</label>
<input type="number" id="jitter-buffer" class="form-control" value="40" min="20" max="100" step="10">
</div>
<button class="btn btn-primary" id="btn-save-audio">Sauvegarder</button>
</div>
</div>
<!-- Groups View -->
<div id="view-groups" class="view">
<h2>Gestion des Groupes</h2>
<button class="btn btn-primary" id="btn-add-group"> Nouveau groupe</button>
<div id="groups-list" class="groups-list">
<p class="empty-state">Chargement des groupes...</p>
</div>
</div>
<!-- Routing View -->
<div id="view-routing" class="view">
<h2>Routing Audio</h2>
<!-- Noms des canaux -->
<div class="section">
<h3>🏷️ Noms des Canaux</h3>
<p class="config-note" style="margin-bottom: 1rem">Définissez les canaux physiques disponibles. Les matrices de routing se mettent à jour après la sauvegarde.</p>
<div class="channel-names-grid">
<div class="channel-names-col">
<div class="channel-names-col-header">
<h4>Canaux Entrée</h4>
<button class="btn btn-small btn-secondary" id="btn-add-input-channel"> Ajouter</button>
</div>
<div id="channel-names-inputs" class="channel-names-list">
<p class="empty-state">Chargement...</p>
</div>
</div>
<div class="channel-names-col">
<div class="channel-names-col-header">
<h4>Canaux Sortie</h4>
<button class="btn btn-small btn-secondary" id="btn-add-output-channel"> Ajouter</button>
</div>
<div id="channel-names-outputs" class="channel-names-list">
<p class="empty-state">Chargement...</p>
</div>
</div>
</div>
</div>
<!-- Matrice Entrées → Groupes -->
<div class="section">
<h3>🎙️ Entrées → Groupes</h3>
<p class="config-note" style="margin-bottom: 1rem">Quels canaux physiques d'entrée alimentent quels groupes LiveKit.</p>
<div class="routing-matrix-wrapper" id="routing-input-matrix">
<p class="empty-state">Chargement...</p>
</div>
</div>
<!-- Matrice Groupes → Sorties -->
<div class="section">
<h3>🔊 Groupes → Sorties</h3>
<p class="config-note" style="margin-bottom: 1rem">Vers quels canaux physiques de sortie chaque groupe est routé.</p>
<div class="routing-matrix-wrapper" id="routing-output-matrix">
<p class="empty-state">Chargement...</p>
</div>
</div>
<!-- Actions -->
<div class="routing-actions">
<button class="btn btn-primary" id="btn-save-routing">Sauvegarder</button>
<button class="btn btn-secondary" id="btn-reload-routing">Recharger</button>
<span class="config-note routing-restart-note hidden" id="routing-server-note">⚠️ Redémarrez le serveur pour appliquer les changements.</span>
</div>
</div>
<!-- Monitoring View -->
<div id="view-monitoring" class="view">
<h2>Monitoring Audio</h2>
<div class="section">
<h3>🔊 VU-Mètres</h3>
<div id="vu-meters" class="vu-meters">
<p class="empty-state">En attente de données audio...</p>
</div>
</div>
</div>
<!-- Logs View -->
<div id="view-logs" class="view">
<h2>Logs Serveur</h2>
<div class="logs-controls">
<button class="btn btn-small" id="btn-clear-logs">Effacer</button>
<button class="btn btn-small btn-secondary" id="btn-export-logs">Exporter JSON</button>
<select id="log-level-filter" class="form-control form-control-small">
<option value="">Tous les niveaux</option>
<option value="error">Erreurs</option>
<option value="warn">Warnings</option>
<option value="info">Info</option>
<option value="debug">Debug</option>
</select>
</div>
<div id="logs-container" class="logs-container">
<p class="empty-state">Aucun log</p>
</div>
</div>
</div>
</main>
</div>
<!-- Modal générique -->
<div id="modal-overlay" class="modal-overlay hidden">
<div class="modal">
<div class="modal-header">
<h3 id="modal-title"></h3>
</div>
<div class="modal-body" id="modal-body"></div>
<div class="modal-footer">
<button class="btn btn-secondary" id="modal-cancel">Annuler</button>
<button class="btn btn-primary" id="modal-confirm">Confirmer</button>
</div>
</div>
</div>
<!-- Le QR Code est généré côté Main Process (lib qrcode Node), pas de dépendance CDN -->
<script src="app.js"></script>
</body>
</html>