diff --git a/public/api.php b/public/api.php
index 9149093..a8be442 100644
--- a/public/api.php
+++ b/public/api.php
@@ -544,6 +544,14 @@ try {
if ($name === '') {
throw new InvalidArgumentException("Le nom du groupe d'appel est obligatoire.");
}
+ if (str_starts_with($failover, 'vm-')) {
+ $vmTarget = substr($failover, 3);
+ $vmStmt = $db->prepare('SELECT voicemail_enabled FROM extensions WHERE extension = :ext AND enabled = 1');
+ $vmStmt->execute([':ext' => $vmTarget]);
+ if (!(int)$vmStmt->fetchColumn()) {
+ throw new InvalidArgumentException("Le répondeur du poste {$vmTarget} n'existe pas ou n'est pas activé.");
+ }
+ }
if ($id) {
$stmt = $db->prepare('
diff --git a/public/assets/app.js b/public/assets/app.js
index 7ade917..8568ae4 100644
--- a/public/assets/app.js
+++ b/public/assets/app.js
@@ -1234,6 +1234,9 @@ function renderGroupsTable() {
const badges = members.length
? members.map(m => `${escapeHtml(m)}`).join(' ')
: 'Aucun membre';
+ const failoverLabel = String(grp.failover_dest || '').startsWith('vm-')
+ ? `📬 Répondeur ${escapeHtml(String(grp.failover_dest).slice(3))}`
+ : `${escapeHtml(grp.failover_dest)}`;
return `
@@ -1241,7 +1244,7 @@ function renderGroupsTable() {
| ${escapeHtml(grp.name)} |
${escapeHtml(grp.strategy)} |
${badges} |
- ${Number(grp.ring_time)}s → ${escapeHtml(grp.failover_dest)} |
+ ${Number(grp.ring_time)}s → ${failoverLabel} |
@@ -1893,6 +1896,21 @@ function buildDestinationOptionsHtml(selectedValue = 'ext-101') {
html += ``;
}
+ const vmExts = exts.filter(e => Number(e.voicemail_enabled) === 1);
+ const hasMissingVm = normVal.startsWith('vm-') && !vmExts.some(e => `vm-${e.extension}` === normVal);
+ if (vmExts.length > 0 || hasMissingVm) {
+ html += ``;
+ }
+
// Piège à Spammeurs / Musique d'attente en boucle continue (MoH)
const mohSounds = sounds.filter(s => s.usage_type === 'moh');
html += ` |