@@ -412,7 +412,7 @@ async function refreshToken() {
}
async function api(path, opts = {}) {
let tok = getToken();
- if (!tok) { location.href = '/sport/static/login.html'; return null; }
+ if (!tok) { location.href = '/static/login.html'; return null; }
const headers = Object.assign({}, opts.headers || {}, {'Authorization': 'Bearer ' + tok});
if (opts.body && !(opts.body instanceof FormData) && !headers['Content-Type']) {
headers['Content-Type'] = 'application/json';
@@ -421,7 +421,7 @@ async function api(path, opts = {}) {
let r = await fetch(API + path, Object.assign({}, opts, {headers}));
if (r.status === 401) {
const newTok = await refreshToken();
- if (!newTok) { clearAuth(); location.href = '/sport/static/login.html'; return null; }
+ if (!newTok) { clearAuth(); location.href = '/static/login.html'; return null; }
headers['Authorization'] = 'Bearer ' + newTok;
r = await fetch(API + path, Object.assign({}, opts, {headers}));
}
@@ -478,7 +478,7 @@ $('#userDropdown').addEventListener('click', e => e.stopPropagation());
$('#menuLogout').addEventListener('click', async () => {
await api('/auth/logout', {method:'POST'});
clearAuth();
- location.href = '/sport/static/login.html';
+ location.href = '/static/login.html';
});
$('#menuExport').addEventListener('click', async () => {
const r = await api('/users/me/gdpr-export', {method:'POST'}); if (!r) return;
@@ -807,9 +807,9 @@ $('#cookieNecessary').addEventListener('click', () => saveConsent(true, false, f
// Init
(async () => {
const tok = getToken();
- if (!tok) { location.href = '/sport/static/login.html'; return; }
+ if (!tok) { location.href = '/static/login.html'; return; }
const r = await api('/auth/me');
- if (!r || !r.ok) { clearAuth(); location.href = '/sport/static/login.html'; return; }
+ if (!r || !r.ok) { clearAuth(); location.href = '/static/login.html'; return; }
const me = await r.json();
localStorage.setItem(USER_KEY, JSON.stringify(me));
$('#userName').textContent = me.full_name || me.email;
diff --git a/static/app.html b/static/app.html
index 5e9ad96..c94f455 100644
--- a/static/app.html
+++ b/static/app.html
@@ -257,8 +257,8 @@ table tbody tr:hover{background:var(--bg3)}
.role-switch{display:none}
}
-
-
+
+
@@ -332,7 +332,15 @@ async function api(path){
}
// JWT-aware fetch wrapper
-function getToken(){ try { return localStorage.getItem('jwt') || localStorage.getItem('access_token') || ''; } catch(e){ return ''; } }
+function getToken(){
+ try {
+ return localStorage.getItem('pgz_access')
+ || sessionStorage.getItem('pgz_access')
+ || localStorage.getItem('jwt')
+ || localStorage.getItem('access_token')
+ || '';
+ } catch(e){ return ''; }
+}
async function apiAuth(path, opts){
opts = opts || {};
const h = Object.assign({}, opts.headers || {});
@@ -631,7 +639,7 @@ function logout(){
localStorage.removeItem('jwt');
} catch(e){}
alert('Odjavljen. (Production: redirect na /login)');
- window.location.href = '/sport/static/sport2.html';
+ window.location.href = '/static/sport2.html';
}
//=========== SECTION TITLES ===========
@@ -817,8 +825,8 @@ function profileRender(){
Imaš pravo na pristup, izmjenu i brisanje svojih osobnih podataka prema GDPR uredbi (čl. 15–17, 20).
-
-
+
+
@@ -832,7 +840,7 @@ SECTIONS['sportas:profil']= profileRender;
// Profile actions
function pickAvatar(){
if(!getToken()){
- alert('Avatar upload zahtijeva login (JWT). U demo modu nije dostupan.');
+ alert('Niste prijavljeni. Idite na /login pa se prijavite kao damir@pgz.hr / PGZ2026!');
return;
}
$('#avatar-input').click();
@@ -933,10 +941,7 @@ async function profileVerify2FA(){
if(r && r.status==='ok'){ alert('2FA aktivirano ✓'); closeDetail(); loadSection(); }
else alert('Pogrešan kod.');
}
-function profileDeleteAccount(){
- if(!confirm('Zaista zatraži brisanje računa? GDPR brisanje je nepovratno.')) return;
- alert('Zahtjev za brisanje poslan na PGŽ admin (M10 — backend).');
-}
+// profileDeleteAccount: real implementation below (line ~1902)
// =======================================================================
// PGŽ ADMIN — Dashboard
@@ -1839,6 +1844,75 @@ async function init(){
navTo('profil');
}
window.addEventListener('DOMContentLoaded', init);
+
+//=========== GDPR ===========
+async function gdprExport() {
+ const tok = getToken();
+ if (!tok) { alert('Niste prijavljeni. Idite na /login'); return; }
+ try {
+ const r = await fetch(API + '/users/me/gdpr-export', {
+ method: 'POST',
+ headers: { 'Authorization': 'Bearer ' + tok }
+ });
+ if (!r.ok) throw new Error('HTTP ' + r.status);
+ const data = await r.json();
+ // Download as JSON file
+ const blob = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json'});
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = 'gdpr-export-' + (data.user?.email || 'me') + '-' + new Date().toISOString().slice(0,10) + '.json';
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ alert('✓ Izvoz uspješan! Datoteka spremljena.');
+ } catch (e) {
+ alert('Greška pri izvozu: ' + e.message);
+ }
+}
+
+async function gdprAuditMy() {
+ const tok = getToken();
+ if (!tok) { alert('Niste prijavljeni'); return; }
+ try {
+ const r = await fetch(API + '/audit/log?user_id=me&limit=100', {
+ headers: { 'Authorization': 'Bearer ' + tok }
+ });
+ if (!r.ok) throw new Error('HTTP ' + r.status);
+ const data = await r.json();
+ const items = data.items || data.entries || [];
+ if (!items.length) {
+ alert('Nema audit zapisa za vaš račun.');
+ return;
+ }
+ const txt = items.slice(0, 30).map(e => {
+ const ts = new Date(e.created_at || e.timestamp).toLocaleString('hr-HR');
+ return ts + ' • ' + (e.action || '?') + ' • ' + (e.resource_type || '?') + ' • ' + (e.user_email || '?');
+ }).join('\n');
+ alert('Audit zapisi (zadnjih ' + Math.min(items.length, 30) + '):\n\n' + txt);
+ } catch (e) {
+ alert('Greška: ' + e.message);
+ }
+}
+
+async function profileDeleteAccount() {
+ if (!confirm('Sigurno želite zatražiti BRISANJE računa? Ovo je trajno.')) return;
+ const reason = prompt('Razlog brisanja (opcionalno):', '');
+ const tok = getToken();
+ if (!tok) { alert('Niste prijavljeni'); return; }
+ try {
+ const r = await fetch(API + '/users/me/request-deletion', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + tok},
+ body: JSON.stringify({reason: reason || ''})
+ });
+ if (r.ok) alert('✓ Zahtjev poslan. Bit ćete kontaktirani u 30 dana.');
+ else alert('Greška: HTTP ' + r.status);
+ } catch (e) {
+ alert('Greška: ' + e.message);
+ }
+}