diff --git a/client/src/Admin.jsx b/client/src/Admin.jsx index def1f6f..80b3f99 100644 --- a/client/src/Admin.jsx +++ b/client/src/Admin.jsx @@ -1,11 +1,17 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import './Admin.css'; import AudioRoutingMatrix from './components/AudioRoutingMatrix'; const API_URL = import.meta.env.VITE_API_URL || '/api'; function Admin() { - const [activeTab, setActiveTab] = useState('groups'); + // Lire l'onglet depuis l'URL hash (ex: #audio) ou utiliser 'groups' par défaut + const getInitialTab = () => { + const hash = window.location.hash.slice(1); // Enlever le # + return ['groups', 'audio', 'users', 'stats', 'logs'].includes(hash) ? hash : 'groups'; + }; + + const [activeTab, setActiveTab] = useState(getInitialTab()); const [groups, setGroups] = useState([]); const [users, setUsers] = useState([]); const [stats, setStats] = useState(null); @@ -19,11 +25,10 @@ function Admin() { const [selectedInputDevice, setSelectedInputDevice] = useState(null); const [selectedOutputDevice, setSelectedOutputDevice] = useState(null); const [selectedSampleRate, setSelectedSampleRate] = useState(48000); - const [isEditingAudio, setIsEditingAudio] = useState(false); + const isEditingAudioRef = useRef(false); // Channel names (Phase 2.5) const [channelNames, setChannelNames] = useState({ inputs: {}, outputs: {} }); - const [editingChannelNames, setEditingChannelNames] = useState(false); // Gestion formulaire nouveau groupe const [showGroupForm, setShowGroupForm] = useState(false); @@ -33,6 +38,19 @@ function Admin() { audioBitrate: 96 }); + // Synchroniser l'onglet avec l'URL hash + useEffect(() => { + const handleHashChange = () => { + const hash = window.location.hash.slice(1); + if (['groups', 'audio', 'users', 'stats', 'logs'].includes(hash)) { + setActiveTab(hash); + } + }; + + window.addEventListener('hashchange', handleHashChange); + return () => window.removeEventListener('hashchange', handleHashChange); + }, []); + // Rafraîchissement automatique useEffect(() => { loadData(); @@ -106,8 +124,8 @@ function Admin() { setCurrentDevice(device); setChannelNames(channelNamesData.channelNames || { inputs: {}, outputs: {} }); - // Ne réinitialiser les sélections que si l'utilisateur n'est pas en train d'éditer - if (!isEditingAudio) { + // Ne réinitialiser les sélections que lors du chargement initial (pas en train d'éditer) + if (!isEditingAudioRef.current) { setSelectedInputDevice(device.inputDeviceId ?? null); setSelectedOutputDevice(device.outputDeviceId ?? null); setSelectedSampleRate(device.sampleRate || 48000); @@ -216,7 +234,6 @@ function Admin() { if (res.ok) { alert('Noms de canaux sauvegardés avec succès!'); - setEditingChannelNames(false); await loadAudioDevices(); } else { const error = await res.json(); @@ -251,7 +268,7 @@ function Admin() { }); if (res.ok) { - setIsEditingAudio(false); // Désactiver le mode édition + isEditingAudioRef.current = false; // Désactiver le mode édition alert('Configuration audio sauvegardée avec succès!'); await loadAudioDevices(); } else { @@ -311,31 +328,31 @@ function Admin() {