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:
@@ -141,6 +141,7 @@ td.actions-col .btn { padding: 4px 8px; font-size: 11px; }
|
||||
.sidebar { display: none; }
|
||||
}
|
||||
</style>
|
||||
<script src="/static/oib_format.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app" id="appShell">
|
||||
@@ -653,7 +654,7 @@ async function loadTenants() {
|
||||
<tr><td>${t.id}</td><td><code>${escapeHtml(t.slug)}</code></td>
|
||||
<td><strong>${escapeHtml(t.display_name)}</strong></td>
|
||||
<td><span class="badge cyan">${escapeHtml(t.type||'—')}</span></td>
|
||||
<td>${escapeHtml(t.oib||'—')}</td>
|
||||
<td>${t.oib?escapeHtml(formatOib(t.oib)):'—'}</td>
|
||||
<td><span class="badge ${t.status==='active'?'green':'gray'}">${escapeHtml(t.status||'—')}</span></td></tr>
|
||||
`).join('') || '<tr><td colspan="6" class="empty">—</td></tr>';
|
||||
$('#savezi2Tbody').innerHTML = (d.savezi || []).map(s => `
|
||||
@@ -663,7 +664,7 @@ async function loadTenants() {
|
||||
$('#klubCount').textContent = `${(d.klubovi||[]).length} prikazano`;
|
||||
$('#klubovi2Tbody').innerHTML = (d.klubovi || []).slice(0, 200).map(k => `
|
||||
<tr><td>${k.id}</td><td>${escapeHtml(k.naziv)}</td><td>${escapeHtml(k.sport||'—')}</td>
|
||||
<td>${escapeHtml(k.grad||'—')}</td><td>${escapeHtml(k.oib||'—')}</td><td>${k.savez_id||'—'}</td></tr>
|
||||
<td>${escapeHtml(k.grad||'—')}</td><td>${k.oib?escapeHtml(formatOib(k.oib,{klub_id:k.id,savez_id:k.savez_id})):'—'}</td><td>${k.savez_id||'—'}</td></tr>
|
||||
`).join('') || '<tr><td colspan="6" class="empty">—</td></tr>';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user