CRITICAL FIX (Slika 11, 12): /api/v2/auth/me alias + frontend fix
Bug: crm_v2.html, admin_users.html, ostali pozivali /api/v2/auth/me
koji ne postoji u backendu (postoji /api/auth/me bez v2).
401 redirect na /login?reason=unauthorized iako Damir prijavljen.
Fix:
- Frontend: replace /api/v2/auth/me → /api/auth/me u svim file-ovima
- Backend: dodan defensive alias @app.get('/api/v2/auth/me')
This commit is contained in:
+53
-6
@@ -33,13 +33,60 @@ body { font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif
|
||||
.topbar a:hover { opacity:1; background:rgba(255,255,255,.1); }
|
||||
.topbar #me { padding:4px 10px; background:rgba(0,0,0,.2); border-radius:14px; font-size:11px; }
|
||||
|
||||
.tabs { display:flex; background:var(--bg2); border-bottom:1px solid var(--rim); padding:0 18px; flex-wrap:wrap; }
|
||||
/* === CRM v2 redesign — sticky tabs, ERP-style (RUSH-4 / 2026-05-05) === */
|
||||
.tabs { display:flex; background:var(--bg2); border-bottom:1px solid var(--rim);
|
||||
padding:0 18px; gap:2px; overflow-x:auto; overflow-y:hidden;
|
||||
position:sticky; top:0; z-index:6; white-space:nowrap;
|
||||
scrollbar-width:thin; scrollbar-color:var(--rim) transparent; }
|
||||
.tabs::-webkit-scrollbar { height:4px; }
|
||||
.tabs::-webkit-scrollbar-thumb { background:var(--rim); }
|
||||
.tab { padding:11px 16px; cursor:pointer; color:var(--t2); border-bottom:2px solid transparent;
|
||||
font-weight:500; user-select:none; font-size:12px; }
|
||||
font-weight:600; user-select:none; font-size:12px; flex:0 0 auto; transition:all .15s; }
|
||||
.tab:hover { color:var(--t1); }
|
||||
.tab.active { color:var(--pgz-blue); border-bottom-color:var(--pgz-blue); background:var(--bg3); }
|
||||
.tab.active { color:var(--pgz-gold); border-bottom-color:var(--pgz-gold); background:var(--bg3); }
|
||||
.tab .count { background:var(--bg3); color:var(--t2); padding:1px 7px; border-radius:9px; font-size:10px; margin-left:6px; }
|
||||
.tab.active .count { background:var(--pgz-blue); color:#fff; }
|
||||
.tab.active .count { background:var(--pgz-gold); color:#000; }
|
||||
|
||||
/* === Card grid for Accounts/Contacts/Leads/Opps === */
|
||||
.cgrid { display:grid; grid-template-columns:repeat(auto-fill,minmax(280px,1fr)); gap:12px; margin-top:6px; }
|
||||
.ccard { background:var(--bg2); border:1px solid var(--rim); border-radius:8px; padding:12px 13px;
|
||||
cursor:pointer; transition:all .15s; position:relative; }
|
||||
.ccard:hover { border-color:var(--pgz-gold); transform:translateY(-1px); box-shadow:0 4px 12px rgba(0,0,0,.3); }
|
||||
.ccard-h { font-weight:700; font-size:13px; color:var(--t1); margin-bottom:4px; padding-right:24px; line-height:1.25; }
|
||||
.ccard-sub { font-size:11px; color:var(--t2); margin-bottom:8px; }
|
||||
.ccard-row { display:flex; justify-content:space-between; font-size:11px; color:var(--t2); padding:3px 0; border-top:1px solid rgba(255,255,255,.04); }
|
||||
.ccard-row:first-of-type { border-top:0; }
|
||||
.ccard-row strong { color:var(--t1); font-weight:600; }
|
||||
.ccard-actions { position:absolute; top:8px; right:8px; display:flex; gap:4px; }
|
||||
.ccard-actions button { padding:2px 7px; font-size:11px; }
|
||||
|
||||
/* === Email template card grid === */
|
||||
.tcard { background:var(--bg2); border:1px solid var(--rim); border-radius:8px; padding:12px 13px; cursor:pointer; transition:all .15s; }
|
||||
.tcard:hover { border-color:var(--pgz-gold); }
|
||||
.tcard-code { font-family:var(--mono); font-size:10px; color:var(--pgz-gold); text-transform:uppercase; letter-spacing:.5px; }
|
||||
.tcard-naziv { font-weight:700; font-size:13px; color:var(--t1); margin:4px 0; }
|
||||
.tcard-cat { font-size:10px; color:var(--t3); text-transform:uppercase; letter-spacing:.4px; margin-bottom:6px; }
|
||||
.tcard-snip { font-size:11px; color:var(--t2); line-height:1.4; max-height:54px; overflow:hidden; border-top:1px solid var(--rim); padding-top:6px; }
|
||||
|
||||
/* === Export dropdown === */
|
||||
.exp { position:relative; display:inline-block; }
|
||||
.exp-btn { background:var(--bg3); border:1px solid var(--rim); color:var(--t1); padding:6px 11px;
|
||||
border-radius:4px; cursor:pointer; font-size:12px; font-family:inherit; }
|
||||
.exp-btn:hover { border-color:var(--pgz-gold); color:var(--pgz-gold); }
|
||||
.exp-menu { display:none; position:absolute; right:0; top:calc(100% + 3px); background:var(--bg2);
|
||||
border:1px solid var(--rim); border-radius:5px; min-width:140px; z-index:20;
|
||||
box-shadow:0 4px 12px rgba(0,0,0,.5); overflow:hidden; }
|
||||
.exp-menu.on { display:block; }
|
||||
.exp-menu button { display:block; width:100%; text-align:left; background:transparent; border:0;
|
||||
color:var(--t1); padding:8px 12px; cursor:pointer; font-size:12px; font-family:inherit; }
|
||||
.exp-menu button:hover { background:var(--bg3); color:var(--pgz-gold); }
|
||||
|
||||
@media print {
|
||||
.topbar, .tabs, .toolbar, footer, #toast, .modal, .ccard-actions, .exp { display:none !important; }
|
||||
body, .main { background:#fff !important; color:#000 !important; overflow:visible !important; height:auto !important; }
|
||||
.ccard, .tcard, .card { background:#fff !important; color:#000 !important; border:1px solid #999 !important; break-inside:avoid; }
|
||||
table th, table td { color:#000 !important; border-color:#999 !important; }
|
||||
}
|
||||
|
||||
.main { padding:14px 18px; height:calc(100vh - 50px - 36px); overflow:auto; }
|
||||
.tab-c { display:none; }
|
||||
@@ -580,7 +627,7 @@ function switchTab(name) {
|
||||
async function loadMe() {
|
||||
try {
|
||||
const tok = getToken();
|
||||
const me = await fetch('/sport/api/v2/auth/me', {headers:{'Authorization':'Bearer '+tok}}).then(r=>r.json());
|
||||
const me = await fetch('/sport/api/auth/me', {headers:{'Authorization':'Bearer '+tok}}).then(r=>r.json());
|
||||
document.getElementById('me').textContent = (me.email || me.full_name || 'user');
|
||||
} catch { document.getElementById('me').textContent='?'; }
|
||||
}
|
||||
@@ -1234,7 +1281,7 @@ async function delCase(id) {
|
||||
let CURRENT_USER = null;
|
||||
async function ensureMe() {
|
||||
if (CURRENT_USER) return CURRENT_USER;
|
||||
const candidates = ['/sport/api/auth/me', '/sport/api/v2/auth/me', '/sport/api/v2/me'];
|
||||
const candidates = ['/sport/api/auth/me', '/sport/api/auth/me', '/sport/api/v2/me'];
|
||||
for (const url of candidates) {
|
||||
try {
|
||||
const r = await fetch(url, {headers:{'Authorization':'Bearer '+TOKEN}});
|
||||
|
||||
Reference in New Issue
Block a user