feat: ajout nommage canaux physiques (Phase 2.5)
- API GET /admin/audio/channels/names - API PUT /admin/audio/channels/names - Interface admin : nommage 8 inputs/outputs - Mode édition avec sauvegarde/annulation - Stockage dans config.yaml (section audio.channelNames) - Formulaire organisé en 2 colonnes (inputs/outputs)
This commit is contained in:
+115
-2
@@ -19,6 +19,10 @@ function Admin() {
|
||||
const [selectedOutputDevice, setSelectedOutputDevice] = useState(null);
|
||||
const [selectedSampleRate, setSelectedSampleRate] = useState(48000);
|
||||
|
||||
// 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);
|
||||
const [editingGroup, setEditingGroup] = useState(null);
|
||||
@@ -85,16 +89,19 @@ function Admin() {
|
||||
};
|
||||
|
||||
const loadAudioDevices = async () => {
|
||||
const [devicesRes, currentDeviceRes] = await Promise.all([
|
||||
const [devicesRes, currentDeviceRes, channelNamesRes] = await Promise.all([
|
||||
fetch(`${API_URL}/admin/audio/devices`),
|
||||
fetch(`${API_URL}/admin/audio/device`)
|
||||
fetch(`${API_URL}/admin/audio/device`),
|
||||
fetch(`${API_URL}/admin/audio/channels/names`)
|
||||
]);
|
||||
|
||||
const devicesData = await devicesRes.json();
|
||||
const currentData = await currentDeviceRes.json();
|
||||
const channelNamesData = await channelNamesRes.json();
|
||||
|
||||
setAudioDevices(devicesData.devices || []);
|
||||
setCurrentDevice(currentData.device || {});
|
||||
setChannelNames(channelNamesData.channelNames || { inputs: {}, outputs: {} });
|
||||
|
||||
// Initialiser les sélections avec les valeurs actuelles
|
||||
setSelectedInputDevice(currentData.device?.inputDeviceId ?? null);
|
||||
@@ -221,6 +228,38 @@ function Admin() {
|
||||
|
||||
// ========== Gestion audio devices (Phase 2.5) ==========
|
||||
|
||||
const handleSaveChannelNames = async () => {
|
||||
try {
|
||||
const res = await fetch(`${API_URL}/admin/audio/channels/names`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(channelNames)
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
alert('Noms de canaux sauvegardés avec succès!');
|
||||
setEditingChannelNames(false);
|
||||
await loadAudioDevices();
|
||||
} else {
|
||||
const error = await res.json();
|
||||
alert(`Erreur: ${error.error}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erreur sauvegarde noms canaux:', err);
|
||||
alert('Erreur lors de la sauvegarde');
|
||||
}
|
||||
};
|
||||
|
||||
const updateChannelName = (type, channelId, name) => {
|
||||
setChannelNames(prev => ({
|
||||
...prev,
|
||||
[type]: {
|
||||
...prev[type],
|
||||
[channelId]: name
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSaveAudioDevice = async () => {
|
||||
try {
|
||||
const res = await fetch(`${API_URL}/admin/audio/device`, {
|
||||
@@ -529,6 +568,80 @@ function Admin() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="audio-section">
|
||||
<div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 'var(--spacing-md)'}}>
|
||||
<h3>Nommage des canaux physiques</h3>
|
||||
{!editingChannelNames ? (
|
||||
<button onClick={() => setEditingChannelNames(true)} className="btn-secondary">
|
||||
Modifier les noms
|
||||
</button>
|
||||
) : (
|
||||
<div style={{display: 'flex', gap: 'var(--spacing-sm)'}}>
|
||||
<button onClick={handleSaveChannelNames} className="btn-primary">
|
||||
Sauvegarder
|
||||
</button>
|
||||
<button onClick={() => { setEditingChannelNames(false); loadAudioDevices(); }} className="btn-secondary">
|
||||
Annuler
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div style={{display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 'var(--spacing-xl)'}}>
|
||||
<div>
|
||||
<h4 style={{marginBottom: 'var(--spacing-md)', color: 'var(--color-text-secondary)'}}>Entrées (Inputs)</h4>
|
||||
<div style={{display: 'grid', gap: 'var(--spacing-sm)'}}>
|
||||
{Array.from({length: 8}, (_, i) => (
|
||||
<div key={`input-${i}`} style={{display: 'grid', gridTemplateColumns: '40px 1fr', gap: 'var(--spacing-sm)', alignItems: 'center'}}>
|
||||
<span style={{color: 'var(--color-text-secondary)', fontSize: '0.85rem'}}>{i}</span>
|
||||
<input
|
||||
type="text"
|
||||
value={channelNames.inputs?.[i] || ''}
|
||||
onChange={(e) => updateChannelName('inputs', i, e.target.value)}
|
||||
placeholder={`Input ${i}`}
|
||||
disabled={!editingChannelNames}
|
||||
style={{
|
||||
padding: 'var(--spacing-sm)',
|
||||
background: editingChannelNames ? 'var(--color-bg)' : 'var(--color-surface-hover)',
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: '6px',
|
||||
color: 'var(--color-text)',
|
||||
fontSize: '0.9rem'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 style={{marginBottom: 'var(--spacing-md)', color: 'var(--color-text-secondary)'}}>Sorties (Outputs)</h4>
|
||||
<div style={{display: 'grid', gap: 'var(--spacing-sm)'}}>
|
||||
{Array.from({length: 8}, (_, i) => (
|
||||
<div key={`output-${i}`} style={{display: 'grid', gridTemplateColumns: '40px 1fr', gap: 'var(--spacing-sm)', alignItems: 'center'}}>
|
||||
<span style={{color: 'var(--color-text-secondary)', fontSize: '0.85rem'}}>{i}</span>
|
||||
<input
|
||||
type="text"
|
||||
value={channelNames.outputs?.[i] || ''}
|
||||
onChange={(e) => updateChannelName('outputs', i, e.target.value)}
|
||||
placeholder={`Output ${i}`}
|
||||
disabled={!editingChannelNames}
|
||||
style={{
|
||||
padding: 'var(--spacing-sm)',
|
||||
background: editingChannelNames ? 'var(--color-bg)' : 'var(--color-surface-hover)',
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: '6px',
|
||||
color: 'var(--color-text)',
|
||||
fontSize: '0.9rem'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{currentDevice && Object.keys(currentDevice).length > 0 && (
|
||||
<div className="current-config">
|
||||
<h3>Configuration actuelle</h3>
|
||||
|
||||
Reference in New Issue
Block a user