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
+68
View File
@@ -0,0 +1,68 @@
{
"before": {
"total": 113,
"ima_web": 0,
"ima_wiki": 0,
"has_cols": false
},
"after": {
"total": 113,
"ima_web": 0,
"ima_wiki": 0,
"has_cols": false
},
"stats": {
"probano": 50,
"succ_wiki_hr": 2,
"succ_wiki_en": 1,
"succ_search_hr": 5,
"succ_search_en": 3,
"applied": 0,
"kandidati": 5,
"zero_match": 45
},
"apply_rows": [],
"candidate_rows": [
{
"id": 4,
"naziv": "Nagrada Grada Čabra",
"predlozeni_url": "https://hr.wikipedia.org/wiki/Nagrada_Grada_Pakraca_(automobilizam)",
"lang": "hr-search",
"confidence": 0.35,
"razlog": "Wikipedia HR opensearch 'Nagrada Grada Pakraca (automobilizam)', matches=2"
},
{
"id": 5,
"naziv": "Rally Opatija",
"predlozeni_url": "https://hr.wikipedia.org/wiki/Rally_Opatija",
"lang": "hr",
"confidence": 0.4,
"razlog": "Wikipedia HR direct slug, matches=2"
},
{
"id": 23,
"naziv": "Sveti Vid",
"predlozeni_url": "https://hr.wikipedia.org/wiki/Sveti_Vid",
"lang": "hr",
"confidence": 0.4,
"razlog": "Wikipedia HR direct slug, matches=2"
},
{
"id": 30,
"naziv": "Rijeka kup",
"predlozeni_url": "https://hr.wikipedia.org/wiki/Rijeka_dubrova%C4%8Dka",
"lang": "hr-search",
"confidence": 0.35,
"razlog": "Wikipedia HR opensearch 'Rijeka dubrovačka', matches=1"
},
{
"id": 31,
"naziv": "Delta kup",
"predlozeni_url": "https://hr.wikipedia.org/wiki/Delta_Dunava",
"lang": "hr-search",
"confidence": 0.35,
"razlog": "Wikipedia HR opensearch 'Delta Dunava', matches=1"
}
],
"ts": "2026-05-05T07:09:59.816086+00:00"
}