[FEAT] Ruolo Consulente + Wizard Registrazione v2
- register.html: step 0 scelta profilo (Azienda / Consulente) - onboarding.html: wizard 4-step con P.IVA obbligatoria (auto-fetch CertiSource) - companies.html: nuova dashboard consulente con cards aziende e compliance score - common.js: org-switcher sidebar + role labels corretti per consulente - login.html: routing post-login (consulente → companies.html) - api.js: isConsultant(), setUserRole(), register con user_type - AuthController: user_type=consultant → role=consultant in users table - OnboardingController: multi-org per consulente, duplicate VAT check - 005_consultant_support.sql: aggiunge 'consultant' a user_organizations.role ENUM Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
ba21534e6a
commit
7080695d06
+102
-2
@@ -230,6 +230,9 @@ function loadSidebar() {
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Org-switcher area (populated async for consultants)
|
||||
navHTML += `<div id="sidebar-org-switcher" style="display:none;padding:8px 12px;border-bottom:1px solid var(--gray-100);"></div>`;
|
||||
|
||||
// Nav sections
|
||||
navHTML += '<nav class="sidebar-nav">';
|
||||
for (const section of navItems) {
|
||||
@@ -263,13 +266,23 @@ function loadSidebar() {
|
||||
|
||||
container.innerHTML = navHTML;
|
||||
|
||||
// Carica info utente
|
||||
// Carica info utente (e org-switcher se consulente)
|
||||
_loadUserInfo();
|
||||
|
||||
// Mobile toggle
|
||||
_setupMobileToggle();
|
||||
}
|
||||
|
||||
const _roleLabels = {
|
||||
super_admin: 'Super Admin',
|
||||
org_admin: 'Admin Org',
|
||||
compliance_manager: 'Compliance Manager',
|
||||
board_member: 'Membro CDA',
|
||||
auditor: 'Auditor',
|
||||
employee: 'Utente',
|
||||
consultant: 'Consulente',
|
||||
};
|
||||
|
||||
async function _loadUserInfo() {
|
||||
try {
|
||||
const result = await api.getMe();
|
||||
@@ -288,13 +301,100 @@ async function _loadUserInfo() {
|
||||
.toUpperCase();
|
||||
avatarEl.textContent = initials;
|
||||
}
|
||||
if (roleEl) roleEl.textContent = user.role === 'admin' ? 'Amministratore' : 'Utente';
|
||||
if (roleEl) roleEl.textContent = _roleLabels[user.role] || user.role || 'Utente';
|
||||
|
||||
// Save role to localStorage (ensures isConsultant() works across pages)
|
||||
if (user.role) api.setUserRole(user.role);
|
||||
|
||||
// For consultants: render org-switcher
|
||||
if (user.role === 'consultant') {
|
||||
_loadConsultantOrgSwitcher();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silenzioso
|
||||
}
|
||||
}
|
||||
|
||||
async function _loadConsultantOrgSwitcher() {
|
||||
try {
|
||||
const result = await api.listOrganizations();
|
||||
if (!result.success || !result.data || result.data.length === 0) return;
|
||||
|
||||
const orgs = result.data;
|
||||
const currentOrgId = parseInt(api.orgId);
|
||||
const currentOrg = orgs.find(o => (o.id || o.organization_id) === currentOrgId);
|
||||
|
||||
const switcher = document.getElementById('sidebar-org-switcher');
|
||||
if (!switcher) return;
|
||||
|
||||
const dropdownId = 'org-dropdown-' + Date.now();
|
||||
switcher.style.display = 'block';
|
||||
switcher.innerHTML = `
|
||||
<div style="position:relative">
|
||||
<button onclick="document.getElementById('${dropdownId}').classList.toggle('open')"
|
||||
style="width:100%;display:flex;align-items:center;gap:8px;padding:6px 10px;
|
||||
background:var(--gray-50);border:1px solid var(--gray-200);border-radius:6px;
|
||||
cursor:pointer;font-size:.8rem;font-weight:600;color:var(--gray-700);">
|
||||
<svg width="14" height="14" viewBox="0 0 20 20" fill="currentColor" style="flex-shrink:0;color:var(--primary)">
|
||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h8a2 2 0 012 2v12a1 1 0 01-1 1H5a1 1 0 01-1-1V4zm3 1h2v2H7V5zm2 4H7v2h2V9zm2-4h2v2h-2V5zm2 4h-2v2h2V9z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span style="flex:1;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
|
||||
${currentOrg ? currentOrg.name : 'Seleziona azienda'}
|
||||
</span>
|
||||
<svg width="12" height="12" viewBox="0 0 20 20" fill="currentColor" style="flex-shrink:0">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div id="${dropdownId}" class="org-switcher-dropdown"
|
||||
style="display:none;position:absolute;left:0;right:0;top:calc(100% + 4px);
|
||||
background:var(--card-bg);border:1px solid var(--gray-200);border-radius:6px;
|
||||
box-shadow:var(--card-shadow);z-index:200;overflow:hidden;">
|
||||
${orgs.map(org => {
|
||||
const orgId = org.id || org.organization_id;
|
||||
const isActive = orgId === currentOrgId;
|
||||
return `<button onclick="_switchOrg(${orgId})"
|
||||
style="width:100%;text-align:left;padding:8px 12px;
|
||||
background:${isActive ? 'var(--primary-bg)' : 'transparent'};
|
||||
border:none;cursor:pointer;font-size:.8rem;
|
||||
color:${isActive ? 'var(--primary)' : 'var(--gray-700)'};
|
||||
font-weight:${isActive ? '600' : '400'};">
|
||||
${isActive ? '✓ ' : ''}${org.name}
|
||||
</button>`;
|
||||
}).join('')}
|
||||
<div style="border-top:1px solid var(--gray-100)">
|
||||
<a href="companies.html"
|
||||
style="display:block;padding:8px 12px;font-size:.8rem;
|
||||
color:var(--primary);text-decoration:none;">
|
||||
← Tutte le aziende
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// Close on outside click
|
||||
document.addEventListener('click', (e) => {
|
||||
const dd = document.getElementById(dropdownId);
|
||||
if (dd && !switcher.contains(e.target)) dd.style.display = 'none';
|
||||
});
|
||||
// Override toggle to handle display
|
||||
const ddEl = document.getElementById(dropdownId);
|
||||
if (ddEl) {
|
||||
const btn = switcher.querySelector('button');
|
||||
if (btn) btn.onclick = () => {
|
||||
ddEl.style.display = ddEl.style.display === 'none' ? 'block' : 'none';
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
// Silenzioso
|
||||
}
|
||||
}
|
||||
|
||||
function _switchOrg(orgId) {
|
||||
api.setOrganization(orgId);
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
function _setupMobileToggle() {
|
||||
// Crea pulsante toggle se non esiste
|
||||
if (!document.querySelector('.sidebar-toggle')) {
|
||||
|
||||
Reference in New Issue
Block a user