CC3: Unified sidebar with external portal links + collapsible icon mode

Shared module:
- /static/shared/sidebar.css   ← unified CSS (#pgz-sb, .pgz-collapsed, mobile overlay, tooltip)
- /static/shared/sidebar.js    ← auto-mounting JS shell + PGZSidebar API
   * Auto-renders #pgz-sb na <body> start (data-inline=1 to opt out)
   * NAV_EXTERNAL: Prijava, Aplikacija, Administracija, CRM, ERP, KPI, Audit, Public portal
   * Toggle (≡) -> localStorage 'sidebarCollapsed' (perzistira preko SVIH stranica)
   * Mobile <768px: ≡ burger + ✕ close, body backdrop
   * Loads /api/auth/me u footer (avatar/username/uloga); ⎋ logout briše JWT i ide na /login
   * data-active="<key>" highlight aktivnog portala

Page integration:
- sport2.html  ← inline NAV_EXTERNAL u buildNav() + "Portali" separator (zadrži postojeći sidebar)
- app.html     ← inline NAV_EXTERNAL u buildNav() (zadrži role-based interni nav, dopuni Portalima)
- admin.html   ← Portali stavke u <aside class="sidebar"> (matching .nav-item style)
- erp.html     ← Portali stavke u <aside class="sidebar"> (matching .nav-item style)
- crm.html     ← include shared sidebar.css + sidebar.js  data-active="crm"
- audit.html   ← include shared sidebar.css + sidebar.js  data-active="audit"
- kpi.html     ← include shared sidebar.css + sidebar.js  data-active="kpi"
- login.html   ← include shared sidebar.css + sidebar.js  data-active="login"

Backups: _backups/{*.cc3_pre_unified_sidebar.*}

Live verified: 8 pages serve HTTP 200; sidebar.css/js HTTP 200; portal markers per page OK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Damir Radulić
2026-05-05 01:11:24 +02:00
parent bd3773434e
commit 8dce58c5f9
10 changed files with 431 additions and 1 deletions
+25 -1
View File
@@ -72,6 +72,11 @@ button,input,select,textarea{font-family:inherit;font-size:inherit;outline:none}
.sb.collapsed .sb-foot{padding:8px;justify-content:center}
.sb.collapsed .sb-foot .ui{display:none}
.sb.collapsed .sb-foot .lo{display:none}
.sb.collapsed .nav-sep{font-size:0;padding:6px 0;text-align:center;border-top:1px dashed var(--rim);margin:6px 8px 4px}
.sb.collapsed .nav-ext span:last-child{display:none}
.nav-ext{color:var(--cyan)}
.nav-ext:hover{color:var(--pgz-gold);background:var(--bg2)}
.nav-ext.active{background:linear-gradient(90deg,var(--pgz-blue) 0%,var(--pgz-blue2) 100%);color:#fff}
.main{margin-left:240px;flex:1;min-width:0;transition:margin-left .22s ease}
.sb.collapsed ~ .main{margin-left:58px}
@@ -592,15 +597,34 @@ function setRole(r){
}
//=========== NAV ===========
const NAV_EXTERNAL = [
{id:'login', href:'/sport/login', ic:'\u{1F511}', label:'Prijava'},
{id:'app', href:'/sport/app', ic:'\u{1F4F1}', label:'Aplikacija'},
{id:'admin', href:'/sport/admin', ic:'\u{1F6E1}', label:'Administracija'},
{id:'crm', href:'/sport/crm', ic:'\u{1F465}', label:'CRM'},
{id:'erp', href:'/sport/erp', ic:'\u{1F4B0}', label:'ERP'},
{id:'kpi', href:'/sport/kpi', ic:'\u{1F4C8}', label:'KPI'},
{id:'audit', href:'/sport/audit', ic:'\u{1F4CB}', label:'Audit'},
{id:'sport2', href:'/sport/static/sport2.html', ic:'\u{1F310}', label:'Public portal'}
];
function buildNav(){
const items = NAV_BY_ROLE[_state.role] || [];
$('#nav').innerHTML = items.map(n =>
let html = items.map(n =>
`<div class="nav-i ${n.id===_state.section?'active':''}" data-id="${n.id}" data-label="${esc(n.label)}" onclick="navTo('${n.id}')">
<span class="ic">${n.ic}</span>
<span class="lbl">${esc(n.label)}</span>
${n.badge?`<span class="badge">${n.badge}</span>`:''}
</div>`
).join('');
// PORTALI separator + external links (active = 'app')
html += '<div class="nav-sep" style="padding:14px 14px 4px 14px;font-size:9.5px;color:var(--t4);text-transform:uppercase;letter-spacing:1.2px;font-weight:700;white-space:nowrap;overflow:hidden">Portali</div>';
html += NAV_EXTERNAL.map(n =>
`<a class="nav-i nav-ext ${n.id==='app'?'active':''}" href="${n.href}" data-id="${n.id}" data-label="${esc(n.label)}" style="text-decoration:none">
<span class="ic">${n.ic}</span><span class="lbl">${esc(n.label)}</span>
<span style="margin-left:auto;font-size:10px;opacity:.5">↗</span>
</a>`
).join('');
$('#nav').innerHTML = html;
}
function navTo(id){
_state.section = id;