feat: ajout filtre canaux nommés dans matrice routing
This commit is contained in:
@@ -177,7 +177,7 @@ Valider la faisabilité technique : 2-4 clients, PTT basique, latence < 150ms, m
|
|||||||
- [x] API PUT /api/audio/channels/names (sauvegarde noms canaux)
|
- [x] API PUT /api/audio/channels/names (sauvegarde noms canaux)
|
||||||
- [x] API GET /api/audio/channels/names (récupération noms)
|
- [x] API GET /api/audio/channels/names (récupération noms)
|
||||||
- [x] Page admin : formulaire nommage canaux (inputs/outputs)
|
- [x] Page admin : formulaire nommage canaux (inputs/outputs)
|
||||||
- [ ] Page admin : filtre "canaux nommés uniquement"
|
- [x] Page admin : filtre "canaux nommés uniquement"
|
||||||
- [x] Sauvegarde automatique dans config.yaml
|
- [x] Sauvegarde automatique dans config.yaml
|
||||||
|
|
||||||
#### Matrice de routing (style Dante Controller)
|
#### Matrice de routing (style Dante Controller)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000';
|
|||||||
function AudioRoutingMatrix({ groups, channelNames }) {
|
function AudioRoutingMatrix({ groups, channelNames }) {
|
||||||
const [routing, setRouting] = useState({ inputToGroup: {}, groupToOutput: {}, gains: {} });
|
const [routing, setRouting] = useState({ inputToGroup: {}, groupToOutput: {}, gains: {} });
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [showOnlyNamedChannels, setShowOnlyNamedChannels] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadRouting();
|
loadRouting();
|
||||||
@@ -104,6 +105,26 @@ function AudioRoutingMatrix({ groups, channelNames }) {
|
|||||||
return name || `${type === 'inputs' ? 'Input' : 'Output'} ${id}`;
|
return name || `${type === 'inputs' ? 'Input' : 'Output'} ${id}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hasCustomName = (type, id) => {
|
||||||
|
return channelNames?.[type]?.[id] !== undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getVisibleInputChannels = () => {
|
||||||
|
const allInputs = Array.from({length: 8}, (_, i) => i);
|
||||||
|
if (showOnlyNamedChannels) {
|
||||||
|
return allInputs.filter(i => hasCustomName('inputs', i));
|
||||||
|
}
|
||||||
|
return allInputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getVisibleOutputChannels = () => {
|
||||||
|
const allOutputs = Array.from({length: 8}, (_, i) => i);
|
||||||
|
if (showOnlyNamedChannels) {
|
||||||
|
return allOutputs.filter(i => hasCustomName('outputs', i));
|
||||||
|
}
|
||||||
|
return allOutputs;
|
||||||
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <div style={{padding: 'var(--spacing-xl)', textAlign: 'center'}}>Chargement...</div>;
|
return <div style={{padding: 'var(--spacing-xl)', textAlign: 'center'}}>Chargement...</div>;
|
||||||
}
|
}
|
||||||
@@ -112,9 +133,19 @@ function AudioRoutingMatrix({ groups, channelNames }) {
|
|||||||
<div className="routing-matrix-container">
|
<div className="routing-matrix-container">
|
||||||
<div className="routing-matrix-header">
|
<div className="routing-matrix-header">
|
||||||
<h3>Matrice de routing audio</h3>
|
<h3>Matrice de routing audio</h3>
|
||||||
<button onClick={saveRouting} className="btn-primary">
|
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
||||||
Sauvegarder le routing
|
<label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer' }}>
|
||||||
</button>
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={showOnlyNamedChannels}
|
||||||
|
onChange={(e) => setShowOnlyNamedChannels(e.target.checked)}
|
||||||
|
/>
|
||||||
|
<span>Afficher uniquement les canaux nommés</span>
|
||||||
|
</label>
|
||||||
|
<button onClick={saveRouting} className="btn-primary">
|
||||||
|
Sauvegarder le routing
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="routing-section">
|
<div className="routing-section">
|
||||||
@@ -132,7 +163,7 @@ function AudioRoutingMatrix({ groups, channelNames }) {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{Array.from({length: 8}, (_, i) => (
|
{getVisibleInputChannels().map(i => (
|
||||||
<React.Fragment key={`input-row-${i}`}>
|
<React.Fragment key={`input-row-${i}`}>
|
||||||
<div className="matrix-label-cell">
|
<div className="matrix-label-cell">
|
||||||
{getChannelName('inputs', i)}
|
{getChannelName('inputs', i)}
|
||||||
@@ -158,10 +189,10 @@ function AudioRoutingMatrix({ groups, channelNames }) {
|
|||||||
Sélectionnez vers quels outputs chaque groupe envoie son audio
|
Sélectionnez vers quels outputs chaque groupe envoie son audio
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="routing-matrix" style={{gridTemplateColumns: '120px repeat(8, minmax(60px, 1fr))'}}>
|
<div className="routing-matrix" style={{gridTemplateColumns: `120px repeat(${getVisibleOutputChannels().length}, minmax(60px, 1fr))`}}>
|
||||||
<div className="matrix-corner"></div>
|
<div className="matrix-corner"></div>
|
||||||
|
|
||||||
{Array.from({length: 8}, (_, i) => (
|
{getVisibleOutputChannels().map(i => (
|
||||||
<div key={`output-header-${i}`} className="matrix-header-cell">
|
<div key={`output-header-${i}`} className="matrix-header-cell">
|
||||||
{getChannelName('outputs', i)}
|
{getChannelName('outputs', i)}
|
||||||
</div>
|
</div>
|
||||||
@@ -173,7 +204,7 @@ function AudioRoutingMatrix({ groups, channelNames }) {
|
|||||||
{group.name}
|
{group.name}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{Array.from({length: 8}, (_, i) => (
|
{getVisibleOutputChannels().map(i => (
|
||||||
<div
|
<div
|
||||||
key={`${group.id}-${i}`}
|
key={`${group.id}-${i}`}
|
||||||
className={`matrix-cell ${isGroupRoutedToOutput(group.id, String(i)) ? 'active' : ''}`}
|
className={`matrix-cell ${isGroupRoutedToOutput(group.id, String(i)) ? 'active' : ''}`}
|
||||||
|
|||||||
Reference in New Issue
Block a user