From d618d426e7eb1e03ce2a79eab7eb2f8f0f777e7d Mon Sep 17 00:00:00 2001 From: sorcier Date: Wed, 11 Nov 2026 19:57:49 +0100 Subject: [PATCH] feat(ucp): onglet Notice & Codes de fonction avec fiche PDF imprimable (v2.5.3) --- src/UcpManager.php | 18 ++++++ ucp/api.php | 1 + ucp/assets/ucp.js | 156 ++++++++++++++++++++++++++++++++++++++++++++- ucp/index.php | 114 ++++++++++++++++++++++++++++++++- version.json | 3 +- 5 files changed, 289 insertions(+), 3 deletions(-) diff --git a/src/UcpManager.php b/src/UcpManager.php index 2927432..6230835 100644 --- a/src/UcpManager.php +++ b/src/UcpManager.php @@ -744,6 +744,24 @@ class UcpManager return is_file($full) && is_readable($full) ? $full : null; } + /** + * Retourne les codes de fonctions téléphoniques actifs pour la notice UCP. + */ + public static function getFeatureCodesForUser(PDO $db): array + { + try { + $stmt = $db->query(' + SELECT feature_key, code, default_code, name, category, description + FROM feature_codes + WHERE enabled = 1 + ORDER BY id ASC + '); + return $stmt ? $stmt->fetchAll(PDO::FETCH_ASSOC) : []; + } catch (\Throwable) { + return []; + } + } + // ========================================================================= // HELPERS VALIDATION // ========================================================================= diff --git a/ucp/api.php b/ucp/api.php index f8b30e6..d21d692 100644 --- a/ucp/api.php +++ b/ucp/api.php @@ -116,6 +116,7 @@ try { 'csrf_token' => UcpAuth::getCsrfToken(), 'extensions' => UcpManager::getExtensionsState($db, $allowedExts), 'forward_targets' => UcpManager::getForwardTargets($db), + 'feature_codes' => UcpManager::getFeatureCodesForUser($db), ], ]); break; diff --git a/ucp/assets/ucp.js b/ucp/assets/ucp.js index 2377833..df9b61a 100644 --- a/ucp/assets/ucp.js +++ b/ucp/assets/ucp.js @@ -4,6 +4,7 @@ let ucpState = { user: null, extensions: [], forward_targets: [], + feature_codes: [], voicemailsByExt: {}, selectedVmExt: '', vmFolderFilter: 'all', @@ -152,6 +153,8 @@ function switchUcpTab(tabName) { loadPersonalCdr(); } else if (tabName === 'phonebook') { loadPhonebook(); + } else if (tabName === 'guide') { + renderFeatureGuide(); } } @@ -163,6 +166,7 @@ async function refreshUcpState(manual = false) { ucpState.user = res.data.user; ucpState.extensions = res.data.extensions || []; ucpState.forward_targets = res.data.forward_targets || []; + ucpState.feature_codes = res.data.feature_codes || []; if (res.data.csrf_token) { window.UCP_CSRF_TOKEN = res.data.csrf_token; } @@ -177,11 +181,16 @@ async function refreshUcpState(manual = false) { renderExtensionsGrid(); renderVmExtPills(); syncVmPrefsForm(); + renderFeatureGuide(); const accExts = document.getElementById('ucp-account-exts'); if (accExts) { accExts.textContent = ucpState.extensions.map(e => `${e.extension} (${e.name})`).join(', ') || 'Aucun poste lié'; } + const guideExts = document.getElementById('ucp-guide-user-exts'); + if (guideExts) { + guideExts.textContent = ucpState.extensions.map(e => e.extension).join(', ') || 'Aucun'; + } if (manual) { showUcpToast('État actualisé.', 'info'); @@ -788,7 +797,152 @@ if (profileEmailForm) { } // ----------------------------------------------------------------------------- -// 5. CHANGEMENT DE MOT DE PASSE UCP +// 5. NOTICE D'UTILISATION, CODES DE FONCTION & EXPORT PDF +// ----------------------------------------------------------------------------- + +function printFeatureGuide() { + if (ucpState.activeTab !== 'guide') { + switchUcpTab('guide'); + } + const dateEl = document.getElementById('ucp-guide-print-date'); + if (dateEl) { + dateEl.textContent = new Date().toLocaleDateString('fr-FR'); + } + setTimeout(() => window.print(), 80); +} + +function renderFeatureGuide() { + const quickBox = document.getElementById('ucp-guide-quickcards'); + const catBox = document.getElementById('ucp-guide-categories'); + if (!quickBox || !catBox) return; + + const allCodes = ucpState.feature_codes || []; + const byKey = new Map(allCodes.map(c => [String(c.feature_key), c])); + const codeOf = (key, fallback = '') => escapeHtml(byKey.get(key)?.code || fallback); + + // 1. Encarts "Notice pratique pas à pas" + const vmOwn = byKey.get('voicemail_own'); + const vmOther = byKey.get('voicemail_general'); + const cfOn = byKey.get('cf_activate'); + const cfOff = byKey.get('cf_deactivate'); + const cfuOn = byKey.get('cfu_activate'); + const dndTog = byKey.get('dnd_toggle'); + const dndOn = byKey.get('dnd_activate'); + const pkBlf = byKey.get('pickup_blf'); + const pkGrp = byKey.get('pickup_group'); + + quickBox.innerHTML = ` +
+
+ 📬 + Utiliser la Messagerie Vocale +
+ +
+ +
+
+ ↪️ + Gérer Renvois & Ne Pas Déranger +
+ +
+ +
+
+ 🎧 + Interception & Prise d'Appel +
+ +
+ `; + + // 2. Regroupement des codes actifs par catégorie + const q = (document.getElementById('ucp-guide-search')?.value || '').trim().toLowerCase(); + const filtered = allCodes.filter(c => { + if (!q) return true; + return String(c.code || '').toLowerCase().includes(q) + || String(c.name || '').toLowerCase().includes(q) + || String(c.description || '').toLowerCase().includes(q) + || String(c.category || '').toLowerCase().includes(q); + }); + + if (!filtered.length) { + catBox.innerHTML = `
Aucun code de fonction correspondant à « ${escapeHtml(q)} ».
`; + return; + } + + const catMeta = { + voicemail: { label: 'Messagerie Vocale', icon: '📬', badge: 'bg-indigo-50 text-indigo-700 border-indigo-200', header: 'bg-indigo-50/60 border-indigo-100 text-indigo-950' }, + forward: { label: "Renvois d'Appel", icon: '↪️', badge: 'bg-amber-50 text-amber-800 border-amber-200', header: 'bg-amber-50/60 border-amber-100 text-amber-950' }, + dnd: { label: 'Ne Pas Déranger (DND)', icon: '🔕', badge: 'bg-rose-50 text-rose-700 border-rose-200', header: 'bg-rose-50/60 border-rose-100 text-rose-950' }, + pickup: { label: "Interception d'Appels", icon: '📲', badge: 'bg-emerald-50 text-emerald-700 border-emerald-200', header: 'bg-emerald-50/60 border-emerald-100 text-emerald-950' }, + security: { label: 'Liste Noire & Blocage Appels', icon: '🛡️', badge: 'bg-slate-100 text-slate-700 border-slate-300', header: 'bg-slate-100/80 border-slate-200 text-slate-900' }, + tools: { label: 'Outils, Tests Audio & Services', icon: '🛠️', badge: 'bg-sky-50 text-sky-700 border-sky-200', header: 'bg-sky-50/60 border-sky-100 text-sky-950' }, + }; + + const orderedCats = ['voicemail', 'forward', 'dnd', 'pickup', 'security', 'tools']; + const grouped = new Map(); + filtered.forEach(c => { + const key = c.category || 'tools'; + if (!grouped.has(key)) grouped.set(key, []); + grouped.get(key).push(c); + }); + + const catKeys = [ + ...orderedCats.filter(k => grouped.has(k)), + ...[...grouped.keys()].filter(k => !orderedCats.includes(k)), + ]; + + catBox.innerHTML = catKeys.map(catKey => { + const meta = catMeta[catKey] || { label: catKey, icon: '📌', badge: 'bg-slate-100 text-slate-700 border-slate-300', header: 'bg-slate-50 border-slate-200 text-slate-900' }; + const items = grouped.get(catKey) || []; + + const rows = items.map(item => ` +
+
+ + ${escapeHtml(item.code)} + +
+
+
${escapeHtml(item.name)}
+

${escapeHtml(item.description || '')}

+
+
+ `).join(''); + + return ` +
+
+
+ ${meta.icon} + ${escapeHtml(meta.label)} +
+ ${items.length} code(s) +
+
${rows}
+
+ `; + }).join(''); +} + +// ----------------------------------------------------------------------------- +// 6. CHANGEMENT DE MOT DE PASSE UCP // ----------------------------------------------------------------------------- const passwordForm = document.getElementById('ucp-password-form'); diff --git a/ucp/index.php b/ucp/index.php index def8e1a..7a67c1b 100644 --- a/ucp/index.php +++ b/ucp/index.php @@ -47,6 +47,54 @@ $csrfToken = UcpAuth::getCsrfToken(); ::-webkit-scrollbar-track { background: #f1f5f9; } ::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: #94a3b8; } + + @page { + size: A4 portrait; + margin: 11mm 12mm; + } + @media print { + html, body { + background: #ffffff !important; + color: #0f172a !important; + -webkit-print-color-adjust: exact !important; + print-color-adjust: exact !important; + } + header, #ucp-toast-container, #ucp-profile-modal, .no-print { + display: none !important; + } + main { + max-width: 100% !important; + padding: 0 !important; + margin: 0 !important; + } + .ucp-panel { + display: none !important; + } + #ucp-panel-guide { + display: block !important; + margin: 0 !important; + padding: 0 !important; + } + #ucp-guide-printable { + border: none !important; + box-shadow: none !important; + padding: 0 !important; + margin: 0 !important; + } + .ucp-print-avoid-break { + break-inside: avoid !important; + page-break-inside: avoid !important; + } + .ucp-print-3col { + display: grid !important; + grid-template-columns: repeat(3, minmax(0, 1fr)) !important; + gap: 0.75rem !important; + } + .ucp-print-row { + flex-direction: row !important; + align-items: center !important; + } + } @@ -160,6 +208,10 @@ $csrfToken = UcpAuth::getCsrfToken(); class="ucp-tab-btn px-3.5 py-2 rounded-xl text-xs font-semibold transition flex items-center gap-2 text-slate-600 hover:bg-slate-100"> 📖 Annuaire & Profil + + + + + +
+ +
+
+
+ + Logo + + NexusPBX + +
+
+
Fiche Mémo Utilisateur • Téléphonie IP
+

+

Guide des codes de fonction, messagerie vocale et renvois d'appel

+
+
+
+
Utilisateur :
+
Poste(s) : —
+
Édité le • v
+
+
+ + +
+ + +
+
Chargement des codes de fonction…
+
+ + +
+
💡 Astuce : composez ces codes directement sur votre combiné IP, ou pilotez DND / renvois / messagerie depuis le portail UCP.
+
— Document interne
+
+
+ + +