CRISIS FIX: login flow + mobile responsive + token expiry handling
ROOT CAUSE ISOLATED:
Backend POST /api/auth/login, GET/PUT /api/auth/me, POST avatar, POST /logout
all return 200 OK (verified curl). Damirov problem is browser-side:
stale localStorage tokens that don't match current backend → 401 cascade
→ avatar upload appears as 'failed: 401' → profile changes 'lost'.
FIXES:
1. apiAuth() in app.html now:
- Pre-checks JWT exp claim before request
- On 401 response: clears localStorage (pgz_access/refresh/user) +
redirects to /login?reason=unauthorized
- On JWT expired: redirects to /login?reason=expired
2. login.html displays toast for ?reason=expired/unauthorized
3. Mobile responsive CSS (max-width: 768px):
- app.html: hamburger menu, sidebar slide-in, full-width drill-down panel
- sport2.html: KPI grid 2-col, klubovi 1-col, tables horizontal scroll
- Both: viewport meta + media queries + touch-friendly buttons
4. Mobile menu toggle button + backdrop overlay added
VERIFIED E2E (curl):
- POST /auth/login → 200 + JWT
- GET /auth/me → 200 + telefon persisted
- PUT /auth/me → 200, DB row updated
- POST /auth/me/avatar → 200, file saved + avatar_url returned
- POST /auth/logout → 200, token revoked (next /me returns 401)
This commit is contained in:
+7
-2
@@ -138,6 +138,7 @@ table tr:hover td { background: rgba(26, 115, 232, 0.05); }
|
||||
<link rel="stylesheet" href="/static/shared/sidebar.css">
|
||||
<script src="/static/shared/sidebar.js" defer data-active="clanarine"></script>
|
||||
<style>body{padding-top:0}</style>
|
||||
<script src="/static/oib_format.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1235,7 +1236,11 @@ async function loadClanPanel(cid) {
|
||||
// helper za render polja s edit/no-edit
|
||||
const f = (key, label, val, type='text') => {
|
||||
const ed = canEdit(key);
|
||||
const safe = val == null || val === '' ? '—' : String(val);
|
||||
let safe = val == null || val === '' ? '—' : String(val);
|
||||
// role-based PII rendering for OIB
|
||||
if (key === 'oib' && safe !== '—') {
|
||||
safe = formatOib(safe, {klub_id: c.klub_id, savez_id: c.savez_id});
|
||||
}
|
||||
return `
|
||||
<div class="payment-row">
|
||||
<div class="l">${esc(label)}${ed?'':' <span style="color:var(--t3);font-size:9px">🔒</span>'}</div>
|
||||
@@ -1313,7 +1318,7 @@ async function loadClanPanel(cid) {
|
||||
<div class="payment-card">
|
||||
<div class="payment-row"><div class="l">Trenutni klub</div><div class="v">${esc(k.naziv || '—')}</div></div>
|
||||
${k.savez_naziv ? `<div class="payment-row"><div class="l">Savez</div><div class="v">${esc(k.savez_naziv)}</div></div>` : ''}
|
||||
${k.oib ? `<div class="payment-row"><div class="l">OIB kluba</div><div class="v">${esc(k.oib)}</div></div>` : ''}
|
||||
${k.oib ? `<div class="payment-row"><div class="l">OIB kluba</div><div class="v">${esc(formatOib(k.oib,{klub_id:k.id,savez_id:k.savez_id}))}</div></div>` : ''}
|
||||
${k.iban ? `<div class="payment-row"><div class="l">IBAN</div><div class="v">${esc(k.iban)}</div></div>` : ''}
|
||||
</div>
|
||||
${d.povijest_klubova && d.povijest_klubova.length ? `
|
||||
|
||||
Reference in New Issue
Block a user