[FEAT] UI Fasi 4+5: Impersonate banner + Preferenze + Branding white-label (v1.6.0)
Completamento UI per gli endpoint backend già attivi (commit e4f9e91):
- admin/users.html: colonna Azioni con pulsante "Impersonate" per utenti non-super_admin
attivi → salva token originale in sessionStorage, sostituisce con quello impersonate,
redirige a dashboard
- js/common.js: banner persistente arancione "Modalità Impersonate" in tutte le
pagine quando sessionStorage ha impersonate origin → pulsante "Esci impersonate"
ripristina token originale e torna ad admin/users
- settings.html: nuovo tab "Preferenze" (lingua/tema/timezone/notifiche email+in-app)
con form salva via PUT /auth/preferences
- settings.html: nuovo tab "Branding" (solo super_admin / consulente) con
brand_name/logo_url/primary_color/secondary_color, PUT /branding
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
e4f9e9179e
commit
a7bd37a797
@@ -291,6 +291,9 @@ function loadSidebar() {
|
||||
|
||||
// Firm branding white-label (Fase 5 / G16) — non bloccante
|
||||
_loadFirmBranding();
|
||||
|
||||
// Impersonate banner (Fase 4 / G11 — UI)
|
||||
_renderImpersonateBanner();
|
||||
}
|
||||
|
||||
const _roleLabels = {
|
||||
@@ -431,6 +434,36 @@ async function _switchOrg(orgId) {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
// ── Impersonate banner persistente (Fase 4 / G11 — UI) ──────────────────
|
||||
function _renderImpersonateBanner() {
|
||||
const originToken = sessionStorage.getItem('nis2_impersonate_origin_token');
|
||||
const targetEmail = sessionStorage.getItem('nis2_impersonate_target_email');
|
||||
if (!originToken) return;
|
||||
|
||||
if (document.getElementById('impersonate-banner')) return;
|
||||
const bar = document.createElement('div');
|
||||
bar.id = 'impersonate-banner';
|
||||
bar.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:9999;background:#F59E0B;color:#1F2937;padding:8px 16px;text-align:center;font-size:.85rem;font-weight:600;box-shadow:0 2px 8px rgba(0,0,0,.2);';
|
||||
bar.innerHTML =
|
||||
'<svg width="16" height="16" viewBox="0 0 20 20" fill="currentColor" style="vertical-align:-3px;margin-right:6px"><path d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.94 6.94a.75.75 0 011.06 0L10 6.94l.5.5L10 7.94l-.5-.5L9.5 8h-1V7l.5-.5z"/></svg>'
|
||||
+ 'Modalità Impersonate — stai usando l\'account di <strong>' + (targetEmail || '?') + '</strong> '
|
||||
+ '<button onclick="_exitImpersonate()" style="background:#1F2937;color:#fff;border:none;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:.8rem;font-weight:600;margin-left:8px;">Esci impersonate</button>';
|
||||
document.body.insertBefore(bar, document.body.firstChild);
|
||||
// Sposta il contenuto sotto
|
||||
document.body.style.paddingTop = (bar.offsetHeight + (parseInt(document.body.style.paddingTop) || 0)) + 'px';
|
||||
}
|
||||
|
||||
function _exitImpersonate() {
|
||||
const originToken = sessionStorage.getItem('nis2_impersonate_origin_token');
|
||||
const originRefresh = sessionStorage.getItem('nis2_impersonate_origin_refresh');
|
||||
if (!originToken) return;
|
||||
api.setTokens(originToken, originRefresh || '');
|
||||
sessionStorage.removeItem('nis2_impersonate_origin_token');
|
||||
sessionStorage.removeItem('nis2_impersonate_origin_refresh');
|
||||
sessionStorage.removeItem('nis2_impersonate_target_email');
|
||||
window.location.href = '/admin/users.html';
|
||||
}
|
||||
|
||||
// ── Firm branding white-label (Fase 5 / G16) ────────────────────────────
|
||||
async function _loadFirmBranding() {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user