CC2 R3 frontend: login.html + admin_users.html (M1+M2+M10 UI)

- static/login.html: dark Palantir-style login with PGŽ branding,
  Prijava se / Zaboravljena lozinka, demo account quick-fills,
  GDPR cookie banner, autostore tokens (local/session)
- static/admin_users.html: full user-management admin panel:
  - Collapsible left sidebar (Pregled, Korisnici, Tenanti, Audit log,
    Sigurnost, GDPR, links to ERP/CRM)
  - Users table with filters (q, tenant, role, status, limit)
  - + Dodaj korisnika modal (CRUD via /api/admin/users/*)
  - Suspend / unsuspend / reset-password / delete actions
  - Audit log viewer + Security KPIs + GDPR queue
  - Self-service: change pwd, export data (Art. 20), erasure request (Art. 17)
- pgz_sport_api.py: /login and /admin/users URL routes
- auth/seed_demo.py: added tajnik@atletski.pgz.hr/Atl2026!,
  admin@ak-kvarner.hr/Kvarner2026! demo users

5/5 live tests pass: login JWT, /me, /admin/users, /gdpr/consent, /gdpr/export

Note: existing admin.html (CC4 ERP/OCR work) preserved intact;
admin_users.html is dedicated user-mgmt page linked from sidebar.
This commit is contained in:
Damir Radulić
2026-05-05 00:20:03 +02:00
parent cef4d2575b
commit 8fe2478b84
17 changed files with 10013 additions and 37 deletions
+9 -5
View File
@@ -80,17 +80,21 @@ def main():
print("== Users ==")
users = [
("damir@pgz.hr", "PGZ2026!", "Damir Radulić", "Damir", "Radulić", "pgz_admin", None, None),
("pero@atletika.pgz.hr", "PGZ2026!", "Pero Perić", "Pero", "Perić", "savez_admin", None, atletski_savez),
("ana@akkvarner.hr", "PGZ2026!", "Ana Anić", "Ana", "Anić", "klub_admin", ak_klub, atletski_savez),
("sportas@akkvarner.hr", "PGZ2026!", "Marko Marković", "Marko", "Marković", "klub_clan", ak_klub, atletski_savez),
("damir@pgz.hr", "PGZ2026!", "Damir Radulić", "Damir", "Radulić", "pgz_admin", None, None),
("tajnik@atletski.pgz.hr", "Atl2026!", "Tajnik Atletski","Tajnik","Atletski", "savez_admin", None, atletski_savez),
("admin@ak-kvarner.hr", "Kvarner2026!", "Admin Kvarner", "Admin", "Kvarner", "klub_admin", ak_klub, atletski_savez),
# Extra demos
("pero@atletika.pgz.hr", "PGZ2026!", "Pero Perić", "Pero", "Perić", "savez_admin", None, atletski_savez),
("ana@akkvarner.hr", "PGZ2026!", "Ana Anić", "Ana", "Anić", "klub_admin", ak_klub, atletski_savez),
("sportas@akkvarner.hr", "PGZ2026!", "Marko Marković", "Marko", "Marković", "klub_clan", ak_klub, atletski_savez),
]
for email, pwd, fn, im, pz, ut, kid, sid in users:
uid, action = upsert_user(email, pwd, fn, im, pz, ut, kid, sid)
print(f" [{action}] {email} (id={uid}, type={ut}, klub_id={kid}, savez_id={sid})")
print("\n== Sanity check ==")
for email in ["damir@pgz.hr","pero@atletika.pgz.hr","ana@akkvarner.hr","sportas@akkvarner.hr"]:
for email in ["damir@pgz.hr","tajnik@atletski.pgz.hr","admin@ak-kvarner.hr",
"pero@atletika.pgz.hr","ana@akkvarner.hr","sportas@akkvarner.hr"]:
u = db_one("SELECT id, email, user_type, klub_id, savez_id, aktivan FROM pgz_sport.users WHERE LOWER(email)=%s", (email,))
print(f" {email}: {u}")