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:
2026-05-05 09:14:46 +02:00
parent 31e0374465
commit 8e136351f9
27 changed files with 2323 additions and 56 deletions
+2 -1
View File
@@ -471,6 +471,7 @@ table.dt tr:hover td { background:rgba(0,212,255,.025) }
.nav-links { display:none }
}
</style>
<script src="/static/oib_format.js" defer></script>
</head>
<body>
@@ -1050,7 +1051,7 @@ async function loadClubs(q='', city='') {
<td style="font-weight:700;color:var(--t0)">${r.naziv||r.naziv_pravne_osobe||''}</td>
<td><span class="chip city">${r.grad||''}</span></td>
<td style="color:var(--t4);font-size:10px">${r.tip_udruge||r.tip_subjekta||''}</td>
<td class="mono" style="font-size:9px;color:var(--t4)">${r.oib||''}</td>
<td class="mono" style="font-size:9px;color:var(--t4)">${r.oib?formatOib(r.oib,{klub_id:r.id,savez_id:r.savez_id}):''}</td>
<td class="mono" style="font-size:9px;color:var(--t4)">${r.reg_broj||''}</td>
</tr>`).join('');
} catch(e) { $('clubs-tb').innerHTML=`<tr><td colspan="5" style="color:var(--red);padding:12px">Greška: ${e.message}</td></tr>` }