From 55838082797269d95925ffd7d461ca2adbb3f26d Mon Sep 17 00:00:00 2001 From: Benoit Date: Mon, 25 May 2026 09:42:10 +0200 Subject: [PATCH] feat: ajout interface admin pour configuration carte son (Phase 2.5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Nouvel onglet Audio dans l'interface admin - Sélection carte son d'entrée/sortie via dropdowns - Configuration sample rate (44.1/48/96kHz) - Affichage liste toutes cartes disponibles - Affichage configuration actuelle - Sauvegarde vers API backend --- client/src/Admin.jsx | 165 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) diff --git a/client/src/Admin.jsx b/client/src/Admin.jsx index d2f5cc4..3832efd 100644 --- a/client/src/Admin.jsx +++ b/client/src/Admin.jsx @@ -12,6 +12,13 @@ function Admin() { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + // Audio devices (Phase 2.5) + const [audioDevices, setAudioDevices] = useState([]); + const [currentDevice, setCurrentDevice] = useState(null); + const [selectedInputDevice, setSelectedInputDevice] = useState(null); + const [selectedOutputDevice, setSelectedOutputDevice] = useState(null); + const [selectedSampleRate, setSelectedSampleRate] = useState(48000); + // Gestion formulaire nouveau groupe const [showGroupForm, setShowGroupForm] = useState(false); const [editingGroup, setEditingGroup] = useState(null); @@ -40,6 +47,8 @@ function Admin() { await loadStats(); } else if (activeTab === 'logs') { await loadLogs(); + } else if (activeTab === 'audio') { + await loadAudioDevices(); } setError(null); @@ -75,6 +84,24 @@ function Admin() { setLogs(data.logs || []); }; + const loadAudioDevices = async () => { + const [devicesRes, currentDeviceRes] = await Promise.all([ + fetch(`${API_URL}/admin/audio/devices`), + fetch(`${API_URL}/admin/audio/device`) + ]); + + const devicesData = await devicesRes.json(); + const currentData = await currentDeviceRes.json(); + + setAudioDevices(devicesData.devices || []); + setCurrentDevice(currentData.device || {}); + + // Initialiser les sélections avec les valeurs actuelles + setSelectedInputDevice(currentData.device?.inputDeviceId ?? null); + setSelectedOutputDevice(currentData.device?.outputDeviceId ?? null); + setSelectedSampleRate(currentData.device?.sampleRate || 48000); + }; + // ========== Gestion groupes ========== const handleCreateGroup = async (e) => { @@ -192,6 +219,33 @@ function Admin() { setGroupForm({ ...groupForm, channels: newChannels }); }; + // ========== Gestion audio devices (Phase 2.5) ========== + + const handleSaveAudioDevice = async () => { + try { + const res = await fetch(`${API_URL}/admin/audio/device`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + inputDeviceId: selectedInputDevice !== null ? parseInt(selectedInputDevice) : undefined, + outputDeviceId: selectedOutputDevice !== null ? parseInt(selectedOutputDevice) : undefined, + sampleRate: parseInt(selectedSampleRate) + }) + }); + + if (res.ok) { + alert('Configuration audio sauvegardée avec succès!'); + await loadAudioDevices(); + } else { + const error = await res.json(); + alert(`Erreur: ${error.error}`); + } + } catch (err) { + console.error('Erreur sauvegarde configuration audio:', err); + alert('Erreur lors de la sauvegarde'); + } + }; + // ========== Gestion utilisateurs ========== const handleDisconnectUser = async (identity) => { @@ -243,6 +297,12 @@ function Admin() { > Groupes + + + + {currentDevice && Object.keys(currentDevice).length > 0 && ( +
+

Configuration actuelle

+
+

Input Device ID: {currentDevice.inputDeviceId ?? 'Non configuré'}

+

Output Device ID: {currentDevice.outputDeviceId ?? 'Non configuré'}

+

Sample Rate: {currentDevice.sampleRate ?? 48000} Hz

+
+
+ )} + +
+

Toutes les cartes son disponibles

+ + + + + + + + + + + + + {audioDevices.map(device => ( + + + + + + + + + ))} + +
IDNomEntréesSortiesSample RateAPI
{device.id}{device.name}{device.maxInputChannels}{device.maxOutputChannels}{device.defaultSampleRate} Hz{device.hostAPIName}
+
+ + + )} + {/* TAB: Utilisateurs */} {activeTab === 'users' && (